A couple PropSpawner changes #5

Open
Irineu wants to merge 7 commits from Irineu/sm-plugins:master into master
2 changed files with 379 additions and 22 deletions
+20
View File
@@ -0,0 +1,20 @@
"Games"
{
"cstrike"
{
"Offsets"
{
"PlayerUse"
{
"linux" "414"
"windows" "413"
}
"ForceVPhysicsCollide"
{
"linux" "160"
"windows" "159"
}
}
}
}
+358 -21
View File
@@ -2,6 +2,8 @@
#include <sourcemod> #include <sourcemod>
#include <sdktools> #include <sdktools>
#include <sdkhooks> #include <sdkhooks>
#include <dhooks>
#include <cstrike>
public Plugin myinfo = public Plugin myinfo =
{ {
@@ -17,21 +19,66 @@ Menu g_RotationMenu = null;
Menu g_PhysicMenu = null; Menu g_PhysicMenu = null;
Menu g_DynamicMenu = null; Menu g_DynamicMenu = null;
Menu g_NpcMenu = null; Menu g_NpcMenu = null;
Menu g_ColorMenu = null;
KeyValues g_Models; KeyValues g_Models;
ConVar g_cvNotifyShot; ConVar g_cvNotifyShot;
ConVar g_cvNotifyTimer; ConVar g_cvNotifyTimer;
ConVar g_cvUseMode;
ArrayList g_aSpawnedProps; // Tracks entity indices of props spawned by this plugin so we can unhook them ArrayList g_aSpawnedProps; // Tracks entity indices of props spawned by this plugin so we can unhook them
ArrayList g_aNotifyList; ArrayList g_aNotifyList;
int g_aPlayerPropColor[MAXPLAYERS + 1][3];
Handle g_PlayerUseSetup = INVALID_HANDLE;
Handle g_ForceVPhysicsCollideSetup = INVALID_HANDLE;
enum struct NotifyListData enum struct NotifyListData
{ {
int client; int client;
Handle timer; Handle timer;
} }
enum
{
PropColors_Default,
PropColors_Black,
PropColors_Red,
PropColors_Purple,
PropColors_Blue,
PropColors_Green,
PropColors_Yellow,
PropColors_Orange,
PropColors_Rainbow,
PropColors_Count
}
char ColorToString[PropColors_Count][] = {
"Default",
"Black",
"Red",
"Purple",
"Blue",
"Green",
"Yellow",
"Orange",
"Rainbow"
};
int ColorToRGB[PropColors_Count][3] = {
{255, 255, 255},
{0, 0, 0},
{255, 0, 0},
{128, 0, 128},
{0, 0, 255},
{0, 255, 0},
{255, 255, 0},
{255, 165, 0},
{256, 256, 256}
};
public void OnPluginStart() public void OnPluginStart()
{ {
LoadTranslations("common.phrases"); LoadTranslations("common.phrases");
@@ -53,10 +100,44 @@ public void OnPluginStart()
} }
g_Models.Rewind(); // Go back to the root node g_Models.Rewind(); // Go back to the root node
GameData gd = new GameData("PropSpawner");
if (gd == null)
{
LogError("PropSpawner: Missing gamedata file");
}
else
{
int PlayerUseOffset = gd.GetOffset("PlayerUse");
if (PlayerUseOffset == -1)
{
LogError("PropSpawner: Missing \"PlayerUse\" offset in gamedata file");
}
else
{
g_PlayerUseSetup = DHookCreate(PlayerUseOffset, HookType_Entity, ReturnType_Void, ThisPointer_CBaseEntity, PlayerUse_Hook);
}
int ForceVPhysicsCollideOffset = gd.GetOffset("ForceVPhysicsCollide");
if (ForceVPhysicsCollideOffset == -1)
{
LogError("PropSpawner: Missing \"ForceVPhysicsCollide\" offset in gamedata file");
}
else
{
g_ForceVPhysicsCollideSetup = DHookCreate(ForceVPhysicsCollideOffset, HookType_Entity, ReturnType_Bool, ThisPointer_CBaseEntity, ForceVPhysicsCollide_Hook);
DHookAddParam(g_ForceVPhysicsCollideSetup, HookParamType_CBaseEntity);
}
delete gd;
}
RegAdminCmd("sm_propspawner", Command_MainMenu, ADMFLAG_CHANGEMAP); //g RegAdminCmd("sm_propspawner", Command_MainMenu, ADMFLAG_CHANGEMAP); //g
RegAdminCmd("sm_setpropcolor", Command_SetPropColor, ADMFLAG_CHANGEMAP, "Set PropSpawner color by RGB value, or \"rainbow\" for that.");
g_cvNotifyShot = CreateConVar("sm_propspawner_notify_shot", "0", "If set to 1, announces in chat when a plugin-spawned prop is shot.", FCVAR_NOTIFY, true, 0.0, true, 1.0); g_cvNotifyShot = CreateConVar("sm_propspawner_notify_shot", "0", "If set to 1, announces in chat when a plugin-spawned prop is shot.", FCVAR_NOTIFY, true, 0.0, true, 1.0);
g_cvNotifyTimer = CreateConVar("sm_propspawner_notify_shot_timer", "0.5", "How long a player stays in the \"shooting couch\" list."); g_cvNotifyTimer = CreateConVar("sm_propspawner_notify_shot_timer", "0.5", "How long a player stays in the \"shooting couch\" list.");
g_cvUseMode = CreateConVar("sm_propspawner_use_mode", "0", "What should happen when a prop is +use'd. " ...
"0 = Normal. 1 = Disallow CTs moving plugin spawned props. 2 = Add player to notify list.");
AutoExecConfig(true, "PropSpawner"); AutoExecConfig(true, "PropSpawner");
g_aSpawnedProps = new ArrayList(); g_aSpawnedProps = new ArrayList();
@@ -65,6 +146,12 @@ public void OnPluginStart()
HookEvent("round_end", Event_RoundEnd); HookEvent("round_end", Event_RoundEnd);
} }
public void OnEntityCreated(int entity, const char[] classname)
{
if (StrEqual(classname, "func_clip_vphysics") && g_ForceVPhysicsCollideSetup != INVALID_HANDLE)
DHookEntity(g_ForceVPhysicsCollideSetup, false, entity);
}
public void OnEntityDestroyed(int entity) public void OnEntityDestroyed(int entity)
{ {
// Safety net: if a tracked prop is removed by any means other than DeleteProp() // Safety net: if a tracked prop is removed by any means other than DeleteProp()
@@ -109,6 +196,7 @@ public void OnMapStart()
g_PhysicMenu = BuildModelsMenu("physic"); g_PhysicMenu = BuildModelsMenu("physic");
g_DynamicMenu = BuildModelsMenu("dynamic"); g_DynamicMenu = BuildModelsMenu("dynamic");
g_NpcMenu = BuildModelsMenu("npc"); g_NpcMenu = BuildModelsMenu("npc");
g_ColorMenu = BuildColorMenu();
} }
public void OnMapEnd() public void OnMapEnd()
@@ -152,6 +240,7 @@ Menu BuildMainMenu(){
menu.AddItem("menu_prop_dynamic", "Dynamic/Static Props"); menu.AddItem("menu_prop_dynamic", "Dynamic/Static Props");
menu.AddItem("menu_prop_physic", "Physic Props"); menu.AddItem("menu_prop_physic", "Physic Props");
menu.AddItem("menu_prop_npc", "NPCs"); menu.AddItem("menu_prop_npc", "NPCs");
menu.AddItem("menu_prop_color", "Choose Color");
menu.ExitButton = true; menu.ExitButton = true;
return menu; return menu;
} }
@@ -202,6 +291,20 @@ Menu BuildModelsMenu(char[] modelType){
return menu; return menu;
} }
Menu BuildColorMenu()
{
Menu menu = new Menu(ColorHandler);
SetMenuTitle(menu, "Choose Prop Color");
for (int i = 0; i < PropColors_Count; i++)
{
menu.AddItem(ColorToString[i], ColorToString[i]);
}
menu.ExitBackButton = true;
menu.ExitButton = true;
return menu;
}
/* ----------------------------------- */ /* ----------------------------------- */
public Action Command_MainMenu(int client, int args) public Action Command_MainMenu(int client, int args)
@@ -210,6 +313,35 @@ public Action Command_MainMenu(int client, int args)
return Plugin_Handled; return Plugin_Handled;
} }
public Action Command_SetPropColor(int client, int args)
{
if (args < 3)
{
if (args != 0)
{
char buf[32];
GetCmdArg(1, buf, sizeof(buf));
if (StrEqual(buf, "rainbow", false))
{
g_aPlayerPropColor[client][0] = 256;
ReplyToCommand(client, "\x01[SM] Changed color to \x04Rainbow\x01.");
}
}
else
{
ReplyToCommand(client, "Usage: sm_setpropcolor <r> <g> <b>");
}
return Plugin_Handled;
}
int r = g_aPlayerPropColor[client][0] = GetCmdArgInt(1) % 256;
int g = g_aPlayerPropColor[client][1] = GetCmdArgInt(2) % 256;
int b = g_aPlayerPropColor[client][2] = GetCmdArgInt(3) % 256;
ReplyToCommand(client, "\x01[SM] Changed color to \x07%02x%02x%02x%d %d %d\x01.", r, g, b, r, g, b);
return Plugin_Handled;
}
public int MainMenuHandler(Menu menu, MenuAction action, int client, int param2) public int MainMenuHandler(Menu menu, MenuAction action, int client, int param2)
{ {
char info[32]; char info[32];
@@ -233,6 +365,9 @@ public int MainMenuHandler(Menu menu, MenuAction action, int client, int param2)
else if(StrEqual(info, "menu_prop_npc")){ else if(StrEqual(info, "menu_prop_npc")){
g_NpcMenu.Display(client, MENU_TIME_FOREVER); g_NpcMenu.Display(client, MENU_TIME_FOREVER);
} }
else if(StrEqual(info, "menu_prop_color")){
g_ColorMenu.Display(client, MENU_TIME_FOREVER);
}
} }
} }
return 0; return 0;
@@ -361,6 +496,37 @@ public NpcHandler(Menu menu, MenuAction action, int client, int param2)
} }
} }
public ColorHandler(Menu menu, MenuAction action, int client, int param2)
{
char info[32];
menu.GetItem(param2, info, sizeof(info));
switch (action)
{
case MenuAction_Select:
{
int colorIdx = -1;
for (int i = 0; i < PropColors_Count; i++)
{
if (StrEqual(ColorToString[i], info))
{
colorIdx = i;
break;
}
}
if (colorIdx != -1)
{
g_aPlayerPropColor[client] = ColorToRGB[colorIdx];
ReplyToCommand(client, "\x01[SM] Changed color to \x04%s\x01.", info);
g_MainMenu.Display(client, MENU_TIME_FOREVER);
}
}
case MenuAction_Cancel:
if (param2 == MenuCancel_ExitBack)
g_MainMenu.Display(client, MENU_TIME_FOREVER);
}
}
bool isAProp(char[] classname){ bool isAProp(char[] classname){
if(StrEqual(classname, "prop_physics") || StrEqual(classname, "prop_physics_override") || StrEqual(classname, "prop_dynamic") || StrEqual(classname, "prop_dynamic_override") || StrEqual(classname, "prop_physics_multiplayer") || StrEqual(classname, "prop_dynamic_ornament") || StrEqual(classname, "prop_static")){ if(StrEqual(classname, "prop_physics") || StrEqual(classname, "prop_physics_override") || StrEqual(classname, "prop_dynamic") || StrEqual(classname, "prop_dynamic_override") || StrEqual(classname, "prop_physics_multiplayer") || StrEqual(classname, "prop_dynamic_ornament") || StrEqual(classname, "prop_static")){
@@ -557,10 +723,22 @@ void prop_any_create(int client)
if (g_cvNotifyShot.BoolValue) if (g_cvNotifyShot.BoolValue)
{ {
SetEntityFlags(prop, FL_CLIENT); //Now youre thinking with portals SetEntityFlags(prop, FL_CLIENT); //Now youre thinking with portals
SetEntProp(prop, Prop_Data, "m_iTeamNum", CS_TEAM_CT); // So it works with filtered triggers
SDKHook(prop, SDKHook_OnTakeDamage, OnPropTakeDamage); SDKHook(prop, SDKHook_OnTakeDamage, OnPropTakeDamage);
g_aSpawnedProps.Push(prop); g_aSpawnedProps.Push(prop);
} }
SetEntityRenderMode(prop, RENDER_TRANSCOLOR);
if (g_aPlayerPropColor[client][0] == 256) // Rainbow
{
CreateTimer(0.2, OnRainbowTimer, EntIndexToEntRef(prop), TIMER_REPEAT);
ApplyRainbow(prop, 1.0);
}
else
{
SetEntityRenderColor(prop, g_aPlayerPropColor[client][0], g_aPlayerPropColor[client][1], g_aPlayerPropColor[client][2], 255);
}
ShowActivity2(client, "\x01[SM] \x04", "\x01Spawned \x04%s\x01.", sModelName); ShowActivity2(client, "\x01[SM] \x04", "\x01Spawned \x04%s\x01.", sModelName);
LogAction(client, -1, "\"%L\" Spawned \"%s\".", client, sModelName); LogAction(client, -1, "\"%L\" Spawned \"%s\".", client, sModelName);
} }
@@ -575,26 +753,7 @@ public Action OnPropTakeDamage(int victim, int &attacker, int &inflictor, float
if (attacker < 1 || attacker > MaxClients || !IsClientInGame(attacker)) if (attacker < 1 || attacker > MaxClients || !IsClientInGame(attacker))
return Plugin_Continue; return Plugin_Continue;
Handle timer = CreateTimer(g_cvNotifyTimer.FloatValue, OnNotifyTimer, attacker); AddToNotifyList(attacker);
int idx = FindInNotifyList(attacker);
if (idx == -1)
{
NotifyListData data;
data.client = attacker;
data.timer = timer;
g_aNotifyList.PushArray(data, sizeof(NotifyListData));
}
else
{
NotifyListData buf;
g_aNotifyList.GetArray(idx, buf, sizeof(NotifyListData));
if (buf.timer != INVALID_HANDLE)
KillTimer(buf.timer);
buf.timer = timer;
g_aNotifyList.SetArray(idx, buf, sizeof(NotifyListData));
}
DisplayNotifyList(); DisplayNotifyList();
return Plugin_Continue; return Plugin_Continue;
@@ -603,6 +762,13 @@ public Action OnPropTakeDamage(int victim, int &attacker, int &inflictor, float
void DisplayNotifyList() void DisplayNotifyList()
{ {
static int channel = -1; static int channel = -1;
static float lastDisplayTime = 0.0;
// Change the channel every once in a while, to avoid conflicts with the map's game_text entities
// (not really sure if sourcemod accounts for them when using -1 as channel but hopefully it does)
if ((GetEngineTime() - lastDisplayTime) > g_cvNotifyTimer.FloatValue)
channel = -1;
char text[255]; char text[255];
strcopy(text, sizeof(text), "Shooting Couch:\n"); strcopy(text, sizeof(text), "Shooting Couch:\n");
@@ -626,12 +792,14 @@ void DisplayNotifyList()
StrCat(text, sizeof(text), "\n"); StrCat(text, sizeof(text), "\n");
} }
SetHudTextParams(0.8, -1.0, g_cvNotifyTimer.FloatValue, 255, 255, 255, 255, 0, 0.0, 0.0, 0.0); SetHudTextParams(0.8, 0.2, g_cvNotifyTimer.FloatValue, 255, 255, 255, 255, 0, 0.0, 0.0, 0.0);
for (int i = 1; i <= MaxClients; i++) for (int i = 1; i <= MaxClients; i++)
{ {
if (IsClientInGame(i)) if (IsClientInGame(i))
channel = ShowHudText(i, channel, text); channel = ShowHudText(i, channel, text);
} }
lastDisplayTime = GetEngineTime();
} }
void OnNotifyTimer(Handle timer, any data) void OnNotifyTimer(Handle timer, any data)
@@ -657,3 +825,172 @@ int FindInNotifyList(int client)
return -1; return -1;
} }
public void OnClientPutInServer(int client)
{
g_aPlayerPropColor[client] = {255, 255, 255};
if (g_PlayerUseSetup != INVALID_HANDLE)
{
DHookEntity(g_PlayerUseSetup, false, client);
DHookEntity(g_PlayerUseSetup, true, client, INVALID_FUNCTION, PlayerUse_HookPost);
}
}
void OnRainbowTimer(Handle timer, any data)
{
int prop = EntRefToEntIndex(view_as<int>(data));
if (prop == -1)
{
KillTimer(timer);
return;
}
ApplyRainbow(prop, 1.0);
}
// Copied from GlowColors
void ApplyRainbow(int ent, float Frequency)
{
float i = GetGameTime();
int Red = RoundFloat(Sine(Frequency * i + 0.0) * 127.0 + 128.0);
int Green = RoundFloat(Sine(Frequency * i + 2.0943951) * 127.0 + 128.0);
int Blue = RoundFloat(Sine(Frequency * i + 4.1887902) * 127.0 + 128.0);
SetEntityRenderColor(ent, Red, Green, Blue, 255);
}
void AddToNotifyList(int client)
{
if (GetClientTeam(client) != CS_TEAM_CT)
return;
Handle timer = CreateTimer(g_cvNotifyTimer.FloatValue, OnNotifyTimer, client);
int idx = FindInNotifyList(client);
if (idx == -1)
{
NotifyListData data;
data.client = client;
data.timer = timer;
g_aNotifyList.PushArray(data, sizeof(NotifyListData));
}
else
{
NotifyListData buf;
g_aNotifyList.GetArray(idx, buf, sizeof(NotifyListData));
if (buf.timer != INVALID_HANDLE)
KillTimer(buf.timer);
buf.timer = timer;
g_aNotifyList.SetArray(idx, buf, sizeof(NotifyListData));
}
}
ConVar sv_pushaway_force = null;
float g_flPushawayRestore = 0.0;
MRESReturn PlayerUse_Hook(int pThis)
{
static ConVar sv_turbophysics = null;
if (!sv_pushaway_force)
sv_pushaway_force = FindConVar("sv_pushaway_force");
if (!sv_turbophysics)
sv_turbophysics = FindConVar("sv_turbophysics");
if (!sv_pushaway_force || !sv_turbophysics)
return MRES_Ignored;
if (!sv_turbophysics.BoolValue)
return MRES_Ignored;
if (g_cvUseMode.IntValue != 1 && g_cvUseMode.IntValue != 2)
return MRES_Ignored;
// ZMs can always push it
if (GetClientTeam(pThis) != CS_TEAM_CT)
return MRES_Ignored;
if ((GetClientButtons(pThis) & IN_USE) == 0)
return MRES_Ignored;
float eyePos[3];
float eyeAng[3];
float eyeFwd[3];
float endPos[3];
GetClientEyePosition(pThis, eyePos);
GetClientEyeAngles(pThis, eyeAng);
GetAngleVectors(eyeAng, eyeFwd, NULL_VECTOR, NULL_VECTOR);
endPos[0] = eyePos[0] + (eyeFwd[0] * 96.0);
endPos[1] = eyePos[1] + (eyeFwd[1] * 96.0);
endPos[2] = eyePos[2] + (eyeFwd[2] * 96.0);
Handle tr = TR_TraceRayFilterEx(eyePos, endPos, MASK_SOLID, RayType_EndPoint, UsePushFilter, 0, TRACE_ENTITIES_ONLY);
if (TR_DidHit(tr))
{
int entHit = TR_GetEntityIndex(tr);
// This currently only works if sm_propspawner_notify_shot is enabled
if (g_aSpawnedProps.FindValue(entHit) != -1)
{
switch (g_cvUseMode.IntValue)
{
// Prevent the prop from being moved
case 1:
{
g_flPushawayRestore = sv_pushaway_force.FloatValue;
sv_pushaway_force.FloatValue = 0.0;
}
// Add to the shooting couch list
case 2:
{
AddToNotifyList(pThis);
DisplayNotifyList();
}
}
}
}
CloseHandle(tr);
return MRES_Handled;
}
MRESReturn PlayerUse_HookPost(int pThis)
{
if (sv_pushaway_force && g_flPushawayRestore != 0.0)
{
sv_pushaway_force.FloatValue = g_flPushawayRestore;
g_flPushawayRestore = 0.0;
}
return MRES_Handled;
}
bool UsePushFilter(int entity, int contentsMask)
{
// Checking if the entity has a physics object
int offs = FindDataMapInfo(entity, "m_pPhysicsObject");
int pt1 = LoadFromAddress(GetEntityAddress(entity) + view_as<Address>(offs), NumberType_Int32);
int pt2 = view_as<int>(Address_PointerSize) == 8 ? LoadFromAddress(GetEntityAddress(entity) + view_as<Address>(offs + 4), NumberType_Int32) : 0;
if (pt1 == 0 && pt2 == 0)
return false;
return true;
}
// No convar for this one rn, didn't find it necessary
MRESReturn ForceVPhysicsCollide_Hook(int pThis, DHookReturn hReturn, DHookParam hParams)
{
int ent = hParams.Get(1);
// Also only works with sm_propspawner_notify_shot 1 cause of this
if (g_aSpawnedProps.FindValue(ent) != -1)
{
hReturn.Value = false;
return MRES_Supercede;
}
return MRES_Ignored;
}