removed that the plugin manually unloads itself. it is important that the server instead handles loading and unloading plugins as unloading and loading can cause slow mapchanges

This commit is contained in:
jenz 2024-05-17 20:27:45 +02:00
parent 5ecfc920c4
commit 189e4b10e2

View File

@ -0,0 +1,500 @@
#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;
}