diff --git a/serverfiles/left4dead2/addons/sourcemod/gamedata/defib_fix.txt b/serverfiles/left4dead2/addons/sourcemod/gamedata/defib_fix.txt new file mode 100644 index 0000000..2b3a385 --- /dev/null +++ b/serverfiles/left4dead2/addons/sourcemod/gamedata/defib_fix.txt @@ -0,0 +1,105 @@ +"Games" +{ + "left4dead2" + { + "Functions" + { + "CItemDefibrillator::OnActionComplete" + { + "offset" "CItemDefibrillator::OnActionComplete" + "hooktype" "entity" + "return" "int" + "this" "ignore" + "arguments" + { + "Reviver" + { + "type" "cbaseentity" + } + "DeathModel" + { + "type" "cbaseentity" + } + } + } + "CItemDefibrillator::OnStartAction" + { + "offset" "CItemDefibrillator::OnStartAction" + "hooktype" "entity" + "return" "int" + "this" "ignore" + "arguments" + { + "BackpackItemActionType" + { + type "int" + } + "Reviver" + { + "type" "cbaseentity" + } + "DeathModel" + { + "type" "cbaseentity" + } + "somefloat" + { + "type" "float" + } + } + } + "CTerrorPlayer::GetPlayerByCharacter" + { + "signature" "CTerrorPlayer::GetPlayerByCharacter" + "callconv" "cdecl" + "return" "cbaseentity" + "this" "ignore" + "arguments" + { + "CharacterIndex" + { + "type" "int" + } + } + } + "CSurvivorDeathModel::Create" + { + "signature" "CSurvivorDeathModel::Create" + "callconv" "thiscall" + "return" "cbaseentity" + "this" "entity" + } + } + "Offsets" + { + "CItemDefibrillator::OnActionComplete" + { + "linux" "456" + "windows" "454" + } + "CItemDefibrillator::OnStartAction" + { + "linux" "451" + "windows" "449" + } + } + "Signatures" + { + "CTerrorPlayer::GetPlayerByCharacter" + { + "library" "server" + "linux" "@_ZN13CTerrorPlayer20GetPlayerByCharacterE21SurvivorCharacterType" + "windows" "\x55\x8B\xEC\x8B\x45\x08\x83\xEC\x08\x83\xF8\x08" + /* 55 8B EC 8B 45 08 83 EC 08 83 F8 08 */ + } + "CSurvivorDeathModel::Create" + { + "library" "server" + "linux" "@_ZN19CSurvivorDeathModel6CreateEP13CTerrorPlayer" + "windows" "\x55\x8B\xEC\x57\x8B\x7D\x08\x85\xFF\x75\x2A\x33\xC0\x5F\x5D\xC3\x8B\x87\x38\x01\x00\x00" + /* 55 8B EC 57 8B 7D 08 85 FF 75 ? 33 C0 5F 5D C3 8B 87 38 01 00 00 */ + } + } + } +} +//some gamedata from here https://github.com/Satanic-Spirit/defib-fix/blob/master/defibfix.txt credit to whoever \ No newline at end of file diff --git a/serverfiles/left4dead2/addons/sourcemod/plugins/Defib_Fix.smx b/serverfiles/left4dead2/addons/sourcemod/plugins/Defib_Fix.smx new file mode 100644 index 0000000..7caf954 Binary files /dev/null and b/serverfiles/left4dead2/addons/sourcemod/plugins/Defib_Fix.smx differ diff --git a/serverfiles/left4dead2/addons/sourcemod/scripting/Defib_Fix.sp b/serverfiles/left4dead2/addons/sourcemod/scripting/Defib_Fix.sp new file mode 100644 index 0000000..c672965 --- /dev/null +++ b/serverfiles/left4dead2/addons/sourcemod/scripting/Defib_Fix.sp @@ -0,0 +1,285 @@ +/* +* Fixes for gamebreaking bugs and stupid gameplay aspects +* Copyright (C) 2019 LuxLuma acceliacat@gmail.com +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*/ + +#pragma semicolon 1 + +#include +#include +#include +#include + +#pragma newdecls required + +#define GAMEDATA "defib_fix" + +#define PLUGIN_VERSION "2.0.1" + +GlobalForward g_hForward_SurvivorDeathModelCreated; + +Handle hOnActionComplete; +Handle hOnStartAction; + +bool g_bFixChar; +int g_iCurrentDeathModel; +int g_iDeathModelOwner[2048+1]; + + +public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) +{ + if(GetEngineVersion() != Engine_Left4Dead2) + { + strcopy(error, err_max, "Plugin only supports Left 4 Dead 2"); + return APLRes_SilentFailure; + } + + g_hForward_SurvivorDeathModelCreated = new GlobalForward("L4D2_OnSurvivorDeathModelCreated", ET_Event, Param_Cell, Param_Cell); + RegPluginLibrary("Defib_Fix"); + return APLRes_Success; +} + +public Plugin myinfo = +{ + name = "[L4D2]Defib_Fix", + author = "Lux", + description = "Fixes defibbing from failing when defibbing an alive character index", + version = PLUGIN_VERSION, + url = "forums.alliedmods.net/showthread.php?p=2647018" +}; + +public void OnPluginStart() +{ + Handle hGamedata = LoadGameConfigFile(GAMEDATA); + if(hGamedata == null) + SetFailState("Failed to load \"%s.txt\" gamedata.", GAMEDATA); + + hOnActionComplete = DHookCreateFromConf(hGamedata, "CItemDefibrillator::OnActionComplete"); + if(hOnActionComplete == null) + SetFailState("Failed to make hook for 'CItemDefibrillator::OnActionComplete'"); + + hOnStartAction = DHookCreateFromConf(hGamedata, "CItemDefibrillator::OnStartAction"); + if(hOnStartAction == null) + SetFailState("Failed to make hook for 'CItemDefibrillator::OnStartAction'"); + + Handle hDetour; + hDetour = DHookCreateFromConf(hGamedata, "CTerrorPlayer::GetPlayerByCharacter"); + if(!hDetour) + SetFailState("Failed to find 'CTerrorPlayer::GetPlayerByCharacter' signature"); + + if(!DHookEnableDetour(hDetour, false, GetPlayerByCharacter)) + SetFailState("Failed to detour 'CTerrorPlayer::GetPlayerByCharacter'"); + + hDetour = DHookCreateFromConf(hGamedata, "CSurvivorDeathModel::Create"); + if(!hDetour) + SetFailState("Failed to find 'CSurvivorDeathModel::Create' signature"); + + if(!DHookEnableDetour(hDetour, false, DeathModelCreatePre)) + SetFailState("Failed to detour 'CSurvivorDeathModel::Create'"); + + if(!DHookEnableDetour(hDetour, true, DeathModelCreatePost)) + SetFailState("Failed to detour 'CSurvivorDeathModel::Create'"); + + delete hGamedata; + + CreateConVar("defib_fix_version", PLUGIN_VERSION, "", FCVAR_NOTIFY|FCVAR_DONTRECORD); +} + +public void OnEntityCreated(int iEntity, const char[] sClassname) +{ + if(sClassname[0] != 'w' || !StrEqual(sClassname, "weapon_defibrillator", false)) + return; + + DHookEntity(hOnActionComplete, false, iEntity, _, OnActionCompletePre); + DHookEntity(hOnActionComplete, true, iEntity, _, OnActionCompletePost); + DHookEntity(hOnStartAction, false, iEntity, _, OnStartActionPre); +} + +public MRESReturn OnActionCompletePre(Handle hReturn, Handle hParams) +{ + int iDeathModel = DHookGetParam(hParams, 2); + if(!iDeathModel) + return MRES_Ignored; + + if(IsAllSurvivorsAlive())//rare case + { + KillAllDeathModels(); + DHookSetReturn(hReturn, 0); + return MRES_Supercede; + } + + g_iCurrentDeathModel = iDeathModel; + g_bFixChar = true; + return MRES_Ignored; +} + +public MRESReturn OnActionCompletePost(Handle hReturn, Handle hParams) +{ + if(IsAllSurvivorsAlive()) + KillAllDeathModels(); + + g_bFixChar = false;//just incase + return MRES_Ignored; +} + +public MRESReturn GetPlayerByCharacter(Handle hReturn, Handle hParams) +{ + if(!g_bFixChar) + return MRES_Ignored; + g_bFixChar = false; + + static int iChar[MAXPLAYERS+1]; + + int iCurrentChar = GetClientOfUserId(g_iDeathModelOwner[g_iCurrentDeathModel]); + if(iCurrentChar > 0 && IsClientInGame(iCurrentChar) && !IsPlayerAlive(iCurrentChar) && GetClientTeam(iCurrentChar) == 2)//check if owner of model death model is dead + { + DHookSetReturn(hReturn, iCurrentChar); + return MRES_Supercede; + } + + iCurrentChar = DHookGetParam(hParams, 1); + + for(int i = 1; i <= MaxClients; i++) + { + if(IsClientInGame(i) && !IsPlayerAlive(i) && GetClientTeam(i) == 2) + { + iChar[i] = GetEntProp(i, Prop_Send, "m_survivorCharacter", 1); + if(iCurrentChar == iChar[i])//if found player same char type + { + DHookSetReturn(hReturn, i); + return MRES_Supercede; + } + } + else + { + iChar[i] = -1; + } + } + + int iFoundPlayer; + iCurrentChar = ConvertToInternalCharacter(iCurrentChar); + for(int i = 1; i <= MaxClients; i++) + { + if(iChar[i] != -1) + iFoundPlayer = i;//fallback incase noone matches + + if(iCurrentChar == iChar[i]) + { + iFoundPlayer = i; + break; + } + } + if(!iFoundPlayer) + return MRES_Ignored;//better safe than sorry + + DHookSetReturn(hReturn, iFoundPlayer); + return MRES_Supercede; +} + +public MRESReturn OnStartActionPre(Handle hReturn, Handle hParams) +{ + if(IsAllSurvivorsAlive()) + { + KillAllDeathModels(); + DHookSetReturn(hReturn, 0); + return MRES_Supercede; + } + return MRES_Ignored; +} + +int g_iTempClient; +public MRESReturn DeathModelCreatePost(int pThis, Handle hReturn) +{ + int iDeathModel = DHookGetReturn(hReturn); + if(!iDeathModel) + return MRES_Ignored; + + float vPos[3]; + GetClientAbsOrigin(g_iTempClient, vPos); + + TeleportEntity(iDeathModel, vPos, NULL_VECTOR, NULL_VECTOR); + + g_iDeathModelOwner[iDeathModel] = GetClientUserId(g_iTempClient); + + Call_StartForward(g_hForward_SurvivorDeathModelCreated); + Call_PushCell(g_iTempClient); + Call_PushCell(iDeathModel); + Call_Finish(); + + return MRES_Ignored; +} + +public MRESReturn DeathModelCreatePre(int pThis) +{ + g_iTempClient = pThis; +} + +public void OnEntityDestroyed(int iEntity) +{ + if(iEntity < 1) + return; + + g_iDeathModelOwner[iEntity] = 0; +} + +int ConvertToInternalCharacter(int iChar) +{ + switch(iChar) + { + case 4: + { + return 0; + } + case 5: + { + return 1; + } + case 6: + { + return 3; + } + case 7: + { + return 2; + } + case 9: + { + return 8; + } + } + return iChar;// some people set survivors to 8 or 9 index so revive them too +} + +bool IsAllSurvivorsAlive() +{ + for(int i = 1; i <= MaxClients; i++)// rare case it could happen + { + if(IsClientInGame(i) && !IsPlayerAlive(i) && GetClientTeam(i) == 2) + { + return false; + } + } + return true; +} + +void KillAllDeathModels() +{ + int i = INVALID_ENT_REFERENCE; + while((i = FindEntityByClassname(i, "survivor_death_model")) != INVALID_ENT_REFERENCE) + { + AcceptEntityInput(i, "Kill"); + } +} diff --git a/serverfiles/left4dead2/addons/sourcemod/scripting/include/Defib_Fix.inc b/serverfiles/left4dead2/addons/sourcemod/scripting/include/Defib_Fix.inc new file mode 100644 index 0000000..21bf5b7 --- /dev/null +++ b/serverfiles/left4dead2/addons/sourcemod/scripting/include/Defib_Fix.inc @@ -0,0 +1,30 @@ +/* +* https://github.com/LuxLuma/Left-4-fix/tree/master/left%204%20fix/Defib_Fix +*/ + +#if defined _Defib_Fix_included +#endinput +#endif +#define _Defib_Fix_included + +/** +* @Note only called if Defib_Fix is installed +* +* Deathmodels are "survivor_death_model" that can be used with defibrillator. +* +* @param iClient Client Index who died. +* @param iDeathModel Entity Index of Deathmodel for client who died +* @no return +*/ +forward void L4D2_OnSurvivorDeathModelCreated(int iClient, int iDeathModel); + +public SharedPlugin __pl_Defib_Fix = +{ + name = "Defib_Fix", + file = "Defib_Fix.smx", +#if defined REQUIRE_PLUGIN + required = 1, +#else + required = 0, +#endif +};