L4D2Tools: Add lots of logic I dont remember what it does or if it even works my brain hurts

Also added support for my custom l4d_survivor_identity_fix to update
This commit is contained in:
Jackzie 2021-01-09 13:47:21 -06:00
parent 2594d9cf5b
commit f664185721
No known key found for this signature in database
GPG key ID: 1E834FE36520537A
4 changed files with 348 additions and 25 deletions

Binary file not shown.

Binary file not shown.

View file

@ -11,12 +11,14 @@
#include <left4dhooks> #include <left4dhooks>
#include "jutils.inc" #include "jutils.inc"
static bool bLasersUsed[2048]; static bool bLasersUsed[2048], waitingForPlayers;
static ConVar hLaserNotice, hFinaleTimer, hFFNotice, hMPGamemode; static ConVar hLaserNotice, hFinaleTimer, hFFNotice, hMPGamemode;
static int iFinaleStartTime, botDropMeleeWeapon[MAXPLAYERS+1]; static int iFinaleStartTime, botDropMeleeWeapon[MAXPLAYERS+1], extraKitsAmount;
static Handle waitTimer = INVALID_HANDLE;
static float OUT_OF_BOUNDS[3] = {0.0, -1000.0, 0.0}; static float OUT_OF_BOUNDS[3] = {0.0, -1000.0, 0.0};
static int extraKitsAmount = 0;
native int IdentityFix_SetPlayerModel(int client, int args);
//TODO: Remove the Plugin_Stop on pickup, and give item back instead. keep reference to dropped weapon to delete. //TODO: Remove the Plugin_Stop on pickup, and give item back instead. keep reference to dropped weapon to delete.
public Plugin myinfo = { public Plugin myinfo = {
@ -27,6 +29,12 @@ public Plugin myinfo = {
url = "" url = ""
}; };
public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max)
{
MarkNativeAsOptional("IdentityFix_SetPlayerModel");
return APLRes_Success;
}
//TODO: Implement automatic extra kits //TODO: Implement automatic extra kits
public void OnPluginStart() { public void OnPluginStart() {
EngineVersion g_Game = GetEngineVersion(); EngineVersion g_Game = GetEngineVersion();
@ -38,7 +46,7 @@ public void OnPluginStart() {
hLaserNotice = CreateConVar("sm_laser_use_notice", "1.0", "Enable notification of a laser box being used", FCVAR_NONE, true, 0.0, true, 1.0); hLaserNotice = CreateConVar("sm_laser_use_notice", "1.0", "Enable notification of a laser box being used", FCVAR_NONE, true, 0.0, true, 1.0);
hFinaleTimer = CreateConVar("sm_time_finale", "0.0", "Record the time it takes to complete finale. 0 -> OFF, 1 -> Gauntlets Only, 2 -> All finales", FCVAR_NONE, true, 0.0, true, 2.0); hFinaleTimer = CreateConVar("sm_time_finale", "0.0", "Record the time it takes to complete finale. 0 -> OFF, 1 -> Gauntlets Only, 2 -> All finales", FCVAR_NONE, true, 0.0, true, 2.0);
hFFNotice = CreateConVar("sm_ff_notice", "0.0", "Notify players if a FF occurs. 0 -> Disabled, 1 -> In chat, 2 -> In Hint text", FCVAR_NONE, true, 0.0, true, 2.0); hFFNotice = CreateConVar("sm_ff_notice", "0.0", "Notify players if a FF occurs. 0 -> Disabled, 1 -> In chat, 2 -> In Hint text", FCVAR_NONE, true, 0.0, true, 2.0);
hMPGamemode = FindConVar("mp_gamemode"); hMPGamemode = FindConVar("mp_gamemode");
HookEvent("player_use", Event_PlayerUse); HookEvent("player_use", Event_PlayerUse);
HookEvent("player_hurt", Event_PlayerHurt); HookEvent("player_hurt", Event_PlayerHurt);
@ -50,38 +58,91 @@ public void OnPluginStart() {
HookEvent("player_bot_replace", Event_BotPlayerSwap); HookEvent("player_bot_replace", Event_BotPlayerSwap);
HookEvent("bot_player_replace", Event_BotPlayerSwap); HookEvent("bot_player_replace", Event_BotPlayerSwap);
HookEvent("map_transition", Event_MapTransition); HookEvent("map_transition", Event_MapTransition);
HookEvent("player_spawn", Event_PlayerSpawn);
AutoExecConfig(true, "l4d2_tools"); AutoExecConfig(true, "l4d2_tools");
for(int client = 1; client < MaxClients; client++) { for(int client = 1; client < MaxClients; client++) {
if(IsClientConnected(client) && IsClientInGame(client) && GetClientTeam(client) == 2) { if(IsClientConnected(client) && IsClientInGame(client) && GetClientTeam(client) == 2 && IsFakeClient(client)) {
if(IsFakeClient(client)) SDKHook(client, SDKHook_WeaponDrop, Event_OnWeaponDrop);
SDKHook(client, SDKHook_WeaponDrop, Event_OnWeaponDrop);
} }
} }
RegAdminCmd("sm_model", Command_SetClientModel, ADMFLAG_ROOT); RegAdminCmd("sm_model", Command_SetClientModel, ADMFLAG_ROOT);
} }
//TODO: Give kits on fresh start as well, need to set extraKitsAmount //TODO: Give kits on fresh start as well, need to set extraKitsAmount
public void OnMapStart() { public Action Event_PlayerSpawn(Event event, const char[] name, bool dontBroadcast) {
if(L4D_IsFirstMapInScenario()) { int client = GetClientOfUserId(event.GetInt("userid"));
extraKitsAmount = GetClientCount(true) - 4;
if(extraKitsAmount < 0) extraKitsAmount = 0;
PrintToServer("New map has started");
}
if(extraKitsAmount > 0) { if(extraKitsAmount > 0) {
for(int i = 1; i < MaxClients + 1; i++) { char wpn[32];
if(IsClientConnected(i) && IsClientInGame(i) && IsPlayerAlive(i) && GetClientTeam(i) == 2) { if(GetClientWeaponName(client, 3, wpn, sizeof(wpn))) {
PrintToServer("Found a client to spawn %d extra kits: %N", extraKitsAmount, i); if(!StrEqual(wpn, "weapon_first_aid_kit")) {
while(extraKitsAmount > 0) { CheatCommand(client, "give", "first_aid_kit", "");
CheatCommand(i, "give", "first_aid_kit", ""); extraKitsAmount--;
extraKitsAmount--;
}
break;
} }
} }
} }
} }
public void OnMapStart() {
if(L4D_IsFirstMapInScenario()) {
extraKitsAmount = GetSurvivorCount() - 4;
if(extraKitsAmount < 0) extraKitsAmount = 0;
waitingForPlayers = true;
PrintToServer("New map has started");
}
if(extraKitsAmount > 0 && !waitingForPlayers) {
int lastClient;
for(int i = 1; i < MaxClients + 1; i++) {
if(IsClientConnected(i) && IsClientInGame(i) && IsPlayerAlive(i) && GetClientTeam(i) == 2) {
PrintToServer("Found a client to spawn %d extra kits: %N", extraKitsAmount, i);
char wpn[32];
if(GetClientWeaponName(i, 3, wpn, sizeof(wpn))) {
if(!StrEqual(wpn, "weapon_first_aid_kit")) {
lastClient = GetClientOfUserId(i);
CreateTimer(5.0, Timer_SpawnKits, lastClient);
extraKitsAmount--;
}
}
}
}
if(extraKitsAmount > 0) {
CreateTimer(0.1, Timer_SpawnKits, lastClient);
}
}
int survivorCount = GetSurvivorCount();
if(survivorCount > 4)
CreateTimer(60.0, Timer_AddExtraCounts, survivorCount);
}
public Action Timer_AddExtraCounts(Handle hd, int players) {
float percentage = 0.042 * players;
PrintToServer("Populating extra items based on player count (%d)", players);
char classname[32];
for(int i = MaxClients + 1; i < 2048; i++) {
if(IsValidEntity(i)) {
GetEntityClassname(i, classname, sizeof(classname));
if(StrContains(classname, "_spawn", true) > -1 && !StrEqual(classname, "info_zombie_spawn", true)) {
int count = GetEntProp(i, Prop_Data, "m_itemCount");
if(GetRandomFloat() < percentage) {
PrintToServer("Debug: Incrementing spawn count for %s from %d", classname, count);
SetEntProp(i, Prop_Data, "m_itemCount", ++count);
}
PrintToServer("%s %d", classname, count);
}
}
}
}
public Action Timer_SpawnKits(Handle timer, int user) {
//After kits given, re-set number to same incase a round restarts.
int prevAmount = extraKitsAmount;
int client = GetClientOfUserId(user);
while(extraKitsAmount > 0) {
CheatCommand(client, "give", "first_aid_kit", "");
extraKitsAmount--;
}
extraKitsAmount = prevAmount;
return Plugin_Handled;
}
public Action Command_SetClientModel(int client, int args) { public Action Command_SetClientModel(int client, int args) {
if(args < 1) { if(args < 1) {
@ -116,6 +177,7 @@ public Action Command_SetClientModel(int client, int args) {
ReplyToTargetError(client, target_count); ReplyToTargetError(client, target_count);
return Plugin_Handled; return Plugin_Handled;
} }
bool identityFixAvailable = GetFeatureStatus(FeatureType_Native, "IdentityFix_SetPlayerModel") == FeatureStatus_Available;
for (int i = 0; i < target_count; i++) { for (int i = 0; i < target_count; i++) {
if(IsClientConnected(target_list[i]) && IsClientInGame(target_list[i]) && IsPlayerAlive(target_list[i]) && GetClientTeam(target_list[i]) == 2) { if(IsClientConnected(target_list[i]) && IsClientInGame(target_list[i]) && IsPlayerAlive(target_list[i]) && GetClientTeam(target_list[i]) == 2) {
SetEntProp(target_list[i], Prop_Send, "m_survivorCharacter", modelID); SetEntProp(target_list[i], Prop_Send, "m_survivorCharacter", modelID);
@ -125,8 +187,10 @@ public Action Command_SetClientModel(int client, int args) {
GetSurvivorName(target_list[i], name, sizeof(name)); GetSurvivorName(target_list[i], name, sizeof(name));
SetClientInfo(target_list[i], "name", name); SetClientInfo(target_list[i], "name", name);
} }
if(identityFixAvailable)
IdentityFix_SetPlayerModel(target_list[i], modelID);
int primaryWeapon = GetPlayerWeaponSlot(client, 0); int primaryWeapon = GetPlayerWeaponSlot(target_list[i], 0);
if(primaryWeapon > -1) { if(primaryWeapon > -1) {
SDKHooks_DropWeapon(target_list[i], primaryWeapon, NULL_VECTOR, NULL_VECTOR); SDKHooks_DropWeapon(target_list[i], primaryWeapon, NULL_VECTOR, NULL_VECTOR);
@ -162,12 +226,27 @@ public Action Event_BotPlayerSwap(Event event, const char[] name, bool dontBroad
EquipPlayerWeapon(client, botDropMeleeWeapon[bot]); EquipPlayerWeapon(client, botDropMeleeWeapon[bot]);
botDropMeleeWeapon[bot] = -1; botDropMeleeWeapon[bot] = -1;
}else{ }else{
PrintToConsole(client, "Could not give back your melee weapon, %N has it instead.", meleeOwnerEnt); PrintToChat(client, "Could not give back your melee weapon, %N has it instead.", meleeOwnerEnt);
} }
} }
SDKUnhook(bot, SDKHook_WeaponDrop, Event_OnWeaponDrop); SDKUnhook(bot, SDKHook_WeaponDrop, Event_OnWeaponDrop);
} }
} }
public bool OnClientConnect(int client) {
if(waitingForPlayers) {
if(waitTimer != INVALID_HANDLE) {
CloseHandle(waitTimer);
}
waitTimer = CreateTimer(2.0, Timer_Wait, client);
}
return true;
}
public Action Timer_Wait(Handle hdl, int client) {
waitingForPlayers = false;
extraKitsAmount = GetSurvivorCount();
CreateTimer(5.0, Timer_SpawnKits, GetClientOfUserId(client));
PrintToServer("Debug: No more players joining in 2.0s, spawning kits.");
}
//TODO: Might have to actually check for the bot they control, or possibly the bot will call this itself. //TODO: Might have to actually check for the bot they control, or possibly the bot will call this itself.
public void OnClientDisconnect(int client) { public void OnClientDisconnect(int client) {
if(botDropMeleeWeapon[client] > -1) { if(botDropMeleeWeapon[client] > -1) {
@ -198,7 +277,8 @@ public void Frame_HideEntity(int entity) {
public void Event_EnterSaferoom(Event event, const char[] name, bool dontBroadcast) { public void Event_EnterSaferoom(Event event, const char[] name, bool dontBroadcast) {
int user = GetClientOfUserId(event.GetInt("userid")); int user = GetClientOfUserId(event.GetInt("userid"));
if(user == 0) return; if(user == 0) return;
if(botDropMeleeWeapon[user] > -1) { if(botDropMeleeWeapon[user] > 0) {
PrintToServer("Giving melee weapon back to %N", user);
float pos[3]; float pos[3];
GetClientAbsOrigin(user, pos); GetClientAbsOrigin(user, pos);
TeleportEntity(botDropMeleeWeapon[user], pos, NULL_VECTOR, NULL_VECTOR); TeleportEntity(botDropMeleeWeapon[user], pos, NULL_VECTOR, NULL_VECTOR);
@ -295,7 +375,7 @@ public void Event_CarAlarmTriggered(Event event, const char[] name, bool dontBro
PrintToChatAll("%N activated a car alarm!", userID); PrintToChatAll("%N activated a car alarm!", userID);
} }
public Action Event_MapTransition(Event event, const char[] name, bool dontBroadcast) { public Action Event_MapTransition(Event event, const char[] name, bool dontBroadcast) {
extraKitsAmount = GetClientCount(true) - 4; extraKitsAmount = GetSurvivorCount() - 4;
if(extraKitsAmount < 0) extraKitsAmount = 0; if(extraKitsAmount < 0) extraKitsAmount = 0;
PrintToServer("Will spawn an extra %d kits", extraKitsAmount); PrintToServer("Will spawn an extra %d kits", extraKitsAmount);
} }
@ -328,4 +408,14 @@ stock int GetAnyValidClient() {
} }
} }
return -1; return -1;
}
stock int GetSurvivorCount() {
int count = 0;
for(int i = 1; i < MaxClients + 1; i++) {
if(IsClientConnected(i) && IsClientInGame(i) && GetClientTeam(i) == 2) {
count++;
}
}
return count;
} }

View file

@ -0,0 +1,233 @@
#pragma semicolon 1
#pragma newdecls required
#define PLUGIN_NAME "[L4D1/2] Survivor Identity Fix for 5+ Survivors"
#define PLUGIN_AUTHOR "Merudo, Shadowysn"
#define PLUGIN_DESC "Fix bug where a survivor will change identity when a player connects/disconnects if there are 5+ survivors"
#define PLUGIN_VERSION "1.6"
#define PLUGIN_URL "https://forums.alliedmods.net/showthread.php?p=2403731#post2403731"
#define PLUGIN_NAME_SHORT "5+ Survivor Identity Fix"
#define PLUGIN_NAME_TECH "survivor_identity_fix"
#include <sourcemod>
#include <sdktools>
#include <dhooks>
#define TEAM_SURVIVOR 2
#define TEAM_PASSING 4
char g_Models[MAXPLAYERS+1][128];
#define GAMEDATA "l4d_survivor_identity_fix"
Handle hConf = null;
#define NAME_SetModel "CBasePlayer::SetModel"
static Handle hDHookSetModel = null;
#define SIG_SetModel_LINUX "@_ZN11CBasePlayer8SetModelEPKc"
#define SIG_SetModel_WINDOWS "\\x55\\x8B\\x2A\\x8B\\x2A\\x2A\\x56\\x57\\x50\\x8B\\x2A\\xE8\\x2A\\x2A\\x2A\\x2A\\x8B\\x2A\\x2A\\x2A\\x2A\\x2A\\x8B\\x2A\\x8B\\x2A\\x2A\\x8B"
#define SIG_L4D1SetModel_WINDOWS "\\x8B\\x2A\\x2A\\x2A\\x56\\x57\\x50\\x8B\\x2A\\xE8\\x2A\\x2A\\x2A\\x2A\\x8B\\x3D"
public Plugin myinfo =
{
name = PLUGIN_NAME,
author = PLUGIN_AUTHOR,
description = PLUGIN_DESC,
version = PLUGIN_VERSION,
url = PLUGIN_URL
}
public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) {
CreateNative("IdentityFix_SetPlayerModel", Native_SetPlayerModel);
return APLRes_Success;
}
public void OnPluginStart()
{
GetGamedata();
CreateConVar("l4d_survivor_identity_fix_version", PLUGIN_VERSION, "Survivor Change Fix Version", FCVAR_SPONLY|FCVAR_NOTIFY|FCVAR_DONTRECORD);
HookEvent("player_bot_replace", Event_PlayerToBot, EventHookMode_Post);
HookEvent("bot_player_replace", Event_BotToPlayer, EventHookMode_Post);
}
// ------------------------------------------------------------------------
// Stores the client of each survivor each time it is changed
// Needed because when Event_PlayerToBot fires, it's hunter model instead
// ------------------------------------------------------------------------
public MRESReturn SetModel_Pre(int client, Handle hParams)
{ } // We need this pre hook even though it's empty, or else the post hook will crash the game.
public MRESReturn SetModel(int client, Handle hParams)
{
if (!IsValidClient(client)) return;
if (!IsSurvivor(client))
{
g_Models[client][0] = '\0';
return;
}
char model[128];
DHookGetParamString(hParams, 1, model, sizeof(model));
if (StrContains(model, "models/infected", false) < 0)
{
strcopy(g_Models[client], 128, model);
}
}
// ------------------------------------------------------------------------
// Models & survivor names so bots can be renamed
// ------------------------------------------------------------------------
char survivor_names[8][] = { "Nick", "Rochelle", "Coach", "Ellis", "Bill", "Zoey", "Francis", "Louis"};
char survivor_models[8][] =
{
"models/survivors/survivor_gambler.mdl",
"models/survivors/survivor_producer.mdl",
"models/survivors/survivor_coach.mdl",
"models/survivors/survivor_mechanic.mdl",
"models/survivors/survivor_namvet.mdl",
"models/survivors/survivor_teenangst.mdl",
"models/survivors/survivor_biker.mdl",
"models/survivors/survivor_manager.mdl"
};
// --------------------------------------
// Bot replaced by player
// --------------------------------------
public Action Event_BotToPlayer(Handle event, const char[] name, bool dontBroadcast)
{
int player = GetClientOfUserId(GetEventInt(event, "player"));
int bot = GetClientOfUserId(GetEventInt(event, "bot"));
if (!IsValidClient(player) || !IsSurvivor(player) || IsFakeClient(player)) return; // ignore fake players (side product of creating bots)
char model[128];
GetClientModel(bot, model, sizeof(model));
SetEntityModel(player, model);
SetEntProp(player, Prop_Send, "m_survivorCharacter", GetEntProp(bot, Prop_Send, "m_survivorCharacter"));
}
// --------------------------------------
// Player -> Bot
// --------------------------------------
public Action Event_PlayerToBot(Handle event, char[] name, bool dontBroadcast)
{
int player = GetClientOfUserId(GetEventInt(event, "player"));
int bot = GetClientOfUserId(GetEventInt(event, "bot"));
if (!IsValidClient(player) || !IsSurvivor(player) || IsFakeClient(player)) return; // ignore fake players (side product of creating bots)
if (g_Models[player][0] != '\0')
{
SetEntProp(bot, Prop_Send, "m_survivorCharacter", GetEntProp(player, Prop_Send, "m_survivorCharacter"));
SetEntityModel(bot, g_Models[player]); // Restore saved model. Player model is hunter at this point
for (int i = 0; i < 8; i++)
{
if (StrEqual(g_Models[player], survivor_models[i])) SetClientInfo(bot, "name", survivor_names[i]);
}
}
}
void GetGamedata()
{
char filePath[PLATFORM_MAX_PATH];
BuildPath(Path_SM, filePath, sizeof(filePath), "gamedata/%s.txt", GAMEDATA);
if( FileExists(filePath) )
{
hConf = LoadGameConfigFile(GAMEDATA); // For some reason this doesn't return null even for invalid files, so check they exist first.
}
else
{
PrintToServer("[SM] %s plugin unable to get %i.txt gamedata file. Generating...", PLUGIN_NAME_SHORT, GAMEDATA);
Handle fileHandle = OpenFile(filePath, "a+");
if (fileHandle == null)
{ SetFailState("[SM] Couldn't generate gamedata file!"); }
WriteFileLine(fileHandle, "\"Games\"");
WriteFileLine(fileHandle, "{");
WriteFileLine(fileHandle, " \"left4dead\"");
WriteFileLine(fileHandle, " {");
WriteFileLine(fileHandle, " \"Signatures\"");
WriteFileLine(fileHandle, " {");
WriteFileLine(fileHandle, " \"%s\"", NAME_SetModel);
WriteFileLine(fileHandle, " {");
WriteFileLine(fileHandle, " \"library\" \"server\"");
WriteFileLine(fileHandle, " \"linux\" \"%s\"", SIG_SetModel_LINUX);
WriteFileLine(fileHandle, " \"windows\" \"%s\"", SIG_L4D1SetModel_WINDOWS);
WriteFileLine(fileHandle, " \"mac\" \"%s\"", SIG_SetModel_LINUX);
WriteFileLine(fileHandle, " }");
WriteFileLine(fileHandle, " }");
WriteFileLine(fileHandle, " }");
WriteFileLine(fileHandle, " \"left4dead2\"");
WriteFileLine(fileHandle, " {");
WriteFileLine(fileHandle, " \"Signatures\"");
WriteFileLine(fileHandle, " {");
WriteFileLine(fileHandle, " \"%s\"", NAME_SetModel);
WriteFileLine(fileHandle, " {");
WriteFileLine(fileHandle, " \"library\" \"server\"");
WriteFileLine(fileHandle, " \"linux\" \"%s\"", SIG_SetModel_LINUX);
WriteFileLine(fileHandle, " \"windows\" \"%s\"", SIG_SetModel_WINDOWS);
WriteFileLine(fileHandle, " \"mac\" \"%s\"", SIG_SetModel_LINUX);
WriteFileLine(fileHandle, " }");
WriteFileLine(fileHandle, " }");
WriteFileLine(fileHandle, " }");
WriteFileLine(fileHandle, "}");
CloseHandle(fileHandle);
hConf = LoadGameConfigFile(GAMEDATA);
if (hConf == null)
{ SetFailState("[SM] Failed to load auto-generated gamedata file!"); }
PrintToServer("[SM] %s successfully generated %s.txt gamedata file!", PLUGIN_NAME_SHORT, GAMEDATA);
}
PrepDHooks();
}
void PrepDHooks()
{
if (hConf == null)
{
SetFailState("Error: Gamedata not found");
}
hDHookSetModel = DHookCreateDetour(Address_Null, CallConv_THISCALL, ReturnType_Void, ThisPointer_CBaseEntity);
DHookSetFromConf(hDHookSetModel, hConf, SDKConf_Signature, NAME_SetModel);
DHookAddParam(hDHookSetModel, HookParamType_CharPtr);
DHookEnableDetour(hDHookSetModel, false, SetModel_Pre);
DHookEnableDetour(hDHookSetModel, true, SetModel);
}
bool IsValidClient(int client, bool replaycheck = true)
{
if (client <= 0 || client > MaxClients) return false;
if (!IsClientInGame(client)) return false;
if (replaycheck)
{
if (IsClientSourceTV(client) || IsClientReplay(client)) return false;
}
return true;
}
bool IsSurvivor(int client)
{
if (GetClientTeam(client) != TEAM_SURVIVOR && GetClientTeam(client) != TEAM_PASSING) return false;
return true;
}
public int Native_SetPlayerModel(Handle plugin, int numParams) {
int client = GetNativeCell(1);
int character = GetNativeCell(2);
if(numParams != 2) {
ThrowNativeError(SP_ERROR_NATIVE, "Incorrect amount of parameters passed");
}else if(client < 1 || client > MaxClients || !IsClientInGame(client)) {
ThrowNativeError(SP_ERROR_INDEX, "Client index %d is not valid or is not in game", client);
} else if(character < 0 || character > 7) {
ThrowNativeError(SP_ERROR_INDEX, "Character ID (%d) is not in range (0-7)", character);
} else {
strcopy(g_Models[client], 64, survivor_models[character]);
}
return 0;
}