501 lines
15 KiB
SourcePawn
501 lines
15 KiB
SourcePawn
#include <sourcemod>
|
|
#include <sdktools>
|
|
#include <cstrike>
|
|
#include <multicolors>
|
|
#include <zombiereloaded>
|
|
|
|
#pragma semicolon 1
|
|
#pragma newdecls required
|
|
|
|
#define PARACHUTE_MDL "models/mark2580/pubg/carepackage/parachutecare.mdl"
|
|
|
|
ConVar g_cvParachuteFallSpeed;
|
|
ConVar g_cvParachuteLinear;
|
|
ConVar g_cvParachuteDecrease;
|
|
ConVar g_cvDelayPlaneLanded;
|
|
|
|
Handle g_hTimerTriggersHook = INVALID_HANDLE;
|
|
Handle g_hTimerPlaneDropTrigger = INVALID_HANDLE;
|
|
Handle g_hPlaneDoorOpen = INVALID_HANDLE;
|
|
Handle g_hTimerPlaneEndPath = INVALID_HANDLE;
|
|
|
|
int g_iVelocity = -1;
|
|
int g_iParachuteEntity[MAXPLAYERS+1];
|
|
int g_iTriggerTeleportID = 0;
|
|
int g_iTriggerTemplateID = 0;
|
|
int g_iTriggerMultipleID = 0;
|
|
int g_iPlaneEndPathID = 0;
|
|
|
|
bool g_bFallSpeed[MAXPLAYERS +1] = {false, ...};
|
|
bool g_bInUse[MAXPLAYERS+1] = {false, ...};
|
|
bool g_bHasParachuteModel[MAXPLAYERS+1] = {false, ...};
|
|
bool g_bRoundStarted = false;
|
|
bool g_bPlayerJumpedFormPlane[MAXPLAYERS+1] = {false, ...};
|
|
bool g_bPlayerLanded[MAXPLAYERS+1] = {false, ...};
|
|
|
|
bool g_bParaLinearFallSpeed = false;
|
|
float g_fFallSpeed = 100.0;
|
|
float g_fDecrease = 0.0;
|
|
float g_fAfterExplodeDelay = 45.0;
|
|
|
|
// Based on SWAT_88's plugin from SRCDSLAB
|
|
public Plugin myinfo =
|
|
{
|
|
name = "VScript - ze_stardust_battleground",
|
|
author = ".Rushaway & SWAT_88",
|
|
description = "Give a single parachute to player for jumping out of the plane.",
|
|
version = "1.0.0",
|
|
url = "https://github.com/srcdslab/sm-plugin-Parachute"
|
|
};
|
|
|
|
public void OnPluginStart()
|
|
{
|
|
g_cvParachuteLinear = CreateConVar("sm_stardust_parachute_linear", "1", "0: disables linear fallspeed - 1: enables it");
|
|
g_cvParachuteFallSpeed = CreateConVar("sm_stardust_parachute_fallspeed", "135", "Speed of the fall when you use the parachute");
|
|
g_cvParachuteDecrease = CreateConVar("sm_stardust_parachute_decrease", "75", "0: dont use Realistic velocity-decrease - x: sets the velocity-decrease");
|
|
g_cvDelayPlaneLanded = CreateConVar("sm_stardust_parachute_delay", "45", "Remove parachute after x seconds of the plane exploded");
|
|
|
|
g_iVelocity = FindSendPropInfo("CBasePlayer", "m_vecVelocity[0]");
|
|
g_bParaLinearFallSpeed = g_cvParachuteLinear.BoolValue;
|
|
g_fFallSpeed = g_cvParachuteFallSpeed.FloatValue;
|
|
g_fDecrease = g_cvParachuteDecrease.FloatValue;
|
|
g_fAfterExplodeDelay = g_cvDelayPlaneLanded.FloatValue;
|
|
|
|
HookConVarChange(g_cvParachuteLinear, OnConVarChanged);
|
|
HookConVarChange(g_cvParachuteFallSpeed, OnConVarChanged);
|
|
HookConVarChange(g_cvParachuteDecrease, OnConVarChanged);
|
|
HookConVarChange(g_cvDelayPlaneLanded, OnConVarChanged);
|
|
|
|
AutoExecConfig(true);
|
|
}
|
|
|
|
public void OnMapStart()
|
|
{
|
|
VerifyMap(false);
|
|
}
|
|
|
|
public void VerifyMap(bool bForceUnload)
|
|
{
|
|
char sCurMap[256];
|
|
GetCurrentMap(sCurMap, sizeof(sCurMap));
|
|
bool bValidMap = strncmp(sCurMap, "ze_stardust_battleground", 24, false) == 0;
|
|
LogMessage("bValidMap %d", bValidMap);
|
|
if (bForceUnload || !bValidMap || g_iVelocity == -1)
|
|
{
|
|
if (bValidMap && g_iVelocity == -1)
|
|
SetFailState("Failed to find m_vecVelocity[0] for CBasePlayer");
|
|
//better to let the server control loading and unloading than the plugin itself.
|
|
//having many plugins unload/load on mapchange can cause very slow mapchanges.
|
|
}
|
|
else
|
|
{
|
|
// Is it needed if the model is already present in the map?
|
|
// PrecacheModel("models/mark2580/pubg/carepackage");
|
|
// AddFileToDownloadsTable(Line);
|
|
|
|
HookEvent("player_death", Event_PlayerDeath);
|
|
HookEvent("round_start", Event_OnRoundStart);
|
|
HookEvent("round_end", Event_OnRoundEnd);
|
|
}
|
|
}
|
|
|
|
public void OnMapEnd()
|
|
{
|
|
EndRoundCleanUp();
|
|
|
|
g_hTimerTriggersHook = INVALID_HANDLE;
|
|
g_hTimerPlaneDropTrigger = INVALID_HANDLE;
|
|
g_hPlaneDoorOpen = INVALID_HANDLE;
|
|
g_hTimerPlaneEndPath = INVALID_HANDLE;
|
|
|
|
UnhookEvent("player_death", Event_PlayerDeath);
|
|
UnhookEvent("round_start", Event_OnRoundStart);
|
|
UnhookEvent("round_end", Event_OnRoundEnd);
|
|
|
|
// Force plugin to unload
|
|
VerifyMap(true);
|
|
}
|
|
|
|
public void OnConVarChanged(ConVar convar, const char[] oldValue, const char[] newValue)
|
|
{
|
|
if (convar == g_cvParachuteLinear)
|
|
g_bParaLinearFallSpeed = g_cvParachuteLinear.BoolValue;
|
|
else if (convar == g_cvParachuteFallSpeed)
|
|
g_fFallSpeed = g_cvParachuteFallSpeed.FloatValue;
|
|
else if (convar == g_cvParachuteDecrease)
|
|
g_fDecrease = g_cvParachuteDecrease.FloatValue;
|
|
else if (convar == g_cvDelayPlaneLanded)
|
|
g_fAfterExplodeDelay = g_cvDelayPlaneLanded.FloatValue;
|
|
}
|
|
|
|
public void OnClientDisconnect(int client)
|
|
{
|
|
StopParachute(client);
|
|
}
|
|
|
|
public void Event_PlayerDeath(Event event, const char[] name, bool dontBroadcast)
|
|
{
|
|
int client = GetClientOfUserId(event.GetInt("userid"));
|
|
StopParachute(client);
|
|
}
|
|
|
|
public void Event_OnRoundStart(Event event, const char[] name, bool dontBroadcast)
|
|
{
|
|
g_bRoundStarted = false;
|
|
// We use 5.0 seconds to let the map initialize the entities and dont stress the server
|
|
g_hTimerTriggersHook = CreateTimer(5.0, Timer_HookTriggers, _, TIMER_FLAG_NO_MAPCHANGE);
|
|
}
|
|
|
|
public void Event_OnRoundEnd(Event event, const char[] name, bool dontBroadcast)
|
|
{
|
|
EndRoundCleanUp();
|
|
}
|
|
|
|
public Action ZR_OnClientInfect(int &client, int &attacker, bool &motherInfect, bool &respawnOverride, bool &respawn)
|
|
{
|
|
MarkPlayerAsLanded(client);
|
|
StopParachute(client);
|
|
return Plugin_Stop;
|
|
}
|
|
|
|
// We need to use OnGameFrame instead of OnPlayerRunCmd for more accurate parachute position rendering
|
|
public void OnGameFrame()
|
|
{
|
|
if (!g_bRoundStarted)
|
|
return;
|
|
|
|
for (int client = 1; client <= MaxClients; client++)
|
|
{
|
|
if (IsClientInGame(client) && !g_bPlayerLanded[client] && IsPlayerAlive(client))
|
|
{
|
|
if (GetClientButtons(client) & IN_USE)
|
|
{
|
|
if (!g_bInUse[client])
|
|
{
|
|
g_bInUse[client] = true;
|
|
g_bFallSpeed[client] = false;
|
|
StartParachute(client, true);
|
|
}
|
|
// else
|
|
// StartParachute(client, false);
|
|
|
|
TeleportParachute(client);
|
|
}
|
|
else
|
|
{
|
|
// Player released the key, parachute will be released
|
|
if (g_bInUse[client])
|
|
{
|
|
MarkPlayerAsLanded(client);
|
|
StopParachute(client);
|
|
CPrintToChat(client, "{pink}[VScripts] {white}You released your parachute.");
|
|
}
|
|
}
|
|
CheckClientLocation(client);
|
|
}
|
|
}
|
|
}
|
|
|
|
stock void StartParachute(int client, bool bOpen)
|
|
{
|
|
float fVelocity[3];
|
|
GetEntDataVector(client, g_iVelocity, fVelocity);
|
|
|
|
float fFallSpeed = g_fFallSpeed * (-1.0);
|
|
|
|
if (fVelocity[2] >= fFallSpeed)
|
|
g_bFallSpeed[client] = true;
|
|
|
|
if (fVelocity[2] < 0.0)
|
|
{
|
|
if ((g_bParaLinearFallSpeed && g_bFallSpeed[client]) || g_fDecrease == 0.0)
|
|
fVelocity[2] = fFallSpeed;
|
|
else
|
|
fVelocity[2] = fVelocity[2] + g_fDecrease;
|
|
|
|
TeleportEntity(client, NULL_VECTOR, NULL_VECTOR, fVelocity);
|
|
SetEntDataVector(client, g_iVelocity, fVelocity);
|
|
SetEntityGravity(client, 0.1);
|
|
|
|
if (bOpen)
|
|
OpenParachute(client);
|
|
}
|
|
}
|
|
|
|
stock void OpenParachute(int client)
|
|
{
|
|
g_iParachuteEntity[client] = CreateEntityByName("prop_dynamic");
|
|
if (!g_iParachuteEntity[client])
|
|
return;
|
|
|
|
DispatchKeyValue(g_iParachuteEntity[client], "model", PARACHUTE_MDL);
|
|
DispatchKeyValue(g_iParachuteEntity[client], "DefaultAnim", "default");
|
|
DispatchKeyValue(g_iParachuteEntity[client], "solid", "0");
|
|
DispatchKeyValue(g_iParachuteEntity[client], "spawnflags", "256");
|
|
DispatchKeyValue(g_iParachuteEntity[client], "rendermode", "1");
|
|
DispatchKeyValue(g_iParachuteEntity[client], "rendercolor", "255 255 255");
|
|
DispatchKeyValue(g_iParachuteEntity[client], "renderamt", "255");
|
|
// Original packed model is big++ if we dont resize it, players views will be blocked when they jump off from the plane
|
|
DispatchKeyValue(g_iParachuteEntity[client], "modelscale", "0.7");
|
|
DispatchKeyValue(g_iParachuteEntity[client], "disablereceiveshadows", "1");
|
|
DispatchKeyValue(g_iParachuteEntity[client], "disableshadows", "1");
|
|
SetEntityMoveType(g_iParachuteEntity[client], MOVETYPE_NOCLIP);
|
|
DispatchSpawn(g_iParachuteEntity[client]);
|
|
g_bHasParachuteModel[client] = true;
|
|
TeleportParachute(client);
|
|
}
|
|
|
|
stock void TeleportParachute(int client)
|
|
{
|
|
if (HasValidParachute(client))
|
|
{
|
|
float fClient_Origin[3], fClient_Angles[3], fParachute_Angles[3];
|
|
|
|
GetClientAbsOrigin(client, fClient_Origin);
|
|
// Adjust the position of the parachute model
|
|
fClient_Origin[2] += 60.0;
|
|
GetClientAbsAngles(client, fClient_Angles);
|
|
fParachute_Angles[1] = fClient_Angles[1];
|
|
TeleportEntity(g_iParachuteEntity[client], fClient_Origin, fParachute_Angles, NULL_VECTOR);
|
|
}
|
|
}
|
|
|
|
stock void StopParachute(int client)
|
|
{
|
|
SetEntityGravity(client, 1.0);
|
|
DeleteParachute(client);
|
|
g_bInUse[client] = false;
|
|
g_bFallSpeed[client] = false;
|
|
}
|
|
|
|
stock void DeleteParachute(int client)
|
|
{
|
|
if (HasValidParachute(client))
|
|
{
|
|
AcceptEntityInput(g_iParachuteEntity[client], "Kill");
|
|
g_bHasParachuteModel[client] = false;
|
|
}
|
|
}
|
|
|
|
stock void CheckClientLocation(int client)
|
|
{
|
|
float fSpeed[3];
|
|
GetEntDataVector(client, g_iVelocity, fSpeed);
|
|
int iFlag = GetEntityFlags(client);
|
|
|
|
if (fSpeed[2] >= 0 || iFlag & FL_ONGROUND)
|
|
{
|
|
StopParachute(client);
|
|
if (g_bPlayerJumpedFormPlane[client] && iFlag & FL_ONGROUND)
|
|
{
|
|
MarkPlayerAsLanded(client);
|
|
CPrintToChat(client, "{pink}[VScripts] {white}You have landed.");
|
|
}
|
|
}
|
|
}
|
|
|
|
stock void EndRoundCleanUp()
|
|
{
|
|
for (int i = 1; i <= MaxClients; i++)
|
|
{
|
|
if (IsClientInGame(i))
|
|
StopParachute(i);
|
|
}
|
|
|
|
if (g_hTimerTriggersHook != INVALID_HANDLE && CloseHandle(g_hTimerTriggersHook))
|
|
g_hTimerTriggersHook = INVALID_HANDLE;
|
|
|
|
if (g_hTimerPlaneDropTrigger != INVALID_HANDLE && CloseHandle(g_hTimerPlaneDropTrigger))
|
|
g_hTimerPlaneDropTrigger = INVALID_HANDLE;
|
|
|
|
if (g_hPlaneDoorOpen != INVALID_HANDLE && CloseHandle(g_hPlaneDoorOpen))
|
|
g_hPlaneDoorOpen = INVALID_HANDLE;
|
|
|
|
if (g_hTimerPlaneEndPath != INVALID_HANDLE && CloseHandle(g_hTimerPlaneEndPath))
|
|
g_hTimerPlaneEndPath = INVALID_HANDLE;
|
|
|
|
g_iTriggerTemplateID = 0;
|
|
g_iTriggerTeleportID = 0;
|
|
g_iTriggerMultipleID = 0;
|
|
g_iPlaneEndPathID = 0;
|
|
|
|
VerifyStartHookedEntity();
|
|
VerifyPlaneJumpHookedEntity();
|
|
}
|
|
|
|
/* TIMERS TO HOOKS ENTITIES */
|
|
public Action Timer_HookTriggers(Handle timer)
|
|
{
|
|
g_hTimerTriggersHook = INVALID_HANDLE;
|
|
|
|
// Credits
|
|
char sBuffer[64];
|
|
Handle hPlugin = GetMyHandle();
|
|
GetPluginInfo(hPlugin, PlInfo_Author, sBuffer, sizeof(sBuffer));
|
|
CPrintToChatAll("{pink}[VScripts] {white}Map using VScripts made by %s", sBuffer);
|
|
delete hPlugin;
|
|
|
|
// Plante template is spawned
|
|
g_iTriggerTemplateID = FindEntityByTargetname(0, "player_drop", "point_template");
|
|
if (g_iTriggerTemplateID != 0)
|
|
HookSingleEntityOutput(g_iTriggerTemplateID, "OnUser1", CallBack_OnPlaneTemplateSpawn);
|
|
|
|
// Humans getting teleported inside the plane
|
|
g_iTriggerTeleportID = FindEntityByTargetname(0, "teleport_spawn_ct", "trigger_teleport");
|
|
if (g_iTriggerTeleportID != 0)
|
|
HookSingleEntityOutput(g_iTriggerTeleportID, "OnUser1", CallBack_OnPlayerTeleportInPlane);
|
|
|
|
if (!VerifyStartHookedEntity())
|
|
CPrintToChatAll("{pink}[VScripts] {red}An error was caught. No parachute this round.");
|
|
|
|
return Plugin_Stop;
|
|
}
|
|
|
|
public Action Timer_HookPlaneDropTrigger(Handle timer)
|
|
{
|
|
g_hTimerPlaneDropTrigger = INVALID_HANDLE;
|
|
|
|
g_iTriggerMultipleID = FindEntityByTargetname(0, "plane_player_drop", "trigger_multiple");
|
|
if (g_iTriggerMultipleID != 0)
|
|
HookSingleEntityOutput(g_iTriggerMultipleID, "OnEndTouch", CallBack_OnPlayerJumpedFromPlane);
|
|
|
|
g_iPlaneEndPathID = FindEntityByTargetname(0, "player_drop_path02", "path_track");
|
|
if (g_iPlaneEndPathID != 0)
|
|
HookSingleEntityOutput(g_iPlaneEndPathID, "OnPass", CallBack_OnPlaneEndPath);
|
|
|
|
if (!VerifyPlaneJumpHookedEntity())
|
|
CPrintToChatAll("{pink}[VScripts] {red}An error was caught. No parachute this round.");
|
|
|
|
return Plugin_Stop;
|
|
}
|
|
|
|
public Action Timer_PlaneHasExploded(Handle timer)
|
|
{
|
|
g_hTimerPlaneEndPath = INVALID_HANDLE;
|
|
g_bRoundStarted = false;
|
|
|
|
for (int i = 1; i <= MaxClients; i++)
|
|
{
|
|
if (IsClientInGame(i) && !g_bPlayerLanded[i])
|
|
{
|
|
g_bPlayerLanded[i] = true;
|
|
StopParachute(i);
|
|
CPrintToChat(i, "{pink}[VScripts] {white}You parachute have a problem, prepare for impact!");
|
|
}
|
|
}
|
|
|
|
return Plugin_Stop;
|
|
}
|
|
|
|
public Action Timer_OnPlayerTeleportInPlane(Handle timer)
|
|
{
|
|
g_hPlaneDoorOpen = INVALID_HANDLE;
|
|
g_bRoundStarted = true;
|
|
for (int i = 1; i <= MaxClients; i++)
|
|
{
|
|
if (!IsClientInGame(i))
|
|
continue;
|
|
|
|
if (GetClientTeam(i) != CS_TEAM_CT)
|
|
continue;
|
|
|
|
InitPlayerToJumpFromPlane(i);
|
|
}
|
|
return Plugin_Stop;
|
|
}
|
|
|
|
/* HOOKS ENTITY CALLBACKS */
|
|
stock void CallBack_OnPlaneTemplateSpawn(const char[] output, int caller, int activator, float delay)
|
|
{
|
|
// "OnUser1" "player_drop_prop,FireUser1,,5,1"
|
|
// So we add a delay of 0.1 seconds to hook the players dropping from the plane trigger
|
|
g_hTimerPlaneDropTrigger = CreateTimer(5.1, Timer_HookPlaneDropTrigger, _, TIMER_FLAG_NO_MAPCHANGE);
|
|
}
|
|
|
|
stock void CallBack_OnPlayerTeleportInPlane(const char[] output, int caller, int activator, float delay)
|
|
{
|
|
g_hPlaneDoorOpen = CreateTimer(8.0, Timer_OnPlayerTeleportInPlane, _, TIMER_FLAG_NO_MAPCHANGE);
|
|
}
|
|
|
|
stock void CallBack_OnPlayerJumpedFromPlane(const char[] output, int caller, int activator, float delay)
|
|
{
|
|
g_bPlayerJumpedFormPlane[activator] = true;
|
|
}
|
|
|
|
stock void CallBack_OnPlaneEndPath(const char[] output, int caller, int activator, float delay)
|
|
{
|
|
// Plane reached the end of the path_track - Some players may still in the air with parachute
|
|
// Give them 45 seconds to land
|
|
if (g_fAfterExplodeDelay <= 0.0)
|
|
g_fAfterExplodeDelay = 45.0;
|
|
|
|
g_hTimerPlaneEndPath = CreateTimer(g_fAfterExplodeDelay, Timer_PlaneHasExploded, _, TIMER_FLAG_NO_MAPCHANGE);
|
|
}
|
|
|
|
/* UTILS */
|
|
stock bool VerifyStartHookedEntity()
|
|
{
|
|
if (g_iTriggerTeleportID != 0)
|
|
return true;
|
|
|
|
UnhookSingleEntityOutput(g_iTriggerTemplateID, "OnUser1", CallBack_OnPlaneTemplateSpawn);
|
|
UnhookSingleEntityOutput(g_iTriggerTeleportID, "OnUser1", CallBack_OnPlayerTeleportInPlane);
|
|
|
|
return false;
|
|
}
|
|
|
|
stock bool VerifyPlaneJumpHookedEntity()
|
|
{
|
|
if (g_iTriggerMultipleID != 0 || g_iPlaneEndPathID != 0)
|
|
return true;
|
|
|
|
UnhookSingleEntityOutput(g_iTriggerMultipleID, "OnEndTouch", CallBack_OnPlayerJumpedFromPlane);
|
|
UnhookSingleEntityOutput(g_iPlaneEndPathID, "OnPass", CallBack_OnPlaneEndPath);
|
|
return false;
|
|
}
|
|
|
|
stock bool HasValidParachute(int client)
|
|
{
|
|
return g_bHasParachuteModel[client] && IsValidEntity(g_iParachuteEntity[client]);
|
|
}
|
|
|
|
stock void InitPlayerToJumpFromPlane(int client)
|
|
{
|
|
g_bPlayerLanded[client] = false;
|
|
g_bPlayerJumpedFormPlane[client] = false;
|
|
}
|
|
|
|
stock void MarkPlayerAsLanded(int client)
|
|
{
|
|
g_bPlayerLanded[client] = true;
|
|
}
|
|
|
|
public int FindEntityByTargetname(int entity, const char[] sTargetname, const char[] sClassname)
|
|
{
|
|
if(sTargetname[0] == '#') // HammerID
|
|
{
|
|
int HammerID = StringToInt(sTargetname[1]);
|
|
|
|
while((entity = FindEntityByClassname(entity, sClassname)) != 0)
|
|
{
|
|
if(GetEntProp(entity, Prop_Data, "m_iHammerID") == HammerID)
|
|
return entity;
|
|
}
|
|
}
|
|
else // Targetname
|
|
{
|
|
int Wildcard = FindCharInString(sTargetname, '*');
|
|
char sTargetnameBuf[64];
|
|
|
|
while((entity = FindEntityByClassname(entity, sClassname)) != 0)
|
|
{
|
|
if(GetEntPropString(entity, Prop_Data, "m_iName", sTargetnameBuf, sizeof(sTargetnameBuf)) <= 0)
|
|
continue;
|
|
|
|
if(strncmp(sTargetnameBuf, sTargetname, Wildcard) == 0)
|
|
return entity;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|