mirror of
https://github.com/shavitush/bhoptimer.git
synced 2025-12-06 18:08:26 +00:00
4710 lines
120 KiB
SourcePawn
4710 lines
120 KiB
SourcePawn
/*
|
||
* shavit's Timer - Map Zones
|
||
* by: shavit, GAMMA CASE, rtldg, KiD Fearless, Kryptanyte, carnifex, rumour, BoomShotKapow, Nuko, Technoblazed, Kxnrl, Extan, sh4hrazad, OliviaMourning
|
||
*
|
||
* 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 <http://www.gnu.org/licenses/>.
|
||
*
|
||
*/
|
||
|
||
#include <sourcemod>
|
||
#include <clientprefs>
|
||
#include <sdktools>
|
||
#include <sdkhooks>
|
||
#include <convar_class>
|
||
#include <dhooks>
|
||
|
||
#include <shavit/core>
|
||
#include <shavit/zones>
|
||
#include <shavit/physicsuntouch>
|
||
|
||
#undef REQUIRE_PLUGIN
|
||
#include <adminmenu>
|
||
#include <shavit/replay-recorder>
|
||
|
||
#undef REQUIRE_EXTENSIONS
|
||
#include <cstrike>
|
||
#include <tf2>
|
||
#include <eventqueuefix>
|
||
|
||
#pragma semicolon 1
|
||
#pragma newdecls required
|
||
|
||
#define DEBUG 0
|
||
|
||
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;
|
||
|
||
char gS_Map[PLATFORM_MAX_PATH];
|
||
|
||
enum struct zone_settings_t
|
||
{
|
||
bool bVisible;
|
||
int iRed;
|
||
int iGreen;
|
||
int iBlue;
|
||
int iAlpha;
|
||
float fWidth;
|
||
bool bFlatZone;
|
||
bool bUseVanillaSprite;
|
||
bool bNoHalo;
|
||
int iBeam;
|
||
int iHalo;
|
||
char sBeam[PLATFORM_MAX_PATH];
|
||
}
|
||
|
||
// 0 - nothing
|
||
// 1 - wait for E tap to setup first coord
|
||
// 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];
|
||
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];
|
||
bool gB_SnapToWall[MAXPLAYERS+1];
|
||
bool gB_CursorTracing[MAXPLAYERS+1];
|
||
|
||
int gI_LatestTeleportTick[MAXPLAYERS+1];
|
||
|
||
// player zone status
|
||
bool gB_InsideZone[MAXPLAYERS+1][ZONETYPES_SIZE][TRACKS_SIZE];
|
||
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_LastStage[MAXPLAYERS+1];
|
||
|
||
char gS_BeamSprite[PLATFORM_MAX_PATH];
|
||
char gS_BeamSpriteIgnoreZ[PLATFORM_MAX_PATH];
|
||
int gI_BeamSpriteIgnoreZ;
|
||
|
||
// admin menu
|
||
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_Interval = null;
|
||
Convar gCV_TeleportToStart = null;
|
||
Convar gCV_TeleportToEnd = null;
|
||
Convar gCV_AllowDrawAllZones = null;
|
||
Convar gCV_UseCustomSprite = null;
|
||
Convar gCV_Height = null;
|
||
Convar gCV_Offset = null;
|
||
Convar gCV_EnforceTracks = null;
|
||
Convar gCV_BoxOffset = null;
|
||
Convar gCV_ExtraSpawnHeight = null;
|
||
Convar gCV_PrebuiltVisualOffset = null;
|
||
|
||
Convar gCV_ForceTargetnameReset = null;
|
||
Convar gCV_ResetTargetnameMain = null;
|
||
Convar gCV_ResetTargetnameBonus = null;
|
||
Convar gCV_ResetClassnameMain = null;
|
||
Convar gCV_ResetClassnameBonus = null;
|
||
|
||
// handles
|
||
Handle gH_DrawVisible = null;
|
||
Handle gH_DrawAllZones = null;
|
||
|
||
bool gB_DrawAllZones[MAXPLAYERS+1];
|
||
Cookie gH_DrawAllZonesCookie = null;
|
||
|
||
// table prefix
|
||
char gS_MySQLPrefix[32];
|
||
|
||
// chat settings
|
||
chatstrings_t gS_ChatStrings;
|
||
|
||
// forwards
|
||
Handle gH_Forwards_EnterZone = null;
|
||
Handle gH_Forwards_LeaveZone = null;
|
||
Handle gH_Forwards_StageMessage = null;
|
||
|
||
// sdkcalls
|
||
Handle gH_PhysicsRemoveTouchedList = null;
|
||
Handle gH_CommitSuicide = null; // sourcemod always finds a way to amaze me
|
||
|
||
// dhooks
|
||
DynamicHook gH_TeleportDhook = null;
|
||
|
||
// kz support
|
||
float gF_ClimbButtonCache[MAXPLAYERS+1][TRACKS_SIZE][2][3]; // 0 - location, 1 - angles
|
||
|
||
// set start
|
||
bool gB_HasSetStart[MAXPLAYERS+1][TRACKS_SIZE];
|
||
bool gB_StartAnglesOnly[MAXPLAYERS+1][TRACKS_SIZE];
|
||
float gF_StartPos[MAXPLAYERS+1][TRACKS_SIZE][3];
|
||
float gF_StartAng[MAXPLAYERS+1][TRACKS_SIZE][3];
|
||
|
||
// modules
|
||
bool gB_Eventqueuefix = false;
|
||
bool gB_ReplayRecorder = false;
|
||
|
||
#define CZONE_VER 'b'
|
||
// custom zone stuff
|
||
Cookie gH_CustomZoneCookie = null;
|
||
int gI_ZoneDisplayType[MAXPLAYERS+1][ZONETYPES_SIZE][TRACKS_SIZE];
|
||
int gI_ZoneColor[MAXPLAYERS+1][ZONETYPES_SIZE][TRACKS_SIZE];
|
||
int gI_ZoneWidth[MAXPLAYERS+1][ZONETYPES_SIZE][TRACKS_SIZE];
|
||
|
||
int gI_LastMenuPos[MAXPLAYERS+1];
|
||
|
||
public Plugin myinfo =
|
||
{
|
||
name = "[shavit] Map Zones",
|
||
author = "shavit, GAMMA CASE, rtldg, KiD Fearless, Kryptanyte, carnifex, rumour, BoomShotKapow, Nuko, Technoblazed, Kxnrl, Extan, sh4hrazad, OliviaMourning",
|
||
description = "Map zones for shavit's bhop timer.",
|
||
version = SHAVIT_VERSION,
|
||
url = "https://github.com/shavitush/bhoptimer"
|
||
}
|
||
|
||
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);
|
||
CreateNative("Shavit_IsClientCreatingZone", Native_IsClientCreatingZone);
|
||
CreateNative("Shavit_ZoneExists", Native_ZoneExists);
|
||
CreateNative("Shavit_Zones_DeleteMap", Native_Zones_DeleteMap);
|
||
CreateNative("Shavit_SetStart", Native_SetStart);
|
||
CreateNative("Shavit_DeleteSetStart", Native_DeleteSetStart);
|
||
CreateNative("Shavit_GetClientLastStage", Native_GetClientLastStage);
|
||
CreateNative("Shavit_GetZoneTrack", Native_GetZoneTrack);
|
||
CreateNative("Shavit_GetZoneType", Native_GetZoneType);
|
||
CreateNative("Shavit_GetZoneID", Native_GetZoneID);
|
||
|
||
// registers library, check "bool LibraryExists(const char[] name)" in order to use with other plugins
|
||
RegPluginLibrary("shavit-zones");
|
||
|
||
gB_Late = late;
|
||
|
||
return APLRes_Success;
|
||
}
|
||
|
||
public void OnPluginStart()
|
||
{
|
||
LoadTranslations("shavit-common.phrases");
|
||
LoadTranslations("shavit-zones.phrases");
|
||
|
||
// game specific
|
||
gEV_Type = GetEngineVersion();
|
||
|
||
// menu
|
||
RegAdminCmd("sm_addzone", Command_Zones, ADMFLAG_RCON, "Opens the mapzones menu.");
|
||
RegAdminCmd("sm_zones", Command_Zones, ADMFLAG_RCON, "Opens the mapzones menu.");
|
||
RegAdminCmd("sm_mapzones", Command_Zones, ADMFLAG_RCON, "Opens the mapzones menu. Alias of sm_zones.");
|
||
|
||
RegAdminCmd("sm_delzone", Command_DeleteZone, ADMFLAG_RCON, "Delete a mapzone");
|
||
RegAdminCmd("sm_deletezone", Command_DeleteZone, ADMFLAG_RCON, "Delete a mapzone");
|
||
RegAdminCmd("sm_deleteallzones", Command_DeleteAllZones, ADMFLAG_RCON, "Delete all mapzones");
|
||
|
||
RegAdminCmd("sm_modifier", Command_Modifier, ADMFLAG_RCON, "Changes the axis modifier for the zone editor. Usage: sm_modifier <number>");
|
||
|
||
RegAdminCmd("sm_addspawn", Command_AddSpawn, ADMFLAG_RCON, "Adds a custom spawn location");
|
||
RegAdminCmd("sm_delspawn", Command_DelSpawn, ADMFLAG_RCON, "Deletes a custom spawn location");
|
||
|
||
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_tptozone", Command_TpToZone, ADMFLAG_RCON, "Teleport to a zone");
|
||
|
||
RegAdminCmd("sm_reloadzonesettings", Command_ReloadZoneSettings, ADMFLAG_ROOT, "Reloads the zone settings.");
|
||
|
||
RegConsoleCmd("sm_stages", Command_Stages, "Opens the stage menu. Usage: sm_stages [stage #]");
|
||
RegConsoleCmd("sm_stage", Command_Stages, "Opens the stage menu. Usage: sm_stage [stage #]");
|
||
RegConsoleCmd("sm_s", Command_Stages, "Opens the stage menu. Usage: sm_s [stage #]");
|
||
|
||
RegConsoleCmd("sm_set", Command_SetStart, "Set current position as spawn location in start zone.");
|
||
RegConsoleCmd("sm_setstart", Command_SetStart, "Set current position as spawn location in start zone.");
|
||
RegConsoleCmd("sm_ss", Command_SetStart, "Set current position as spawn location in start zone.");
|
||
RegConsoleCmd("sm_sp", Command_SetStart, "Set current position as spawn location in start zone.");
|
||
RegConsoleCmd("sm_startpoint", Command_SetStart, "Set current position as spawn location in start zone.");
|
||
|
||
RegConsoleCmd("sm_deletestart", Command_DeleteSetStart, "Deletes the custom set start position.");
|
||
RegConsoleCmd("sm_deletesetstart", Command_DeleteSetStart, "Deletes the custom set start position.");
|
||
RegConsoleCmd("sm_delss", Command_DeleteSetStart, "Deletes the custom set start position.");
|
||
RegConsoleCmd("sm_delsp", Command_DeleteSetStart, "Deletes the custom set start position.");
|
||
|
||
RegConsoleCmd("sm_drawallzones", Command_DrawAllZones, "Toggles drawing all zones.");
|
||
RegConsoleCmd("sm_drawzones", Command_DrawAllZones, "Toggles drawing all zones.");
|
||
gH_DrawAllZonesCookie = new Cookie("shavit_drawallzones", "Draw all zones cookie", CookieAccess_Protected);
|
||
|
||
RegConsoleCmd("sm_czone", Command_CustomZones, "Customize start and end zone for each track");
|
||
RegConsoleCmd("sm_czones", Command_CustomZones, "Customize start and end zone for each track");
|
||
RegConsoleCmd("sm_customzones", Command_CustomZones, "Customize start and end zone for each track");
|
||
|
||
gH_CustomZoneCookie = new Cookie("shavit_customzones", "Cookie for storing custom zone stuff", CookieAccess_Private);
|
||
|
||
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);
|
||
}
|
||
|
||
// events
|
||
if(gEV_Type == Engine_TF2)
|
||
{
|
||
HookEvent("teamplay_round_start", Round_Start);
|
||
}
|
||
else
|
||
{
|
||
HookEvent("round_start", Round_Start);
|
||
}
|
||
|
||
HookEvent("player_spawn", Player_Spawn);
|
||
|
||
// 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_StageMessage = CreateGlobalForward("Shavit_OnStageMessage", ET_Event, Param_Cell, Param_Cell, Param_String, Param_Cell);
|
||
|
||
// cvars and stuff
|
||
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);
|
||
gCV_AllowDrawAllZones = new Convar("shavit_zones_allowdrawallzones", "1", "Allow players to use !drawallzones to see all zones regardless of zone visibility settings.\n0 - nobody can use !drawallzones\n1 - admins (sm_zones access) can use !drawallzones\n2 - anyone can use !drawallzones", 0, true, 0.0, true, 2.0);
|
||
gCV_UseCustomSprite = new Convar("shavit_zones_usecustomsprite", "1", "Use custom sprite for zone drawing?\nSee `configs/shavit-zones.cfg`.\n0 - Disabled\n1 - Enabled", 0, true, 0.0, true, 1.0);
|
||
gCV_Height = new Convar("shavit_zones_height", "128.0", "Height to use for the start zone.", 0, true, 0.0, false);
|
||
gCV_Offset = new Convar("shavit_zones_offset", "1.0", "When calculating a zone's *VISUAL* box, by how many units, should we scale it to the center?\n0.0 - no downscaling. Values above 0 will scale it inward and negative numbers will scale it outwards.\nAdjust this value if the zones clip into walls.");
|
||
gCV_EnforceTracks = new Convar("shavit_zones_enforcetracks", "1", "Enforce zone tracks upon entry?\n0 - allow every zone except for start/end to affect users on every zone.\n1 - require the user's track to match the zone's track.", 0, true, 0.0, true, 1.0);
|
||
gCV_BoxOffset = new Convar("shavit_zones_box_offset", "16", "Offset zone trigger boxes by this many unit\n0 - matches players bounding box\n16 - matches players center");
|
||
gCV_ExtraSpawnHeight = new Convar("shavit_zones_extra_spawn_height", "0.0", "YOU DONT NEED TO TOUCH THIS USUALLY. FIX YOUR ACTUAL ZONES.\nUsed to fix some shit prebuilt zones that are in the ground like bhop_strafecontrol");
|
||
gCV_PrebuiltVisualOffset = new Convar("shavit_zones_prebuilt_visual_offset", "0", "YOU DONT NEED TO TOUCH THIS USUALLY.\nUsed to fix the VISUAL beam offset for prebuilt zones on a map.\nExample maps you'd want to use 16 on: bhop_tranquility and bhop_amaranthglow");
|
||
|
||
gCV_ForceTargetnameReset = new Convar("shavit_zones_forcetargetnamereset", "0", "Reset the player's targetname upon timer start?\nRecommended to leave disabled. Enable via per-map configs when necessary.\n0 - Disabled\n1 - Enabled", 0, true, 0.0, true, 1.0);
|
||
gCV_ResetTargetnameMain = new Convar("shavit_zones_resettargetname_main", "", "What targetname 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_ResetTargetnameBonus = new Convar("shavit_zones_resettargetname_bonus", "", "What targetname 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_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_Interval.AddChangeHook(OnConVarChanged);
|
||
gCV_UseCustomSprite.AddChangeHook(OnConVarChanged);
|
||
gCV_Offset.AddChangeHook(OnConVarChanged);
|
||
gCV_PrebuiltVisualOffset.AddChangeHook(OnConVarChanged);
|
||
gCV_BoxOffset.AddChangeHook(OnConVarChanged);
|
||
|
||
Convar.AutoExecConfig();
|
||
|
||
LoadDHooks();
|
||
|
||
// misc cvars
|
||
sv_gravity = FindConVar("sv_gravity");
|
||
|
||
for(int i = 0; i < ZONETYPES_SIZE; i++)
|
||
{
|
||
for(int j = 0; j < TRACKS_SIZE; j++)
|
||
{
|
||
gA_ZoneSettings[i][j].bVisible = true;
|
||
gA_ZoneSettings[i][j].iRed = 255;
|
||
gA_ZoneSettings[i][j].iGreen = 255;
|
||
gA_ZoneSettings[i][j].iBlue = 255;
|
||
gA_ZoneSettings[i][j].iAlpha = 255;
|
||
gA_ZoneSettings[i][j].fWidth = 2.0;
|
||
gA_ZoneSettings[i][j].bFlatZone = false;
|
||
}
|
||
}
|
||
|
||
for(int i = 0; i < sizeof(gI_EntityZone); i++)
|
||
{
|
||
gI_EntityZone[i] = -1;
|
||
}
|
||
|
||
gB_ReplayRecorder = LibraryExists("shavit-replay-recorder");
|
||
gB_Eventqueuefix = LibraryExists("eventqueuefix");
|
||
|
||
if (gB_Late)
|
||
{
|
||
Shavit_OnChatConfigLoaded();
|
||
Shavit_OnDatabaseLoaded();
|
||
|
||
for(int i = 1; i <= MaxClients; i++)
|
||
{
|
||
if (IsValidClient(i))
|
||
{
|
||
OnClientConnected(i);
|
||
OnClientPutInServer(i);
|
||
|
||
if (AreClientCookiesCached(i) && !IsFakeClient(i))
|
||
{
|
||
OnClientCookiesCached(i);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
public void OnPluginEnd()
|
||
{
|
||
UnloadZones(0);
|
||
}
|
||
|
||
void LoadDHooks()
|
||
{
|
||
Handle hGameData = LoadGameConfigFile("shavit.games");
|
||
|
||
if (hGameData == null)
|
||
{
|
||
SetFailState("Failed to load shavit gamedata");
|
||
}
|
||
|
||
LoadPhysicsUntouch(hGameData);
|
||
|
||
if (gEV_Type == Engine_CSGO)
|
||
{
|
||
StartPrepSDKCall(SDKCall_Entity);
|
||
}
|
||
else
|
||
{
|
||
StartPrepSDKCall(SDKCall_Static);
|
||
}
|
||
|
||
if (!PrepSDKCall_SetFromConf(hGameData, SDKConf_Signature, "PhysicsRemoveTouchedList"))
|
||
{
|
||
SetFailState("Failed to find \"PhysicsRemoveTouchedList\" signature!");
|
||
}
|
||
|
||
if (gEV_Type != Engine_CSGO)
|
||
{
|
||
PrepSDKCall_AddParameter(SDKType_CBaseEntity, SDKPass_Pointer);
|
||
}
|
||
|
||
gH_PhysicsRemoveTouchedList = EndPrepSDKCall();
|
||
|
||
if (!gH_PhysicsRemoveTouchedList)
|
||
{
|
||
SetFailState("Failed to create sdkcall to \"PhysicsRemoveTouchedList\"!");
|
||
}
|
||
|
||
delete hGameData;
|
||
|
||
hGameData = LoadGameConfigFile("sdktools.games");
|
||
if (hGameData == null)
|
||
{
|
||
SetFailState("Failed to load sdktools gamedata");
|
||
}
|
||
|
||
int iOffset = GameConfGetOffset(hGameData, "Teleport");
|
||
if (iOffset == -1)
|
||
{
|
||
SetFailState("Couldn't get the offset for \"Teleport\"!");
|
||
}
|
||
|
||
gH_TeleportDhook = new DynamicHook(iOffset, HookType_Entity, ReturnType_Void, ThisPointer_CBaseEntity);
|
||
|
||
gH_TeleportDhook.AddParam(HookParamType_VectorPtr);
|
||
gH_TeleportDhook.AddParam(HookParamType_VectorPtr);
|
||
gH_TeleportDhook.AddParam(HookParamType_VectorPtr);
|
||
if (GetEngineVersion() == Engine_CSGO)
|
||
{
|
||
gH_TeleportDhook.AddParam(HookParamType_Bool);
|
||
}
|
||
|
||
StartPrepSDKCall(SDKCall_Entity);
|
||
PrepSDKCall_SetFromConf(hGameData, SDKConf_Virtual, "CommitSuicide");
|
||
PrepSDKCall_AddParameter(SDKType_Bool, SDKPass_ByValue); // explode
|
||
PrepSDKCall_AddParameter(SDKType_Bool, SDKPass_ByValue); // force
|
||
if (!(gH_CommitSuicide = EndPrepSDKCall()))
|
||
{
|
||
SetFailState("Failed to create sdkcall to \"CommitSuicide\"");
|
||
}
|
||
|
||
delete hGameData;
|
||
}
|
||
|
||
public void OnAllPluginsLoaded()
|
||
{
|
||
// admin menu
|
||
if(LibraryExists("adminmenu") && ((gH_AdminMenu = GetAdminTopMenu()) != null))
|
||
{
|
||
OnAdminMenuReady(gH_AdminMenu);
|
||
}
|
||
}
|
||
|
||
public void OnLibraryAdded(const char[] name)
|
||
{
|
||
if (strcmp(name, "adminmenu") == 0)
|
||
{
|
||
if ((gH_AdminMenu = GetAdminTopMenu()) != null)
|
||
{
|
||
OnAdminMenuReady(gH_AdminMenu);
|
||
}
|
||
}
|
||
else if (StrEqual(name, "shavit-replay-recorder"))
|
||
{
|
||
gB_ReplayRecorder = true;
|
||
}
|
||
else if (StrEqual(name, "eventqueuefix"))
|
||
{
|
||
gB_Eventqueuefix = true;
|
||
}
|
||
}
|
||
|
||
public void OnLibraryRemoved(const char[] name)
|
||
{
|
||
if (strcmp(name, "adminmenu") == 0)
|
||
{
|
||
gH_AdminMenu = null;
|
||
gH_TimerCommands = INVALID_TOPMENUOBJECT;
|
||
}
|
||
else if (StrEqual(name, "shavit-replay-recorder"))
|
||
{
|
||
gB_ReplayRecorder = false;
|
||
}
|
||
else if (StrEqual(name, "eventqueuefix"))
|
||
{
|
||
gB_Eventqueuefix = false;
|
||
}
|
||
}
|
||
|
||
public void OnConVarChanged(ConVar convar, const char[] oldValue, const char[] newValue)
|
||
{
|
||
if(convar == gCV_Interval)
|
||
{
|
||
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);
|
||
}
|
||
else if ((convar == gCV_Offset || convar == gCV_PrebuiltVisualOffset) && gI_MapZones > 0)
|
||
{
|
||
for(int i = 0; i < gI_MapZones; i++)
|
||
{
|
||
if(!gA_ZoneCache[i].bZoneInitialized)
|
||
{
|
||
continue;
|
||
}
|
||
|
||
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))
|
||
{
|
||
LoadZoneSettings();
|
||
}
|
||
else if (convar == gCV_BoxOffset)
|
||
{
|
||
CreateZoneEntities(false);
|
||
}
|
||
}
|
||
|
||
public void OnAdminMenuCreated(Handle topmenu)
|
||
{
|
||
if(gH_AdminMenu == null || (topmenu == gH_AdminMenu && gH_TimerCommands != INVALID_TOPMENUOBJECT))
|
||
{
|
||
return;
|
||
}
|
||
|
||
if ((gH_TimerCommands = gH_AdminMenu.FindCategory("Timer Commands")) != INVALID_TOPMENUOBJECT)
|
||
{
|
||
return;
|
||
}
|
||
|
||
gH_TimerCommands = gH_AdminMenu.AddCategory("Timer Commands", CategoryHandler, "shavit_admin", ADMFLAG_RCON);
|
||
}
|
||
|
||
public void CategoryHandler(Handle topmenu, TopMenuAction action, TopMenuObject object_id, int param, char[] buffer, int maxlength)
|
||
{
|
||
if(action == TopMenuAction_DisplayTitle)
|
||
{
|
||
FormatEx(buffer, maxlength, "%T:", "TimerCommands", param);
|
||
}
|
||
else if(action == TopMenuAction_DisplayOption)
|
||
{
|
||
FormatEx(buffer, maxlength, "%T", "TimerCommands", param);
|
||
}
|
||
}
|
||
|
||
public void OnAdminMenuReady(Handle topmenu)
|
||
{
|
||
if((gH_AdminMenu = GetAdminTopMenu()) != null)
|
||
{
|
||
if(gH_TimerCommands == INVALID_TOPMENUOBJECT)
|
||
{
|
||
gH_TimerCommands = gH_AdminMenu.FindCategory("Timer Commands");
|
||
|
||
if(gH_TimerCommands == INVALID_TOPMENUOBJECT)
|
||
{
|
||
OnAdminMenuCreated(topmenu);
|
||
}
|
||
}
|
||
|
||
gH_AdminMenu.AddItem("sm_zones", AdminMenu_Zones, gH_TimerCommands, "sm_zones", ADMFLAG_RCON);
|
||
gH_AdminMenu.AddItem("sm_deletezone", AdminMenu_DeleteZone, gH_TimerCommands, "sm_deletezone", ADMFLAG_RCON);
|
||
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);
|
||
}
|
||
}
|
||
|
||
public void AdminMenu_Zones(Handle topmenu, TopMenuAction action, TopMenuObject object_id, int param, char[] buffer, int maxlength)
|
||
{
|
||
if(action == TopMenuAction_DisplayOption)
|
||
{
|
||
FormatEx(buffer, maxlength, "%T", "AddMapZone", param);
|
||
}
|
||
else if(action == TopMenuAction_SelectOption)
|
||
{
|
||
Command_Zones(param, 0);
|
||
}
|
||
}
|
||
|
||
public void AdminMenu_DeleteZone(Handle topmenu, TopMenuAction action, TopMenuObject object_id, int param, char[] buffer, int maxlength)
|
||
{
|
||
if(action == TopMenuAction_DisplayOption)
|
||
{
|
||
FormatEx(buffer, maxlength, "%T", "DeleteMapZone", param);
|
||
}
|
||
else if(action == TopMenuAction_SelectOption)
|
||
{
|
||
Command_DeleteZone(param, 0);
|
||
}
|
||
}
|
||
|
||
public void AdminMenu_DeleteAllZones(Handle topmenu, TopMenuAction action, TopMenuObject object_id, int param, char[] buffer, int maxlength)
|
||
{
|
||
if(action == TopMenuAction_DisplayOption)
|
||
{
|
||
FormatEx(buffer, maxlength, "%T", "DeleteAllMapZone", param);
|
||
}
|
||
else if(action == TopMenuAction_SelectOption)
|
||
{
|
||
Command_DeleteAllZones(param, 0);
|
||
}
|
||
}
|
||
|
||
public void AdminMenu_ZoneEdit(Handle topmenu, TopMenuAction action, TopMenuObject object_id, int param, char[] buffer, int maxlength)
|
||
{
|
||
if(action == TopMenuAction_DisplayOption)
|
||
{
|
||
FormatEx(buffer, maxlength, "%T", "ZoneEdit", param);
|
||
}
|
||
else if(action == TopMenuAction_SelectOption)
|
||
{
|
||
Reset(param);
|
||
OpenEditMenu(param);
|
||
}
|
||
}
|
||
|
||
public void AdminMenu_TpToZone(Handle topmenu, TopMenuAction action, TopMenuObject object_id, int param, char[] buffer, int maxlength)
|
||
{
|
||
if (action == TopMenuAction_DisplayOption)
|
||
{
|
||
FormatEx(buffer, maxlength, "%T", "TpToZone", param);
|
||
}
|
||
else if (action == TopMenuAction_SelectOption)
|
||
{
|
||
OpenTpToZoneMenu(param);
|
||
}
|
||
}
|
||
|
||
public int Native_ZoneExists(Handle handler, int numParams)
|
||
{
|
||
return (GetZoneIndex(GetNativeCell(1), GetNativeCell(2)) != -1);
|
||
}
|
||
|
||
public int Native_GetZoneData(Handle handler, int numParams)
|
||
{
|
||
return gA_ZoneCache[GetNativeCell(1)].iZoneData;
|
||
}
|
||
|
||
public int Native_GetZoneFlags(Handle handler, int numParams)
|
||
{
|
||
return gA_ZoneCache[GetNativeCell(1)].iZoneFlags;
|
||
}
|
||
|
||
public int Native_InsideZone(Handle handler, int numParams)
|
||
{
|
||
return InsideZone(GetNativeCell(1), GetNativeCell(2), (numParams > 2) ? GetNativeCell(3) : -1);
|
||
}
|
||
|
||
public int Native_InsideZoneGetID(Handle handler, int numParams)
|
||
{
|
||
int client = GetNativeCell(1);
|
||
int iType = GetNativeCell(2);
|
||
int iTrack = GetNativeCell(3);
|
||
|
||
for(int i = 0; i < MAX_ZONES; i++)
|
||
{
|
||
if(gB_InsideZoneID[client][i] &&
|
||
gA_ZoneCache[i].iZoneType == iType &&
|
||
(gA_ZoneCache[i].iZoneTrack == iTrack || iTrack == -1))
|
||
{
|
||
SetNativeCellRef(4, i);
|
||
|
||
return true;
|
||
}
|
||
}
|
||
|
||
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)];
|
||
}
|
||
|
||
public int Native_Zones_DeleteMap(Handle handler, int numParams)
|
||
{
|
||
char sMap[PLATFORM_MAX_PATH];
|
||
GetNativeString(1, sMap, sizeof(sMap));
|
||
LowercaseString(sMap);
|
||
|
||
char sQuery[512];
|
||
FormatEx(sQuery, sizeof(sQuery), "DELETE FROM %smapzones WHERE map = '%s';", gS_MySQLPrefix, sMap);
|
||
gH_SQL.Query2(SQL_DeleteMap_Callback, sQuery, StrEqual(gS_Map, sMap, false), DBPrio_High);
|
||
return 1;
|
||
}
|
||
|
||
public void SQL_DeleteMap_Callback(Database db, DBResultSet results, const char[] error, any data)
|
||
{
|
||
if(results == null)
|
||
{
|
||
LogError("Timer (zones deletemap) SQL query failed. Reason: %s", error);
|
||
|
||
return;
|
||
}
|
||
|
||
if(view_as<bool>(data))
|
||
{
|
||
OnMapStart();
|
||
}
|
||
}
|
||
|
||
bool InsideZone(int client, int type, int track)
|
||
{
|
||
if(track != -1)
|
||
{
|
||
return gB_InsideZone[client][type][track];
|
||
}
|
||
else
|
||
{
|
||
for(int i = 0; i < TRACKS_SIZE; i++)
|
||
{
|
||
if(gB_InsideZone[client][type][i])
|
||
{
|
||
return true;
|
||
}
|
||
}
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
public int Native_IsClientCreatingZone(Handle handler, int numParams)
|
||
{
|
||
return (gI_MapStep[GetNativeCell(1)] != 0);
|
||
}
|
||
|
||
public int Native_SetStart(Handle handler, int numParams)
|
||
{
|
||
SetStart(GetNativeCell(1), GetNativeCell(2), view_as<bool>(GetNativeCell(3)));
|
||
return 1;
|
||
}
|
||
|
||
public int Native_DeleteSetStart(Handle handler, int numParams)
|
||
{
|
||
DeleteSetStart(GetNativeCell(1), GetNativeCell(2));
|
||
return 1;
|
||
}
|
||
|
||
public int Native_GetClientLastStage(Handle plugin, int numParams)
|
||
{
|
||
return gI_LastStage[GetNativeCell(1)];
|
||
}
|
||
|
||
public any Native_GetZoneTrack(Handle plugin, int numParams)
|
||
{
|
||
int zoneid = GetNativeCell(1);
|
||
return gA_ZoneCache[zoneid].iZoneTrack;
|
||
}
|
||
|
||
public any Native_GetZoneType(Handle plugin, int numParams)
|
||
{
|
||
int zoneid = GetNativeCell(1);
|
||
return gA_ZoneCache[zoneid].iZoneType;
|
||
}
|
||
|
||
public any Native_GetZoneID(Handle plugin, int numParams)
|
||
{
|
||
int entity = GetNativeCell(1);
|
||
return gI_EntityZone[entity];
|
||
}
|
||
|
||
bool JumpToZoneType(KeyValues kv, int type, int track)
|
||
{
|
||
static const char config_keys[ZONETYPES_SIZE][2][50] = {
|
||
{"Start", ""},
|
||
{"End", ""},
|
||
{"Glitch_Respawn", "Glitch Respawn"},
|
||
{"Glitch_Stop", "Glitch Stop"},
|
||
{"Glitch_Slay", "Glitch Slay"},
|
||
{"Freestyle", ""},
|
||
{"Custom Speed Limit", "Nolimit"},
|
||
{"Teleport", ""},
|
||
{"SPAWN POINT", ""},
|
||
{"Easybhop", ""},
|
||
{"Slide", ""},
|
||
{"Airaccelerate", ""},
|
||
{"Stage", ""},
|
||
{"No Timer Gravity", ""},
|
||
{"Gravity", ""},
|
||
{"Speedmod", ""},
|
||
};
|
||
|
||
char key[4][50];
|
||
|
||
if (track == Track_Main)
|
||
{
|
||
key[0] = config_keys[type][0];
|
||
key[1] = config_keys[type][1];
|
||
}
|
||
else
|
||
{
|
||
FormatEx(key[0], sizeof(key[]), "Bonus %d %s", track, config_keys[type][0]);
|
||
if (track == Track_Bonus)
|
||
FormatEx(key[1], sizeof(key[]), "Bonus %s", config_keys[type][0]);
|
||
|
||
if (config_keys[type][0][0])
|
||
{
|
||
FormatEx(key[2], sizeof(key[]), "Bonus %d %s", track, config_keys[type][1]);
|
||
if (track == Track_Bonus)
|
||
FormatEx(key[3], sizeof(key[]), "Bonus %s", config_keys[type][1]);
|
||
}
|
||
}
|
||
|
||
for (int i = 0; i < 4; i++)
|
||
{
|
||
if (key[i][0] && kv.JumpToKey(key[i]))
|
||
{
|
||
return true;
|
||
}
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
bool LoadZonesConfig()
|
||
{
|
||
char sPath[PLATFORM_MAX_PATH];
|
||
BuildPath(Path_SM, sPath, PLATFORM_MAX_PATH, "configs/shavit-zones.cfg");
|
||
|
||
KeyValues kv = new KeyValues("shavit-zones");
|
||
|
||
if(!kv.ImportFromFile(sPath))
|
||
{
|
||
delete kv;
|
||
|
||
return false;
|
||
}
|
||
|
||
kv.JumpToKey("Sprites");
|
||
kv.GetString("beam", gS_BeamSprite, PLATFORM_MAX_PATH);
|
||
kv.GetString("beam_ignorez", gS_BeamSpriteIgnoreZ, PLATFORM_MAX_PATH, gS_BeamSprite);
|
||
|
||
char sDownloads[PLATFORM_MAX_PATH * 8];
|
||
kv.GetString("downloads", sDownloads, (PLATFORM_MAX_PATH * 8));
|
||
|
||
char sDownloadsExploded[PLATFORM_MAX_PATH][PLATFORM_MAX_PATH];
|
||
int iDownloads = ExplodeString(sDownloads, ";", sDownloadsExploded, PLATFORM_MAX_PATH, PLATFORM_MAX_PATH, false);
|
||
|
||
for(int i = 0; i < iDownloads; i++)
|
||
{
|
||
if(strlen(sDownloadsExploded[i]) > 0)
|
||
{
|
||
TrimString(sDownloadsExploded[i]);
|
||
AddFileToDownloadsTable(sDownloadsExploded[i]);
|
||
}
|
||
}
|
||
|
||
kv.GoBack();
|
||
kv.JumpToKey("Colors");
|
||
|
||
for (int type = 0; type < ZONETYPES_SIZE; type++)
|
||
{
|
||
if (type == Zone_CustomSpawn)
|
||
{
|
||
continue;
|
||
}
|
||
|
||
for (int track = 0; track < TRACKS_SIZE; track++)
|
||
{
|
||
if (JumpToZoneType(kv, type, track))
|
||
{
|
||
gA_ZoneSettings[type][track].bVisible = view_as<bool>(kv.GetNum("visible", 1));
|
||
gA_ZoneSettings[type][track].iRed = kv.GetNum("red", 255);
|
||
gA_ZoneSettings[type][track].iGreen = kv.GetNum("green", 255);
|
||
gA_ZoneSettings[type][track].iBlue = kv.GetNum("blue", 255);
|
||
gA_ZoneSettings[type][track].iAlpha = kv.GetNum("alpha", 255);
|
||
gA_ZoneSettings[type][track].fWidth = kv.GetFloat("width", 2.0);
|
||
gA_ZoneSettings[type][track].bFlatZone = view_as<bool>(kv.GetNum("flat", false));
|
||
gA_ZoneSettings[type][track].bUseVanillaSprite = view_as<bool>(kv.GetNum("vanilla_sprite", false));
|
||
gA_ZoneSettings[type][track].bNoHalo = view_as<bool>(kv.GetNum("no_halo", false));
|
||
kv.GetString("beam", gA_ZoneSettings[type][track].sBeam, sizeof(zone_settings_t::sBeam), "");
|
||
kv.GoBack();
|
||
}
|
||
else if (track > Track_Bonus)
|
||
{
|
||
// Copy bonus 1 settings to any other bonuses that are missing this zone...
|
||
gA_ZoneSettings[type][track] = gA_ZoneSettings[type][Track_Bonus];
|
||
}
|
||
}
|
||
}
|
||
|
||
delete kv;
|
||
|
||
return true;
|
||
}
|
||
|
||
void LoadZoneSettings()
|
||
{
|
||
if(!LoadZonesConfig())
|
||
{
|
||
SetFailState("Cannot open \"configs/shavit-zones.cfg\". Make sure this file exists and that the server has read permissions to it.");
|
||
}
|
||
|
||
int defaultBeam;
|
||
int defaultHalo;
|
||
int customBeam;
|
||
|
||
if(IsSource2013(gEV_Type))
|
||
{
|
||
defaultBeam = PrecacheModel("sprites/laser.vmt", true);
|
||
defaultHalo = PrecacheModel("sprites/halo01.vmt", true);
|
||
}
|
||
else
|
||
{
|
||
defaultBeam = PrecacheModel("sprites/laserbeam.vmt", true);
|
||
defaultHalo = PrecacheModel("sprites/glow01.vmt", true);
|
||
}
|
||
|
||
if(gCV_UseCustomSprite.BoolValue)
|
||
{
|
||
customBeam = PrecacheModel(gS_BeamSprite, true);
|
||
}
|
||
else
|
||
{
|
||
customBeam = defaultBeam;
|
||
}
|
||
|
||
gI_BeamSpriteIgnoreZ = PrecacheModel(gS_BeamSpriteIgnoreZ, true);
|
||
|
||
for (int i = 0; i < ZONETYPES_SIZE; i++)
|
||
{
|
||
for (int j = 0; j < TRACKS_SIZE; j++)
|
||
{
|
||
|
||
if (gA_ZoneSettings[i][j].bUseVanillaSprite)
|
||
{
|
||
gA_ZoneSettings[i][j].iBeam = defaultBeam;
|
||
}
|
||
else
|
||
{
|
||
gA_ZoneSettings[i][j].iBeam = (gA_ZoneSettings[i][j].sBeam[0] != 0)
|
||
? PrecacheModel(gA_ZoneSettings[i][j].sBeam, true)
|
||
: customBeam;
|
||
}
|
||
|
||
gA_ZoneSettings[i][j].iHalo = (gA_ZoneSettings[i][j].bNoHalo) ? 0 : defaultHalo;
|
||
}
|
||
}
|
||
}
|
||
|
||
public void OnMapStart()
|
||
{
|
||
if (!gB_PrecachedStuff)
|
||
{
|
||
GetLowercaseMapName(gS_Map);
|
||
LoadZoneSettings();
|
||
|
||
if (gEV_Type == Engine_TF2)
|
||
{
|
||
PrecacheModel("models/error.mdl");
|
||
}
|
||
else
|
||
{
|
||
PrecacheModel("models/props/cs_office/vending_machine.mdl");
|
||
}
|
||
|
||
gB_PrecachedStuff = true;
|
||
}
|
||
|
||
if(!gB_Connected)
|
||
{
|
||
return;
|
||
}
|
||
|
||
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))
|
||
{
|
||
GetStartPosition(i);
|
||
}
|
||
}
|
||
}
|
||
|
||
public void OnMapEnd()
|
||
{
|
||
gB_PrecachedStuff = false;
|
||
gB_InsertedPrebuiltZones = false;
|
||
delete gH_DrawVisible;
|
||
delete gH_DrawAllZones;
|
||
}
|
||
|
||
public void OnClientPutInServer(int client)
|
||
{
|
||
gI_LatestTeleportTick[client] = 0;
|
||
|
||
if (!IsFakeClient(client) && gH_TeleportDhook != null)
|
||
{
|
||
gH_TeleportDhook.HookEntity(Hook_Pre, client, DHooks_OnTeleport);
|
||
}
|
||
}
|
||
|
||
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)
|
||
{
|
||
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
|
||
);
|
||
}
|
||
}
|
||
|
||
public void Shavit_OnChatConfigLoaded()
|
||
{
|
||
Shavit_GetChatStringsStruct(gS_ChatStrings);
|
||
}
|
||
|
||
void ClearZone(int index)
|
||
{
|
||
gV_MapZones[index][0] = NULL_VECTOR;
|
||
gV_MapZones[index][1] = NULL_VECTOR;
|
||
gV_Destinations[index] = NULL_VECTOR;
|
||
gV_ZoneCenter[index] = NULL_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)
|
||
{
|
||
gA_ZoneCache[index].iEntityID = -1;
|
||
gI_EntityZone[entity] = -1;
|
||
|
||
for(int i = 1; i <= MaxClients; i++)
|
||
{
|
||
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");
|
||
}
|
||
}
|
||
}
|
||
|
||
void KillAllZones()
|
||
{
|
||
char sTargetname[32];
|
||
int iEntity = -1;
|
||
|
||
while ((iEntity = FindEntityByClassname(iEntity, "trigger_multiple")) != -1)
|
||
{
|
||
GetEntPropString(iEntity, Prop_Data, "m_iName", sTargetname, sizeof(sTargetname));
|
||
|
||
bool shavit_created = (StrContains(sTargetname, "shavit_zones_") == 0);
|
||
|
||
if (shavit_created
|
||
|| (StrContains(sTargetname, "mod_zone_") == 0)
|
||
|| (StrContains(sTargetname, "climb_") == 0)
|
||
)
|
||
{
|
||
SDKUnhook(iEntity, SDKHook_StartTouchPost, StartTouchPost);
|
||
SDKUnhook(iEntity, SDKHook_EndTouchPost, EndTouchPost);
|
||
SDKUnhook(iEntity, SDKHook_TouchPost, TouchPost);
|
||
|
||
if (shavit_created)
|
||
{
|
||
AcceptEntityInput(iEntity, "Kill");
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
void UnloadZones2()
|
||
{
|
||
KillAllZones();
|
||
|
||
for (int i = 0; i < MAX_ZONES; i++)
|
||
{
|
||
ClearZone(i);
|
||
}
|
||
|
||
ClearCustomSpawn(-1);
|
||
}
|
||
|
||
// 0 - all zones
|
||
void UnloadZones(int zone)
|
||
{
|
||
if (zone != Zone_CustomSpawn)
|
||
{
|
||
for(int i = 0; i < MAX_ZONES; i++)
|
||
{
|
||
if((zone == 0 || gA_ZoneCache[i].iZoneType == zone) && gA_ZoneCache[i].bZoneInitialized)
|
||
{
|
||
KillZoneEntity(i);
|
||
ClearZone(i);
|
||
}
|
||
}
|
||
}
|
||
|
||
ClearCustomSpawn(-1);
|
||
}
|
||
|
||
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';",
|
||
(gB_MySQL)? "id":"rowid", gS_MySQLPrefix, gS_Map);
|
||
|
||
gH_SQL.Query2(SQL_RefreshZones_Callback, sQuery, 0, DBPrio_High);
|
||
}
|
||
|
||
public void SQL_RefreshZones_Callback(Database db, DBResultSet results, const char[] error, any data)
|
||
{
|
||
if(results == null)
|
||
{
|
||
LogError("Timer (zone refresh) SQL query failed. Reason: %s", error);
|
||
return;
|
||
}
|
||
|
||
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)
|
||
{
|
||
gF_CustomSpawn[track][0] = results.FetchFloat(7);
|
||
gF_CustomSpawn[track][1] = results.FetchFloat(8);
|
||
gF_CustomSpawn[track][2] = results.FetchFloat(9);
|
||
}
|
||
else
|
||
{
|
||
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
|
||
);
|
||
}
|
||
}
|
||
|
||
if (!gB_InsertedPrebuiltZones)
|
||
{
|
||
gB_InsertedPrebuiltZones = true;
|
||
|
||
char sQuery[1024];
|
||
Transaction2 hTransaction;
|
||
|
||
for (int i = 0; i < gI_MapZones; i++)
|
||
{
|
||
if (gA_ZoneCache[i].bPrebuilt)
|
||
{
|
||
if (hTransaction == null)
|
||
{
|
||
hTransaction = new Transaction2();
|
||
}
|
||
|
||
InsertPrebuiltZone(i, false, sQuery, sizeof(sQuery));
|
||
hTransaction.AddQuery2(sQuery);
|
||
}
|
||
}
|
||
|
||
if (hTransaction != null)
|
||
{
|
||
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++;
|
||
}
|
||
|
||
public void OnClientConnected(int client)
|
||
{
|
||
bool empty_InsideZone[TRACKS_SIZE];
|
||
|
||
for (int i = 0; i < ZONETYPES_SIZE; i++)
|
||
{
|
||
gB_InsideZone[client][i] = empty_InsideZone;
|
||
|
||
for(int j = 0; j < TRACKS_SIZE; j++)
|
||
{
|
||
gI_ZoneDisplayType[client][i][j] = ZoneDisplay_Default;
|
||
gI_ZoneColor[client][i][j] = ZoneColor_Default;
|
||
gI_ZoneWidth[client][i][j] = ZoneWidth_Default;
|
||
}
|
||
}
|
||
|
||
bool empty_InsideZoneID[MAX_ZONES];
|
||
gB_InsideZoneID[client] = empty_InsideZoneID;
|
||
|
||
for (int i = 0; i < TRACKS_SIZE; i++)
|
||
{
|
||
gF_ClimbButtonCache[client][i][0] = NULL_VECTOR;
|
||
gF_ClimbButtonCache[client][i][1] = NULL_VECTOR;
|
||
}
|
||
|
||
bool empty_HasSetStart[TRACKS_SIZE];
|
||
gB_HasSetStart[client] = empty_HasSetStart;
|
||
|
||
Reset(client);
|
||
|
||
gF_Modifier[client] = 16.0;
|
||
gI_GridSnap[client] = 16;
|
||
gB_SnapToWall[client] = false;
|
||
gB_CursorTracing[client] = true;
|
||
gB_DrawAllZones[client] = false;
|
||
}
|
||
|
||
public void OnClientAuthorized(int client)
|
||
{
|
||
if (gB_Connected && !IsFakeClient(client))
|
||
{
|
||
GetStartPosition(client);
|
||
}
|
||
}
|
||
|
||
public void OnClientCookiesCached(int client)
|
||
{
|
||
if (IsFakeClient(client))
|
||
{
|
||
return;
|
||
}
|
||
|
||
char setting[8];
|
||
gH_DrawAllZonesCookie.Get(client, setting, sizeof(setting));
|
||
gB_DrawAllZones[client] = view_as<bool>(StringToInt(setting));
|
||
|
||
char czone[100]; // #define MAX_VALUE_LENGTH 100
|
||
gH_CustomZoneCookie.Get(client, czone, sizeof(czone));
|
||
|
||
char ver = czone[0];
|
||
|
||
if (ver == 'a' || ver == 'b') // "version number"
|
||
{
|
||
// a = [1 + 2*2*TRACKS_SIZE + 1]; // version + ((start + end) * 2 chars * tracks) + NUL terminator
|
||
// b = [1 + ZONETYPES_SIZE*2*2 + 1] // version + (ZONETYPES_SIZE * 2 chars * (main+bonus)) + NUL terminator
|
||
|
||
int p = 1;
|
||
char c;
|
||
|
||
while ((c = czone[p++]) != 0)
|
||
{
|
||
int track = c & 0xf;
|
||
#if CZONE_VER == 'b'
|
||
if (track > Track_Bonus)
|
||
{
|
||
++p;
|
||
continue;
|
||
}
|
||
#endif
|
||
int type = (c >> 4) & 1;
|
||
gI_ZoneDisplayType[client][type][track] = (c >> 5) & 3;
|
||
c = czone[p++];
|
||
gI_ZoneColor[client][type][track] = c & 0xf;
|
||
gI_ZoneWidth[client][type][track] = (c >> 4) & 7;
|
||
}
|
||
}
|
||
}
|
||
|
||
void GetStartPosition(int client)
|
||
{
|
||
int steamID = GetSteamAccountID(client);
|
||
|
||
if(steamID == 0 || client == 0)
|
||
{
|
||
return;
|
||
}
|
||
|
||
char query[512];
|
||
|
||
FormatEx(query, 512,
|
||
"SELECT track, pos_x, pos_y, pos_z, ang_x, ang_y, ang_z, angles_only FROM %sstartpositions WHERE auth = %d AND map = '%s'",
|
||
gS_MySQLPrefix, steamID, gS_Map);
|
||
|
||
gH_SQL.Query2(SQL_GetStartPosition_Callback, query, GetClientSerial(client));
|
||
}
|
||
|
||
public void SQL_GetStartPosition_Callback(Database db, DBResultSet results, const char[] error, any data)
|
||
{
|
||
if(results == null)
|
||
{
|
||
LogError("Timer (get start position) SQL query failed. Reason: %s", error);
|
||
return;
|
||
}
|
||
|
||
int client = GetClientFromSerial(data);
|
||
|
||
if(client == 0)
|
||
{
|
||
return;
|
||
}
|
||
|
||
while(results.FetchRow())
|
||
{
|
||
int track = results.FetchInt(0);
|
||
|
||
gF_StartPos[client][track][0] = results.FetchFloat(1);
|
||
gF_StartPos[client][track][1] = results.FetchFloat(2);
|
||
gF_StartPos[client][track][2] = results.FetchFloat(3);
|
||
|
||
gF_StartAng[client][track][0] = results.FetchFloat(4);
|
||
gF_StartAng[client][track][1] = results.FetchFloat(5);
|
||
gF_StartAng[client][track][2] = results.FetchFloat(6);
|
||
|
||
gB_StartAnglesOnly[client][track] = results.FetchInt(7) > 0;
|
||
gB_HasSetStart[client][track] = true;
|
||
}
|
||
}
|
||
|
||
public Action Command_SetStart(int client, int args)
|
||
{
|
||
if(!IsValidClient(client, true))
|
||
{
|
||
Shavit_PrintToChat(client, "%T", "SetStartCommandAlive", client, gS_ChatStrings.sVariable2, gS_ChatStrings.sText);
|
||
|
||
return Plugin_Handled;
|
||
}
|
||
|
||
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);
|
||
|
||
return Plugin_Handled;
|
||
}
|
||
|
||
void SetStart(int client, int track, bool anglesonly)
|
||
{
|
||
gB_HasSetStart[client][track] = true;
|
||
gB_StartAnglesOnly[client][track] = anglesonly;
|
||
|
||
if (anglesonly)
|
||
{
|
||
gF_StartPos[client][track] = NULL_VECTOR;
|
||
}
|
||
else
|
||
{
|
||
GetClientAbsOrigin(client, gF_StartPos[client][track]);
|
||
}
|
||
|
||
GetClientEyeAngles(client, gF_StartAng[client][track]);
|
||
|
||
char query[1024];
|
||
|
||
FormatEx(query, sizeof(query),
|
||
"REPLACE INTO %sstartpositions (auth, track, map, pos_x, pos_y, pos_z, ang_x, ang_y, ang_z, angles_only) VALUES (%d, %d, '%s', %.03f, %.03f, %.03f, %.03f, %.03f, %.03f, %d);",
|
||
gS_MySQLPrefix, GetSteamAccountID(client), track, gS_Map,
|
||
gF_StartPos[client][track][0], gF_StartPos[client][track][1], gF_StartPos[client][track][2],
|
||
gF_StartAng[client][track][0], gF_StartAng[client][track][1], gF_StartAng[client][track][2], anglesonly);
|
||
|
||
gH_SQL.Query2(SQL_InsertStartPosition_Callback, query);
|
||
}
|
||
|
||
public void SQL_InsertStartPosition_Callback(Database db, DBResultSet results, const char[] error, DataPack data)
|
||
{
|
||
if(!db || !results || error[0])
|
||
{
|
||
LogError("Timer (zones) InsertStartPosition_Callback SQL query failed! (%s)", error);
|
||
|
||
return;
|
||
}
|
||
}
|
||
|
||
public Action Command_DeleteSetStart(int client, int args)
|
||
{
|
||
if(!IsValidClient(client))
|
||
{
|
||
return Plugin_Handled;
|
||
}
|
||
|
||
Shavit_PrintToChat(client, "%T", "DeleteSetStart", client, gS_ChatStrings.sWarning, gS_ChatStrings.sText);
|
||
|
||
DeleteSetStart(client, Shavit_GetClientTrack(client));
|
||
|
||
return Plugin_Handled;
|
||
}
|
||
|
||
void DeleteSetStart(int client, int track)
|
||
{
|
||
gB_HasSetStart[client][track] = false;
|
||
gF_StartPos[client][track] = view_as<float>({0.0, 0.0, 0.0});
|
||
gF_StartAng[client][track] = view_as<float>({0.0, 0.0, 0.0});
|
||
|
||
char query[512];
|
||
|
||
FormatEx(query, 512,
|
||
"DELETE FROM %sstartpositions WHERE auth = %d AND track = %d AND map = '%s';",
|
||
gS_MySQLPrefix, GetSteamAccountID(client), track, gS_Map);
|
||
|
||
gH_SQL.Query2(SQL_DeleteSetStart_Callback, query);
|
||
}
|
||
|
||
public void SQL_DeleteSetStart_Callback(Database db, DBResultSet results, const char[] error, DataPack data)
|
||
{
|
||
if(!db || !results || error[0])
|
||
{
|
||
LogError("SQL_DeleteSetStart_Callback - Query failed! (%s)", error);
|
||
|
||
return;
|
||
}
|
||
}
|
||
|
||
public Action Command_Modifier(int client, int args)
|
||
{
|
||
if(!IsValidClient(client))
|
||
{
|
||
return Plugin_Handled;
|
||
}
|
||
|
||
if(args == 0)
|
||
{
|
||
Shavit_PrintToChat(client, "%T", "ModifierCommandNoArgs", client);
|
||
|
||
return Plugin_Handled;
|
||
}
|
||
|
||
char sArg1[16];
|
||
GetCmdArg(1, sArg1, 16);
|
||
|
||
float fArg1 = StringToFloat(sArg1);
|
||
|
||
if(fArg1 <= 0.0)
|
||
{
|
||
Shavit_PrintToChat(client, "%T", "ModifierTooLow", client);
|
||
|
||
return Plugin_Handled;
|
||
}
|
||
|
||
gF_Modifier[client] = fArg1;
|
||
|
||
Shavit_PrintToChat(client, "%T %s%.01f%s.", "ModifierSet", client, gS_ChatStrings.sVariable, fArg1, gS_ChatStrings.sText);
|
||
|
||
return Plugin_Handled;
|
||
}
|
||
|
||
bool CanDrawAllZones(int client)
|
||
{
|
||
if (!gCV_AllowDrawAllZones.BoolValue)
|
||
{
|
||
return false;
|
||
}
|
||
|
||
return gCV_AllowDrawAllZones.IntValue == 2 || CheckCommandAccess(client, "sm_zones", ADMFLAG_RCON);
|
||
}
|
||
|
||
public Action Command_DrawAllZones(int client, int args)
|
||
{
|
||
if (CanDrawAllZones(client))
|
||
{
|
||
gB_DrawAllZones[client] = !gB_DrawAllZones[client];
|
||
gH_DrawAllZonesCookie.Set(client, gB_DrawAllZones[client] ? "1" : "0");
|
||
}
|
||
|
||
return Plugin_Handled;
|
||
}
|
||
|
||
public Action Command_AddSpawn(int client, int args)
|
||
{
|
||
if(!IsValidClient(client))
|
||
{
|
||
return Plugin_Handled;
|
||
}
|
||
|
||
return DisplayCustomSpawnMenu(client);
|
||
}
|
||
|
||
Action DisplayCustomSpawnMenu(int client)
|
||
{
|
||
Menu menu = new Menu(MenuHandler_AddCustomSpawn);
|
||
menu.SetTitle("%T\n ", "ZoneCustomSpawnMenuTitle", client);
|
||
|
||
for(int i = 0; i < TRACKS_SIZE; i++)
|
||
{
|
||
char sInfo[8];
|
||
IntToString(i, sInfo, 8);
|
||
|
||
char sTrack[32];
|
||
GetTrackName(client, i, sTrack, 32);
|
||
|
||
menu.AddItem(sInfo, sTrack);
|
||
}
|
||
|
||
menu.ExitButton = true;
|
||
menu.Display(client, 300);
|
||
|
||
return Plugin_Handled;
|
||
}
|
||
|
||
public int MenuHandler_AddCustomSpawn(Menu menu, MenuAction action, int param1, int param2)
|
||
{
|
||
if(action == MenuAction_Select)
|
||
{
|
||
if(!IsPlayerAlive(param1))
|
||
{
|
||
Shavit_PrintToChat(param1, "%T", "ZoneDead", param1);
|
||
|
||
return 0;
|
||
}
|
||
|
||
char sInfo[8];
|
||
menu.GetItem(param2, sInfo, 8);
|
||
int iTrack = StringToInt(sInfo);
|
||
|
||
if(!EmptyVector(gF_CustomSpawn[iTrack]))
|
||
{
|
||
char sTrack[32];
|
||
GetTrackName(param1, iTrack, sTrack, 32);
|
||
|
||
Shavit_PrintToChat(param1, "%T", "ZoneCustomSpawnExists", param1, gS_ChatStrings.sVariable, sTrack, gS_ChatStrings.sText);
|
||
|
||
return 0;
|
||
}
|
||
|
||
gI_ZoneType[param1] = Zone_CustomSpawn;
|
||
gI_ZoneTrack[param1] = iTrack;
|
||
GetClientAbsOrigin(param1, gV_Point1[param1]);
|
||
|
||
InsertZone(param1);
|
||
}
|
||
else if(action == MenuAction_End)
|
||
{
|
||
delete menu;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
public Action Command_DelSpawn(int client, int args)
|
||
{
|
||
if(!IsValidClient(client))
|
||
{
|
||
return Plugin_Handled;
|
||
}
|
||
|
||
return DisplayCustomSpawnDeleteMenu(client);
|
||
}
|
||
|
||
Action DisplayCustomSpawnDeleteMenu(int client)
|
||
{
|
||
Menu menu = new Menu(MenuHandler_DeleteCustomSpawn);
|
||
menu.SetTitle("%T\n ", "ZoneCustomSpawnMenuDeleteTitle", client);
|
||
|
||
for(int i = 0; i < TRACKS_SIZE; i++)
|
||
{
|
||
char sInfo[8];
|
||
IntToString(i, sInfo, 8);
|
||
|
||
char sTrack[32];
|
||
GetTrackName(client, i, sTrack, 32);
|
||
|
||
menu.AddItem(sInfo, sTrack, (EmptyVector(gF_CustomSpawn[i]))? ITEMDRAW_DISABLED:ITEMDRAW_DEFAULT);
|
||
}
|
||
|
||
menu.ExitButton = true;
|
||
menu.Display(client, 300);
|
||
|
||
return Plugin_Handled;
|
||
}
|
||
|
||
public int MenuHandler_DeleteCustomSpawn(Menu menu, MenuAction action, int param1, int param2)
|
||
{
|
||
if(action == MenuAction_Select)
|
||
{
|
||
char sInfo[8];
|
||
menu.GetItem(param2, sInfo, 8);
|
||
int iTrack = StringToInt(sInfo);
|
||
|
||
if(EmptyVector(gF_CustomSpawn[iTrack]))
|
||
{
|
||
char sTrack[32];
|
||
GetTrackName(param1, iTrack, sTrack, 32);
|
||
|
||
Shavit_PrintToChat(param1, "%T", "ZoneCustomSpawnMissing", param1, gS_ChatStrings.sVariable, sTrack, gS_ChatStrings.sText);
|
||
|
||
return 0;
|
||
}
|
||
|
||
gI_ZoneTrack[param1] = iTrack;
|
||
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;",
|
||
gS_MySQLPrefix, Zone_CustomSpawn, gS_Map, iTrack);
|
||
|
||
gH_SQL.Query2(SQL_DeleteCustom_Spawn_Callback, sQuery, GetClientSerial(param1));
|
||
}
|
||
else if(action == MenuAction_End)
|
||
{
|
||
delete menu;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
public void SQL_DeleteCustom_Spawn_Callback(Database db, DBResultSet results, const char[] error, any data)
|
||
{
|
||
if(results == null)
|
||
{
|
||
LogError("Timer (custom spawn delete) SQL query failed. Reason: %s", error);
|
||
|
||
return;
|
||
}
|
||
|
||
int client = GetClientFromSerial(data);
|
||
|
||
if(client == 0)
|
||
{
|
||
return;
|
||
}
|
||
|
||
ClearCustomSpawn(gI_ZoneTrack[client]);
|
||
Shavit_PrintToChat(client, "%T", "ZoneCustomSpawnDelete", client);
|
||
}
|
||
|
||
void ClearCustomSpawn(int track)
|
||
{
|
||
if(track != -1)
|
||
{
|
||
gF_CustomSpawn[track] = NULL_VECTOR;
|
||
|
||
return;
|
||
}
|
||
|
||
for(int i = 0; i < TRACKS_SIZE; i++)
|
||
{
|
||
gF_CustomSpawn[i] = NULL_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))
|
||
{
|
||
return Plugin_Handled;
|
||
}
|
||
|
||
return OpenTpToZoneMenu(client);
|
||
}
|
||
|
||
public Action Command_ZoneEdit(int client, int args)
|
||
{
|
||
if(!IsValidClient(client))
|
||
{
|
||
return Plugin_Handled;
|
||
}
|
||
|
||
Reset(client);
|
||
|
||
return OpenEditMenu(client);
|
||
}
|
||
|
||
public Action Command_ReloadZoneSettings(int client, int args)
|
||
{
|
||
LoadZoneSettings();
|
||
|
||
ReplyToCommand(client, "Reloaded zone settings.");
|
||
|
||
return Plugin_Handled;
|
||
}
|
||
|
||
public Action Command_Stages(int client, int args)
|
||
{
|
||
if(!IsValidClient(client))
|
||
{
|
||
return Plugin_Handled;
|
||
}
|
||
|
||
if(!IsPlayerAlive(client))
|
||
{
|
||
Shavit_PrintToChat(client, "%T", "StageCommandAlive", client, gS_ChatStrings.sVariable2, gS_ChatStrings.sText);
|
||
|
||
return Plugin_Handled;
|
||
}
|
||
|
||
int iStage = -1;
|
||
char sCommand[16];
|
||
GetCmdArg(0, sCommand, 16);
|
||
|
||
if ('0' <= sCommand[4] <= '9')
|
||
{
|
||
iStage = sCommand[4] - '0';
|
||
}
|
||
else if (args > 0)
|
||
{
|
||
char arg1[8];
|
||
GetCmdArg(1, arg1, 8);
|
||
iStage = StringToInt(arg1);
|
||
}
|
||
|
||
if (iStage > -1)
|
||
{
|
||
for(int i = 0; i < gI_MapZones; i++)
|
||
{
|
||
if(gA_ZoneCache[i].bZoneInitialized && gA_ZoneCache[i].iZoneType == Zone_Stage && gA_ZoneCache[i].iZoneData == iStage)
|
||
{
|
||
Shavit_StopTimer(client);
|
||
if(!EmptyVector(gV_Destinations[i]))
|
||
{
|
||
TeleportEntity(client, gV_Destinations[i], NULL_VECTOR, view_as<float>({0.0, 0.0, 0.0}));
|
||
}
|
||
else
|
||
{
|
||
TeleportEntity(client, gV_ZoneCenter[i], NULL_VECTOR, view_as<float>({0.0, 0.0, 0.0}));
|
||
}
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
Menu menu = new Menu(MenuHandler_SelectStage);
|
||
menu.SetTitle("%T", "ZoneMenuStage", client);
|
||
|
||
char sDisplay[64];
|
||
|
||
for(int i = 0; i < gI_MapZones; i++)
|
||
{
|
||
if(gA_ZoneCache[i].bZoneInitialized && gA_ZoneCache[i].iZoneType == Zone_Stage)
|
||
{
|
||
char sTrack[32];
|
||
GetTrackName(client, gA_ZoneCache[i].iZoneTrack, sTrack, 32);
|
||
|
||
FormatEx(sDisplay, 64, "#%d - %T (%s)", (i + 1), "ZoneSetStage", client, gA_ZoneCache[i].iZoneData, sTrack);
|
||
|
||
char sInfo[8];
|
||
IntToString(i, sInfo, 8);
|
||
|
||
menu.AddItem(sInfo, sDisplay);
|
||
}
|
||
}
|
||
|
||
menu.ExitButton = true;
|
||
menu.Display(client, MENU_TIME_FOREVER);
|
||
}
|
||
|
||
return Plugin_Handled;
|
||
}
|
||
|
||
public int MenuHandler_SelectStage(Menu menu, MenuAction action, int param1, int param2)
|
||
{
|
||
if(action == MenuAction_Select)
|
||
{
|
||
char sInfo[8];
|
||
menu.GetItem(param2, sInfo, 8);
|
||
int iIndex = StringToInt(sInfo);
|
||
|
||
Shavit_StopTimer(param1);
|
||
|
||
if(!EmptyVector(gV_Destinations[iIndex]))
|
||
{
|
||
TeleportEntity(param1, gV_Destinations[iIndex], NULL_VECTOR, view_as<float>({0.0, 0.0, 0.0}));
|
||
}
|
||
else
|
||
{
|
||
TeleportEntity(param1, gV_ZoneCenter[iIndex], NULL_VECTOR, view_as<float>({0.0, 0.0, 0.0}));
|
||
}
|
||
}
|
||
else if(action == MenuAction_End)
|
||
{
|
||
delete menu;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
public Action Command_Zones(int client, int args)
|
||
{
|
||
if(!IsValidClient(client))
|
||
{
|
||
return Plugin_Handled;
|
||
}
|
||
|
||
if(!IsPlayerAlive(client))
|
||
{
|
||
Shavit_PrintToChat(client, "%T", "ZonesCommand", client, gS_ChatStrings.sWarning, gS_ChatStrings.sText);
|
||
|
||
return Plugin_Handled;
|
||
}
|
||
|
||
Reset(client);
|
||
|
||
Menu menu = new Menu(MenuHandler_SelectZoneTrack);
|
||
menu.SetTitle("%T", "ZoneMenuTrack", client);
|
||
|
||
for(int i = 0; i < TRACKS_SIZE; i++)
|
||
{
|
||
char sInfo[8];
|
||
IntToString(i, sInfo, 8);
|
||
|
||
char sDisplay[16];
|
||
GetTrackName(client, i, sDisplay, 16);
|
||
|
||
menu.AddItem(sInfo, sDisplay);
|
||
}
|
||
|
||
menu.ExitButton = true;
|
||
menu.Display(client, 300);
|
||
|
||
return Plugin_Handled;
|
||
}
|
||
|
||
public int MenuHandler_SelectZoneTrack(Menu menu, MenuAction action, int param1, int param2)
|
||
{
|
||
if(action == MenuAction_Select)
|
||
{
|
||
char sInfo[8];
|
||
menu.GetItem(param2, sInfo, 8);
|
||
gI_ZoneTrack[param1] = StringToInt(sInfo);
|
||
|
||
char sTrack[16];
|
||
GetTrackName(param1, gI_ZoneTrack[param1], sTrack, 16);
|
||
|
||
Menu submenu = new Menu(MenuHandler_SelectZoneType);
|
||
submenu.SetTitle("%T\n ", "ZoneMenuTitle", param1, sTrack);
|
||
|
||
char sZoneName[32];
|
||
|
||
for(int i = 0; i < ZONETYPES_SIZE; i++)
|
||
{
|
||
if(i == Zone_CustomSpawn)
|
||
{
|
||
continue;
|
||
}
|
||
|
||
GetZoneName(param1, i, sZoneName, sizeof(sZoneName));
|
||
|
||
IntToString(i, sInfo, 8);
|
||
submenu.AddItem(sInfo, sZoneName);
|
||
}
|
||
|
||
submenu.ExitButton = true;
|
||
submenu.Display(param1, 300);
|
||
}
|
||
else if(action == MenuAction_End)
|
||
{
|
||
delete menu;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
Action OpenTpToZoneMenu(int client, int pagepos=0)
|
||
{
|
||
Menu menu = new Menu(MenuHandler_TpToEdit);
|
||
menu.SetTitle("%T\n ", "TpToZone", client);
|
||
|
||
int newPageInterval = (gEV_Type == Engine_CSGO) ? 6 : 7;
|
||
char sDisplay[64];
|
||
FormatEx(sDisplay, 64, "%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);
|
||
menu.AddItem("-2", sDisplay);
|
||
}
|
||
|
||
char sInfo[8];
|
||
IntToString(i, sInfo, 8);
|
||
|
||
char sPrebuilt[16];
|
||
sPrebuilt = gA_ZoneCache[i].bPrebuilt ? " (prebuilt)" : "";
|
||
|
||
char sTrack[32];
|
||
GetTrackName(client, gA_ZoneCache[i].iZoneTrack, sTrack, 32);
|
||
|
||
char sZoneName[32];
|
||
GetZoneName(client, gA_ZoneCache[i].iZoneType, sZoneName, sizeof(sZoneName));
|
||
|
||
if (gA_ZoneCache[i].iZoneType == Zone_CustomSpeedLimit || gA_ZoneCache[i].iZoneType == Zone_Stage || gA_ZoneCache[i].iZoneType == Zone_Airaccelerate)
|
||
{
|
||
FormatEx(sDisplay, 64, "#%d - %s %d (%s)%s", (i + 1), sZoneName, gA_ZoneCache[i].iZoneData, sTrack, sPrebuilt);
|
||
}
|
||
else if (gA_ZoneCache[i].iZoneType == Zone_Gravity || gA_ZoneCache[i].iZoneType == Zone_Speedmod)
|
||
{
|
||
FormatEx(sDisplay, 64, "#%d - %s %.2f (%s)%s", (i + 1), sZoneName, gA_ZoneCache[i].iZoneData, sTrack, sPrebuilt);
|
||
}
|
||
else
|
||
{
|
||
FormatEx(sDisplay, 64, "#%d - %s (%s)%s", (i + 1), sZoneName, sTrack, sPrebuilt);
|
||
}
|
||
|
||
if (gB_InsideZoneID[client][i])
|
||
{
|
||
Format(sDisplay, 64, "%s %T", sDisplay, "ZoneInside", client);
|
||
}
|
||
|
||
menu.AddItem(sInfo, sDisplay, ITEMDRAW_DEFAULT);
|
||
}
|
||
|
||
menu.ExitButton = true;
|
||
menu.DisplayAt(client, pagepos, MENU_TIME_FOREVER);
|
||
|
||
return Plugin_Handled;
|
||
}
|
||
|
||
public int MenuHandler_TpToEdit(Menu menu, MenuAction action, int param1, int param2)
|
||
{
|
||
if (action == MenuAction_Select)
|
||
{
|
||
char info[8];
|
||
menu.GetItem(param2, info, 8);
|
||
|
||
int id = StringToInt(info);
|
||
|
||
switch (id)
|
||
{
|
||
case -2:
|
||
{
|
||
}
|
||
case -1:
|
||
{
|
||
Shavit_PrintToChat(param1, "%T", "ZonesMenuNoneFound", param1);
|
||
}
|
||
default:
|
||
{
|
||
Shavit_StopTimer(param1);
|
||
|
||
float fCenter[3];
|
||
fCenter[0] = gV_ZoneCenter[id][0];
|
||
fCenter[1] = gV_ZoneCenter[id][1];
|
||
fCenter[2] = gV_MapZones[id][0][2];
|
||
|
||
TeleportEntity(param1, fCenter, NULL_VECTOR, view_as<float>({0.0, 0.0, 0.0}));
|
||
}
|
||
}
|
||
|
||
OpenTpToZoneMenu(param1, GetMenuSelectionPosition());
|
||
}
|
||
else if (action == MenuAction_End)
|
||
{
|
||
delete menu;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
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);
|
||
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);
|
||
menu.AddItem("-2", sDisplay);
|
||
}
|
||
|
||
char sInfo[8];
|
||
IntToString(i, sInfo, 8);
|
||
|
||
char sPrebuilt[16];
|
||
sPrebuilt = gA_ZoneCache[i].bPrebuilt ? " (prebuilt)" : "";
|
||
|
||
char sTrack[32];
|
||
GetTrackName(client, gA_ZoneCache[i].iZoneTrack, sTrack, 32);
|
||
|
||
char sZoneName[32];
|
||
GetZoneName(client, gA_ZoneCache[i].iZoneType, sZoneName, sizeof(sZoneName));
|
||
|
||
if(gA_ZoneCache[i].iZoneType == Zone_CustomSpeedLimit || gA_ZoneCache[i].iZoneType == Zone_Stage || gA_ZoneCache[i].iZoneType == Zone_Airaccelerate)
|
||
{
|
||
FormatEx(sDisplay, 64, "#%d - %s %d (%s)%s", (i + 1), sZoneName, gA_ZoneCache[i].iZoneData, sTrack, sPrebuilt);
|
||
}
|
||
else if (gA_ZoneCache[i].iZoneType == Zone_Gravity || gA_ZoneCache[i].iZoneType == Zone_Speedmod)
|
||
{
|
||
FormatEx(sDisplay, 64, "#%d - %s %.2f (%s)%s", (i + 1), sZoneName, gA_ZoneCache[i].iZoneData, sTrack, sPrebuilt);
|
||
}
|
||
else
|
||
{
|
||
FormatEx(sDisplay, 64, "#%d - %s (%s)%s", (i + 1), sZoneName, sTrack, sPrebuilt);
|
||
}
|
||
|
||
if(gB_InsideZoneID[client][i])
|
||
{
|
||
Format(sDisplay, 64, "%s %T", sDisplay, "ZoneInside", client);
|
||
}
|
||
|
||
menu.AddItem(sInfo, sDisplay, gA_ZoneCache[i].bPrebuilt ? ITEMDRAW_DISABLED : ITEMDRAW_DEFAULT);
|
||
}
|
||
|
||
menu.ExitButton = true;
|
||
menu.DisplayAt(client, pos, MENU_TIME_FOREVER);
|
||
|
||
return Plugin_Handled;
|
||
}
|
||
|
||
public int MenuHandler_ZoneEdit(Menu menu, MenuAction action, int param1, int param2)
|
||
{
|
||
if(action == MenuAction_Select)
|
||
{
|
||
char info[8];
|
||
menu.GetItem(param2, info, 8);
|
||
|
||
int id = StringToInt(info);
|
||
|
||
switch(id)
|
||
{
|
||
case -2:
|
||
{
|
||
OpenEditMenu(param1, GetMenuSelectionPosition());
|
||
}
|
||
|
||
case -1:
|
||
{
|
||
Shavit_PrintToChat(param1, "%T", "ZonesMenuNoneFound", param1);
|
||
}
|
||
|
||
default:
|
||
{
|
||
// 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;
|
||
gI_ZoneID[param1] = id;
|
||
|
||
// to stop the original zone from drawing
|
||
gA_ZoneCache[id].bZoneInitialized = false;
|
||
|
||
// draw the zone edit
|
||
CreateTimer(0.1, Timer_Draw, GetClientSerial(param1), TIMER_REPEAT);
|
||
|
||
CreateEditMenu(param1);
|
||
}
|
||
}
|
||
}
|
||
else if(action == MenuAction_End)
|
||
{
|
||
delete menu;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
public Action Command_CustomZones(int client, int args)
|
||
{
|
||
if (!client) return Plugin_Handled;
|
||
|
||
OpenCustomZoneMenu(client);
|
||
|
||
return Plugin_Handled;
|
||
}
|
||
|
||
void OpenCustomZoneMenu(int client, int pos=0)
|
||
{
|
||
Menu menu = new Menu(MenuHandler_CustomZones);
|
||
menu.SetTitle("%T", "CustomZone_MainMenuTitle", client);
|
||
|
||
// Only start zone and end zone are customizable imo, why do you even want to customize the zones that arent often used/seen???
|
||
#if CZONE_VER == 'b'
|
||
for (int i = 0; i <= Track_Bonus; i++)
|
||
{
|
||
for (int j = 0; j < ZONETYPES_SIZE; j++)
|
||
#else
|
||
for (int i = 0; i < TRACKS_SIZE; i++)
|
||
{
|
||
for (int j = 0; j <= Zone_End; j++)
|
||
#endif
|
||
{
|
||
if (j != Zone_CustomSpawn)// && gA_ZoneSettings[j][i].bVisible)
|
||
{
|
||
char info[8];
|
||
FormatEx(info, sizeof(info), "%i;%i", i, j);
|
||
char trackName[32], zoneName[32], display[64];
|
||
GetTrackName(client, i, trackName, sizeof(trackName), !(CZONE_VER == 'b'));
|
||
GetZoneName(client, j, zoneName, sizeof(zoneName));
|
||
|
||
FormatEx(display, sizeof(display), "%s - %s", trackName, zoneName);
|
||
menu.AddItem(info, display);
|
||
}
|
||
}
|
||
}
|
||
|
||
menu.DisplayAt(client, pos, MENU_TIME_FOREVER);
|
||
}
|
||
|
||
public int MenuHandler_CustomZones(Menu menu, MenuAction action, int client, int position)
|
||
{
|
||
if(action == MenuAction_Select)
|
||
{
|
||
char info[8];
|
||
menu.GetItem(position, info, sizeof(info));
|
||
|
||
char exploded[2][4];
|
||
ExplodeString(info, ";", exploded, 2, 4);
|
||
|
||
int track = StringToInt(exploded[0]);
|
||
int zoneType = StringToInt(exploded[1]);
|
||
|
||
gI_LastMenuPos[client] = GetMenuSelectionPosition();
|
||
OpenSubCustomZoneMenu(client, track, zoneType);
|
||
}
|
||
else if(action == MenuAction_End)
|
||
{
|
||
delete menu;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
void OpenSubCustomZoneMenu(int client, int track, int zoneType)
|
||
{
|
||
Menu menu = new Menu(MenuHandler_SubCustomZones);
|
||
|
||
char trackName[32], zoneName[32];
|
||
GetTrackName(client, track, trackName, sizeof(trackName));
|
||
GetZoneName(client, zoneType, zoneName, sizeof(zoneName));
|
||
|
||
menu.SetTitle("%T", "CustomZone_SubMenuTitle", client, trackName, zoneName);
|
||
|
||
char info[16], display[64];
|
||
|
||
static char displayName[ZoneDisplay_Size][] =
|
||
{
|
||
"CustomZone_Default",
|
||
"CustomZone_DisplayType_Flat",
|
||
"CustomZone_DisplayType_Box",
|
||
"CustomZone_DisplayType_None",
|
||
};
|
||
|
||
static char colorName[ZoneColor_Size][] =
|
||
{
|
||
"CustomZone_Default",
|
||
"CustomZone_Color_White",
|
||
"CustomZone_Color_Red",
|
||
"CustomZone_Color_Orange",
|
||
"CustomZone_Color_Yellow",
|
||
"CustomZone_Color_Green",
|
||
"CustomZone_Color_Cyan",
|
||
"CustomZone_Color_Blue",
|
||
"CustomZone_Color_Purple",
|
||
"CustomZone_Color_Pink"
|
||
};
|
||
|
||
static char widthName[ZoneWidth_Size][] =
|
||
{
|
||
"CustomZone_Default",
|
||
"CustomZone_Width_UltraThin",
|
||
"CustomZone_Width_Thin",
|
||
"CustomZone_Width_Normal",
|
||
"CustomZone_Width_Thick"
|
||
};
|
||
|
||
FormatEx(info, sizeof(info), "%i;%i;0", track, zoneType);
|
||
FormatEx(display, sizeof(display), "%T: %T", "CustomZone_DisplayType", client, displayName[gI_ZoneDisplayType[client][zoneType][track]], client);
|
||
menu.AddItem(info, display);
|
||
|
||
FormatEx(info, sizeof(info), "%i;%i;1", track, zoneType);
|
||
FormatEx(display, sizeof(display), "%T: %T", "CustomZone_Color", client, colorName[gI_ZoneColor[client][zoneType][track]], client);
|
||
menu.AddItem(info, display);
|
||
|
||
FormatEx(info, sizeof(info), "%i;%i;2", track, zoneType);
|
||
FormatEx(display, sizeof(display), "%T: %T", "CustomZone_Width", client, widthName[gI_ZoneWidth[client][zoneType][track]], client);
|
||
menu.AddItem(info, display);
|
||
|
||
menu.ExitBackButton = true;
|
||
menu.Display(client, MENU_TIME_FOREVER);
|
||
}
|
||
|
||
void HandleCustomZoneCookie(int client)
|
||
{
|
||
char buf[100]; // #define MAX_VALUE_LENGTH 100
|
||
int p = 0;
|
||
|
||
#if CZONE_VER == 'b'
|
||
for (int type = Zone_Start; type < ZONETYPES_SIZE; type++)
|
||
{
|
||
for (int track = Track_Main; track <= Track_Bonus; track++)
|
||
#else
|
||
for (int type = Zone_Start; type <= Zone_End; type++)
|
||
{
|
||
for (int track = Track_Main; track < TRACKS_SIZE; track++)
|
||
#endif
|
||
{
|
||
if (gI_ZoneDisplayType[client][type][track] || gI_ZoneColor[client][type][track] || gI_ZoneWidth[client][type][track])
|
||
{
|
||
if (!p) buf[p++] = CZONE_VER;
|
||
// highest bit (0x80) set so we don't get a zero byte terminating the cookie early
|
||
buf[p++] = 0x80 | (gI_ZoneDisplayType[client][type][track] << 5) | (type << 4) | track;
|
||
buf[p++] = 0x80 | (gI_ZoneWidth[client][type][track] << 4) | gI_ZoneColor[client][type][track];
|
||
}
|
||
}
|
||
}
|
||
|
||
gH_CustomZoneCookie.Set(client, buf);
|
||
}
|
||
|
||
public int MenuHandler_SubCustomZones(Menu menu, MenuAction action, int client, int param2)
|
||
{
|
||
if (action == MenuAction_Select)
|
||
{
|
||
char info[16];
|
||
menu.GetItem(param2, info, sizeof(info));
|
||
|
||
char exploded[3][4];
|
||
ExplodeString(info, ";", exploded, 3, 4);
|
||
|
||
int track = StringToInt(exploded[0]);
|
||
int zoneType = StringToInt(exploded[1]);
|
||
int option = StringToInt(exploded[2]);
|
||
|
||
if (option == 0) // Display type
|
||
{
|
||
gI_ZoneDisplayType[client][zoneType][track]++;
|
||
|
||
if (gI_ZoneDisplayType[client][zoneType][track] >= ZoneDisplay_Size)
|
||
gI_ZoneDisplayType[client][zoneType][track] = ZoneDisplay_Default;
|
||
|
||
HandleCustomZoneCookie(client);
|
||
}
|
||
else if (option == 1) // Color
|
||
{
|
||
gI_ZoneColor[client][zoneType][track]++;
|
||
|
||
if (gI_ZoneColor[client][zoneType][track] >= ZoneColor_Size)
|
||
gI_ZoneColor[client][zoneType][track] = ZoneColor_Default;
|
||
|
||
HandleCustomZoneCookie(client);
|
||
}
|
||
else if (option == 2) // Width
|
||
{
|
||
gI_ZoneWidth[client][zoneType][track]++;
|
||
|
||
if (gI_ZoneWidth[client][zoneType][track] >= ZoneWidth_Size)
|
||
gI_ZoneWidth[client][zoneType][track] = ZoneWidth_Default;
|
||
|
||
HandleCustomZoneCookie(client);
|
||
}
|
||
|
||
OpenSubCustomZoneMenu(client, track, zoneType);
|
||
}
|
||
else if (action == MenuAction_Cancel && param2 == MenuCancel_ExitBack)
|
||
{
|
||
OpenCustomZoneMenu(client, gI_LastMenuPos[client]);
|
||
}
|
||
else if (action == MenuAction_End)
|
||
{
|
||
delete menu;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
public Action Command_DeleteZone(int client, int args)
|
||
{
|
||
if(!IsValidClient(client))
|
||
{
|
||
return Plugin_Handled;
|
||
}
|
||
|
||
return OpenDeleteMenu(client);
|
||
}
|
||
|
||
Action OpenDeleteMenu(int client, int pos = 0)
|
||
{
|
||
Menu menu = new Menu(MenuHandler_DeleteZone);
|
||
menu.SetTitle("%T\n ", "ZoneMenuDeleteTitle", client);
|
||
|
||
int newPageInterval = (gEV_Type == Engine_CSGO) ? 6 : 7;
|
||
char sDisplay[64];
|
||
FormatEx(sDisplay, 64, "%T", "ZoneEditRefresh", client);
|
||
menu.AddItem("-2", sDisplay);
|
||
|
||
for(int i = 0; i < gI_MapZones; i++)
|
||
{
|
||
if (gA_ZoneCache[i].bZoneInitialized)
|
||
{
|
||
if ((menu.ItemCount % newPageInterval) == 0)
|
||
{
|
||
FormatEx(sDisplay, 64, "%T", "ZoneEditRefresh", client);
|
||
menu.AddItem("-2", sDisplay);
|
||
}
|
||
|
||
char sPrebuilt[16];
|
||
sPrebuilt = gA_ZoneCache[i].bPrebuilt ? " (prebuilt)" : "";
|
||
|
||
char sTrack[32];
|
||
GetTrackName(client, gA_ZoneCache[i].iZoneTrack, sTrack, 32);
|
||
|
||
char sZoneName[32];
|
||
GetZoneName(client, gA_ZoneCache[i].iZoneType, sZoneName, sizeof(sZoneName));
|
||
|
||
if(gA_ZoneCache[i].iZoneType == Zone_CustomSpeedLimit || gA_ZoneCache[i].iZoneType == Zone_Stage || gA_ZoneCache[i].iZoneType == Zone_Airaccelerate)
|
||
{
|
||
FormatEx(sDisplay, 64, "#%d - %s %d (%s)%s", (i + 1), sZoneName, gA_ZoneCache[i].iZoneData, sTrack, sPrebuilt);
|
||
}
|
||
else if (gA_ZoneCache[i].iZoneType == Zone_Gravity || gA_ZoneCache[i].iZoneType == Zone_Speedmod)
|
||
{
|
||
FormatEx(sDisplay, 64, "#%d - %s %.2f (%s)%s", (i + 1), sZoneName, gA_ZoneCache[i].iZoneData, sTrack, sPrebuilt);
|
||
}
|
||
else
|
||
{
|
||
FormatEx(sDisplay, 64, "#%d - %s (%s)%s", (i + 1), sZoneName, sTrack, sPrebuilt);
|
||
}
|
||
|
||
char sInfo[8];
|
||
IntToString(i, sInfo, 8);
|
||
|
||
if(gB_InsideZoneID[client][i])
|
||
{
|
||
Format(sDisplay, 64, "%s %T", sDisplay, "ZoneInside", client);
|
||
}
|
||
|
||
menu.AddItem(sInfo, sDisplay, gA_ZoneCache[i].bPrebuilt ? ITEMDRAW_DISABLED : ITEMDRAW_DEFAULT);
|
||
}
|
||
}
|
||
|
||
menu.ExitButton = true;
|
||
menu.DisplayAt(client, pos, MENU_TIME_FOREVER);
|
||
|
||
return Plugin_Handled;
|
||
}
|
||
|
||
public int MenuHandler_DeleteZone(Menu menu, MenuAction action, int param1, int param2)
|
||
{
|
||
if(action == MenuAction_Select)
|
||
{
|
||
char info[8];
|
||
menu.GetItem(param2, info, 8);
|
||
|
||
int id = StringToInt(info);
|
||
|
||
switch(id)
|
||
{
|
||
case -2:
|
||
{
|
||
OpenDeleteMenu(param1, GetMenuSelectionPosition());
|
||
}
|
||
|
||
case -1:
|
||
{
|
||
Shavit_PrintToChat(param1, "%T", "ZonesMenuNoneFound", param1);
|
||
}
|
||
|
||
default:
|
||
{
|
||
char sZoneName[32];
|
||
GetZoneName(LANG_SERVER, gA_ZoneCache[id].iZoneType, sZoneName, sizeof(sZoneName));
|
||
|
||
Shavit_LogMessage("%L - deleted %s (id %d) from map `%s`.", param1, sZoneName, gA_ZoneCache[id].iDatabaseID, gS_Map);
|
||
|
||
char sQuery[256];
|
||
FormatEx(sQuery, 256, "DELETE FROM %smapzones WHERE %s = %d;", gS_MySQLPrefix, (gB_MySQL)? "id":"rowid", gA_ZoneCache[id].iDatabaseID);
|
||
|
||
DataPack hDatapack = new DataPack();
|
||
hDatapack.WriteCell(GetClientSerial(param1));
|
||
hDatapack.WriteCell(gA_ZoneCache[id].iZoneType);
|
||
|
||
gH_SQL.Query2(SQL_DeleteZone_Callback, sQuery, hDatapack);
|
||
}
|
||
}
|
||
}
|
||
else if(action == MenuAction_End)
|
||
{
|
||
delete menu;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
public void SQL_DeleteZone_Callback(Database db, DBResultSet results, const char[] error, DataPack data)
|
||
{
|
||
data.Reset();
|
||
int client = GetClientFromSerial(data.ReadCell());
|
||
int type = data.ReadCell();
|
||
|
||
delete data;
|
||
|
||
if(results == null)
|
||
{
|
||
LogError("Timer (single zone delete) SQL query failed. Reason: %s", error);
|
||
|
||
return;
|
||
}
|
||
|
||
UnloadZones(type);
|
||
RefreshZones();
|
||
|
||
if(client == 0)
|
||
{
|
||
return;
|
||
}
|
||
|
||
char sZoneName[32];
|
||
GetZoneName(client, type, sZoneName, sizeof(sZoneName));
|
||
|
||
Shavit_PrintToChat(client, "%T", "ZoneDeleteSuccessful", client, gS_ChatStrings.sVariable, sZoneName, gS_ChatStrings.sText);
|
||
}
|
||
|
||
public Action Command_DeleteAllZones(int client, int args)
|
||
{
|
||
if(!IsValidClient(client))
|
||
{
|
||
return Plugin_Handled;
|
||
}
|
||
|
||
Menu menu = new Menu(MenuHandler_DeleteAllZones);
|
||
menu.SetTitle("%T", "ZoneMenuDeleteALLTitle", client);
|
||
|
||
char sMenuItem[64];
|
||
|
||
for(int i = 1; i <= GetRandomInt(1, 4); i++)
|
||
{
|
||
FormatEx(sMenuItem, 64, "%T", "ZoneMenuNo", client);
|
||
menu.AddItem("-1", sMenuItem);
|
||
}
|
||
|
||
FormatEx(sMenuItem, 64, "%T", "ZoneMenuYes", client);
|
||
menu.AddItem("yes", sMenuItem);
|
||
|
||
for(int i = 1; i <= GetRandomInt(1, 3); i++)
|
||
{
|
||
FormatEx(sMenuItem, 64, "%T", "ZoneMenuNo", client);
|
||
menu.AddItem("-1", sMenuItem);
|
||
}
|
||
|
||
menu.ExitButton = true;
|
||
menu.Display(client, 300);
|
||
|
||
return Plugin_Handled;
|
||
}
|
||
|
||
public int MenuHandler_DeleteAllZones(Menu menu, MenuAction action, int param1, int param2)
|
||
{
|
||
if(action == MenuAction_Select)
|
||
{
|
||
char info[8];
|
||
menu.GetItem(param2, info, 8);
|
||
|
||
int iInfo = StringToInt(info);
|
||
|
||
if(iInfo == -1)
|
||
{
|
||
return 0;
|
||
}
|
||
|
||
Shavit_LogMessage("%L - deleted all zones from map `%s`.", param1, gS_Map);
|
||
|
||
char sQuery[512];
|
||
FormatEx(sQuery, sizeof(sQuery), "DELETE FROM %smapzones WHERE map = '%s';", gS_MySQLPrefix, gS_Map);
|
||
|
||
gH_SQL.Query2(SQL_DeleteAllZones_Callback, sQuery, GetClientSerial(param1));
|
||
}
|
||
else if(action == MenuAction_End)
|
||
{
|
||
delete menu;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
public void SQL_DeleteAllZones_Callback(Database db, DBResultSet results, const char[] error, any data)
|
||
{
|
||
if(results == null)
|
||
{
|
||
LogError("Timer (single zone delete) SQL query failed. Reason: %s", error);
|
||
|
||
return;
|
||
}
|
||
|
||
UnloadZones(0);
|
||
RequestFrame(ReloadPrebuiltZones);
|
||
|
||
int client = GetClientFromSerial(data);
|
||
|
||
if(client == 0)
|
||
{
|
||
return;
|
||
}
|
||
|
||
Shavit_PrintToChat(client, "%T", "ZoneDeleteAllSuccessful", client);
|
||
}
|
||
|
||
public int MenuHandler_SelectZoneType(Menu menu, MenuAction action, int param1, int param2)
|
||
{
|
||
if(action == MenuAction_Select)
|
||
{
|
||
char info[8];
|
||
menu.GetItem(param2, info, 8);
|
||
|
||
gI_ZoneType[param1] = StringToInt(info);
|
||
|
||
if (gI_ZoneType[param1] == Zone_Gravity || gI_ZoneType[param1] == Zone_Speedmod)
|
||
{
|
||
gI_ZoneData[param1] = view_as<int>(1.0);
|
||
}
|
||
|
||
ShowPanel(param1, 1);
|
||
}
|
||
else if(action == MenuAction_End)
|
||
{
|
||
delete menu;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
void Reset(int client)
|
||
{
|
||
gI_ZoneTrack[client] = Track_Main;
|
||
gI_MapStep[client] = 0;
|
||
gI_ZoneFlags[client] = 0;
|
||
gI_ZoneData[client] = 0;
|
||
gI_ZoneDatabaseID[client] = -1;
|
||
gB_WaitingForChatInput[client] = false;
|
||
gI_ZoneID[client] = -1;
|
||
|
||
gV_Point1[client] = NULL_VECTOR;
|
||
gV_Point2[client] = NULL_VECTOR;
|
||
gV_Teleport[client] = NULL_VECTOR;
|
||
gV_WallSnap[client] = NULL_VECTOR;
|
||
}
|
||
|
||
void ShowPanel(int client, int step)
|
||
{
|
||
gI_MapStep[client] = step;
|
||
|
||
if(step == 1)
|
||
{
|
||
CreateTimer(0.1, Timer_Draw, GetClientSerial(client), TIMER_REPEAT);
|
||
}
|
||
|
||
Panel pPanel = new Panel();
|
||
|
||
char sPanelText[128];
|
||
char sFirst[64];
|
||
char sSecond[64];
|
||
FormatEx(sFirst, 64, "%T", "ZoneFirst", client);
|
||
FormatEx(sSecond, 64, "%T", "ZoneSecond", client);
|
||
|
||
if(gEV_Type == Engine_TF2)
|
||
{
|
||
FormatEx(sPanelText, 128, "%T", "ZonePlaceTextTF2", client, (step == 1)? sFirst:sSecond);
|
||
}
|
||
else
|
||
{
|
||
FormatEx(sPanelText, 128, "%T", "ZonePlaceText", client, (step == 1)? sFirst:sSecond);
|
||
}
|
||
|
||
pPanel.DrawItem(sPanelText, ITEMDRAW_RAWLINE);
|
||
char sPanelItem[64];
|
||
FormatEx(sPanelItem, 64, "%T", "AbortZoneCreation", client);
|
||
pPanel.DrawItem(sPanelItem);
|
||
|
||
char sDisplay[64];
|
||
FormatEx(sDisplay, 64, "%T", "GridSnapPlus", client, gI_GridSnap[client]);
|
||
pPanel.DrawItem(sDisplay);
|
||
|
||
FormatEx(sDisplay, 64, "%T", "GridSnapMinus", client);
|
||
pPanel.DrawItem(sDisplay);
|
||
|
||
FormatEx(sDisplay, 64, "%T", "WallSnap", client, (gB_SnapToWall[client])? "ZoneSetYes":"ZoneSetNo", client);
|
||
pPanel.DrawItem(sDisplay);
|
||
|
||
FormatEx(sDisplay, 64, "%T", "CursorZone", client, (gB_CursorTracing[client])? "ZoneSetYes":"ZoneSetNo", client);
|
||
pPanel.DrawItem(sDisplay);
|
||
|
||
pPanel.Send(client, ZoneCreation_Handler, 600);
|
||
|
||
delete pPanel;
|
||
}
|
||
|
||
public int ZoneCreation_Handler(Menu menu, MenuAction action, int param1, int param2)
|
||
{
|
||
if(action == MenuAction_Select)
|
||
{
|
||
switch(param2)
|
||
{
|
||
case 1:
|
||
{
|
||
Reset(param1);
|
||
|
||
return 0;
|
||
}
|
||
|
||
case 2:
|
||
{
|
||
gI_GridSnap[param1] *= 2;
|
||
|
||
if(gI_GridSnap[param1] > 64)
|
||
{
|
||
gI_GridSnap[param1] = 1;
|
||
}
|
||
}
|
||
|
||
case 3:
|
||
{
|
||
gI_GridSnap[param1] /= 2;
|
||
|
||
if(gI_GridSnap[param1] < 1)
|
||
{
|
||
gI_GridSnap[param1] = 64;
|
||
}
|
||
}
|
||
|
||
case 4:
|
||
{
|
||
gB_SnapToWall[param1] = !gB_SnapToWall[param1];
|
||
|
||
if(gB_SnapToWall[param1])
|
||
{
|
||
gB_CursorTracing[param1] = false;
|
||
|
||
if(gI_GridSnap[param1] < 32)
|
||
{
|
||
gI_GridSnap[param1] = 32;
|
||
}
|
||
}
|
||
}
|
||
|
||
case 5:
|
||
{
|
||
gB_CursorTracing[param1] = !gB_CursorTracing[param1];
|
||
|
||
if(gB_CursorTracing[param1])
|
||
{
|
||
gB_SnapToWall[param1] = false;
|
||
}
|
||
}
|
||
}
|
||
|
||
ShowPanel(param1, gI_MapStep[param1]);
|
||
}
|
||
else if(action == MenuAction_End)
|
||
{
|
||
delete menu;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
float[] SnapToGrid(float pos[3], int grid, bool third)
|
||
{
|
||
float origin[3];
|
||
origin = pos;
|
||
|
||
origin[0] = float(RoundToNearest(pos[0] / grid) * grid);
|
||
origin[1] = float(RoundToNearest(pos[1] / grid) * grid);
|
||
|
||
if(third)
|
||
{
|
||
origin[2] = float(RoundToNearest(pos[2] / grid) * grid);
|
||
}
|
||
|
||
return origin;
|
||
}
|
||
|
||
bool SnapToWall(float pos[3], int client, float final[3])
|
||
{
|
||
bool hit = false;
|
||
|
||
float end[3];
|
||
float temp[3];
|
||
|
||
float prefinal[3];
|
||
prefinal = pos;
|
||
|
||
for(int i = 0; i < 4; i++)
|
||
{
|
||
end = pos;
|
||
|
||
int axis = (i / 2);
|
||
end[axis] += (((i % 2) == 1)? -gI_GridSnap[client]:gI_GridSnap[client]);
|
||
|
||
TR_TraceRayFilter(pos, end, MASK_PLAYERSOLID, RayType_EndPoint, TraceFilter_NoClients, client);
|
||
|
||
if(TR_DidHit())
|
||
{
|
||
TR_GetEndPosition(temp);
|
||
prefinal[axis] = temp[axis];
|
||
hit = true;
|
||
}
|
||
}
|
||
|
||
if(hit && GetVectorDistance(prefinal, pos) <= gI_GridSnap[client])
|
||
{
|
||
final = SnapToGrid(prefinal, gI_GridSnap[client], false);
|
||
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
public bool TraceFilter_NoClients(int entity, int contentsMask, any data)
|
||
{
|
||
return (entity != data && !IsValidClient(data));
|
||
}
|
||
|
||
float[] GetAimPosition(int client)
|
||
{
|
||
float pos[3];
|
||
GetClientEyePosition(client, pos);
|
||
|
||
float angles[3];
|
||
GetClientEyeAngles(client, angles);
|
||
|
||
TR_TraceRayFilter(pos, angles, MASK_PLAYERSOLID, RayType_Infinite, TraceFilter_NoClients, client);
|
||
|
||
if(TR_DidHit())
|
||
{
|
||
float end[3];
|
||
TR_GetEndPosition(end);
|
||
|
||
return SnapToGrid(end, gI_GridSnap[client], true);
|
||
}
|
||
|
||
return pos;
|
||
}
|
||
|
||
public bool TraceFilter_World(int entity, int contentsMask)
|
||
{
|
||
return (entity == 0);
|
||
}
|
||
|
||
void FillBoxMinMax(float point1[3], float point2[3], float boxmin[3], float boxmax[3])
|
||
{
|
||
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];
|
||
}
|
||
}
|
||
|
||
bool BoxesIntersect(float amin[3], float amax[3], float bmin[3], float bmax[3])
|
||
{
|
||
return (amin[0] <= bmax[0] && amax[0] >= bmin[0]) &&
|
||
(amin[1] <= bmax[1] && amax[1] >= bmin[1]) &&
|
||
(amin[2] <= bmax[2] && amax[2] >= bmin[2]);
|
||
}
|
||
|
||
bool PointInBox(float point[3], float bmin[3], float bmax[3])
|
||
{
|
||
return (bmin[0] <= point[0] <= bmax[0]) &&
|
||
(bmin[1] <= point[1] <= bmax[1]) &&
|
||
(bmin[2] <= point[2] <= bmax[2]);
|
||
}
|
||
|
||
bool InStartOrEndZone(float point1[3], float point2[3], int track, int type)
|
||
{
|
||
if (type != Zone_Start && type != Zone_End)
|
||
{
|
||
return false;
|
||
}
|
||
|
||
float amin[3], amax[3];
|
||
bool box = !IsNullVector(point2);
|
||
|
||
if (box)
|
||
{
|
||
FillBoxMinMax(point1, point2, amin, amax);
|
||
}
|
||
|
||
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))
|
||
{
|
||
continue;
|
||
}
|
||
|
||
float bmin[3], bmax[3];
|
||
FillBoxMinMax(gV_MapZones_Visual[i][0], gV_MapZones_Visual[i][7], bmin, bmax);
|
||
|
||
if (box)
|
||
{
|
||
if (BoxesIntersect(amin, amax, bmin, bmax))
|
||
{
|
||
return true;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (PointInBox(point1, bmin, bmax))
|
||
{
|
||
return true;
|
||
}
|
||
}
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
public Action Shavit_OnUserCmdPre(int client, int &buttons, int &impulse, float vel[3], float angles[3], TimerStatus status, int track, int style)
|
||
{
|
||
if(gI_MapStep[client] > 0 && gI_MapStep[client] != 3)
|
||
{
|
||
int button = (gEV_Type == Engine_TF2)? IN_ATTACK2:IN_USE;
|
||
|
||
if((buttons & button) > 0)
|
||
{
|
||
if(!gB_Button[client])
|
||
{
|
||
float vPlayerOrigin[3];
|
||
GetClientAbsOrigin(client, vPlayerOrigin);
|
||
|
||
float origin[3];
|
||
|
||
if(gB_CursorTracing[client])
|
||
{
|
||
origin = GetAimPosition(client);
|
||
}
|
||
else if(!(gB_SnapToWall[client] && SnapToWall(vPlayerOrigin, client, origin)))
|
||
{
|
||
origin = SnapToGrid(vPlayerOrigin, gI_GridSnap[client], false);
|
||
}
|
||
else
|
||
{
|
||
gV_WallSnap[client] = origin;
|
||
}
|
||
|
||
origin[2] = vPlayerOrigin[2];
|
||
|
||
if(gI_MapStep[client] == 1)
|
||
{
|
||
origin[2] += 1.0;
|
||
|
||
if (!InStartOrEndZone(origin, NULL_VECTOR, gI_ZoneTrack[client], gI_ZoneType[client]))
|
||
{
|
||
gV_Point1[client] = origin;
|
||
ShowPanel(client, 2);
|
||
}
|
||
}
|
||
else if(gI_MapStep[client] == 2)
|
||
{
|
||
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]))
|
||
{
|
||
gV_Point2[client] = origin;
|
||
gI_MapStep[client]++;
|
||
|
||
CreateEditMenu(client);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
gB_Button[client] = (buttons & button) > 0;
|
||
}
|
||
|
||
if(InsideZone(client, Zone_Slide, (gCV_EnforceTracks.BoolValue)? track:-1) && GetEntPropEnt(client, Prop_Send, "m_hGroundEntity") == -1)
|
||
{
|
||
// trace down, see if there's 8 distance or less to ground
|
||
float fPosition[3];
|
||
GetClientAbsOrigin(client, fPosition);
|
||
TR_TraceRayFilter(fPosition, view_as<float>({90.0, 0.0, 0.0}), MASK_PLAYERSOLID, RayType_Infinite, TRFilter_NoPlayers, client);
|
||
|
||
float fGroundPosition[3];
|
||
|
||
if(TR_DidHit() && TR_GetEndPosition(fGroundPosition) && GetVectorDistance(fPosition, fGroundPosition) <= 8.0)
|
||
{
|
||
float fSpeed[3];
|
||
GetEntPropVector(client, Prop_Data, "m_vecAbsVelocity", fSpeed);
|
||
|
||
fSpeed[2] = 8.0 * GetEntityGravity(client) * GetEntPropFloat(client, Prop_Data, "m_flLaggedMovementValue") * (sv_gravity.FloatValue / 800);
|
||
TeleportEntity(client, NULL_VECTOR, NULL_VECTOR, fSpeed);
|
||
}
|
||
}
|
||
|
||
return Plugin_Continue;
|
||
}
|
||
|
||
public bool TRFilter_NoPlayers(int entity, int mask, any data)
|
||
{
|
||
return (entity != view_as<int>(data) || (entity < 1 || entity > MaxClients));
|
||
}
|
||
|
||
public int CreateZoneConfirm_Handler(Menu menu, MenuAction action, int param1, int param2)
|
||
{
|
||
if(action == MenuAction_Select)
|
||
{
|
||
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;
|
||
}
|
||
|
||
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"))
|
||
{
|
||
UpdateTeleportZone(param1);
|
||
}
|
||
else if(StrEqual(sInfo, "datafromchat"))
|
||
{
|
||
gI_ZoneData[param1] = 0;
|
||
gB_WaitingForChatInput[param1] = true;
|
||
|
||
Shavit_PrintToChat(param1, "%T", "ZoneEnterDataChat", param1);
|
||
|
||
return 0;
|
||
}
|
||
else if(StrEqual(sInfo, "forcerender"))
|
||
{
|
||
gI_ZoneFlags[param1] ^= ZF_ForceRender;
|
||
}
|
||
|
||
CreateEditMenu(param1);
|
||
}
|
||
else if (action == MenuAction_Cancel)
|
||
{
|
||
if (!gB_HackyResetCheck[param1])
|
||
{
|
||
if (gI_ZoneID[param1] != -1)
|
||
{
|
||
gA_ZoneCache[gI_ZoneID[param1]].bZoneInitialized = true;
|
||
}
|
||
}
|
||
}
|
||
else if(action == MenuAction_End)
|
||
{
|
||
delete menu;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
public Action OnClientSayCommand(int client, const char[] command, const char[] sArgs)
|
||
{
|
||
if(gB_WaitingForChatInput[client] && gI_MapStep[client] == 3)
|
||
{
|
||
if (gI_ZoneType[client] == Zone_Gravity || gI_ZoneType[client] == Zone_Speedmod)
|
||
{
|
||
gI_ZoneData[client] = view_as<int>(StringToFloat(sArgs));
|
||
}
|
||
else
|
||
{
|
||
gI_ZoneData[client] = StringToInt(sArgs);
|
||
}
|
||
|
||
CreateEditMenu(client);
|
||
|
||
return Plugin_Handled;
|
||
}
|
||
|
||
return Plugin_Continue;
|
||
}
|
||
|
||
void UpdateTeleportZone(int client)
|
||
{
|
||
float vTeleport[3];
|
||
GetClientAbsOrigin(client, vTeleport);
|
||
vTeleport[2] += 2.0;
|
||
|
||
if(gI_ZoneType[client] == Zone_Stage)
|
||
{
|
||
gV_Teleport[client] = vTeleport;
|
||
|
||
Shavit_PrintToChat(client, "%T", "ZoneTeleportUpdated", client);
|
||
}
|
||
else
|
||
{
|
||
bool bInside = true;
|
||
|
||
for(int i = 0; i < 3; i++)
|
||
{
|
||
if(gV_Point1[client][i] >= vTeleport[i] == gV_Point2[client][i] >= vTeleport[i])
|
||
{
|
||
bInside = false;
|
||
}
|
||
}
|
||
|
||
if(bInside)
|
||
{
|
||
Shavit_PrintToChat(client, "%T", "ZoneTeleportInsideZone", client);
|
||
}
|
||
else
|
||
{
|
||
gV_Teleport[client] = vTeleport;
|
||
|
||
Shavit_PrintToChat(client, "%T", "ZoneTeleportUpdated", client);
|
||
}
|
||
}
|
||
}
|
||
|
||
void CreateEditMenu(int client)
|
||
{
|
||
char sTrack[32];
|
||
GetTrackName(client, gI_ZoneTrack[client], sTrack, 32);
|
||
|
||
gB_HackyResetCheck[client] = false;
|
||
Menu menu = new Menu(CreateZoneConfirm_Handler);
|
||
menu.SetTitle("%T\n%T\n ", "ZoneEditConfirm", client, "ZoneEditTrack", client, sTrack);
|
||
|
||
char sMenuItem[64];
|
||
|
||
if(gI_ZoneType[client] == Zone_Teleport)
|
||
{
|
||
if(EmptyVector(gV_Teleport[client]))
|
||
{
|
||
FormatEx(sMenuItem, 64, "%T", "ZoneSetTP", client);
|
||
menu.AddItem("-1", sMenuItem, ITEMDRAW_DISABLED);
|
||
}
|
||
else
|
||
{
|
||
FormatEx(sMenuItem, 64, "%T", "ZoneSetYes", client);
|
||
menu.AddItem("yes", sMenuItem);
|
||
}
|
||
|
||
FormatEx(sMenuItem, 64, "%T", "ZoneSetTPZone", client);
|
||
menu.AddItem("tpzone", sMenuItem);
|
||
}
|
||
else if(gI_ZoneType[client] == Zone_Stage)
|
||
{
|
||
FormatEx(sMenuItem, 64, "%T", "ZoneSetYes", client);
|
||
menu.AddItem("yes", sMenuItem);
|
||
|
||
FormatEx(sMenuItem, 64, "%T", "ZoneSetTPZone", client);
|
||
menu.AddItem("tpzone", sMenuItem);
|
||
}
|
||
else
|
||
{
|
||
FormatEx(sMenuItem, 64, "%T", "ZoneSetYes", client);
|
||
menu.AddItem("yes", sMenuItem);
|
||
}
|
||
|
||
FormatEx(sMenuItem, 64, "%T", "ZoneSetNo", client);
|
||
menu.AddItem("no", sMenuItem);
|
||
|
||
FormatEx(sMenuItem, 64, "%T", "ZoneSetAdjust", client);
|
||
menu.AddItem("adjust", sMenuItem);
|
||
|
||
FormatEx(sMenuItem, 64, "%T", "ZoneForceRender", client, ((gI_ZoneFlags[client] & ZF_ForceRender) > 0)? "+":"-");
|
||
menu.AddItem("forcerender", sMenuItem);
|
||
|
||
if(gI_ZoneType[client] == Zone_Stage)
|
||
{
|
||
FormatEx(sMenuItem, 64, "%T", "ZoneSetStage", client, gI_ZoneData[client]);
|
||
menu.AddItem("datafromchat", sMenuItem);
|
||
}
|
||
else if(gI_ZoneType[client] == Zone_Airaccelerate)
|
||
{
|
||
FormatEx(sMenuItem, 64, "%T", "ZoneSetAiraccelerate", client, gI_ZoneData[client]);
|
||
menu.AddItem("datafromchat", sMenuItem);
|
||
}
|
||
else if(gI_ZoneType[client] == Zone_CustomSpeedLimit)
|
||
{
|
||
if(gI_ZoneData[client] == 0)
|
||
{
|
||
FormatEx(sMenuItem, 64, "%T", "ZoneSetSpeedLimitUnlimited", client, gI_ZoneData[client]);
|
||
}
|
||
else
|
||
{
|
||
FormatEx(sMenuItem, 64, "%T", "ZoneSetSpeedLimit", client, gI_ZoneData[client]);
|
||
}
|
||
|
||
menu.AddItem("datafromchat", sMenuItem);
|
||
}
|
||
else if (gI_ZoneType[client] == Zone_Gravity)
|
||
{
|
||
float g = view_as<float>(gI_ZoneData[client]);
|
||
FormatEx(sMenuItem, sizeof(sMenuItem), "%T", "ZoneSetGravity", client, g);
|
||
menu.AddItem("datafromchat", sMenuItem);
|
||
}
|
||
else if (gI_ZoneType[client] == Zone_Speedmod)
|
||
{
|
||
float speed = view_as<float>(gI_ZoneData[client]);
|
||
FormatEx(sMenuItem, sizeof(sMenuItem), "%T", "ZoneSetSpeedmod", client, speed);
|
||
menu.AddItem("datafromchat", sMenuItem);
|
||
}
|
||
|
||
menu.ExitButton = true;
|
||
menu.Display(client, MENU_TIME_FOREVER);
|
||
}
|
||
|
||
void CreateAdjustMenu(int client, int page)
|
||
{
|
||
Menu hMenu = new Menu(ZoneAdjuster_Handler);
|
||
char sMenuItem[64];
|
||
hMenu.SetTitle("%T", "ZoneAdjustPosition", client);
|
||
|
||
FormatEx(sMenuItem, 64, "%T", "ZoneAdjustDone", client);
|
||
hMenu.AddItem("done", sMenuItem);
|
||
FormatEx(sMenuItem, 64, "%T", "ZoneAdjustCancel", client);
|
||
hMenu.AddItem("cancel", sMenuItem);
|
||
|
||
char sAxis[4];
|
||
strcopy(sAxis, 4, "XYZ");
|
||
|
||
char sDisplay[32];
|
||
char sInfo[16];
|
||
|
||
for(int iPoint = 1; iPoint <= 2; iPoint++)
|
||
{
|
||
for(int iAxis = 0; iAxis < 3; iAxis++)
|
||
{
|
||
for(int iState = 1; iState <= 2; iState++)
|
||
{
|
||
FormatEx(sDisplay, 32, "%T %c%.01f", "ZonePoint", client, iPoint, sAxis[iAxis], (iState == 1)? '+':'-', gF_Modifier[client]);
|
||
FormatEx(sInfo, 16, "%d;%d;%d", iPoint, iAxis, iState);
|
||
hMenu.AddItem(sInfo, sDisplay);
|
||
}
|
||
}
|
||
}
|
||
|
||
hMenu.ExitButton = false;
|
||
hMenu.DisplayAt(client, page, MENU_TIME_FOREVER);
|
||
}
|
||
|
||
public int ZoneAdjuster_Handler(Menu menu, MenuAction action, int param1, int param2)
|
||
{
|
||
if(action == MenuAction_Select)
|
||
{
|
||
char sInfo[16];
|
||
menu.GetItem(param2, sInfo, 16);
|
||
|
||
if(StrEqual(sInfo, "done"))
|
||
{
|
||
CreateEditMenu(param1);
|
||
}
|
||
else if(StrEqual(sInfo, "cancel"))
|
||
{
|
||
if (gI_ZoneID[param1] != -1)
|
||
{
|
||
// reenable original zone
|
||
gA_ZoneCache[gI_ZoneID[param1]].bZoneInitialized = true;
|
||
}
|
||
|
||
Reset(param1);
|
||
}
|
||
else
|
||
{
|
||
char sAxis[4];
|
||
strcopy(sAxis, 4, "XYZ");
|
||
|
||
char sExploded[3][8];
|
||
ExplodeString(sInfo, ";", sExploded, 3, 8);
|
||
|
||
int iPoint = StringToInt(sExploded[0]);
|
||
int iAxis = StringToInt(sExploded[1]);
|
||
bool bIncrease = view_as<bool>(StringToInt(sExploded[2]) == 1);
|
||
|
||
((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());
|
||
}
|
||
}
|
||
else if (action == MenuAction_Cancel)
|
||
{
|
||
if (gI_ZoneID[param1] != -1)
|
||
{
|
||
// reenable original zone
|
||
gA_ZoneCache[gI_ZoneID[param1]].bZoneInitialized = true;
|
||
}
|
||
|
||
Reset(param1);
|
||
}
|
||
else if(action == MenuAction_End)
|
||
{
|
||
delete menu;
|
||
}
|
||
|
||
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));
|
||
|
||
char sQuery[1024];
|
||
char sTrack[64], sZoneName[32];
|
||
GetTrackName(LANG_SERVER, gI_ZoneTrack[client], sTrack, sizeof(sTrack));
|
||
GetZoneName(LANG_SERVER, iType, sZoneName, sizeof(sZoneName));
|
||
|
||
if(iType == Zone_CustomSpawn)
|
||
{
|
||
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);
|
||
|
||
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]);
|
||
}
|
||
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;
|
||
}
|
||
}
|
||
}
|
||
|
||
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]);
|
||
}
|
||
|
||
gH_SQL.Query2(SQL_InsertZone_Callback, sQuery, GetClientSerial(client));
|
||
}
|
||
|
||
public void SQL_InsertZone_Callback(Database db, DBResultSet results, const char[] error, any data)
|
||
{
|
||
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)
|
||
{
|
||
if(gI_MapZones == 0)
|
||
{
|
||
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;
|
||
|
||
if(gA_ZoneSettings[type][track].bVisible || (gA_ZoneCache[i].iZoneFlags & 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;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
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;
|
||
|
||
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,
|
||
true, // <==== this is the the important part,
|
||
0
|
||
);
|
||
|
||
if (++iDrawn % iMaxZonesPerFrame == 0)
|
||
{
|
||
return Plugin_Continue;
|
||
}
|
||
}
|
||
}
|
||
|
||
iCycle = 0;
|
||
|
||
return Plugin_Continue;
|
||
}
|
||
|
||
int[] GetZoneColors(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)
|
||
{
|
||
int client = GetClientFromSerial(data);
|
||
|
||
if(client == 0 || gI_MapStep[client] == 0)
|
||
{
|
||
Reset(client);
|
||
|
||
return Plugin_Stop;
|
||
}
|
||
|
||
float vPlayerOrigin[3];
|
||
GetClientAbsOrigin(client, vPlayerOrigin);
|
||
|
||
float origin[3];
|
||
|
||
if(gB_CursorTracing[client])
|
||
{
|
||
origin = GetAimPosition(client);
|
||
}
|
||
else if(!(gB_SnapToWall[client] && SnapToWall(vPlayerOrigin, client, origin)))
|
||
{
|
||
origin = SnapToGrid(vPlayerOrigin, gI_GridSnap[client], false);
|
||
}
|
||
else
|
||
{
|
||
gV_WallSnap[client] = origin;
|
||
}
|
||
|
||
if(gI_MapStep[client] == 1 || gV_Point2[client][0] == 0.0)
|
||
{
|
||
origin[2] = (vPlayerOrigin[2] + gCV_Height.FloatValue);
|
||
}
|
||
else
|
||
{
|
||
origin = gV_Point2[client];
|
||
}
|
||
|
||
int type = gI_ZoneType[client];
|
||
int track = gI_ZoneTrack[client];
|
||
|
||
if(!EmptyVector(gV_Point1[client]) || !EmptyVector(gV_Point2[client]))
|
||
{
|
||
float points[8][3];
|
||
points[0] = gV_Point1[client];
|
||
points[7] = origin;
|
||
CreateZonePoints(points, gCV_Offset.FloatValue);
|
||
|
||
// 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);
|
||
|
||
if(gI_ZoneType[client] == Zone_Teleport && !EmptyVector(gV_Teleport[client]))
|
||
{
|
||
TE_SetupEnergySplash(gV_Teleport[client], NULL_VECTOR, false);
|
||
TE_SendToAll(0.0);
|
||
}
|
||
}
|
||
|
||
if(gI_MapStep[client] != 3 && !EmptyVector(origin))
|
||
{
|
||
origin[2] -= gCV_Height.FloatValue;
|
||
|
||
TE_SetupBeamPoints(vPlayerOrigin, origin, gI_BeamSpriteIgnoreZ, gA_ZoneSettings[type][track].iHalo, 0, 0, 0.1, 1.0, 1.0, 0, 0.0, {255, 255, 255, 75}, 0);
|
||
TE_SendToAll(0.0);
|
||
|
||
// visualize grid snap
|
||
float snap1[3];
|
||
float snap2[3];
|
||
|
||
for(int i = 0; i < 3; i++)
|
||
{
|
||
snap1 = origin;
|
||
snap1[i] -= gI_GridSnap[client];
|
||
|
||
snap2 = origin;
|
||
snap2[i] += gI_GridSnap[client];
|
||
|
||
TE_SetupBeamPoints(snap1, snap2, gI_BeamSpriteIgnoreZ, gA_ZoneSettings[type][track].iHalo, 0, 0, 0.1, 1.0, 1.0, 0, 0.0, {255, 255, 255, 75}, 0);
|
||
TE_SendToAll(0.0);
|
||
}
|
||
}
|
||
|
||
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)
|
||
{
|
||
static int pairs[][] =
|
||
{
|
||
{ 0, 2 },
|
||
{ 2, 6 },
|
||
{ 6, 4 },
|
||
{ 4, 0 },
|
||
{ 0, 1 },
|
||
{ 3, 1 },
|
||
{ 3, 2 },
|
||
{ 3, 7 },
|
||
{ 5, 1 },
|
||
{ 5, 4 },
|
||
{ 6, 7 },
|
||
{ 7, 5 }
|
||
};
|
||
|
||
static int clrs[][4] =
|
||
{
|
||
{ 255, 255, 255, 255 }, // White
|
||
{ 255, 0, 0, 255 }, // Red
|
||
{ 255, 128, 0, 255 }, // Orange
|
||
{ 255, 255 ,0, 255 }, // Yellow
|
||
{ 0, 255, 0, 255}, // Green
|
||
{ 0, 255, 255, 255 }, // Cyan
|
||
{ 0, 0, 255, 255 }, // Blue
|
||
{ 128, 0, 128, 255 }, // Purple
|
||
{ 255, 192, 203, 255 }, // Pink
|
||
};
|
||
|
||
static float some_width[4] =
|
||
{
|
||
0.1, 0.5, 2.0, 8.0
|
||
};
|
||
|
||
#if CZONE_VER == 'b'
|
||
track = (track > Track_Bonus) ? Track_Bonus : Track_Main;
|
||
#endif
|
||
|
||
int clients[MAXPLAYERS+1];
|
||
int count = 0;
|
||
|
||
if (single_client)
|
||
{
|
||
clients[count++] = single_client;
|
||
}
|
||
else
|
||
{
|
||
for(int i = 1; i <= MaxClients; i++)
|
||
{
|
||
if(IsClientInGame(i) && !IsFakeClient(i) && (!drawallzones || (gB_DrawAllZones[i] && CanDrawAllZones(i))))
|
||
{
|
||
float eyes[3];
|
||
GetClientEyePosition(i, eyes);
|
||
|
||
if(gI_ZoneDisplayType[i][type][track] != ZoneDisplay_None &&
|
||
(GetVectorDistance(eyes, center) <= 2048.0 ||
|
||
(TR_TraceRayFilter(eyes, center, MASK_PLAYERSOLID, RayType_EndPoint, TraceFilter_World) && !TR_DidHit())))
|
||
{
|
||
clients[count++] = i;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
for (int i = 0; i < count; i++)
|
||
{
|
||
int point_size = (gI_ZoneDisplayType[clients[i]][type][track] == ZoneDisplay_Flat ||
|
||
gI_ZoneDisplayType[clients[i]][type][track] == ZoneDisplay_Default && flat) ? 4 : 12;
|
||
|
||
int actual_color[4];
|
||
actual_color = (gI_ZoneColor[clients[i]][type][track] == ZoneColor_Default) ? color : clrs[gI_ZoneColor[clients[i]][type][track] - 1];
|
||
|
||
float actual_width = (gI_ZoneWidth[clients[i]][type][track] == ZoneWidth_Default) ? width : some_width[gI_ZoneWidth[clients[i]][type][track] - 1];
|
||
|
||
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_SendToClient(clients[i], 0.0);
|
||
}
|
||
}
|
||
}
|
||
|
||
// original by blacky
|
||
// creates 3d box from 2 points
|
||
void CreateZonePoints(float point[8][3], float offset = 0.0)
|
||
{
|
||
// calculate all zone edges
|
||
for(int i = 1; i < 7; i++)
|
||
{
|
||
for(int j = 0; j < 3; j++)
|
||
{
|
||
point[i][j] = point[((i >> (2 - j)) & 1) * 7][j];
|
||
}
|
||
}
|
||
|
||
// apply beam offset
|
||
if(offset != 0.0)
|
||
{
|
||
float center[2];
|
||
center[0] = ((point[0][0] + point[7][0]) / 2);
|
||
center[1] = ((point[0][1] + point[7][1]) / 2);
|
||
|
||
for(int i = 0; i < 8; i++)
|
||
{
|
||
for(int j = 0; j < 2; j++)
|
||
{
|
||
if(point[i][j] < center[j])
|
||
{
|
||
point[i][j] += offset;
|
||
}
|
||
else if(point[i][j] > center[j])
|
||
{
|
||
point[i][j] -= offset;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
public void Shavit_OnDatabaseLoaded()
|
||
{
|
||
GetTimerSQLPrefix(gS_MySQLPrefix, 32);
|
||
gH_SQL = view_as<Database2>(Shavit_GetDatabase());
|
||
gB_MySQL = IsMySQLDatabase(gH_SQL);
|
||
|
||
gB_Connected = true;
|
||
|
||
if (!gB_Late)
|
||
{
|
||
OnMapStart();
|
||
}
|
||
}
|
||
|
||
void ResetClientTargetNameAndClassName(int client, int track)
|
||
{
|
||
char targetname[64];
|
||
char classname[64];
|
||
|
||
if (track == Track_Main)
|
||
{
|
||
gCV_ResetTargetnameMain.GetString(targetname, sizeof(targetname));
|
||
gCV_ResetClassnameMain.GetString(classname, sizeof(classname));
|
||
}
|
||
else
|
||
{
|
||
gCV_ResetTargetnameBonus.GetString(targetname, sizeof(targetname));
|
||
gCV_ResetClassnameBonus.GetString(classname, sizeof(classname));
|
||
}
|
||
|
||
DispatchKeyValue(client, "targetname", targetname);
|
||
|
||
if (!classname[0])
|
||
{
|
||
classname = "player";
|
||
}
|
||
|
||
SetEntPropString(client, Prop_Data, "m_iClassname", classname);
|
||
}
|
||
|
||
public Action Shavit_OnStart(int client, int track)
|
||
{
|
||
if(gCV_ForceTargetnameReset.BoolValue)
|
||
{
|
||
ResetClientTargetNameAndClassName(client, track);
|
||
}
|
||
}
|
||
|
||
public void Shavit_OnRestart(int client, int track)
|
||
{
|
||
gI_LastStage[client] = 0;
|
||
|
||
if (!IsPlayerAlive(client))
|
||
{
|
||
return;
|
||
}
|
||
|
||
int iIndex = GetZoneIndex(Zone_Start, track);
|
||
|
||
if(gCV_TeleportToStart.BoolValue)
|
||
{
|
||
bool bCustomStart = gB_HasSetStart[client][track] && !gB_StartAnglesOnly[client][track];
|
||
bool use_CustomStart_over_CustomSpawn = (iIndex != -1) && bCustomStart;
|
||
|
||
// custom spawns
|
||
if (!use_CustomStart_over_CustomSpawn && !EmptyVector(gF_CustomSpawn[track]))
|
||
{
|
||
TeleportEntity(client, gF_CustomSpawn[track], NULL_VECTOR, view_as<float>({0.0, 0.0, 0.0}));
|
||
}
|
||
// standard zoning
|
||
else if (iIndex != -1)
|
||
{
|
||
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;
|
||
|
||
if (bCustomStart)
|
||
{
|
||
fCenter = gF_StartPos[client][track];
|
||
}
|
||
|
||
fCenter[2] += 1.0;
|
||
|
||
TeleportEntity(client, fCenter, gB_HasSetStart[client][track] ? gF_StartAng[client][track] : NULL_VECTOR, view_as<float>({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])
|
||
{
|
||
Shavit_HijackAngles(client, gF_StartAng[client][track][0], gF_StartAng[client][track][1], -1, true);
|
||
}
|
||
|
||
if (!gB_HasSetStart[client][track] || gB_StartAnglesOnly[client][track])
|
||
{
|
||
ResetClientTargetNameAndClassName(client, track);
|
||
// normally StartTimer will happen on zone-touch BUT we have this here for zones that are in the air
|
||
Shavit_StartTimer(client, track);
|
||
}
|
||
}
|
||
// kz buttons
|
||
else if (Shavit_IsKZMap(track))
|
||
{
|
||
if (EmptyVector(gF_ClimbButtonCache[client][track][0]) || EmptyVector(gF_ClimbButtonCache[client][track][1]))
|
||
{
|
||
return;
|
||
}
|
||
|
||
TeleportEntity(client, gF_ClimbButtonCache[client][track][0], gF_ClimbButtonCache[client][track][1], view_as<float>({0.0, 0.0, 0.0}));
|
||
|
||
return;
|
||
}
|
||
}
|
||
|
||
if (iIndex != -1)
|
||
{
|
||
DrawZone(
|
||
gV_MapZones_Visual[iIndex],
|
||
GetZoneColors(Zone_Start, track),
|
||
gCV_Interval.FloatValue,
|
||
gA_ZoneSettings[Zone_Start][track].fWidth,
|
||
gA_ZoneSettings[Zone_Start][track].bFlatZone,
|
||
gV_ZoneCenter[iIndex],
|
||
gA_ZoneSettings[Zone_Start][track].iBeam,
|
||
gA_ZoneSettings[Zone_Start][track].iHalo,
|
||
track,
|
||
Zone_Start,
|
||
false,
|
||
client
|
||
);
|
||
}
|
||
}
|
||
|
||
public void Shavit_OnEnd(int client, int track)
|
||
{
|
||
int iIndex = GetZoneIndex(Zone_End, track);
|
||
|
||
if(gCV_TeleportToEnd.BoolValue)
|
||
{
|
||
if(iIndex != -1)
|
||
{
|
||
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
|
||
|
||
TeleportEntity(client, fCenter, NULL_VECTOR, view_as<float>({0.0, 0.0, 0.0}));
|
||
}
|
||
}
|
||
|
||
if (iIndex != -1)
|
||
{
|
||
DrawZone(
|
||
gV_MapZones_Visual[iIndex],
|
||
GetZoneColors(Zone_End, track),
|
||
gCV_Interval.FloatValue,
|
||
gA_ZoneSettings[Zone_End][track].fWidth,
|
||
gA_ZoneSettings[Zone_End][track].bFlatZone,
|
||
gV_ZoneCenter[iIndex],
|
||
gA_ZoneSettings[Zone_End][track].iBeam,
|
||
gA_ZoneSettings[Zone_End][track].iHalo,
|
||
track,
|
||
Zone_End,
|
||
false,
|
||
client
|
||
);
|
||
}
|
||
}
|
||
|
||
bool EmptyVector(float vec[3])
|
||
{
|
||
return (IsNullVector(vec) || (vec[0] == 0.0 && vec[1] == 0.0 && vec[2] == 0.0));
|
||
}
|
||
|
||
// 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))
|
||
{
|
||
return i;
|
||
}
|
||
}
|
||
|
||
return -1;
|
||
}
|
||
|
||
public void Player_Spawn(Event event, const char[] name, bool dontBroadcast)
|
||
{
|
||
Reset(GetClientOfUserId(event.GetInt("userid")));
|
||
}
|
||
|
||
public void Round_Start(Event event, const char[] name, bool dontBroadcast)
|
||
{
|
||
bool empty_InsideZone[TRACKS_SIZE];
|
||
|
||
for (int i = 0; i <= MaxClients; i++)
|
||
{
|
||
for (int j = 0; j < ZONETYPES_SIZE; j++)
|
||
{
|
||
gB_InsideZone[i][j] = empty_InsideZone;
|
||
}
|
||
|
||
bool empty_InsideZoneID[MAX_ZONES];
|
||
gB_InsideZoneID[i] = empty_InsideZoneID;
|
||
}
|
||
}
|
||
|
||
float Abs(float input)
|
||
{
|
||
if(input < 0.0)
|
||
{
|
||
return -input;
|
||
}
|
||
|
||
return input;
|
||
}
|
||
|
||
public void CreateZoneEntities(bool only_create_dead_entities)
|
||
{
|
||
for(int i = 0; i < gI_MapZones; i++)
|
||
{
|
||
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);
|
||
}
|
||
|
||
gB_ZoneCreationQueued = false;
|
||
}
|
||
|
||
public MRESReturn DHooks_OnTeleport(int pThis, DHookParam hParams)
|
||
{
|
||
if (!IsValidEntity(pThis) || !IsClientInGame(pThis))
|
||
{
|
||
return MRES_Ignored;
|
||
}
|
||
|
||
if (!hParams.IsNull(1))
|
||
{
|
||
gI_LatestTeleportTick[pThis] = GetGameTickCount();
|
||
}
|
||
|
||
return MRES_Ignored;
|
||
}
|
||
|
||
void PhysicsRemoveTouchedList(int client)
|
||
{
|
||
SDKCall(gH_PhysicsRemoveTouchedList, client);
|
||
}
|
||
|
||
void ACTUALLY_ForcePlayerSuicide(int client)
|
||
{
|
||
SDKCall(gH_CommitSuicide, client, false, true);
|
||
}
|
||
|
||
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)))
|
||
{
|
||
return;
|
||
}
|
||
|
||
TimerStatus status = Shavit_GetTimerStatus(other);
|
||
|
||
switch(gA_ZoneCache[gI_EntityZone[entity]].iZoneType)
|
||
{
|
||
case Zone_Respawn:
|
||
{
|
||
CS_RespawnPlayer(other);
|
||
}
|
||
|
||
case Zone_Teleport:
|
||
{
|
||
TeleportEntity(other, gV_Destinations[gI_EntityZone[entity]], NULL_VECTOR, NULL_VECTOR);
|
||
}
|
||
|
||
case Zone_Slay:
|
||
{
|
||
if (status != Timer_Stopped)
|
||
{
|
||
Shavit_StopTimer(other);
|
||
ACTUALLY_ForcePlayerSuicide(other);
|
||
Shavit_PrintToChat(other, "%T", "ZoneSlayEnter", other, gS_ChatStrings.sWarning, gS_ChatStrings.sVariable2, gS_ChatStrings.sWarning);
|
||
}
|
||
}
|
||
|
||
case Zone_Stop:
|
||
{
|
||
if(status != Timer_Stopped)
|
||
{
|
||
Shavit_StopTimer(other);
|
||
Shavit_PrintToChat(other, "%T", "ZoneStopEnter", other, gS_ChatStrings.sWarning, gS_ChatStrings.sVariable2, gS_ChatStrings.sWarning);
|
||
}
|
||
}
|
||
|
||
case Zone_End:
|
||
{
|
||
if (status == Timer_Running && Shavit_GetClientTrack(other) == gA_ZoneCache[gI_EntityZone[entity]].iZoneTrack)
|
||
{
|
||
Shavit_FinishMap(other, gA_ZoneCache[gI_EntityZone[entity]].iZoneTrack);
|
||
}
|
||
}
|
||
|
||
case Zone_Stage:
|
||
{
|
||
int num = gA_ZoneCache[gI_EntityZone[entity]].iZoneData;
|
||
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)))
|
||
{
|
||
gI_LastStage[other] = num;
|
||
char sTime[32];
|
||
FormatSeconds(Shavit_GetClientTime(other), sTime, 32, true);
|
||
|
||
char sMessage[255];
|
||
FormatEx(sMessage, 255, "%T", "ZoneStageEnter", other, gS_ChatStrings.sText, gS_ChatStrings.sVariable2, num, gS_ChatStrings.sText, gS_ChatStrings.sVariable2, sTime, gS_ChatStrings.sText);
|
||
|
||
Action aResult = Plugin_Continue;
|
||
Call_StartForward(gH_Forwards_StageMessage);
|
||
Call_PushCell(other);
|
||
Call_PushCell(num);
|
||
Call_PushStringEx(sMessage, 255, SM_PARAM_STRING_COPY, SM_PARAM_COPYBACK);
|
||
Call_PushCell(255);
|
||
Call_Finish(aResult);
|
||
|
||
if(aResult < Plugin_Handled)
|
||
{
|
||
Shavit_PrintToChat(other, "%s", sMessage);
|
||
}
|
||
}
|
||
}
|
||
|
||
case Zone_Speedmod:
|
||
{
|
||
char s[16];
|
||
FloatToString(view_as<float>(gA_ZoneCache[gI_EntityZone[entity]].iZoneData), 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;
|
||
|
||
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(entity);
|
||
Call_PushCell(gA_ZoneCache[gI_EntityZone[entity]].iZoneData);
|
||
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))
|
||
{
|
||
return Plugin_Stop;
|
||
}
|
||
|
||
return Plugin_Continue;
|
||
}
|
||
|
||
public void EndTouchPost(int entity, int other)
|
||
{
|
||
if(other < 1 || other > MaxClients || gI_EntityZone[entity] == -1 || IsFakeClient(other))
|
||
{
|
||
return;
|
||
}
|
||
|
||
int entityzone = gI_EntityZone[entity];
|
||
int type = gA_ZoneCache[entityzone].iZoneType;
|
||
int track = gA_ZoneCache[entityzone].iZoneTrack;
|
||
|
||
if (type < 0 || track < 0) // odd
|
||
{
|
||
return;
|
||
}
|
||
|
||
gB_InsideZone[other][type][track] = false;
|
||
gB_InsideZoneID[other][entityzone] = false;
|
||
|
||
if (type == Zone_Speedmod)
|
||
{
|
||
SetVariantString("1.0"); // surely nothing can go wrong with this
|
||
AcceptEntityInput(entity, "ModifySpeed", other, entity, 0);
|
||
}
|
||
|
||
Call_StartForward(gH_Forwards_LeaveZone);
|
||
Call_PushCell(other);
|
||
Call_PushCell(type);
|
||
Call_PushCell(track);
|
||
Call_PushCell(entityzone);
|
||
Call_PushCell(entity);
|
||
Call_PushCell(gA_ZoneCache[entityzone].iZoneData);
|
||
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)))
|
||
{
|
||
return;
|
||
}
|
||
|
||
// do precise stuff here, this will be called *A LOT*
|
||
switch(gA_ZoneCache[gI_EntityZone[entity]].iZoneType)
|
||
{
|
||
case Zone_Start:
|
||
{
|
||
if (gB_Eventqueuefix)
|
||
{
|
||
static int tick_served[MAXPLAYERS + 1];
|
||
int curr_tick = GetGameTickCount();
|
||
|
||
// GAMMACASE: This prevents further abuses related to external events being ran after you teleport from the trigger, with events setup, outside the start zone into the start zone.
|
||
// This accounts for the io events that might be set inside the start zone trigger in OnStartTouch and wont reset them!
|
||
// Logic behind this code is that all events in this chain are not instantly fired, so checking if there were teleport from the outside of a start zone in last couple of ticks
|
||
// and doing PhysicsRemoveTouchedList() now to trigger all OnEndTouch that should happen at the same tick but later and removing them allows further events from OnStartTouch be separated
|
||
// and be fired after which is the expected and desired effect.
|
||
// This also kills all ongoing events that were active on the client prior to the teleportation to start and also resets targetname and classname
|
||
// before the OnStartTouch from triggers in start zone are run, thus preventing the maps to be abusable if they don't have any reset triggers in place
|
||
if (gI_LatestTeleportTick[other] <= curr_tick <= gI_LatestTeleportTick[other] + 4)
|
||
{
|
||
if (curr_tick != tick_served[other])
|
||
{
|
||
ResetClientTargetNameAndClassName(other, gA_ZoneCache[gI_EntityZone[entity]].iZoneTrack);
|
||
|
||
PhysicsRemoveTouchedList(other);
|
||
ClearClientEvents(other);
|
||
|
||
tick_served[other] = curr_tick;
|
||
}
|
||
|
||
return;
|
||
}
|
||
else if (curr_tick != tick_served[other])
|
||
{
|
||
tick_served[other] = 0;
|
||
}
|
||
}
|
||
|
||
if (GetEntPropEnt(other, Prop_Send, "m_hGroundEntity") == -1 && !Shavit_GetStyleSettingBool(Shavit_GetBhopStyle(other), "startinair"))
|
||
{
|
||
return;
|
||
}
|
||
|
||
// 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)
|
||
{
|
||
Shavit_StartTimer(other, gA_ZoneCache[gI_EntityZone[entity]].iZoneTrack);
|
||
}
|
||
else if(gA_ZoneCache[gI_EntityZone[entity]].iZoneTrack == Track_Main)
|
||
{
|
||
Shavit_StartTimer(other, Track_Main);
|
||
}
|
||
}
|
||
case Zone_Respawn:
|
||
{
|
||
CS_RespawnPlayer(other);
|
||
}
|
||
|
||
case Zone_Teleport:
|
||
{
|
||
TeleportEntity(other, gV_Destinations[gI_EntityZone[entity]], NULL_VECTOR, NULL_VECTOR);
|
||
}
|
||
|
||
case Zone_Slay:
|
||
{
|
||
TimerStatus status = Shavit_GetTimerStatus(other);
|
||
|
||
if (status != Timer_Stopped)
|
||
{
|
||
Shavit_StopTimer(other);
|
||
ACTUALLY_ForcePlayerSuicide(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)
|
||
{
|
||
Shavit_StopTimer(other);
|
||
Shavit_PrintToChat(other, "%T", "ZoneStopEnter", other, gS_ChatStrings.sWarning, gS_ChatStrings.sVariable2, gS_ChatStrings.sWarning);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
public void UsePost(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;
|
||
|
||
if (!GetButtonInfo(entity, zone, track))
|
||
{
|
||
return;
|
||
}
|
||
|
||
if(zone == Zone_Start)
|
||
{
|
||
if (GetEntPropEnt(activator, Prop_Send, "m_hGroundEntity") == -1)
|
||
{
|
||
return;
|
||
}
|
||
|
||
GetClientAbsOrigin(activator, gF_ClimbButtonCache[activator][track][0]);
|
||
GetClientEyeAngles(activator, gF_ClimbButtonCache[activator][track][1]);
|
||
|
||
Shavit_StartTimer(activator, track);
|
||
}
|
||
else if (zone == Zone_End && !Shavit_IsPaused(activator) && Shavit_GetTimerStatus(activator) == Timer_Running && Shavit_GetClientTrack(activator) == track)
|
||
{
|
||
Shavit_FinishMap(activator, track);
|
||
}
|
||
}
|