Advertisement
Guest User

Untitled

a guest
Apr 26th, 2019
468
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 43.33 KB | None | 0 0
  1. #include <sourcemod>
  2. #include <sdkhooks>
  3. #include <tf2_stocks>
  4. #include <clientprefs>
  5. #include <morecolors>
  6.  
  7. #pragma semicolon 1
  8. #pragma newdecls required
  9.  
  10. //Define version number in a needlessly complex way
  11. #define MAJOR   "1"
  12. #define MINOR   "1"
  13. #define PATCH   "0"
  14. #define PLUGIN_VERSION  MAJOR..."."...MINOR..."."...PATCH
  15.  
  16. //Debug "Mode"
  17. // #define DEBUG    //Uncomment for "debug" stuff. Just some 'PrintToChatAll's here and there.
  18.  
  19. //Enums
  20. enum PadCond (<<= 1)    //Custom Conditions - Use Pad_IsPlayerInCond() to see if a Custom Condition is active on a player.
  21. {
  22.     PadCond_None = 0,       // 0 << 0 = 0
  23.     PadCond_Boost = 1,      // 1 << 0 = 1
  24.     PadCond_NoFallDmg,      // 1 << 1 = 2
  25.     PadCond_DelayResponse   // 1 << 2 = 4
  26. }
  27.  
  28. enum //Custom ObjectType
  29. {
  30.     PadType_None = 0,
  31.     PadType_Boost,
  32.     PadType_Jump
  33. }
  34.  
  35. enum //Teleporter states
  36. {
  37.     TELEPORTER_STATE_BUILDING = 0,              // Building, not active yet
  38.     TELEPORTER_STATE_IDLE,                      // Does not have a matching teleporter yet
  39.     TELEPORTER_STATE_READY,                     // Found match, charged and ready
  40.     TELEPORTER_STATE_SENDING,                   // Teleporting a player away
  41.     TELEPORTER_STATE_RECEIVING,                
  42.     TELEPORTER_STATE_RECEIVING_RELEASE,
  43.     TELEPORTER_STATE_RECHARGING,                // Waiting for recharge
  44.     TELEPORTER_STATE_UPGRADING                  // Upgrading
  45. }
  46.  
  47. enum //CvarName
  48. {
  49.     PadsEnabled,
  50.     PadsAnnounce,
  51.     PadSize,
  52.     PadHealth,
  53.     JumpSpeed,
  54.     JumpHeight,
  55.     JumpCrouchSpeedMult,
  56.     JumpCrouchHeightMult,
  57.     JumpBlockSnipers,
  58.     JumpCooldown,
  59.     BoostDuration,
  60.     BoostSpeed,
  61.     BoostDamage,
  62.     BoostAirblast,
  63.     BoostBlockAiming,
  64.     BoostCooldown,
  65.     BotsCanBuild,
  66.     BlockEureka,
  67.     VersionNumber
  68. }
  69.  
  70. enum //Plugin Enabled states
  71. {
  72.     EngiPads_Disabled,
  73.     EngiPads_Enabled,
  74.     EngiPads_BoostOnly,
  75.     EngiPads_JumpOnly
  76. }
  77.  
  78. /* Global vars */
  79. static int g_iPadType[2048];
  80. static int g_iObjectParticle[2048];
  81. static int g_iPlayerDamageTaken[MAXPLAYERS + 1];
  82. static float g_flPlayerBoostEndTime[MAXPLAYERS + 1];
  83. static PadCond g_fPadCondFlags[MAXPLAYERS + 1];
  84.  
  85. static char g_szOffsetStartProp[64];
  86. static int g_iOffsetMatchingTeleporter = -1;
  87.  
  88. static Handle g_hPadCookie;
  89. static Handle g_hPadTimerAnnounce;
  90.  
  91. /* Convars */
  92. ConVar cvarPads[VersionNumber + 1];
  93.  
  94.  
  95. public Plugin myinfo =
  96. {
  97.     name            = "[TF2] Engineer Pads",
  98.     author          = "Starblaster 64",
  99.     description     = "Custom Teleporter building replacements.",
  100.     version         = PLUGIN_VERSION,
  101.     url             = "https://forums.alliedmods.net/showthread.php?t=304025"
  102. };
  103.  
  104. //Initialise gamedata to be used for getting/setting Teleporter pairs.
  105. void InitGamedata()
  106. {
  107.     Handle hGameConf = LoadGameConfigFile("tf2.teleporters");
  108.     if (hGameConf == INVALID_HANDLE)
  109.     {
  110.         SetFailState("[EngiPads] Unable to load gamedata file 'tf2.teleporters.txt'");
  111.     }
  112.    
  113.     bool bFoundProp = GameConfGetKeyValue(hGameConf, "StartProp", g_szOffsetStartProp, sizeof(g_szOffsetStartProp));
  114.     g_iOffsetMatchingTeleporter = GameConfGetOffset(hGameConf, "m_hMatchingTeleporter");
  115.    
  116.     if (!bFoundProp || g_iOffsetMatchingTeleporter < 0)
  117.     {
  118.         SetFailState("[EngiPads] Unable to get m_hMatchingTeleporter offset from 'tf2.teleporters.txt'. Check gamedata!");
  119.     }
  120.    
  121.     CloseHandle(hGameConf);
  122. }
  123.  
  124. public void OnPluginStart()
  125. {
  126.     InitGamedata();
  127.    
  128.     RegConsoleCmd("sm_pad", TogglePadsMenuCmd);
  129.     RegConsoleCmd("sm_pads", TogglePadsMenuCmd);
  130.     RegConsoleCmd("sm_pad_help", ShowPadsInfoCmd);
  131.     RegConsoleCmd("sm_padhelp", ShowPadsInfoCmd);
  132.    
  133.     AddCommandListener(EurekaTeleport, "eureka_teleport");
  134.    
  135.     cvarPads[VersionNumber] = CreateConVar("pads_version", PLUGIN_VERSION, "EngiPads version number. Don't touch this!", FCVAR_NOTIFY|FCVAR_DONTRECORD|FCVAR_CHEAT);
  136.     cvarPads[PadsEnabled] = CreateConVar("pads_enabled", "1", "Enables/Disables the plugin. (2 - BoostPads only, 3 - JumpPads only)", FCVAR_NOTIFY, true, 0.0, true, 3.0);
  137.     cvarPads[PadsAnnounce] = CreateConVar("pads_announce", "347.0", "Interval between chat announcements about the plugin. 0.0 to disable.", FCVAR_NOTIFY, true, 0.0);
  138.    
  139.     cvarPads[PadSize] = CreateConVar("pads_size", "0.7", "Pad size multiplier.", FCVAR_NOTIFY, true, 0.0, true, 5.0);
  140.     cvarPads[PadHealth] = CreateConVar("pads_health", "100", "How much HP Pads will have.", FCVAR_NOTIFY, true, 1.0);
  141.     cvarPads[JumpSpeed] = CreateConVar("pads_jump_speed", "700.0", "How fast players will be launched horizontally by Jump Pads.", FCVAR_NOTIFY, true, 0.0);
  142.     cvarPads[JumpHeight] = CreateConVar("pads_jump_height", "700.0", "How fast players will be launched vertically by Jump Pads.", FCVAR_NOTIFY, true, 0.0);
  143.     cvarPads[JumpCrouchSpeedMult] = CreateConVar("pads_jump_crouch_speed_mult", "1.0", "Multiply crouching players' speed by this much when using Jump Pads.", FCVAR_NOTIFY, true, 0.0);
  144.     cvarPads[JumpCrouchHeightMult] = CreateConVar("pads_jump_crouch_height_mult", "1.0", "Multiply crouching players' height by this much when using Jump Pads.", FCVAR_NOTIFY, true, 0.0);
  145.     cvarPads[JumpBlockSnipers] = CreateConVar("pads_jump_block_snipers", "1", "If enabled, prevents Snipers from scoping in while using Jump Pads.", FCVAR_NOTIFY, true, 0.0, true, 1.0);
  146.     cvarPads[JumpCooldown] = CreateConVar("pads_jump_cooldown", "3.0", "How long, in seconds, should Jump Pads take to recharge?", FCVAR_NOTIFY, true, 0.1);
  147.     cvarPads[BoostDuration] = CreateConVar("pads_boost_duration", "5.0", "How long, in seconds, should Boost Pads boost players for?", FCVAR_NOTIFY, true, 0.0);
  148.     cvarPads[BoostSpeed] = CreateConVar("pads_boost_speed", "520.0", "What minimum speed should players be boosted to when using Boost Pads?", FCVAR_NOTIFY, true, 0.0);
  149.     cvarPads[BoostDamage] = CreateConVar("pads_boost_damage_threshold", "35", "How much damage can a boosted player take before losing their boost? 0 to disable.", FCVAR_NOTIFY, true, 0.0);
  150.     cvarPads[BoostAirblast] = CreateConVar("pads_boost_airblast", "1", "Should boosted players lose their boost when airblasted?", FCVAR_NOTIFY, true, 0.0, true, 1.0);
  151.     cvarPads[BoostBlockAiming] = CreateConVar("pads_boost_block_aiming", "1", "Set to 1 to prevent scoped-in/revved up players from being speed boosted.", FCVAR_NOTIFY, true, 0.0, true, 1.0);
  152.     cvarPads[BoostCooldown] = CreateConVar("pads_boost_cooldown", "3.0", "How long in seconds should Boost Pads take to recharge?", FCVAR_NOTIFY, true, 0.1);
  153.     cvarPads[BotsCanBuild] = CreateConVar("pads_bots_can_build", "0", "If enabled, Bots will build Boost Pads instead of Teleporters.", FCVAR_NOTIFY, true, 0.0, true, 1.0);
  154.     cvarPads[BlockEureka] = CreateConVar("pads_block_eureka", "1", "Toggle blocking Eureka Effect from teleporting to Pads that are Exits.", FCVAR_NOTIFY, true, 0.0, true, 1.0);
  155.  
  156.     AutoExecConfig(true, "engipads");
  157.     LoadTranslations("engipads.phrases");
  158.    
  159.     //Cookies
  160.     g_hPadCookie = RegClientCookie("engipads_toggle", "Client's EngiPads toggle preference.", CookieAccess_Protected);
  161.    
  162.     //Hooks
  163.     cvarPads[VersionNumber].AddChangeHook(CvarChange);
  164.     cvarPads[PadsEnabled].AddChangeHook(CvarChange);
  165.     cvarPads[PadsAnnounce].AddChangeHook(CvarChange);
  166.    
  167.     HookEvent("player_death", PlayerDeath, EventHookMode_Post);
  168.     HookEvent("player_hurt", PlayerHurt, EventHookMode_Post);
  169.    
  170.     HookEvent("player_builtobject", ObjectBuilt, EventHookMode_Post);
  171.     HookEvent("player_sapped_object", ObjectSapped, EventHookMode_Post);
  172.     HookEvent("player_carryobject", ObjectDestroyed, EventHookMode_Post);
  173.    
  174.     HookEvent("object_destroyed", ObjectDestroyed, EventHookMode_Post);
  175.     HookEvent("object_removed", ObjectDestroyed, EventHookMode_Post);
  176.    
  177.     HookEvent("object_deflected", ObjectDeflected, EventHookMode_Post);
  178.    
  179.     AddNormalSoundHook(HookSound);
  180.    
  181.     for (int i = 1; i <= MaxClients; i++)
  182.     {
  183.         OnClientPostAdminCheck(i);
  184.     }
  185. }
  186.  
  187. public void OnPluginEnd()
  188. {
  189.     ConvertAllPadsToTeleporters(); //Convert all Pads back to teleporters if the plugin is unloaded.
  190. }
  191.  
  192. public void OnConfigsExecuted()
  193. {
  194.     /* Version checker taken from VSH */
  195.     static char szOldVersion[12];
  196.     cvarPads[VersionNumber].GetString(szOldVersion, sizeof(szOldVersion));
  197.     if (!StrEqual(szOldVersion, PLUGIN_VERSION))
  198.         cvarPads[VersionNumber].SetString(PLUGIN_VERSION, false, true);
  199.    
  200.     if (cvarPads[PadsEnabled].IntValue > EngiPads_Disabled && cvarPads[PadsAnnounce].FloatValue > 0.0)
  201.     {
  202.         g_hPadTimerAnnounce = CreateTimer(cvarPads[PadsAnnounce].FloatValue, Timer_PadsAnnounce, _, TIMER_FLAG_NO_MAPCHANGE);
  203.     }
  204. }
  205.  
  206. public void CvarChange(ConVar cvar, const char[] szOldValue, const char[] szNewValue)
  207. {
  208.     if (cvar == cvarPads[VersionNumber])
  209.     {
  210.         if (!StrEqual(szNewValue, PLUGIN_VERSION))
  211.             cvarPads[VersionNumber].SetString(PLUGIN_VERSION);  //If config version number does not match plugin, plugin will override it.
  212.     }
  213.     else if (cvar == cvarPads[PadsEnabled])
  214.     {
  215.         if (RoundToFloor(StringToFloat(szNewValue)) == EngiPads_Disabled && RoundToFloor(StringToFloat(szOldValue)) > EngiPads_Disabled)
  216.         {
  217.             ConvertAllPadsToTeleporters();
  218.             ClearTimer(g_hPadTimerAnnounce);
  219.         }
  220.         else if (cvarPads[PadsAnnounce].FloatValue > 0.0 && RoundToFloor(StringToFloat(szOldValue)) == EngiPads_Disabled)
  221.         {
  222.             ClearTimer(g_hPadTimerAnnounce);
  223.             g_hPadTimerAnnounce = CreateTimer(cvarPads[PadsAnnounce].FloatValue, Timer_PadsAnnounce, _, TIMER_FLAG_NO_MAPCHANGE);
  224.         }
  225.     }
  226.     else if (cvar == cvarPads[PadsAnnounce])
  227.     {
  228.         if (StringToFloat(szNewValue) > 0.0 && cvarPads[PadsEnabled].IntValue > EngiPads_Disabled)
  229.         {
  230.             ClearTimer(g_hPadTimerAnnounce);
  231.             g_hPadTimerAnnounce = CreateTimer(cvarPads[PadsAnnounce].FloatValue, Timer_PadsAnnounce, _, TIMER_FLAG_NO_MAPCHANGE);
  232.         }
  233.         else if (StringToFloat(szNewValue) <= 0.0)
  234.         {
  235.             ClearTimer(g_hPadTimerAnnounce);
  236.         }
  237.     }
  238. }
  239.  
  240. public Action Timer_PadsAnnounce(Handle hTimer)
  241. {
  242.     if (cvarPads[PadsEnabled].IntValue == EngiPads_Disabled || cvarPads[PadsAnnounce].FloatValue <= 0.0)
  243.     {
  244.         g_hPadTimerAnnounce = INVALID_HANDLE;
  245.         return Plugin_Stop;
  246.     }
  247.    
  248.     int iAnnouncement = GetRandomInt(0, 5);
  249.    
  250.     switch (iAnnouncement)
  251.     {
  252.         case 0:
  253.         {
  254.             CPrintToChatAll("{orange}[EngiPads]{default} %t", "padphrase_announcement1", PLUGIN_VERSION);
  255.         }
  256.         default:
  257.         {
  258.             CPrintToChatAll("{orange}[EngiPads]{default} %t", "padphrase_announcement2");
  259.         }
  260.     }
  261.    
  262.     g_hPadTimerAnnounce = CreateTimer(cvarPads[PadsAnnounce].FloatValue, Timer_PadsAnnounce, _, TIMER_FLAG_NO_MAPCHANGE);
  263.    
  264.     return Plugin_Handled;
  265. }
  266.  
  267. public void OnMapStart()
  268. {
  269.     g_hPadTimerAnnounce = INVALID_HANDLE;
  270.    
  271.     /* Precache Sounds */
  272.     PrecacheSound(")items/powerup_pickup_haste.wav", true); //For some reason, using PrecacheScriptSound doesn't work for this sound.
  273.     PrecacheScriptSound("Passtime.BallSmack");
  274.     //The other gamesounds I emit should already be precached by the game
  275.    
  276.     /* Precache Particles */
  277.     PrecacheParticleSystem("powerup_icon_haste_red");
  278.     PrecacheParticleSystem("powerup_icon_haste_blue");
  279.    
  280.     PrecacheParticleSystem("powerup_icon_agility_red");
  281.     PrecacheParticleSystem("powerup_icon_agility_blue");
  282. }
  283.  
  284. public void OnMapEnd()
  285. {
  286.     ClearTimer(g_hPadTimerAnnounce);
  287. }
  288.  
  289. public void OnClientPostAdminCheck(int iClient)
  290. {
  291.     //Reset players' global vars on connect/disconnect/death/pluginstart
  292.     Pad_SetConds(iClient, PadCond_None);
  293.     g_iPlayerDamageTaken[iClient] = 0;
  294.     g_flPlayerBoostEndTime[iClient] = 0.0;
  295. }
  296.  
  297. public void OnClientDisconnect(int iClient)
  298. {
  299.     OnClientPostAdminCheck(iClient);
  300. }
  301.  
  302. public void PlayerDeath(Event event, const char[] name, bool dontBroadcast)
  303. {
  304.     int iClient = GetClientOfUserId(event.GetInt("userid"));
  305.     if (IsValidClient(iClient))
  306.         OnClientPostAdminCheck(iClient);
  307. }
  308.  
  309. public void PlayerHurt(Event event, const char[] name, bool dontBroadcast)
  310. {
  311.     if (cvarPads[BoostDamage].IntValue <= 0)
  312.         return;
  313.    
  314.     int iVictim = GetClientOfUserId(event.GetInt("userid"));
  315.     int iAttacker = GetClientOfUserId(event.GetInt("attacker"));
  316.     int iDamage = event.GetInt("damageamount");
  317.    
  318.     if (iAttacker != iVictim && Pad_IsPlayerInCond(iVictim, PadCond_Boost)) //Ignore self damage
  319.     {
  320.         g_iPlayerDamageTaken[iVictim] += iDamage;
  321.         if (g_iPlayerDamageTaken[iVictim] >= cvarPads[BoostDamage].IntValue)
  322.         {
  323.             g_flPlayerBoostEndTime[iVictim] = 0.0;
  324.         }
  325.     }
  326. }
  327.  
  328. public void ObjectDeflected(Event event, const char[] name, bool dontBroadcast)
  329. {
  330.     if (!cvarPads[BoostAirblast].BoolValue)
  331.         return;
  332.    
  333.     int iOwner = GetClientOfUserId(event.GetInt("ownerid"));
  334.     int iWeapon = event.GetInt("weaponid");
  335.    
  336.     if (!iWeapon && IsValidClient(iOwner) && Pad_IsPlayerInCond(iOwner, PadCond_Boost))     //0 means ownerid was airblasted
  337.     {
  338.         g_flPlayerBoostEndTime[iOwner] = 0.0;
  339.     }
  340. }
  341.  
  342. public void ObjectBuilt(Event event, const char[] name, bool dontBroadcast)
  343. {
  344.     if (view_as<TFObjectType>(event.GetInt("object")) != TFObject_Teleporter)
  345.         return;
  346.    
  347.     int iBuilder = GetClientOfUserId(event.GetInt("userid"));
  348.     int iObj = event.GetInt("index");
  349.    
  350.     if (!GetClientPadsEnabled(iBuilder) || cvarPads[PadsEnabled].IntValue == EngiPads_Disabled || !CheckCommandAccess(iBuilder, "sm_engipads_override", 0))
  351.     {
  352.         if (g_iPadType[iObj])
  353.         {
  354.             ConvertPadToTeleporter(iObj);
  355.         }
  356.         if (TF2_GetMatchingTeleporter(iObj) == iObj)
  357.         {
  358.             TF2_SetMatchingTeleporter(iObj, -1);    //Reset m_hMatchingTeleporter if the buidling is no longer a Pad.
  359.         }
  360.         return;
  361.     }
  362.    
  363.     ConvertTeleporterToPad(iObj, GetPadType(iObj), GetEntProp(iObj, Prop_Send, "m_bCarryDeploy") ? false : true);
  364.    
  365.     PrintPadTypeNameToClient(g_iPadType[iObj], iBuilder);
  366. }
  367.  
  368. public void ObjectDestroyed(Event event, const char[] name, bool dontBroadcast)
  369. {
  370.     int iObj = event.GetInt("index");
  371.     if ((view_as<TFObjectType>(event.GetInt("object")) != TFObject_Teleporter && view_as<TFObjectType>(event.GetInt("objecttype")) != TFObject_Teleporter) || !g_iPadType[iObj])
  372.         return;
  373.    
  374.     int iObjParti = EntRefToEntIndex(g_iObjectParticle[iObj]);
  375.     if (IsValidEntity(iObjParti))
  376.         AcceptEntityInput(iObjParti, "Kill");
  377.     g_iObjectParticle[iObj] = -1;
  378.    
  379.     #if defined DEBUG
  380.     PrintToChatAll("%i Destroyed!", iObj);
  381.     #endif
  382.    
  383.     if (!StrEqual(name, "player_carryobject"))
  384.         g_iPadType[iObj] = PadType_None;
  385. }
  386.  
  387. public void ObjectSapped(Event event, const char[] name, bool dontBroadcast)
  388. {
  389.     int iSapper = event.GetInt("sapperid");
  390.     int iObj = GetEntPropEnt(iSapper, Prop_Send, "m_hBuiltOnEntity");
  391.    
  392.     if (view_as<TFObjectType>(event.GetInt("object")) != TFObject_Teleporter || !g_iPadType[iObj])
  393.         return;
  394.    
  395.     SetVariantInt(GetEntProp(iSapper, Prop_Send, "m_iMaxHealth") * 2);
  396.     AcceptEntityInput(iSapper, "SetHealth", iSapper); //Double Sapper HP. Since I set the Tele's matching Tele to itself, Sappers take 2 instances of damage per hit.
  397.    
  398.     SetEntPropFloat(iSapper, Prop_Send, "m_flModelScale", cvarPads[PadSize].FloatValue);    //Scale down Sapper to match Pad size.
  399. }
  400.  
  401. public Action HookSound(int clients[MAXPLAYERS], int &numClients, char sample[PLATFORM_MAX_PATH],
  402.         int &entity, int &channel, float &volume, int &level, int &pitch, int &flags,
  403.         char soundEntry[PLATFORM_MAX_PATH], int &seed)
  404. {
  405.     if (IsValidEntity(entity))
  406.     {
  407.         char className[64];
  408.         GetEntityClassname(entity, className, sizeof(className));
  409.    
  410.         if (StrEqual(className, "obj_attachment_sapper") && TF2_GetObjectType(entity) == TFObject_Sapper && channel == SNDCHAN_STATIC)
  411.         {
  412.             if (GetEntPropEnt(entity, Prop_Send, "m_hBuiltOnEntity") == -1)
  413.             {
  414.                 if (StrEqual(sample, "weapons/sapper_timer.wav") || StrContains(sample, "spy_tape") != -1)
  415.                 {
  416.                     return Plugin_Handled;  //I need to block the duplicate sapping sound otherwise it'll loop forever.
  417.                 }
  418.             }
  419.         }
  420.     }
  421.        
  422.     return Plugin_Continue;
  423. }
  424.  
  425. public Action EurekaTeleport(int iClient, const char[] szCommand, int nArgs)
  426. {
  427.     if (cvarPads[PadsEnabled].IntValue == EngiPads_Disabled || !cvarPads[BlockEureka].BoolValue)
  428.         return Plugin_Continue;
  429.    
  430.     if (IsValidClient(iClient) && IsPlayerAlive(iClient))
  431.     {
  432.         char arg[8]; GetCmdArg(1, arg, sizeof(arg));
  433.         int iDest = StringToInt(arg);
  434.        
  435.         if (iDest != 1 || !GetCmdArgs())    //If teleport destination is not 1 or unspecified (Spawn)
  436.             return Plugin_Continue;
  437.        
  438.         int i = -1;
  439.         while ((i = FindEntityByClassname2(i, "obj_teleporter")) != -1)
  440.         {
  441.             if (IsValidEntity(i) && g_iPadType[i] && TF2_GetObjectMode(i) == TFObjectMode_Exit)
  442.             {
  443.                 EmitGameSoundToClient(iClient, "Player.UseDeny", iClient);
  444.                 return Plugin_Handled;
  445.             }
  446.         }
  447.     }
  448.     return Plugin_Continue;
  449. }
  450.  
  451. public void OnGameFrame()
  452. {
  453.     if (cvarPads[PadsEnabled].IntValue == EngiPads_Disabled)
  454.         return;
  455.    
  456.     int i = -1;
  457.     while ((i = FindEntityByClassname2(i, "obj_teleporter")) != -1)
  458.     {
  459.         if (IsValidEntity(i))
  460.         {
  461.             if (g_iPadType[i])
  462.                 OnPadThink(i);
  463.            
  464.             else if (g_iPadType[i] == PadType_None && !GetEntProp(i, Prop_Send, "m_bWasMapPlaced"))
  465.             {
  466.                 int iMatch = TF2_GetMatchingTeleporter(i);
  467.                 if (IsValidEntity(iMatch))
  468.                 {
  469.                     if ((g_iPadType[iMatch] || iMatch == i) && !GetEntProp(i, Prop_Send, "m_bDisabled"))
  470.                     {
  471.                         TF2_DisableObject(i);   //Disable Teleporters that are matched with Pads.
  472.                         if (iMatch != i)
  473.                             TF2_SetMatchingTeleporter(i, i);    //Unlink them so upgrades/sappers don't transfer over.
  474.                     }
  475.                 }
  476.             }
  477.         }
  478.     }
  479. }
  480.  
  481. void OnPadThink(int iPad)
  482. {
  483.     float flConstructed = GetEntPropFloat(iPad, Prop_Send, "m_flPercentageConstructed");
  484.     bool bBuilding = view_as<bool>(GetEntProp(iPad, Prop_Send, "m_bBuilding"));
  485.     bool bCarried = view_as<bool>(GetEntProp(iPad, Prop_Send, "m_bCarried"));
  486.     bool bPlacing = view_as<bool>(GetEntProp(iPad, Prop_Send, "m_bPlacing"));
  487.     bool bDisabled = view_as<bool>(GetEntProp(iPad, Prop_Send, "m_bDisabled"));
  488.     bool bSapped = view_as<bool>(GetEntProp(iPad, Prop_Send, "m_bHasSapper"));
  489.    
  490.     if (bBuilding && flConstructed < 1.0)
  491.     {
  492.         if (TF2_GetBuildingState(iPad) != TELEPORTER_STATE_BUILDING)
  493.             TF2_SetBuildingState(iPad, TELEPORTER_STATE_BUILDING);
  494.         if (GetEntProp(iPad, Prop_Send, "m_iUpgradeLevel") != 3 && !bSapped)
  495.         {
  496.             SetEntProp(iPad, Prop_Send, "m_iHighestUpgradeLevel", 3);
  497.             SetEntProp(iPad, Prop_Send, "m_iUpgradeLevel", 3);
  498.         }
  499.         return;
  500.     }
  501.    
  502.     int iObjParti = EntRefToEntIndex(g_iObjectParticle[iPad]);
  503.    
  504.     if (bCarried || bPlacing || bDisabled)
  505.     {
  506.         if (bSapped)
  507.         {
  508.             if (GetEntProp(iPad, Prop_Send, "m_iUpgradeLevel") > 1)
  509.             {
  510.                 SetEntProp(iPad, Prop_Send, "m_iUpgradeLevel", 1);  //Prevents the Red-Tape Recorder having to downgrade Pads before deconstructing.
  511.                 SetEntProp(iPad, Prop_Send, "m_iHighestUpgradeLevel", 1);
  512.             }
  513.         }
  514.         if (IsValidEntity(iObjParti) && GetEntProp(iObjParti, Prop_Send, "m_bActive"))
  515.             AcceptEntityInput(iObjParti, "Stop");
  516.         return;
  517.     }
  518.    
  519.     if (TF2_GetBuildingState(iPad) > TELEPORTER_STATE_BUILDING && TF2_GetBuildingState(iPad) < TELEPORTER_STATE_UPGRADING)
  520.     {
  521.         if (TF2_GetBuildingState(iPad) != TELEPORTER_STATE_READY && GetEntPropFloat(iPad, Prop_Send, "m_flRechargeTime") <= GetGameTime())
  522.         {
  523.             TF2_SetBuildingState(iPad, TELEPORTER_STATE_READY); //Make sure the Pad always re-activates when it's supposed to.
  524.            
  525.             #if defined DEBUG
  526.             PrintToChatAll("%i Ready!", iPad);
  527.             #endif
  528.         }
  529.         if (TF2_GetBuildingState(iPad) == TELEPORTER_STATE_READY && IsValidEntity(iObjParti) && !GetEntProp(iObjParti, Prop_Send, "m_bActive"))
  530.             AcceptEntityInput(iObjParti, "Start");
  531.     }
  532.    
  533.     float flCooldown;
  534.     switch (g_iPadType[iPad])
  535.     {
  536.         case PadType_Boost: flCooldown = cvarPads[BoostCooldown].FloatValue;
  537.         case PadType_Jump:  flCooldown = cvarPads[JumpCooldown].FloatValue;
  538.     }
  539.     SetEntPropFloat(iPad, Prop_Send, "m_flCurrentRechargeDuration", flCooldown);
  540.    
  541.     SetEntPropFloat(iPad, Prop_Send, "m_flYawToExit", GetEntPropFloat(iPad, Prop_Send, "m_flYawToExit") + 10.0);    //Make the arrow spin for fun, and to indicate its not a Teleporter (but mostly for fun)
  542.     if (GetEntPropFloat(iPad, Prop_Send, "m_flYawToExit") > 360.0)
  543.         SetEntPropFloat(iPad, Prop_Send, "m_flYawToExit", 0.0);
  544. }
  545.  
  546. public Action OnPadTouch(int iPad, int iToucher)
  547. {
  548.     if (IsValidClient(iToucher))
  549.     {      
  550.         if (TF2_GetBuildingState(iPad) != TELEPORTER_STATE_READY)
  551.             return Plugin_Continue;
  552.        
  553.         int iPadTeam = GetEntProp(iPad, Prop_Data, "m_iTeamNum");
  554.         int iPadBuilder = GetEntPropEnt(iPad, Prop_Send, "m_hBuilder");
  555.        
  556.         if ((GetClientTeam(iToucher) == iPadTeam ||
  557.             (TF2_GetPlayerClass(iToucher) == TFClass_Spy && TF2_IsPlayerInCondition(iToucher, TFCond_Disguised) && GetEntProp(iToucher, Prop_Send, "m_nDisguiseTeam") == iPadTeam)) &&
  558.             GetEntPropEnt(iToucher, Prop_Send, "m_hGroundEntity") == iPad)
  559.         {
  560.             switch(g_iPadType[iPad])
  561.             {
  562.                 case PadType_Boost:
  563.                 {
  564.                     if (!Pad_AddCond(iToucher, PadCond_Boost))
  565.                         return Plugin_Handled;  //Ignore players who already have a boost
  566.                    
  567.                     float flDur = cvarPads[BoostDuration].FloatValue;
  568.                    
  569.                     TF2_AddCondition(iToucher, TFCond_SpeedBuffAlly, flDur);
  570.                     TF2_AddCondition(iToucher, TFCond_TeleportedGlow, flDur);
  571.                     Pad_AddCond(iToucher, PadCond_DelayResponse);
  572.                    
  573.                     SDKHook(iToucher, SDKHook_PreThink, OnPreThink);
  574.                     g_flPlayerBoostEndTime[iToucher] = GetGameTime() + flDur;
  575.                     g_iPlayerDamageTaken[iToucher] = 0;
  576.                    
  577.                     TF2_SetBuildingState(iPad, TELEPORTER_STATE_RECEIVING_RELEASE);
  578.                    
  579.                     SetEntPropFloat(iPad, Prop_Send, "m_flRechargeTime", GetGameTime() + cvarPads[BoostCooldown].FloatValue);
  580.                    
  581.                     AcceptEntityInput(EntRefToEntIndex(g_iObjectParticle[iPad]), "Stop");
  582.                    
  583.                     EmitGameSoundToAll("Powerup.PickUpHaste", iToucher);
  584.                     EmitGameSoundToAll("Building_Teleporter.Send", iPad);
  585.                    
  586.                     #if defined DEBUG
  587.                     PrintToChatAll("%N Boosted!", iToucher);
  588.                     #endif
  589.                 }
  590.                 case PadType_Jump:
  591.                 {
  592.                     if (!Pad_AddCond(iToucher, PadCond_NoFallDmg))  //Wait for launched players to be unhooked before re-launching them
  593.                         return Plugin_Handled;
  594.                    
  595.                     TF2_AddCondition(iToucher, TFCond_TeleportedGlow, 5.0);
  596.                     Pad_AddCond(iToucher, PadCond_DelayResponse);
  597.                    
  598.                     RequestFrame(LaunchPlayer, iToucher);
  599.                    
  600.                     TF2_SetBuildingState(iPad, TELEPORTER_STATE_RECEIVING_RELEASE);
  601.                    
  602.                     SetEntPropFloat(iPad, Prop_Send, "m_flRechargeTime", GetGameTime() + cvarPads[JumpCooldown].FloatValue);
  603.                    
  604.                     AcceptEntityInput(EntRefToEntIndex(g_iObjectParticle[iPad]), "Stop");
  605.                    
  606.                     EmitGameSoundToAll("Passtime.BallSmack", iPad);
  607.                     EmitGameSoundToAll("TFPlayer.AirBlastImpact", iPad);
  608.                     EmitGameSoundToAll("Building_Teleporter.Send", iPad);
  609.                    
  610.                     #if defined DEBUG
  611.                     PrintToChatAll("%N Launched!", iToucher);
  612.                     #endif
  613.                 }
  614.             }
  615.             if (iToucher != iPadBuilder)
  616.             {
  617.                 SetEntProp(iPad, Prop_Send, "m_iTimesUsed", GetEntProp(iPad, Prop_Send, "m_iTimesUsed") + 1);
  618.                
  619.                 if (!(GetEntProp(iPad, Prop_Send, "m_iTimesUsed") % 6)) //Add +2 points every 6 uses
  620.                 {
  621.                     Event event = CreateEvent("player_escort_score", true); //Using player_teleported unfortunately does not work.
  622.                     if (event != null)
  623.                     {
  624.                         event.SetInt("player", iPadBuilder);
  625.                         event.SetInt("points", 1);  //Not sure why this is adding double points
  626.                         event.Fire();
  627.                     }
  628.                 }
  629.             }
  630.             #if defined DEBUG
  631.             PrintToChatAll("Conds: %i", view_as<int>(Pad_GetConds(iToucher)));
  632.             #endif
  633.         }
  634.         return Plugin_Handled;  //Block client touch events to prevent enemy spies messing stuff up.
  635.     }
  636.     return Plugin_Continue;
  637. }
  638.  
  639. /* Boost Pad Effects */
  640. public void OnPreThink(int iClient)
  641. {
  642.     if (g_flPlayerBoostEndTime[iClient] <= GetGameTime() || !Pad_IsPlayerInCond(iClient, PadCond_Boost))    //If the player's boost duration is over, or they're still hooked without the boost cond.
  643.     {
  644.         if (IsPlayerAlive(iClient))
  645.         {
  646.             g_flPlayerBoostEndTime[iClient] = 0.0;
  647.             g_iPlayerDamageTaken[iClient] = 0;
  648.             Pad_RemoveCond(iClient, PadCond_Boost);
  649.             TF2_RemoveCondition(iClient, TFCond_TeleportedGlow);
  650.             TF2_RemoveCondition(iClient, TFCond_SpeedBuffAlly);
  651.             TF2_AddCondition(iClient, TFCond_SpeedBuffAlly, 0.01);  //Recalc player's speed so they don't keep the boost forever
  652.            
  653.             if (Pad_IsPlayerInCond(iClient, PadCond_DelayResponse))
  654.             {
  655.                 if (!Pad_IsPlayerInCond(iClient, PadCond_NoFallDmg)) //If player is still going to negate fall dmg, they'll need to say thanks later.
  656.                 {
  657.                     Pad_RemoveCond(iClient, PadCond_DelayResponse);
  658.                     TF2_SayTeleportResponse(iClient);
  659.                 }
  660.             }
  661.            
  662.             #if defined DEBUG
  663.             PrintToChatAll("%N's Boost Ended!", iClient);
  664.             #endif
  665.         }
  666.         SDKUnhook(iClient, SDKHook_PreThink, OnPreThink);
  667.     }
  668.    
  669.     else if (Pad_IsPlayerInCond(iClient, PadCond_Boost))
  670.     {
  671.         float flBoostSpeed = cvarPads[BoostSpeed].FloatValue;
  672.         if (flBoostSpeed && (!TF2_IsPlayerInCondition(iClient, TFCond_Slowed) || !cvarPads[BoostBlockAiming].BoolValue))    //Don't apply speed boost to Revved/Aiming players
  673.         {
  674.             if (GetEntPropFloat(iClient, Prop_Send, "m_flMaxspeed") < flBoostSpeed)
  675.                 SetEntPropFloat(iClient, Prop_Send, "m_flMaxspeed", flBoostSpeed);
  676.         }
  677.     }
  678. }
  679.  
  680. /* Jump Pad Effects */
  681. void LaunchPlayer(int iClient)
  682. {
  683.     float vVel[3], vVel2[3];
  684.     float flMaxSpeed = GetEntPropFloat(iClient, Prop_Send, "m_flMaxspeed");
  685.     float flJumpSpeed = cvarPads[JumpSpeed].FloatValue;
  686.     float flJumpHeight = cvarPads[JumpHeight].FloatValue;
  687.     float flRatio = flJumpSpeed / flMaxSpeed;
  688.    
  689.     GetEntPropVector(iClient, Prop_Data, "m_vecAbsVelocity", vVel);
  690.    
  691.     ScaleVector(vVel, flRatio);  //This ensures all classes will have the same launch distance.
  692.    
  693.     /* Get the horizontal vectors */
  694.     vVel2[0] = vVel[0];
  695.     vVel2[1] = vVel[1];
  696.    
  697.     float flHorizontalSpeed = GetVectorLength(vVel2);
  698.     if (flHorizontalSpeed > flJumpSpeed)
  699.         ScaleVector(vVel, flJumpSpeed / flHorizontalSpeed);
  700.    
  701.     vVel[2] = flJumpHeight;
  702.     if (GetEntityFlags(iClient) & FL_DUCKING)
  703.     {
  704.         ScaleVector(vVel, cvarPads[JumpCrouchSpeedMult].FloatValue);
  705.         vVel[2] = flJumpHeight * cvarPads[JumpCrouchHeightMult].FloatValue;
  706.     }
  707.    
  708.     #if defined DEBUG
  709.     PrintToChatAll("Speed: %.2f (%.0f%%)", flHorizontalSpeed / flRatio, flHorizontalSpeed / flJumpSpeed * 100);
  710.     PrintToChatAll("SpeedLaunch: %.2f", flHorizontalSpeed);
  711.     PrintToChatAll("ScaleVector: %.2f", flJumpSpeed / flHorizontalSpeed);
  712.     PrintToChatAll("Height: %.2f", vVel[2]);
  713.     #endif
  714.    
  715.     if (vVel[2] < 300.0)    //Teleport the player up slightly to allow 'flJumpHeight' values lower than 300.0.
  716.     {
  717.         float vPos[3];
  718.         GetClientAbsOrigin(iClient, vPos);
  719.         vPos[2] += 20.0;
  720.         SetEntPropVector(iClient, Prop_Data, "m_vecAbsOrigin", vPos);
  721.     }
  722.    
  723.     SetEntPropVector(iClient, Prop_Data, "m_vecAbsVelocity", vVel);
  724.     SetEntProp(iClient, Prop_Send, "m_bJumping", cvarPads[JumpBlockSnipers].IntValue);
  725.    
  726.     TF2_AddCondition(iClient, TFCond_GrapplingHookSafeFall, TFCondDuration_Infinite);
  727.    
  728.     SDKHook(iClient, SDKHook_OnTakeDamage, OnPlayerTakeDamage);
  729. }
  730.  
  731. public Action OnPlayerTakeDamage(int iClient, int &iAttacker, int &iInflictor, float &flDamage, int &iDamageType, int &iWeapon, float flDamageForce[3], float flDamagePosition[3], int iDamageCustom)
  732. {
  733.     if (!IsValidClient(iClient))
  734.         return Plugin_Continue;
  735.    
  736.     if (iDamageType & DMG_FALL && Pad_IsPlayerInCond(iClient, PadCond_NoFallDmg))
  737.     {          
  738.         if (Pad_IsPlayerInCond(iClient, PadCond_DelayResponse))
  739.         {
  740.             if (!Pad_IsPlayerInCond(iClient, PadCond_Boost)) //If player is still being boosted, they'll need to say thanks later.
  741.             {
  742.                 Pad_RemoveCond(iClient, PadCond_DelayResponse);
  743.                 TF2_SayTeleportResponse(iClient);
  744.             }
  745.         }
  746.        
  747.         TF2_AddCondition(iClient, TFCond_PasstimeInterception, 0.01);
  748.        
  749.         SDKUnhook(iClient, SDKHook_OnTakeDamage, OnPlayerTakeDamage);
  750.        
  751.         #if defined DEBUG
  752.         PrintToChatAll("%N's Fall Damage negated!", iClient);
  753.         #endif
  754.        
  755.         // return Plugin_Handled; //Returning Plugin_Handled causes fall damage sound+blood, which I don't want
  756.     }
  757.    
  758.     return Plugin_Continue;
  759. }
  760.  
  761. public void TF2_OnConditionRemoved(int iClient, TFCond iCond)
  762. {
  763.     switch (iCond)
  764.     {
  765.         case TFCond_GrapplingHookSafeFall:
  766.         {
  767.             if (Pad_RemoveCond(iClient, PadCond_NoFallDmg))
  768.             {
  769.                 SDKUnhook(iClient, SDKHook_OnTakeDamage, OnPlayerTakeDamage);
  770.                
  771.                 if (Pad_IsPlayerInCond(iClient, PadCond_DelayResponse))
  772.                 {
  773.                     if (!Pad_IsPlayerInCond(iClient, PadCond_Boost)) //If player is still being boosted, they'll need to say thanks later.
  774.                     {
  775.                         Pad_RemoveCond(iClient, PadCond_DelayResponse);
  776.                         TF2_SayTeleportResponse(iClient);
  777.                     }
  778.                 }
  779.                 #if defined DEBUG
  780.                 PrintToChatAll("%N OTD Unhooked!", iClient);
  781.                 #endif
  782.             }
  783.         }
  784.     }
  785. }
  786.  
  787. public void OnEntityDestroyed(int entity)
  788. {
  789.     if(entity<=2047 && entity>0)
  790.     {
  791.         g_iPadType[entity]=PadType_None;
  792.     }
  793. }
  794.  
  795. /* Pad Creation/Revertion */
  796. void ConvertTeleporterToPad(int iEnt, int iPadType, bool bAddHealth)
  797. {
  798.     g_iPadType[iEnt] = iPadType;
  799.    
  800.     // SetEntityModel(iEnt, "MODEL_PAD");   //Coming soonâ„¢, maybe...
  801.    
  802.     SetEntProp(iEnt, Prop_Send, "m_iHighestUpgradeLevel", 3);   //Set Pads to level 3 for cosmetic reasons related to recharging
  803.     SetEntProp(iEnt, Prop_Send, "m_iUpgradeLevel", 3);
  804.     SetEntProp(iEnt, Prop_Send, "m_bMiniBuilding", true);           //Prevent upgrades and metal from gibs
  805.     SetEntProp(iEnt, Prop_Send, "m_iMaxHealth", cvarPads[PadHealth].IntValue);          //Max HP reduced to 100
  806.     if (bAddHealth)
  807.     {
  808.         SetVariantInt(RoundFloat(cvarPads[PadHealth].IntValue * 0.5));
  809.         AcceptEntityInput(iEnt, "AddHealth", iEnt); //Spawns at 50% HP.
  810.         SetEntProp(iEnt, Prop_Send, "m_iTimesUsed", 0);
  811.     }
  812.    
  813.     SetEntProp(iEnt, Prop_Send, "m_nBody", 2);  //Give the arrow to Exits as well.
  814.     SetEntPropFloat(iEnt, Prop_Send, "m_flModelScale", cvarPads[PadSize].FloatValue);
  815.     RequestFrame(ResetSkin, iEnt); //Setting m_bMiniBuilding tries to set the skin to a 'mini' skin. Since teles don't have one, reset the skin.
  816.    
  817.     g_iObjectParticle[iEnt] = EntIndexToEntRef(CreatePadParticle(iEnt));
  818.    
  819.     TF2_SetMatchingTeleporter(iEnt, iEnt); //Set its matching Teleporter to itself.
  820.    
  821.     SDKHook(iEnt, SDKHook_Touch, OnPadTouch);
  822. }
  823.  
  824. void ConvertPadToTeleporter(int iEnt)
  825. {
  826.     g_iPadType[iEnt] = PadType_None;
  827.    
  828.     SetEntProp(iEnt, Prop_Send, "m_iHighestUpgradeLevel", 1);
  829.     SetEntProp(iEnt, Prop_Send, "m_iUpgradeLevel", 1);
  830.     SetEntProp(iEnt, Prop_Send, "m_bMiniBuilding", false);
  831.     SetVariantInt(150);
  832.     AcceptEntityInput(iEnt, "SetHealth", iEnt);
  833.    
  834.     SetEntProp(iEnt, Prop_Send, "m_iTimesUsed", 0);
  835.    
  836.     SetEntProp(iEnt, Prop_Send, "m_nBody", 1);
  837.     SetEntPropFloat(iEnt, Prop_Send, "m_flModelScale", 1.0);
  838.     RequestFrame(ResetSkin, iEnt);
  839.    
  840.     int iObjParti = EntRefToEntIndex(g_iObjectParticle[iEnt]);
  841.     if (IsValidEntity(iObjParti))
  842.     {
  843.         AcceptEntityInput(iObjParti, "Kill");
  844.     }
  845.     g_iObjectParticle[iEnt] = -1;
  846.    
  847.     SDKUnhook(iEnt, SDKHook_Touch, OnPadTouch);
  848. }
  849.  
  850. void ConvertAllPadsToTeleporters()
  851. {
  852.     int i = -1;
  853.     while ((i = FindEntityByClassname2(i, "obj_teleporter")) != -1)
  854.     {
  855.         if (IsValidEntity(i) && g_iPadType[i])
  856.             ConvertPadToTeleporter(i);
  857.            
  858.         if (!GetEntProp(i, Prop_Send, "m_bHasSapper") && !GetEntProp(i, Prop_Send, "m_bPlasmaDisable") && TF2_GetMatchingTeleporter(i) == i)
  859.             TF2_EnableObject(i);    //Re-enable disabled un-matched Teleporters
  860.            
  861.         TF2_SetMatchingTeleporter(i, -1); //If MatchingTeleporter is invalid, the game will auto-search for one (if it's active). Do this for all Teleporters just to be safe.
  862.     }
  863. }
  864.  
  865. int CreatePadParticle(int iPad)
  866. {
  867.     TFTeam iPadTeam = view_as<TFTeam>(GetEntProp(iPad, Prop_Send, "m_iTeamNum"));
  868.     char szParticleName[128];
  869.     switch (g_iPadType[iPad])
  870.     {
  871.         case PadType_Boost: strcopy(szParticleName, sizeof(szParticleName), "powerup_icon_haste");
  872.         case PadType_Jump:  strcopy(szParticleName, sizeof(szParticleName), "powerup_icon_agility");
  873.     }
  874.     switch (iPadTeam)
  875.     {
  876.         case TFTeam_Red:    StrCat(szParticleName, sizeof(szParticleName), "_red");
  877.         case TFTeam_Blue:   StrCat(szParticleName, sizeof(szParticleName), "_blue");
  878.     }
  879.     int iParticle = SpawnParticle(szParticleName);
  880.    
  881.     float vPos[3];
  882.     GetEntPropVector(iPad, Prop_Data, "m_vecAbsOrigin", vPos);
  883.     vPos[2] += 40.0;
  884.     TeleportEntity(iParticle, vPos, NULL_VECTOR, NULL_VECTOR);
  885.    
  886.     SetParent(iPad, iParticle);
  887.    
  888.     return iParticle;
  889. }
  890.  
  891. /* Commands */
  892. public Action TogglePadsMenuCmd(int iClient, int nArgs)
  893. {
  894.     if (!iClient)   //if client was the server console
  895.     {
  896.         ReplyToCommand(iClient, "This command can only be used in-game!");
  897.         return Plugin_Handled;
  898.     }
  899.    
  900.     ShowPadMenu(iClient);
  901.     return Plugin_Handled;
  902. }
  903.  
  904. public Action ShowPadsInfoCmd(int iClient, int nArgs)
  905. {
  906.     if (!iClient)
  907.     {
  908.         ReplyToCommand(iClient, "This command can only be used in-game!");
  909.         return Plugin_Handled;
  910.     }
  911.    
  912.     ShowPadInfo(iClient);
  913.    
  914.     return Plugin_Handled;
  915. }
  916.  
  917. /* Menus/Panels */
  918. void ShowPadMenu(int iClient)
  919. {
  920.     if (!CheckCommandAccess(iClient, "sm_engipads_override", 0))
  921.     {
  922.         CPrintToChat(iClient, "{orange}[EngiPads]{default} %t", "padphrase_noaccess");
  923.         return;
  924.     }
  925.    
  926.     Menu menu = new Menu(MenuHandlerPadMenu);
  927.    
  928.     char szTranslation[64];
  929.    
  930.     Format(szTranslation, sizeof(szTranslation), "%T", "padphrase_menutitle", iClient);
  931.     menu.SetTitle(szTranslation);
  932.    
  933.     Format(szTranslation, sizeof(szTranslation), "%T", "padphrase_menuenable", iClient);
  934.     menu.AddItem("on", szTranslation);
  935.    
  936.     Format(szTranslation, sizeof(szTranslation), "%T", "padphrase_menudisable", iClient);
  937.     menu.AddItem("off", szTranslation);
  938.    
  939.     menu.ExitButton = true;
  940.        
  941.     menu.Display(iClient, MENU_TIME_FOREVER);
  942. }
  943.  
  944. void ShowPadInfo(int iClient)
  945. {
  946.     Panel panel = new Panel();
  947.    
  948.     char szTranslation[256];
  949.     Format(szTranslation, sizeof(szTranslation), "%T", "padphrase_infotitle", iClient);
  950.     panel.SetTitle(szTranslation);
  951.    
  952.     Format(szTranslation, sizeof(szTranslation), "%T", "padphrase_infogeneral", iClient, cvarPads[PadHealth].IntValue);
  953.     panel.DrawItem(szTranslation, ITEMDRAW_RAWLINE);
  954.    
  955.     if (cvarPads[PadsEnabled].IntValue != EngiPads_JumpOnly)
  956.     {  
  957.         Format(szTranslation, sizeof(szTranslation), "%T", "padphrase_infoboost", iClient, cvarPads[BoostDuration].FloatValue, cvarPads[BoostCooldown].FloatValue);
  958.         panel.DrawItem(szTranslation, ITEMDRAW_RAWLINE);
  959.     }
  960.    
  961.     if (cvarPads[PadsEnabled].IntValue != EngiPads_BoostOnly)
  962.     {
  963.         Format(szTranslation, sizeof(szTranslation), "%T", "padphrase_infojump", iClient, cvarPads[JumpCooldown].FloatValue);
  964.         panel.DrawItem(szTranslation, ITEMDRAW_RAWLINE);
  965.     }
  966.    
  967.     if (cvarPads[BlockEureka].BoolValue)
  968.     {  
  969.         Format(szTranslation, sizeof(szTranslation), "%T", "padphrase_infoeureka", iClient);
  970.         panel.DrawItem(szTranslation, ITEMDRAW_RAWLINE);
  971.     }
  972.    
  973.     if (cvarPads[JumpBlockSnipers].BoolValue)
  974.     {
  975.         Format(szTranslation, sizeof(szTranslation), "%T", "padphrase_infosniper", iClient);
  976.         panel.DrawItem(szTranslation, ITEMDRAW_RAWLINE);
  977.     }
  978.    
  979.     if (cvarPads[BoostBlockAiming].BoolValue)
  980.     {
  981.         Format(szTranslation, sizeof(szTranslation), "%T", "padphrase_infoaiming", iClient);
  982.         panel.DrawItem(szTranslation, ITEMDRAW_RAWLINE);
  983.     }
  984.    
  985.     Format(szTranslation, sizeof(szTranslation), "%T", "padphrase_infoexit", iClient);
  986.     panel.DrawItem(szTranslation, ITEMDRAW_CONTROL);
  987.    
  988.     panel.Send(iClient, MenuHandlerPadInfo, MENU_TIME_FOREVER);
  989. }
  990.  
  991. int MenuHandlerPadMenu(Menu menu, MenuAction iAction, int iClient, int iOption)
  992. {
  993.     if (iAction == MenuAction_Select)
  994.     {
  995.         if (!iOption) //Enabled EngiPads
  996.         {
  997.             CPrintToChat(iClient, "{orange}[EngiPads]{default} %t", "padphrase_enabled");
  998.         }
  999.         else    //Disabled EngiPads
  1000.         {
  1001.             CPrintToChat(iClient, "{orange}[EngiPads]{default} %t", "padphrase_disabled");
  1002.         }
  1003.        
  1004.         SetClientPadsEnabled(iClient, iOption ? false : true);
  1005.     }
  1006.     else if (iAction == MenuAction_End)
  1007.     {
  1008.         delete menu;
  1009.     }
  1010. }
  1011.  
  1012. int MenuHandlerPadInfo(Menu menu, MenuAction iAction, int iClient, int iOption)
  1013. {
  1014.     delete menu;
  1015. }
  1016.  
  1017. /* "Stocks" */
  1018. stock int TF2_GetMatchingTeleporter(int iTele)  //Get the matching teleporter entity of a given Teleporter
  1019. {
  1020.     int iMatch = -1;
  1021.    
  1022.     if (IsValidEntity(iTele) && HasEntProp(iTele, Prop_Send, g_szOffsetStartProp))
  1023.     {
  1024.         int iOffs = FindSendPropInfo("CObjectTeleporter", g_szOffsetStartProp) + g_iOffsetMatchingTeleporter;
  1025.         iMatch = GetEntDataEnt2(iTele, iOffs);
  1026.     }
  1027.    
  1028.     return iMatch;
  1029. }
  1030.  
  1031. stock void TF2_SetMatchingTeleporter(int iTele, int iMatch) //Set the matching teleporter entity of a given Teleporter
  1032. {
  1033.     if (IsValidEntity(iTele) && HasEntProp(iTele, Prop_Send, g_szOffsetStartProp))
  1034.     {
  1035.         int iOffs = FindSendPropInfo("CObjectTeleporter", g_szOffsetStartProp) + g_iOffsetMatchingTeleporter;
  1036.         SetEntDataEnt2(iTele, iOffs, iMatch, true);
  1037.     }
  1038. }
  1039.  
  1040. stock void TF2_SayTeleportResponse(int iClient) //Plays the appropriate ThanksForTheTeleporter response line.
  1041. {
  1042.     if (IsValidClient(iClient) && IsPlayerAlive(iClient))
  1043.     {
  1044.         char szVO[512];
  1045.        
  1046.         TFClassType iClass = TF2_GetPlayerClass(iClient);
  1047.         if (iClass == TFClass_Spy && (TF2_IsPlayerInCondition(iClient, TFCond_Disguised) && GetEntProp(iClient, Prop_Send, "m_nDisguiseClass") != view_as<int>(iClass)))
  1048.             iClass = view_as<TFClassType>(GetEntProp(iClient, Prop_Send, "m_nDisguiseClass"));
  1049.        
  1050.         switch (iClass)
  1051.         {
  1052.             case TFClass_Scout:
  1053.             {
  1054.                 Format(szVO, sizeof(szVO), "Scout.ThanksForTheTeleporter0%d", GetRandomInt(1, 3));
  1055.             }
  1056.             case TFClass_Soldier:
  1057.             {
  1058.                 Format(szVO, sizeof(szVO), "Soldier.ThanksForTheTeleporter0%d", GetRandomInt(1, 3));
  1059.             }
  1060.             case TFClass_Pyro:
  1061.             {
  1062.                 strcopy(szVO, sizeof(szVO), "Pyro.ThanksForTheTeleporter01");
  1063.             }
  1064.             case TFClass_DemoMan:
  1065.             {
  1066.                 Format(szVO, sizeof(szVO), "Demoman.ThanksForTheTeleporter0%d", GetRandomInt(1, 2));
  1067.             }
  1068.             case TFClass_Heavy:
  1069.             {
  1070.                 Format(szVO, sizeof(szVO), "Heavy.ThanksForTheTeleporter0%d", GetRandomInt(1, 3));
  1071.             }
  1072.             case TFClass_Engineer:
  1073.             {
  1074.                 Format(szVO, sizeof(szVO), "Engineer.ThanksForTheTeleporter0%d", GetRandomInt(1, 2));
  1075.             }
  1076.             case TFClass_Medic:
  1077.             {
  1078.                 Format(szVO, sizeof(szVO), "Medic.ThanksForTheTeleporter0%d", GetRandomInt(1, 3));
  1079.             }
  1080.             case TFClass_Sniper:
  1081.             {
  1082.                 Format(szVO, sizeof(szVO), "Sniper.ThanksForTheTeleporter0%d", GetRandomInt(1, 3));
  1083.             }
  1084.             case TFClass_Spy:
  1085.             {
  1086.                 Format(szVO, sizeof(szVO), "Spy.ThanksForTheTeleporter0%d", GetRandomInt(1, 3));
  1087.             }
  1088.         }
  1089.         EmitGameSoundToAll(szVO, iClient);
  1090.     }
  1091. }
  1092.  
  1093. /* Returns true if player has condition */
  1094. stock bool Pad_IsPlayerInCond(int iClient, PadCond fCond)
  1095. {
  1096.     if (IsValidClient(iClient))
  1097.     {
  1098.         if (Pad_GetConds(iClient) & fCond) //Check if player has specified custom condition flag
  1099.             return true;
  1100.     }
  1101.     return false;
  1102. }
  1103.  
  1104. /* Returns true if condition was added to player (as in, not already present) */
  1105. stock bool Pad_AddCond(int iClient, PadCond fCond)
  1106. {
  1107.     if (IsValidClient(iClient))
  1108.     {
  1109.         if (!Pad_IsPlayerInCond(iClient, fCond))
  1110.         {
  1111.             g_fPadCondFlags[iClient] |= fCond;
  1112.             return true;
  1113.         }
  1114.     }
  1115.     return false;
  1116. }
  1117.  
  1118. /* Returns true if condition was removed from player */
  1119. stock bool Pad_RemoveCond(int iClient, PadCond fCond)
  1120. {
  1121.     if (IsValidClient(iClient))
  1122.     {
  1123.         if (Pad_IsPlayerInCond(iClient, fCond))
  1124.         {
  1125.             g_fPadCondFlags[iClient] &= ~fCond;
  1126.             return true;
  1127.         }
  1128.     }
  1129.     return false;
  1130. }
  1131.  
  1132. /* Set PadCondFlags directly*/
  1133. stock void Pad_SetConds(int iClient, PadCond fConds)
  1134. {
  1135.     g_fPadCondFlags[iClient] = fConds;
  1136. }
  1137.  
  1138. /* Get PadCondFlags directly*/
  1139. stock PadCond Pad_GetConds(int iClient)
  1140. {
  1141.     return g_fPadCondFlags[iClient];
  1142. }
  1143.  
  1144. stock int GetPadType(int iPad) //Actually just a GetDesiredBuildRotations stock.
  1145. {
  1146.     int iType = PadType_None;
  1147.    
  1148.     if (IsValidEntity(iPad))
  1149.     {
  1150.         iType = (GetEntProp(iPad, Prop_Send, "m_iDesiredBuildRotations") % 2) + 1; //Rotation 0/2 (Horizontal) = PadType 1 | Rotation 1/3 (Vertical) = PadType 2.
  1151.         switch (cvarPads[PadsEnabled].IntValue)
  1152.         {
  1153.             case EngiPads_BoostOnly: iType = PadType_Boost;
  1154.             case EngiPads_JumpOnly: iType = PadType_Jump;
  1155.         }
  1156.     }
  1157.    
  1158.     return iType;
  1159. }
  1160.  
  1161. stock int TF2_GetBuildingState(int iBuilding)
  1162. {
  1163.     int iState = -1;
  1164.    
  1165.     if (IsValidEntity(iBuilding))
  1166.     {
  1167.         iState = GetEntProp(iBuilding, Prop_Send, "m_iState");
  1168.     }
  1169.    
  1170.     return iState;
  1171. }
  1172.  
  1173. stock void TF2_SetBuildingState(int iBuilding, int iState = 0)
  1174. {  
  1175.     if (IsValidEntity(iBuilding))
  1176.     {
  1177.         SetEntProp(iBuilding, Prop_Send, "m_iState", iState);
  1178.     }
  1179. }
  1180.  
  1181. stock void TF2_DisableObject(int iObj)
  1182. {
  1183.     if (IsValidEntity(iObj))
  1184.     {
  1185.         AcceptEntityInput(iObj, "Disable");
  1186.     }
  1187. }
  1188.  
  1189. stock void TF2_EnableObject(int iObj)
  1190. {
  1191.     if (IsValidEntity(iObj))
  1192.     {
  1193.         AcceptEntityInput(iObj, "Enable");
  1194.     }
  1195. }
  1196.  
  1197. stock void ResetSkin(int iEnt)
  1198. {
  1199.     if (IsValidEntity(iEnt) && HasEntProp(iEnt, Prop_Send, "m_nSkin"))
  1200.     {
  1201.         int iTeam = GetEntProp(iEnt, Prop_Data, "m_iTeamNum");
  1202.         SetEntProp(iEnt, Prop_Send, "m_nSkin", iTeam - 2);
  1203.     }
  1204. }
  1205.  
  1206. stock void PrintPadTypeNameToClient(int iObjType, int iClient)
  1207. {
  1208.     char szType[64];
  1209.    
  1210.     switch(iObjType)
  1211.     {
  1212.         case PadType_Boost: strcopy(szType, sizeof(szType), "padphrase_boost");
  1213.         case PadType_Jump: Format(szType, sizeof(szType), "padphrase_jump");
  1214.     }
  1215.     CPrintToChatEx(iClient, iClient, "{orange}[EngiPads]{default} %t", "padphrase_deploy", szType);
  1216. }
  1217.  
  1218. stock bool GetClientPadsEnabled(int iClient)
  1219. {
  1220.     if (!IsValidClient(iClient)) return false;
  1221.     if (IsFakeClient(iClient)) return cvarPads[BotsCanBuild].BoolValue;
  1222.     if (!AreClientCookiesCached(iClient)) return false;
  1223.     char szToggle[3];
  1224.     GetClientCookie(iClient, g_hPadCookie, szToggle, sizeof(szToggle));
  1225.     return view_as<bool>(StringToInt(szToggle));
  1226. }
  1227.  
  1228. stock void SetClientPadsEnabled(int iClient, bool bEnabled)
  1229. {
  1230.     if (!IsValidClient(iClient)) return;
  1231.     if (IsFakeClient(iClient)) return;
  1232.     if (!AreClientCookiesCached(iClient)) return;
  1233.     char szToggle[3];
  1234.     IntToString(view_as<int>(bEnabled), szToggle, sizeof(szToggle));
  1235.     SetClientCookie(iClient, g_hPadCookie, szToggle);
  1236. }
  1237.  
  1238. stock bool IsValidClient(int iClient)
  1239. {
  1240.     return (0 < iClient && iClient <= MaxClients && IsClientInGame(iClient));
  1241. }
  1242.  
  1243. stock int SpawnParticle(char[] szParticleType)
  1244. {
  1245.     int iParti = CreateEntityByName("info_particle_system");
  1246.     if (IsValidEntity(iParti))
  1247.     {
  1248.         DispatchKeyValue(iParti, "effect_name", szParticleType);
  1249.         DispatchSpawn(iParti);
  1250.         ActivateEntity(iParti);
  1251.     }
  1252.     return iParti;
  1253. }
  1254.  
  1255. stock void SetParent(int iParent, int iChild, char[] szAttachPoint = "")
  1256. {
  1257.     SetVariantString("!activator");
  1258.     AcceptEntityInput(iChild, "SetParent", iParent, iChild);
  1259.    
  1260.     if (szAttachPoint[0] != '\0')
  1261.     {
  1262.         if (IsValidClient(iParent) && IsPlayerAlive(iParent))
  1263.         {
  1264.             SetVariantString(szAttachPoint);
  1265.             AcceptEntityInput(iChild, "SetParentAttachmentMaintainOffset", iChild, iChild, 0);
  1266.         }
  1267.     }
  1268. }
  1269.  
  1270. stock void ClearTimer(Handle &hTimer)
  1271. {
  1272.     if (hTimer != INVALID_HANDLE)
  1273.     {
  1274.         KillTimer(hTimer);
  1275.         hTimer = INVALID_HANDLE;
  1276.        
  1277.         #if defined DEBUG
  1278.         PrintToChatAll("Timer cleared!");
  1279.         #endif
  1280.     }
  1281. }
  1282.  
  1283. stock int FindEntityByClassname2(int startEnt, char[] classname)
  1284. {
  1285.     /* If startEnt isn't valid shifting it back to the nearest valid one */
  1286.     while (startEnt > -1 && !IsValidEntity(startEnt)) startEnt--;
  1287.     return FindEntityByClassname(startEnt, classname);
  1288. }
  1289.  
  1290. #if !defined _smlib_included
  1291. /* SMLIB
  1292.  * Precaches the given particle system.
  1293.  * It's best to call this OnMapStart().
  1294.  * Code based on Rochellecrab's, thanks.
  1295.  *
  1296.  * @param particleSystem    Name of the particle system to precache.
  1297.  * @return                  Returns the particle system index, INVALID_STRING_INDEX on error.
  1298.  */
  1299. stock int PrecacheParticleSystem(char[] particleSystem)
  1300. {
  1301.     int particleEffectNames = INVALID_STRING_TABLE;
  1302.  
  1303.     if (particleEffectNames == INVALID_STRING_TABLE) {
  1304.         if ((particleEffectNames = FindStringTable("ParticleEffectNames")) == INVALID_STRING_TABLE) {
  1305.             return INVALID_STRING_INDEX;
  1306.         }
  1307.     }
  1308.  
  1309.     int index = FindStringIndex2(particleEffectNames, particleSystem);
  1310.     if (index == INVALID_STRING_INDEX) {
  1311.         int numStrings = GetStringTableNumStrings(particleEffectNames);
  1312.         if (numStrings >= GetStringTableMaxStrings(particleEffectNames)) {
  1313.             return INVALID_STRING_INDEX;
  1314.         }
  1315.  
  1316.         AddToStringTable(particleEffectNames, particleSystem);
  1317.         index = numStrings;
  1318.     }
  1319.  
  1320.     return index;
  1321. }
  1322.  
  1323. /* SMLIB
  1324.  * Rewrite of FindStringIndex, because in my tests
  1325.  * FindStringIndex failed to work correctly.
  1326.  * Searches for the index of a given string in a string table.
  1327.  *
  1328.  * @param tableidx      A string table index.
  1329.  * @param str           String to find.
  1330.  * @return              String index if found, INVALID_STRING_INDEX otherwise.
  1331.  */
  1332. stock int FindStringIndex2(int tableidx, char[] str)
  1333. {
  1334.     char buf[1024];
  1335.  
  1336.     int numStrings = GetStringTableNumStrings(tableidx);
  1337.     for (int i=0; i < numStrings; i++) {
  1338.         ReadStringTable(tableidx, i, buf, sizeof(buf));
  1339.  
  1340.         if (StrEqual(buf, str)) {
  1341.             return i;
  1342.         }
  1343.     }
  1344.  
  1345.     return INVALID_STRING_INDEX;
  1346. }
  1347. #endif
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement