ftt: Use a queue for special spawning

This commit is contained in:
Jackzie 2022-05-11 23:04:21 -05:00
parent c3e43d744a
commit d9241b2949
No known key found for this signature in database
GPG key ID: 1E834FE36520537A
11 changed files with 329 additions and 158 deletions

Binary file not shown.

View file

@ -4,7 +4,7 @@
//Allow MAX_TROLLS to be defined elsewhere
#if defined MAX_TROLLS
#else
#define MAX_TROLLS 34
#define MAX_TROLLS 37
#endif
enum trollModifier {
@ -35,6 +35,10 @@ bool SilentMenuSelected[MAXPLAYERS+1];
static int g_trollAddPromptIndex;
ArrayList gRandomClients;
char SPECIAL_NAMES[][] = {
"Smoker", "Boomer", "Hunter", "Spitter", "Jockey", "Charger", "Witch", "Tank"
};
enum struct TrollFlagPrompt {
char promptText[MAX_TROLL_FLAG_LENGTH];
int flags;
@ -53,6 +57,15 @@ enum struct TrollFlagPrompt {
}
}
int GetIndexFromPower(int powerOfTwo) {
for(int i = 0; i < 16; i++) {
if(1 << i == powerOfTwo) {
return i;
}
}
return -1;
}
enum struct Troll {
int id;
int categoryID;
@ -112,9 +125,11 @@ enum struct Troll {
int AddFlag(const char[] name, bool defaultOn) {
if(this.flagNames == null) this.flagNames = new ArrayList(MAX_TROLL_FLAG_LENGTH);
// Check if flag already added
int flagIndex = this.GetFlagIndex(name);
if(flagIndex == -1) flagIndex = this.flagNames.PushString(name);
// Grab the prompt
static TrollFlagPrompt prompt;
this.flagPrompts.GetArray(g_trollAddPromptIndex, prompt);
@ -346,20 +361,33 @@ void ApplyTroll(int victim, const char[] name, int activator, trollModifier modi
ShowActivityEx(activator, "[FTT] ", "deactivated troll \"%s\" on %N. ", troll.name, victim);
LogAction(activator, victim, "\"%L\" deactivated troll \"%s\" on \"%L\"", activator, troll.name, victim);
} else {
static char flagName[MAX_TROLL_FLAG_LENGTH];
if(flags > 0 && flags & flags - 1 == 0) {
// Get the flag name if there is only one flag set
troll.GetFlagName(GetIndexFromPower(flags), flagName, sizeof(flagName));
}
if(modifier & TrollMod_Constant) {
if(flags > 0) {
ShowActivityEx(activator, "[FTT] ", "activated constant troll \"%s\" (%d) for %N. ", troll.name, flags, victim);
if(flagName[0] != '\0') {
ShowActivityEx(activator, "[FTT] ", "activated constant troll \"%s\" (%s) for %N. ", troll.name, flagName, victim);
} else {
ShowActivityEx(activator, "[FTT] ", "activated constant troll \"%s\" (%d) for %N. ", troll.name, flags, victim);
}
} else
ShowActivityEx(activator, "[FTT] ", "activated constant troll \"%s\" for %N. ", troll.name, victim);
} else if(flags > 0)
ShowActivityEx(activator, "[FTT] ", "activated troll \"%s\" (%d) for %N. ", troll.name, flags, victim);
else
} else if(flags > 0) {
if(flagName[0] != '\0') {
ShowActivityEx(activator, "[FTT] ", "activated troll \"%s\" (%s) for %N. ", troll.name, flagName, victim);
} else {
ShowActivityEx(activator, "[FTT] ", "activated troll \"%s\" (%d) for %N. ", troll.name, flags, victim);
}
} else
ShowActivityEx(activator, "[FTT] ", "activated troll \"%s\" for %N. ", troll.name, victim);
LogAction(activator, victim, "\"%L\" activated troll \"%s\" (%d) for \"%L\"", activator, troll.name, flags, victim);
}
} else {
ReplyToCommand(activator, "apply troll \"%s\" flags=%d on %N", troll.name, flags, victim);
ReplyToCommand(activator, "ftt: Applied Troll \"%s\" on %N with flags=%d", troll.name, victim, flags);
}
// Toggle on flags for client, if it's not a single run.
if(modifier & TrollMod_Constant) {

View file

@ -13,10 +13,6 @@ public Action Command_InstaSpecial(int client, int args) {
menu.ExitButton = true;
menu.Display(client, 0);
} else {
if(gInstaSpecialType > -1) {
ReplyToCommand(client, "Please wait for last Insta to spawn.");
return Plugin_Handled;
}
char arg1[32], arg2[32] = "jockey";
GetCmdArg(1, arg1, sizeof(arg1));
if(args >= 2) {
@ -49,7 +45,7 @@ public Action Command_InstaSpecial(int client, int args) {
for (int i = 0; i < target_count; i++) {
int target = target_list[i];
if(GetClientTeam(target) == 2) {
if(SpawnSpecialNear(target, specialType)) {
if(SpawnSpecialForTarget(specialType, target)) {
LogAction(client, target, "\"%L\" spawned Insta-%s™ nearby \"%L\"", client, arg2, target);
successes++;
} else {
@ -82,10 +78,6 @@ public Action Command_InstaSpecialFace(int client, int args) {
menu.ExitButton = true;
menu.Display(client, 0);
} else {
if(gInstaSpecialType > -1) {
ReplyToCommand(client, "Please wait for last Insta to spawn.");
return Plugin_Handled;
}
char arg1[32], arg2[32] = "jockey";
GetCmdArg(1, arg1, sizeof(arg1));
if(args >= 2) {
@ -118,7 +110,7 @@ public Action Command_InstaSpecialFace(int client, int args) {
for (int i = 0; i < target_count; i++) {
int target = target_list[i];
if(GetClientTeam(target) == 2) {
if(SpawnSpecialInFace(target, specialType)) {
if(SpawnSpecialForTarget(specialType, target, Special_OnTarget)) {
LogAction(client, target, "\"%L\" spawned Insta-%s™ at player \"%L\"", client, arg2, target);
successes++;
} else {

View file

@ -15,6 +15,9 @@ public void OnMapStart() {
PrecacheSound("player/footsteps/clown/concrete1.wav");
PrecacheSound("weapons/ceda_jar/ceda_jar_explode.wav");
PrecacheSound("weapons/molotov/molotov_detonate_1.wav");
g_spSpawnQueue.Clear();
spIsActive = false;
//CreateTimer(30.0, Timer_AutoPunishCheck, _, TIMER_REPEAT | TIMER_FLAG_NO_MAPCHANGE);
}
public void OnClientPutInServer(int client) {
@ -26,36 +29,47 @@ public void OnClientPutInServer(int client) {
}
public void Event_PlayerSpawn(Event event, const char[] name, bool dontBroadcast) {
int userid = event.GetInt("userid");
CreateTimer(0.1, Timer_CheckSpecial, userid);
if(spIsActive)
CreateTimer(0.1, Timer_CheckSpecial, userid);
}
public void Event_DoorToggle(Event event, const char[] name, bool dontBroadcast) {
int client = GetClientOfUserId(event.GetInt("userid"));
if(client && Trolls[slipperyShoesIndex].IsActive(client) && Trolls[slipperyShoesIndex].activeFlagClients[client] & 2) {
L4D_StaggerPlayer(client, client, NULL_VECTOR);
}
}
public void Event_SecondaryHealthUsed(Event event, const char[] name, bool dontBroadcast) {
int client = GetClientOfUserId(event.GetInt("userid"));
if(client && Trolls[slipperyShoesIndex].IsActive(client) && Trolls[slipperyShoesIndex].activeFlagClients[client] & 8) {
L4D_StaggerPlayer(client, client, NULL_VECTOR);
}
}
public Action Timer_CheckSpecial(Handle h, int specialID) {
int special = GetClientOfUserId(specialID);
// Check if new player is the spawned special:
if(special > 0 && gInstaSpecialType > -1 && IsFakeClient(special) && GetClientTeam(special) == 3) {
int type = GetEntProp(special, Prop_Send, "m_zombieClass");
// Verify type of special is spawned special
if(type == gInstaSpecialType) {
// Ignore 'ManualDirectorBot' or abm bots
if(spIsActive && special > 0 && IsFakeClient(special) && GetClientTeam(special) == 3) {
SpecialType type = view_as<SpecialType>(GetEntProp(special, Prop_Send, "m_zombieClass"));
if(type == spActiveRequest.type) {
// Ignore any fake clients with 'Bot' in name
static char buf[32];
GetClientName(special, buf, sizeof(buf));
if(StrContains(buf, "bot", false) == -1) {
gInstaSpecialType = -1;
// Set special to only attack them:
g_iAttackerTarget[special] = gInstaSpecialTarget;
// Incremenet count of specials targetting player:
gInstaSpecialMagnet[GetClientOfUserId(gInstaSpecialTarget)]++;
if(spActiveRequest.targetUserId)
g_iAttackerTarget[special] = spActiveRequest.targetUserId;
gInstaSpecialMagnet[GetClientOfUserId(spActiveRequest.targetUserId)]++;
TeleportEntity(special, gInstaSpecialSpawnPos, gInstaSpecialSpawnAng, NULL_VECTOR);
if(gInstaSpecialInstaKill) {
TeleportEntity(special, spActiveRequest.position, spActiveRequest.angle, NULL_VECTOR);
if(spActiveRequest.flags & view_as<int>(SPI_KillOnSpawn)) {
RequestFrame(Frame_Boom, special);
}
ProcessSpecialQueue();
}
}
}
}
}
public void Frame_Boom(int special) {
SDKHooks_TakeDamage(special, special, special, 1000.0);
gInstaSpecialInstaKill = false;
}
public void Event_PlayerFirstSpawn(Event event, const char[] name, bool dontBroadcast) {
int client = GetClientOfUserId(event.GetInt("userid"));
@ -136,6 +150,12 @@ public void Event_CarAlarm(Event event, const char[] name, bool dontBroadcast) {
//Ignore car alarms for autopunish
lastButtonUser = -1;
}
public void Event_EnteredSpit(Event event, const char[] name, bool dontBroadcast) {
int client = GetClientOfUserId(event.GetInt("userid"));
if(client) {
g_iInSpit[client] = true;
}
}
public Action RushPlayer(Handle h, int user) {
L4D2_RunScript("RushVictim(GetPlayerFromUserID(%d), %d)", user, 15000);
}
@ -407,6 +427,11 @@ static char AWP[16] = "sniper_awp";
public Action Event_ItemPickup(int client, int weapon) {
static int NoPickupIndex;
if(NoPickupIndex == 0) NoPickupIndex = GetTrollID("No Pickup");
static int SpicyGasIndex;
if(SpicyGasIndex == 0) SpicyGasIndex = GetTrollID("Spicy Gas");
static int UziRulesIndex;
if(UziRulesIndex == 0) UziRulesIndex = GetTrollID("UziRules / AwpSmells");
static char wpnName[64];
if(Trolls[NoPickupIndex].IsActive(client)) {
int flags = Trolls[NoPickupIndex].activeFlagClients[client];
@ -425,9 +450,10 @@ public Action Event_ItemPickup(int client, int weapon) {
} else if(flags & 16 && GetPlayerWeaponSlot(client, view_as<int>(L4DWeaponSlot_Pills)) == weapon) {
// No Pills / Adr
return Plugin_Handled;
} else if(flags & 32 && GetEntityClassname(weapon, wpnName, sizeof(wpnName)) && StrEqual(wpnName, "weapon_gascan")) {
return Plugin_Handled;
}
return Plugin_Continue;
} else {
} else if(Trolls[UziRulesIndex].IsActive(client) || IsTrollActive(client, "Primary Disable")) {
GetEdictClassname(weapon, wpnName, sizeof(wpnName));
if(strcmp(wpnName[7], "rifle") >= 0
|| strcmp(wpnName[7], "smg") >= 0
@ -435,8 +461,6 @@ public Action Event_ItemPickup(int client, int weapon) {
|| strcmp(wpnName[7], "sniper") > -1
|| StrContains(wpnName, "shotgun") > -1
) {
static int UziRulesIndex;
if(UziRulesIndex == 0) UziRulesIndex = GetTrollID("UziRules / AwpSmells");
//If 4: Only UZI, if 5: Can't switch.
if(Trolls[UziRulesIndex].IsActive(client)) {
static char comp[16];
@ -461,9 +485,22 @@ public Action Event_ItemPickup(int client, int weapon) {
return Plugin_Stop;
}
}
return Plugin_Continue;
} else if(Trolls[SpicyGasIndex].IsActive(client)) {
PrintToChat(client, "spice active");
if(GetEntityClassname(weapon, wpnName, sizeof(wpnName))) {
float max = 1.0;
if(Trolls[SpicyGasIndex].activeFlagClients[client] & 2) max = 0.5;
else if(Trolls[SpicyGasIndex].activeFlagClients[client] & 4) max = 0.1;
if(GetRandomFloat() <= max) {
if(StrEqual(wpnName, "weapon_gascan")) {
AcceptEntityInput(weapon, "Ignite", client, client);
} else if(StrEqual(wpnName, "weapon_propanetank") || StrEqual(wpnName, "weapon_oxygentank")) {
ExplodeProjectile(weapon);
}
}
}
}
return Plugin_Continue;
}
public Action OnPlayerRunCmd(int client, int& buttons, int& impulse, float vel[3], float angles[3], int& weapon, int& subtype, int& cmdnum, int& tickcount, int& seed, int mouse[2]) {
@ -529,17 +566,29 @@ public Action Event_TakeDamage(int victim, int& attacker, int& inflictor, float&
//Stop FF from marked:
static int reverseFF;
if(reverseFF == 0) reverseFF = GetTrollID("Reverse FF");
if(attacker > 0 && attacker <= MaxClients && GetClientTeam(attacker) == 4 && IsFakeClient(attacker)) return Plugin_Stop;
if(attacker > 0 && attacker <= MaxClients && GetClientTeam(attacker) == 4 && IsFakeClient(attacker)) return Plugin_Continue;
if (victim > 0 && victim <= MaxClients && damagetype != 263168 && damagetype != 265216) {
PrintToChat(victim, "you are in spit.");
}
if(attacker > 0 && victim <= MaxClients && attacker <= MaxClients && IsClientInGame(attacker) && IsPlayerAlive(attacker)) {
if(shootAtTarget[attacker] == victim) return Plugin_Continue;
if(g_PendingBanTroll[attacker] > 0 && GetClientTeam(attacker) == 2 && GetClientTeam(victim) == 2) {
return Plugin_Stop;
}
if(damage > 0.0 && victim != attacker && Trolls[slipperyShoesIndex].IsActive(victim) && Trolls[slipperyShoesIndex].activeFlagClients[victim] & 16) {
L4D_StaggerPlayer(victim, victim, NULL_VECTOR);
}
if(IsTrollActive(victim, "Damage Boost")) {
damage * 2;
return Plugin_Changed;
} else if(Trolls[reverseFF].IsActive(attacker) && damagetype != DMG_BURN && attacker != victim && GetClientTeam(attacker) == GetClientTeam(victim)) {
float returnDmg = damage; //default is 1:1
if(Trolls[reverseFF].activeFlagClients[attacker] & 4) {
returnDmg /= 2.0;
@ -668,9 +717,7 @@ void EntityCreateCallback(int entity) {
static char class[16];
static int badThrowID;
if(badThrowID == 0) {
badThrowID = GetTrollID("Bad Throw");
}
if(badThrowID == 0) badThrowID = GetTrollID("Bad Throw");
GetEntityClassname(entity, class, sizeof(class));
int entOwner = GetEntPropEnt(entity, Prop_Send, "m_hOwnerEntity");
@ -701,10 +748,16 @@ void EntityCreateCallback(int entity) {
AcceptEntityInput(entity, "Kill");
}
} else if(Trolls[badThrowID].IsFlagActive(entOwner, Flag_3) && StrContains(class, "pipe_bomb", true) > -1) {
if(hBadThrowHitSelf.FloatValue > 0.0 && GetRandomFloat() <= hBadThrowHitSelf.FloatValue)
if(hBadThrowHitSelf.FloatValue > 0.0 && GetRandomFloat() <= hBadThrowHitSelf.FloatValue) {
TeleportEntity(entity, pos, NULL_VECTOR, NULL_VECTOR);
ExplodeProjectile(entity);
}
SpawnItem("pipe_bomb", pos);
}
} else if(Trolls[slipperyShoesIndex].IsActive(entOwner)) {
if(Trolls[slipperyShoesIndex].activeFlagClients[entOwner] & 4) {
L4D_StaggerPlayer(entOwner, entOwner, NULL_VECTOR);
}
}
}
}
@ -738,13 +791,12 @@ public bool TraceEntityFilterPlayer(int entity, int mask, any data) {
float iLastAntiRushEvent[MAXPLAYERS+1];
public Action OnAntiRush(int client, int &type, float distance) {
PrintToConsoleAll("[FTT] Antirush: %N (dist=%d) (GameTime=%f)", client, distance, GetGameTime());
if(type == 3 && IsPlayerAlive(client) && !IsPlayerIncapped(client)) {
if(GetGameTime() - iLastAntiRushEvent[client] > 30.0) {
SpecialType special = view_as<SpecialType>(GetRandomInt(0,6));
SpecialType special = view_as<SpecialType>(GetRandomInt(1,6));
iLastAntiRushEvent[client] = GetGameTime();
SpawnSpecialNear(client, special);
PrintToConsoleAll("[FTT] Spawning anti-rush special on %N (dist=%f) (special=%d)", client, distance, special);
SpawnSpecialForTarget(special, client);
PrintToConsoleAll("[FTT] Spawning anti-rush special on %N (dist=%f) (special=%N)", client, distance, SPECIAL_NAMES[view_as<int>(special)-1]);
}
}
}

View file

@ -38,14 +38,14 @@ public int Insta_SpecialHandler(Menu menu, MenuAction action, int client, int pa
}
SpecialType special = view_as<SpecialType>(specialInt);
if(inFace) {
if(SpawnSpecialInFace(target, special)) {
if(SpawnSpecialForTarget(special, target, Special_OnTarget)) {
LogAction(client, target, "\"%L\" spawned Insta-%s™ on \"%L\"", client, SPECIAL_NAMES[specialInt-1], target);
ShowActivityEx(client, "[FTT] ", "spawned Insta-%s™ on %N", SPECIAL_NAMES[specialInt-1], target);
} else {
ReplyToCommand(client, "Could not spawn special.");
}
} else {
if(SpawnSpecialNear(target, special)) {
if(SpawnSpecialForTarget(special, target)) {
LogAction(client, target, "\"%L\" spawned Insta-%s™ near \"%L\"", client, SPECIAL_NAMES[specialInt-1], target);
ShowActivityEx(client, "[FTT] ", "spawned Insta-%s™ near %N", SPECIAL_NAMES[specialInt-1], target);
} else {
@ -488,7 +488,7 @@ void ShowThrowItAllMenu(int client, int userid) {
}
// Grab all the items the player has, add to menu
for(int slot = 0; slot <= 4; slot++) {
for(int slot = 0; slot <= 5; slot++) {
int item = GetPlayerWeaponSlot(victim, slot);
if(item > -1) {
GetEdictClassname(item, itmName, sizeof(itmName));

View file

@ -368,3 +368,11 @@ bool SetBotTarget(int bot, int target, int targetHP, int loops = 15) {
return false;
}
}
// Taken from https://forums.alliedmods.net/showthread.php?t=220132&page=2
stock void ExplodeProjectile(int entity) {
SetEntProp(entity, Prop_Data, "m_takedamage", 2);
SetEntProp(entity, Prop_Data, "m_iHealth", 1);
SDKHooks_TakeDamage(entity, 0, 0, 1.0);
SetEntProp(entity, Prop_Data, "m_nNextThinkTick", 1); //for smoke
}

View file

@ -1,6 +1,4 @@
char SPECIAL_NAMES[][] = {
"Smoker", "Boomer", "Hunter", "Spitter", "Jockey", "Charger", "Witch", "Tank"
};
enum SpecialType {
Special_Invalid = -1,
Special_Smoker = 1,
@ -13,6 +11,104 @@ enum SpecialType {
Special_Tank
}
enum struct SpecialSpawnRequest {
SpecialType type;
int targetUserId;
float position[3];
float angle[3];
int flags;
}
ArrayList g_spSpawnQueue;
// Finds position, sends to SpawnSpecialAtPosition
// target client index
stock bool SpawnSpecialForTarget(SpecialType specialType, int target, SpecialSpawnFlags spawnFlags = Special_Anywhere) {
static float pos[3];
int internalFlags = 0;
if(spawnFlags == Special_OnTarget) {
static float ang[3];
static float testPos[3];
testPos = pos;
GetClientAbsOrigin(target, pos);
GetClientEyeAngles(target, ang);
if(specialType == Special_Boomer)
internalFlags |= view_as<int>(SPI_KillOnSpawn);
if(specialType == Special_Jockey || specialType == Special_Boomer) { // Else spawn a little bit off, and above (above for jockeys)
pos[2] += 25.0;
pos[0] += 5.0;
} else { //If not jockey/hunter find a suitable area that is at least 5 m away
float minDistance = GetIdealMinDistance(specialType);
GetHorizontalPositionFromOrigin(pos, ang, minDistance, testPos);
if(!FindSuitablePosition(pos, testPos, minDistance, 100)) {
L4D_GetRandomPZSpawnPosition(target, view_as<int>(specialType), 10, testPos);
}
pos = testPos;
}
pos[2] += 1.0;
return SpawnSpecialAtPosition(specialType, pos, ang, target, internalFlags);
} else {
if(L4D_GetRandomPZSpawnPosition(target, view_as<int>(specialType), 10, pos)) {
return SpawnSpecialAtPosition(specialType, pos, NULL_VECTOR, target, internalFlags);
}
}
return false;
}
// Target is optional
stock bool SpawnSpecialAtPosition(SpecialType special, const float destination[3], const float angle[3], int target = 0, int flags) {
SpecialSpawnRequest request;
request.type = special;
if(target)
request.targetUserId = GetClientUserId(target);
request.flags = flags;
request.position = destination;
request.angle = angle;
g_spSpawnQueue.PushArray(request);
if(!spIsActive) ProcessSpecialQueue();
return true;
}
SpecialSpawnRequest spActiveRequest;
bool ProcessSpecialQueue() {
if(g_spSpawnQueue.Length > 0) {
if(g_spSpawnQueue.GetArray(0, spActiveRequest, sizeof(spActiveRequest))) {
spIsActive = true;
}
g_spSpawnQueue.Erase(0);
int target = GetClientOfUserId(spActiveRequest.targetUserId);
if(view_as<int>(spActiveRequest.type) <= 6) {
// CreateTimer(2.0, Timer_CheckSpecialSpawn, spCurrentId);
int bot = CreateFakeClient("FTTSpecialBot");
if (bot != 0) {
ChangeClientTeam(bot, 3);
CreateTimer(0.1, Timer_KickBot, bot);
}
CheatCommand(target, "z_spawn_old", SPECIAL_NAMES[view_as<int>(spActiveRequest.type) - 1], "auto");
} else if(spActiveRequest.type == Special_Witch) {
int witch = L4D2_SpawnWitch(spActiveRequest.position, spActiveRequest.angle);
if(witch != -1)
SetWitchTarget(witch, target);
return ProcessSpecialQueue();
} else if(spActiveRequest.type == Special_Tank) {
// BypassLimit();
int tank = L4D2_SpawnTank(spActiveRequest.position, spActiveRequest.angle);
if(tank > 0 && IsClientConnected(tank))
g_iAttackerTarget[tank] = spActiveRequest.targetUserId;
return ProcessSpecialQueue();
}
return true;
}
spIsActive = false;
return false;
}
stock SpecialType GetSpecialType(const char[] input) {
for(int i = 0; i < 8; i++) {
if(strcmp(SPECIAL_NAMES[i], input, false) == 0) return view_as<SpecialType>(i + 1);
@ -47,85 +143,6 @@ float GetIdealMinDistance(SpecialType specialType) {
}
}
bool SpawnSpecialInFace(int target, SpecialType specialType) {
static float pos[3], ang[3];
static float testPos[3];
testPos = pos;
GetClientAbsOrigin(target, pos);
GetClientEyeAngles(target, ang);
if(specialType == Special_Boomer)
gInstaSpecialInstaKill = true;
if(specialType == Special_Jockey || specialType == Special_Boomer) { // Else spawn a little bit off, and above (above for jockeys)
pos[2] += 25.0;
pos[0] += 5.0;
} else { //If not jockey/hunter find a suitable area that is at least 5 m away
float minDistance = GetIdealMinDistance(specialType);
GetHorizontalPositionFromOrigin(pos, ang, minDistance, testPos);
if(!FindSuitablePosition(pos, testPos, minDistance, 100)) {
L4D_GetRandomPZSpawnPosition(target, view_as<int>(specialType), 10, testPos);
}
pos = testPos;
}
pos[2] += 1.0;
// NegateVector(ang);
return SpawnSpecialInternal(specialType, target, pos, NULL_VECTOR) != -1;
}
bool SpawnSpecialNear(int target, SpecialType type) {
gInstaSpecialInstaKill = false;
static float pos[3];
if(L4D_GetRandomPZSpawnPosition(target, view_as<int>(type), 10, pos)) {
return SpawnSpecialInternal(type, target, pos, NULL_VECTOR) != -1;
}
return false;
}
// doesnt seem to work with l4dhooks methods
void BypassLimit() {
int bot = CreateFakeClient("Infected Bot");
if (bot != 0) {
ChangeClientTeam(bot, 3);
CreateTimer(0.1, Timer_KickBot, bot);
}
}
int SpawnSpecialInternal(SpecialType type, int target, float pos[3], float ang[3]) {
if(view_as<int>(type) <= 6) {
// Bypass limit:
gInstaSpecialType = view_as<int>(type);
gInstaSpecialTarget = GetClientUserId(target);
gInstaSpecialSpawnPos = pos;
gInstaSpecialSpawnAng = pos;
CreateTimer(2.0, Timer_InstaFailed);
int bot = CreateFakeClient("ManualDirectorBot");
if (bot != 0) {
ChangeClientTeam(bot, 3);
CreateTimer(0.1, Timer_KickBot, bot);
}
CheatCommand(target, "z_spawn_old", SPECIAL_NAMES[view_as<int>(type) - 1], "auto");
return 0;
}
else if(type == Special_Witch) {
int witch = L4D2_SpawnWitch(pos, ang);
if(witch != -1)
SetWitchTarget(witch, target);
return witch;
}
else if(type == Special_Tank) {
// BypassLimit();
int tank = L4D2_SpawnTank(pos, ang);
if(tank <= 0 || !IsClientConnected(tank)) return -1;
if(tank != -1)
g_iAttackerTarget[tank] = GetClientUserId(target);
return tank;
}
else return -1;
}
stock bool GetGroundBehind(int client, float vPos[3], float vAng[3]) {
GetClientEyePosition(client, vPos);
GetClientEyeAngles(client, vAng);

View file

@ -11,18 +11,25 @@ public Action Timer_ThrowTimer(Handle timer) {
}
public Action Timer_Main(Handle timer) {
static int loop;
static int loopTick;
static int slowDrainIndex;
if(!slowDrainIndex) slowDrainIndex = GetTrollID("Slow Drain");
static int tempHealthQuickDrainIndex;
if(!tempHealthQuickDrainIndex) tempHealthQuickDrainIndex = GetTrollID("Temp Health Quick Drain");
static int swarmIndex;
if(!swarmIndex) swarmIndex = GetTrollID("Swarm");
for(int i = 1; i <= MaxClients; i++) {
if(IsClientConnected(i) && IsClientInGame(i) && IsPlayerAlive(i)) {
if(IsTrollActive(i, "Slow Drain")) {
if(loop % 4 == 0) {
if(Trolls[slowDrainIndex].IsActive(i)) {
if(loopTick % 4 == 0) {
int hp = GetClientHealth(i);
if(hp > 50) {
SetEntProp(i, Prop_Send, "m_iHealth", hp - 1);
}
}
}else if(IsTrollActive(i, "Temp Health Quick Drain")) {
if(loop % 2 == 0) {
}else if(Trolls[tempHealthQuickDrainIndex].IsActive(i)) {
if(loopTick % 2 == 0) {
float bufferTime = GetEntPropFloat(i, Prop_Send, "m_healthBufferTime");
float buffer = GetEntPropFloat(i, Prop_Send, "m_healthBuffer");
float tempHealth = GetTempHealth(i);
@ -32,13 +39,17 @@ public Action Timer_Main(Handle timer) {
SetEntPropFloat(i, Prop_Send, "m_healthBufferTime", bufferTime - 7.0);
}
}
}else if(IsTrollActive(i, "Swarm")) {
}else if(Trolls[swarmIndex].IsActive(i)) {
L4D2_RunScript("RushVictim(GetPlayerFromUserID(%d), %d)", GetClientUserId(i), 15000);
} else if(Trolls[slipperyShoesIndex].IsActive(i) && Trolls[slipperyShoesIndex].activeFlagClients[i] & 1) {
if(GetRandomFloat() <= 0.4) {
L4D_StaggerPlayer(i, i, NULL_VECTOR);
}
}
}
}
if(++loop >= 60) {
loop = 0;
if(++loopTick >= 60) {
loopTick = 0;
}
return Plugin_Continue;
}
@ -102,13 +113,6 @@ public Action Timer_KickBot(Handle timer, int client) {
}
}
public Action Timer_InstaFailed(Handle h) {
if(gInstaSpecialType != -1) {
gInstaSpecialType = -1;
gInstaSpecialTarget = 0;
}
}
public Action Timer_ShootReverse(Handle h, DataPack pack) {
pack.Reset();
int attacker = pack.ReadCell();

View file

@ -1,5 +1,7 @@
// UP THE VALUE 'MAX_TROLLS' in base.inc before adding new ones!
int slipperyShoesIndex = 0;
void SetupTrolls() {
trollKV = new StringMap();
categories = new ArrayList(ByteCountToCells(16));
@ -7,6 +9,7 @@ void SetupTrolls() {
int index;
SetupTroll("Reset User", "Resets the user, removes all troll effects", TrollMod_Instant);
/// CATEGORY: Magnets
SetCategory("Magnets");
index = SetupTroll("Special Magnet", "Attracts ALL specials to any alive target with this troll enabled", TrollMod_Constant);
AddMagnetFlags(index);
@ -14,13 +17,23 @@ void SetupTrolls() {
AddMagnetFlags(index);
index = SetupTroll("Witch Magnet", "All witches when startled will target any player with this troll", TrollMod_Constant);
/// CATEGORY: Infected
SetCategory("Infected");
SetupTroll("Swarm", "Swarms a player with zombies. Requires swarm plugin", TrollMod_Instant | TrollMod_Constant);
SetupTroll("Vomit Player", "Shortcut to sm_vomitplayer. vomits the player.", TrollMod_Instant);
SetupTroll("Inface Special", "Shortcut to sm_inface", TrollMod_Instant);
SetupTroll("Insta Special", "Shortcut to sm_insta", TrollMod_Instant);
SetupTroll("Goo", "Spawns a spitter puddle underneath them", TrollMod_Instant);
index = SetupTroll("Amazon Special Combo", "Choose multiple different specials to spawn", TrollMod_Instant);
Trolls[index].AddFlagPrompt(true);
for(int i = 0; i < 8; i++) {
Trolls[index].AddFlag(SPECIAL_NAMES[i], false); // 1 << 7 max
}
Trolls[index].AddFlagPrompt(false);
Trolls[index].AddFlag("Spawn On Face", false); // 1 << 8
Trolls[index].AddFlag("Spawn Near", false); // 1 << 9
// CATEGORY: Items
SetCategory("Items");
index = SetupTroll("Throw It All", "Player throws their item(s) periodically to a nearby player", TrollMod_Instant);
//Can't add directly, is custom troll:
@ -32,6 +45,11 @@ void SetupTrolls() {
Trolls[index].AddFlag("Biles", true);
Trolls[index].AddFlag("Molotovs", true);
Trolls[index].AddFlag("Pipebombs", true);
index = SetupTroll("Spicy Gas", "Gascans player picks up just ignite. Magic.", TrollMod_Constant);
Trolls[index].AddFlagPrompt(false);
Trolls[index].AddFlag("Always (100%)", false);
Trolls[index].AddFlag("Half Time (50%)", true);
Trolls[index].AddFlag("Rare (10%)", false);
index = SetupTroll("No Pickup", "Prevents a player from picking up ANY (new) item. Use ThrowItAll to make them drop", TrollMod_Constant);
Trolls[index].AddFlagPrompt(true);
Trolls[index].AddFlag("No Primary", false);
@ -39,6 +57,7 @@ void SetupTrolls() {
Trolls[index].AddFlag("No Throwables", true);
Trolls[index].AddFlag("No Kits", true);
Trolls[index].AddFlag("No Pills / Adr", true);
Trolls[index].AddFlag("No GASCANS", true);
index = SetupTroll("UziRules / AwpSmells", "Picking up a weapon gives them a UZI or AWP instead", TrollMod_Constant);
Trolls[index].AddFlagPrompt(false);
Trolls[index].AddFlag("UZI Only", true);
@ -51,6 +70,7 @@ void SetupTrolls() {
Trolls[index].AddFlag("Rare (10%)", false);
SetupTroll("Half Primary Ammo", "Cuts their primary reserve ammo in half", TrollMod_Instant);
/// CATEGORY: Chat
SetCategory("Chat");
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);
@ -75,6 +95,7 @@ void SetupTrolls() {
SetupTroll("Reversed", "Reserves their message", TrollMod_Constant);
SetupTroll("Voice Mute", "Mutes from voice", TrollMod_Constant);
/// CATEGORY: Health
SetCategory("Health");
SetupTroll("Damage Boost", "Makes a player take more damage than normal", TrollMod_Constant);
SetupTroll("Temp Health Quick Drain", "Makes a player's temporarily health drain very quickly", TrollMod_Constant);
@ -87,15 +108,28 @@ void SetupTrolls() {
Trolls[index].AddFlag("0.5x Ratio", false); //4
Trolls[index].AddFlag("0.0x Ratio (None)", false); //8
Trolls[index].AddFlag("3x Ratio", false); //16
Trolls[index].AddFlag("-2x Ratio", false); //16
SetCategory("Misc");
SetupTroll("Gun Jam", "On reload, small chance their gun gets jammed - Can't reload.", TrollMod_Constant);
/// CATEGORY: Movement
SetCategory("Movement");
SetupTroll("Slow Speed", "Sets player speed to 0.8x of normal speed", TrollMod_Constant);
SetupTroll("Higher Gravity", "Sets player gravity to 1.3x of normal gravity", TrollMod_Constant);
SetupTroll("No Shove", "Prevents a player from shoving", TrollMod_Constant);
SetupTroll("CameTooEarly", "When they shoot, random chance they empty whole clip", TrollMod_Constant);
SetupTroll("Inverted Controls", "Well, aint it obvious", TrollMod_Constant);
SetupTroll("Stagger", "Like a slap, but different", TrollMod_Instant);
index = SetupTroll("Slippery Shoes", "Periodically stumbles around.", TrollMod_Constant);
Trolls[index].AddFlagPrompt(true);
Trolls[index].AddFlag("Periodically", true);
Trolls[index].AddFlag("When using doors", false);
Trolls[index].AddFlag("On throwable use", false);
Trolls[index].AddFlag("On pills/adrenaline use", false);
Trolls[index].AddFlag("On zombie bite", false);
slipperyShoesIndex = index;
/// CATEGORY: MISC
SetCategory("Misc");
SetupTroll("Gun Jam", "On reload, small chance their gun gets jammed - Can't reload.", TrollMod_Constant);
SetupTroll("No Shove", "Prevents a player from shoving", TrollMod_Constant);
SetupTroll("CameTooEarly", "When they shoot, random chance they empty whole clip", TrollMod_Constant);
index = SetupTroll("Meta: Inverse", "Uhm you are not supposed to see this...", TrollMod_Instant);
Trolls[index].hidden = true;
Trolls[index].AddFlagPrompt(false);
@ -142,7 +176,6 @@ bool ApplyAffect(int victim, const Troll troll, int activator, trollModifier mod
} else if(StrEqual(troll.name, "UziRules / AwpSmells")) {
DisableTroll(victim, "No Pickup");
DisableTroll(victim, "Primary Disable");
SDKHook(victim, SDKHook_WeaponCanUse, Event_ItemPickup);
} else if(StrEqual(troll.name, "Primary Disable")) {
DisableTroll(victim, "UziRules / AwpSmells");
DisableTroll(victim, "No Pickup");
@ -201,6 +234,17 @@ bool ApplyAffect(int victim, const Troll troll, int activator, trollModifier mod
L4D_StaggerPlayer(victim, victim, NULL_VECTOR);
} else if(StrEqual(troll.name, "Voice Mute")) {
BaseComm_SetClientMute(victim, !isActive);
} else if(StrEqual(troll.name, "Spicy Gas")) {
SDKHook(victim, SDKHook_WeaponCanUse, Event_ItemPickup);
} else if(StrEqual(troll.name, "Amazon Special Combo")) {
SpecialSpawnFlags spawnFlag = Special_Anywhere;
if(flags & (1 << 8)) spawnFlag = Special_OnTarget;
PrintToChatAll("spawnFlag: %d", spawnFlag);
for(int i = 1; i < 7; i++) {
if(flags & (1 << i)) {
SpawnSpecialForTarget(view_as<SpecialType>(i), victim, spawnFlag);
}
}
} else if(modifier != TrollMod_Constant) {
PrintToServer("[FTT] Warn: Possibly invalid troll, no apply action defined for \"%s\"", troll.name);
#if defined DEBUG

View file

@ -37,12 +37,9 @@ int lastButtonUser;
int lastCrescendoUser;
int g_iAttackerTarget[MAXPLAYERS+1];
int g_PendingBanTroll[MAXPLAYERS+1];
int g_iInSpit[MAXPLAYERS+1];
Timer g_inSpitTimer;
int gInstaSpecialType = -1;
int gInstaSpecialTarget;
float gInstaSpecialSpawnPos[3];
float gInstaSpecialSpawnAng[3];
bool gInstaSpecialInstaKill;
int gInstaSpecialMagnet[MAXPLAYERS+1];
char steamids[MAXPLAYERS+1][64];
@ -51,6 +48,17 @@ int shootAtTarget[MAXPLAYERS+1];
int shootAtTargetLoops[MAXPLAYERS+1];
int shootAtTargetHP[MAXPLAYERS+1];
bool spIsActive;
enum SpecialSpawnFlags {
Special_Anywhere = 0,
Special_OnTarget = 1
}
enum SpecialInternalFlags {
SPI_KillOnSpawn = 1
}
#include <feedthetrolls/base>
#include <feedthetrolls/trolls>
#include <feedthetrolls/combos>

View file

@ -20,6 +20,17 @@
#include <l4d_anti_rush>
/*
TODO IDEAS:
1. [x] Instant pipebomb explosion
2. [x] Spicy gas (instant ignite), ignite propane/gas canisters
3. [x] Amazon Special Combo (flags, choose combinations) (NEED QUEUE)
4. Random weapons (on interval, possible second option of random capacity)
5. [x] Slippery shoes (periodic stagger, or on certain events)
6. (on hold) Sticky goo (slow user down in goo, or freezes)
7. Follow goo
*/
public Plugin myinfo =
{
name = "L4D2 Feed The Trolls",
@ -47,6 +58,8 @@ public void OnPluginStart() {
SetupTrolls();
SetupsTrollCombos();
g_spSpawnQueue = new ArrayList(sizeof(SpecialSpawnRequest));
// Witch target overwrite stuff:
GameData data = new GameData("l4d2_behavior");
@ -96,14 +109,19 @@ public void OnPluginStart() {
HookEvent("player_death", Event_PlayerDeath);
HookEvent("triggered_car_alarm", Event_CarAlarm);
HookEvent("witch_harasser_set", Event_WitchVictimSet);
HookEvent("door_open", Event_DoorToggle);
HookEvent("door_close", Event_DoorToggle);
HookEvent("adrenaline_used", Event_SecondaryHealthUsed);
HookEvent("pills_used", Event_SecondaryHealthUsed);
AddNormalSoundHook(view_as<NormalSHook>(SoundHook));
AutoExecConfig(true, "l4d2_feedthetrolls");
for(int i = 1; i <= MaxClients; i++) {
if(IsClientConnected(i) && IsClientInGame(i))
if(IsClientConnected(i) && IsClientInGame(i)) {
SDKHook(i, SDKHook_OnTakeDamage, Event_TakeDamage);
}
}
}
///////////////////////////////////////////////////////////////////////////////