Add sm_gotocouch & effect intensity

This commit is contained in:
2026-09-29 02:41:20 +01:00
parent 867b3bfbba
commit 598dc20ae3
3 changed files with 133 additions and 2 deletions
+91 -1
View File
@@ -64,6 +64,7 @@ ConVar g_cvModel;
ConVar g_cvPushScale;
ConVar g_cvPistolOnly;
ConVar g_cvEffect;
ConVar g_cvEffectIntensity;
ConVar g_cvIgnoreClip;
ConVar g_cvCollisionGroup;
@@ -116,6 +117,7 @@ public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max
CreateNative("Couch_IsPlaced", Native_IsPlaced);
CreateNative("Couch_Spawn", Native_Spawn);
CreateNative("Couch_Bring", Native_Bring);
CreateNative("Couch_Goto", Native_Goto);
CreateNative("Couch_Remove", Native_Remove);
CreateNative("Couch_IsEffectEnabled", Native_IsEffectEnabled);
CreateNative("Couch_SetEffectEnabled", Native_SetEffectEnabled);
@@ -141,12 +143,14 @@ public void OnPluginStart()
g_cvPushScale = CreateConVar("sm_couch_push_scale", "1.0", "Multiplier on the force bullets apply to the couch. 1.0 is the engine's own push.", FCVAR_NOTIFY, true, 0.0, true, 100.0);
g_cvPistolOnly = CreateConVar("sm_couch_pistol_only", "0", "Only pistols and knives move the couch; other guns and grenades are ignored.", FCVAR_NOTIFY, true, 0.0, true, 1.0);
g_cvEffect = CreateConVar("sm_couch_effect", "0", "Colour cycle and pulsing light on the couch.", FCVAR_NOTIFY, true, 0.0, true, 1.0);
g_cvEffectIntensity = CreateConVar("sm_couch_effect_intensity", "3", "Brightness of the couch effect light. 0 is off, 3 is the default, 8 is very bright. Applies immediately.", FCVAR_NOTIFY, true, 0.0, true, 10.0);
g_cvIgnoreClip = CreateConVar("sm_couch_ignore_clip", "1", "Let the couch pass through func_clip_vphysics brushes.", FCVAR_NOTIFY, true, 0.0, true, 1.0);
g_cvTierRequirement = CreateConVar("sm_couch_tier_requirement", "0", "Minimum playtime tier needed to shoot the couch.", FCVAR_NOTIFY, true, 0.0);
g_cvCollisionGroup = CreateConVar("sm_couch_collision_group", "5", "Collision group given to the couch at spawn. 5 (player), 0 (prop)", FCVAR_NOTIFY, true, 0.0, true, 20.0);
g_cvModel.AddChangeHook(OnModelChanged);
g_cvEffect.AddChangeHook(OnEffectChanged);
g_cvEffectIntensity.AddChangeHook(OnEffectIntensityChanged);
AutoExecConfig(true, "Couch");
@@ -338,6 +342,56 @@ bool TraceFilter_NotSelfOrCouch(int entity, int contentsMask, any client)
return entity != client && entity != GetCouchIndex();
}
// Landing spot for a player sent to the couch: on top of it when it is upright, otherwise beside it.
// The couch is kept solid in the trace so the player never arrives inside it.
#define COUCH_GOTO_LIFT 8.0
#define COUCH_GOTO_SIDE_GAP 24.0
bool FindCouchLandingSpot(int client, int couch, float vecSpot[3])
{
float vecOrigin[3];
float vecMaxs[3];
GetEntPropVector(couch, Prop_Send, "m_vecOrigin", vecOrigin);
GetEntPropVector(couch, Prop_Send, "m_vecMaxs", vecMaxs);
// Tried in order: on top when upright, on top when tipped over, then each side.
float fTop = vecMaxs[2] + COUCH_GOTO_LIFT;
float fTipped = GetVectorLength(vecMaxs) + COUCH_GOTO_LIFT;
float fSideX = vecMaxs[0] + COUCH_GOTO_SIDE_GAP;
float fSideY = vecMaxs[1] + COUCH_GOTO_SIDE_GAP;
float vecOffsets[6][3];
vecOffsets[0][2] = fTop;
vecOffsets[1][2] = fTipped;
vecOffsets[2][0] = fSideX; vecOffsets[2][2] = COUCH_GOTO_LIFT;
vecOffsets[3][0] = -fSideX; vecOffsets[3][2] = COUCH_GOTO_LIFT;
vecOffsets[4][1] = fSideY; vecOffsets[4][2] = COUCH_GOTO_LIFT;
vecOffsets[5][1] = -fSideY; vecOffsets[5][2] = COUCH_GOTO_LIFT;
float vecMins[3];
float vecPlayerMaxs[3];
GetClientMins(client, vecMins);
GetClientMaxs(client, vecPlayerMaxs);
for (int i = 0; i < sizeof(vecOffsets); i++)
{
AddVectors(vecOrigin, vecOffsets[i], vecSpot);
TR_TraceHullFilter(vecSpot, vecSpot, vecMins, vecPlayerMaxs, MASK_PLAYERSOLID, TraceFilter_NotSelf, client);
if (!TR_DidHit())
return true;
}
// Nothing clear; go high above the couch so the command still does something.
AddVectors(vecOrigin, vecOffsets[1], vecSpot);
return false;
}
bool TraceFilter_NotSelf(int entity, int contentsMask, any client)
{
return entity != client;
}
// The only place the couch is created. Returns the entity or -1.
int SpawnCouchAt(const float vecOrigin[3], const float vecAngles[3])
{
@@ -664,6 +718,22 @@ public void OnEffectChanged(ConVar convar, const char[] oldValue, const char[] n
ApplyEffectState();
}
public void OnEffectIntensityChanged(ConVar convar, const char[] oldValue, const char[] newValue)
{
ApplyLightIntensity();
}
// light_dynamic takes brightness as an input, so a running light updates in place.
void ApplyLightIntensity()
{
int light = GetLightIndex();
if (light == -1)
return;
SetVariantInt(g_cvEffectIntensity.IntValue);
AcceptEntityInput(light, "brightness");
}
// Brings the light and beacon in line with sm_couch_effect. Safe to call at any time.
void ApplyEffectState()
{
@@ -699,7 +769,7 @@ int SpawnCouchLight(int couch)
DispatchKeyValue(light, "pitch", "-90");
DispatchKeyValue(light, "angles", "0 0 0");
DispatchKeyValue(light, "_light", "255 255 255 255");
DispatchKeyValue(light, "brightness", "3");
DispatchKeyValueInt(light, "brightness", g_cvEffectIntensity.IntValue);
DispatchKeyValue(light, "distance", "130");
DispatchKeyValue(light, "spotlight_radius", "70");
@@ -839,6 +909,26 @@ public any Native_Bring(Handle plugin, int numParams)
return true;
}
public any Native_Goto(Handle plugin, int numParams)
{
int client = GetNativeCell(1);
if (client < 1 || client > MaxClients || !IsClientInGame(client))
return ThrowNativeError(SP_ERROR_NATIVE, "Client %d is not in game", client);
int couch = GetCouchIndex();
if (couch == -1)
return false;
float vecSpot[3];
FindCouchLandingSpot(client, couch, vecSpot);
// Arrives still, keeps their view angles.
float vecStill[3];
TeleportEntity(client, vecSpot, NULL_VECTOR, vecStill);
return true;
}
public any Native_Remove(Handle plugin, int numParams)
{
bool bHadSomething = g_bPlaced || GetCouchIndex() != -1;
+32 -1
View File
@@ -23,6 +23,7 @@ public void OnPluginStart()
RegAdminCmd("sm_couch", Command_Couch, ADMFLAG_CHANGEMAP, "Opens the couch menu");
RegAdminCmd("sm_spawncouch", Command_SpawnCouch, ADMFLAG_CHANGEMAP, "Spawns the couch where you are aiming");
RegAdminCmd("sm_bringcouch", Command_BringCouch, ADMFLAG_CHANGEMAP, "Brings the couch to where you are aiming");
RegAdminCmd("sm_gotocouch", Command_GotoCouch, ADMFLAG_CHANGEMAP, "Teleports you to the couch");
RegAdminCmd("sm_removecouch", Command_RemoveCouch, ADMFLAG_CHANGEMAP, "Removes the couch");
RegAdminCmd("sm_couchpistolonly", Command_PistolOnly, ADMFLAG_CHANGEMAP, "sm_couchpistolonly [0|1] - only pistols and knives move the couch; no argument toggles");
}
@@ -75,6 +76,15 @@ public Action Command_BringCouch(int client, int args)
return Plugin_Handled;
}
public Action Command_GotoCouch(int client, int args)
{
if (!RequireInGame(client) || !RequireCore(client))
return Plugin_Handled;
DoGoto(client);
return Plugin_Handled;
}
// Needs no position, so it also works from the server console.
public Action Command_RemoveCouch(int client, int args)
{
@@ -128,7 +138,7 @@ void DoSpawn(int client)
return;
}
ShowActivity2(client, PREFIX, "\x01Spawned the \x04couch\x01.");
ShowActivity2(client, PREFIX, "\x01Spawned the \x04couch\x01, it will respawn here each round until removed.");
LogAction(client, -1, "\"%L\" spawned the couch.", client);
}
@@ -144,6 +154,24 @@ void DoBring(int client)
LogAction(client, -1, "\"%L\" brought the couch.", client);
}
void DoGoto(int client)
{
if (!IsPlayerAlive(client))
{
ReplyToCommand(client, "[SM] You must be alive to go to the couch.");
return;
}
if (!Couch_Goto(client))
{
ReplyToCommand(client, "[SM] There is no couch to go to. Use sm_spawncouch first.");
return;
}
ShowActivity2(client, PREFIX, "\x01Teleported to the \x04couch\x01.");
LogAction(client, -1, "\"%L\" teleported to the couch.", client);
}
void DoRemove(int client)
{
if (!Couch_Remove())
@@ -190,6 +218,7 @@ void ShowCouchMenu(int client)
// With a couch out, bring is the way to move it; without one there is nothing to bring.
menu.AddItem("spawn", "Spawn couch at crosshair", bHasCouch ? ITEMDRAW_DISABLED : ITEMDRAW_DEFAULT);
menu.AddItem("bring", "Bring couch to crosshair", bHasCouch ? ITEMDRAW_DEFAULT : ITEMDRAW_DISABLED);
menu.AddItem("goto", "Teleport to couch", bHasCouch ? ITEMDRAW_DEFAULT : ITEMDRAW_DISABLED);
if (bHasCouch)
menu.AddItem("remove", "Remove couch");
@@ -230,6 +259,8 @@ public int MenuHandler_Couch(Menu menu, MenuAction action, int param1, int param
DoSpawn(client);
else if (StrEqual(sInfo, "bring"))
DoBring(client);
else if (StrEqual(sInfo, "goto"))
DoGoto(client);
else if (StrEqual(sInfo, "remove"))
DoRemove(client);
else if (StrEqual(sInfo, "effect"))
+10
View File
@@ -67,6 +67,15 @@ native int Couch_Spawn(int client);
*/
native bool Couch_Bring(int client);
/**
* Teleports the client onto the couch, or beside it when the top is blocked. The client keeps
* their view angles and arrives with no velocity.
*
* @param client Client to teleport. Must be in game.
* @return True on success, false if there is no couch.
*/
native bool Couch_Goto(int client);
/**
* Removes the couch and forgets its placement.
*
@@ -131,6 +140,7 @@ public SharedPlugin __pl_Couch =
MarkNativeAsOptional("Couch_IsPlaced");
MarkNativeAsOptional("Couch_Spawn");
MarkNativeAsOptional("Couch_Bring");
MarkNativeAsOptional("Couch_Goto");
MarkNativeAsOptional("Couch_Remove");
MarkNativeAsOptional("Couch_IsEffectEnabled");
MarkNativeAsOptional("Couch_SetEffectEnabled");