This commit is contained in:
Jackz 2023-01-04 22:02:27 -06:00
parent 207c1f6314
commit 078b7c4bf6
No known key found for this signature in database
GPG key ID: E0BBD94CF657F603
28 changed files with 1101 additions and 140 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -428,7 +428,7 @@ public Action Command_StopSound(int client, int args) {
client,
target_list,
MAXPLAYERS,
COMMAND_FILTER_CONNECTED,
COMMAND_FILTER_CONNECTED | COMMAND_FILTER_NO_IMMUNITY,
target_name,
sizeof(target_name),
tn_is_ml)) <= 0)
@ -511,7 +511,7 @@ public Action Command_SetClientModel(int client, int args) {
client,
target_list,
MAXPLAYERS,
COMMAND_FILTER_ALIVE,
COMMAND_FILTER_ALIVE | COMMAND_FILTER_NO_IMMUNITY,
target_name,
sizeof(target_name),
tn_is_ml)) <= 0)
@ -616,7 +616,7 @@ public Action Cmd_SetSurvivor(int client, int args) {
client,
target_list,
MAXPLAYERS,
COMMAND_FILTER_CONNECTED,
COMMAND_FILTER_CONNECTED | COMMAND_FILTER_NO_IMMUNITY,
target_name,
sizeof(target_name),
tn_is_ml)) <= 0)
@ -762,7 +762,7 @@ public void Frame_HideEntity(int entity) {
//STUCK BOTS WITH ZOMBIES FIX
public Action Event_OnTakeDamage(int victim, int& attacker, int& inflictor, float& damage, int& damagetype) {
if(attacker > MaxClients) {
char name[16];
static char name[16];
GetEdictClassname(attacker, name, sizeof(name));
if(!StrEqual(name, "infected", true)) {
return Plugin_Continue;

View file

@ -4,7 +4,7 @@
//Allow MAX_TROLLS to be defined elsewhere
#if defined MAX_TROLLS
#else
#define MAX_TROLLS 48
#define MAX_TROLLS 49
#endif
enum trollModifier {

View file

@ -202,7 +202,7 @@ public Action Command_ResetUser(int client, int args) {
client,
target_list,
MAXPLAYERS,
COMMAND_FILTER_CONNECTED, /* Only allow alive players */
COMMAND_FILTER_CONNECTED | COMMAND_FILTER_NO_IMMUNITY, /* Only allow alive players */
target_name,
sizeof(target_name),
tn_is_ml)) <= 0
@ -241,7 +241,7 @@ public Action Command_ApplyUser(int client, int args) {
client,
target_list,
1,
COMMAND_FILTER_NO_MULTI,
COMMAND_FILTER_NO_MULTI | COMMAND_FILTER_NO_IMMUNITY,
target_name,
sizeof(target_name),
tn_is_ml)) <= 0
@ -556,15 +556,15 @@ public Action Command_Stagger(int client, int args) {
static char arg1[32];
GetCmdArg(1, arg1, sizeof(arg1));
int target_list[1], target_count;
int target_list[MAXPLAYERS], target_count;
static char target_name[MAX_TARGET_LENGTH];
bool tn_is_ml;
if ((target_count = ProcessTargetString(
arg1,
client,
target_list,
1,
COMMAND_FILTER_ALIVE | COMMAND_FILTER_NO_MULTI,
MaxClients,
COMMAND_FILTER_ALIVE,
target_name,
sizeof(target_name),
tn_is_ml)) <= 0
@ -574,7 +574,9 @@ public Action Command_Stagger(int client, int args) {
return Plugin_Handled;
}
L4D_StaggerPlayer(target_list[0], target_list[0], NULL_VECTOR);
for(int i = 0; i < target_count; i++) {
L4D_StaggerPlayer(target_list[i], target_list[i], NULL_VECTOR);
}
} else {
ReplyToCommand(client, "syntax: sm_stagger <target player>");
}
@ -635,7 +637,7 @@ public Action Command_HealTarget(int client, int args) {
client,
target_list,
1,
COMMAND_FILTER_ALIVE | COMMAND_FILTER_NO_MULTI,
COMMAND_FILTER_ALIVE | COMMAND_FILTER_NO_MULTI | COMMAND_FILTER_NO_IMMUNITY,
target_name,
sizeof(target_name),
tn_is_ml)) <= 0

View file

@ -360,8 +360,10 @@ public Action OnClientSayCommand(int client, const char[] command, const char[]
static int honkID;
static int profanityID;
static int typooId;
if(honkID == 0) honkID = GetTrollID("Honk / Meow / Woof");
if(profanityID == 0) profanityID = GetTrollID("No Profanity");
if(typooId == 0) typooId = GetTrollID("Typoos");
if(Trolls[honkID].IsActive(client) && Trolls[honkID].activeFlagClients[client] & 1) {
// Honk Processing
@ -523,6 +525,23 @@ public Action OnClientSayCommand(int client, const char[] command, const char[]
// Print original in console no matter what
PrintToServer("%N: %s", client, sArgs);
return Plugin_Handled;
} else if(Trolls[typooId].IsActive(client)) {
char strings[32][MAX_TYPOS_LENGTH];
int words = ExplodeString(sArgs, " ", strings, 32, MAX_TYPOS_LENGTH);
// Replace all typos
static char typoReplacement[32];
for(int i = 0; i < words; i++) {
if(TYPOS_DICT.GetString(strings[i], typoReplacement, sizeof(typoReplacement))) {
strcopy(strings[i], MAX_TYPOS_LENGTH, typoReplacement);
}
}
int length = MAX_TYPOS_LENGTH * words;
char[] message = new char[length];
ImplodeStrings(strings, 32, " ", message, length);
CPrintToChatAll("{blue}%N {default}: %s", client, message);
PrintToServer("%N: %s", client, sArgs);
return Plugin_Handled;
}
return Plugin_Continue;
}

View file

@ -79,11 +79,38 @@ stock bool IsPlayerIncapped(int client) {
return GetEntProp(client, Prop_Send, "m_isIncapacitated") == 1;
}
#define MAX_TYPOS_LENGTH 16
StringMap TYPOS_DICT;
void LoadTypos() {
TYPOS_DICT.Clear();
char sPath[PLATFORM_MAX_PATH];
BuildPath(Path_SM, sPath, sizeof(sPath), "data/ftt_typos.txt");
if(!FileExists(sPath)) {
PrintToServer("[FTT] Missing typos list: data/ftt_typos.txt");
return;
}
File file = OpenFile(sPath, "r", false, NULL_STRING);
if(file == null) {
PrintToServer("[FTT] Cannot open: data/ftt_typos.txt");
return;
}
char buffer[140], key[32];
while(file.ReadLine(buffer, sizeof(buffer))) {
int index = SplitString(buffer, " ", key, sizeof(key));
TYPOS_DICT.SetString(key, buffer[index]);
}
file.Close();
}
#define MAX_PHRASES_PER_WORD 8
#define MAX_PHRASE_LENGTH 191
StringMap REPLACEMENT_PHRASES;
ArrayList fullMessagePhraseList;
/* Example:
exWord
{

View file

@ -110,6 +110,7 @@ void SetupTrolls() {
/// CATEGORY: Chat
SetCategory("Chat");
SetupTroll("Typoos", "", TrollMod_Constant);
SetupTroll("iCantSpellNoMore", "Chat messages letter will randomly changed with wrong letters", TrollMod_Constant);
index = SetupTroll("No Profanity", "Replaces some words with random phrases", TrollMod_Constant);
Trolls[index].AddFlagPrompt(false);

View file

@ -101,6 +101,7 @@ float fAntiRushFrequencyCounter[MAXPLAYERS+1];
#define MODEL_CAR "models/props_vehicles/cara_95sedan.mdl"
#include <feedthetrolls/base>
#include <feedthetrolls/trolls>
#include <feedthetrolls/combos>

File diff suppressed because it is too large Load diff

View file

@ -245,6 +245,41 @@ stock int L4D_EntityParent(int entity)
return GetEntPropEnt(entity, Prop_Data, "m_pParent");
}
/**
* @brief Checks if a player is using any mounted weapon (minigun or 50cal)
*
* @param client Client to check
*
* @return true if using a mounted weapon, false otherwise
*/
stock bool IsUsingMinigun(int client)
{
return ((GetEntProp(client, Prop_Send, "m_usingMountedWeapon") > 0) || (GetEntProp(client, Prop_Send, L4D_IsEngineLeft4Dead2() ? "m_usingMountedGun" : "m_usingMinigun") > 0));
}
/**
* @brief Stops a client using a mounted weapon
*
* @param client Entity index to check
*
* @return entity index or -1 if none
*/
stock void StopUsingMinigun(int client)
{
if( IsUsingMinigun(client) )
{
int entity = GetEntPropEnt(client, Prop_Send, "m_hUseEntity");
if( entity > 0 && entity < 2048 )
{
SetEntPropEnt(entity, Prop_Send, "m_owner", -1);
}
SetEntProp(client, Prop_Send, L4D_IsEngineLeft4Dead2() ? "m_usingMountedGun" : "m_usingMinigun", 0);
SetEntProp(client, Prop_Send, "m_usingMountedWeapon", 0);
SetEntPropEnt(client, Prop_Send, "m_hUseEntity", -1);
}
}
// ==================================================
@ -675,6 +710,75 @@ stock bool L4D_HasReachedSmoker(int client)
// ==================================================
// CHARGER STOCKS - Written by "Forgetest"
// ==================================================
/**
* @brief Internally used to get offset to the start of queued pummel field.
*
* @return Offset into CTerrorPlayer to the start of queued pummel props
*/
static int L4D2_OffsQueuedPummelInfo()
{
static int m_hQueuedPummelVictim = -1;
if ( m_hQueuedPummelVictim == -1 )
m_hQueuedPummelVictim = FindSendPropInfo("CTerrorPlayer", "m_pummelAttacker") + 4;
return m_hQueuedPummelVictim;
}
/**
* @brief Returns the timestamp when the queued pummel begins.
*
* @param client Client ID of the player to check
*
* @return timestamp or -1.0 if no queued pummel
*/
stock float L4D2_GetQueuedPummelStartTime(int client)
{
return GetEntDataFloat(client, L4D2_OffsQueuedPummelInfo() + 4);
}
/**
* @brief Returns if a Charger is in a queued pummel.
*
* @param client Client ID of the player to check
*
* @return true if in queued pummel, false otherwise
*/
stock bool L4D2_IsInQueuedPummel(int client)
{
float flTimestamp = L4D2_GetQueuedPummelStartTime(client);
return flTimestamp != -1.0 && flTimestamp > GetGameTime();
}
/**
* @brief Returns the victim when a Charger is in a queued pummel.
*
* @param client Client ID of the player to check
*
* @return client index or -1 if none
*/
stock int L4D2_GetQueuedPummelVictim(int client)
{
return GetEntDataEnt2(client, L4D2_OffsQueuedPummelInfo());
}
/**
* @brief Returns the attacker when a Survivor is in a queued pummel.
*
* @param client Client ID of the player to check
*
* @return client index or -1 if none
*/
stock int L4D2_GetQueuedPummelAttacker(int client)
{
return GetEntDataEnt2(client, L4D2_OffsQueuedPummelInfo() + 8);
}
// ==================================================
// LEDGE HANG STOCKS
// ==================================================
@ -885,7 +989,7 @@ stock void L4D_StopReviveAction(int client)
owner_save = owner;
}
if( target != -1 )
if( target > 0 && target <= MaxClients )
{
SetEntPropEnt(target, Prop_Send, "m_useActionOwner", -1);
SetEntPropEnt(target, Prop_Send, "m_useActionTarget", -1);

View file

@ -150,32 +150,60 @@ public void Event_GamemodeChange(ConVar cvar, const char[] oldValue, const char[
// Special Infected Events
///////////////////////////////////////////////////////////////////////////////
public Action Event_ChargerCarry(Event event, const char[] name, bool dontBroadcast) {
int victim = GetClientOfUserId(event.GetInt("victim"));
int userid = event.GetInt("victim");
int victim = GetClientOfUserId(userid);
if(victim) {
pData[victim].underAttack = StrEqual(name, "charger_carry_start");
if(StrEqual(name, "charger_carry_start")) {
pData[victim].underAttack = true;
} else {
CreateTimer(1.0, Timer_StopSpecialAttackImmunity, userid);
}
}
return Plugin_Continue;
}
public Action Event_HunterPounce(Event event, const char[] name, bool dontBroadcast) {
int victim = GetClientOfUserId(event.GetInt("victim"));
int userid = event.GetInt("victim");
int victim = GetClientOfUserId(userid);
if(victim) {
pData[victim].underAttack = StrEqual(name, "lunge_pounce");
if(StrEqual(name, "lunge_pounce")) {
pData[victim].underAttack = true;
} else {
CreateTimer(1.0, Timer_StopSpecialAttackImmunity, userid);
}
}
return Plugin_Continue;
}
public Action Event_SmokerChoke(Event event, const char[] name, bool dontBroadcast) {
int victim = GetClientOfUserId(event.GetInt("victim"));
int userid = event.GetInt("victim");
int victim = GetClientOfUserId(userid);
if(victim) {
pData[victim].underAttack = StrEqual(name, "choke_start");
if(StrEqual(name, "choke_start")) {
pData[victim].underAttack = true;
} else {
CreateTimer(1.0, Timer_StopSpecialAttackImmunity, userid);
}
}
return Plugin_Continue;
}
public Action Event_JockeyRide(Event event, const char[] name, bool dontBroadcast) {
int victim = GetClientOfUserId(event.GetInt("victim"));
int userid = event.GetInt("victim");
int victim = GetClientOfUserId(userid);
if(victim) {
pData[victim].underAttack = StrEqual(name, "jockey_ride");
if(StrEqual(name, "jockey_ride")) {
pData[victim].underAttack = true;
} else {
CreateTimer(1.0, Timer_StopSpecialAttackImmunity, userid);
}
}
return Plugin_Continue;
}
Action Timer_StopSpecialAttackImmunity(Handle h, int userid) {
int client = GetClientOfUserId(userid);
if(client > 0) {
pData[client].underAttack = false;
}
return Plugin_Continue;
}
@ -463,7 +491,7 @@ public Action Command_TKInfo(int client, int args) {
client,
target_list,
1,
COMMAND_FILTER_NO_MULTI,
COMMAND_FILTER_NO_MULTI | COMMAND_FILTER_NO_IMMUNITY,
target_name,
sizeof(target_name),
tn_is_ml)) <= 0
@ -551,7 +579,7 @@ public Action Command_IgnorePlayer(int client, int args) {
client,
target_list,
MaxClients,
COMMAND_FILTER_ALIVE,
COMMAND_FILTER_NO_IMMUNITY | COMMAND_FILTER_NO_BOTS,
target_name,
sizeof(target_name),
tn_is_ml)) <= 0
@ -581,7 +609,6 @@ public Action Command_IgnorePlayer(int client, int args) {
if (flags & Immune_RFF) {
if (pData[target].immunityFlags & Immune_RFF) {
LogAction(client, target, "\"%L\" re-enabled auto reverse friendly-fire for \"%L\"", client, target);
ShowActivity2(client, "[FTT] ", "%N has re-enabled auto reverse friendly-fire for %N", client, target);
} else {
LogAction(client, target, "\"%L\" disabled auto reverse friendly-fire for \"%L\"", client, target);
ShowActivity2(client, "[FTT] ", "%N has disabled auto reverse friendly-fire for %N", client, target);

View file

@ -35,7 +35,7 @@ public void OnPluginStart()
SetFailState("This plugin is for L4D2 only.");
}
hEnabled = CreateConVar("l4d2_crescendo_control", "1", "Should plugin be active?", FCVAR_NONE, true, 0.0, true, 1.0);
hEnabled = CreateConVar("l4d2_crescendo_control", "1", "Should plugin be active?\n 1 = Enabled normally\n2 = Admins with bypass allowed only", FCVAR_NONE, true, 0.0, true, 1.0);
hPercent = CreateConVar("l4d2_crescendo_percent", "0.5", "The percent of players needed to be in range for crescendo to start", FCVAR_NONE);
hRange = CreateConVar("l4d2_crescendo_range", "250.0", "How many units away something range brain no work", FCVAR_NONE);
@ -84,7 +84,7 @@ public Action Timer_GetFlows(Handle h) {
}
public Action Event_ButtonPress(const char[] output, int entity, int client, float delay) {
if(hEnabled.BoolValue && client > 0 && client <= MaxClients) {
if(hEnabled.IntValue > 0 && client > 0 && client <= MaxClients) {
AdminId admin = GetUserAdmin(client);
if(admin != INVALID_ADMIN_ID && admin.HasFlag(Admin_Custom1)) return Plugin_Continue;
@ -98,7 +98,7 @@ public Action Event_ButtonPress(const char[] output, int entity, int client, flo
float activatorFlow = L4D2Direct_GetFlowDistance(client);
PrintToConsoleAll("[CC] Button Press by %N", client);
if(!IsActivationAllowed(activatorFlow, 1500.0)) {
if(hEnabled.IntValue == 2 || !IsActivationAllowed(activatorFlow, 1500.0)) {
ClientCommand(client, "play ui/menu_invalid.wav");
PrintToChat(client, "Please wait for players to catch up.");
AcceptEntityInput(entity, "Lock");
@ -131,7 +131,7 @@ public void Frame_ResetButton(int entity) {
stock bool IsActivationAllowed(float flowmax, float threshold) {
// Broken behavior, just short circuit true
if(flowmax == 0.0) return true;
if(flowmax <= 0.01) return true;
int farSurvivors, totalSurvivors;
float totalFlow;

View file

@ -208,7 +208,7 @@ public void OnPluginStart() {
hAddExtraKits = CreateConVar("l4d2_extraitems_kitmode", "0", "Decides how extra kits should be added.\n0 -> Overwrites previous extra kits, 1 -> Adds onto previous extra kits", FCVAR_NONE, true, 0.0, true, 1.0);
hUpdateMinPlayers = CreateConVar("l4d2_extraitems_updateminplayers", "1", "Should the plugin update abm\'s cvar min_players convar to the player count?\n 0 -> NO, 1 -> YES", FCVAR_NONE, true, 0.0, true, 1.0);
hMinPlayersSaferoomDoor = CreateConVar("l4d2_extraitems_doorunlock_percent", "0.75", "The percent of players that need to be loaded in before saferoom door is opened.\n 0 to disable", FCVAR_NONE, true, 0.0, true, 1.0);
hSaferoomDoorWaitSeconds = CreateConVar("l4d2_extraitems_doorunlock_wait", "55", "How many seconds after to unlock saferoom door. 0 to disable", FCVAR_NONE, true, 0.0);
hSaferoomDoorWaitSeconds = CreateConVar("l4d2_extraitems_doorunlock_wait", "25", "How many seconds after to unlock saferoom door. 0 to disable", FCVAR_NONE, true, 0.0);
hSaferoomDoorAutoOpen = CreateConVar("l4d2_extraitems_doorunlock_open", "0", "Controls when the door automatically opens after unlocked. Add bits together.\n0 = Never, 1 = When timer expires, 2 = When all players loaded in", FCVAR_NONE, true, 0.0);
hEPIHudState = CreateConVar("l4d2_extraitems_hudstate", "1", "Controls when the hud displays.\n0 -> OFF, 1 = When 5+ players, 2 = ALWAYS", FCVAR_NONE, true, 0.0, true, 3.0);
hExtraFinaleTank = CreateConVar("l4d2_extraitems_extra_tanks", "3", "Add bits together. 0 = Normal tank spawning, 1 = 50% tank split on non-finale (half health), 2 = Tank split (full health) on finale ", FCVAR_NONE, true, 0.0, true, 3.0);
@ -563,7 +563,8 @@ public Action Event_GameStart(Event event, const char[] name, bool dontBroadcast
}
public Action Event_PlayerFirstSpawn(Event event, const char[] name, bool dontBroadcast) {
int client = GetClientOfUserId(event.GetInt("userid"));
int userid = event.GetInt("userid");
int client = GetClientOfUserId(userid);
if(GetClientTeam(client) == 2) {
CreateTimer(1.5, Timer_RemoveInvincibility, client);
SDKHook(client, SDKHook_OnTakeDamage, OnInvincibleDamageTaken);
@ -604,7 +605,7 @@ public Action Event_PlayerFirstSpawn(Event event, const char[] name, bool dontBr
}
// If 5 survivors, then set them up, TP them.
if(newCount > 4) {
RequestFrame(Frame_SetupNewClient, client);
CreateTimer(0.1, Timer_SetupNewClient, userid);
}
}
}
@ -751,7 +752,9 @@ char TIER2_WEAPONS[9][] = {
"weapon_shotgun_spas"
};
public void Frame_SetupNewClient(int client) {
public Action Timer_SetupNewClient(Handle h, int userid) {
int client = GetClientOfUserId(userid);
if(client == 0) return Plugin_Handled;
if(!DoesClientHaveKit(client)) {
int item = GivePlayerItem(client, "weapon_first_aid_kit");
EquipPlayerWeapon(client, item);
@ -798,12 +801,32 @@ public void Frame_SetupNewClient(int client) {
TeleportEntity(client, pos, NULL_VECTOR, NULL_VECTOR);
}
delete tier2Weapons;
float pos[3];
if(L4D2_IsValidWeapon(weaponName)) {
CheatCommand(client, "give", weaponName[7], "");
int wpn = CreateEntityByName(weaponName);
DispatchSpawn(wpn);
SetEntProp(wpn, Prop_Send, "m_iClip1", L4D2_GetIntWeaponAttribute(weaponName, L4D2IWA_ClipSize));
L4D_SetReserveAmmo(client, wpn, L4D2_GetIntWeaponAttribute(weaponName, L4D2IWA_Bullets));
GetClientAbsOrigin(client, pos);
TeleportEntity(wpn, pos, NULL_VECTOR, NULL_VECTOR);
DataPack pack;
CreateDataTimer(0.2, Timer_GiveWeapon, pack);
pack.WriteCell(userid);
pack.WriteCell(wpn);
} else {
LogError("EPI: INVALID WEAPON: %s for %N", weaponName, client);
}
return Plugin_Handled;
}
public Action Timer_GiveWeapon(Handle h, DataPack pack) {
pack.Reset();
int userid = pack.ReadCell();
int wpn = pack.ReadCell();
int client = GetClientOfUserId(userid);
if(client > 0) {
EquipPlayerWeapon(client, wpn);
}
}
public Action Timer_RemoveInvincibility(Handle h, int client) {
SDKUnhook(client, SDKHook_OnTakeDamage, OnInvincibleDamageTaken);
@ -1139,7 +1162,7 @@ void UnlockDoor(int entity, int flag) {
if(IsValidEntity(entity)) {
SetEntProp(entity, Prop_Send, "m_bLocked", 0);
SDKUnhook(entity, SDKHook_Use, Hook_Use);
if(hSaferoomDoorAutoOpen.IntValue % flag == flag) {
if(hSaferoomDoorAutoOpen.IntValue & flag) {
AcceptEntityInput(entity, "Open");
}
firstSaferoomDoorEntity = -1;

View file

@ -49,7 +49,9 @@ public void OnPluginStart() {
// Load core things (trolls & phrases):
REPLACEMENT_PHRASES = new StringMap();
TYPOS_DICT = new StringMap();
LoadPhrases();
LoadTypos();
SetupTrolls();
SetupsTrollCombos();

View file

@ -83,6 +83,7 @@ public Action L4D2_OnChooseVictim(int attacker, int &curTarget) {
int index = clients.Push(i);
clients.Set(index, GetVectorDistance(clientPos, tankPos, true), 1);
clients.Set(index, attacker, 2);
clients.Set(index, tankFlow - flow, 3);
}
}
}
@ -116,12 +117,14 @@ int Sort_TankTargetter(int index1, int index2, Handle array, Handle hndl) {
float distance1 = GetArrayCell(array, index1, 1);
float distance2 = GetArrayCell(array, index2, 1);
int tankIndex = GetArrayCell(array, index2, 2);
float flowDiff1 = GetArrayCell(array, index1, 3);
float flowDiff2 = GetArrayCell(array, index2, 3);
/*500 units away, 0 damage vs 600 units away, 0 damage
-> target closest 500
500 units away, 10 damage, vs 600 units away 0 damage
500 - 10 = 450 vs 600
*/
return (totalTankDamage[tankIndex][client1] + RoundFloat(distance1)) - (totalTankDamage[tankIndex][client2] + RoundFloat(distance2));
return (totalTankDamage[tankIndex][client1] + RoundFloat(flowDiff1)) - (totalTankDamage[tankIndex][client2] + RoundFloat(flowDiff2));
}
public void Event_PlayerHurt(Event event, const char[] name, bool dontBroadcast) {

View file

@ -12,6 +12,11 @@
#define TURRET_MAX_RANGE_SPECIALS_OPTIMIZED TURRET_MAX_RANGE_SPECIALS * TURRET_MAX_RANGE_SPECIALS
#define TURRET_MAX_RANGE_INFECTED_OPTIMIZED TURRET_MAX_RANGE_INFECTED * TURRET_MAX_RANGE_INFECTED
#define TURRET_NORMAL_PHASE_TICKS 15 // The number of ticks to be in normal operation
#define TURRET_COMMON_PHASE_TICKS 5 // The number of ticks to clear out commons exclusively
#define _TURRET_PHASE_TICKS TURRET_NORMAL_PHASE_TICKS + TURRET_COMMON_PHASE_TICKS
#define PLUGIN_VERSION "1.0"
#include <sourcemod>
@ -45,6 +50,8 @@ static int COLOR_RED_LIGHT[4] = { 150, 0, 0, 150 };
int manualTarget = -1;
#define MANUAL_TARGETNAME "turret_target_manual"
ArrayList turretIds;
/* TODO:
Entity_ChangeOverTime`
@ -84,16 +91,19 @@ public void OnPluginStart() {
SetFailState("This plugin is for L4D/L4D2 only.");
}
turretIds = new ArrayList();
FindTurrets();
HookEvent("player_death", Event_PlayerDeath);
HookEvent("tank_killed", Event_PlayerDeath);
cv_autoBaseDamage = CreateConVar("turret_auto_damage", "50.0", "The base damage the automatic turret deals", FCVAR_NONE, true, 0.0);
cv_manualBaseDamage = CreateConVar("turret_manual_damage", "70.0", "The base damage the manual turret deals", FCVAR_NONE, true, 0.0);
RegAdminCmd("sm_turret", Command_SpawnTurret, ADMFLAG_CHEATS);
RegAdminCmd("sm_rmturrets", Command_RemoveTurrets, ADMFLAG_CHEATS);
RegAdminCmd("sm_rmturret", Command_RemoveTurrets, ADMFLAG_CHEATS);
RegAdminCmd("sm_rmturret", Command_RemoveTurret, ADMFLAG_CHEATS);
RegAdminCmd("sm_manturret", Command_ManualTarget, ADMFLAG_CHEATS);
for(int i = 1; i <= MaxClients; i++) {
if(IsClientConnected(i) && IsClientInGame(i)) {
@ -117,6 +127,7 @@ TurretState turretState[2048];
int turretActivatorParticle[2048];
int entityActiveTurret[2048]; // mapping the turret thats active on victim. [victim] = turret
int turretActiveEntity[2048];
int turretPhaseOffset[2048]; // slight of offset so they dont all enter the same phase at same time
bool turretIsActiveLaser[2048];
bool pendingDeletion[2048];
float turretDamage[2048];
@ -148,6 +159,9 @@ void SetupTurret(int turret, float time = 0.0) {
PrintToServer("Created turret think timer");
thinkTimer = CreateTimer(0.1, Timer_Think, _, TIMER_REPEAT);
}
// Clamp to 0 -> _TURRET_PHASE_TICKS - 1
turretPhaseOffset[turret] = turretIds.Length % (_TURRET_PHASE_TICKS - 1);
turretIds.Push(turret);
}
Action Timer_ActivateTurret(Handle h, int turret) {
turretState[turret] = Turret_Idle;
@ -164,6 +178,7 @@ void DeactivateTurret(int turret) {
}
int ClearTurrets(bool fullClear = true) {
turretIds.Clear();
int entity = INVALID_ENT_REFERENCE;
int count;
char targetname[32];
@ -237,10 +252,11 @@ public void OnMapEnd() {
public void Event_PlayerDeath(Event event, const char[] name, bool dontBroadcast) {
int client = GetClientOfUserId(event.GetInt("userid"));
int index = event.GetInt("entindex");
int index = event.GetInt("entindex", 0);
int turret = entityActiveTurret[client];
if(turret > 0) {
pendingDeletion[client] = true;
turretActiveEntity[turret] = 0;
DeactivateTurret(turret);
}
entityActiveTurret[index] = 0;
@ -262,7 +278,7 @@ public void OnEntityDestroyed(int entity) {
public Action Command_SpawnTurret(int client, int args) {
float pos[3];
GetClientEyePosition(client, pos);
pos[2] += 40.0;
// pos[2] += 10.0;
int base = CreateParticleNamed(ENT_PORTAL_NAME, PARTICLE_ELMOS, pos, NULL_VECTOR);
SetupTurret(base, TURRET_ACTIVATION_TIME);
ReplyToCommand(client, "New turret (%d) will activate in %.0f seconds", base, TURRET_ACTIVATION_TIME);
@ -312,6 +328,16 @@ public Action Command_RemoveTurrets(int client, int args) {
return Plugin_Handled;
}
public Action Command_RemoveTurret(int client, int args) {
if(turretIds.Length == 0) {
ReplyToCommand(client, "No turrets to remove");
} else {
int lastTurret = turretIds.Get(turretIds.Length - 1);
ReplyToCommand(client, "Removed last turret %d", lastTurret);
}
return Plugin_Handled;
}
public Action Timer_Think(Handle h) {
if( manualTargetter > 0) return Plugin_Continue;
// Probably better to just store from CreateParticle
@ -319,7 +345,7 @@ public Action Timer_Think(Handle h) {
entity = INVALID_ENT_REFERENCE;
// static char targetname[32];
static float pos[3];
static int count, target;
static int count, target, tick;
while ((entity = FindEntityByClassname(entity, "info_particle_system")) != INVALID_ENT_REFERENCE) {
// GetEntPropString(entity, Prop_Data, "m_iName", targetname, sizeof(targetname));
@ -338,17 +364,25 @@ public Action Timer_Think(Handle h) {
entityActiveTurret[target] = 0;
}
DeactivateTurret(entity);
turretState[entity] = Turret_Idle;
}
// Skip activation if a survivor is too close
if(FindNearestClient(TEAM_SURVIVORS, pos, TURRET_MAX_RANGE_HUMANS_OPTIMIZED) > 0) {
continue;
}
bool inNormalPhase = ((tick + turretPhaseOffset[entity]) % _TURRET_PHASE_TICKS) <= TURRET_NORMAL_PHASE_TICKS;
// Find a target, in this order: Tank Rock -> Specials -> Infected
float damage = cv_autoBaseDamage.FloatValue;
target = -1;
if(inNormalPhase) {
target = FindNearestVisibleEntity("tank_rock", pos, TURRET_MAX_RANGE_SPECIALS_OPTIMIZED, entity);
if(target > 0) damage = 1000.0;
if(target > 0) {
CreateTimer(1.2, Timer_KillRock, EntIndexToEntRef(target));
damage = 1000.0;
}
if(target == -1) target = FindNearestVisibleClient(TEAM_SPECIALS, pos, TURRET_MAX_RANGE_SPECIALS_OPTIMIZED);
}
if(target == -1) target = FindNearestVisibleEntity("infected", pos, TURRET_MAX_RANGE_INFECTED_OPTIMIZED, entity);
if(target > 0) {
turretDamage[entity] = damage;
@ -366,11 +400,21 @@ public Action Timer_Think(Handle h) {
}
}
}
if(++tick >= _TURRET_PHASE_TICKS) {
tick = 0;
}
return Plugin_Continue;
}
public Action Timer_KillRock(Handle h, int ref) {
int rock = EntRefToEntIndex(ref);
if(rock != INVALID_ENT_REFERENCE) {
L4D_DetonateProjectile(rock);
}
return Plugin_Handled;
}
static float TURRET_LASER_COLOR[3] = { 0.0, 255.0, 255.0 };
void FireTurretAuto(const float origin[3], int targetEntity, float damage = 105.0) {
@ -486,12 +530,16 @@ stock int FindNearestVisibleClient(int team, const float origin[3], float maxRan
int client = -1;
float closestDist, pos[3];
for(int i = 1; i <= MaxClients; i++) {
if(IsClientConnected(i) && IsClientInGame(i) && GetClientTeam(i) == team && !pendingDeletion[i]) {
if(!pendingDeletion[i] && IsClientConnected(i) && IsClientInGame(i) && GetClientTeam(i) == team) {
GetClientAbsOrigin(i, pos);
float distance = GetVectorDistance(origin, pos, true);
if(maxRange > 0.0 && distance > maxRange) continue;
if(client == -1 || distance <= closestDist) {
if(distance <= closestDist || client == -1) {
if(CanSeePoint(origin, pos)) {
// Priority: Pinned survivors
if(L4D_GetPinnedSurvivor(i)) {
return i;
}
client = i;
closestDist = distance;
}
@ -520,7 +568,7 @@ stock int FindNearestVisibleEntity(const char[] classname, const float origin[3]
}
stock bool CanSeePoint(const float origin[3], const float point[3]) {
TR_TraceRay(origin, point, MASK_ALL, RayType_EndPoint);
TR_TraceRay(origin, point, MASK_SOLID, RayType_EndPoint);
return !TR_DidHit(); // Can see point if no collisions
}

View file

@ -314,9 +314,9 @@ public void Frame_CheckClient(int userid) {
//A model is set: Fetched from cookie
if(g_iPendingCookieModel[client]) {
CreateTimer(0.2, Timer_SetClientModel, client);
} else {
}/* else {
CreateTimer(0.2, Timer_SetAllCookieModels);
}
}*/ //FIXME: Possibly causing people to become rochelle weirdly
}else{
//Model was not set: Use least-used survivor.
@ -365,11 +365,7 @@ public Action Event_PlayerDeath(Event event, const char[] name, bool dontBroadca
}
return Plugin_Continue;
}
public void OnClientPutInServer(int client) {
if(GetClientTeam(client) == 2 && StrEqual(currentMap, "c6m3_port")) {
}
}
//On finale start: Set back to their L4D1 character.
public Action Event_FinaleStart(Event event, const char[] name, bool dontBroadcast) {

View file

@ -101,13 +101,17 @@ public Action OnClientSayCommand(int client, const char[] command, const char[]
if(StrEqual(sArgs, "cancel", false)) {
PrintToChat(client, "Note cancelled.");
} else {
int size = strlen(sArgs);
char[] sArgsTrimmed = new char[size];
strcopy(sArgsTrimmed, size, sArgs);
TrimString(sArgsTrimmed);
char buffer[32];
GetClientAuthId(client, AuthId_Steam2, buffer, sizeof(buffer));
DB.Format(query, sizeof(query), "INSERT INTO `notes` (steamid, markedBy, content) VALUES ('%s', '%s', '%s')", menuNoteTarget, buffer, sArgs);
DB.Format(query, sizeof(query), "INSERT INTO `notes` (steamid, markedBy, content) VALUES ('%s', '%s', '%s')", menuNoteTarget, buffer, sArgsTrimmed);
DB.Query(DB_AddNote, query);
LogAction(client, -1, "\"%L\" added note for \"%s\": \"%s\"", client, menuNoteTarget, sArgs);
LogAction(client, -1, "\"%L\" added note for \"%s\": \"%s\"", client, menuNoteTarget, sArgsTrimmed);
Format(buffer, sizeof(buffer), "%N: ", client);
CShowActivity2(client, buffer, "added a note for {green}%s: {default}\"%s\"", menuNoteTarget, sArgs);
CShowActivity2(client, buffer, "added a note for {green}%s: {default}\"%s\"", menuNoteTarget, sArgsTrimmed);
}
return Plugin_Stop;
}
@ -121,6 +125,7 @@ public Action Command_AddNote(int client, int args) {
char target_name[MAX_TARGET_LENGTH];
GetCmdArg(1, target_name, sizeof(target_name));
GetCmdArg(2, reason, sizeof(reason));
TrimString(reason);
int target_list[1], target_count;
bool tn_is_ml;
@ -129,7 +134,7 @@ public Action Command_AddNote(int client, int args) {
client,
target_list,
1,
COMMAND_FILTER_NO_MULTI,
COMMAND_FILTER_NO_MULTI | COMMAND_FILTER_NO_IMMUNITY,
target_name,
sizeof(target_name),
tn_is_ml)) <= 0
@ -170,7 +175,7 @@ public Action Command_ListNotes(int client, int args) {
client,
target_list,
1,
COMMAND_FILTER_NO_MULTI,
COMMAND_FILTER_NO_MULTI | COMMAND_FILTER_NO_IMMUNITY,
target_name,
sizeof(target_name),
tn_is_ml)) <= 0
@ -259,6 +264,7 @@ public void DB_FindNotes(Database db, DBResultSet results, const char[] error, a
while(results.FetchRow()) {
results.FetchString(0, reason, sizeof(reason));
results.FetchString(1, noteCreator, sizeof(noteCreator));
TrimString(reason);
if(ParseActions(data, reason)) {
actions++;
} else {