diff --git a/common/database.cpp b/common/database.cpp index 00ce26b08..e35f2a16a 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -1143,21 +1143,21 @@ bool Database::GetZoneGraveyard(const uint32 graveyard_id, uint32* graveyard_zon return true; } -uint8 Database::GetPEQZone(uint32 zoneID, uint32 version){ - - std::string query = StringFormat("SELECT peqzone from zone where zoneidnumber='%i' AND (version=%i OR version=0) ORDER BY version DESC", zoneID, version); +uint8 Database::GetPEQZone(uint32 zone_id, uint32 version){ + std::string query = fmt::format( + "SELECT peqzone FROM zone WHERE zoneidnumber = {} AND (version = {} OR version = 0) ORDER BY version DESC LIMIT 1", + zone_id, + version + ); auto results = QueryDatabase(query); - if (!results.Success()) { + if (!results.Success() || !results.RowCount()) { return 0; } - if (results.RowCount() == 0) - return 0; - auto row = results.begin(); - return atoi(row[0]); + return static_cast(std::stoi(row[0])); } bool Database::CheckNameFilter(const char* name, bool surname) diff --git a/common/database.h b/common/database.h index 66f7cd1fa..ecd38a905 100644 --- a/common/database.h +++ b/common/database.h @@ -254,7 +254,7 @@ public: uint32 GetZoneGraveyardID(uint32 zone_id, uint32 version); - uint8 GetPEQZone(uint32 zoneID, uint32 version); + uint8 GetPEQZone(uint32 zone_id, uint32 version); uint8 GetRaceSkill(uint8 skillid, uint8 in_race); uint8 GetServerType(); uint8 GetSkillCap(uint8 skillid, uint8 in_race, uint8 in_class, uint16 in_level); diff --git a/common/database_schema.h b/common/database_schema.h index fc110bf2d..072e1e799 100644 --- a/common/database_schema.h +++ b/common/database_schema.h @@ -62,6 +62,7 @@ namespace DatabaseSchema { {"character_pet_buffs", "char_id"}, {"character_pet_info", "char_id"}, {"character_pet_inventory", "char_id"}, + {"character_peqzone_flags", "id"}, {"character_potionbelt", "id"}, {"character_skills", "id"}, {"character_spells", "id"}, @@ -129,6 +130,7 @@ namespace DatabaseSchema { "character_pet_buffs", "character_pet_info", "character_pet_inventory", + "character_peqzone_flags", "character_potionbelt", "character_skills", "character_spells", diff --git a/common/version.h b/common/version.h index 690983e3f..c5c8a36a5 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 9179 +#define CURRENT_BINARY_DATABASE_VERSION 9180 #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 2a734f866..df6d14e17 100644 --- a/utils/sql/db_update_manifest.txt +++ b/utils/sql/db_update_manifest.txt @@ -433,6 +433,7 @@ 9177|2022_03_06_table_structure_changes.sql|SHOW COLUMNS FROM `pets` LIKE 'id'|empty| 9178|2022_03_07_saylink_collation.sql|SELECT * FROM db_version WHERE version >= 9178|empty| 9179|2022_04_30_hp_regen_per_second.sql|SHOW COLUMNS FROM `npc_types` LIKE 'hp_regen_per_second'|empty| +9180|2022_05_01_character_peqzone_flags.sql|SHOW TABLES LIKE 'character_peqzone_flags'|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/2022_04_30_hp_regen_per_second.sql b/utils/sql/git/required/2022_04_30_hp_regen_per_second.sql index e36fc4d45..cc2d85a47 100644 --- a/utils/sql/git/required/2022_04_30_hp_regen_per_second.sql +++ b/utils/sql/git/required/2022_04_30_hp_regen_per_second.sql @@ -1 +1 @@ -ALTER TABLE npc_types ADD COLUMN hp_regen_per_second bigint(11) DEFAULT 0 AFTER hp_regen_rate; +ALTER TABLE npc_types ADD COLUMN hp_regen_per_second bigint(11) DEFAULT 0 AFTER hp_regen_rate; \ No newline at end of file diff --git a/utils/sql/git/required/2022_05_01_character_peqzone_flags.sql b/utils/sql/git/required/2022_05_01_character_peqzone_flags.sql new file mode 100644 index 000000000..9e4436ada --- /dev/null +++ b/utils/sql/git/required/2022_05_01_character_peqzone_flags.sql @@ -0,0 +1,14 @@ +SET NAMES utf8mb4; +SET FOREIGN_KEY_CHECKS = 0; + +-- ---------------------------- +-- Table structure for character_peqzone_flags +-- ---------------------------- +DROP TABLE IF EXISTS `character_peqzone_flags`; +CREATE TABLE `character_peqzone_flags` ( + `id` int NOT NULL DEFAULT 0, + `zone_id` int NOT NULL DEFAULT 0, + PRIMARY KEY (`id`, `zone_id`) USING BTREE +) ENGINE = InnoDB CHARACTER SET = latin1 COLLATE = latin1_swedish_ci ROW_FORMAT = Compact; + +SET FOREIGN_KEY_CHECKS = 1; diff --git a/zone/CMakeLists.txt b/zone/CMakeLists.txt index 256cb5706..51fc7f875 100644 --- a/zone/CMakeLists.txt +++ b/zone/CMakeLists.txt @@ -287,7 +287,6 @@ SET(zone_headers zone_store.h ) - ADD_EXECUTABLE(zone ${zone_sources} ${zone_headers}) INSTALL(TARGETS zone RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin) diff --git a/zone/client.h b/zone/client.h index c5998406c..8e3f22f01 100644 --- a/zone/client.h +++ b/zone/client.h @@ -992,11 +992,17 @@ public: virtual void ThrowingAttack(Mob* other, bool CanDoubleAttack = false); void DoClassAttacks(Mob *ca_target, uint16 skill = -1, bool IsRiposte=false); - void SetZoneFlag(uint32 zone_id); void ClearZoneFlag(uint32 zone_id); bool HasZoneFlag(uint32 zone_id) const; - void SendZoneFlagInfo(Client *to) const; void LoadZoneFlags(); + void SendZoneFlagInfo(Client *to) const; + void SetZoneFlag(uint32 zone_id); + + void ClearPEQZoneFlag(uint32 zone_id); + bool HasPEQZoneFlag(uint32 zone_id) const; + void LoadPEQZoneFlags(); + void SendPEQZoneFlagInfo(Client *to) const; + void SetPEQZoneFlag(uint32 zone_id); bool CanFish(); void GoFish(); @@ -1917,6 +1923,7 @@ private: float AreaEndRegen; std::set zone_flags; + std::set peqzone_flags; ClientTaskState *task_state; int TotalSecondsPlayed; diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index e14266a23..60849c1ae 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -536,6 +536,7 @@ void Client::CompleteConnect() SetDueling(false); EnteringMessages(this); + LoadPEQZoneFlags(); LoadZoneFlags(); /* Sets GM Flag if needed & Sends Petition Queue */ diff --git a/zone/command.cpp b/zone/command.cpp index a2eb3757b..efe6b1a2a 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -184,7 +184,7 @@ int command_init(void) 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. 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("flags", "- displays the Zone 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) || @@ -276,6 +276,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", "[Zone ID|Zone Short Name] - Teleports you to the specified zone if you meet the requirements.", AccountStatus::Player, command_peqzone) || + command_add("peqzone_flags", "- displays the PEQZone Flags of you or your target", AccountStatus::Player, command_peqzone_flags) || 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) || @@ -1331,6 +1332,7 @@ void command_bot(Client *c, const Seperator *sep) #include "gm_commands/path.cpp" #include "gm_commands/peekinv.cpp" #include "gm_commands/peqzone.cpp" +#include "gm_commands/peqzone_flags.cpp" #include "gm_commands/permaclass.cpp" #include "gm_commands/permagender.cpp" #include "gm_commands/permarace.cpp" diff --git a/zone/command.h b/zone/command.h index d65f8ad94..26476becc 100644 --- a/zone/command.h +++ b/zone/command.h @@ -192,6 +192,7 @@ void command_packetprofile(Client *c, const Seperator *sep); void command_path(Client *c, const Seperator *sep); void command_peekinv(Client *c, const Seperator *sep); void command_peqzone(Client *c, const Seperator *sep); +void command_peqzone_flags(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); diff --git a/zone/gm_commands/peqzone.cpp b/zone/gm_commands/peqzone.cpp index 3c94965fc..1632087fc 100755 --- a/zone/gm_commands/peqzone.cpp +++ b/zone/gm_commands/peqzone.cpp @@ -68,12 +68,8 @@ void command_peqzone(Client *c, const Seperator *sep) return; } - bool allows_peqzone = ( - content_db.GetPEQZone(zone_id, 0) ? - true : - false - ); - if (!allows_peqzone) { + uint8 peqzone_flag = content_db.GetPEQZone(zone_id, 0); + if (peqzone_flag == 0) { c->Message( Chat::White, fmt::format( @@ -83,6 +79,16 @@ void command_peqzone(Client *c, const Seperator *sep) ).c_str() ); return; + } else if (peqzone_flag == 2 && !c->HasPEQZoneFlag(zone_id)) { + c->Message( + Chat::White, + fmt::format( + "You do not have the required PEQZone flag to use this command to enter {} ({}).", + zone_long_name, + zone_short_name + ).c_str() + ); + return; } if (zone_id == zone->GetZoneID()) { diff --git a/zone/gm_commands/peqzone_flags.cpp b/zone/gm_commands/peqzone_flags.cpp new file mode 100644 index 000000000..cde3eb4cd --- /dev/null +++ b/zone/gm_commands/peqzone_flags.cpp @@ -0,0 +1,17 @@ +#include "../client.h" + +void command_peqzone_flags(Client *c, const Seperator *sep) +{ + Client *target = c; + + if ( + c->GetTarget() && + c->GetTarget()->IsClient() && + c->Admin() >= minStatusToSeeOthersZoneFlags + ) { + target = c->GetTarget()->CastToClient(); + } + + target->SendPEQZoneFlagInfo(c); +} + diff --git a/zone/lua_client.cpp b/zone/lua_client.cpp index 6abd1d895..59b22a91f 100644 --- a/zone/lua_client.cpp +++ b/zone/lua_client.cpp @@ -1100,17 +1100,17 @@ int Lua_Client::GetCharacterFactionLevel(int faction_id) { return self->GetCharacterFactionLevel(faction_id); } -void Lua_Client::SetZoneFlag(int zone_id) { +void Lua_Client::SetZoneFlag(uint32 zone_id) { Lua_Safe_Call_Void(); self->SetZoneFlag(zone_id); } -void Lua_Client::ClearZoneFlag(int zone_id) { +void Lua_Client::ClearZoneFlag(uint32 zone_id) { Lua_Safe_Call_Void(); self->ClearZoneFlag(zone_id); } -bool Lua_Client::HasZoneFlag(int zone_id) { +bool Lua_Client::HasZoneFlag(uint32 zone_id) { Lua_Safe_Call_Bool(); return self->HasZoneFlag(zone_id); } @@ -2446,6 +2446,36 @@ bool Lua_Client::TakePlatinum(uint32 platinum, bool update_client) { return self->TakePlatinum(platinum, update_client); } +void Lua_Client::LoadZoneFlags() { + Lua_Safe_Call_Void(); + self->LoadZoneFlags(); +} + +void Lua_Client::ClearPEQZoneFlag(uint32 zone_id) { + Lua_Safe_Call_Void(); + self->ClearPEQZoneFlag(zone_id); +} + +bool Lua_Client::HasPEQZoneFlag(uint32 zone_id) { + Lua_Safe_Call_Bool(); + return self->HasPEQZoneFlag(zone_id); +} + +void Lua_Client::LoadPEQZoneFlags() { + Lua_Safe_Call_Void(); + self->LoadPEQZoneFlags(); +} + +void Lua_Client::SendPEQZoneFlagInfo(Lua_Client to) { + Lua_Safe_Call_Void(); + self->SendPEQZoneFlagInfo(to); +} + +void Lua_Client::SetPEQZoneFlag(uint32 zone_id) { + Lua_Safe_Call_Void(); + self->SetPEQZoneFlag(zone_id); +} + luabind::scope lua_register_client() { return luabind::class_("Client") .def(luabind::constructor<>()) @@ -2489,7 +2519,8 @@ luabind::scope lua_register_client() { .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("ClearPEQZoneFlag", (void(Lua_Client::*)(uint32))&Lua_Client::ClearPEQZoneFlag) + .def("ClearZoneFlag", (void(Lua_Client::*)(uint32))&Lua_Client::ClearZoneFlag) .def("Connected", (bool(Lua_Client::*)(void))&Lua_Client::Connected) .def("CountAugmentEquippedByID", (int(Lua_Client::*)(uint32))&Lua_Client::CountAugmentEquippedByID) .def("CountItem", (int(Lua_Client::*)(uint32))&Lua_Client::CountItem) @@ -2646,9 +2677,10 @@ luabind::scope lua_register_client() { .def("HasDisciplineLearned", (bool(Lua_Client::*)(uint16))&Lua_Client::HasDisciplineLearned) .def("HasExpeditionLockout", (bool(Lua_Client::*)(std::string, std::string))&Lua_Client::HasExpeditionLockout) .def("HasItemEquippedByID", (bool(Lua_Client::*)(uint32))&Lua_Client::HasItemEquippedByID) + .def("HasPEQZoneFlag", (bool(Lua_Client::*)(uint32))&Lua_Client::HasPEQZoneFlag) .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("HasZoneFlag", (bool(Lua_Client::*)(uint32))&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) @@ -2675,6 +2707,8 @@ luabind::scope lua_register_client() { .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("LoadPEQZoneFlags", (void(Lua_Client::*)(void))&Lua_Client::LoadPEQZoneFlags) + .def("LoadZoneFlags", (void(Lua_Client::*)(void))&Lua_Client::LoadZoneFlags) .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) @@ -2749,6 +2783,7 @@ luabind::scope lua_register_client() { .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("SendPEQZoneFlagInfo", (void(Lua_Client::*)(Lua_Client))&Lua_Client::SendPEQZoneFlagInfo) .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) @@ -2795,6 +2830,7 @@ luabind::scope lua_register_client() { .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("SetPEQZoneFlag", (void(Lua_Client::*)(uint32))&Lua_Client::SetPEQZoneFlag) .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) @@ -2809,7 +2845,7 @@ luabind::scope lua_register_client() { .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("SetZoneFlag", (void(Lua_Client::*)(uint32))&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) diff --git a/zone/lua_client.h b/zone/lua_client.h index cc48ba4f2..f38516cdf 100644 --- a/zone/lua_client.h +++ b/zone/lua_client.h @@ -255,10 +255,16 @@ public: bool UseDiscipline(int spell_id, int target_id); bool HasDisciplineLearned(uint16 spell_id); int GetCharacterFactionLevel(int faction_id); - void SetZoneFlag(int zone_id); - void ClearZoneFlag(int zone_id); - bool HasZoneFlag(int zone_id); + void ClearZoneFlag(uint32 zone_id); + bool HasZoneFlag(uint32 zone_id); + void LoadZoneFlags(); void SendZoneFlagInfo(Lua_Client to); + void SetZoneFlag(uint32 zone_id); + void ClearPEQZoneFlag(uint32 zone_id); + bool HasPEQZoneFlag(uint32 zone_id); + void LoadPEQZoneFlags(); + void SendPEQZoneFlagInfo(Lua_Client to); + void SetPEQZoneFlag(uint32 zone_id); void SetAATitle(const char *title); int GetClientVersion(); uint32 GetClientVersionBit(); diff --git a/zone/perl_client.cpp b/zone/perl_client.cpp index 0853d786e..6371fb4f9 100644 --- a/zone/perl_client.cpp +++ b/zone/perl_client.cpp @@ -6252,6 +6252,86 @@ XS(XS_Client_TakePlatinum) { XSRETURN(1); } +XS(XS_Client_ClearPEQZoneFlag); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_ClearPEQZoneFlag) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Client::ClearPEQZoneFlag(THIS, uint32 zone_id)"); // @categories Script Utility + { + Client *THIS; + uint32 zone_id = (uint32) SvUV(ST(1)); + VALIDATE_THIS_IS_CLIENT; + THIS->ClearPEQZoneFlag(zone_id); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_HasPEQZoneFlag); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_HasPEQZoneFlag) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Client::HasPEQZoneFlag(THIS, uint32 zone_id)"); // @categories Account and Character + { + Client *THIS; + bool RETVAL; + uint32 zone_id = (uint32) SvUV(ST(1)); + VALIDATE_THIS_IS_CLIENT; + RETVAL = THIS->HasPEQZoneFlag(zone_id); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Client_LoadPEQZoneFlags); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_LoadPEQZoneFlags) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::LoadPEQZoneFlags(THIS)"); // @categories Zones + { + Client *THIS; + VALIDATE_THIS_IS_CLIENT; + THIS->LoadPEQZoneFlags(); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_SendPEQZoneFlagInfo); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_SendPEQZoneFlagInfo) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Client::SendPEQZoneFlagInfo(THIS, Client* to)"); // @categories Account and Character, Zones + { + Client *THIS; + Client *to; + VALIDATE_THIS_IS_CLIENT; + 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."); + + THIS->SendPEQZoneFlagInfo(to); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_SetPEQZoneFlag); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_SetPEQZoneFlag) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Client::SetPEQZoneFlag(THIS, uint32 zone_id)"); // @categories Account and Character, PEQZones + { + Client *THIS; + uint32 zone_id = (uint32) SvUV(ST(1)); + VALIDATE_THIS_IS_CLIENT; + THIS->SetPEQZoneFlag(zone_id); + } + XSRETURN_EMPTY; +} + #ifdef __cplusplus extern "C" #endif @@ -6298,6 +6378,7 @@ XS(boot_Client) { newXSproto(strcpy(buf, "CheckIncreaseSkill"), XS_Client_CheckIncreaseSkill, file, "$$;$"); newXSproto(strcpy(buf, "CheckSpecializeIncrease"), XS_Client_CheckSpecializeIncrease, file, "$$"); newXSproto(strcpy(buf, "ClearCompassMark"), XS_Client_ClearCompassMark, file, "$"); + newXSproto(strcpy(buf, "ClearPEQZoneFlag"), XS_Client_ClearPEQZoneFlag, file, "$$"); newXSproto(strcpy(buf, "ClearZoneFlag"), XS_Client_ClearZoneFlag, file, "$$"); newXSproto(strcpy(buf, "Connected"), XS_Client_Connected, file, "$"); newXSproto(strcpy(buf, "CountAugmentEquippedByID"), XS_Client_CountAugmentEquippedByID, file, "$$"); @@ -6436,6 +6517,7 @@ XS(boot_Client) { newXSproto(strcpy(buf, "HasDisciplineLearned"), XS_Client_HasDisciplineLearned, file, "$$"); newXSproto(strcpy(buf, "HasExpeditionLockout"), XS_Client_HasExpeditionLockout, file, "$$$"); newXSproto(strcpy(buf, "HasItemEquippedByID"), XS_Client_HasItemEquippedByID, file, "$$"); + newXSproto(strcpy(buf, "HasPEQZoneFlag"), XS_Client_HasPEQZoneFlag, file, "$$"); newXSproto(strcpy(buf, "HasSkill"), XS_Client_HasSkill, file, "$$"); newXSproto(strcpy(buf, "HasSpellScribed"), XS_Client_HasSkill, file, "$$"); newXSproto(strcpy(buf, "HasZoneFlag"), XS_Client_HasZoneFlag, file, "$$"); @@ -6463,6 +6545,7 @@ XS(boot_Client) { 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, "LoadPEQZoneFlags"), XS_Client_LoadPEQZoneFlags, 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, "$$;$$"); @@ -6507,6 +6590,7 @@ XS(boot_Client) { 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, "SendPEQZoneFlagInfo"), XS_Client_SendPEQZoneFlagInfo, 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, "$$"); @@ -6550,6 +6634,7 @@ XS(boot_Client) { newXSproto(strcpy(buf, "SetInvulnerableEnvironmentDamage"), XS_Client_SetInvulnerableEnvironmentDamage, file, "$$"); newXSproto(strcpy(buf, "SetLanguageSkill"), XS_Client_SetLanguageSkill, file, "$$$"); newXSproto(strcpy(buf, "SetMaterial"), XS_Client_SetMaterial, file, "$$$"); + newXSproto(strcpy(buf, "SetPEQZoneFlag"), XS_Client_SetPEQZoneFlag, 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, "$$"); diff --git a/zone/zoning.cpp b/zone/zoning.cpp index 7ce199d80..e35fc5595 100644 --- a/zone/zoning.cpp +++ b/zone/zoning.cpp @@ -980,53 +980,50 @@ void Client::GoToDeath() { 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) { - if(HasZoneFlag(zone_id)) - return; - - zone_flags.insert(zone_id); - - // Retrieve all waypoints for this grid - std::string query = StringFormat("INSERT INTO zone_flags (charID,zoneID) VALUES(%d,%d)", CharacterID(), zone_id); - auto results = database.QueryDatabase(query); - if(!results.Success()) - LogError("MySQL Error while trying to set zone flag for [{}]: [{}]", GetName(), results.ErrorMessage().c_str()); -} - void Client::ClearZoneFlag(uint32 zone_id) { - if(!HasZoneFlag(zone_id)) + if (!HasZoneFlag(zone_id)) { return; + } zone_flags.erase(zone_id); - // Retrieve all waypoints for this grid - std::string query = StringFormat("DELETE FROM zone_flags WHERE charID=%d AND zoneID=%d", CharacterID(), zone_id); + std::string query = fmt::format( + "DELETE FROM zone_flags WHERE charID = {} AND zoneID = {}", + CharacterID(), + zone_id + ); auto results = database.QueryDatabase(query); - if(!results.Success()) - LogError("MySQL Error while trying to clear zone flag for [{}]: [{}]", GetName(), results.ErrorMessage().c_str()); + if (!results.Success()) { + LogError("MySQL Error while trying to clear zone flag for [{}]: [{}]", GetName(), results.ErrorMessage().c_str()); + } +} + +bool Client::HasZoneFlag(uint32 zone_id) const { + return zone_flags.find(zone_id) != zone_flags.end(); } void Client::LoadZoneFlags() { - - // Retrieve all waypoints for this grid - std::string query = StringFormat("SELECT zoneID from zone_flags WHERE charID=%d", CharacterID()); + std::string query = fmt::format( + "SELECT zoneID from zone_flags WHERE charID = {}", + CharacterID() + ); auto results = database.QueryDatabase(query); + if (!results.Success()) { LogError("MySQL Error while trying to load zone flags for [{}]: [{}]", GetName(), results.ErrorMessage().c_str()); return; } - for(auto row = results.begin(); row != results.end(); ++row) - zone_flags.insert(atoi(row[0])); -} + zone_flags.clear(); -bool Client::HasZoneFlag(uint32 zone_id) const { - return(zone_flags.find(zone_id) != zone_flags.end()); + for (auto row : results) { + zone_flags.insert(std::stoul(row[0])); + } } void Client::SendZoneFlagInfo(Client *to) const { - if(zone_flags.empty()) { + if (zone_flags.empty()) { to->Message( Chat::White, fmt::format( @@ -1041,7 +1038,7 @@ void Client::SendZoneFlagInfo(Client *to) const { to->Message( Chat::White, fmt::format( - "{} {} the following Flags:", + "{} {} the following Zone Flags:", to == this ? "You" : GetName(), to == this ? "have" : "has" ).c_str() @@ -1050,38 +1047,47 @@ void Client::SendZoneFlagInfo(Client *to) const { 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 = ZoneLongName(zone_id); - float safe_x, safe_y, safe_z, safe_heading; - int16 min_status = AccountStatus::Player; - uint8 min_level = 0; - char flag_name[128]; - 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"); - } - - to->Message( - Chat::White, - fmt::format( - "Zone Flag {} | Zone ID: {} Zone Name: {} ({}) Flag Name: {}", - flag_number, - zone_id, - zone_long_name, + const char* zone_short_name = ZoneName(zone_id, true); + if (zone_short_name != "UNKNOWN") { + 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; + char flag_name[128]; + if (!content_db.GetSafePoints( zone_short_name, + 0, + &safe_x, + &safe_y, + &safe_z, + &safe_heading, + &min_status, + &min_level, flag_name - ).c_str() - ); - flag_count++; + )) { + strcpy(flag_name, "ERROR"); + } + + to->Message( + Chat::White, + fmt::format( + "Flag {} | Zone ID: {} Zone Name: {} ({}){}", + flag_number, + zone_id, + zone_long_name, + zone_short_name, + ( + flag_name != "" ? + fmt::format( + " Flag Required: {}", + flag_name + ) : + "" + ) + ).c_str() + ); + flag_count++; + } } to->Message( @@ -1095,6 +1101,139 @@ void Client::SendZoneFlagInfo(Client *to) const { ); } +void Client::SetZoneFlag(uint32 zone_id) { + if (HasZoneFlag(zone_id)) { + return; + } + + zone_flags.insert(zone_id); + + std::string query = fmt::format( + "INSERT INTO zone_flags (charID, zoneID) VALUES ({}, {})", + CharacterID(), + zone_id + ); + auto results = database.QueryDatabase(query); + + if (!results.Success()) { + LogError("MySQL Error while trying to set zone flag for [{}]: [{}]", GetName(), results.ErrorMessage().c_str()); + } +} + +void Client::ClearPEQZoneFlag(uint32 zone_id) { + if (!HasPEQZoneFlag(zone_id)) { + return; + } + + peqzone_flags.erase(zone_id); + + auto query = fmt::format( + "DELETE FROM character_peqzone_flags WHERE id = {} AND zone_id = {}", + CharacterID(), + zone_id + ); + auto results = database.QueryDatabase(query); + + if (!results.Success()) { + LogError("MySQL Error while trying to clear zone flag for [{}]: [{}]", GetName(), results.ErrorMessage().c_str()); + } +} + +bool Client::HasPEQZoneFlag(uint32 zone_id) const { + return peqzone_flags.find(zone_id) != peqzone_flags.end(); +} + +void Client::LoadPEQZoneFlags() { + std::string query = fmt::format( + "SELECT zone_id from character_peqzone_flags WHERE id = {}", + CharacterID() + ); + auto results = database.QueryDatabase(query); + + if (!results.Success()) { + LogError("MySQL Error while trying to load zone flags for [{}]: [{}]", GetName(), results.ErrorMessage().c_str()); + return; + } + + peqzone_flags.clear(); + + for (auto row : results) { + peqzone_flags.insert(std::stoul(row[0])); + } +} + +void Client::SendPEQZoneFlagInfo(Client *to) const { + if (peqzone_flags.empty()) { + to->Message( + Chat::White, + fmt::format( + "{} {} no PEQZone Flags.", + to == this ? "You" : GetName(), + to == this ? "have" : "has" + ).c_str() + ); + return; + } + + to->Message( + Chat::White, + fmt::format( + "{} {} the following PEQZone Flags:", + to == this ? "You" : GetName(), + to == this ? "have" : "has" + ).c_str() + ); + + int flag_count = 0; + for (const auto& zone_id : peqzone_flags) { + int flag_number = (flag_count + 1); + std::string zone_short_name = ZoneName(zone_id, true); + if (zone_short_name != "UNKNOWN") { + std::string zone_long_name = ZoneLongName(zone_id); + to->Message( + Chat::White, + fmt::format( + "Flag {} | Zone ID: {} Zone Name: {} ({})", + flag_number, + zone_id, + zone_long_name, + zone_short_name + ).c_str() + ); + flag_count++; + } + } + + to->Message( + Chat::White, + fmt::format( + "{} {} {} PEQZone Flags.", + to == this ? "You" : GetName(), + to == this ? "have" : "has", + flag_count + ).c_str() + ); +} + +void Client::SetPEQZoneFlag(uint32 zone_id) { + if (HasPEQZoneFlag(zone_id)) { + return; + } + + peqzone_flags.insert(zone_id); + + auto query = fmt::format( + "INSERT INTO character_peqzone_flags (id, zone_id) VALUES ({}, {})", + CharacterID(), + zone_id + ); + auto results = database.QueryDatabase(query); + + if (!results.Success()) { + LogError("MySQL Error while trying to set zone flag for [{}]: [{}]", GetName(), results.ErrorMessage().c_str()); + } +} + bool Client::CanBeInZone() { //check some critial rules to see if this char needs to be booted from the zone //only enforce rules here which are serious enough to warrant being kicked from