diff --git a/addons/sourcemod/gamedata/shavit.games.txt b/addons/sourcemod/gamedata/shavit.games.txt index 295515fe..3875a8a1 100644 --- a/addons/sourcemod/gamedata/shavit.games.txt +++ b/addons/sourcemod/gamedata/shavit.games.txt @@ -17,6 +17,13 @@ "windows" "@CreateInterface" "linux" "@CreateInterface" } + + "CreateInterface_Engine" + { + "library" "engine" + "windows" "@CreateInterface" + "linux" "@CreateInterface" + } } "Offsets" @@ -39,6 +46,12 @@ { "Offsets" { + // todo + "CBaseTrigger::PassesTriggerFilters" + { + "windows" "210" + "linux" "211" + } // search string: "start %f %f %f" and then check the function call offsets above it and convert them to vtable offsets (divide by 4 most likely or whatever) "CCSPlayer::GetPlayerMaxSpeed" { @@ -73,6 +86,18 @@ "windows" "8" "linux" "8" } + // TODO + "GetClusterForOrigin" + { + "windows" "12" + "linux" "12" + } + // TODO + "GetArea" + { + "windows" "65" + "linux" "65" + } } "Signatures" @@ -116,6 +141,12 @@ { "Offsets" { + // https://asherkin.github.io/vtable/ + "CBaseTrigger::PassesTriggerFilters" + { + "windows" "197" + "linux" "198" + } // https://asherkin.github.io/vtable/ "CCSPlayer::GetPlayerMaxSpeed" { @@ -173,6 +204,18 @@ "windows" "5724" "linux" "5744" // +20 wow that's easy! } + // TODO + "GetClusterForOrigin" + { + "windows" "11" + "linux" "11" + } + // TODO + "GetArea" + { + "windows" "64" + "linux" "64" + } } "Signatures" @@ -223,6 +266,12 @@ { "Offsets" { + // https://asherkin.github.io/vtable/ + "CBaseTrigger::PassesTriggerFilters" + { + "windows" "201" + "linux" "202" + } // https://asherkin.github.io/vtable/ "CGameRules::IsSpawnPointValid" { diff --git a/addons/sourcemod/scripting/include/shavit/area_and_cluster_stages.sp b/addons/sourcemod/scripting/include/shavit/area_and_cluster_stages.sp new file mode 100644 index 00000000..e369522a --- /dev/null +++ b/addons/sourcemod/scripting/include/shavit/area_and_cluster_stages.sp @@ -0,0 +1,87 @@ +/* + * shavit's Timer - area_and_cluster_stages.sp + * by: carnifex + * + * This file is part of shavit's Timer. + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + */ + +// originally sourced from https://github.com/hermansimensen/mapstages + +Address IVEngineServer; +Handle gH_GetCluster; +Handle gH_GetArea; + +void LoadDHooks_mapstages(GameData gamedata) +{ + StartPrepSDKCall(SDKCall_Static); + if (!PrepSDKCall_SetFromConf(gamedata, SDKConf_Signature, "CreateInterface")) + { + SetFailState("Failed to get CreateInterface"); + } + + PrepSDKCall_AddParameter(SDKType_String, SDKPass_Pointer); + PrepSDKCall_AddParameter(SDKType_PlainOldData, SDKPass_Pointer, VDECODE_FLAG_ALLOWNULL); + PrepSDKCall_SetReturnInfo(SDKType_PlainOldData, SDKPass_Plain); + Handle CreateInterface = EndPrepSDKCall(); + + if (CreateInterface == null) + { + SetFailState("Unable to prepare SDKCall for CreateInterface"); + } + + char interfaceName[64]; + if (!GameConfGetKeyValue(gamedata, "IVEngineServer", interfaceName, sizeof(interfaceName))) + { + SetFailState("Failed to get IVEngineServer interface name"); + } + + IVEngineServer = SDKCall(CreateInterface, interfaceName, 0); + + if (!IVEngineServer) + { + SetFailState("Failed to get IVEngineServer pointer"); + } + + StartPrepSDKCall(SDKCall_Raw); + if (!PrepSDKCall_SetFromConf(gamedata, SDKConf_Virtual, "GetClusterForOrigin")) + { + SetFailState("Couldn't find GetClusterForOrigin offset"); + } + PrepSDKCall_AddParameter(SDKType_Vector, SDKPass_Plain); + PrepSDKCall_SetReturnInfo(SDKType_PlainOldData, SDKPass_Plain); + gH_GetCluster = EndPrepSDKCall(); + + StartPrepSDKCall(SDKCall_Raw); + if (!PrepSDKCall_SetFromConf(gamedata, SDKConf_Virtual, "GetArea")) + { + SetFailState("Couldn't find GetArea offset"); + } + PrepSDKCall_AddParameter(SDKType_Vector, SDKPass_Plain); + PrepSDKCall_SetReturnInfo(SDKType_PlainOldData, SDKPass_Plain); + gH_GetArea = EndPrepSDKCall(); + + delete CreateInterface; +} + +public int GetClusterForOrigin(const float pos[3]) +{ + return SDKCall(gH_GetCluster, IVEngineServer, pos); +} + +public int GetAreaForOrigin(const float pos[3]) +{ + return SDKCall(gH_GetArea, IVEngineServer, pos); +} diff --git a/addons/sourcemod/scripting/include/shavit/core.inc b/addons/sourcemod/scripting/include/shavit/core.inc index c583664d..62d9898b 100644 --- a/addons/sourcemod/scripting/include/shavit/core.inc +++ b/addons/sourcemod/scripting/include/shavit/core.inc @@ -30,6 +30,9 @@ // god i fucking hate sourcemod. NULL_VECTOR isn't const so it's not guaranteed to be 0,0,0 #define ZERO_VECTOR view_as({0,0,0}) +// stolen from boosterfix +#define EXPAND_VECTOR(%1) %1[0], %1[1], %1[2] + #define SHAVIT_LOG_QUERIES 0 #include diff --git a/addons/sourcemod/scripting/include/shavit/sql-create-tables-and-migrations.sp b/addons/sourcemod/scripting/include/shavit/sql-create-tables-and-migrations.sp index 1aae6290..1b19dc2f 100644 --- a/addons/sourcemod/scripting/include/shavit/sql-create-tables-and-migrations.sp +++ b/addons/sourcemod/scripting/include/shavit/sql-create-tables-and-migrations.sp @@ -47,6 +47,8 @@ enum Migration_Lowercase_startpositions, Migration_AddPlayertimesPointsCalcedFrom, // points calculated from wr float added to playertimes Migration_RemovePlayertimesPointsCalcedFrom, // lol + Migration_NormalizeMapzonePoints, + Migration_AddMapzonesFormAndTarget, // 25 MIGRATIONS_END }; @@ -194,7 +196,7 @@ public void SQL_CreateTables(Database2 hSQL, const char[] prefix, bool mysql) // FormatEx(sQuery, sizeof(sQuery), - "CREATE TABLE IF NOT EXISTS `%smapzones` (`id` INT AUTO_INCREMENT, `map` VARCHAR(255) NOT NULL, `type` INT, `corner1_x` FLOAT, `corner1_y` FLOAT, `corner1_z` FLOAT, `corner2_x` FLOAT, `corner2_y` FLOAT, `corner2_z` FLOAT, `destination_x` FLOAT NOT NULL DEFAULT 0, `destination_y` FLOAT NOT NULL DEFAULT 0, `destination_z` FLOAT NOT NULL DEFAULT 0, `track` INT NOT NULL DEFAULT 0, `flags` INT NOT NULL DEFAULT 0, `data` INT NOT NULL DEFAULT 0, `prebuilt` BOOL, PRIMARY KEY (`id`)) %s;", + "CREATE TABLE IF NOT EXISTS `%smapzones` (`id` INT AUTO_INCREMENT, `map` VARCHAR(255) NOT NULL, `type` INT, `corner1_x` FLOAT, `corner1_y` FLOAT, `corner1_z` FLOAT, `corner2_x` FLOAT, `corner2_y` FLOAT, `corner2_z` FLOAT, `destination_x` FLOAT NOT NULL DEFAULT 0, `destination_y` FLOAT NOT NULL DEFAULT 0, `destination_z` FLOAT NOT NULL DEFAULT 0, `track` INT NOT NULL DEFAULT 0, `flags` INT NOT NULL DEFAULT 0, `data` INT NOT NULL DEFAULT 0, `form` TINYINT, `target` VARCHAR(63), PRIMARY KEY (`id`)) %s;", gS_SQLPrefix, sOptionalINNODB); hTrans.AddQuery2(sQuery); @@ -305,6 +307,8 @@ void ApplyMigration(int migration) case Migration_Lowercase_startpositions: ApplyMigration_LowercaseMaps("startpositions", migration); case Migration_AddPlayertimesPointsCalcedFrom: ApplyMigration_AddPlayertimesPointsCalcedFrom(); case Migration_RemovePlayertimesPointsCalcedFrom: ApplyMigration_RemovePlayertimesPointsCalcedFrom(); + case Migration_NormalizeMapzonePoints: ApplyMigration_NormalizeMapzonePoints(); + case Migration_AddMapzonesFormAndTarget: ApplyMigration_AddMapzonesFormAndTarget(); } } @@ -366,9 +370,13 @@ void ApplyMigration_FixOldCompletionCounts() void ApplyMigration_AddPrebuiltToMapZonesTable() { +#if 0 char sQuery[192]; FormatEx(sQuery, 192, "ALTER TABLE `%smapzones` ADD COLUMN `prebuilt` BOOL;", gS_SQLPrefix); gH_SQL.Query2(SQL_TableMigrationSingleQuery_Callback, sQuery, Migration_AddPrebuiltToMapZonesTable, DBPrio_High); +#else + SQL_TableMigrationSingleQuery_Callback(null, null, "", Migration_AddPrebuiltToMapZonesTable); +#endif } // double up on this migration because some people may have used shavit-playtime which uses INT but I want FLOAT @@ -411,6 +419,38 @@ void ApplyMigration_RemovePlayertimesPointsCalcedFrom() gH_SQL.Query2(SQL_TableMigrationSingleQuery_Callback, sQuery, Migration_RemovePlayertimesPointsCalcedFrom, DBPrio_High); } +void ApplyMigration_NormalizeMapzonePoints() // TODO: test with sqlite lol +{ + char sQuery[666], greatest[16], least[16], id[16]; + greatest = gB_MySQL ? "GREATEST" : "MAX"; + least = gB_MySQL ? "LEAST" : "MIN"; + id = gB_MySQL ? "id" : "rowid"; + + FormatEx(sQuery, sizeof(sQuery), + "UPDATE `%smapzones` A, `%smapzones` B SET \ + A.corner1_x=%s(B.corner1_x, B.corner2_x), \ + A.corner1_y=%s(B.corner1_y, B.corner2_y), \ + A.corner1_z=%s(B.corner1_z, B.corner2_z), \ + A.corner2_x=%s(B.corner1_x, B.corner2_x), \ + A.corner2_y=%s(B.corner1_y, B.corner2_y), \ + A.corner2_z=%s(B.corner1_z, B.corner2_z) \ + WHERE A.%s = B.%s;", + gS_SQLPrefix, gS_SQLPrefix, + least, least, least, + greatest, greatest, greatest, + id, id + ); + + gH_SQL.Query2(SQL_TableMigrationSingleQuery_Callback, sQuery, Migration_NormalizeMapzonePoints, DBPrio_High); +} + +void ApplyMigration_AddMapzonesFormAndTarget() +{ + char sQuery[192]; + FormatEx(sQuery, sizeof(sQuery), "ALTER TABLE `%smapzones` ADD COLUMN `form` TINYINT, ADD COLUMN `target` VARCHAR(63);", gS_SQLPrefix); + gH_SQL.Query2(SQL_TableMigrationSingleQuery_Callback, sQuery, Migration_AddMapzonesFormAndTarget, DBPrio_High); +} + public void SQL_TableMigrationSingleQuery_Callback(Database db, DBResultSet results, const char[] error, any data) { InsertMigration(data); diff --git a/addons/sourcemod/scripting/include/shavit/zones.inc b/addons/sourcemod/scripting/include/shavit/zones.inc index 1d37dc4e..ba7bc8cf 100644 --- a/addons/sourcemod/scripting/include/shavit/zones.inc +++ b/addons/sourcemod/scripting/include/shavit/zones.inc @@ -24,8 +24,8 @@ #endif #define _shavit_zones_included -#define MAX_ZONES 64 -#define MAX_STAGES 51 // 😐 kind of arbitrary but also some space between this and MAX_ZONES +#define MAX_ZONES 128 +#define MAX_STAGES 69 enum { @@ -37,7 +37,7 @@ enum Zone_Freestyle, // ignores style physics when at this zone. e.g. WASD when SWing Zone_CustomSpeedLimit, // overwrites velocity limit in the zone Zone_Teleport, // teleports to a defined point - Zone_CustomSpawn, // << unused + Zone_CustomSpawn, // spawn position for a track. not a physical zone. Zone_Easybhop, // forces easybhop whether if the player is in non-easy styles or if the server has different settings Zone_Slide, // allows players to slide, in order to fix parts like the 5th stage of bhop_arcane Zone_Airaccelerate, // custom sv_airaccelerate inside this, @@ -51,7 +51,8 @@ enum enum { ZF_ForceRender = (1 << 0), - ZF_Prebuilt = (1 << 1), // comes from mod_zone_* entities + ZF_Hammerid = (1 << 1), // used by ZoneForm_{trigger_{multiple, teleport}, func_button} sometimes + ZF_Solid = (1 << 2), // forces the zone to block people... }; // Zone Display type @@ -90,33 +91,30 @@ enum ZoneWidth_Size }; -#if 0 +enum +{ + ZoneForm_Box, + ZoneForm_trigger_multiple, + ZoneForm_trigger_teleport, + ZoneForm_func_button, + ZoneForm_AreasAndClusters, +}; + enum struct zone_cache_t { - bool bZoneInitialized; - int iType; + int iType; // The type of zone. Zone_Start, Zone_End, etc... int iTrack; // 0 - main, 1 - bonus1 etc - int iEntity; - int iDatabaseID; // when ZF_Prebuilt, this is the entity's m_iHammerID - int iFlags; - int iData; - float fCorner1[3]; - float fCorner2[3]; - float fDestination[3]; + int iEntity; // Filled by Shavit_GetZone() if applicable. + int iDatabaseID; // Can be the database ID (> 0) for "sql" sources. Non-sql sources can fill this with whatever. + int iFlags; // The ZF_* flags. + int iData; // Depends on the zone. Zone_Stage stores the stage number in this for example. + float fCorner1[3]; // the hull mins. (or unused if ZoneForm_AreasAndClusters) // TODO, maybe reuse for cluster & areas? + float fCorner2[3]; // the hull maxs. (or unused if ZoneForm_AreasAndClusters) + float fDestination[3]; // Used by Zone_CustomSpawn, Zone_Teleport, and Zone_Stage. + int iForm; // ZoneForm_* + char sSource[16]; // "sql", "autobutton", "autozone", "sourcejump", "http", etc... + char sTarget[64]; // either the hammerid or the targetname } -#else -enum struct zone_cache_t -{ - bool bZoneInitialized; - bool bPrebuilt; // comes from mod_zone_* entities - int iZoneType; - int iZoneTrack; // 0 - main, 1 - bonus etc - int iEntityID; - int iDatabaseID; - int iZoneFlags; - int iZoneData; -} -#endif stock void GetZoneName(int client, int zoneType, char[] output, int size) { @@ -136,15 +134,114 @@ stock void GetZoneName(int client, int zoneType, char[] output, int size) "Zone_Stage", "Zone_NoTimerGravity", "Zone_Gravity", - "Zone_Speedmod" + "Zone_Speedmod", }; - if (zoneType >= ZONETYPES_SIZE) + if (zoneType < 0 || zoneType >= ZONETYPES_SIZE) FormatEx(output, size, "%T", "Zone_Unknown", client); else FormatEx(output, size, "%T", sTranslationStrings[zoneType], client); } +// Please follow something like this: +// mod_zone_start +// mod_zone_end +// mod_zone_checkpoint_X +// mod_zone_bonus_X_start +// mod_zone_bonus_X_end +// mod_zone_bonus_X_checkpoint_X +// +// climb_startbutton +// climb_endbutton +// climb_bonusX_startbutton +// climb_bonusX_endbutton +// +// climb_startzone +// climb_endzone +// climb_bonusX_startzone +// climb_bonusX_endzone +stock bool Shavit_ParseZoneTargetname(const char[] targetname, bool button, int& type, int& track, int& stage, const char[] mapname_for_log="") +{ + track = Track_Main; + type = -1; + stage = 0; + + if (strncmp(targetname, "climb_", 6) != 0 + && (button || strncmp(targetname, "mod_zone_", 9) != 0)) + { + return false; + } + + if (StrContains(targetname, "start") != -1) + { + type = Zone_Start; + } + else if (StrContains(targetname, "end") != -1) + { + type = Zone_End; + } + + int bonus = StrContains(targetname, "bonus"); + + if (bonus != -1) + { + track = Track_Bonus; + bonus += 5; // skip past "bonus" + if (targetname[bonus] == '_') bonus += 1; + + if ('1' <= targetname[bonus] <= '9') + { + track = StringToInt(targetname[bonus]); + + if (track < Track_Bonus || track > Track_Bonus_Last) + { + if (mapname_for_log[0]) LogError("invalid track in prebuilt map zone (%s) on %s", targetname, mapname_for_log); + return false; + } + } + } + + int checkpoint = StrContains(targetname, "checkpoint"); + + if (checkpoint != -1) + { + if (button) + { + if (mapname_for_log[0]) LogError("invalid button (%s) (has checkpoint) on %s", targetname, mapname_for_log); + return false; + } + + if (type != -1) + { + if (mapname_for_log[0]) LogError("invalid type (start/end + checkpoint) in prebuilt map zone (%s) on %s", targetname, mapname_for_log); + return false; // end/start & checkpoint... + } + + type = Zone_Stage; + checkpoint += 10; // skip past "checkpoint" + if (targetname[checkpoint] == '_') checkpoint += 1; + + if ('1' <= targetname[checkpoint] <= '9') + { + stage = StringToInt(targetname[checkpoint]); + + if (stage <= 0 || stage > MAX_STAGES) + { + if (mapname_for_log[0]) LogError("invalid stage number in prebuilt map zone (%s) on %s", targetname, mapname_for_log); + return false; + } + } + } + + if (type == -1) + { + if (mapname_for_log[0]) LogError("invalid zone type in prebuilt map zone (%s) on %s", targetname, mapname_for_log); + return false; + } + + return true; +} + /** * Called when a player enters a zone. * @@ -171,6 +268,11 @@ forward void Shavit_OnEnterZone(int client, int type, int track, int id, int ent */ forward void Shavit_OnLeaveZone(int client, int type, int track, int id, int entity, int data); +/** + * + */ +forward void Shavit_LoadZonesHere(); + /** * Called when a player leaves a zone. * @@ -182,16 +284,6 @@ forward void Shavit_OnLeaveZone(int client, int type, int track, int id, int ent */ forward Action Shavit_OnStageMessage(int client, int stageNumber, char[] message, int maxlen); -/** - * Retrieve the zone ID for a given stage number. - * Will return exception if stagenumber doesn't have a zone. - * - * @param stage Stage number. - * @param track Track number. - * @return Zone ID of stage. - */ -native int Shavit_GetStageZone(int stage, int track=Track_Main); - /** * Checks if a mapzone exists. * @@ -314,15 +406,56 @@ native void Shavit_SetStart(int client, int track, bool anglesonly); */ native void Shavit_DeleteSetStart(int client, int track); -#if 0 -native void Shavit_RemoveAllZones(); +/** + * Removes all zones from memory and then reloads zones from the database & any plugins. + * @noreturn + */ +native void Shavit_ReloadZones(); +/** + * Removes all zones from memory and unhooks any zones and kills any created entities. + * @noreturn + */ +native void Shavit_UnloadZones(); + +/** + * Returns the number of zones that are currently loaded in memory. + * + * @return The number of zones currently loaded in memory. + */ native int Shavit_GetZoneCount(); +/** + * Retrieves the zone_cache_t for a zone. + * + * @param index The zone index. 0 through Shavit_GetZoneCount()-1. + * @param zonecache The zone_cache_t struct that is retrieved from the zone. + * @param size sizeof(zone_cache_t) to make sure the caller has a matching struct version. + * + * @noreturn + */ native void Shavit_GetZone(int index, any[] zonecache, int size = sizeof(zone_cache_t)); +/** + * Adds a zone to memory. (Does NOT insert into DB). + * + * @param zonecache The zone_cache_t struct that is used to create or hook a zone. + * @param size sizeof(zone_cache_t) to make sure the caller has a matching struct version. + * + * @return The zone index on success (index for a particular zone can change). <0 on failure. + */ native int Shavit_AddZone(any[] zonecache, int size = sizeof(zone_cache_t)); -#endif + +/** + * Removes a zone from memory. (Does NOT delete from DB). + * WARNING: If there are zones after `index`, then they will be moved down in memory to fill this slot after removal. + * This unhooks the zone's entity too. + * + * @param index The zone's index. + * + * @noreturn + */ +native void Shavit_RemoveZone(int index); public SharedPlugin __pl_shavit_zones = { @@ -340,7 +473,6 @@ public void __pl_shavit_zones_SetNTVOptional() { MarkNativeAsOptional("Shavit_GetZoneData"); MarkNativeAsOptional("Shavit_GetZoneFlags"); - MarkNativeAsOptional("Shavit_GetStageZone"); MarkNativeAsOptional("Shavit_GetStageCount"); MarkNativeAsOptional("Shavit_InsideZone"); MarkNativeAsOptional("Shavit_InsideZoneGetID"); @@ -353,5 +485,11 @@ public void __pl_shavit_zones_SetNTVOptional() MarkNativeAsOptional("Shavit_GetZoneTrack"); MarkNativeAsOptional("Shavit_GetZoneType"); MarkNativeAsOptional("Shavit_GetZoneID"); + MarkNativeAsOptional("Shavit_ReloadZones"); + MarkNativeAsOptional("Shavit_UnloadZones"); + MarkNativeAsOptional("Shavit_GetZoneCount"); + MarkNativeAsOptional("Shavit_GetZone"); + MarkNativeAsOptional("Shavit_AddZone"); + MarkNativeAsOptional("Shavit_RemoveZone"); } #endif diff --git a/addons/sourcemod/scripting/shavit-misc.sp b/addons/sourcemod/scripting/shavit-misc.sp index 83147633..7268aaa1 100644 --- a/addons/sourcemod/scripting/shavit-misc.sp +++ b/addons/sourcemod/scripting/shavit-misc.sp @@ -466,7 +466,7 @@ void LoadMapFixes() kv.GetSectionName(key, sizeof(key)); kv.GetString(NULL_STRING, value, sizeof(value)); - PrintToServer(">>>> mapfixes: %s \"%s\"", key, value); + PrintToServer(">>>> shavit-misc/mapfixes: %s \"%s\"", key, value); ConVar cvar = FindConVar(key); diff --git a/addons/sourcemod/scripting/shavit-zones-http.sp b/addons/sourcemod/scripting/shavit-zones-http.sp new file mode 100644 index 00000000..603493dd --- /dev/null +++ b/addons/sourcemod/scripting/shavit-zones-http.sp @@ -0,0 +1,323 @@ +/* + * shavit's Timer - HTTP API module for shavit-zones + * by: rtldg + * + * This file is part of shavit's Timer (https://github.com/shavitush/bhoptimer) + * + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + */ + +#include +#include + +#include +#include + +#define USE_RIPEXT 1 +#if USE_RIPEXT +#include // https://github.com/ErikMinekus/sm-ripext +#else +#include // https://github.com/clugg/sm-json +#include // HTTP stuff +#endif + +#undef REQUIRE_PLUGIN + + +// todo: defines for JSON_Array & JSONArray? +// todo: or even compile this including both and have cvar determine whether ripext or not is used? + +#pragma semicolon 1 +#pragma newdecls required + + +static char gS_ZoneTypes[ZONETYPES_SIZE][18] = { + "start", + "end", + "respawn", + "stop", + "slay", + "freestyle", + "customspeedlimit", + "teleport", + "customspawn", + "easybhop", + "slide", + "airaccel", + "stage", + "notimergravity", + "gravity", + "speedmod", +}; + + +bool gB_YouCanLoadZonesNow = false; +char gS_Map[PLATFORM_MAX_PATH]; +char gS_ZonesForMap[PLATFORM_MAX_PATH]; +ArrayList gA_Zones = null; + +Convar gCV_Enable = null; +Convar gCV_ApiUrl = null; +Convar gCV_ApiKey = null; +Convar gCV_Source = null; + + +public Plugin myinfo = +{ + name = "[shavit] Map Zones (HTTP API)", + author = "rtldg, KiD Fearless", + description = "Retrieves map zones for bhoptimer from an HTTP API.", + version = SHAVIT_VERSION, + url = "https://github.com/shavitush/bhoptimer" +} + + +public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) +{ + RegPluginLibrary("shavit-zones-http"); + return APLRes_Success; +} + +public void OnPluginStart() +{ + gA_Zones = new ArrayList(sizeof(zone_cache_t)); + + gCV_Enable = new Convar("shavit_zones_http_enable", "1", "Whether to enable this or not...", 0, true, 0.0, true, 1.0); + gCV_ApiUrl = new Convar("shavit_zones_http_url", "", "API URL. Will replace `{map}` and `{key}` with the mapname and api key.\nExample sourcejump url:\n https://sourcejump.net/api/v2/maps/{map}/zones", FCVAR_PROTECTED); + gCV_ApiKey = new Convar("shavit_zones_http_key", "", "API key that some APIs might require.", FCVAR_PROTECTED); + gCV_Source = new Convar("shavit_zones_http_src", "http", "A string used by plugins to identify where a zone came from (http, sourcejump, sql, etc)"); + + Convar.AutoExecConfig(); +} + +public void OnMapEnd() +{ + gB_YouCanLoadZonesNow = false; +} + +public void OnConfigsExecuted() +{ + GetLowercaseMapName(gS_Map); + + if (!StrEqual(gS_Map, gS_ZonesForMap)) + { + RetrieveZones(gS_Map); + } +} + +public void Shavit_LoadZonesHere() +{ + gB_YouCanLoadZonesNow = true; + + if (StrEqual(gS_Map, gS_ZonesForMap)) + { + LoadCachedZones(); + } +} + +void LoadCachedZones() +{ + if (!gCV_Enable.BoolValue) + return; + + for (int i = 0; i < gA_Zones.Length; i++) + { + zone_cache_t cache; + gA_Zones.GetArray(i, cache); + Shavit_AddZone(cache); + } +} + +void RetrieveZones(const char[] mapname) +{ + if (!gCV_Enable.BoolValue) + return; + + char apikey[64], apiurl[333]; + gCV_ApiKey.GetString(apikey, sizeof(apikey)); + gCV_ApiUrl.GetString(apiurl, sizeof(apiurl)); + + if (!apiurl[0]) + { + LogError("Missing API URL"); + return; + } + + ReplaceString(apiurl, sizeof(apiurl), "{map}", mapname); + ReplaceString(apiurl, sizeof(apiurl), "{key}", apikey); + + DataPack pack = new DataPack(); + pack.WriteString(mapname); + +#if USE_RIPEXT + HTTPRequest http = new HTTPRequest(apiurl); + if (apikey[0]) + http.SetHeader("api-key", "%s", apikey); + http.SetHeader("map", "%s", mapname); + http.Get(RequestCallback_Ripext, pack); +#else + Handle request; + if (!(request = SteamWorks_CreateHTTPRequest(k_EHTTPMethodGET, apiurl)) + || (apikey[0] && !SteamWorks_SetHTTPRequestHeaderValue(request, "api-key", apikey)) + || !SteamWorks_SetHTTPRequestHeaderValue(request, "accept", "application/json") + || !SteamWorks_SetHTTPRequestContextValue(request, pack) + || !SteamWorks_SetHTTPRequestAbsoluteTimeoutMS(request, 4000) + //|| !SteamWorks_SetHTTPRequestRequiresVerifiedCertificate(request, true) + || !SteamWorks_SetHTTPCallbacks(request, RequestCompletedCallback_Steamworks) + || !SteamWorks_SendHTTPRequest(request) + ) + { + CloseHandle(request); + LogError("failed to setup & send HTTP request"); + return; + } +#endif +} + +#if USE_RIPEXT +void RequestCallback_Ripext(HTTPResponse response, DataPack pack, const char[] error) +{ + if (response.Status != HTTPStatus_OK || response.Data == null) + { + LogError("HTTP API request failed"); + delete pack; + return; + } + + handlestuff(pack, view_as(response.Data)); +} +#else +public void RequestCompletedCallback_Steamworks(Handle request, bool bFailure, bool bRequestSuccessful, EHTTPStatusCode eStatusCode, DataPack pack) +{ + if (bFailure || !bRequestSuccessful || eStatusCode != k_EHTTPStatusCode200OK) + { + pack.Reset(); + char mapname[PLATFORM_MAX_PATH]; + pack.ReadString(mapname, sizeof(mapname)); + delete pack; + LogError("HTTP API failed for '%s'. statuscode=%d", mapname, eStatusCode); + return; + } + + SteamWorks_GetHTTPResponseBodyCallback(request, RequestCallback_Steamworks, pack); +} + +void RequestCallback_Steamworks(const char[] data, DataPack pack, int datalen) +{ + JSON_Array records = view_as(json_decode(data)); + + if (records) + { + handlestuff(pack, records); + json_cleanup(records); + } +} +#endif + +#if USE_RIPEXT +void ReadVec(const char[] key, JSONObject json, float vec[3]) +#else +void ReadVec(const char[] key, JSON_Object json, float vec[3]) +#endif +{ + if (json.HasKey(key)) + { +#if USE_RIPEXT + JSONArray arr = view_as(json.Get(key)); +#else + JSON_Array arr = view_as(json.GetObject(key)); +#endif + vec[0] = arr.GetFloat(0); + vec[1] = arr.GetFloat(1); + vec[2] = arr.GetFloat(2); + } +} + +#if USE_RIPEXT +void handlestuff(DataPack pack, JSONArray records) +#else +void handlestuff(DataPack pack, JSON_Array records) +#endif +{ + pack.Reset(); + char mapname[PLATFORM_MAX_PATH]; + pack.ReadString(mapname, sizeof(mapname)); + delete pack; + + if (!StrEqual(mapname, gS_Map)) + { + return; + } + + char source[16]; + gCV_Source.GetString(source, sizeof(source)); + if (!source[0]) source = "http"; + + gS_ZonesForMap = mapname; + + gA_Zones.Clear(); + + for (int RN = 0; RN < records.Length; RN++) + { +#if USE_RIPEXT + JSONObject json = view_as(records.Get(RN)); +#else + JSON_Object json = records.GetObject(RN); +#endif + + char buf[32]; + zone_cache_t cache; + + json.GetString("type", buf, sizeof(buf)); + cache.iType = -1; + + for (int i = 0; i < ZONETYPES_SIZE; i++) + { + if (StrEqual(buf, gS_ZoneTypes[i])) + { + cache.iType = i; + } + } + + if (cache.iType == -1) + { + //PrintToServer(""); + continue; + } + + cache.iTrack = json.GetInt("track"); + //cache.iEntity + cache.iDatabaseID = json.GetInt("id"); + if (json.HasKey("flags")) cache.iFlags = json.GetInt("flags"); + if (json.HasKey("data")) cache.iData = json.GetInt("data"); + + if (cache.iType == Zone_Stage) + if (json.HasKey("index")) cache.iData = json.GetInt("index"); + + ReadVec("point_a", json, cache.fCorner1); + ReadVec("point_b", json, cache.fCorner2); + ReadVec("dest", json, cache.fDestination); + + if (json.HasKey("form")) cache.iForm = json.GetInt("form"); + json.GetString("target", cache.sTarget, sizeof(cache.sTarget)); + //json.GetString("source", cache.sSource, sizeof(cache.sSource)); + cache.sSource = source; + + gA_Zones.PushArray(cache); + } + + if (gB_YouCanLoadZonesNow) + LoadCachedZones(); +} diff --git a/addons/sourcemod/scripting/shavit-zones.sp b/addons/sourcemod/scripting/shavit-zones.sp index 3adc27f9..a6422ac8 100644 --- a/addons/sourcemod/scripting/shavit-zones.sp +++ b/addons/sourcemod/scripting/shavit-zones.sp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -44,13 +45,17 @@ #define DEBUG 0 +#define FSOLID_NOT_SOLID 4 +#define FSOLID_TRIGGER 8 +#define EF_NODRAW 32 +#define SOLID_BBOX 2 + EngineVersion gEV_Type = Engine_Unknown; Database2 gH_SQL = null; -bool gB_Connected = false; bool gB_MySQL = false; -bool gB_InsertedPrebuiltZones = false; -bool gB_PrecachedStuff = false; + +bool gB_YouCanLoadZonesNow = false; char gS_Map[PLATFORM_MAX_PATH]; @@ -67,6 +72,7 @@ enum struct zone_settings_t bool bNoHalo; int iBeam; int iHalo; + int iSpeed; char sBeam[PLATFORM_MAX_PATH]; } @@ -75,19 +81,14 @@ enum struct zone_settings_t // 2 - wait for E tap to setup second coord // 3 - confirm int gI_MapStep[MAXPLAYERS+1]; -int gI_ZoneFlags[MAXPLAYERS+1]; -int gI_ZoneData[MAXPLAYERS+1]; -int gI_ZoneTrack[MAXPLAYERS+1]; -int gI_ZoneType[MAXPLAYERS+1]; -int gI_ZoneDatabaseID[MAXPLAYERS+1]; +Handle gH_StupidTimer[MAXPLAYERS+1]; +int gI_CurrentTraceEntity = 0; +zone_cache_t gA_EditCache[MAXPLAYERS+1]; +int gI_HookListPos[MAXPLAYERS+1]; int gI_ZoneID[MAXPLAYERS+1]; bool gB_WaitingForChatInput[MAXPLAYERS+1]; -float gV_Point1[MAXPLAYERS+1][3]; -float gV_Point2[MAXPLAYERS+1][3]; -float gV_Teleport[MAXPLAYERS+1][3]; float gV_WallSnap[MAXPLAYERS+1][3]; bool gB_Button[MAXPLAYERS+1]; -bool gB_HackyResetCheck[MAXPLAYERS+1]; float gF_Modifier[MAXPLAYERS+1]; int gI_GridSnap[MAXPLAYERS+1]; @@ -97,21 +98,18 @@ bool gB_CursorTracing[MAXPLAYERS+1]; int gI_LatestTeleportTick[MAXPLAYERS+1]; // player zone status -bool gB_InsideZone[MAXPLAYERS+1][ZONETYPES_SIZE][TRACKS_SIZE]; +int gI_InsideZone[MAXPLAYERS+1][TRACKS_SIZE]; // bit flag bool gB_InsideZoneID[MAXPLAYERS+1][MAX_ZONES]; // zone cache zone_settings_t gA_ZoneSettings[ZONETYPES_SIZE][TRACKS_SIZE]; zone_cache_t gA_ZoneCache[MAX_ZONES]; // Vectors will not be inside this array. int gI_MapZones = 0; -float gV_MapZones[MAX_ZONES][2][3]; float gV_MapZones_Visual[MAX_ZONES][8][3]; -float gV_Destinations[MAX_ZONES][3]; float gV_ZoneCenter[MAX_ZONES][3]; -int gI_StageZoneID[TRACKS_SIZE][MAX_ZONES]; int gI_HighestStage[TRACKS_SIZE]; float gF_CustomSpawn[TRACKS_SIZE][3]; -int gI_EntityZone[4096]; +int gI_EntityZone[2048] = {-1, ...}; int gI_LastStage[MAXPLAYERS+1]; char gS_BeamSprite[PLATFORM_MAX_PATH]; @@ -123,11 +121,13 @@ TopMenu gH_AdminMenu = null; TopMenuObject gH_TimerCommands = INVALID_TOPMENUOBJECT; // misc cache -bool gB_ZoneCreationQueued = false; bool gB_Late = false; ConVar sv_gravity = null; // cvars +Convar gCV_SQLZones = null; +Convar gCV_PrebuiltZones = null; +Convar gCV_ClimbButtons = null; Convar gCV_Interval = null; Convar gCV_TeleportToStart = null; Convar gCV_TeleportToEnd = null; @@ -162,10 +162,12 @@ chatstrings_t gS_ChatStrings; // forwards Handle gH_Forwards_EnterZone = null; Handle gH_Forwards_LeaveZone = null; +Handle gH_Forwards_LoadZonesHere = null; Handle gH_Forwards_StageMessage = null; // sdkcalls Handle gH_PhysicsRemoveTouchedList = null; +Handle gH_PassesTriggerFilters = null; Handle gH_CommitSuicide = null; // sourcemod always finds a way to amaze me // dhooks @@ -208,7 +210,6 @@ public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max // zone natives CreateNative("Shavit_GetZoneData", Native_GetZoneData); CreateNative("Shavit_GetZoneFlags", Native_GetZoneFlags); - CreateNative("Shavit_GetStageZone", Native_GetStageZone); CreateNative("Shavit_GetStageCount", Native_GetStageCount); CreateNative("Shavit_InsideZone", Native_InsideZone); CreateNative("Shavit_InsideZoneGetID", Native_InsideZoneGetID); @@ -221,6 +222,12 @@ public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max CreateNative("Shavit_GetZoneTrack", Native_GetZoneTrack); CreateNative("Shavit_GetZoneType", Native_GetZoneType); CreateNative("Shavit_GetZoneID", Native_GetZoneID); + CreateNative("Shavit_ReloadZones", Native_ReloadZones); + CreateNative("Shavit_UnloadZones", Native_UnloadZones); + CreateNative("Shavit_GetZoneCount", Native_GetZoneCount); + CreateNative("Shavit_GetZone", Native_GetZone); + CreateNative("Shavit_AddZone", Native_AddZone); + CreateNative("Shavit_RemoveZone", Native_RemoveZone); // registers library, check "bool LibraryExists(const char[] name)" in order to use with other plugins RegPluginLibrary("shavit-zones"); @@ -255,6 +262,7 @@ public void OnPluginStart() RegAdminCmd("sm_zoneedit", Command_ZoneEdit, ADMFLAG_RCON, "Modify an existing zone."); RegAdminCmd("sm_editzone", Command_ZoneEdit, ADMFLAG_RCON, "Modify an existing zone. Alias of sm_zoneedit."); RegAdminCmd("sm_modifyzone", Command_ZoneEdit, ADMFLAG_RCON, "Modify an existing zone. Alias of sm_zoneedit."); + RegAdminCmd("sm_hookzone", Command_HookZone, ADMFLAG_RCON, "Hook an existing trigger, teleporter, or button."); RegAdminCmd("sm_tptozone", Command_TpToZone, ADMFLAG_RCON, "Teleport to a zone"); @@ -289,10 +297,9 @@ public void OnPluginStart() for (int i = 0; i <= 9; i++) { - char cmd[10], helptext[50]; - FormatEx(cmd, sizeof(cmd), "sm_s%d", i); - FormatEx(helptext, sizeof(helptext), "Go to stage %d", i); - RegConsoleCmd(cmd, Command_Stages, helptext); + char cmd[30]; + FormatEx(cmd, sizeof(cmd), "sm_s%d%cGo to stage %d", i, 0, i); // 😈 + RegConsoleCmd(cmd, Command_Stages, cmd[6]); } // events @@ -310,9 +317,13 @@ public void OnPluginStart() // forwards gH_Forwards_EnterZone = CreateGlobalForward("Shavit_OnEnterZone", ET_Event, Param_Cell, Param_Cell, Param_Cell, Param_Cell, Param_Cell, Param_Cell); gH_Forwards_LeaveZone = CreateGlobalForward("Shavit_OnLeaveZone", ET_Event, Param_Cell, Param_Cell, Param_Cell, Param_Cell, Param_Cell, Param_Cell); + gH_Forwards_LoadZonesHere = CreateGlobalForward("Shavit_LoadZonesHere", ET_Event); gH_Forwards_StageMessage = CreateGlobalForward("Shavit_OnStageMessage", ET_Event, Param_Cell, Param_Cell, Param_String, Param_Cell); // cvars and stuff + gCV_SQLZones = new Convar("shavit_zones_usesql", "1", "Whether to automatically load zones from the database or not.\n0 - Load nothing. (You'll need a plugin to add zones with `Shavit_AddZone()`)\n1 - Load zones from database.", 0, true, 0.0, true, 1.0); + gCV_PrebuiltZones = new Convar("shavit_zones_useprebuilt", "1", "Whether to automatically hook mod_zone_* zone entities.", 0, true, 0.0, true, 1.0); + gCV_ClimbButtons = new Convar("shavit_zones_usebuttons", "1", "Whether to automatically hook climb_* buttons.", 0, true, 0.0, true, 1.0); gCV_Interval = new Convar("shavit_zones_interval", "1.0", "Interval between each time a mapzone is being drawn to the players.", 0, true, 0.25, true, 5.0); gCV_TeleportToStart = new Convar("shavit_zones_teleporttostart", "1", "Teleport players to the start zone on timer restart?\n0 - Disabled\n1 - Enabled", 0, true, 0.0, true, 1.0); gCV_TeleportToEnd = new Convar("shavit_zones_teleporttoend", "1", "Teleport players to the end zone on sm_end?\n0 - Disabled\n1 - Enabled", 0, true, 0.0, true, 1.0); @@ -331,6 +342,7 @@ public void OnPluginStart() gCV_ResetClassnameMain = new Convar("shavit_zones_resetclassname_main", "", "What classname to use when resetting the player.\nWould be applied once player teleports to the start zone or on every start if shavit_zones_forcetargetnamereset cvar is set to 1.\nYou don't need to touch this"); gCV_ResetClassnameBonus = new Convar("shavit_zones_resetclassname_bonus", "", "What classname to use when resetting the player (on bonus tracks).\nWould be applied once player teleports to the start zone or on every start if shavit_zones_forcetargetnamereset cvar is set to 1.\nYou don't need to touch this"); + gCV_SQLZones.AddChangeHook(OnConVarChanged); gCV_Interval.AddChangeHook(OnConVarChanged); gCV_UseCustomSprite.AddChangeHook(OnConVarChanged); gCV_Offset.AddChangeHook(OnConVarChanged); @@ -358,11 +370,6 @@ public void OnPluginStart() } } - for(int i = 0; i < sizeof(gI_EntityZone); i++) - { - gI_EntityZone[i] = -1; - } - gB_ReplayRecorder = LibraryExists("shavit-replay-recorder"); gB_Eventqueuefix = LibraryExists("eventqueuefix"); gB_AdminMenu = LibraryExists("adminmenu"); @@ -384,7 +391,7 @@ public void OnPluginStart() OnClientConnected(i); OnClientPutInServer(i); - if (AreClientCookiesCached(i) && !IsFakeClient(i)) + if (AreClientCookiesCached(i)) { OnClientCookiesCached(i); } @@ -393,9 +400,26 @@ public void OnPluginStart() } } +void KillShavitZoneEnts(const char[] classname) +{ + char targetname[64]; + int ent = -1; + + while ((ent = FindEntityByClassname(ent, classname)) != -1) + { + GetEntPropString(ent, Prop_Data, "m_iName", targetname, sizeof(targetname)); + + if (StrContains(targetname, "shavit_zones_") == 0) + { + AcceptEntityInput(ent, "Kill"); + } + } +} + public void OnPluginEnd() { - UnloadZones(0); + KillShavitZoneEnts("trigger_multiple"); + KillShavitZoneEnts("player_speedmod"); } void LoadDHooks() @@ -435,6 +459,21 @@ void LoadDHooks() SetFailState("Failed to create sdkcall to \"PhysicsRemoveTouchedList\"!"); } + StartPrepSDKCall(SDKCall_Entity); + + if (!PrepSDKCall_SetFromConf(hGameData, SDKConf_Virtual, "CBaseTrigger::PassesTriggerFilters")) + { + SetFailState("Failed to find \"CBaseTrigger::PassesTriggerFilters\" offset!"); + } + + PrepSDKCall_SetReturnInfo(SDKType_Bool, SDKPass_Plain); + PrepSDKCall_AddParameter(SDKType_CBaseEntity, SDKPass_Pointer); + + if (!(gH_PassesTriggerFilters = EndPrepSDKCall())) + { + SetFailState("Failed to create sdkcall to \"CBaseTrigger::PassesTriggerFilters\"!"); + } + delete hGameData; hGameData = LoadGameConfigFile("sdktools.games"); @@ -511,27 +550,21 @@ public void OnConVarChanged(ConVar convar, const char[] oldValue, const char[] n { delete gH_DrawVisible; delete gH_DrawAllZones; - gH_DrawVisible = CreateTimer(gCV_Interval.FloatValue, Timer_DrawVisible, INVALID_HANDLE, TIMER_REPEAT|TIMER_FLAG_NO_MAPCHANGE); - gH_DrawAllZones = CreateTimer(gCV_Interval.FloatValue, Timer_DrawAllZones, INVALID_HANDLE, TIMER_REPEAT|TIMER_FLAG_NO_MAPCHANGE); + gH_DrawVisible = CreateTimer(gCV_Interval.FloatValue, Timer_DrawZones, 0, TIMER_REPEAT|TIMER_FLAG_NO_MAPCHANGE); + gH_DrawAllZones = CreateTimer(gCV_Interval.FloatValue, Timer_DrawZones, 1, TIMER_REPEAT|TIMER_FLAG_NO_MAPCHANGE); } - else if ((convar == gCV_Offset || convar == gCV_PrebuiltVisualOffset) && gI_MapZones > 0) + else if (convar == gCV_Offset || convar == gCV_PrebuiltVisualOffset) { - for(int i = 0; i < gI_MapZones; i++) + for (int i = 0; i < gI_MapZones; i++) { - if(!gA_ZoneCache[i].bZoneInitialized) + if ((convar == gCV_Offset && gA_ZoneCache[i].iForm == ZoneForm_Box) + || (convar == gCV_PrebuiltVisualOffset && gA_ZoneCache[i].iForm == ZoneForm_trigger_multiple)) { - continue; + gV_MapZones_Visual[i][0] = gA_ZoneCache[i].fCorner1; + gV_MapZones_Visual[i][7] = gA_ZoneCache[i].fCorner2; + + CreateZonePoints(gV_MapZones_Visual[i], convar == gCV_PrebuiltVisualOffset); } - - gV_MapZones_Visual[i][0][0] = gV_MapZones[i][0][0]; - gV_MapZones_Visual[i][0][1] = gV_MapZones[i][0][1]; - gV_MapZones_Visual[i][0][2] = gV_MapZones[i][0][2]; - gV_MapZones_Visual[i][7][0] = gV_MapZones[i][1][0]; - gV_MapZones_Visual[i][7][1] = gV_MapZones[i][1][1]; - gV_MapZones_Visual[i][7][2] = gV_MapZones[i][1][2]; - - float offset = -(gA_ZoneCache[i].bPrebuilt ? gCV_PrebuiltVisualOffset.FloatValue : 0.0) + gCV_Offset.FloatValue; - CreateZonePoints(gV_MapZones_Visual[i], offset); } } else if(convar == gCV_UseCustomSprite && !StrEqual(oldValue, newValue)) @@ -540,7 +573,43 @@ public void OnConVarChanged(ConVar convar, const char[] oldValue, const char[] n } else if (convar == gCV_BoxOffset) { - CreateZoneEntities(false); + for (int i = 0; i < gI_MapZones; i++) + { + if (gA_ZoneCache[i].iForm == ZoneForm_Box && gA_ZoneCache[i].iEntity > 0) + { + SetZoneMinsMaxs(i); + } + } + } + else if (convar == gCV_SQLZones) + { + for (int i = gI_MapZones; i > 0; i++) + { + if (StrEqual(gA_ZoneCache[i-1].sSource, "sql")) + Shavit_RemoveZone(i-1); + } + + if (convar.BoolValue) RefreshZones(); + } + else if (convar == gCV_PrebuiltZones) + { + for (int i = gI_MapZones; i > 0; i++) + { + if (StrEqual(gA_ZoneCache[i-1].sSource, "autozone")) + Shavit_RemoveZone(i-1); + } + + if (convar.BoolValue) add_prebuilts_to_cache("trigger_multiple", false); + } + else if (convar == gCV_ClimbButtons) + { + for (int i = gI_MapZones; i > 0; i++) + { + if (StrEqual(gA_ZoneCache[i-1].sSource, "autobutton")) + Shavit_RemoveZone(i-1); + } + + if (convar.BoolValue) add_prebuilts_to_cache("func_button", true); } } @@ -555,6 +624,7 @@ public void OnAdminMenuReady(Handle topmenu) gH_AdminMenu.AddItem("sm_deleteallzones", AdminMenu_DeleteAllZones, gH_TimerCommands, "sm_deleteallzones", ADMFLAG_RCON); gH_AdminMenu.AddItem("sm_zoneedit", AdminMenu_ZoneEdit, gH_TimerCommands, "sm_zoneedit", ADMFLAG_RCON); gH_AdminMenu.AddItem("sm_tptozone", AdminMenu_TpToZone, gH_TimerCommands, "sm_tptozone", ADMFLAG_RCON); + gH_AdminMenu.AddItem("sm_hookzone", AdminMenu_HookZone, gH_TimerCommands, "sm_hookzone", ADMFLAG_RCON); } } @@ -619,6 +689,18 @@ public void AdminMenu_TpToZone(Handle topmenu, TopMenuAction action, TopMenuObje } } +public void AdminMenu_HookZone(Handle topmenu, TopMenuAction action, TopMenuObject object_id, int param, char[] buffer, int maxlength) +{ + if (action == TopMenuAction_DisplayOption) + { + FormatEx(buffer, maxlength, "%T", "HookZone", param); + } + else if (action == TopMenuAction_SelectOption) + { + OpenHookMenu_Form(param); + } +} + public int Native_ZoneExists(Handle handler, int numParams) { return (GetZoneIndex(GetNativeCell(1), GetNativeCell(2)) != -1); @@ -626,12 +708,12 @@ public int Native_ZoneExists(Handle handler, int numParams) public int Native_GetZoneData(Handle handler, int numParams) { - return gA_ZoneCache[GetNativeCell(1)].iZoneData; + return gA_ZoneCache[GetNativeCell(1)].iData; } public int Native_GetZoneFlags(Handle handler, int numParams) { - return gA_ZoneCache[GetNativeCell(1)].iZoneFlags; + return gA_ZoneCache[GetNativeCell(1)].iFlags; } public int Native_InsideZone(Handle handler, int numParams) @@ -645,11 +727,16 @@ public int Native_InsideZoneGetID(Handle handler, int numParams) int iType = GetNativeCell(2); int iTrack = GetNativeCell(3); - for(int i = 0; i < MAX_ZONES; i++) + if (iTrack >= 0 && !(gI_InsideZone[client][iTrack] & (1 << iType))) + { + return false; + } + + for (int i = 0; i < gI_MapZones; i++) { if(gB_InsideZoneID[client][i] && - gA_ZoneCache[i].iZoneType == iType && - (gA_ZoneCache[i].iZoneTrack == iTrack || iTrack == -1)) + gA_ZoneCache[i].iType == iType && + (gA_ZoneCache[i].iTrack == iTrack || iTrack == -1)) { SetNativeCellRef(4, i); @@ -660,13 +747,6 @@ public int Native_InsideZoneGetID(Handle handler, int numParams) return false; } -public int Native_GetStageZone(Handle handler, int numParams) -{ - int iStageNumber = GetNativeCell(1); - int iTrack = GetNativeCell(2); - return gI_StageZoneID[iTrack][iStageNumber]; -} - public int Native_GetStageCount(Handle handler, int numParas) { return gI_HighestStage[GetNativeCell(1)]; @@ -695,7 +775,7 @@ public void SQL_DeleteMap_Callback(Database db, DBResultSet results, const char[ if(view_as(data)) { - OnMapStart(); + //DBConnectedSoDoStuff(); } } @@ -703,20 +783,19 @@ bool InsideZone(int client, int type, int track) { if(track != -1) { - return gB_InsideZone[client][type][track]; + return (gI_InsideZone[client][track] & (1 << type)) != 0; } else { + int res = 0; + for(int i = 0; i < TRACKS_SIZE; i++) { - if(gB_InsideZone[client][type][i]) - { - return true; - } + res |= gI_InsideZone[client][i]; } - } - return false; + return (res & (1 << type)) != 0; + } } public int Native_IsClientCreatingZone(Handle handler, int numParams) @@ -744,13 +823,13 @@ public int Native_GetClientLastStage(Handle plugin, int numParams) public any Native_GetZoneTrack(Handle plugin, int numParams) { int zoneid = GetNativeCell(1); - return gA_ZoneCache[zoneid].iZoneTrack; + return gA_ZoneCache[zoneid].iTrack; } public any Native_GetZoneType(Handle plugin, int numParams) { int zoneid = GetNativeCell(1); - return gA_ZoneCache[zoneid].iZoneType; + return gA_ZoneCache[zoneid].iType; } public any Native_GetZoneID(Handle plugin, int numParams) @@ -759,6 +838,131 @@ public any Native_GetZoneID(Handle plugin, int numParams) return gI_EntityZone[entity]; } +public any Native_ReloadZones(Handle plugin, int numParams) +{ + LoadZonesHere(); + return 0; +} + +public any Native_UnloadZones(Handle plugin, int numParams) +{ + UnloadZones(); + return 0; +} + +public any Native_GetZoneCount(Handle plugin, int numParams) +{ + return gI_MapZones; +} + +public any Native_GetZone(Handle plugin, int numParams) +{ + if (GetNativeCell(3) != sizeof(zone_cache_t)) + { + return ThrowNativeError(200, "zone_cache_t does not match latest(got %i expected %i). Please update your includes and recompile your plugins", GetNativeCell(3), sizeof(zone_cache_t)); + } + + SetNativeArray(2, gA_ZoneCache[GetNativeCell(1)], sizeof(zone_cache_t)); + return 0; +} + +public any Native_AddZone(Handle plugin, int numParams) +{ + if (gI_MapZones >= MAX_ZONES) + { + return -1; + } + + if (GetNativeCell(2) != sizeof(zone_cache_t)) + { + return ThrowNativeError(200, "zone_cache_t does not match latest(got %i expected %i). Please update your includes and recompile your plugins", GetNativeCell(2), sizeof(zone_cache_t)); + } + + zone_cache_t cache; + GetNativeArray(1, cache, sizeof(cache)); + cache.iEntity = -1; + + // normalize zone points... + FillBoxMinMax(cache.fCorner1, cache.fCorner2, cache.fCorner1, cache.fCorner2); + + gA_ZoneCache[gI_MapZones] = cache; + + gV_MapZones_Visual[gI_MapZones][0] = cache.fCorner1; + gV_MapZones_Visual[gI_MapZones][7] = cache.fCorner2; + + CreateZonePoints(gV_MapZones_Visual[gI_MapZones], cache.iForm == ZoneForm_trigger_multiple); + + AddVectors(cache.fCorner1, cache.fCorner2, gV_ZoneCenter[gI_MapZones]); + ScaleVector(gV_ZoneCenter[gI_MapZones], 0.5); + + if (cache.iType == Zone_Stage) + { + if (cache.iData > gI_HighestStage[cache.iTrack]) + { + gI_HighestStage[cache.iTrack] = cache.iData; + } + } + + return gI_MapZones++; +} + +public any Native_RemoveZone(Handle plugin, int numParams) +{ + int index = GetNativeCell(1); + + if (gI_MapZones <= 0 || index >= gI_MapZones) + { + return 0; + } + + zone_cache_t cache; cache = gA_ZoneCache[index]; + + int ent = gA_ZoneCache[index].iEntity; + ClearZoneEntity(index, true); + + if (ent && gA_ZoneCache[index].iForm == ZoneForm_Box) // created by shavit-zones + { + AcceptEntityInput(ent, "Kill"); + } + + int top = --gI_MapZones; + + if (index < top) + { + gI_EntityZone[gA_ZoneCache[top].iEntity] = index; + gA_ZoneCache[index] = gA_ZoneCache[top]; + gV_ZoneCenter[index] = gV_ZoneCenter[top]; + + for (int i = 0; i < sizeof(gV_MapZones_Visual[]); i++) + { + gV_MapZones_Visual[index][i] = gV_MapZones_Visual[top][i]; + } + + for (int i = 1; i <= MaxClients; i++) + { + gB_InsideZoneID[i][index] = gB_InsideZoneID[i][top]; + } + } + else + { + bool empty_InsideZoneID[MAX_ZONES]; + + for (int i = 1; i <= MaxClients; i++) + { + gB_InsideZoneID[i] = empty_InsideZoneID; + } + } + + RecalcInsideZoneAll(); + + if (cache.iType == Zone_Stage && cache.iData == gI_HighestStage[cache.iTrack]) + RecalcHighestStage(); + + // call EndTouchPost(zoneent, player) manually here? + + return 0; +} + bool JumpToZoneType(KeyValues kv, int type, int track) { static const char config_keys[ZONETYPES_SIZE][2][50] = { @@ -868,6 +1072,7 @@ bool LoadZonesConfig() gA_ZoneSettings[type][track].bFlatZone = view_as(kv.GetNum("flat", false)); gA_ZoneSettings[type][track].bUseVanillaSprite = view_as(kv.GetNum("vanilla_sprite", false)); gA_ZoneSettings[type][track].bNoHalo = view_as(kv.GetNum("no_halo", false)); + gA_ZoneSettings[type][track].iSpeed = kv.GetNum("speed", 0); kv.GetString("beam", gA_ZoneSettings[type][track].sBeam, sizeof(zone_settings_t::sBeam), ""); kv.GoBack(); } @@ -940,53 +1145,219 @@ void LoadZoneSettings() public void OnMapStart() { - if (!gB_PrecachedStuff) - { - GetLowercaseMapName(gS_Map); - LoadZoneSettings(); + GetLowercaseMapName(gS_Map); + LoadZoneSettings(); + //UnloadZones(); - if (gEV_Type == Engine_TF2) + if (gEV_Type == Engine_TF2) + { + PrecacheModel("models/error.mdl"); + } + else + { + PrecacheModel("models/props/cs_office/vending_machine.mdl"); + } +} + +public void OnConfigsExecuted() +{ + if (gH_DrawAllZones == null) + { + gH_DrawVisible = CreateTimer(gCV_Interval.FloatValue, Timer_DrawZones, 0, TIMER_REPEAT|TIMER_FLAG_NO_MAPCHANGE); + gH_DrawAllZones = CreateTimer(gCV_Interval.FloatValue, Timer_DrawZones, 1, TIMER_REPEAT|TIMER_FLAG_NO_MAPCHANGE); + } + + RequestFrame(LoadZonesHere); +} + +void LoadZonesHere() +{ + gB_YouCanLoadZonesNow = true; + UnloadZones(); + Call_StartForward(gH_Forwards_LoadZonesHere); + Call_Finish(); +} + +public void Shavit_LoadZonesHere() +{ + if (gCV_SQLZones.BoolValue && gH_SQL) + { + RefreshZones(); + } + + if (gCV_PrebuiltZones.BoolValue) + { + add_prebuilts_to_cache("trigger_multiple", false); + } + + if (gCV_ClimbButtons.BoolValue) + { + add_prebuilts_to_cache("func_button", true); + } +} + +void add_prebuilts_to_cache(const char[] classname, bool button) +{ + char targetname[64]; + + int ent = -1; + while (-1 != (ent = FindEntityByClassname(ent, classname))) + { + GetEntPropString(ent, Prop_Data, "m_iName", targetname, sizeof(targetname)); + + zone_cache_t cache; + + if (!Shavit_ParseZoneTargetname(targetname, button, cache.iType, cache.iTrack, cache.iData, gS_Map)) { - PrecacheModel("models/error.mdl"); + continue; + } + + int hammerid = GetEntProp(ent, Prop_Data, "m_iHammerID"); + + if (IntToString(hammerid, cache.sTarget, sizeof(cache.sTarget))) + { + cache.iFlags |= ZF_Hammerid; } else { - PrecacheModel("models/props/cs_office/vending_machine.mdl"); + cache.sTarget = targetname; } - gB_PrecachedStuff = true; - } + PrintToServer(">>>> shavit-zones: Hooking '%s' '%s' (%d)", classname, targetname, hammerid); - if(!gB_Connected) - { - return; - } + cache.iDatabaseID = -1; + cache.iForm = button ? ZoneForm_func_button : ZoneForm_trigger_multiple; + cache.sSource = button ? "autobutton" : "autozone"; - UnloadZones(0); - RefreshZones(); - - // start drawing mapzones here - if(gH_DrawAllZones == null) - { - gH_DrawVisible = CreateTimer(gCV_Interval.FloatValue, Timer_DrawVisible, INVALID_HANDLE, TIMER_REPEAT|TIMER_FLAG_NO_MAPCHANGE); - gH_DrawAllZones = CreateTimer(gCV_Interval.FloatValue, Timer_DrawAllZones, INVALID_HANDLE, TIMER_REPEAT|TIMER_FLAG_NO_MAPCHANGE); - } - - for(int i = 1; i <= MaxClients; i++) - { - if (IsValidClient(i) && !IsFakeClient(i)) + if (button) { - GetStartPosition(i); + Shavit_MarkKZMap(cache.iTrack); + } + else + { + float origin[3]; + GetEntPropVector(ent, Prop_Send, "m_vecOrigin", origin); + GetEntPropVector(ent, Prop_Send, "m_vecMins", cache.fCorner1); + GetEntPropVector(ent, Prop_Send, "m_vecMaxs", cache.fCorner2); + + //origin[2] -= (maxs[2] - 2.0); // so you don't get stuck in the ground + origin[2] += 1.0; // so you don't get stuck in the ground + AddVectors(origin, cache.fCorner1, cache.fCorner1); + AddVectors(origin, cache.fCorner2, cache.fCorner2); + } + + Shavit_AddZone(cache); + } +} + +public void OnGameFrame() +{ + bool search_trigger_multiple; + bool search_trigger_teleport; + bool search_func_button; + + for (int i = 0; i < gI_MapZones; i++) + { + if (gA_ZoneCache[i].iEntity > 0) + { + continue; + } + + switch (gA_ZoneCache[i].iForm) + { + case ZoneForm_Box: + { + if (!CreateZoneTrigger(i)) + { + return; // uhhhhhhhh + } + break; + } + case ZoneForm_trigger_multiple: + { + if (gA_ZoneCache[i].sTarget[0]) + search_trigger_multiple = true; + break; + } + case ZoneForm_trigger_teleport: + { + if (gA_ZoneCache[i].sTarget[0]) + search_trigger_teleport = true; + break; + } + case ZoneForm_func_button: + { + if (gA_ZoneCache[i].sTarget[0]) + search_func_button = true; + break; + } + } + } + + if (search_trigger_multiple) + { + FindEntitiesToHook("trigger_multiple", ZoneForm_trigger_multiple); + } + + if (search_trigger_teleport) + { + FindEntitiesToHook("trigger_teleport", ZoneForm_trigger_teleport); + } + + if (search_func_button) + { + FindEntitiesToHook("func_button", ZoneForm_func_button); + } +} + +void FindEntitiesToHook(const char[] classname, int form) +{ + char targetname[64]; + int ent = -1; + + while ((ent = FindEntityByClassname(ent, classname)) != -1) + { + if (gI_EntityZone[ent] > MaxClients) + { + continue; + } + + GetEntPropString(ent, Prop_Data, form == ZoneForm_trigger_teleport ? "m_target" : "m_iName", targetname, sizeof(targetname)); + + if (form == ZoneForm_trigger_multiple && StrContains(targetname, "shavit_zones_") == 0) + { + continue; + } + + char hammerid[12]; + IntToString(GetEntProp(ent, Prop_Data, "m_iHammerID"), hammerid, sizeof(hammerid)); // xd string comparisons + + for (int i = 0; i < gI_MapZones; i++) + { + if (gA_ZoneCache[i].iEntity > 0 || gA_ZoneCache[i].iForm != form + || !StrEqual(gA_ZoneCache[i].sTarget, (gA_ZoneCache[i].iFlags & ZF_Hammerid) ? hammerid : targetname)) + { + continue; + } + + if (form == ZoneForm_func_button) + { + HookButton(i, ent); + } + else + { + HookZoneTrigger(i, ent); + } } } } public void OnMapEnd() { - gB_PrecachedStuff = false; - gB_InsertedPrebuiltZones = false; + gB_YouCanLoadZonesNow = false; delete gH_DrawVisible; delete gH_DrawAllZones; + UnloadZones(); } public void OnClientPutInServer(int client) @@ -999,283 +1370,11 @@ public void OnClientPutInServer(int client) } } -public void OnEntityCreated(int entity, const char[] classname) -{ - if(StrEqual(classname, "func_button", false)) - { - RequestFrame(Frame_HookButton, EntIndexToEntRef(entity)); - } - else if(StrEqual(classname, "trigger_multiple", false)) - { - RequestFrame(Frame_HookTrigger, EntIndexToEntRef(entity)); - } -} - public void OnEntityDestroyed(int entity) { - if (entity > MaxClients && entity < 4096 && gI_EntityZone[entity] > -1) + if (entity > MaxClients && entity < 2048 && gI_EntityZone[entity] > -1) { - KillZoneEntity(gI_EntityZone[entity], false); - - if (!gB_ZoneCreationQueued) - { - RequestFrame(CreateZoneEntities, true); - gB_ZoneCreationQueued = true; - } - } -} - -bool GetButtonInfo(int entity, int& zone, int& track) -{ - char sName[32]; - GetEntPropString(entity, Prop_Data, "m_iName", sName, 32); - - if(StrContains(sName, "climb_") == -1) - { - return false; - } - - if(StrContains(sName, "startbutton") != -1) - { - zone = Zone_Start; - } - else if(StrContains(sName, "endbutton") != -1) - { - zone = Zone_End; - } - else - { - return false; - } - - int bonus = StrContains(sName, "bonus"); - - if (bonus != -1) - { - track = Track_Bonus; - - if ('0' <= sName[bonus+5] <= '9') - { - track = StringToInt(sName[bonus+5]); - - if (track < Track_Bonus || track > Track_Bonus_Last) - { - LogError("invalid track in climb button (%s) on %s", sName, gS_Map); - return false; - } - } - } - else - { - track = Track_Main; - } - - return true; -} - -public void Frame_HookButton(any data) -{ - int entity = EntRefToEntIndex(data); - - if(entity == INVALID_ENT_REFERENCE) - { - return; - } - - int zone = -1; - int track = Track_Main; - - if (!GetButtonInfo(entity, zone, track)) - { - return; - } - - Shavit_MarkKZMap(track); - SDKHook(entity, SDKHook_UsePost, UsePost); -} - -bool parse_mod_zone(char sName[32], int& zone, int& track, int& zonedata) -{ - // Please follow this naming scheme for this zones https://github.com/PMArkive/fly#trigger_multiple - // mod_zone_start - // mod_zone_end - // mod_zone_checkpoint_X - // mod_zone_bonus_X_start - // mod_zone_bonus_X_end - // mod_zone_bonus_X_checkpoint_X - - // Normalize some zone names that bhop_somp_island and bhop_overthinker use - if (StrEqual(sName, "mod_zone_start_bonus") || StrEqual(sName, "mod_zone_bonus_start")) - { - sName = "mod_zone_bonus_1_start"; - } - else if (StrEqual(sName, "mod_zone_end_bonus") || StrEqual(sName, "mod_zone_bonus_end")) - { - sName = "mod_zone_bonus_1_end"; - } - - if (StrContains(sName, "start") != -1) - { - zone = Zone_Start; - } - else if (StrContains(sName, "end") != -1) - { - zone = Zone_End; - } - - if (StrContains(sName, "bonus") != -1 || StrContains(sName, "checkpoint") != -1) - { - char sections[8][12]; - ExplodeString(sName, "_", sections, 8, 12, false); - - int iCheckpointIndex = 3; // mod_zone_checkpoint_X - - if (StrContains(sName, "bonus") != -1) - { - iCheckpointIndex = 5; // mod_zone_bonus_X_checkpoint_X - - track = StringToInt(sections[3]); // 0 on failure to parse. 0 is less than Track_Bonus - - if (track < Track_Bonus || track > Track_Bonus_Last) - { - LogError("invalid track in prebuilt map zone (%s) on %s", sName, gS_Map); - return false; - } - } - - if (StrContains(sName, "checkpoint") != -1) - { - zone = Zone_Stage; - zonedata = StringToInt(sections[iCheckpointIndex]); - - if (zonedata <= 0 || zonedata > MAX_STAGES) - { - LogError("invalid stage number in prebuilt map zone (%s) on %s", sName, gS_Map); - return false; - } - } - } - - return true; -} - -bool parse_climb_zone(char sName[32], int& zone, int& track, int& zonedata) -{ - // climb_startzone for the start of the main course. - // climb_endzone for the end of the main course. - // climb_bonusX_startzone for the start of a bonus course where X is the bonus number. - // climb_bonusX_endzone for the end of a bonus course where X is the bonus number. - - if (StrContains(sName, "startzone") != -1) - { - zone = Zone_Start; - } - else if (StrContains(sName, "endzone") != -1) - { - zone = Zone_End; - } - - int bonus = StrContains(sName, "bonus"); - - if (bonus != -1) - { - track = Track_Bonus; - - if ('0' <= sName[bonus+5] <= '9') - { - track = StringToInt(sName[bonus+5]); - - if (track < Track_Bonus || track > Track_Bonus_Last) - { - LogError("invalid track in prebuilt map zone (%s) on %s", sName, gS_Map); - return false; - } - } - } - - return true; -} - -public void Frame_HookTrigger(any data) -{ - int entity = EntRefToEntIndex(data); - - if (entity == INVALID_ENT_REFERENCE || gI_EntityZone[entity] > -1) - { - return; - } - - char sName[32]; - GetEntPropString(entity, Prop_Data, "m_iName", sName, 32); - - int zone = -1; - int zonedata = 0; - int track = Track_Main; - - if (StrContains(sName, "mod_zone_") == 0) - { - if (!parse_mod_zone(sName, zone, track, zonedata)) - { - return; - } - } - else if (StrContains(sName, "climb_") == 0) - { - if (!parse_climb_zone(sName, zone, track, zonedata)) - { - return; - } - } - else - { - return; - } - - if(zone != -1) - { - int iZoneIndex = gI_MapZones; - - // Check for existing prebuilt zone in the cache and reuse slot. - for (int i = 0; i < gI_MapZones; i++) - { - if (gA_ZoneCache[i].bPrebuilt && gA_ZoneCache[i].iZoneType == zone && gA_ZoneCache[i].iZoneTrack == track && gA_ZoneCache[i].iZoneData == zonedata) - { - iZoneIndex = i; - break; - } - } - - gI_EntityZone[entity] = iZoneIndex; - gA_ZoneCache[iZoneIndex].iEntityID = entity; - - SDKHook(entity, SDKHook_StartTouchPost, StartTouchPost); - SDKHook(entity, SDKHook_EndTouchPost, EndTouchPost); - SDKHook(entity, SDKHook_TouchPost, TouchPost); - - if (iZoneIndex != gI_MapZones) - { - return; - } - - float maxs[3], mins[3], origin[3]; - GetEntPropVector(entity, Prop_Send, "m_vecMaxs", maxs); - GetEntPropVector(entity, Prop_Send, "m_vecMins", mins); - GetEntPropVector(entity, Prop_Send, "m_vecOrigin", origin); - - //origin[2] -= (maxs[2] - 2.0); // so you don't get stuck in the ground - origin[2] += 1.0; // so you don't get stuck in the ground - - AddZoneToCache( - zone, - origin[0]+mins[0], origin[1]+mins[1], origin[2]+mins[2], // corner1_xyz - origin[0]+maxs[0], origin[1]+maxs[1], origin[2]+maxs[2], // corner2_xyz - 0.0, 0.0, 0.0, // destination_xyz (Zone_Teleport/Zone_Stage) - track, - -1, // iDatabaseID - 0, // iZoneFlags - zonedata, - true // bPrebuilt - ); + ClearZoneEntity(gI_EntityZone[entity], false); } } @@ -1284,124 +1383,213 @@ public void Shavit_OnChatConfigLoaded() Shavit_GetChatStringsStruct(gS_ChatStrings); } -void ClearZone(int index) +void ClearZoneEntity(int index, bool unhook) { - gV_MapZones[index][0] = ZERO_VECTOR; - gV_MapZones[index][1] = ZERO_VECTOR; - gV_Destinations[index] = ZERO_VECTOR; - gV_ZoneCenter[index] = ZERO_VECTOR; - - gA_ZoneCache[index].bZoneInitialized = false; - gA_ZoneCache[index].bPrebuilt = false; - gA_ZoneCache[index].iZoneType = -1; - gA_ZoneCache[index].iZoneTrack = -1; - gA_ZoneCache[index].iEntityID = -1; - gA_ZoneCache[index].iDatabaseID = -1; - gA_ZoneCache[index].iZoneFlags = 0; - gA_ZoneCache[index].iZoneData = 0; -} - -void KillZoneEntity(int index, bool kill=true) -{ - int entity = gA_ZoneCache[index].iEntityID; - - if(entity > MaxClients) + for (int i = 1; i <= MaxClients; i++) + { + gB_InsideZoneID[i][index] = false; + } + + int entity = gA_ZoneCache[index].iEntity; + + gA_ZoneCache[index].iEntity = -1; + + if (entity > MaxClients) { - gA_ZoneCache[index].iEntityID = -1; gI_EntityZone[entity] = -1; - for(int i = 1; i <= MaxClients; i++) + if (unhook && IsValidEntity(entity)) { - for(int j = 0; j < TRACKS_SIZE; j++) - { - gB_InsideZone[i][gA_ZoneCache[index].iZoneType][j] = false; - } - - gB_InsideZoneID[i][index] = false; - } - - if (!IsValidEntity(entity)) - { - return; - } - - if (kill && !gA_ZoneCache[index].bPrebuilt) - { - AcceptEntityInput(entity, "Kill"); + UnhookZone(gA_ZoneCache[index]); } } } -void KillAllZones() +void UnhookZone(zone_cache_t cache) { - char sTargetname[32]; - int iEntity = -1; + int entity = cache.iEntity; - while ((iEntity = FindEntityByClassname(iEntity, "trigger_multiple")) != -1) + if (cache.iForm == ZoneForm_func_button) { - GetEntPropString(iEntity, Prop_Data, "m_iName", sTargetname, sizeof(sTargetname)); + SDKUnhook(entity, SDKHook_UsePost, UsePost_HookedButton); + } + else if (cache.iForm == ZoneForm_Box) + { + SDKUnhook(entity, SDKHook_StartTouchPost, StartTouchPost); + SDKUnhook(entity, SDKHook_EndTouchPost, EndTouchPost); + SDKUnhook(entity, SDKHook_TouchPost, TouchPost); - bool shavit_created = (StrContains(sTargetname, "shavit_zones_") == 0); - - if (shavit_created - || (StrContains(sTargetname, "mod_zone_") == 0) - || (StrContains(sTargetname, "climb_") == 0) - ) + if (cache.iType == Zone_Speedmod) { - SDKUnhook(iEntity, SDKHook_StartTouchPost, StartTouchPost); - SDKUnhook(iEntity, SDKHook_EndTouchPost, EndTouchPost); - SDKUnhook(iEntity, SDKHook_TouchPost, TouchPost); - - if (shavit_created) - { - AcceptEntityInput(iEntity, "Kill"); - } + SDKUnhook(entity, SDKHook_StartTouch, SameTrack_StartTouch_er); } } } -void UnloadZones2() +bool CreateZoneTrigger(int zone) { - KillAllZones(); + bool speedmod = (gA_ZoneCache[zone].iType == Zone_Speedmod); + char classname[32]; classname = speedmod ? "player_speedmod" : "trigger_multiple"; + int entity = CreateEntityByName(classname); - for (int i = 0; i < MAX_ZONES; i++) + if (entity == -1) { - ClearZone(i); + LogError("\"%s\" creation failed, map %s.", classname, gS_Map); + return false; } - ClearCustomSpawn(-1); + if (!speedmod) + { + DispatchKeyValue(entity, "wait", "0"); // useless??? set to 0.0001 or something? ::Spawn() m_flWait 0 turns into 0.2 which then isn't even used anyway because eventqueuefix sets nextthinktick now??? + DispatchKeyValue(entity, "spawnflags", "4097"); // 1|4096 = allow clients|disallow bots + } + + if (!DispatchSpawn(entity)) + { + AcceptEntityInput(entity, "Kill"); + LogError("\"%s\" spawning failed, map %s.", classname, gS_Map); + return false; + } + + gA_ZoneCache[zone].iEntity = entity; + + ActivateEntity(entity); + SetEntityModel(entity, (gEV_Type == Engine_TF2) ? "models/error.mdl" : "models/props/cs_office/vending_machine.mdl"); + SetEntProp(entity, Prop_Send, "m_fEffects", EF_NODRAW); + SetEntProp(entity, Prop_Send, "m_nSolidType", SOLID_BBOX); + + if (speedmod) + { + SetEntProp(entity, Prop_Send, "m_usSolidFlags", FSOLID_TRIGGER|FSOLID_NOT_SOLID); + } + + if (gA_ZoneCache[zone].iFlags & ZF_Solid) + { + SetEntProp(entity, Prop_Send, "m_usSolidFlags", + GetEntProp(entity, Prop_Send, "m_usSolidFlags") & ~(FSOLID_TRIGGER|FSOLID_NOT_SOLID)); + } + + TeleportEntity(entity, gV_ZoneCenter[zone], NULL_VECTOR, NULL_VECTOR); + + SetZoneMinsMaxs(zone); + HookZoneTrigger(zone, entity); + + char sTargetname[64]; + FormatEx(sTargetname, sizeof(sTargetname), "shavit_zones_%d_%d", gA_ZoneCache[zone].iTrack, gA_ZoneCache[zone].iType); + + if (gA_ZoneCache[zone].iType == Zone_Stage) + { + Format(sTargetname, sizeof(sTargetname), "%s_stage%d", sTargetname, gA_ZoneCache[zone].iData); + } + + DispatchKeyValue(entity, "targetname", sTargetname); + + return true; } -// 0 - all zones -void UnloadZones(int zone) +void HookButton(int zone, int entity) { - if (zone != Zone_CustomSpawn) + Shavit_MarkKZMap(gA_ZoneCache[zone].iTrack); + SDKHook(entity, SDKHook_UsePost, UsePost_HookedButton); +} + +void HookZoneTrigger(int zone, int entity) +{ + SDKHook(entity, SDKHook_StartTouchPost, StartTouchPost); + SDKHook(entity, SDKHook_EndTouchPost, EndTouchPost); + SDKHook(entity, SDKHook_TouchPost, TouchPost); + + if (gA_ZoneCache[zone].iType == Zone_Speedmod) { - for(int i = 0; i < MAX_ZONES; i++) - { - if((zone == 0 || gA_ZoneCache[i].iZoneType == zone) && gA_ZoneCache[i].bZoneInitialized) - { - KillZoneEntity(i); - ClearZone(i); - } - } + SDKHook(entity, SDKHook_StartTouch, SameTrack_StartTouch_er); } - ClearCustomSpawn(-1); + gI_EntityZone[entity] = zone; + gA_ZoneCache[zone].iEntity = entity; +} + +void UnloadZones() +{ + for (int i = 0; i < gI_MapZones; i++) + { + int ent = gA_ZoneCache[i].iEntity; + ClearZoneEntity(i, true); + + if (ent && gA_ZoneCache[i].iForm == ZoneForm_Box) // created by shavit-zones + { + AcceptEntityInput(ent, "Kill"); + } + + zone_cache_t empty_cache; + gA_ZoneCache[i] = empty_cache; + } + + gI_MapZones = 0; + + int empty_tracks[TRACKS_SIZE]; + bool empty_InsideZoneID[MAX_ZONES]; + + for (int i = 1; i <= MaxClients; i++) + { + gI_InsideZone[i] = empty_tracks; + gB_InsideZoneID[i] = empty_InsideZoneID; + } + + gI_HighestStage = empty_tracks; + + for(int i = 0; i < TRACKS_SIZE; i++) + { + gF_CustomSpawn[i] = ZERO_VECTOR; + } +} + +void RecalcInsideZoneAll() +{ + for (int i = 1; i <= MaxClients; i++) + { + RecalcInsideZone(i); + } +} + +void RecalcInsideZone(int client) +{ + int empty_array[TRACKS_SIZE]; + gI_InsideZone[client] = empty_array; + + for (int i = 0; i < gI_MapZones; i++) + { + if (gB_InsideZoneID[client][i]) + { + int track = gA_ZoneCache[i].iTrack; + int type = gA_ZoneCache[i].iType; + gI_InsideZone[client][track] |= (1 << type); + } + } +} + +void RecalcHighestStage() +{ + int empty_tracks[TRACKS_SIZE]; + gI_HighestStage = empty_tracks; + + for (int i = 0; i < gI_MapZones; i++) + { + int type = gA_ZoneCache[i].iType; + if (type == Zone_Stage) continue; + + int track = gA_ZoneCache[i].iTrack; + int stagenum = gA_ZoneCache[i].iData; + + if (stagenum > gI_HighestStage[track]) + gI_HighestStage[track] = stagenum; + } } void RefreshZones() { - gI_MapZones = 0; - - int empty_array[TRACKS_SIZE]; - gI_HighestStage = empty_array; - - ReloadPrebuiltZones(); - char sQuery[512]; FormatEx(sQuery, 512, - "SELECT type, corner1_x, corner1_y, corner1_z, corner2_x, corner2_y, corner2_z, destination_x, destination_y, destination_z, track, %s, flags, data, prebuilt FROM %smapzones WHERE map = '%s';", + "SELECT type, corner1_x, corner1_y, corner1_z, corner2_x, corner2_y, corner2_z, destination_x, destination_y, destination_z, track, %s, flags, data, form, target FROM %smapzones WHERE map = '%s';", (gB_MySQL)? "id":"rowid", gS_MySQLPrefix, gS_Map); gH_SQL.Query2(SQL_RefreshZones_Callback, sQuery, 0, DBPrio_High); @@ -1417,38 +1605,55 @@ public void SQL_RefreshZones_Callback(Database db, DBResultSet results, const ch while(results.FetchRow()) { - if (results.FetchInt(14)) // prebuilt - { - // prebuilt zones already exist in db so we can mark them as already inserted - gB_InsertedPrebuiltZones = true; - continue; - } - int type = results.FetchInt(0); int track = results.FetchInt(10); - if(type == Zone_CustomSpawn) + float destination[3]; + destination[0] = results.FetchFloat(7); + destination[1] = results.FetchFloat(8); + destination[2] = results.FetchFloat(9); + + if (type == Zone_CustomSpawn) { - gF_CustomSpawn[track][0] = results.FetchFloat(7); - gF_CustomSpawn[track][1] = results.FetchFloat(8); - gF_CustomSpawn[track][2] = results.FetchFloat(9); + gF_CustomSpawn[track] = destination; + continue; } - else + + zone_cache_t cache; + cache.iEntity = -1; + cache.iType = type; + cache.fCorner1[0] = results.FetchFloat(1); + cache.fCorner1[1] = results.FetchFloat(2); + cache.fCorner1[2] = results.FetchFloat(3); + cache.fCorner2[0] = results.FetchFloat(4); + cache.fCorner2[1] = results.FetchFloat(5); + cache.fCorner2[2] = results.FetchFloat(6); + cache.fDestination = destination; + cache.iTrack = track; + cache.iDatabaseID = results.FetchInt(11); + cache.iFlags = results.FetchInt(12); + cache.iData = results.FetchInt(13); + cache.iForm = results.FetchInt(14); + results.FetchString(15, cache.sTarget, sizeof(cache.sTarget)); + cache.sSource = "sql"; + + if (cache.iForm == ZoneForm_Box) { - AddZoneToCache( - type, - results.FetchFloat(1), results.FetchFloat(2), results.FetchFloat(3), // corner1_xyz - results.FetchFloat(4), results.FetchFloat(5), results.FetchFloat(6), // corner2_xyz - results.FetchFloat(7), results.FetchFloat(8), results.FetchFloat(9), // destination_xyz (Zone_Teleport/Zone_Stage) - track, - results.FetchInt(11), // iDatabaseID - results.FetchInt(12), // iZoneFlags - results.FetchInt(13), // iZoneData - false // bPrebuilt - ); + // } + else if (cache.iForm == ZoneForm_trigger_multiple) + { + if (!cache.sTarget[0]) + { + // ~~Migrate previous `prebuilt`-column-having zones~~ nevermind... TODO + continue; + } + } + + Shavit_AddZone(cache); } +#if 0 if (!gB_InsertedPrebuiltZones) { gB_InsertedPrebuiltZones = true; @@ -1475,66 +1680,16 @@ public void SQL_RefreshZones_Callback(Database db, DBResultSet results, const ch gH_SQL.Execute(hTransaction); } } - - CreateZoneEntities(false); -} - -public void AddZoneToCache(int type, float corner1_x, float corner1_y, float corner1_z, float corner2_x, float corner2_y, float corner2_z, float destination_x, float destination_y, float destination_z, int track, int id, int flags, int data, bool prebuilt) -{ - gV_MapZones[gI_MapZones][0][0] = gV_MapZones_Visual[gI_MapZones][0][0] = corner1_x; - gV_MapZones[gI_MapZones][0][1] = gV_MapZones_Visual[gI_MapZones][0][1] = corner1_y; - gV_MapZones[gI_MapZones][0][2] = gV_MapZones_Visual[gI_MapZones][0][2] = corner1_z; - gV_MapZones[gI_MapZones][1][0] = gV_MapZones_Visual[gI_MapZones][7][0] = corner2_x; - gV_MapZones[gI_MapZones][1][1] = gV_MapZones_Visual[gI_MapZones][7][1] = corner2_y; - gV_MapZones[gI_MapZones][1][2] = gV_MapZones_Visual[gI_MapZones][7][2] = corner2_z; - - float offset = -(prebuilt ? gCV_PrebuiltVisualOffset.FloatValue : 0.0) + gCV_Offset.FloatValue; - CreateZonePoints(gV_MapZones_Visual[gI_MapZones], offset); - - gV_ZoneCenter[gI_MapZones][0] = (gV_MapZones[gI_MapZones][0][0] + gV_MapZones[gI_MapZones][1][0]) / 2.0; - gV_ZoneCenter[gI_MapZones][1] = (gV_MapZones[gI_MapZones][0][1] + gV_MapZones[gI_MapZones][1][1]) / 2.0; - gV_ZoneCenter[gI_MapZones][2] = (gV_MapZones[gI_MapZones][0][2] + gV_MapZones[gI_MapZones][1][2]) / 2.0; - - if(type == Zone_Teleport || type == Zone_Stage) - { - gV_Destinations[gI_MapZones][0] = destination_x; - gV_Destinations[gI_MapZones][1] = destination_y; - gV_Destinations[gI_MapZones][2] = destination_z; - } - - if (type == Zone_Stage) - { - gI_StageZoneID[track][data] = id; - - if (data > gI_HighestStage[track]) - { - gI_HighestStage[track] = data; - } - } - - gA_ZoneCache[gI_MapZones].bZoneInitialized = true; - gA_ZoneCache[gI_MapZones].bPrebuilt = prebuilt; - gA_ZoneCache[gI_MapZones].iZoneType = type; - gA_ZoneCache[gI_MapZones].iZoneTrack = track; - gA_ZoneCache[gI_MapZones].iDatabaseID = id; - gA_ZoneCache[gI_MapZones].iZoneFlags = flags; - gA_ZoneCache[gI_MapZones].iZoneData = data; - - if (!prebuilt) - { - gA_ZoneCache[gI_MapZones].iEntityID = -1; - } - - gI_MapZones++; +#endif } public void OnClientConnected(int client) { - bool empty_InsideZone[TRACKS_SIZE]; + int empty_InsideZone[TRACKS_SIZE]; for (int i = 0; i < ZONETYPES_SIZE; i++) { - gB_InsideZone[client][i] = empty_InsideZone; + gI_InsideZone[client] = empty_InsideZone; for(int j = 0; j < TRACKS_SIZE; j++) { @@ -1567,7 +1722,7 @@ public void OnClientConnected(int client) public void OnClientAuthorized(int client) { - if (gB_Connected && !IsFakeClient(client)) + if (gH_SQL && !IsFakeClient(client)) { GetStartPosition(client); } @@ -1677,15 +1832,6 @@ public Action Command_SetStart(int client, int args) int track = Shavit_GetClientTrack(client); -#if 0 - if(!InsideZone(client, Zone_Start, track)) - { - Shavit_PrintToChat(client, "%T", "SetStartNotInStartZone", client, gS_ChatStrings.sWarning, gS_ChatStrings.sText, gS_ChatStrings.sVariable2, gS_ChatStrings.sText); - - return Plugin_Handled; - } -#endif - Shavit_PrintToChat(client, "%T", "SetStart", client, gS_ChatStrings.sVariable2, gS_ChatStrings.sText); SetStart(client, track, GetEntPropEnt(client, Prop_Send, "m_hGroundEntity") == -1); @@ -1830,6 +1976,12 @@ public Action Command_AddSpawn(int client, int args) return Plugin_Handled; } + if (!gCV_SQLZones.BoolValue) + { + Shavit_PrintToChat(client, "%T", "ZonesNotSQL", client, gS_ChatStrings.sWarning, gS_ChatStrings.sText); + return Plugin_Handled; + } + return DisplayCustomSpawnMenu(client); } @@ -1880,9 +2032,11 @@ public int MenuHandler_AddCustomSpawn(Menu menu, MenuAction action, int param1, return 0; } - gI_ZoneType[param1] = Zone_CustomSpawn; - gI_ZoneTrack[param1] = iTrack; - GetClientAbsOrigin(param1, gV_Point1[param1]); + zone_cache_t cache; + cache.iType = Zone_CustomSpawn; + cache.iTrack = iTrack; + GetClientAbsOrigin(param1, cache.fDestination); + gA_EditCache[param1] = cache; InsertZone(param1); } @@ -1944,12 +2098,12 @@ public int MenuHandler_DeleteCustomSpawn(Menu menu, MenuAction action, int param return 0; } - gI_ZoneTrack[param1] = iTrack; + gF_CustomSpawn[iTrack] = ZERO_VECTOR; Shavit_LogMessage("%L - deleted custom spawn from map `%s`.", param1, gS_Map); char sQuery[512]; FormatEx(sQuery, sizeof(sQuery), - "DELETE FROM %smapzones WHERE type = '%d' AND map = '%s' AND track = %d;", + "DELETE FROM %smapzones WHERE type = %d AND map = '%s' AND track = %d;", gS_MySQLPrefix, Zone_CustomSpawn, gS_Map, iTrack); gH_SQL.Query2(SQL_DeleteCustom_Spawn_Callback, sQuery, GetClientSerial(param1)); @@ -1978,52 +2132,9 @@ public void SQL_DeleteCustom_Spawn_Callback(Database db, DBResultSet results, co return; } - ClearCustomSpawn(gI_ZoneTrack[client]); Shavit_PrintToChat(client, "%T", "ZoneCustomSpawnDelete", client); } -void ClearCustomSpawn(int track) -{ - if(track != -1) - { - gF_CustomSpawn[track] = ZERO_VECTOR; - return; - } - - for(int i = 0; i < TRACKS_SIZE; i++) - { - gF_CustomSpawn[i] = ZERO_VECTOR; - } -} - -void ReloadPrebuiltZones() -{ - char sTargetname[32]; - int iEntity = INVALID_ENT_REFERENCE; - - while((iEntity = FindEntityByClassname(iEntity, "trigger_multiple")) != INVALID_ENT_REFERENCE) - { - GetEntPropString(iEntity, Prop_Data, "m_iName", sTargetname, 32); - - if(StrContains(sTargetname, "mod_zone_") != -1) - { - Frame_HookTrigger(EntIndexToEntRef(iEntity)); - } - } - - iEntity = INVALID_ENT_REFERENCE; - - while ((iEntity = FindEntityByClassname(iEntity, "func_button")) != INVALID_ENT_REFERENCE) - { - GetEntPropString(iEntity, Prop_Data, "m_iName", sTargetname, sizeof(sTargetname)); - - if (StrContains(sTargetname, "climb_") != -1) - { - Frame_HookButton(EntIndexToEntRef(iEntity)); - } - } -} - public Action Command_TpToZone(int client, int args) { if (!IsValidClient(client)) @@ -2041,11 +2152,34 @@ public Action Command_ZoneEdit(int client, int args) return Plugin_Handled; } + if (!gCV_SQLZones.BoolValue) + { + Shavit_PrintToChat(client, "%T", "ZonesNotSQL", client, gS_ChatStrings.sWarning, gS_ChatStrings.sText); + return Plugin_Handled; + } + Reset(client); return OpenEditMenu(client); } +public Action Command_HookZone(int client, int args) +{ + if (!IsValidClient(client)) + { + return Plugin_Handled; + } + + if (!gCV_SQLZones.BoolValue) + { + Shavit_PrintToChat(client, "%T", "ZonesNotSQL", client, gS_ChatStrings.sWarning, gS_ChatStrings.sText); + return Plugin_Handled; + } + + OpenHookMenu_Form(client); + return Plugin_Handled; +} + public Action Command_ReloadZoneSettings(int client, int args) { LoadZoneSettings(); @@ -2181,12 +2315,13 @@ public Action Command_Stages(int client, int args) { for(int i = 0; i < gI_MapZones; i++) { - if(gA_ZoneCache[i].bZoneInitialized && gA_ZoneCache[i].iZoneType == Zone_Stage && gA_ZoneCache[i].iZoneData == iStage) + if (gA_ZoneCache[i].iType == Zone_Stage && gA_ZoneCache[i].iData == iStage) { Shavit_StopTimer(client); - if(!EmptyVector(gV_Destinations[i])) + + if (!EmptyVector(gA_ZoneCache[i].fDestination)) { - TeleportEntity(client, gV_Destinations[i], NULL_VECTOR, view_as({0.0, 0.0, 0.0})); + TeleportEntity(client, gA_ZoneCache[i].fDestination, NULL_VECTOR, view_as({0.0, 0.0, 0.0})); } else { @@ -2204,12 +2339,12 @@ public Action Command_Stages(int client, int args) for(int i = 0; i < gI_MapZones; i++) { - if(gA_ZoneCache[i].bZoneInitialized && gA_ZoneCache[i].iZoneType == Zone_Stage) + if (gA_ZoneCache[i].iType == Zone_Stage) { char sTrack[32]; - GetTrackName(client, gA_ZoneCache[i].iZoneTrack, sTrack, 32); + GetTrackName(client, gA_ZoneCache[i].iTrack, sTrack, 32); - FormatEx(sDisplay, 64, "#%d - %T (%s)", (i + 1), "ZoneSetStage", client, gA_ZoneCache[i].iZoneData, sTrack); + FormatEx(sDisplay, 64, "#%d - %T (%s)", (i + 1), "ZoneSetStage", client, gA_ZoneCache[i].iData, sTrack); char sInfo[8]; IntToString(i, sInfo, 8); @@ -2235,9 +2370,9 @@ public int MenuHandler_SelectStage(Menu menu, MenuAction action, int param1, int Shavit_StopTimer(param1); - if(!EmptyVector(gV_Destinations[iIndex])) + if (!EmptyVector(gA_ZoneCache[iIndex].fDestination)) { - TeleportEntity(param1, gV_Destinations[iIndex], NULL_VECTOR, view_as({0.0, 0.0, 0.0})); + TeleportEntity(param1, gA_ZoneCache[iIndex].fDestination, NULL_VECTOR, view_as({0.0, 0.0, 0.0})); } else { @@ -2254,6 +2389,12 @@ public int MenuHandler_SelectStage(Menu menu, MenuAction action, int param1, int public Action Command_Zones(int client, int args) { + if (!gH_SQL) + { + Shavit_PrintToChat(client, "Database not loaded. Check your error logs."); + return Plugin_Handled; + } + if(!IsValidClient(client)) { return Plugin_Handled; @@ -2262,7 +2403,12 @@ public Action Command_Zones(int client, int args) if(!IsPlayerAlive(client)) { Shavit_PrintToChat(client, "%T", "ZonesCommand", client, gS_ChatStrings.sWarning, gS_ChatStrings.sText); + return Plugin_Handled; + } + if (!gCV_SQLZones.BoolValue) + { + Shavit_PrintToChat(client, "%T", "ZonesNotSQL", client, gS_ChatStrings.sWarning, gS_ChatStrings.sText); return Plugin_Handled; } @@ -2294,10 +2440,10 @@ public int MenuHandler_SelectZoneTrack(Menu menu, MenuAction action, int param1, { char sInfo[8]; menu.GetItem(param2, sInfo, 8); - gI_ZoneTrack[param1] = StringToInt(sInfo); + gA_EditCache[param1].iTrack = StringToInt(sInfo); char sTrack[16]; - GetTrackName(param1, gI_ZoneTrack[param1], sTrack, 16); + GetTrackName(param1, gA_EditCache[param1].iTrack, sTrack, 16); Menu submenu = new Menu(MenuHandler_SelectZoneType); submenu.SetTitle("%T\n ", "ZoneMenuTitle", param1, sTrack); @@ -2334,51 +2480,53 @@ Action OpenTpToZoneMenu(int client, int pagepos=0) menu.SetTitle("%T\n ", "TpToZone", client); int newPageInterval = (gEV_Type == Engine_CSGO) ? 6 : 7; - char sDisplay[64]; - FormatEx(sDisplay, 64, "%T", "ZoneEditRefresh", client); + char sDisplay[128]; + FormatEx(sDisplay, sizeof(sDisplay), "%T", "ZoneEditRefresh", client); menu.AddItem("-2", sDisplay); for (int i = 0; i < gI_MapZones; i++) { - if (!gA_ZoneCache[i].bZoneInitialized) - { - continue; - } - if ((menu.ItemCount % newPageInterval) == 0) { - FormatEx(sDisplay, 64, "%T", "ZoneEditRefresh", client); + FormatEx(sDisplay, sizeof(sDisplay), "%T", "ZoneEditRefresh", client); menu.AddItem("-2", sDisplay); } char sInfo[8]; IntToString(i, sInfo, 8); - char sPrebuilt[16]; - sPrebuilt = gA_ZoneCache[i].bPrebuilt ? " (prebuilt)" : ""; + char sTarget[64]; + + switch (gA_ZoneCache[i].iForm) + { + case ZoneForm_func_button, ZoneForm_trigger_multiple, ZoneForm_trigger_teleport: + { + FormatEx(sTarget, sizeof(sTarget), " (%s)", gA_ZoneCache[i].sTarget); + } + } char sTrack[32]; - GetTrackName(client, gA_ZoneCache[i].iZoneTrack, sTrack, 32); + GetTrackName(client, gA_ZoneCache[i].iTrack, sTrack, 32); char sZoneName[32]; - GetZoneName(client, gA_ZoneCache[i].iZoneType, sZoneName, sizeof(sZoneName)); + GetZoneName(client, gA_ZoneCache[i].iType, sZoneName, sizeof(sZoneName)); - if (gA_ZoneCache[i].iZoneType == Zone_CustomSpeedLimit || gA_ZoneCache[i].iZoneType == Zone_Stage || gA_ZoneCache[i].iZoneType == Zone_Airaccelerate) + if (gA_ZoneCache[i].iType == Zone_CustomSpeedLimit || gA_ZoneCache[i].iType == Zone_Stage || gA_ZoneCache[i].iType == Zone_Airaccelerate) { - FormatEx(sDisplay, 64, "#%d - %s %d (%s)%s", (i + 1), sZoneName, gA_ZoneCache[i].iZoneData, sTrack, sPrebuilt); + FormatEx(sDisplay, sizeof(sDisplay), "#%d - %s %d (%s)%s", (i + 1), sZoneName, gA_ZoneCache[i].iData, sTrack, sTarget); } - else if (gA_ZoneCache[i].iZoneType == Zone_Gravity || gA_ZoneCache[i].iZoneType == Zone_Speedmod) + else if (gA_ZoneCache[i].iType == Zone_Gravity || gA_ZoneCache[i].iType == Zone_Speedmod) { - FormatEx(sDisplay, 64, "#%d - %s %.2f (%s)%s", (i + 1), sZoneName, gA_ZoneCache[i].iZoneData, sTrack, sPrebuilt); + FormatEx(sDisplay, 64, "#%d - %s %.2f (%s)", (i + 1), sZoneName, gA_ZoneCache[i].iData, sTrack); } else { - FormatEx(sDisplay, 64, "#%d - %s (%s)%s", (i + 1), sZoneName, sTrack, sPrebuilt); + FormatEx(sDisplay, sizeof(sDisplay), "#%d - %s (%s)%s", (i + 1), sZoneName, sTrack, sTarget); } if (gB_InsideZoneID[client][i]) { - Format(sDisplay, 64, "%s %T", sDisplay, "ZoneInside", client); + Format(sDisplay, sizeof(sDisplay), "%s %T", sDisplay, "ZoneInside", client); } menu.AddItem(sInfo, sDisplay, ITEMDRAW_DEFAULT); @@ -2415,7 +2563,7 @@ public int MenuHandler_TpToEdit(Menu menu, MenuAction action, int param1, int pa float fCenter[3]; fCenter[0] = gV_ZoneCenter[id][0]; fCenter[1] = gV_ZoneCenter[id][1]; - fCenter[2] = gV_MapZones[id][0][2]; + fCenter[2] = gA_ZoneCache[id].fCorner1[2]; TeleportEntity(param1, fCenter, NULL_VECTOR, view_as({0.0, 0.0, 0.0})); } @@ -2431,61 +2579,454 @@ public int MenuHandler_TpToEdit(Menu menu, MenuAction action, int param1, int pa return 0; } +public int MenuHandler_HookZone_Editor(Menu menu, MenuAction action, int param1, int param2) +{ + if (action == MenuAction_Select) + { + char info[32]; + menu.GetItem(param2, info, sizeof(info)); + + if (StrEqual(info, "tpto")) + { + Shavit_StopTimer(param1); + float center[3]; + center[0] = (gA_EditCache[param1].fCorner1[0] + gA_EditCache[param1].fCorner2[0]) * 0.5; + center[1] = (gA_EditCache[param1].fCorner1[1] + gA_EditCache[param1].fCorner2[1]) * 0.5; + center[2] = gA_EditCache[param1].fCorner1[2] + 1.0; + TeleportEntity(param1, center, NULL_VECTOR, ZERO_VECTOR); + } + else if (StrEqual(info, "track")) + { + if ((gA_EditCache[param1].iTrack += 1) > Track_Bonus_Last) + gA_EditCache[param1].iTrack = 0; + } + else if (StrEqual(info, "ztype")) + { + static int allowed_types[] = { + // ZoneForm_Box (unused) + 0 + // ZoneForm_trigger_multiple + , (1 << Zone_Start) + | (1 << Zone_End) + | (1 << Zone_Respawn) + | (1 << Zone_Stop) + | (1 << Zone_Slay) + | (1 << Zone_Freestyle) + | (1 << Zone_CustomSpeedLimit) + | (1 << Zone_Teleport) + | (1 << Zone_Easybhop) + | (1 << Zone_Slide) + | (1 << Zone_Airaccelerate) + | (1 << Zone_Stage) + | (1 << Zone_NoTimerGravity) + | (1 << Zone_Gravity) + // ZoneForm_trigger_teleport + , (1 << Zone_End) + | (1 << Zone_Respawn) + | (1 << Zone_Stop) + | (1 << Zone_Slay) + | (1 << Zone_Freestyle) + | (1 << Zone_CustomSpeedLimit) + | (1 << Zone_Stage) + | (1 << Zone_NoTimerGravity) + // ZoneForm_func_button + , (1 << Zone_Start) + | (1 << Zone_End) + | (1 << Zone_Stop) + | (1 << Zone_Slay) + | (1 << Zone_Stage) + }; + + int form = gA_EditCache[param1].iForm; + + for (int i = 0; i < 100; i++) // no infinite loops = good :) + { + if (++gA_EditCache[param1].iType >= ZONETYPES_SIZE) + gA_EditCache[param1].iType = 0; + if (allowed_types[form] & (1 << gA_EditCache[param1].iType)) + break; + } + } + else if (StrEqual(info, "htype")) + { + if (gA_EditCache[param1].iFlags == -1) + gA_EditCache[param1].iFlags = 0; + else + gA_EditCache[param1].iFlags ^= ZF_Hammerid; + } + else if (StrEqual(info, "hook")) + { + if (gA_EditCache[param1].iFlags & ZF_Hammerid) + IntToString(GetEntProp(gA_EditCache[param1].iEntity, Prop_Data, "m_iHammerID"), gA_EditCache[param1].sTarget, sizeof(gA_EditCache[].sTarget)); + else + GetEntPropString(gA_EditCache[param1].iEntity, Prop_Data, gA_EditCache[param1].iForm == ZoneForm_trigger_teleport ? "m_target" : "m_iName", gA_EditCache[param1].sTarget, sizeof(gA_EditCache[].sTarget)); + + CreateEditMenu(param1); + return 0; + } + + OpenHookMenu_Editor(param1); + } + else if (action == MenuAction_Cancel && param2 == MenuCancel_ExitBack) + { + OpenHookMenu_List(param1, gA_EditCache[param1].iForm, gI_HookListPos[param1]); + } + else if (action == MenuAction_End) + { + delete menu; + } + + return 0; +} + +void OpenHookMenu_Editor(int client) +{ + int ent = gA_EditCache[client].iEntity; + int form = gA_EditCache[client].iForm; + int track = gA_EditCache[client].iTrack; + int hooktype = gA_EditCache[client].iFlags; + int zonetype = gA_EditCache[client].iType; + + char classname[32], targetname[64], hammerid[16]; + GetEntityClassname(ent, classname, sizeof(classname)); + GetEntPropString(ent, Prop_Data, form == ZoneForm_trigger_teleport ? "m_target" : "m_iName", targetname, sizeof(targetname)); + IntToString(GetEntProp(ent, Prop_Data, "m_iHammerID"), hammerid, sizeof(hammerid)); + + Menu menu = new Menu(MenuHandler_HookZone_Editor); + menu.SetTitle("%s\nhammerid = %s\n%s = '%s'\n ", classname, hammerid, form == ZoneForm_trigger_teleport ? "target" : "targetname", targetname); + + char display[128], buf[32]; + + FormatEx(display, sizeof(display), "%T\n ", "ZoneHook_Tpto", client); + menu.AddItem("tpto", display);//, form == ZoneForm_trigger_teleport ? ITEMDRAW_DISABLED : ITEMDRAW_DEFAULT); + + GetTrackName(client, track, buf, sizeof(buf), true); + FormatEx(display, sizeof(display), "%T", "ZoneEditTrack", client, buf); + menu.AddItem("track", display); + GetZoneName(client, zonetype, buf, sizeof(buf)); + FormatEx(display, sizeof(display), "%T", "ZoneHook_Zonetype", client, buf); + menu.AddItem("ztype", display); + FormatEx(display, sizeof(display), "%T\n ", "ZoneHook_Hooktype", client, + (hooktype == -1) ? "UNKNOWN" : + (hooktype & ZF_Hammerid) ? "hammerid" : (form == ZoneForm_trigger_teleport ? "target" : "targetname"), + (hooktype == -1) ? "": + (hooktype & ZF_Hammerid) ? hammerid : targetname + ); + menu.AddItem("htype", display); + + FormatEx(display, sizeof(display), "%T", "ZoneHook_Confirm", client); + menu.AddItem( + "hook", display, + (zonetype != -1 && hooktype != -1 && track != -1) ? ITEMDRAW_DEFAULT : ITEMDRAW_DISABLED + ); + + menu.ExitBackButton = true; + menu.Display(client, MENU_TIME_FOREVER); +} + +void HookZone_SetupEditor(int client, int ent) +{ + float origin[3]; + GetEntPropVector(ent, Prop_Send, "m_vecOrigin", origin); + GetEntPropVector(ent, Prop_Send, "m_vecMins", gA_EditCache[client].fCorner1); + GetEntPropVector(ent, Prop_Send, "m_vecMaxs", gA_EditCache[client].fCorner2); + origin[2] += 1.0; // so you don't get stuck in the ground + AddVectors(origin, gA_EditCache[client].fCorner1, gA_EditCache[client].fCorner1); + AddVectors(origin, gA_EditCache[client].fCorner2, gA_EditCache[client].fCorner2); + + gI_MapStep[client] = 3; + gA_EditCache[client].iEntity = ent; + gA_EditCache[client].iType = -1; + gA_EditCache[client].iTrack = -1; + gA_EditCache[client].iFlags = -1; + OpenHookMenu_Editor(client); + + //if (gA_EditCache[client].iForm == ZoneForm_trigger_multiple) + gH_StupidTimer[client] = CreateTimer(0.1, Timer_Draw, GetClientSerial(client), TIMER_REPEAT); +} + +public int MenuHandle_HookZone_List(Menu menu, MenuAction action, int param1, int param2) +{ + if (action == MenuAction_Select) + { + char info[20]; + menu.GetItem(param2, info, sizeof(info)); + int ent = EntRefToEntIndex(StringToInt(info)); + + if (ent <= MaxClients) + { + Shavit_PrintToChat(param1, "Invalid entity index???"); + OpenHookMenu_List(param1, gA_EditCache[param1].iForm, 0); + return 0; + } + + gI_HookListPos[param1] = GetMenuSelectionPosition(); + HookZone_SetupEditor(param1, ent); + } + else if (action == MenuAction_Cancel && param2 == MenuCancel_ExitBack) + { + OpenHookMenu_Form(param1); + } + else if (action == MenuAction_End) + { + delete menu; + } + + return 0; +} + +enum struct ent_list_thing +{ + float dist; + int ent; +} + +void OpenHookMenu_List(int client, int form, int pos = 0) +{ + Reset(client); + gA_EditCache[client].iForm = form; + gA_EditCache[client].sSource = "sql"; + + float player_origin[3]; + GetClientAbsOrigin(client, player_origin); + + char classname[32]; classname = + (form == ZoneForm_trigger_multiple) ? "trigger_multiple" : + ((form == ZoneForm_trigger_teleport) ? "trigger_teleport" : "func_button"); + + char targetname[64], info[20], display[128]; + int ent = -1; + + ArrayList list = new ArrayList(sizeof(ent_list_thing)); + + while ((ent = FindEntityByClassname(ent, classname)) != -1) + { + if (gI_EntityZone[ent] > -1) continue; + + float ent_origin[3]; + GetEntPropVector(ent, Prop_Send, "m_vecOrigin", ent_origin); + + ent_list_thing thing; + thing.dist = GetVectorDistance(player_origin, ent_origin); + thing.ent = ent; + list.PushArray(thing); + } + + if (!list.Length) + { + Shavit_PrintToChat(client, "No unhooked entities found"); + delete list; + OpenHookMenu_Form(client); + return; + } + + list.Sort(Sort_Ascending, Sort_Float); + + Menu menu = new Menu(MenuHandle_HookZone_List); + menu.SetTitle("%T\n ", "HookZone2", client, classname); + + for (int i = 0; i < list.Length; i++) + { + ent_list_thing thing; + list.GetArray(i, thing); + + GetEntPropString(thing.ent, Prop_Data, form == ZoneForm_trigger_teleport ? "m_target" : "m_iName", targetname, sizeof(targetname)); + + if (form == ZoneForm_trigger_multiple && StrContains(targetname, "shavit_zones_") == 0) + { + continue; + } + + FormatEx(display, sizeof(display), "%s | %d dist=%.1fm", targetname, GetEntProp(thing.ent, Prop_Data, "m_iHammerID"), thing.dist*0.01905); + FormatEx(info, sizeof(info), "%d", EntIndexToEntRef(thing.ent)); + menu.AddItem(info, display); + } + + delete list; + + if (!menu.ItemCount) + { + Shavit_PrintToChat(client, "No unhooked entities found"); + delete menu; + OpenHookMenu_Form(client); + return; + } + + menu.ExitBackButton = true; + menu.DisplayAt(client, pos, MENU_TIME_FOREVER); +} + + +bool TeleportFilter(int entity) +{ + char classname[20]; + GetEntityClassname(entity, classname, sizeof(classname)); + + if (StrEqual(classname, "trigger_teleport") || StrEqual(classname, "trigger_multiple") || StrEqual(classname, "func_button")) + { + //TR_ClipCurrentRayToEntity(MASK_ALL, entity); + gI_CurrentTraceEntity = entity; + return false; + } + + return true; +} + +public int MenuHandle_HookZone_Form(Menu menu, MenuAction action, int param1, int param2) +{ + if (action == MenuAction_Select) + { + char info[20]; + menu.GetItem(param2, info, sizeof(info)); + int form = StringToInt(info); + + if (form != -1) + { + OpenHookMenu_List(param1, form, 0); + return 0; + } + + // entity under crosshair + + float origin[3], endpos[3]; + GetClientEyePosition(param1, origin); + GetClientEyeAngles(param1, endpos); + GetAngleVectors(endpos, endpos, NULL_VECTOR, NULL_VECTOR); + ScaleVector(endpos, 30000.0); + AddVectors(origin, endpos, endpos); + + gI_CurrentTraceEntity = 0; // had some troubles in mpbhops_but_working with TR_EnumerateEntitiesHull. So I did this. And copied it to bhoptimer + TR_EnumerateEntitiesHull(origin, endpos, + view_as({-8.0, -8.0, 0.0}), view_as({8.0, 8.0, 0.0}), + PARTITION_TRIGGER_EDICTS, TeleportFilter, 0); + int ent = gI_CurrentTraceEntity; + + if (ent <= MaxClients || ent >= 2048) + { + Shavit_PrintToChat(param1, "Couldn't find entity under crosshair"); + OpenHookMenu_Form(param1); + return 0; + } + + char classname[32]; + GetEntityClassname(ent, classname, sizeof(classname)); + + if (StrEqual(classname, "func_button")) + { + form = ZoneForm_func_button; + } + else if (StrEqual(classname, "trigger_multiple")) + { + form = ZoneForm_trigger_multiple; + } + else if (StrEqual(classname, "trigger_teleport")) + { + form = ZoneForm_trigger_teleport; + } + else + { + Shavit_PrintToChat(param1, "Entity class %s (%d) not supported", classname, ent); + OpenHookMenu_Form(param1); + return 0; + } + + if (gI_EntityZone[ent] > -1) + { + Shavit_PrintToChat(param1, "Entity %s (%d) is already hooked", classname, ent); + OpenHookMenu_Form(param1); + return 0; + } + + Reset(param1); + gI_HookListPos[param1] = 0; + gA_EditCache[param1].iForm = form; + gA_EditCache[param1].sSource = "sql"; + HookZone_SetupEditor(param1, ent); + } + else if (action == MenuAction_End) + { + delete menu; + } + + return 0; +} + +void OpenHookMenu_Form(int client) +{ + Reset(client); + + Menu menu = new Menu(MenuHandle_HookZone_Form); + menu.SetTitle("%T\n ", "HookZone", client); + + char display[128]; + + FormatEx(display, sizeof(display), "%T\n ", "ZoneHook_Crosshair", client); + menu.AddItem("-1", display); + // hardcoded ZoneForm_ values + menu.AddItem("3", "func_button"); + menu.AddItem("1", "trigger_multiple"); + menu.AddItem("2", "trigger_teleport"); + + menu.ExitButton = true; // TODO Back button to admin menu list... + menu.Display(client, MENU_TIME_FOREVER); +} + Action OpenEditMenu(int client, int pos = 0) { Menu menu = new Menu(MenuHandler_ZoneEdit); menu.SetTitle("%T\n ", "ZoneEditTitle", client); - int newPageInterval = (gEV_Type == Engine_CSGO) ? 6 : 7; - char sDisplay[64]; - FormatEx(sDisplay, 64, "%T", "ZoneEditRefresh", client); + char sDisplay[128]; + FormatEx(sDisplay, sizeof(sDisplay), "%T", "ZoneEditRefresh", client); menu.AddItem("-2", sDisplay); for(int i = 0; i < gI_MapZones; i++) { - if(!gA_ZoneCache[i].bZoneInitialized) - { - continue; - } - if ((menu.ItemCount % newPageInterval) == 0) { - FormatEx(sDisplay, 64, "%T", "ZoneEditRefresh", client); + FormatEx(sDisplay, sizeof(sDisplay), "%T", "ZoneEditRefresh", client); menu.AddItem("-2", sDisplay); } char sInfo[8]; IntToString(i, sInfo, 8); - char sPrebuilt[16]; - sPrebuilt = gA_ZoneCache[i].bPrebuilt ? " (prebuilt)" : ""; + char sTarget[64]; + + switch (gA_ZoneCache[i].iForm) + { + case ZoneForm_func_button, ZoneForm_trigger_multiple, ZoneForm_trigger_teleport: + { + FormatEx(sTarget, sizeof(sTarget), " (%s)", gA_ZoneCache[i].sTarget); + } + } char sTrack[32]; - GetTrackName(client, gA_ZoneCache[i].iZoneTrack, sTrack, 32); + GetTrackName(client, gA_ZoneCache[i].iTrack, sTrack, 32); char sZoneName[32]; - GetZoneName(client, gA_ZoneCache[i].iZoneType, sZoneName, sizeof(sZoneName)); + GetZoneName(client, gA_ZoneCache[i].iType, sZoneName, sizeof(sZoneName)); - if(gA_ZoneCache[i].iZoneType == Zone_CustomSpeedLimit || gA_ZoneCache[i].iZoneType == Zone_Stage || gA_ZoneCache[i].iZoneType == Zone_Airaccelerate) + if (gA_ZoneCache[i].iType == Zone_CustomSpeedLimit || gA_ZoneCache[i].iType == Zone_Stage || gA_ZoneCache[i].iType == Zone_Airaccelerate) { - FormatEx(sDisplay, 64, "#%d - %s %d (%s)%s", (i + 1), sZoneName, gA_ZoneCache[i].iZoneData, sTrack, sPrebuilt); + FormatEx(sDisplay, sizeof(sDisplay), "#%d - %s %d (%s)%s", (i + 1), sZoneName, gA_ZoneCache[i].iData, sTrack, sTarget); } - else if (gA_ZoneCache[i].iZoneType == Zone_Gravity || gA_ZoneCache[i].iZoneType == Zone_Speedmod) + else if (gA_ZoneCache[i].iType == Zone_Gravity || gA_ZoneCache[i].iType == Zone_Speedmod) { - FormatEx(sDisplay, 64, "#%d - %s %.2f (%s)%s", (i + 1), sZoneName, gA_ZoneCache[i].iZoneData, sTrack, sPrebuilt); + FormatEx(sDisplay, 64, "#%d - %s %.2f (%s)", (i + 1), sZoneName, gA_ZoneCache[i].iData, sTrack); } else { - FormatEx(sDisplay, 64, "#%d - %s (%s)%s", (i + 1), sZoneName, sTrack, sPrebuilt); + FormatEx(sDisplay, sizeof(sDisplay), "#%d - %s (%s)%s", (i + 1), sZoneName, sTrack, sTarget); } if(gB_InsideZoneID[client][i]) { - Format(sDisplay, 64, "%s %T", sDisplay, "ZoneInside", client); + Format(sDisplay, sizeof(sDisplay), "%s %T", sDisplay, "ZoneInside", client); } - menu.AddItem(sInfo, sDisplay, gA_ZoneCache[i].bPrebuilt ? ITEMDRAW_DISABLED : ITEMDRAW_DEFAULT); + menu.AddItem(sInfo, sDisplay, StrEqual(gA_ZoneCache[i].sSource, "sql") ? ITEMDRAW_DEFAULT : ITEMDRAW_DISABLED); } menu.ExitButton = true; @@ -2519,18 +3060,9 @@ public int MenuHandler_ZoneEdit(Menu menu, MenuAction action, int param1, int pa { // a hack to place the player in the last step of zone editing gI_MapStep[param1] = 3; - gV_Point1[param1] = gV_MapZones[id][0]; - gV_Point2[param1] = gV_MapZones[id][1]; - gI_ZoneType[param1] = gA_ZoneCache[id].iZoneType; - gI_ZoneTrack[param1] = gA_ZoneCache[id].iZoneTrack; - gV_Teleport[param1] = gV_Destinations[id]; - gI_ZoneDatabaseID[param1] = gA_ZoneCache[id].iDatabaseID; - gI_ZoneFlags[param1] = gA_ZoneCache[id].iZoneFlags; - gI_ZoneData[param1] = gA_ZoneCache[id].iZoneData; + gA_EditCache[param1] = gA_ZoneCache[id]; gI_ZoneID[param1] = id; - - // to stop the original zone from drawing - gA_ZoneCache[id].bZoneInitialized = false; + gI_LastMenuPos[param1] = GetMenuSelectionPosition(); // draw the zone edit CreateTimer(0.1, Timer_Draw, GetClientSerial(param1), TIMER_REPEAT); @@ -2758,11 +3290,23 @@ public int MenuHandler_SubCustomZones(Menu menu, MenuAction action, int client, public Action Command_DeleteZone(int client, int args) { + if (!gH_SQL) + { + Shavit_PrintToChat(client, "Database not loaded. Check your error logs."); + return Plugin_Handled; + } + if(!IsValidClient(client)) { return Plugin_Handled; } + if (!gCV_SQLZones.BoolValue) + { + Shavit_PrintToChat(client, "%T", "ZonesNotSQL", client, gS_ChatStrings.sWarning, gS_ChatStrings.sText); + return Plugin_Handled; + } + return OpenDeleteMenu(client); } @@ -2772,40 +3316,47 @@ Action OpenDeleteMenu(int client, int pos = 0) menu.SetTitle("%T\n ", "ZoneMenuDeleteTitle", client); int newPageInterval = (gEV_Type == Engine_CSGO) ? 6 : 7; - char sDisplay[64]; - FormatEx(sDisplay, 64, "%T", "ZoneEditRefresh", client); + char sDisplay[128]; + FormatEx(sDisplay, sizeof(sDisplay), "%T", "ZoneEditRefresh", client); menu.AddItem("-2", sDisplay); for(int i = 0; i < gI_MapZones; i++) { - if (gA_ZoneCache[i].bZoneInitialized) + if (true)//(gA_ZoneCache[i].bInitialized) { if ((menu.ItemCount % newPageInterval) == 0) { - FormatEx(sDisplay, 64, "%T", "ZoneEditRefresh", client); + FormatEx(sDisplay, sizeof(sDisplay), "%T", "ZoneEditRefresh", client); menu.AddItem("-2", sDisplay); } - char sPrebuilt[16]; - sPrebuilt = gA_ZoneCache[i].bPrebuilt ? " (prebuilt)" : ""; + char sTarget[64]; + + switch (gA_ZoneCache[i].iForm) + { + case ZoneForm_func_button, ZoneForm_trigger_multiple, ZoneForm_trigger_teleport: + { + FormatEx(sTarget, sizeof(sTarget), " (%s)", gA_ZoneCache[i].sTarget); + } + } char sTrack[32]; - GetTrackName(client, gA_ZoneCache[i].iZoneTrack, sTrack, 32); + GetTrackName(client, gA_ZoneCache[i].iTrack, sTrack, 32); char sZoneName[32]; - GetZoneName(client, gA_ZoneCache[i].iZoneType, sZoneName, sizeof(sZoneName)); + GetZoneName(client, gA_ZoneCache[i].iType, sZoneName, sizeof(sZoneName)); - if(gA_ZoneCache[i].iZoneType == Zone_CustomSpeedLimit || gA_ZoneCache[i].iZoneType == Zone_Stage || gA_ZoneCache[i].iZoneType == Zone_Airaccelerate) + if(gA_ZoneCache[i].iType == Zone_CustomSpeedLimit || gA_ZoneCache[i].iType == Zone_Stage || gA_ZoneCache[i].iType == Zone_Airaccelerate) { - FormatEx(sDisplay, 64, "#%d - %s %d (%s)%s", (i + 1), sZoneName, gA_ZoneCache[i].iZoneData, sTrack, sPrebuilt); + FormatEx(sDisplay, sizeof(sDisplay), "#%d - %s %d (%s)%s", (i + 1), sZoneName, gA_ZoneCache[i].iData, sTrack, sTarget); } - else if (gA_ZoneCache[i].iZoneType == Zone_Gravity || gA_ZoneCache[i].iZoneType == Zone_Speedmod) + else if (gA_ZoneCache[i].iType == Zone_Gravity || gA_ZoneCache[i].iType == Zone_Speedmod) { - FormatEx(sDisplay, 64, "#%d - %s %.2f (%s)%s", (i + 1), sZoneName, gA_ZoneCache[i].iZoneData, sTrack, sPrebuilt); + FormatEx(sDisplay, 64, "#%d - %s %.2f (%s)", (i + 1), sZoneName, gA_ZoneCache[i].iData, sTrack); } else { - FormatEx(sDisplay, 64, "#%d - %s (%s)%s", (i + 1), sZoneName, sTrack, sPrebuilt); + FormatEx(sDisplay, sizeof(sDisplay), "#%d - %s (%s)%s", (i + 1), sZoneName, sTrack, sTarget); } char sInfo[8]; @@ -2813,10 +3364,10 @@ Action OpenDeleteMenu(int client, int pos = 0) if(gB_InsideZoneID[client][i]) { - Format(sDisplay, 64, "%s %T", sDisplay, "ZoneInside", client); + Format(sDisplay, sizeof(sDisplay), "%s %T", sDisplay, "ZoneInside", client); } - menu.AddItem(sInfo, sDisplay, gA_ZoneCache[i].bPrebuilt ? ITEMDRAW_DISABLED : ITEMDRAW_DEFAULT); + menu.AddItem(sInfo, sDisplay, StrEqual(gA_ZoneCache[i].sSource, "sql") ? ITEMDRAW_DEFAULT : ITEMDRAW_DISABLED); } } @@ -2850,7 +3401,7 @@ public int MenuHandler_DeleteZone(Menu menu, MenuAction action, int param1, int default: { char sZoneName[32]; - GetZoneName(LANG_SERVER, gA_ZoneCache[id].iZoneType, sZoneName, sizeof(sZoneName)); + GetZoneName(LANG_SERVER, gA_ZoneCache[id].iType, sZoneName, sizeof(sZoneName)); Shavit_LogMessage("%L - deleted %s (id %d) from map `%s`.", param1, sZoneName, gA_ZoneCache[id].iDatabaseID, gS_Map); @@ -2859,9 +3410,11 @@ public int MenuHandler_DeleteZone(Menu menu, MenuAction action, int param1, int DataPack hDatapack = new DataPack(); hDatapack.WriteCell(GetClientSerial(param1)); - hDatapack.WriteCell(gA_ZoneCache[id].iZoneType); + hDatapack.WriteCell(gA_ZoneCache[id].iType); gH_SQL.Query2(SQL_DeleteZone_Callback, sQuery, hDatapack); + + Shavit_RemoveZone(id); } } } @@ -2888,9 +3441,6 @@ public void SQL_DeleteZone_Callback(Database db, DBResultSet results, const char return; } - UnloadZones(type); - RefreshZones(); - if(client == 0) { return; @@ -2904,6 +3454,12 @@ public void SQL_DeleteZone_Callback(Database db, DBResultSet results, const char public Action Command_DeleteAllZones(int client, int args) { + if (!gH_SQL) + { + Shavit_PrintToChat(client, "Database not loaded. Check your error logs."); + return Plugin_Handled; + } + if(!IsValidClient(client)) { return Plugin_Handled; @@ -2973,8 +3529,7 @@ public void SQL_DeleteAllZones_Callback(Database db, DBResultSet results, const return; } - UnloadZones(0); - RequestFrame(ReloadPrebuiltZones); + UnloadZones(); int client = GetClientFromSerial(data); @@ -2993,11 +3548,11 @@ public int MenuHandler_SelectZoneType(Menu menu, MenuAction action, int param1, char info[8]; menu.GetItem(param2, info, 8); - gI_ZoneType[param1] = StringToInt(info); + gA_EditCache[param1].iType = StringToInt(info); - if (gI_ZoneType[param1] == Zone_Gravity || gI_ZoneType[param1] == Zone_Speedmod) + if (gA_EditCache[param1].iType == Zone_Gravity || gA_EditCache[param1].iType == Zone_Speedmod) { - gI_ZoneData[param1] = view_as(1.0); + gA_EditCache[param1].iData = view_as(1.0); } ShowPanel(param1, 1); @@ -3012,17 +3567,15 @@ public int MenuHandler_SelectZoneType(Menu menu, MenuAction action, int param1, void Reset(int client) { - gI_ZoneTrack[client] = Track_Main; + zone_cache_t cache; + cache.iDatabaseID = -1; + gA_EditCache[client] = cache; gI_MapStep[client] = 0; - gI_ZoneFlags[client] = 0; - gI_ZoneData[client] = 0; - gI_ZoneDatabaseID[client] = -1; + gI_HookListPos[client] = -1; + delete gH_StupidTimer[client]; gB_WaitingForChatInput[client] = false; gI_ZoneID[client] = -1; - gV_Point1[client] = ZERO_VECTOR; - gV_Point2[client] = ZERO_VECTOR; - gV_Teleport[client] = ZERO_VECTOR; gV_WallSnap[client] = ZERO_VECTOR; } @@ -3232,8 +3785,19 @@ void FillBoxMinMax(float point1[3], float point2[3], float boxmin[3], float boxm { for (int i = 0; i < 3; i++) { - boxmin[i] = (point1[i] < point2[i]) ? point1[i] : point2[i]; - boxmax[i] = (point1[i] < point2[i]) ? point2[i] : point1[i]; + float a = point1[i]; + float b = point2[i]; + + if (a < b) + { + boxmin[i] = a; + boxmax[i] = b; + } + else + { + boxmin[i] = b; + boxmax[i] = a; + } } } @@ -3268,7 +3832,8 @@ bool InStartOrEndZone(float point1[3], float point2[3], int track, int type) for (int i = 0; i < MAX_ZONES; i++) { - if (!gA_ZoneCache[i].bZoneInitialized || (gA_ZoneCache[i].iZoneTrack == track && gA_ZoneCache[i].iZoneType == type) || (gA_ZoneCache[i].iZoneType != Zone_End && gA_ZoneCache[i].iZoneType != Zone_Start)) + if ((gA_ZoneCache[i].iTrack == track && gA_ZoneCache[i].iType == type) + || (gA_ZoneCache[i].iType != Zone_End && gA_ZoneCache[i].iType != Zone_Start)) { continue; } @@ -3329,9 +3894,9 @@ public Action Shavit_OnUserCmdPre(int client, int &buttons, int &impulse, float { origin[2] += 1.0; - if (!InStartOrEndZone(origin, NULL_VECTOR, gI_ZoneTrack[client], gI_ZoneType[client])) + if (!InStartOrEndZone(origin, NULL_VECTOR, gA_EditCache[client].iTrack, gA_EditCache[client].iType)) { - gV_Point1[client] = origin; + gA_EditCache[client].fCorner1 = origin; ShowPanel(client, 2); } } @@ -3339,9 +3904,9 @@ public Action Shavit_OnUserCmdPre(int client, int &buttons, int &impulse, float { origin[2] += gCV_Height.FloatValue; - if (origin[0] != gV_Point1[client][0] && origin[1] != gV_Point1[client][1] && !InStartOrEndZone(gV_Point1[client], origin, gI_ZoneTrack[client], gI_ZoneType[client])) + if (origin[0] != gA_EditCache[client].fCorner1[0] && origin[1] != gA_EditCache[client].fCorner1[1] && !InStartOrEndZone(gA_EditCache[client].fCorner1, origin, gA_EditCache[client].iTrack, gA_EditCache[client].iType)) { - gV_Point2[client] = origin; + gA_EditCache[client].fCorner2 = origin; gI_MapStep[client]++; CreateEditMenu(client); @@ -3388,36 +3953,24 @@ public int CreateZoneConfirm_Handler(Menu menu, MenuAction action, int param1, i char sInfo[16]; menu.GetItem(param2, sInfo, 16); - gB_HackyResetCheck[param1] = true; - if(StrEqual(sInfo, "yes")) { if (gI_ZoneID[param1] != -1) { - // reenable so it can be wiped in the subsequent InsertZones->SQL_Callback->UnloadZones - gA_ZoneCache[gI_ZoneID[param1]].bZoneInitialized = true; + Shavit_RemoveZone(gI_ZoneID[param1]); // TODO: gI_ZoneID can be wiped mid menu or something... } InsertZone(param1); - gI_MapStep[param1] = 0; - return 0; } else if(StrEqual(sInfo, "no")) { - if (gI_ZoneID[param1] != -1) - { - gA_ZoneCache[gI_ZoneID[param1]].bZoneInitialized = true; - } - Reset(param1); - return 0; } else if(StrEqual(sInfo, "adjust")) { CreateAdjustMenu(param1, 0); - return 0; } else if(StrEqual(sInfo, "tpzone")) @@ -3426,28 +3979,34 @@ public int CreateZoneConfirm_Handler(Menu menu, MenuAction action, int param1, i } else if(StrEqual(sInfo, "datafromchat")) { - gI_ZoneData[param1] = 0; + gA_EditCache[param1].iData = 0; gB_WaitingForChatInput[param1] = true; - Shavit_PrintToChat(param1, "%T", "ZoneEnterDataChat", param1); - return 0; } else if(StrEqual(sInfo, "forcerender")) { - gI_ZoneFlags[param1] ^= ZF_ForceRender; + gA_EditCache[param1].iFlags ^= ZF_ForceRender; } CreateEditMenu(param1); } else if (action == MenuAction_Cancel) { - if (!gB_HackyResetCheck[param1]) + if (param2 == MenuCancel_ExitBack) { - if (gI_ZoneID[param1] != -1) + if (gI_ZoneID[param1] == -1) { - gA_ZoneCache[gI_ZoneID[param1]].bZoneInitialized = true; + OpenHookMenu_Editor(param1); } + else + { + OpenEditMenu(param1, gI_LastMenuPos[param1]); + } + } + else + { + Reset(param1); } } else if(action == MenuAction_End) @@ -3462,13 +4021,13 @@ public Action OnClientSayCommand(int client, const char[] command, const char[] { if(gB_WaitingForChatInput[client] && gI_MapStep[client] == 3) { - if (gI_ZoneType[client] == Zone_Gravity || gI_ZoneType[client] == Zone_Speedmod) + if (gA_EditCache[client].iType == Zone_Gravity || gA_EditCache[client].iType == Zone_Speedmod) { - gI_ZoneData[client] = view_as(StringToFloat(sArgs)); + gA_EditCache[client].iData = view_as(StringToFloat(sArgs)); } else { - gI_ZoneData[client] = StringToInt(sArgs); + gA_EditCache[client].iData = StringToInt(sArgs); } CreateEditMenu(client); @@ -3485,9 +4044,9 @@ void UpdateTeleportZone(int client) GetClientAbsOrigin(client, vTeleport); vTeleport[2] += 2.0; - if(gI_ZoneType[client] == Zone_Stage) + if (gA_EditCache[client].iType == Zone_Stage) { - gV_Teleport[client] = vTeleport; + gA_EditCache[client].fDestination = vTeleport; Shavit_PrintToChat(client, "%T", "ZoneTeleportUpdated", client); } @@ -3497,7 +4056,7 @@ void UpdateTeleportZone(int client) for(int i = 0; i < 3; i++) { - if(gV_Point1[client][i] >= vTeleport[i] == gV_Point2[client][i] >= vTeleport[i]) + if (gA_EditCache[client].fCorner1[i] >= vTeleport[i] == gA_EditCache[client].fCorner2[i] >= vTeleport[i]) { bInside = false; } @@ -3509,7 +4068,7 @@ void UpdateTeleportZone(int client) } else { - gV_Teleport[client] = vTeleport; + gA_EditCache[client].fDestination = vTeleport; Shavit_PrintToChat(client, "%T", "ZoneTeleportUpdated", client); } @@ -3518,18 +4077,37 @@ void UpdateTeleportZone(int client) void CreateEditMenu(int client) { - char sTrack[32]; - GetTrackName(client, gI_ZoneTrack[client], sTrack, 32); + bool hookmenu = gI_HookListPos[client] != -1; + + char sTrack[32], sType[32]; + GetTrackName(client, gA_EditCache[client].iTrack, sTrack, 32); + GetZoneName(client, gA_EditCache[client].iType, sType, sizeof(sType)); - gB_HackyResetCheck[client] = false; Menu menu = new Menu(CreateZoneConfirm_Handler); - menu.SetTitle("%T\n%T\n ", "ZoneEditConfirm", client, "ZoneEditTrack", client, sTrack); + + if (hookmenu) + { + menu.SetTitle("%T\n%T\n%T\n%T\n ", + "ZoneEditConfirm", client, + "ZoneEditTrack", client, sTrack, + "ZoneHook_Zonetype", client, sType, + "ZoneHook_Hooktype", client, + (gA_EditCache[client].iFlags & ZF_Hammerid) ? "hammerid" : "targetname", + gA_EditCache[client].sTarget); + } + else + { + menu.SetTitle("%T\n%T\n%T\n ", + "ZoneEditConfirm", client, + "ZoneEditTrack", client, sTrack, + "ZoneHook_Zonetype", client, sType); + } char sMenuItem[64]; - if(gI_ZoneType[client] == Zone_Teleport) + if (gA_EditCache[client].iType == Zone_Teleport) { - if(EmptyVector(gV_Teleport[client])) + if (EmptyVector(gA_EditCache[client].fDestination)) { FormatEx(sMenuItem, 64, "%T", "ZoneSetTP", client); menu.AddItem("-1", sMenuItem, ITEMDRAW_DISABLED); @@ -3543,7 +4121,7 @@ void CreateEditMenu(int client) FormatEx(sMenuItem, 64, "%T", "ZoneSetTPZone", client); menu.AddItem("tpzone", sMenuItem); } - else if(gI_ZoneType[client] == Zone_Stage) + else if (gA_EditCache[client].iType == Zone_Stage) { FormatEx(sMenuItem, 64, "%T", "ZoneSetYes", client); menu.AddItem("yes", sMenuItem); @@ -3561,48 +4139,52 @@ void CreateEditMenu(int client) menu.AddItem("no", sMenuItem); FormatEx(sMenuItem, 64, "%T", "ZoneSetAdjust", client); - menu.AddItem("adjust", sMenuItem); + menu.AddItem("adjust", sMenuItem, + (hookmenu || gA_EditCache[client].iForm != ZoneForm_Box) ? ITEMDRAW_DISABLED : ITEMDRAW_DEFAULT); - FormatEx(sMenuItem, 64, "%T", "ZoneForceRender", client, ((gI_ZoneFlags[client] & ZF_ForceRender) > 0)? "+":"-"); + FormatEx(sMenuItem, 64, "%T", "ZoneForceRender", client, ((gA_EditCache[client].iFlags & ZF_ForceRender) > 0)? "+":"-"); menu.AddItem("forcerender", sMenuItem); - if(gI_ZoneType[client] == Zone_Stage) + if (gA_EditCache[client].iType == Zone_Stage) { - FormatEx(sMenuItem, 64, "%T", "ZoneSetStage", client, gI_ZoneData[client]); + FormatEx(sMenuItem, 64, "%T", "ZoneSetStage", client, gA_EditCache[client].iData); menu.AddItem("datafromchat", sMenuItem); } - else if(gI_ZoneType[client] == Zone_Airaccelerate) + else if (gA_EditCache[client].iType == Zone_Airaccelerate) { - FormatEx(sMenuItem, 64, "%T", "ZoneSetAiraccelerate", client, gI_ZoneData[client]); + FormatEx(sMenuItem, 64, "%T", "ZoneSetAiraccelerate", client, gA_EditCache[client].iData); menu.AddItem("datafromchat", sMenuItem); } - else if(gI_ZoneType[client] == Zone_CustomSpeedLimit) + else if (gA_EditCache[client].iType == Zone_CustomSpeedLimit) { - if(gI_ZoneData[client] == 0) + if (gA_EditCache[client].iData == 0) { - FormatEx(sMenuItem, 64, "%T", "ZoneSetSpeedLimitUnlimited", client, gI_ZoneData[client]); + FormatEx(sMenuItem, 64, "%T", "ZoneSetSpeedLimitUnlimited", client, gA_EditCache[client].iData); } else { - FormatEx(sMenuItem, 64, "%T", "ZoneSetSpeedLimit", client, gI_ZoneData[client]); + FormatEx(sMenuItem, 64, "%T", "ZoneSetSpeedLimit", client, gA_EditCache[client].iData); } menu.AddItem("datafromchat", sMenuItem); } - else if (gI_ZoneType[client] == Zone_Gravity) + else if (gA_EditCache[client].iType == Zone_Gravity) { - float g = view_as(gI_ZoneData[client]); + float g = view_as(gA_EditCache[client].iData); FormatEx(sMenuItem, sizeof(sMenuItem), "%T", "ZoneSetGravity", client, g); menu.AddItem("datafromchat", sMenuItem); } - else if (gI_ZoneType[client] == Zone_Speedmod) + else if (gA_EditCache[client].iType == Zone_Speedmod) { - float speed = view_as(gI_ZoneData[client]); + float speed = view_as(gA_EditCache[client].iData); FormatEx(sMenuItem, sizeof(sMenuItem), "%T", "ZoneSetSpeedmod", client, speed); menu.AddItem("datafromchat", sMenuItem); } - menu.ExitButton = true; + if (hookmenu || gI_ZoneID[client] != -1) + menu.ExitBackButton = true; + else + menu.ExitButton = true; menu.Display(client, MENU_TIME_FOREVER); } @@ -3656,7 +4238,7 @@ public int ZoneAdjuster_Handler(Menu menu, MenuAction action, int param1, int pa if (gI_ZoneID[param1] != -1) { // reenable original zone - gA_ZoneCache[gI_ZoneID[param1]].bZoneInitialized = true; + //gA_ZoneCache[gI_ZoneID[param1]].bInitialized = true; } Reset(param1); @@ -3672,8 +4254,13 @@ public int ZoneAdjuster_Handler(Menu menu, MenuAction action, int param1, int pa int iPoint = StringToInt(sExploded[0]); int iAxis = StringToInt(sExploded[1]); bool bIncrease = view_as(StringToInt(sExploded[2]) == 1); + float mod = ((bIncrease)? gF_Modifier[param1]:-gF_Modifier[param1]); + + if (iPoint == 1) + gA_EditCache[param1].fCorner1[iAxis] += mod; + else + gA_EditCache[param1].fCorner2[iAxis] += mod; - ((iPoint == 1)? gV_Point1:gV_Point2)[param1][iAxis] += ((bIncrease)? gF_Modifier[param1]:-gF_Modifier[param1]); Shavit_PrintToChat(param1, "%T", (bIncrease)? "ZoneSizeIncrease":"ZoneSizeDecrease", param1, gS_ChatStrings.sVariable2, sAxis[iAxis], gS_ChatStrings.sText, iPoint, gS_ChatStrings.sVariable, gF_Modifier[param1], gS_ChatStrings.sText); CreateAdjustMenu(param1, GetMenuSelectionPosition()); @@ -3684,7 +4271,7 @@ public int ZoneAdjuster_Handler(Menu menu, MenuAction action, int param1, int pa if (gI_ZoneID[param1] != -1) { // reenable original zone - gA_ZoneCache[gI_ZoneID[param1]].bZoneInitialized = true; + //gA_ZoneCache[gI_ZoneID[param1]].bInitialized = true; } Reset(param1); @@ -3697,185 +4284,143 @@ public int ZoneAdjuster_Handler(Menu menu, MenuAction action, int param1, int pa return 0; } -void InsertPrebuiltZone(int zone, bool update, char[] sQuery, int sQueryLen) -{ - if (update) - { - FormatEx(sQuery, sQueryLen, - "UPDATE %smapzones SET corner1_x = '%.03f', corner1_y = '%.03f', corner1_z = '%.03f', corner2_x = '%.03f', corner2_y = '%.03f', corner2_z = '%.03f', prebuilt = 1 WHERE map = '%s' AND type = %d AND track = %d;", - gS_MySQLPrefix, - gV_MapZones[zone][0][0], gV_MapZones[zone][0][1], gV_MapZones[zone][0][2], - gV_MapZones[zone][1][0], gV_MapZones[zone][1][1], gV_MapZones[zone][1][2], - gS_Map, gA_ZoneCache[zone].iZoneType, gA_ZoneCache[zone].iZoneTrack - ); - } - else - { - FormatEx(sQuery, sQueryLen, - "INSERT INTO %smapzones (map, type, corner1_x, corner1_y, corner1_z, corner2_x, corner2_y, corner2_z, track, data, prebuilt) VALUES ('%s', %d, '%.03f', '%.03f', '%.03f', '%.03f', '%.03f', '%.03f', %d, %d, 1);", - gS_MySQLPrefix, gS_Map, gA_ZoneCache[zone].iZoneType, - gV_MapZones[zone][0][0], gV_MapZones[zone][0][1], gV_MapZones[zone][0][2], - gV_MapZones[zone][1][0], gV_MapZones[zone][1][1], gV_MapZones[zone][1][2], - gA_ZoneCache[zone].iZoneTrack, gA_ZoneCache[zone].iZoneData - ); - } -} - void InsertZone(int client) { - int iType = gI_ZoneType[client]; - int iIndex = GetZoneIndex(iType, gI_ZoneTrack[client]); - bool bInsert = (gI_ZoneDatabaseID[client] == -1 && (iIndex == -1 || iType >= Zone_Respawn)); + zone_cache_t c; c = gA_EditCache[client]; + c.iEntity = -1; char sQuery[1024]; char sTrack[64], sZoneName[32]; - GetTrackName(LANG_SERVER, gI_ZoneTrack[client], sTrack, sizeof(sTrack)); - GetZoneName(LANG_SERVER, iType, sZoneName, sizeof(sZoneName)); + GetTrackName(LANG_SERVER, c.iTrack, sTrack, sizeof(sTrack)); + GetZoneName(LANG_SERVER, c.iType, sZoneName, sizeof(sZoneName)); - if(iType == Zone_CustomSpawn) + // normalize zone points... + FillBoxMinMax(c.fCorner1, c.fCorner2, c.fCorner1, c.fCorner2); + + Shavit_AddZone(c); + + if (c.iDatabaseID == -1) // insert { - Shavit_LogMessage("%L - added custom spawn {%.2f, %.2f, %.2f} to map `%s`.", client, gV_Point1[client][0], gV_Point1[client][1], gV_Point1[client][2], gS_Map); + Shavit_LogMessage( + "%L - added %s %s to map `%s`. \ + p1(%f, %f, %f), p2(%f, %f, %f), dest(%f, %f, %f), \ + flags=%d, data=%d, form=%d, target='%s'", + client, sTrack, sZoneName, gS_Map, + EXPAND_VECTOR(c.fCorner1), + EXPAND_VECTOR(c.fCorner2), + EXPAND_VECTOR(c.fDestination), + c.iFlags, c.iData, + c.iForm, c.sTarget + ); FormatEx(sQuery, sizeof(sQuery), - "INSERT INTO %smapzones (map, type, destination_x, destination_y, destination_z, track) VALUES ('%s', %d, '%.03f', '%.03f', '%.03f', %d);", - gS_MySQLPrefix, gS_Map, Zone_CustomSpawn, gV_Point1[client][0], gV_Point1[client][1], gV_Point1[client][2], gI_ZoneTrack[client]); - } - else if(bInsert) // insert - { - Shavit_LogMessage("%L - added %s %s to map `%s`.", client, sTrack, sZoneName, gS_Map); - - FormatEx(sQuery, sizeof(sQuery), - "INSERT INTO %smapzones (map, type, corner1_x, corner1_y, corner1_z, corner2_x, corner2_y, corner2_z, destination_x, destination_y, destination_z, track, flags, data) VALUES ('%s', %d, '%.03f', '%.03f', '%.03f', '%.03f', '%.03f', '%.03f', '%.03f', '%.03f', '%.03f', %d, %d, %d);", - gS_MySQLPrefix, gS_Map, iType, gV_Point1[client][0], gV_Point1[client][1], gV_Point1[client][2], gV_Point2[client][0], gV_Point2[client][1], gV_Point2[client][2], gV_Teleport[client][0], gV_Teleport[client][1], gV_Teleport[client][2], gI_ZoneTrack[client], gI_ZoneFlags[client], gI_ZoneData[client]); + "INSERT INTO %smapzones (map, type, \ + corner1_x, corner1_y, corner1_z, \ + corner2_x, corner2_y, corner2_z, \ + destination_x, destination_y, destination_z, \ + track, flags, data, form, target) VALUES \ + ('%s', %d, \ + %f, %f, %f, \ + %f, %f, %f, \ + %f, %f, %f, \ + %d, %d, %d, \ + %d, '%s');", + gS_MySQLPrefix, + gS_Map, c.iType, + EXPAND_VECTOR(c.fCorner1), + EXPAND_VECTOR(c.fCorner2), + EXPAND_VECTOR(c.fDestination), + c.iTrack, c.iFlags, c.iData, + c.iForm, c.sTarget + ); } else // update { - Shavit_LogMessage("%L - updated %s %s in map `%s`.", client, sTrack, sZoneName, gS_Map); - - if(gI_ZoneDatabaseID[client] == -1) - { - for(int i = 0; i < gI_MapZones; i++) - { - if(gA_ZoneCache[i].bZoneInitialized && gA_ZoneCache[i].iZoneType == iType && gA_ZoneCache[i].iZoneTrack == gI_ZoneTrack[client]) - { - gI_ZoneDatabaseID[client] = gA_ZoneCache[i].iDatabaseID; - } - } - } + Shavit_LogMessage( + "%L - updated %s %s (%d) in map `%s`. \ + p1(%f, %f, %f), p2(%f, %f, %f), dest(%f, %f, %f), \ + flags=%d, data=%d, form=%d, target='%s'", + client, sTrack, sZoneName, c.iDatabaseID, gS_Map, + EXPAND_VECTOR(c.fCorner1), + EXPAND_VECTOR(c.fCorner2), + EXPAND_VECTOR(c.fDestination), + c.iFlags, c.iData, + c.iForm, c.sTarget + ); FormatEx(sQuery, sizeof(sQuery), - "UPDATE %smapzones SET corner1_x = '%.03f', corner1_y = '%.03f', corner1_z = '%.03f', corner2_x = '%.03f', corner2_y = '%.03f', corner2_z = '%.03f', destination_x = '%.03f', destination_y = '%.03f', destination_z = '%.03f', track = %d, flags = %d, data = %d WHERE %s = %d;", - gS_MySQLPrefix, gV_Point1[client][0], gV_Point1[client][1], gV_Point1[client][2], gV_Point2[client][0], gV_Point2[client][1], gV_Point2[client][2], gV_Teleport[client][0], gV_Teleport[client][1], gV_Teleport[client][2], gI_ZoneTrack[client], gI_ZoneFlags[client], gI_ZoneData[client], (gB_MySQL)? "id":"rowid", gI_ZoneDatabaseID[client]); + "UPDATE %smapzones SET \ + corner1_x = '%f', corner1_y = '%f', corner1_z = '%f' \ + , corner2_x = '%f', corner2_y = '%f', corner2_z = '%f' \ + , destination_x = '%f', destination_y = '%f', destination_z = '%f' \ + , flags = %d, data = %d \ + , form = %d, target = '%s' \ + WHERE %s = %d;", + gS_MySQLPrefix, + EXPAND_VECTOR(c.fCorner1), + EXPAND_VECTOR(c.fCorner2), + EXPAND_VECTOR(c.fDestination), + c.iFlags, c.iData, + c.iForm, c.sTarget, + gB_MySQL ? "id" : "rowid", c.iDatabaseID + ); } - gH_SQL.Query2(SQL_InsertZone_Callback, sQuery, GetClientSerial(client)); + Reset(client); + + gH_SQL.Query2(SQL_InsertZone_Callback, sQuery, 0); } -public void SQL_InsertZone_Callback(Database db, DBResultSet results, const char[] error, any data) +public void SQL_InsertZone_Callback(Database db, DBResultSet results, const char[] error, DataPack data) { - if(results == null) + if (results == null) { LogError("Timer (zone insert) SQL query failed. Reason: %s", error); - return; } - - int client = GetClientFromSerial(data); - - if(client == 0) - { - return; - } - - if(gI_ZoneType[client] == Zone_CustomSpawn) - { - Shavit_PrintToChat(client, "%T", "ZoneCustomSpawnSuccess", client); - } - - UnloadZones(0); - RefreshZones(); - Reset(client); } -public Action Timer_DrawVisible(Handle Timer) +public Action Timer_DrawZones(Handle Timer, any drawAll) { - if(gI_MapZones == 0) + if (gI_MapZones == 0) { return Plugin_Continue; } - static int iCycle = 0; + if (drawAll && !gCV_AllowDrawAllZones.BoolValue) + { + return Plugin_Continue; + } + + static int iCycle[2]; static int iMaxZonesPerFrame = 5; - if(iCycle >= gI_MapZones) + if (iCycle[drawAll] >= gI_MapZones) { - iCycle = 0; + iCycle[drawAll] = 0; } int iDrawn = 0; - for(int i = iCycle; i < gI_MapZones; i++, iCycle++) + for (int i = iCycle[drawAll]; i < gI_MapZones; i++, iCycle[drawAll]++) { - if(gA_ZoneCache[i].bZoneInitialized) - { - int type = gA_ZoneCache[i].iZoneType; - int track = gA_ZoneCache[i].iZoneTrack; + int form = gA_ZoneCache[i].iForm; + int type = gA_ZoneCache[i].iType; + int track = gA_ZoneCache[i].iTrack; - if(gA_ZoneSettings[type][track].bVisible || (gA_ZoneCache[i].iZoneFlags & ZF_ForceRender) > 0) + if (drawAll || gA_ZoneSettings[type][track].bVisible || (gA_ZoneCache[i].iFlags & ZF_ForceRender) > 0) + { + if (form == ZoneForm_trigger_teleport && !(drawAll || (gA_ZoneCache[i].iFlags & ZF_ForceRender) > 0)) { - DrawZone(gV_MapZones_Visual[i], - GetZoneColors(type, track), - RoundToCeil(float(gI_MapZones) / iMaxZonesPerFrame + 2.0) * gCV_Interval.FloatValue, - gA_ZoneSettings[type][track].fWidth, - gA_ZoneSettings[type][track].bFlatZone, - gV_ZoneCenter[i], - gA_ZoneSettings[type][track].iBeam, - gA_ZoneSettings[type][track].iHalo, - track, - type, - false, - 0); - - if (++iDrawn % iMaxZonesPerFrame == 0) - { - return Plugin_Continue; - } + continue; } - } - } - iCycle = 0; - - return Plugin_Continue; -} - -public Action Timer_DrawAllZones(Handle Timer) -{ - if (gI_MapZones == 0 || !gCV_AllowDrawAllZones.BoolValue) - { - return Plugin_Continue; - } - - static int iCycle = 0; - static int iMaxZonesPerFrame = 5; - - if (iCycle >= gI_MapZones) - { - iCycle = 0; - } - - int iDrawn = 0; - - for (int i = iCycle; i < gI_MapZones; i++, iCycle++) - { - if (gA_ZoneCache[i].bZoneInitialized) - { - int type = gA_ZoneCache[i].iZoneType; - int track = gA_ZoneCache[i].iZoneTrack; + int colors[4]; + GetZoneColors(colors, type, track); DrawZone( gV_MapZones_Visual[i], - GetZoneColors(type, track), + colors, RoundToCeil(float(gI_MapZones) / iMaxZonesPerFrame + 2.0) * gCV_Interval.FloatValue, gA_ZoneSettings[type][track].fWidth, gA_ZoneSettings[type][track].bFlatZone, @@ -3884,7 +4429,8 @@ public Action Timer_DrawAllZones(Handle Timer) gA_ZoneSettings[type][track].iHalo, track, type, - true, // <==== this is the the important part, + gA_ZoneSettings[type][track].iSpeed, + !!drawAll, 0 ); @@ -3895,20 +4441,17 @@ public Action Timer_DrawAllZones(Handle Timer) } } - iCycle = 0; + iCycle[drawAll] = 0; return Plugin_Continue; } -int[] GetZoneColors(int type, int track, int customalpha = 0) +void GetZoneColors(int colors[4], int type, int track, int customalpha = 0) { - int colors[4]; colors[0] = gA_ZoneSettings[type][track].iRed; colors[1] = gA_ZoneSettings[type][track].iGreen; colors[2] = gA_ZoneSettings[type][track].iBlue; colors[3] = (customalpha > 0)? customalpha:gA_ZoneSettings[type][track].iAlpha; - - return colors; } public Action Timer_Draw(Handle Timer, any data) @@ -3919,6 +4462,15 @@ public Action Timer_Draw(Handle Timer, any data) { Reset(client); + for (int i = 1; i < MAXPLAYERS+1; i++) + { + if (gH_StupidTimer[i] == Timer) + { + gH_StupidTimer[i] = null; + break; + } + } + return Plugin_Stop; } @@ -3940,33 +4492,35 @@ public Action Timer_Draw(Handle Timer, any data) gV_WallSnap[client] = origin; } - if(gI_MapStep[client] == 1 || gV_Point2[client][0] == 0.0) + if (gI_MapStep[client] == 1 || gA_EditCache[client].fCorner2[0] == 0.0) { origin[2] = (vPlayerOrigin[2] + gCV_Height.FloatValue); } else { - origin = gV_Point2[client]; + origin = gA_EditCache[client].fCorner2; } - int type = gI_ZoneType[client]; - int track = gI_ZoneTrack[client]; + int type = gA_EditCache[client].iType; type = type < 0 ? 0 : type; + int track = gA_EditCache[client].iTrack; track = track < 0 ? 0 : track; - if(!EmptyVector(gV_Point1[client]) || !EmptyVector(gV_Point2[client])) + if (!EmptyVector(gA_EditCache[client].fCorner1) || !EmptyVector(gA_EditCache[client].fCorner2)) { float points[8][3]; - points[0] = gV_Point1[client]; + points[0] = gA_EditCache[client].fCorner1; points[7] = origin; - CreateZonePoints(points, gCV_Offset.FloatValue); + CreateZonePoints(points, false); // This is here to make the zone setup grid snapping be 1:1 to how it looks when done with the setup. origin = points[7]; - DrawZone(points, GetZoneColors(type, track, 125), 0.1, gA_ZoneSettings[type][track].fWidth, false, origin, gI_BeamSpriteIgnoreZ, gA_ZoneSettings[type][track].iHalo, track, type); + int colors[4]; + GetZoneColors(colors, type, track, 125); + DrawZone(points, colors, 0.1, gA_ZoneSettings[type][track].fWidth, false, origin, gI_BeamSpriteIgnoreZ, gA_ZoneSettings[type][track].iHalo, track, type, gA_ZoneSettings[type][track].iSpeed, false, 0); - if(gI_ZoneType[client] == Zone_Teleport && !EmptyVector(gV_Teleport[client])) + if (gA_EditCache[client].iType == Zone_Teleport && !EmptyVector(gA_EditCache[client].fDestination)) { - TE_SetupEnergySplash(gV_Teleport[client], ZERO_VECTOR, false); + TE_SetupEnergySplash(gA_EditCache[client].fDestination, ZERO_VECTOR, false); TE_SendToAll(0.0); } } @@ -3998,7 +4552,7 @@ public Action Timer_Draw(Handle Timer, any data) return Plugin_Continue; } -void DrawZone(float points[8][3], int color[4], float life, float width, bool flat, float center[3], int beam, int halo, int track, int type, bool drawallzones=false, int single_client=0) +void DrawZone(float points[8][3], int color[4], float life, float width, bool flat, float center[3], int beam, int halo, int track, int type, int speed, bool drawallzones, int single_client) { static int pairs[][] = { @@ -4076,7 +4630,7 @@ void DrawZone(float points[8][3], int color[4], float life, float width, bool fl for(int j = 0; j < point_size; j++) { - TE_SetupBeamPoints(points[pairs[j][0]], points[pairs[j][1]], beam, halo, 0, 0, life, actual_width, actual_width, 0, 0.0, actual_color, 0); + TE_SetupBeamPoints(points[pairs[j][0]], points[pairs[j][1]], beam, halo, 0, 0, life, actual_width, actual_width, 0, 0.0, actual_color, speed); TE_SendToClient(clients[i], 0.0); } } @@ -4084,8 +4638,10 @@ void DrawZone(float points[8][3], int color[4], float life, float width, bool fl // original by blacky // creates 3d box from 2 points -void CreateZonePoints(float point[8][3], float offset = 0.0) +void CreateZonePoints(float point[8][3], bool prebuilt) { + float offset = -(prebuilt ? gCV_PrebuiltVisualOffset.FloatValue : 0.0) + gCV_Offset.FloatValue; + // calculate all zone edges for(int i = 1; i < 7; i++) { @@ -4125,11 +4681,17 @@ public void Shavit_OnDatabaseLoaded() gH_SQL = view_as(Shavit_GetDatabase()); gB_MySQL = IsMySQLDatabase(gH_SQL); - gB_Connected = true; - - if (!gB_Late) + if (gB_YouCanLoadZonesNow && gCV_SQLZones.BoolValue) { - OnMapStart(); + RefreshZones(); + } + + for(int i = 1; i <= MaxClients; i++) + { + if (IsValidClient(i) && !IsFakeClient(i)) + { + GetStartPosition(i); + } } } @@ -4194,7 +4756,7 @@ public void Shavit_OnRestart(int client, int track) float fCenter[3]; fCenter[0] = gV_ZoneCenter[iIndex][0]; fCenter[1] = gV_ZoneCenter[iIndex][1]; - fCenter[2] = gV_MapZones[iIndex][0][2] + gCV_ExtraSpawnHeight.FloatValue; + fCenter[2] = gA_ZoneCache[iIndex].fCorner1[2] + gCV_ExtraSpawnHeight.FloatValue; if (bCustomStart) { @@ -4204,7 +4766,6 @@ public void Shavit_OnRestart(int client, int track) fCenter[2] += 1.0; TeleportEntity(client, fCenter, gB_HasSetStart[client][track] ? gF_StartAng[client][track] : NULL_VECTOR, view_as({0.0, 0.0, 0.0})); - // I would like to put MaybeDoPhysicsUntouch() here but then it doesn't retrigger the zone's starttouch until next frame so the hud can show the wrong thing if I spam !r since the player isn't "in-the-zone"... TODO maybe... if (gB_ReplayRecorder && gB_HasSetStart[client][track]) { @@ -4234,9 +4795,12 @@ public void Shavit_OnRestart(int client, int track) if (iIndex != -1) { + int colors[4]; + GetZoneColors(colors, Zone_Start, track); + DrawZone( gV_MapZones_Visual[iIndex], - GetZoneColors(Zone_Start, track), + colors, gCV_Interval.FloatValue, gA_ZoneSettings[Zone_Start][track].fWidth, gA_ZoneSettings[Zone_Start][track].bFlatZone, @@ -4245,6 +4809,7 @@ public void Shavit_OnRestart(int client, int track) gA_ZoneSettings[Zone_Start][track].iHalo, track, Zone_Start, + gA_ZoneSettings[Zone_Start][track].iSpeed, false, client ); @@ -4262,7 +4827,7 @@ public void Shavit_OnEnd(int client, int track) float fCenter[3]; fCenter[0] = gV_ZoneCenter[iIndex][0]; fCenter[1] = gV_ZoneCenter[iIndex][1]; - fCenter[2] = gV_MapZones[iIndex][0][2] + 1.0; // no stuck in floor please + fCenter[2] = gA_ZoneCache[iIndex].fCorner1[2] + 1.0; // no stuck in floor please TeleportEntity(client, fCenter, NULL_VECTOR, view_as({0.0, 0.0, 0.0})); } @@ -4270,9 +4835,12 @@ public void Shavit_OnEnd(int client, int track) if (iIndex != -1) { + int colors[4]; + GetZoneColors(colors, Zone_End, track); + DrawZone( gV_MapZones_Visual[iIndex], - GetZoneColors(Zone_End, track), + colors, gCV_Interval.FloatValue, gA_ZoneSettings[Zone_End][track].fWidth, gA_ZoneSettings[Zone_End][track].bFlatZone, @@ -4281,6 +4849,7 @@ public void Shavit_OnEnd(int client, int track) gA_ZoneSettings[Zone_End][track].iHalo, track, Zone_End, + gA_ZoneSettings[Zone_End][track].iSpeed, false, client ); @@ -4295,14 +4864,9 @@ bool EmptyVector(float vec[3]) // returns -1 if there's no zone int GetZoneIndex(int type, int track, int start = 0) { - if(gI_MapZones == 0) - { - return -1; - } - for(int i = start; i < gI_MapZones; i++) { - if(gA_ZoneCache[i].bZoneInitialized && gA_ZoneCache[i].iZoneType == type && (gA_ZoneCache[i].iZoneTrack == track || track == -1)) + if (gA_ZoneCache[i].iType == type && (gA_ZoneCache[i].iTrack == track || track == -1)) { return i; } @@ -4318,16 +4882,12 @@ public void Player_Spawn(Event event, const char[] name, bool dontBroadcast) public void Round_Start(Event event, const char[] name, bool dontBroadcast) { - bool empty_InsideZone[TRACKS_SIZE]; + int empty_InsideZone[TRACKS_SIZE]; + bool empty_InsideZoneID[MAX_ZONES]; - for (int i = 0; i <= MaxClients; i++) + for (int i = 1; i <= MaxClients; i++) { - for (int j = 0; j < ZONETYPES_SIZE; j++) - { - gB_InsideZone[i][j] = empty_InsideZone; - } - - bool empty_InsideZoneID[MAX_ZONES]; + gI_InsideZone[i] = empty_InsideZone; gB_InsideZoneID[i] = empty_InsideZoneID; } } @@ -4342,128 +4902,40 @@ float Abs(float input) return input; } -public void CreateZoneEntities(bool only_create_dead_entities) +void SetZoneMinsMaxs(int zone) { - for(int i = 0; i < gI_MapZones; i++) + float distance_x = Abs(gA_ZoneCache[zone].fCorner1[0] - gA_ZoneCache[zone].fCorner2[0]) / 2; + float distance_y = Abs(gA_ZoneCache[zone].fCorner1[1] - gA_ZoneCache[zone].fCorner2[1]) / 2; + float distance_z = Abs(gA_ZoneCache[zone].fCorner1[2] - gA_ZoneCache[zone].fCorner2[2]) / 2; + + float height = ((IsSource2013(gEV_Type))? 62.0:72.0) / 2; + + float mins[3]; + mins[0] = -distance_x; + mins[1] = -distance_y; + mins[2] = -distance_z + height; + + float maxs[3]; + maxs[0] = distance_x; + maxs[1] = distance_y; + maxs[2] = distance_z - height; + + float offset = gCV_BoxOffset.FloatValue; + + if (distance_x > offset) { - if(gA_ZoneCache[i].bPrebuilt) - { - continue; - } - - for(int j = 1; j <= MaxClients; j++) - { - for(int k = 0; k < TRACKS_SIZE; k++) - { - gB_InsideZone[j][gA_ZoneCache[i].iZoneType][k] = false; - } - - gB_InsideZoneID[j][i] = false; - } - - if(gA_ZoneCache[i].iEntityID != -1) - { - if (only_create_dead_entities) - { - continue; - } - - KillZoneEntity(i); - } - - if(!gA_ZoneCache[i].bZoneInitialized) - { - continue; - } - - bool speedmod = (gA_ZoneCache[i].iZoneType == Zone_Speedmod); - char classname[32]; classname = speedmod ? "player_speedmod" : "trigger_multiple"; - - int entity = CreateEntityByName(classname); - - if(entity == -1) - { - LogError("\"%s\" creation failed, map %s.", classname, gS_Map); - - continue; - } - - // TODO: look into storing the SF_SPEED_MOD_SUPPRESS_* in iZoneFlags - if (!speedmod) - { - DispatchKeyValue(entity, "wait", "0"); - DispatchKeyValue(entity, "spawnflags", "4097"); - } - - if(!DispatchSpawn(entity)) - { - LogError("\"%s\" spawning failed, map %s.", classname, gS_Map); - - continue; - } - - ActivateEntity(entity); - SetEntityModel(entity, (gEV_Type == Engine_TF2)? "models/error.mdl":"models/props/cs_office/vending_machine.mdl"); - SetEntProp(entity, Prop_Send, "m_fEffects", 32); - - TeleportEntity(entity, gV_ZoneCenter[i], NULL_VECTOR, NULL_VECTOR); - - float distance_x = Abs(gV_MapZones[i][0][0] - gV_MapZones[i][1][0]) / 2; - float distance_y = Abs(gV_MapZones[i][0][1] - gV_MapZones[i][1][1]) / 2; - float distance_z = Abs(gV_MapZones[i][0][2] - gV_MapZones[i][1][2]) / 2; - - float height = ((IsSource2013(gEV_Type))? 62.0:72.0) / 2; - - float min[3]; - min[0] = -distance_x; - min[1] = -distance_y; - min[2] = -distance_z + height; - - float max[3]; - max[0] = distance_x; - max[1] = distance_y; - max[2] = distance_z - height; - - float offset = gCV_BoxOffset.FloatValue; - - if (distance_x > offset) - { - min[0] += offset; - max[0] -= offset; - } - - if (distance_y > offset) - { - min[1] += offset; - max[1] -= offset; - } - - SetEntPropVector(entity, Prop_Send, "m_vecMins", min); - SetEntPropVector(entity, Prop_Send, "m_vecMaxs", max); - - if (speedmod) - { - int FSOLID_NOT_SOLID = 4; - int FSOLID_TRIGGER = 8; - SetEntProp(entity, Prop_Send, "m_usSolidFlags", FSOLID_TRIGGER|FSOLID_NOT_SOLID); - SDKHook(entity, SDKHook_StartTouch, SameTrack_StartTouch_er); - } - - SetEntProp(entity, Prop_Send, "m_nSolidType", 2); - - SDKHook(entity, SDKHook_StartTouchPost, StartTouchPost); - SDKHook(entity, SDKHook_EndTouchPost, EndTouchPost); - SDKHook(entity, SDKHook_TouchPost, TouchPost); - - gI_EntityZone[entity] = i; - gA_ZoneCache[i].iEntityID = entity; - - char sTargetname[32]; - FormatEx(sTargetname, 32, "shavit_zones_%d_%d", gA_ZoneCache[i].iZoneTrack, gA_ZoneCache[i].iZoneType); - DispatchKeyValue(entity, "targetname", sTargetname); + mins[0] += offset; + maxs[0] -= offset; } - gB_ZoneCreationQueued = false; + if (distance_y > offset) + { + mins[1] += offset; + maxs[1] -= offset; + } + + SetEntPropVector(gA_ZoneCache[zone].iEntity, Prop_Send, "m_vecMins", mins); + SetEntPropVector(gA_ZoneCache[zone].iEntity, Prop_Send, "m_vecMaxs", maxs); } public MRESReturn DHooks_OnTeleport(int pThis, DHookParam hParams) @@ -4493,15 +4965,32 @@ void ACTUALLY_ForcePlayerSuicide(int client) public void StartTouchPost(int entity, int other) { - if(other < 1 || other > MaxClients || gI_EntityZone[entity] == -1 || !gA_ZoneCache[gI_EntityZone[entity]].bZoneInitialized || IsFakeClient(other) || - (gCV_EnforceTracks.BoolValue && gA_ZoneCache[gI_EntityZone[entity]].iZoneType > Zone_End && gA_ZoneCache[gI_EntityZone[entity]].iZoneTrack != Shavit_GetClientTrack(other))) - { + if (other < 1 || other > MaxClients || IsFakeClient(other)) return; + + int zone = gI_EntityZone[entity]; + + if (zone == -1) + return; + + int type = gA_ZoneCache[zone].iType; + int track = gA_ZoneCache[zone].iTrack; + + // todo: do this after the inside-zone is set? + if (gCV_EnforceTracks.BoolValue && type > Zone_End && track != Shavit_GetClientTrack(other)) + return; + + if (gA_ZoneCache[zone].iForm == ZoneForm_trigger_multiple || gA_ZoneCache[zone].iForm == ZoneForm_trigger_teleport) + { + if (!SDKCall(gH_PassesTriggerFilters, entity, other)) + { + return; + } } TimerStatus status = Shavit_GetTimerStatus(other); - switch(gA_ZoneCache[gI_EntityZone[entity]].iZoneType) + switch (type) { case Zone_Respawn: { @@ -4510,7 +4999,7 @@ public void StartTouchPost(int entity, int other) case Zone_Teleport: { - TeleportEntity(other, gV_Destinations[gI_EntityZone[entity]], NULL_VECTOR, NULL_VECTOR); + TeleportEntity(other, gA_ZoneCache[zone].fDestination, NULL_VECTOR, NULL_VECTOR); } case Zone_Slay: @@ -4534,19 +5023,19 @@ public void StartTouchPost(int entity, int other) case Zone_End: { - if (status == Timer_Running && Shavit_GetClientTrack(other) == gA_ZoneCache[gI_EntityZone[entity]].iZoneTrack) + if (status == Timer_Running && Shavit_GetClientTrack(other) == track) { - Shavit_FinishMap(other, gA_ZoneCache[gI_EntityZone[entity]].iZoneTrack); + Shavit_FinishMap(other, track); } } case Zone_Stage: { - int num = gA_ZoneCache[gI_EntityZone[entity]].iZoneData; + int num = gA_ZoneCache[zone].iData; int iStyle = Shavit_GetBhopStyle(other); bool bTASSegments = Shavit_GetStyleSettingBool(iStyle, "tas") || Shavit_GetStyleSettingBool(iStyle, "segments"); - if (status == Timer_Running && Shavit_GetClientTrack(other) == gA_ZoneCache[gI_EntityZone[entity]].iZoneTrack && (num > gI_LastStage[other] || bTASSegments || Shavit_IsPracticeMode(other))) + if (status == Timer_Running && Shavit_GetClientTrack(other) == track && (num > gI_LastStage[other] || bTASSegments || Shavit_IsPracticeMode(other))) { gI_LastStage[other] = num; char sTime[32]; @@ -4573,57 +5062,62 @@ public void StartTouchPost(int entity, int other) case Zone_Speedmod: { char s[16]; - FloatToString(view_as(gA_ZoneCache[gI_EntityZone[entity]].iZoneData), s, sizeof(s)); + FloatToString(view_as(gA_ZoneCache[zone].iData), s, sizeof(s)); SetVariantString(s); AcceptEntityInput(entity, "ModifySpeed", other, entity, 0); } } - gB_InsideZone[other][gA_ZoneCache[gI_EntityZone[entity]].iZoneType][gA_ZoneCache[gI_EntityZone[entity]].iZoneTrack] = true; - gB_InsideZoneID[other][gI_EntityZone[entity]] = true; + gI_InsideZone[other][track] |= (1 << type); + gB_InsideZoneID[other][zone] = true; Call_StartForward(gH_Forwards_EnterZone); Call_PushCell(other); - Call_PushCell(gA_ZoneCache[gI_EntityZone[entity]].iZoneType); - Call_PushCell(gA_ZoneCache[gI_EntityZone[entity]].iZoneTrack); - Call_PushCell(gI_EntityZone[entity]); + Call_PushCell(type); + Call_PushCell(track); + Call_PushCell(zone); Call_PushCell(entity); - Call_PushCell(gA_ZoneCache[gI_EntityZone[entity]].iZoneData); + Call_PushCell(gA_ZoneCache[zone].iData); Call_Finish(); } public Action SameTrack_StartTouch_er(int entity, int other) { - if (other < 1 || other > MaxClients || gI_EntityZone[entity] == -1 || !gA_ZoneCache[gI_EntityZone[entity]].bZoneInitialized || IsFakeClient(other) || gA_ZoneCache[gI_EntityZone[entity]].iZoneTrack != Shavit_GetClientTrack(other)) - { + if (other < 1 || other > MaxClients || IsFakeClient(other)) + return Plugin_Stop; + + int zone = gI_EntityZone[entity]; + + if (zone == -1 || gA_ZoneCache[zone].iTrack != Shavit_GetClientTrack(other)) return Plugin_Stop; - } return Plugin_Continue; } public void EndTouchPost(int entity, int other) { - if(other < 1 || other > MaxClients || gI_EntityZone[entity] == -1 || IsFakeClient(other)) - { + if (other < 1 || other > MaxClients || IsFakeClient(other)) return; - } int entityzone = gI_EntityZone[entity]; - int type = gA_ZoneCache[entityzone].iZoneType; - int track = gA_ZoneCache[entityzone].iZoneTrack; + + if (entityzone == -1) + return; + + int type = gA_ZoneCache[entityzone].iType; + int track = gA_ZoneCache[entityzone].iTrack; if (type < 0 || track < 0) // odd { return; } - gB_InsideZone[other][type][track] = false; gB_InsideZoneID[other][entityzone] = false; + RecalcInsideZone(other); if (type == Zone_Speedmod) { - SetVariantString("1.0"); // surely nothing can go wrong with this + SetVariantString("1.0"); AcceptEntityInput(entity, "ModifySpeed", other, entity, 0); } @@ -4633,20 +5127,36 @@ public void EndTouchPost(int entity, int other) Call_PushCell(track); Call_PushCell(entityzone); Call_PushCell(entity); - Call_PushCell(gA_ZoneCache[entityzone].iZoneData); + Call_PushCell(gA_ZoneCache[entityzone].iData); Call_Finish(); } public void TouchPost(int entity, int other) { - if(other < 1 || other > MaxClients || gI_EntityZone[entity] == -1 || IsFakeClient(other) || - (gCV_EnforceTracks.BoolValue && gA_ZoneCache[gI_EntityZone[entity]].iZoneType > Zone_End && gA_ZoneCache[gI_EntityZone[entity]].iZoneTrack != Shavit_GetClientTrack(other))) - { + if (other < 1 || other > MaxClients || IsFakeClient(other)) return; + + int zone = gI_EntityZone[entity]; + + if (zone == -1) + return; + + int type = gA_ZoneCache[zone].iType; + int track = gA_ZoneCache[zone].iTrack; + + if (gCV_EnforceTracks.BoolValue && type > Zone_End && track != Shavit_GetClientTrack(other)) + return; + + if (gA_ZoneCache[zone].iForm == ZoneForm_trigger_multiple || gA_ZoneCache[zone].iForm == ZoneForm_trigger_teleport) + { + if (!SDKCall(gH_PassesTriggerFilters, entity, other)) + { + return; + } } // do precise stuff here, this will be called *A LOT* - switch(gA_ZoneCache[gI_EntityZone[entity]].iZoneType) + switch (type) { case Zone_Start: { @@ -4666,7 +5176,7 @@ public void TouchPost(int entity, int other) { if (curr_tick != tick_served[other]) { - ResetClientTargetNameAndClassName(other, gA_ZoneCache[gI_EntityZone[entity]].iZoneTrack); + ResetClientTargetNameAndClassName(other, track); PhysicsRemoveTouchedList(other); ClearClientEvents(other); @@ -4689,11 +5199,11 @@ public void TouchPost(int entity, int other) // start timer instantly for main track, but require bonuses to have the current timer stopped // so you don't accidentally step on those while running - if(Shavit_GetTimerStatus(other) == Timer_Stopped || Shavit_GetClientTrack(other) != Track_Main) + if (Shavit_GetTimerStatus(other) == Timer_Stopped || Shavit_GetClientTrack(other) != Track_Main) { - Shavit_StartTimer(other, gA_ZoneCache[gI_EntityZone[entity]].iZoneTrack); + Shavit_StartTimer(other, track); } - else if(gA_ZoneCache[gI_EntityZone[entity]].iZoneTrack == Track_Main) + else if (track == Track_Main) { Shavit_StartTimer(other, Track_Main); } @@ -4702,12 +5212,10 @@ public void TouchPost(int entity, int other) { CS_RespawnPlayer(other); } - case Zone_Teleport: { - TeleportEntity(other, gV_Destinations[gI_EntityZone[entity]], NULL_VECTOR, NULL_VECTOR); + TeleportEntity(other, gA_ZoneCache[zone].fDestination, NULL_VECTOR, NULL_VECTOR); } - case Zone_Slay: { TimerStatus status = Shavit_GetTimerStatus(other); @@ -4719,7 +5227,6 @@ public void TouchPost(int entity, int other) Shavit_PrintToChat(other, "%T", "ZoneSlayEnter", other, gS_ChatStrings.sWarning, gS_ChatStrings.sVariable2, gS_ChatStrings.sWarning); } } - case Zone_Stop: { if(Shavit_GetTimerStatus(other) != Timer_Stopped) @@ -4731,22 +5238,24 @@ public void TouchPost(int entity, int other) } } -public void UsePost(int entity, int activator, int caller, UseType type, float value) +public void UsePost_HookedButton(int entity, int activator, int caller, UseType type, float value) { if (activator < 1 || activator > MaxClients || IsFakeClient(activator)) { return; } - int zone = -1; - int track = Track_Main; + int zone = gI_EntityZone[entity]; - if (!GetButtonInfo(entity, zone, track)) + if (zone > MaxClients) { - return; + ButtonLogic(activator, gA_ZoneCache[zone].iType, gA_ZoneCache[zone].iTrack); } +} - if(zone == Zone_Start) +void ButtonLogic(int activator, int type, int track) +{ + if (type == Zone_Start) { if (GetEntPropEnt(activator, Prop_Send, "m_hGroundEntity") == -1) { @@ -4758,7 +5267,7 @@ public void UsePost(int entity, int activator, int caller, UseType type, float v Shavit_StartTimer(activator, track); } - else if (zone == Zone_End && !Shavit_IsPaused(activator) && Shavit_GetTimerStatus(activator) == Timer_Running && Shavit_GetClientTrack(activator) == track) + else if (type == Zone_End && !Shavit_IsPaused(activator) && Shavit_GetTimerStatus(activator) == Timer_Running && Shavit_GetClientTrack(activator) == track) { Shavit_FinishMap(activator, track); } diff --git a/addons/sourcemod/translations/shavit-zones.phrases.txt b/addons/sourcemod/translations/shavit-zones.phrases.txt index f9ec80b9..2fc2f015 100644 --- a/addons/sourcemod/translations/shavit-zones.phrases.txt +++ b/addons/sourcemod/translations/shavit-zones.phrases.txt @@ -24,16 +24,38 @@ "#format" "{1:s},{2:s}" "en" "You have to be {1}alive{2} to set your start position." } - "SetStartNotInStartZone" - { - "#format" "{1:s},{2:s},{3:s},{4:s}" - "en" "You {1}must{2} be in the {3}start zone{4} to use set start." - } "DeleteSetStart" { "#format" "{1:s},{2:s}" "en" "Start position has been {1}deleted{2}." } + // ---------- ZoneHook ---------- // + "ZoneHook_Crosshair" + { + "en" "Entity under crosshair" + } + "ZoneHook_Tpto" + { + "en" "Teleport to (stops timer)" + } + "ZoneHook_Draw" + { + "en" "Draw beams around entity" + } + "ZoneHook_Zonetype" + { + "#format" "{1:s}" + "en" "Zone type: {1}" + } + "ZoneHook_Hooktype" + { + "#format" "{1:s},{2:s}" + "en" "Hook type: {1} ({2})" + } + "ZoneHook_Confirm" + { + "en" "Go to confirm" + } // ---------- Commands ---------- // "StageCommandAlive" { @@ -57,6 +79,11 @@ "#format" "{1:s},{2:s}" "en" "You {1}cannot{2} setup mapzones when you're dead." } + "ZonesNotSQL" + { + "#format" "{1:s},{2:s}" + "en" "You {1}cannot{2} add/edit/delete non-sql zones." + } "ZoneCustomSpawnMenuTitle" { "en" "Add custom spawn for track:" @@ -291,6 +318,15 @@ { "en" "Edit a map zone" } + "HookZone" + { + "en" "Hook a trigger, teleporter, or button." + } + "HookZone2" + { + "#format" "{1:s}" + "en" "Hook {1}" + } "ZoneEditTitle" { "en" "Choose a zone to edit:" @@ -414,7 +450,7 @@ "ZoneStageEnter" { "#format" "{1:s},{2:s},{3:d},{4:s},{5:s},{6:s}{7:s}" - "en" "{1}You have reached stage {2}{3}{4} with a time of {5}{6}{7}." + "en" "{1}Stage {2}{3}{4} @ {5}{6}{7}" } "Zone_Start" { @@ -484,4 +520,4 @@ { "en" "UNKNOWN ZONE" } -} \ No newline at end of file +} diff --git a/addons/stripper/maps/bhop_n0bs1_css.cfg b/addons/stripper/maps/bhop_n0bs1_css.cfg new file mode 100644 index 00000000..ef4e9f56 --- /dev/null +++ b/addons/stripper/maps/bhop_n0bs1_css.cfg @@ -0,0 +1,5 @@ +;; shit zone +filter: + { + "targetname" "mod_zone_start" + }