diff --git a/VScripts/scripting/VScript_ze_stardust_battleground.sp b/VScripts/scripting/VScript_ze_stardust_battleground.sp new file mode 100644 index 0000000..667715d --- /dev/null +++ b/VScripts/scripting/VScript_ze_stardust_battleground.sp @@ -0,0 +1,500 @@ +#include +#include +#include +#include +#include + +#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; +}