sourcemod-plugins/scripting/GrabEnt.sp
2024-11-14 08:56:04 -06:00

600 lines
No EOL
19 KiB
SourcePawn

#define DEBUG
#define PLUGIN_AUTHOR "Stugger"
#define PLUGIN_VERSION "2.2"
#include <sourcemod>
#include <sdktools>
#include <sdkhooks>
#include <smlib/effects>
public Plugin myinfo =
{
name = "GrabEnt",
author = PLUGIN_AUTHOR,
description = "Grab then Move, Push/Pull or Rotate the entity you're looking at until released",
version = PLUGIN_VERSION,
url = ""
};
int g_pGrabbedEnt[MAXPLAYERS + 1];
int g_eRotationAxis[MAXPLAYERS + 1] = { -1, ... };
int g_eOriginalColor[MAXPLAYERS + 1][4];
float g_pLastButtonPress[MAXPLAYERS + 1];
float g_fGrabOffset[MAXPLAYERS + 1][3];
float g_fGrabDistance[MAXPLAYERS + 1];
MoveType g_pLastMoveType[MAXPLAYERS + 1];
bool g_pInRotationMode[MAXPLAYERS + 1];
bool g_eReleaseFreeze[MAXPLAYERS + 1] = { true, ... };
bool g_bHighlightEntity[MAXPLAYERS+1];
Handle g_eGrabTimer[MAXPLAYERS+1];
int g_BeamSprite;
int g_HaloSprite;
int g_iLaserIndex;
#define MAX_FORBIDDEN_CLASSNAMES 10
static char FORBIDDEN_CLASSNAMES[MAX_FORBIDDEN_CLASSNAMES][] = {
// "env_physics_blocker",
// "env_player_blocker",
"func_brush",
"func_simpleladder",
"func_button",
"func_elevator",
"func_button_timed",
"func_movelinear",
"func_tracktrain",
// "infected",
"func_lod",
"prop_ragdoll",
"move_rope"
};
#define MAX_FORBIDDEN_MODELS 2
char FORBIDDEN_MODELS[MAX_FORBIDDEN_MODELS][] = {
"models/props_vehicles/c130.mdl",
"models/props_vehicles/helicopter_rescue.mdl"
};
#define MAX_HIGHLIGHTED_CLASSNAMES 3
static char HIGHLIGHTED_CLASSNAMES[MAX_HIGHLIGHTED_CLASSNAMES][] = {
"env_physics_blocker",
"env_player_blocker",
"func_brush"
}
ConVar g_cvarEnabled;
public void OnPluginStart()
{
g_cvarEnabled = CreateConVar("sm_grabent_allow", "1", "Is grabent allowed", FCVAR_NONE, true, 0.0, true, 1.0);
RegAdminCmd("sm_grabent_freeze", Cmd_ReleaseFreeze, ADMFLAG_CHEATS, "<0/1> - Toggle entity freeze/unfreeze on release.");
RegAdminCmd("sm_grab", Cmd_Grab, ADMFLAG_CHEATS, "Toggle Grab the entity in your crosshair.");
RegAdminCmd("+grabent", Cmd_Grab, ADMFLAG_CHEATS, "Grab the entity in your crosshair.");
RegAdminCmd("-grabent", Cmd_Release, ADMFLAG_CHEATS, "Release the grabbed entity.");
}
public void OnMapStart()
{
g_iLaserIndex = PrecacheModel("materials/sprites/laserbeam.vmt");
g_BeamSprite = PrecacheModel("materials/sprites/laser.vmt", true);
g_HaloSprite = PrecacheModel("materials/sprites/halo01.vmt", true);
for (int i = 0; i < MAXPLAYERS; i++) {
g_pGrabbedEnt[i] = -1;
g_eRotationAxis[i] = -1;
g_pLastButtonPress[i] = 0.0;
g_pInRotationMode[i] = false;
g_eReleaseFreeze[i] = true;
g_eGrabTimer[i] = null;
}
}
public void OnClientDisconnect(client)
{
if (g_pGrabbedEnt[client] != -1 && IsValidEntity(g_pGrabbedEnt[client]))
Cmd_Release(client, 0);
g_eRotationAxis[client] = -1;
g_pLastButtonPress[client] = 0.0;
g_pInRotationMode[client] = false;
g_eReleaseFreeze[client] = true;
}
//============================================================================
// FREEZE SETTING COMMAND //
//============================================================================
public Action Cmd_ReleaseFreeze(client, args)
{
if (args < 1) {
ReplyToCommand(client, "\x04[SM]\x01 \x05sm_grabent_freeze <0/1>\x01 -- \x050\x01: Entity unfreeze on release, \x051\x01: Entity freeze on release");
return Plugin_Handled;
}
char sArg[16];
GetCmdArg(1, sArg, sizeof(sArg)); TrimString(sArg);
if (!StrEqual(sArg, "0") && !StrEqual(sArg, "1")) {
ReplyToCommand(client, "\x04[SM]\x01 ERROR: Value can only be either 0 or 1");
return Plugin_Handled;
}
g_eReleaseFreeze[client] = StrEqual(sArg, "1");
PrintToChat(client, "\x04[SM]\x01 Entities will now be \x05%s\x01 on Release!", g_eReleaseFreeze[client] == true ? "Frozen" : "Unfrozen");
return Plugin_Handled;
}
//============================================================================
// GRAB ENTITY COMMAND //
//============================================================================
Action Cmd_Grab(int client, int args) {
if(!g_cvarEnabled.BoolValue) {
ReplyToCommand(client, "[SM] Grabent is disabled");
return Plugin_Handled;
} else if (client < 1 || client > MaxClients || !IsClientInGame(client)) {
return Plugin_Handled;
} else if (g_pGrabbedEnt[client] > 0 && IsValidEntity(g_pGrabbedEnt[client])) {
Cmd_Release(client, 0);
return Plugin_Handled;
}
// int ent = GetClientAimTarget(client, false);
int ent = GetLookingEntity(client, Filter_IgnoreForbidden);
if (ent == -1 || !IsValidEntity(ent))
return Plugin_Handled; //<-- timer to allow search for entity??
// Grab the parent
int parent = GetEntPropEnt(ent, Prop_Data, "m_hParent");
if(parent > 0) {
ent = parent;
}
if(!CheckBlacklist(ent)) {
return Plugin_Handled;
}
float entOrigin[3], playerGrabOrigin[3];
GetEntPropVector(ent, Prop_Send, "m_vecOrigin", entOrigin);
GetClientEyePosition(client, playerGrabOrigin);
g_pGrabbedEnt[client] = ent;
// Get the point at which the ray first hit the entity
float initialRay[3];
GetInitialRayPosition(client, initialRay);
// Calculate the offset between intitial ray hit and the entities origin
g_fGrabOffset[client][0] = entOrigin[0] - initialRay[0];
g_fGrabOffset[client][1] = entOrigin[1] - initialRay[1];
g_fGrabOffset[client][2] = entOrigin[2] - initialRay[2];
// Calculate the distance between ent and player
float xDis = Pow(initialRay[0]-(playerGrabOrigin[0]), 2.0);
float yDis = Pow(initialRay[1]-(playerGrabOrigin[1]), 2.0);
float zDis = Pow(initialRay[2]-(playerGrabOrigin[2]), 2.0);
g_fGrabDistance[client] = SquareRoot((xDis)+(yDis)+(zDis));
// Get and Store entities original color (useful if colored)
int entColor[4];
int colorOffset = GetEntSendPropOffs(ent, "m_clrRender");
if (colorOffset > 0)
{
entColor[0] = GetEntData(ent, colorOffset, 1);
entColor[1] = GetEntData(ent, colorOffset + 1, 1);
entColor[2] = GetEntData(ent, colorOffset + 2, 1);
entColor[3] = GetEntData(ent, colorOffset + 3, 1);
}
g_eOriginalColor[client][0] = entColor[0];
g_eOriginalColor[client][1] = entColor[1];
g_eOriginalColor[client][2] = entColor[2];
g_eOriginalColor[client][3] = entColor[3];
// Set entities color to grab color (green and semi-transparent)
SetEntityRenderMode(ent, RENDER_TRANSALPHA);
SetEntityRenderColor(ent, 0, 255, 0, 235);
// Freeze entity
char sClass[64];
GetEntityClassname(ent, sClass, sizeof(sClass)); TrimString(sClass);
if (StrEqual(sClass, "player", false)) {
g_pLastMoveType[ent] = GetEntityMoveType(ent);
SetEntityMoveType(ent, MOVETYPE_NONE);
} else
AcceptEntityInput(ent, "DisableMotion");
g_pLastMoveType[client] = GetEntityMoveType(client);
// Disable weapon prior to timer
SetWeaponDelay(client, 1.0);
// Make sure rotation mode can immediately be entered
g_pLastButtonPress[client] = GetGameTime() - 2.0;
g_pInRotationMode[client] = false;
g_bHighlightEntity[client] = false;
for(int i = 0; i < MAX_HIGHLIGHTED_CLASSNAMES; i++) {
if(StrEqual(HIGHLIGHTED_CLASSNAMES[i], sClass)) {
g_bHighlightEntity[client] = true;
break;
}
}
DataPack pack;
g_eGrabTimer[client] = CreateDataTimer(0.1, Timer_UpdateGrab, pack, TIMER_REPEAT);
pack.WriteCell(client);
return Plugin_Handled;
}
//============================================================================
// TIMER FOR GRAB ENTITY //
//============================================================================
public Action Timer_UpdateGrab(Handle timer, DataPack pack) {
int client;
pack.Reset();
client = pack.ReadCell();
if (!IsValidEntity(client) || client < 1 || client > MaxClients || !IsClientInGame(client))
return Plugin_Stop;
if (g_pGrabbedEnt[client] == -1 || !IsValidEntity(g_pGrabbedEnt[client]))
return Plugin_Stop;
// Continuously delay use of weapon, as to not fire any bullets when pushing/pulling/rotating
SetWeaponDelay(client, 1.0);
if(g_bHighlightEntity[client]) {
char targetname[64];
GetEntPropString(g_pGrabbedEnt[client], Prop_Data, "m_iName", targetname, sizeof(targetname));
PrintCenterText(client, "%s", targetname);
GlowEntity(client, g_pGrabbedEnt[client]);
}
// *** Enable/Disable Rotation Mode
if (GetClientButtons(client) & IN_RELOAD) {
// Avoid instant enable/disable of rotation mode by requiring a one second buffer
if (GetGameTime() - g_pLastButtonPress[client] >= 1.0) {
g_pLastButtonPress[client] = GetGameTime();
g_pInRotationMode[client] = g_pInRotationMode[client] == true ? false : true;
PrintToChat(client, "\x04[SM]\x01 Rotation Mode \x05%s\x01", g_pInRotationMode[client] == true ? "Enabled" : "Disabled");
// Restore the entities color and alpha if enabling
if(g_pInRotationMode[client]) {
SetEntityRenderColor(g_pGrabbedEnt[client], 255, 255, 255, 255);
PrintToChat(client, "\x05[A]\x01 RED \x05[S]\x01 GREEN \x05[D]\x01 BLUE \x05[W]\x01 SHOW RINGS");
}
// Change back to grabbed color if disabling
else
SetEntityRenderColor(g_pGrabbedEnt[client], 0, 255, 0, 235);
}
}
// ***In Rotation Mode
if (g_pInRotationMode[client]) {
SetEntityMoveType(client, MOVETYPE_NONE);
float ang[3], pos[3], mins[3], maxs[3];
GetEntPropVector(g_pGrabbedEnt[client], Prop_Send, "m_angRotation", ang);
GetEntPropVector(g_pGrabbedEnt[client], Prop_Send, "m_vecOrigin", pos);
GetEntPropVector(g_pGrabbedEnt[client], Prop_Send, "m_vecMins", mins);
GetEntPropVector(g_pGrabbedEnt[client], Prop_Send, "m_vecMaxs", maxs);
// If the entity is a child, it will have a null position, so we'll hesitantly use the parents position
int parent = GetEntPropEnt(g_pGrabbedEnt[client], Prop_Data, "m_hMoveParent");
if (parent > 0 && IsValidEntity(parent))
GetEntPropVector(parent, Prop_Send, "m_vecOrigin", pos);
// Get rotation axis from button press
int buttonPress = GetClientButtons(client);
switch(buttonPress) {
case IN_FORWARD: {
g_eRotationAxis[client] = -1; // [W] = Show Rings
PrintToChat(client, "\x04[SM]\x01 Show Rings \x05On\x01");
}
case IN_MOVELEFT: {
g_eRotationAxis[client] = 0; // [A] = x axis
PrintToChat(client, "\x04[SM]\x01 Rotation Axis \x05X\x01");
}
case IN_BACK: {
g_eRotationAxis[client] = 1; // [S] = y axis
PrintToChat(client, "\x04[SM]\x01 Rotation Axis \x05Y\x01");
}
case IN_MOVERIGHT: {
g_eRotationAxis[client] = 2; // [D] = z axis
PrintToChat(client, "\x04[SM]\x01 Rotation Axis \x05Z\x01");
}
}
// Reset angles when A+S+D is pressed
if((buttonPress & IN_MOVELEFT) && (buttonPress & IN_BACK) && (buttonPress & IN_MOVERIGHT)) {
ang[0] = 0.0; ang[1] = 0.0; ang[2] = 0.0;
g_eRotationAxis[client] = -1;
}
// Largest side should dictate the diameter of the rings
float diameter, sendAng[3];
diameter = (maxs[0] > maxs[1]) ? (maxs[0] + 10.0) : (maxs[1] + 10.0);
diameter = ((maxs[2] + 10.0) > diameter) ? (maxs[2] + 10.0) : diameter;
// Sending original ang will cause non-stop rotation issue
sendAng = ang;
// Draw rotation rings
switch(g_eRotationAxis[client]) {
case -1: CreateRing(client, sendAng, pos, diameter, 0, true); // all 3 rings
case 0: CreateRing(client, sendAng, pos, diameter, 0, false); // red (x)
case 1: CreateRing(client, sendAng, pos, diameter, 1, false); // green (y)
case 2: CreateRing(client, sendAng, pos, diameter, 2, false); // blue (z)
}
// Rotate with mouse if on a rotation axis (A,S,D)
if (g_eRotationAxis[client] != -1) {
// + Rotate
if (GetClientButtons(client) & IN_ATTACK)
ang[g_eRotationAxis[client]] += 10.0;
// - Rotate
else if (GetClientButtons(client) & IN_ATTACK2)
ang[g_eRotationAxis[client]] -= 10.0;
}
TeleportEntity(g_pGrabbedEnt[client], NULL_VECTOR, ang, NULL_VECTOR);
}
// ***Not in Rotation Mode
if (!g_pInRotationMode[client] || g_eRotationAxis[client] == -1) {
// Keep track of player noclip as to avoid forced enable/disable
if(!g_pInRotationMode[client]) {
SetEntityMoveType(client, g_pLastMoveType[client])
}
// Push entity (Allowed if we're in rotation mode, not on a rotation axis (-1))
if (GetClientButtons(client) & IN_ATTACK)
{
if (g_fGrabDistance[client] < 80)
g_fGrabDistance[client] += 10;
else
g_fGrabDistance[client] += g_fGrabDistance[client] / 25;
}
// Pull entity (Allowed if we're in rotation mode, not on a rotation axis (-1))
else if (GetClientButtons(client) & IN_ATTACK2 && g_fGrabDistance[client] > 25)
{
if (g_fGrabDistance[client] < 80)
g_fGrabDistance[client] -= 10;
else
g_fGrabDistance[client] -= g_fGrabDistance[client] / 25;
}
g_eRotationAxis[client] = -1;
}
// *** Runs whether in rotation mode or not
float entNewPos[3];
int buttons = GetClientButtons(client);
GetEntNewPosition(client, entNewPos, buttons & IN_SPEED == 0);
entNewPos[0] += g_fGrabOffset[client][0];
entNewPos[1] += g_fGrabOffset[client][1];
entNewPos[2] += g_fGrabOffset[client][2];
float mins[3];
GetEntPropVector(g_pGrabbedEnt[client], Prop_Data, "m_vecMins", mins);
entNewPos[2] -= mins[2];
TeleportEntity(g_pGrabbedEnt[client], entNewPos, NULL_VECTOR, NULL_VECTOR);
return Plugin_Handled;
}
//============================================================================
// RELEASE ENTITY COMMAND //
//============================================================================
public Action Cmd_Release(client, args) {
if (!IsValidEntity(client) || client < 1 || client > MaxClients || !IsClientInGame(client))
return Plugin_Handled;
if (g_pGrabbedEnt[client] == -1 || !IsValidEntity(g_pGrabbedEnt[client]))
return Plugin_Handled;
// Allow near-immediate use of weapon
SetWeaponDelay(client, 0.2);
SetEntityMoveType(client, g_pLastMoveType[client]);
// Unfreeze if target was a player and unfreeze if setting is set to 0
char sClass[64];
GetEntityClassname(g_pGrabbedEnt[client], sClass, sizeof(sClass)); TrimString(sClass);
if (StrEqual(sClass, "player", false))
SetEntityMoveType(g_pGrabbedEnt[client], g_pLastMoveType[g_pGrabbedEnt[client]]);
else if (g_eReleaseFreeze[client] == false)
AcceptEntityInput(g_pGrabbedEnt[client], "EnableMotion");
// Restore color and alpha to original prior to grab
SetEntityRenderColor(g_pGrabbedEnt[client], g_eOriginalColor[client][0], g_eOriginalColor[client][1], g_eOriginalColor[client][2], g_eOriginalColor[client][3]);
// Kill the grab timer and reset control values
if (IsValidHandle(g_eGrabTimer[client])) {
delete g_eGrabTimer[client];
}
g_pGrabbedEnt[client] = -1;
g_eRotationAxis[client] = -1;
g_pInRotationMode[client] = false;
return Plugin_Handled;
}
//============================================================================
// *** UTILITIES *** //
//============================================================================
int GetLookingEntity(int client, TraceEntityFilter filter) {
static float pos[3], ang[3];
GetClientEyePosition(client, pos);
GetClientEyeAngles(client, ang);
TR_TraceRayFilter(pos, ang, MASK_ALL, RayType_Infinite, filter, client);
if(TR_DidHit()) {
return TR_GetEntityIndex();
}
return -1;
}
stock bool GetEntNewPosition(int client, float endPos[3], bool doCollision = true)
{
if (client > 0 && client <= MaxClients && IsClientInGame(client)) {
float clientEye[3], clientAngle[3], direction[3];
GetClientEyePosition(client, clientEye);
GetClientEyeAngles(client, clientAngle);
GetAngleVectors(clientAngle, direction, NULL_VECTOR, NULL_VECTOR);
ScaleVector(direction, g_fGrabDistance[client]);
AddVectors(clientEye, direction, endPos);
if(doCollision) {
TR_TraceRayFilter(clientEye, endPos, MASK_OPAQUE, RayType_EndPoint, TraceRayFilterEnt, client);
if (TR_DidHit(INVALID_HANDLE)) {
TR_GetEndPosition(endPos);
}
}
return true;
}
return false;
}
/////
stock bool GetInitialRayPosition(int client, float endPos[3])
{
if (client > 0 && client <= MaxClients && IsClientInGame(client)) {
float clientEye[3], clientAngle[3];
GetClientEyePosition(client, clientEye);
GetClientEyeAngles(client, clientAngle);
TR_TraceRayFilter(clientEye, clientAngle, MASK_SOLID, RayType_Infinite, TraceRayFilterActivator, client);
if (TR_DidHit(INVALID_HANDLE))
TR_GetEndPosition(endPos);
return true;
}
return false;
}
/////
stock void SetWeaponDelay(int client, float delay)
{
if (IsValidEntity(client) && client > 0 && client <= MaxClients && IsClientInGame(client)) {
int pWeapon = GetEntPropEnt(client, Prop_Send, "m_hActiveWeapon");
if (IsValidEntity(pWeapon) && pWeapon != -1) {
SetEntPropFloat(pWeapon, Prop_Send, "m_flNextPrimaryAttack", GetGameTime() + delay);
SetEntPropFloat(pWeapon, Prop_Send, "m_flNextSecondaryAttack", GetGameTime() + delay);
}
}
}
/////
stock void CreateRing(int client, float ang[3], float pos[3], float diameter, int axis, bool trio)
{
if (!IsValidEntity(client) || client < 1 || client > MaxClients || !IsClientInGame(client))
return;
float ringVecs[26][3];
int ringColor[3][4];
ringColor[0] = { 255, 0, 0, 255 };
ringColor[1] = { 0, 255, 0, 255 };
ringColor[2] = { 0, 0, 255, 255 };
int numSides = (!trio) ? 26 : 17;
float angIncrement = (!trio) ? 15.0 : 24.0;
for (int i = 1; i < numSides; i++) {
float direction[3], endPos[3];
switch(axis) {
case 0: GetAngleVectors(ang, direction, NULL_VECTOR, NULL_VECTOR);
case 1:
{
ang[2] = 0.0;
GetAngleVectors(ang, NULL_VECTOR, direction, NULL_VECTOR);
}
case 2: GetAngleVectors(ang, NULL_VECTOR, NULL_VECTOR, direction);
}
ScaleVector(direction, diameter);
AddVectors(pos, direction, endPos);
if (i == 1) ringVecs[0] = endPos;
ringVecs[i] = endPos;
ang[axis] += angIncrement;
TE_SetupBeamPoints(ringVecs[i-1], ringVecs[i], g_BeamSprite, g_HaloSprite, 0, 15, 0.2, 2.5, 2.5, 1, 0.0, ringColor[axis], 10);
TE_SendToClient(client, 0.0);
if(trio && i == numSides-1 && axis < 2) {
i = 0;
ang[axis] -= angIncrement * (numSides-1);
axis += 1;
}
}
}
void GlowEntity(int client, int entity) {
float pos[3], mins[3], maxs[3], angles[3];
GetEntPropVector(entity, Prop_Send, "m_angRotation", angles);
GetEntPropVector(entity, Prop_Send, "m_vecOrigin", pos);
GetEntPropVector(entity, Prop_Send, "m_vecMins", mins);
GetEntPropVector(entity, Prop_Send, "m_vecMaxs", maxs);
Effect_DrawBeamBoxRotatableToClient(client, pos, mins, maxs, angles, g_iLaserIndex, 0, 0, 30, 0.1, 0.4, 0.4, 0, 0.1, { 0, 255, 0, 235 }, 0);
}
//============================================================================
// *** FILTERS *** //
//============================================================================
public bool TraceRayFilterEnt(int entity, int mask, any:client)
{
if (entity == client || entity == g_pGrabbedEnt[client])
return false;
return true;
}
/////
public bool TraceRayFilterActivator(int entity, int mask, any:activator)
{
if (entity == activator)
return false;
return true;
}
bool Filter_IgnoreForbidden(int entity, int mask, int data) {
if(entity == data || entity == 0) return false;
if(entity <= MaxClients) return true;
return CheckBlacklist(entity);
}
bool CheckBlacklist(int entity) {
if(entity == 0) return false;
static char buffer[64];
GetEntityClassname(entity, buffer, sizeof(buffer));
for(int i = 0; i < MAX_FORBIDDEN_CLASSNAMES; i++) {
if(StrEqual(FORBIDDEN_CLASSNAMES[i], buffer)) {
return false;
}
}
GetEntPropString(entity, Prop_Data, "m_ModelName", buffer, sizeof(buffer));
for(int i = 0; i < MAX_FORBIDDEN_MODELS; i++) {
if(StrEqual(FORBIDDEN_MODELS[i], buffer)) {
return false;
}
}
GetEntPropString(entity, Prop_Data, "m_iName", buffer, sizeof(buffer));
if(StrContains(buffer, "randomizer") == 0) {
return false;
}
GetEntityClassname(entity, buffer, sizeof(buffer));
return true;
}