From 00fb9bc9f95830169de1f52a5a613ce5b3263bf0 Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 22 Apr 2021 23:49:44 -0400 Subject: [PATCH] [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); }