diff --git a/addons/sourcemod/gamedata/si_pool.txt b/addons/sourcemod/gamedata/si_pool.txt deleted file mode 100644 index 7aa7d5d31..000000000 --- a/addons/sourcemod/gamedata/si_pool.txt +++ /dev/null @@ -1,114 +0,0 @@ -/* - * @Author: 我是派蒙啊 - * @Last Modified by: 我是派蒙啊 - * @Create Date: 2024-02-17 16:48:01 - * @Last Modified time: 2024-03-25 19:03:25 - * @Github: https://github.com/Paimon-Kawaii - */ -"Games" -{ - "left4dead2" - { - "Addresses" - { - "NextBotCreatePlayerBot.jumptable" - { - "windows" - { - "signature" "CTerrorPlayer::ReplaceWithBot.jumptable" - "offset" "7" - } - } - } - - "Signatures" - { - "CTerrorPlayer::ReplaceWithBot.jumptable" - { - "library" "server" - // Switch jump with a bunch of cases matching... - // PUSH rel32 - // CALL rel32 - // JUMP rel8 - // There are acutally 2 matches of this in the windows binary, - // but they appear to be the same functionality--so it doesn't matter which we get. - /* FF 24 85 ? ? ? ? 68 ? ? ? ? E8 ? ? ? ? EB ? 68 ? ? ? ? E8 ? ? ? ? EB ? 68 ? ? ? ? E8 ? ? ? ? EB ? 68 ? ? ? ? E8 ? ? ? ? EB ? 68 ? ? ? ? E8 ? ? ? ? EB ? 68 ? ? ? ? E8 ? ? ? ? EB ? 68 ? ? ? ? E8 */ - "windows" "\xFF\x24\x85\x2A\x2A\x2A\x2A\x68\x2A\x2A\x2A\x2A\xE8\x2A\x2A\x2A\x2A\xEB\x2A\x68\x2A\x2A\x2A\x2A\xE8\x2A\x2A\x2A\x2A\xEB\x2A\x68\x2A\x2A\x2A\x2A\xE8\x2A\x2A\x2A\x2A\xEB\x2A\x68\x2A\x2A\x2A\x2A\xE8\x2A\x2A\x2A\x2A\xEB\x2A\x68\x2A\x2A\x2A\x2A\xE8\x2A\x2A\x2A\x2A\xEB\x2A\x68\x2A\x2A\x2A\x2A\xE8\x2A\x2A\x2A\x2A\xEB\x2A\x68\x2A\x2A\x2A\x2A\xE8" - } - - "NextBotCreatePlayerBot" - { - "library" "server" - "linux" "@_Z22NextBotCreatePlayerBotI6HunterEPT_PKc" - } - - "NextBotCreatePlayerBot" - { - "library" "server" - "linux" "@_Z22NextBotCreatePlayerBotI6JockeyEPT_PKc" - } - "NextBotCreatePlayerBot" - { - "library" "server" - "linux" "@_Z22NextBotCreatePlayerBotI7SpitterEPT_PKc" - } - "NextBotCreatePlayerBot" - { - "library" "server" - "linux" "@_Z22NextBotCreatePlayerBotI7ChargerEPT_PKc" - } - "NextBotCreatePlayerBot" - { - "library" "server" - "linux" "@_Z22NextBotCreatePlayerBotI6SmokerEPT_PKc" - } - "NextBotCreatePlayerBot" - { - "library" "server" - "linux" "@_Z22NextBotCreatePlayerBotI6BoomerEPT_PKc" - } - - /* - * CTerrorPlayer::SetClass(CBaseEntity *, int) - */ - "CTerrorPlayer::SetClass" - { - "library" "server" - "linux" "@_ZN13CTerrorPlayer8SetClassE15ZombieClassType" - "windows" "\x55\x8B\x2A\x56\x8B\x2A\xE8\x2A\x2A\x2A\x2A\x83\x2A\x2A\x0F\x85\x2A\x2A\x2A\x2A\xA1\x2A\x2A\x2A\x2A\x40\xA3" - /* 55 8B ? 56 8B ? E8 ? ? ? ? 83 ? ? 0F 85 ? ? ? ? A1 ? ? ? ? 40 A3 */ // Updated by SilverShot. - /* Search "weapon_smoker_claw" */ - } - - /* - * CBaseAbility::CreateForPlayer(CBaseAbility *this, CTerrorPlayer *) - */ - "CBaseAbility::CreateForPlayer" - { - "library" "server" - "linux" "@_ZN12CBaseAbility15CreateForPlayerEP13CTerrorPlayer" - "windows" "\x55\x8B\x2A\x83\x2A\x2A\x56\x8B\x2A\x2A\x85\x2A\x0F\x84\x2A\x2A\x2A\x2A\x8B\x2A\xE8\x2A\x2A\x2A\x2A\x83" - /* 55 8B ? 83 ? ? 56 8B ? ? 85 ? 0F 84 ? ? ? ? 8B ? E8 ? ? ? ? 83 */ // Updated by SilverShot. - /* Search "ability_tongue" */ - } - - /* CCSPlayer::State_Transition(CSPlayerState) */ - "CCSPlayer::State_Transition" - { - "library" "server" - "linux" "@_ZN9CCSPlayer16State_TransitionE13CSPlayerState" - "windows" "\x55\x8B\xEC\x56\x8B\xF1\x8B\x86\x2A\x2A\x2A\x2A\x57\x8B\x7D\x2A\x85\xC0\x74\x2A\x83" - /* 55 8B EC 56 8B F1 8B 86 ? ? ? ? 57 8B 7D ? 85 C0 74 ? 83 */ - } - - /* CTerrorPlayer::RoundRespawn */ - "CTerrorPlayer::RoundRespawn" - { - "library" "server" - "linux" "@_ZN13CTerrorPlayer12RoundRespawnEv" - "windows" "\x56\x8B\xF1\xE8\x2A\x2A\x2A\x2A\xE8\x2A\x2A\x2A\x2A\x84\xC0\x75" - /* 56 8B F1 E8 ? ? ? ? E8 ? ? ? ? 84 C0 75 */ - } - } - } -} \ No newline at end of file diff --git a/addons/sourcemod/plugins/optional/AnneHappy/infected_control.smx b/addons/sourcemod/plugins/optional/AnneHappy/infected_control.smx index 30d524618..9e44d8c5d 100644 Binary files a/addons/sourcemod/plugins/optional/AnneHappy/infected_control.smx and b/addons/sourcemod/plugins/optional/AnneHappy/infected_control.smx differ diff --git a/addons/sourcemod/plugins/optional/AnneHappy/si_pool.smx b/addons/sourcemod/plugins/optional/AnneHappy/si_pool.smx deleted file mode 100644 index 4052f7d47..000000000 Binary files a/addons/sourcemod/plugins/optional/AnneHappy/si_pool.smx and /dev/null differ diff --git a/addons/sourcemod/scripting/AnneHappy/infected_control.sp b/addons/sourcemod/scripting/AnneHappy/infected_control.sp index ce5575c71..e812aecfb 100644 --- a/addons/sourcemod/scripting/AnneHappy/infected_control.sp +++ b/addons/sourcemod/scripting/AnneHappy/infected_control.sp @@ -10,280 +10,249 @@ #include #include #include -#include -#define CVAR_FLAG FCVAR_NOTIFY -#define TEAM_SURVIVOR 2 -#define TEAM_INFECTED 3 +#define CVAR_FLAG FCVAR_NOTIFY +#define TEAM_SURVIVOR 2 +#define TEAM_INFECTED 3 // 数据 -#define NAV_MESH_HEIGHT 20.0 -#define PLAYER_HEIGHT 72.0 -#define PLAYER_CHEST 45.0 -#define HIGHERPOS 300.0 -#define HIGHERPOSADDDISTANCE 300.0 +#define NAV_MESH_HEIGHT 20.0 +#define PLAYER_HEIGHT 72.0 +#define PLAYER_CHEST 45.0 +#define HIGHERPOS 300.0 +#define HIGHERPOSADDDISTANCE 300.0 #define INCAPSURVIVORCHECKDIS 500.0 -#define NORMALPOSMULT 1.4 +#define NORMALPOSMULT 1.4 // 启用特感类型 -#define ENABLE_SMOKER (1 << 0) -#define ENABLE_BOOMER (1 << 1) -#define ENABLE_HUNTER (1 << 2) -#define ENABLE_SPITTER (1 << 3) -#define ENABLE_JOCKEY (1 << 4) -#define ENABLE_CHARGER (1 << 5) +#define ENABLE_SMOKER (1 << 0) +#define ENABLE_BOOMER (1 << 1) +#define ENABLE_HUNTER (1 << 2) +#define ENABLE_SPITTER (1 << 3) +#define ENABLE_JOCKEY (1 << 4) +#define ENABLE_CHARGER (1 << 5) // Spitter吐口水之后能传送的时间 -#define SPIT_INTERVAL 2.0 +#define SPIT_INTERVAL 2.0 //确认为跑男的距离 -#define RushManDistance 1200.0 - -stock const char InfectedName[10][] = { - "common", - "smoker", - "boomer", - "hunter", - "spitter", - "jockey", - "charger", - "witch", - "tank", - "survivor" +#define RushManDistance 1200.0 + +stock const char InfectedName[10][] = +{ + "common", + "smoker", + "boomer", + "hunter", + "spitter", + "jockey", + "charger", + "witch", + "tank", + "survivor" }; + #if (DEBUG) char sLogFile[PLATFORM_MAX_PATH] = "addons/sourcemod/logs/infected_control.txt"; #endif - // 插件基本信息,根据 GPL 许可证条款,需要修改插件请勿修改此信息! -public Plugin myinfo = +public Plugin myinfo = { - name = "Direct InfectedSpawn", - author = "Caibiii, 夜羽真白,东, 派蒙", - description = "特感刷新控制,传送落后特感", - version = "2024.04.05", - url = "https://github.com/fantasylidong/CompetitiveWithAnne", - - + name = "Direct InfectedSpawn", + author = "Caibiii, 夜羽真白,东", + description = "特感刷新控制,传送落后特感", + version = "2023.09.04", + url = "https://github.com/fantasylidong/CompetitiveWithAnne" } // Cvars -ConVar g_hSpawnDistanceMin, // 特感最低生成距离 - g_hSpawnDistanceMax, // 特感最大生成距离 - g_hTeleportSi, // 是否打开特感传送 - // g_hTeleportDistance, - g_hSiLimit, // 一波特感生成数量上限 - g_hSiInterval, // 每波特感生成基础间隔 - g_hMaxPlayerZombies, // 设置导演系统的特感数量上限 - g_hTeleportCheckTime, // 几秒不被看到后可以传送 - g_hEnableSIoption, // 设置生成哪几种特感 - g_hAllChargerMode, // 是否为全牛模式 - g_hAutoSpawnTimeControl, // 自动设置增加时间,加到基础间隔之上,这项不打开,增加时间默认为g_hSiInterval/2.打开为特感数量小于g_hSiLimit/3 + 1后再过基准时间开始刷特。 - // 但是这个值大于g_hSiInterval/2也会开始强制刷特 - g_hAddDamageToSmoker, // 被smoker拉的时候是否对smoker是否进行增伤 - g_hIgnoreIncappedSurvivorSight, // 是否忽视掉倒地生还者视线 - g_hVsBossFlowBuffer, g_hAllHunterMode; +ConVar + g_hSpawnDistanceMin, //特感最低生成距离 + g_hSpawnDistanceMax, //特感最大生成距离 + g_hTeleportSi, //是否打开特感传送 +// g_hTeleportDistance, + g_hSiLimit, //一波特感生成数量上限 + g_hSiInterval, //每波特感生成基础间隔 + g_hMaxPlayerZombies, //设置导演系统的特感数量上限 + g_hTeleportCheckTime, //几秒不被看到后可以传送 + g_hEnableSIoption, //设置生成哪几种特感 + g_hAllChargerMode, //是否为全牛模式 + g_hAutoSpawnTimeControl, //自动设置增加时间,加到基础间隔之上,这项不打开,增加时间默认为g_hSiInterval/2.打开为特感数量小于g_hSiLimit/3 + 1后再过基准时间开始刷特。 + //但是这个值大于g_hSiInterval/2也会开始强制刷特 + g_hAddDamageToSmoker, //被smoker拉的时候是否对smoker是否进行增伤 + g_hIgnoreIncappedSurvivorSight, //是否忽视掉倒地生还者视线 + g_hVsBossFlowBuffer, + g_hAllHunterMode; // Ints -int - g_iSiLimit, //特感数量 - g_iRushManIndex, //跑男id - g_iWaveTime, // Debug时输出这是第几波刷特 - g_iLastSpawnTime, //离上次刷特过去了多久 - g_iTotalSINum = 0, //总共还活着的特感 - g_iEnableSIoption = 63, //可生成的特感种类 - g_iTeleportCheckTime = 5, //特感传送要求的不被看到的次数(1s检查一次) - g_iSINum[6] = { 0 }, //记录当前还存活的特感数量 - g_ArraySIlimit[6] = { 0 }, //记录去除队列里特感数量后还能生成的特感 - g_iTeleCount[MAXPLAYERS + 1] = { 0 }, //每个特感传送的不被看到次数 - g_iTargetSurvivor = -1, // OnGameFrame参数里,以该目标生成生成网络,寻找生成目标 - g_iQueueIndex = 0, //当前生成队列长度 - g_iTeleportIndex = 0, //当前传送队列长度 - g_iSpawnMaxCount = 0, //当前可生成特感数量 - g_iSurvivorNum = 0, //活着的生还者数量 - g_iSurvivors[MAXPLAYERS + 1] = { 0 }; //活着生还者的索引 +int + g_iSiLimit, //特感数量 + g_iRushManIndex, //跑男id + g_iWaveTime, //Debug时输出这是第几波刷特 + g_iLastSpawnTime, //离上次刷特过去了多久 + g_iTotalSINum = 0, //总共还活着的特感 + g_iEnableSIoption = 63, //可生成的特感种类 + g_iTeleportCheckTime = 5, //特感传送要求的不被看到的次数(1s检查一次) + g_iSINum[6] = {0}, //记录当前还存活的特感数量 + g_ArraySIlimit[6] = {0}, //记录去除队列里特感数量后还能生成的特感 + g_iTeleCount[MAXPLAYERS + 1] = {0}, //每个特感传送的不被看到次数 + g_iTargetSurvivor = -1, //OnGameFrame参数里,以该目标生成生成网络,寻找生成目标 + g_iQueueIndex = 0, //当前生成队列长度 + g_iTeleportIndex = 0, //当前传送队列长度 + g_iSpawnMaxCount = 0, //当前可生成特感数量 + g_iSurvivorNum = 0, //活着的生还者数量 + g_iSurvivors[MAXPLAYERS + 1] = {0}; //活着生还者的索引 // Floats -float - g_fSpitterSpitTime[MAXPLAYERS + 1], // Spitter吐口水时间 - g_fSpawnDistanceMin, //特感的最小生成距离 - g_fSpawnDistanceMax, //特感的最大生成距离 - g_fSpawnDistance, //特感的当前生成距离 - // g_fTeleportDistanceMin, //特感传送距离生还的最小距离 - g_fTeleportDistance, //特感当前传送生成距离 - g_fLastSISpawnStartTime, //上一波特感生成时间 - g_fUnpauseNextSpawnTime, //因为暂停记录下下一波特感的时间,方便解除暂停时创建处理线程 - g_fSiInterval; //特感的生成时间间隔 +float + g_fSpitterSpitTime[MAXPLAYERS + 1], //Spitter吐口水时间 + g_fSpawnDistanceMin, //特感的最小生成距离 + g_fSpawnDistanceMax, //特感的最大生成距离 + g_fSpawnDistance, //特感的当前生成距离 +// g_fTeleportDistanceMin, //特感传送距离生还的最小距离 + g_fTeleportDistance, //特感当前传送生成距离 + g_fLastSISpawnStartTime, //上一波特感生成时间 + g_fUnpauseNextSpawnTime, //因为暂停记录下下一波特感的时间,方便解除暂停时创建处理线程 + g_fSiInterval; //特感的生成时间间隔 // Bools -bool - g_bTeleportSi, //是否开启特感传送检测 - g_bPickRushMan, //是否针对跑男 - g_bShouldCheck, //是否开启时间检测 - g_bAutoSpawnTimeControl, //是否开启自动增加时间 - g_bAddDamageToSmoker, //是否对smoker增伤(一般alone模式开启) - g_bIgnoreIncappedSurvivorSight, //是否忽略倒地生还者的视线 - g_bIsLate = false, // text插件是否发送开启刷特命令 - g_bSmokerAvailable = false, // ai_smoker_new是否存在 - g_bSIPoolAvailable = false, //特感池是否存在 - g_bTargetSystemAvailable = false; //目标选择插件是否存在 - +bool + g_bTeleportSi, //是否开启特感传送检测 + g_bPickRushMan, //是否针对跑男 + g_bShouldCheck, //是否开启时间检测 + g_bAutoSpawnTimeControl, //是否开启自动增加时间 + g_bAddDamageToSmoker, //是否对smoker增伤(一般alone模式开启) + g_bIgnoreIncappedSurvivorSight, //是否忽略倒地生还者的视线 + g_bIsLate = false, //text插件是否发送开启刷特命令 + g_bSmokerAvailable = false, //ai_smoker_new是否存在 + g_bTargetSystemAvailable = false; //目标选择插件是否存在 // Handle -Handle - g_hCheckShouldSpawnOrNot = INVALID_HANDLE, // 1s检测一次是否开启刷特进程的维护进程 - g_hSpawnProcess = INVALID_HANDLE, //刷特 handle - g_hTeleHandle = INVALID_HANDLE, //传送sdk Handle - g_hRushManNotifyForward = INVALID_HANDLE; //检测到跑男提醒Target_limit插件放开单人目标限制 - +Handle + g_hCheckShouldSpawnOrNot = INVALID_HANDLE, //1s检测一次是否开启刷特进程的维护进程 + g_hSpawnProcess = INVALID_HANDLE, //刷特 handle + g_hTeleHandle = INVALID_HANDLE, //传送sdk Handle + g_hRushManNotifyForward = INVALID_HANDLE; //检测到跑男提醒Target_limit插件放开单人目标限制 // ArrayList -ArrayList - aTeleportQueue, //传送队列 - // aSpawnNavList, //储存特感生成的navid,用来限制特感不能生成在同一块Navid上 - aSpawnQueue; //刷特队列 - -SIPool - g_hSIPool; +ArrayList + aTeleportQueue, //传送队列 + //aSpawnNavList, //储存特感生成的navid,用来限制特感不能生成在同一块Navid上 + aSpawnQueue; //刷特队列 public APLRes AskPluginLoad2(Handle plugin, bool late, char[] error, int err_max) { - RegPluginLibrary("infected_control"); - g_hRushManNotifyForward = CreateGlobalForward("OnDetectRushman", ET_Ignore, Param_Cell); - CreateNative("GetNextSpawnTime", Native_GetNextSpawnTime); - return APLRes_Success; + RegPluginLibrary("infected_control"); + g_hRushManNotifyForward = CreateGlobalForward("OnDetectRushman", ET_Ignore, Param_Cell); + CreateNative("GetNextSpawnTime", Native_GetNextSpawnTime); + return APLRes_Success; } -any Native_GetNextSpawnTime(Handle plugin, int numParams) -{ - float time = 0.0; - //如果刷特进程还不开始,直接返回刷特间隔 - if (g_hSpawnProcess == null) - time = g_fSiInterval; - else time = g_fSiInterval - (GetGameTime() - g_fLastSISpawnStartTime); - Debug_Print("下一波特感生成时间是%.2f秒后", time); - return time; -} - -public void OnAllPluginsLoaded() +public any Native_GetNextSpawnTime(Handle plugin, int numParams) { - g_bTargetSystemAvailable = LibraryExists("si_target_limit"); - g_bSmokerAvailable = LibraryExists("ai_smoker_new"); - g_bSIPoolAvailable = LibraryExists("si_pool"); + float time = 0.0; + //如果刷特进程还不开始,直接返回刷特间隔 + if (g_hSpawnProcess == null) + { + time = g_fSiInterval; + } + else + { + time = g_fSiInterval - (GetGameTime() - g_fLastSISpawnStartTime); + } + Debug_Print("下一波特感生成时间是%.2f秒后", time); + return time; } +public void OnAllPluginsLoaded(){ + g_bTargetSystemAvailable = LibraryExists("si_target_limit"); + g_bSmokerAvailable = LibraryExists("ai_smoker_new"); +} public void OnLibraryAdded(const char[] name) { - if (StrEqual(name, "si_target_limit")) - g_bTargetSystemAvailable = true; - else if (StrEqual(name, "ai_smoker_new")) - g_bSmokerAvailable = true; - else if (StrEqual(name, "si_pool")) - g_bSIPoolAvailable = true; + if ( StrEqual(name, "si_target_limit") ) { g_bTargetSystemAvailable = true; } + else if( StrEqual(name, "ai_smoker_new") ) { g_bSmokerAvailable = true; } } - public void OnLibraryRemoved(const char[] name) { - if (StrEqual(name, "si_target_limit")) - g_bTargetSystemAvailable = false; - else if (StrEqual(name, "ai_smoker_new")) - g_bSmokerAvailable = false; - else if (StrEqual(name, "si_pool")) - g_bSIPoolAvailable = false; + if ( StrEqual(name, "si_target_limit") ) { g_bTargetSystemAvailable = false; } + else if( StrEqual(name, "ai_smoker_new") ) { g_bSmokerAvailable = false; } } - public void OnPluginStart() { - // CreateConVar - g_hSpawnDistanceMin = CreateConVar("inf_SpawnDistanceMin", "250.0", "特感复活离生还者最近的距离限制", CVAR_FLAG, true, 0.0); - g_hSpawnDistanceMax = CreateConVar("inf_SpawnDistanceMax", "1500.0", "特感复活离生还者最远的距离限制", CVAR_FLAG, true, g_hSpawnDistanceMin.FloatValue); - g_hTeleportSi = CreateConVar("inf_TeleportSi", "1", "是否开启特感距离生还者一定距离将其传送至生还者周围", CVAR_FLAG, true, 0.0, true, 1.0); - g_hTeleportCheckTime = CreateConVar("inf_TeleportCheckTime", "5", "特感几秒后没被看到开始传送", CVAR_FLAG, true, 0.0); - g_hEnableSIoption = CreateConVar("inf_EnableSIoption", "63", "启用生成的特感类型,1 smoker 2 boomer 4 hunter 8 spitter 16 jockey 32 charger,把你想要生成的特感值加起来", CVAR_FLAG, true, 0.0, true, 63.0); - g_hAllChargerMode = CreateConVar("inf_AllChargerMode", "0", "是否是全牛模式", CVAR_FLAG, true, 0.0, true, 1.0); - g_hAllHunterMode = CreateConVar("inf_AllHunterMode", "0", "是否是全猎人模式", CVAR_FLAG, true, 0.0, true, 1.0); - g_hAutoSpawnTimeControl = CreateConVar("inf_EnableAutoSpawnTime", "1", "是否开启自动设置增加时间", CVAR_FLAG, true, 0.0, true, 1.0); - g_hIgnoreIncappedSurvivorSight = CreateConVar("inf_IgnoreIncappedSurvivorSight", "1", "特感传送检测是否被看到的时候是否忽略倒地生还者视线", CVAR_FLAG, true, 0.0, true, 1.0); - g_hAddDamageToSmoker = CreateConVar("inf_AddDamageToSmoker", "0", "单人模式smoker拉人时是否5倍伤害", CVAR_FLAG, true, 0.0, true, 1.0); - // 传送会根据这个数值画一个以选定生还者为核心,两边各长inf_TeleportDistance单位距离,高inf_TeleportDistance距离的长方形区域内找复活位置,PS传送最好近一点 - // g_hTeleportDistance = CreateConVar("inf_TeleportDistance", "600.0", "特感传送区域的最小复活大小", CVAR_FLAG, true, g_hSpawnDistanceMin.FloatValue); - g_hSiLimit = CreateConVar("l4d_infected_limit", "6", "一次刷出多少特感", CVAR_FLAG, true, 0.0); - g_hSiInterval = CreateConVar("versus_special_respawn_interval", "16.0", "对抗模式下刷特时间控制", CVAR_FLAG, true, 0.0); - g_hMaxPlayerZombies = FindConVar("z_max_player_zombies"); - g_hVsBossFlowBuffer = FindConVar("versus_boss_buffer"); - SetConVarInt(FindConVar("director_no_specials"), 1); - - // HookEvents - // PostNoCopy是绝对不正确的,NoCopy 会导致 event 丢弃所有的数据("Use 'PostNoCopy' if your action is Post and ONLY requires the event name.") - // 丢弃的数据包括 userid,attackerid,weaponid 等等,对于求生来说这几个字节的内存没必要省略 - // 详见:https://wiki.alliedmods.net/Events_(SourceMod_Scripting) - HookEvent("finale_win", evt_RoundEnd); - HookEvent("mission_lost", evt_RoundEnd); - HookEvent("player_hurt", evt_PlayerHurt); - HookEvent("ability_use", evt_GetSpitTime); - HookEvent("round_start", evt_RoundStart); - HookEvent("map_transition", evt_RoundEnd); - HookEvent("player_death", evt_PlayerDeath); - HookEvent("player_spawn", evt_PlayerSpawn); - - // AddChangeHook - g_hSpawnDistanceMax.AddChangeHook(ConVarChanged_Cvars); - g_hSpawnDistanceMin.AddChangeHook(ConVarChanged_Cvars); - g_hTeleportSi.AddChangeHook(ConVarChanged_Cvars); - g_hTeleportCheckTime.AddChangeHook(ConVarChanged_Cvars); - // g_hTeleportDistance.AddChangeHook(ConVarChanged_Cvars); - g_hSiInterval.AddChangeHook(ConVarChanged_Cvars); - g_hIgnoreIncappedSurvivorSight.AddChangeHook(ConVarChanged_Cvars); - g_hEnableSIoption.AddChangeHook(ConVarChanged_Cvars); - g_hAllChargerMode.AddChangeHook(ConVarChanged_Cvars); - g_hAllHunterMode.AddChangeHook(ConVarChanged_Cvars); - g_hAutoSpawnTimeControl.AddChangeHook(ConVarChanged_Cvars); - g_hAddDamageToSmoker.AddChangeHook(ConVarChanged_Cvars); - g_hSiLimit.AddChangeHook(MaxPlayerZombiesChanged_Cvars); - - // ArrayList - aSpawnQueue = new ArrayList(); - aTeleportQueue = new ArrayList(); - // aSpawnNavList = new ArrayList(); - - // GetCvars - GetCvars(); - GetSiLimit(); - - // SetConVarBonus - SetConVarBounds(g_hMaxPlayerZombies, ConVarBound_Upper, true, g_hSiLimit.FloatValue); - - // Debug - RegAdminCmd("sm_startspawn", Cmd_StartSpawn, ADMFLAG_ROOT, "管理员开启特感产卵"); - RegAdminCmd("sm_stopspawn", Cmd_StopSpawn, ADMFLAG_ROOT, "管理员关闭特感产卵"); -} - -public void OnMapStart() -{ - if (g_bSIPoolAvailable && !g_hSIPool) g_hSIPool = SIPool.Instance(); -} - -public void OnPluginEnd() -{ - if (g_hAllChargerMode.BoolValue) - { - FindConVar("z_charger_health").RestoreDefault(); - FindConVar("z_charge_max_speed").RestoreDefault(); - FindConVar("z_charge_start_speed").RestoreDefault(); - FindConVar("z_charger_pound_dmg").RestoreDefault(); - FindConVar("z_charge_max_damage").RestoreDefault(); - FindConVar("z_charge_interval").RestoreDefault(); - } + // CreateConVar + g_hSpawnDistanceMin = CreateConVar("inf_SpawnDistanceMin", "250.0", "特感复活离生还者最近的距离限制", CVAR_FLAG, true, 0.0); + g_hSpawnDistanceMax = CreateConVar("inf_SpawnDistanceMax", "1500.0", "特感复活离生还者最远的距离限制", CVAR_FLAG, true, g_hSpawnDistanceMin.FloatValue); + g_hTeleportSi = CreateConVar("inf_TeleportSi", "1", "是否开启特感距离生还者一定距离将其传送至生还者周围", CVAR_FLAG, true, 0.0, true, 1.0); + g_hTeleportCheckTime = CreateConVar("inf_TeleportCheckTime", "5", "特感几秒后没被看到开始传送", CVAR_FLAG, true, 0.0); + g_hEnableSIoption = CreateConVar("inf_EnableSIoption", "63", "启用生成的特感类型,1 smoker 2 boomer 4 hunter 8 spitter 16 jockey 32 charger,把你想要生成的特感值加起来", CVAR_FLAG, true, 0.0, true, 63.0); + g_hAllChargerMode = CreateConVar("inf_AllChargerMode", "0", "是否是全牛模式", CVAR_FLAG, true, 0.0, true, 1.0); + g_hAllHunterMode = CreateConVar("inf_AllHunterMode", "0", "是否是全猎人模式", CVAR_FLAG, true, 0.0, true, 1.0); + g_hAutoSpawnTimeControl = CreateConVar("inf_EnableAutoSpawnTime", "1", "是否开启自动设置增加时间", CVAR_FLAG, true, 0.0, true, 1.0); + g_hIgnoreIncappedSurvivorSight = CreateConVar("inf_IgnoreIncappedSurvivorSight", "1", "特感传送检测是否被看到的时候是否忽略倒地生还者视线", CVAR_FLAG, true, 0.0, true, 1.0); + g_hAddDamageToSmoker= CreateConVar("inf_AddDamageToSmoker", "0", "单人模式smoker拉人时是否5倍伤害", CVAR_FLAG, true, 0.0, true, 1.0); + //传送会根据这个数值画一个以选定生还者为核心,两边各长inf_TeleportDistance单位距离,高inf_TeleportDistance距离的长方形区域内找复活位置,PS传送最好近一点 + //g_hTeleportDistance = CreateConVar("inf_TeleportDistance", "600.0", "特感传送区域的最小复活大小", CVAR_FLAG, true, g_hSpawnDistanceMin.FloatValue); + g_hSiLimit = CreateConVar("l4d_infected_limit", "6", "一次刷出多少特感", CVAR_FLAG, true, 0.0); + g_hSiInterval = CreateConVar("versus_special_respawn_interval", "16.0", "对抗模式下刷特时间控制", CVAR_FLAG, true, 0.0); + g_hMaxPlayerZombies = FindConVar("z_max_player_zombies"); + g_hVsBossFlowBuffer = FindConVar("versus_boss_buffer"); + SetConVarInt(FindConVar("director_no_specials"), 1); + // HookEvents + HookEvent("player_death", evt_PlayerDeath, EventHookMode_PostNoCopy); + HookEvent("round_start", evt_RoundStart, EventHookMode_PostNoCopy); + HookEvent("finale_win", evt_RoundEnd, EventHookMode_PostNoCopy); + HookEvent("map_transition", evt_RoundEnd, EventHookMode_PostNoCopy); + HookEvent("mission_lost", evt_RoundEnd, EventHookMode_PostNoCopy); + HookEvent("player_hurt", evt_PlayerHurt, EventHookMode_PostNoCopy); + HookEvent("ability_use", evt_GetSpitTime, EventHookMode_PostNoCopy); + HookEvent("player_spawn", evt_PlayerSpawn, EventHookMode_PostNoCopy); + // AddChangeHook + g_hSpawnDistanceMax.AddChangeHook(ConVarChanged_Cvars); + g_hSpawnDistanceMin.AddChangeHook(ConVarChanged_Cvars); + g_hTeleportSi.AddChangeHook(ConVarChanged_Cvars); + g_hTeleportCheckTime.AddChangeHook(ConVarChanged_Cvars); + //g_hTeleportDistance.AddChangeHook(ConVarChanged_Cvars); + g_hSiInterval.AddChangeHook(ConVarChanged_Cvars); + g_hIgnoreIncappedSurvivorSight.AddChangeHook(ConVarChanged_Cvars); + g_hEnableSIoption.AddChangeHook(ConVarChanged_Cvars); + g_hAllChargerMode.AddChangeHook(ConVarChanged_Cvars); + g_hAllHunterMode.AddChangeHook(ConVarChanged_Cvars); + g_hAutoSpawnTimeControl.AddChangeHook(ConVarChanged_Cvars); + g_hAddDamageToSmoker.AddChangeHook(ConVarChanged_Cvars); + g_hSiLimit.AddChangeHook(MaxPlayerZombiesChanged_Cvars); + + // ArrayList + aSpawnQueue = new ArrayList(); + aTeleportQueue = new ArrayList(); + //aSpawnNavList = new ArrayList(); + // GetCvars + GetCvars(); + GetSiLimit(); + // SetConVarBonus + SetConVarBounds(g_hMaxPlayerZombies, ConVarBound_Upper, true, g_hSiLimit.FloatValue); + // Debug + RegAdminCmd("sm_startspawn", Cmd_StartSpawn, ADMFLAG_ROOT, "管理员重置刷特时钟"); + RegAdminCmd("sm_stopspawn", Cmd_StopSpawn, ADMFLAG_ROOT, "管理员重置刷特时钟"); +} + +public void OnPluginEnd() { + if(g_hAllChargerMode.BoolValue){ + FindConVar("z_charger_health").RestoreDefault(); + FindConVar("z_charge_max_speed").RestoreDefault(); + FindConVar("z_charge_start_speed").RestoreDefault(); + FindConVar("z_charger_pound_dmg").RestoreDefault(); + FindConVar("z_charge_max_damage").RestoreDefault(); + FindConVar("z_charge_interval").RestoreDefault(); + } } -void TweakSettings() -{ - if (g_hAllChargerMode.BoolValue) - { - FindConVar("z_charger_health").SetFloat(500.0); - FindConVar("z_charge_max_speed").SetFloat(750.0); - FindConVar("z_charge_start_speed").SetFloat(350.0); - FindConVar("z_charger_pound_dmg").SetFloat(10.0); - FindConVar("z_charge_max_damage").SetFloat(6.0); - FindConVar("z_charge_interval").SetFloat(2.0); - } +void TweakSettings() { + if(g_hAllChargerMode.BoolValue){ + FindConVar("z_charger_health").SetFloat(500.0); + FindConVar("z_charge_max_speed").SetFloat(750.0); + FindConVar("z_charge_start_speed").SetFloat(350.0); + FindConVar("z_charger_pound_dmg").SetFloat(10.0); + FindConVar("z_charge_max_damage").SetFloat(6.0); + FindConVar("z_charge_interval").SetFloat(2.0); + } } // 向量绘制 @@ -291,20 +260,20 @@ void TweakSettings() stock Action Cmd_StartSpawn(int client, int args) { - if (L4D_HasAnySurvivorLeftSafeArea()) - { - ResetStatus(); - CreateTimer(0.1, SpawnFirstInfected); - GetSiLimit(); - TweakSettings(); - } - return Plugin_Handled; + if (L4D_HasAnySurvivorLeftSafeArea()) + { + ResetStatus(); + CreateTimer(0.1, SpawnFirstInfected); + GetSiLimit(); + TweakSettings(); + } + return Plugin_Handled; } stock Action Cmd_StopSpawn(int client, int args) { - StopSpawn(); - return Plugin_Handled; + StopSpawn(); + return Plugin_Handled; } // ********************* @@ -312,181 +281,195 @@ stock Action Cmd_StopSpawn(int client, int args) // ********************* void ConVarChanged_Cvars(ConVar convar, const char[] oldValue, const char[] newValue) { - GetCvars(); + GetCvars(); } void MaxPlayerZombiesChanged_Cvars(ConVar convar, const char[] oldValue, const char[] newValue) { - g_iSiLimit = g_hSiLimit.IntValue; - CreateTimer(0.1, MaxSpecialsSet); + g_iSiLimit = g_hSiLimit.IntValue; + CreateTimer(0.1, MaxSpecialsSet); } void GetCvars() { - g_fSpawnDistanceMax = g_hSpawnDistanceMax.FloatValue; - g_fSpawnDistanceMin = g_hSpawnDistanceMin.FloatValue; - g_bTeleportSi = g_hTeleportSi.BoolValue; - // g_fTeleportDistanceMin = g_hTeleportDistance.FloatValue; - g_fSiInterval = g_hSiInterval.FloatValue; - g_iSiLimit = g_hSiLimit.IntValue; - g_iTeleportCheckTime = g_hTeleportCheckTime.IntValue; - g_iEnableSIoption = g_hEnableSIoption.IntValue; - g_bAddDamageToSmoker = g_hAddDamageToSmoker.BoolValue; - g_bAutoSpawnTimeControl = g_hAutoSpawnTimeControl.BoolValue; - g_bIgnoreIncappedSurvivorSight = g_hIgnoreIncappedSurvivorSight.BoolValue; - if (g_hAllChargerMode.BoolValue) - TweakSettings(); + g_fSpawnDistanceMax = g_hSpawnDistanceMax.FloatValue; + g_fSpawnDistanceMin = g_hSpawnDistanceMin.FloatValue; + g_bTeleportSi = g_hTeleportSi.BoolValue; + //g_fTeleportDistanceMin = g_hTeleportDistance.FloatValue; + g_fSiInterval = g_hSiInterval.FloatValue; + g_iSiLimit = g_hSiLimit.IntValue; + g_iTeleportCheckTime = g_hTeleportCheckTime.IntValue; + g_iEnableSIoption = g_hEnableSIoption.IntValue; + g_bAddDamageToSmoker = g_hAddDamageToSmoker.BoolValue; + g_bAutoSpawnTimeControl = g_hAutoSpawnTimeControl.BoolValue; + g_bIgnoreIncappedSurvivorSight = g_hIgnoreIncappedSurvivorSight.BoolValue; + if(g_hAllChargerMode.BoolValue){ + TweakSettings(); + } } public Action MaxSpecialsSet(Handle timer) { - SetConVarBounds(g_hMaxPlayerZombies, ConVarBound_Upper, true, g_hSiLimit.FloatValue); - g_hMaxPlayerZombies.IntValue = g_iSiLimit; - return Plugin_Continue; + SetConVarBounds(g_hMaxPlayerZombies, ConVarBound_Upper, true, g_hSiLimit.FloatValue); + g_hMaxPlayerZombies.IntValue = g_iSiLimit; + return Plugin_Continue; } // ********************* // 事件 // ********************* -// Spitter出生重置能力 -void evt_PlayerSpawn(Event event, const char[] name, bool dont_broadcast) +//Spitter出生重置能力 +public void evt_PlayerSpawn(Event event, const char[] name, bool dont_broadcast) { - int client = GetClientOfUserId(event.GetInt("userid")); - if (IsSpitter(client)) - g_fSpitterSpitTime[client] = GetGameTime(); - else if (IsAiTank(client)) - Debug_Print("系统生成一只tank,特感总数量 %d, 真实特感数量:%d", g_iTotalSINum, GetCurrentSINum()); + int client = GetClientOfUserId(event.GetInt("userid")); + if(IsSpitter(client)) + { + g_fSpitterSpitTime[client] = GetGameTime(); + } + if(IsAiTank(client)) + { + Debug_Print("系统生成一只tank,特感总数量 %d, 真实特感数量:%d", g_iTotalSINum, GetCurrentSINum()); + } } //获取spitter口水时间 -void evt_GetSpitTime(Event event, const char[] name, bool dont_broadcast) +public void evt_GetSpitTime(Event event, const char[] name, bool dont_broadcast) { - int client = GetClientOfUserId(event.GetInt("userid")); - if (!client || !IsClientInGame(client) || !IsFakeClient(client)) - return; + int client = GetClientOfUserId(event.GetInt("userid")); + if (!client || !IsClientInGame(client) || !IsFakeClient(client)) + return; - static char ability[16]; - event.GetString("ability", ability, sizeof ability); - if (strcmp(ability, "ability_spit") == 0) - g_fSpitterSpitTime[client] = GetGameTime(); + static char ability[16]; + event.GetString("ability", ability, sizeof ability); + if (strcmp(ability, "ability_spit") == 0) + { + g_fSpitterSpitTime[client] = GetGameTime(); + } } /* 玩家受伤,增加对smoker得伤害 */ -void evt_PlayerHurt(Event event, const char[] name, bool dont_broadcast) -{ - if (!g_bAddDamageToSmoker) return; - - int victim = GetClientOfUserId(GetEventInt(event, "userid")); - int attacker = GetClientOfUserId(GetEventInt(event, "attacker")); - int damage = GetEventInt(event, "dmg_health"); - int eventhealth = GetEventInt(event, "health"); - int AddDamage = 0; - if (IsValidSurvivor(attacker) && IsInfectedBot(victim) && GetEntProp(victim, Prop_Send, "m_zombieClass") == 1) - { - if (GetEntPropEnt(victim, Prop_Send, "m_tongueVictim") > 0) - AddDamage = damage * 5; - - int health = eventhealth - AddDamage; - if (health < 1) health = 0; - - SetEntityHealth(victim, health); - SetEventInt(event, "health", health); - } +public void evt_PlayerHurt(Event event, const char[] name, bool dont_broadcast) +{ + if(g_bAddDamageToSmoker){ + int victim = GetClientOfUserId(GetEventInt(event, "userid")); + int attacker = GetClientOfUserId(GetEventInt(event, "attacker")); + int damage = GetEventInt(event, "dmg_health"); + int eventhealth = GetEventInt(event, "health"); + int AddDamage = 0; + if (IsValidSurvivor(attacker) && IsInfectedBot(victim) && GetEntProp(victim, Prop_Send, "m_zombieClass") == 1) + { + if( GetEntPropEnt(victim, Prop_Send, "m_tongueVictim") > 0 ) + { + AddDamage = damage * 5; + } + int health = eventhealth - AddDamage; + if (health < 1) + { + health = 0; + } + SetEntityHealth(victim, health); + SetEventInt(event, "health", health); + } + } } -void InitStatus() -{ - if (g_hTeleHandle != INVALID_HANDLE) - { - delete g_hTeleHandle; - g_hTeleHandle = INVALID_HANDLE; - //这里其实可以不用赋值,delete 后变量会被分配为 null,可以使用 if(g_hTeleHandle != null) 进行判断 - } - - if (g_hCheckShouldSpawnOrNot != INVALID_HANDLE) - { - delete g_hCheckShouldSpawnOrNot; - g_hCheckShouldSpawnOrNot = INVALID_HANDLE; - } - - if (g_hSpawnProcess != INVALID_HANDLE) - { - KillTimer(g_hSpawnProcess); - Debug_Print("刷特进程终止"); - g_hSpawnProcess = INVALID_HANDLE; - } +public void InitStatus(){ + if (g_hTeleHandle != INVALID_HANDLE) + { + delete g_hTeleHandle; + g_hTeleHandle = INVALID_HANDLE; + } + if (g_hCheckShouldSpawnOrNot != INVALID_HANDLE) + { + delete g_hCheckShouldSpawnOrNot; + g_hCheckShouldSpawnOrNot = INVALID_HANDLE; + } + if (g_hSpawnProcess != INVALID_HANDLE) + { + KillTimer(g_hSpawnProcess); + Debug_Print("刷特进程终止"); + g_hSpawnProcess = INVALID_HANDLE; + } + + g_bPickRushMan = false; + g_bShouldCheck = false; + g_bIsLate = false; + g_iSpawnMaxCount = 0; + g_fLastSISpawnStartTime = 0.0; + g_fUnpauseNextSpawnTime = 0.0; + aSpawnQueue.Clear(); + aTeleportQueue.Clear(); + //aSpawnNavList.Clear(); + g_iQueueIndex = 0; + g_iTeleportIndex = 0; + g_iWaveTime=0; + for(int i = 0; i <= MAXPLAYERS; i++) + { + g_fSpitterSpitTime[i] = 0.0; + } + for(int i = 0; i < 6; i++){ + g_iSINum[i] =0; + } +} - g_bPickRushMan = false; - g_bShouldCheck = false; - g_bIsLate = false; - g_iSpawnMaxCount = 0; - g_fLastSISpawnStartTime = 0.0; - g_fUnpauseNextSpawnTime = 0.0; - aSpawnQueue.Clear(); - aTeleportQueue.Clear(); - // aSpawnNavList.Clear(); - g_iQueueIndex = 0; - g_iTeleportIndex = 0; - g_iWaveTime = 0; - for (int i = 0; i <= MAXPLAYERS; i++) - g_fSpitterSpitTime[i] = 0.0; - - for (int i = 0; i < 6; i++) - g_iSINum[i] = 0; -} - -void StopSpawn() -{ - InitStatus(); +public void StopSpawn(){ + InitStatus(); } -void evt_RoundStart(Event event, const char[] name, bool dontBroadcast) +public void evt_RoundStart(Event event, const char[] name, bool dontBroadcast) { - InitStatus(); - CreateTimer(0.1, MaxSpecialsSet); - CreateTimer(1.0, SafeRoomReset, _, TIMER_FLAG_NO_MAPCHANGE); + InitStatus(); + CreateTimer(0.1, MaxSpecialsSet); + CreateTimer(1.0, SafeRoomReset, _, TIMER_FLAG_NO_MAPCHANGE); } -void evt_RoundEnd(Event event, const char[] name, bool dontBroadcast) +public void evt_RoundEnd(Event event, const char[] name, bool dontBroadcast) { - InitStatus(); + InitStatus(); } -void evt_PlayerDeath(Event event, const char[] name, bool dontBroadcast) +public void evt_PlayerDeath(Event event, const char[] name, bool dontBroadcast) { - int client = GetClientOfUserId(event.GetInt("userid")); - if (!IsInfectedBot(client)) return; - - int type = GetEntProp(client, Prop_Send, "m_zombieClass"); - //防止无声口水 - if (type != ZC_SPITTER || g_bSIPoolAvailable) // 使用SIPool后不会出现无声口水 - CreateTimer(0.5, Timer_KickBot, client); - - if (type >= 1 && type <= 6) - { - if (g_iSINum[type - 1] > 0) g_iSINum[type - 1]--; - else g_iSINum[type - 1] = 0; - - if (g_iTotalSINum > 0) g_iTotalSINum--; - else g_iTotalSINum = 0; - - Debug_Print("杀死%N,特感总数和该种类特感数量减1分别为%d %d", client, g_iTotalSINum, g_iSINum[type - 1]); - } - g_iTeleCount[client] = 0; + int client = GetClientOfUserId(event.GetInt("userid")); + if (IsInfectedBot(client)) + { + int type = GetEntProp(client, Prop_Send, "m_zombieClass"); + //防止无声口水 + if (type != ZC_SPITTER) + { + CreateTimer(0.5, Timer_KickBot, client); + } + if(type >= 1 && type <=6){ + if(g_iSINum[type - 1] > 0) + { + g_iSINum[type - 1] --; + } + else + { + g_iSINum[type - 1] = 0; + } + if(g_iTotalSINum > 0) + { + g_iTotalSINum --; + } + else + { + g_iTotalSINum = 0; + } + Debug_Print("杀死%N,特感总数和该种类特感数量减1分别为%d %d", client, g_iTotalSINum, g_iSINum[type - 1]); + } + g_iTeleCount[client] = 0; + } } -Action Timer_KickBot(Handle timer, int client) +public Action Timer_KickBot(Handle timer, int client) { - if (IsClientInGame(client) && !IsClientInKickQueue(client) && IsFakeClient(client)) - { - Debug_Print("踢出特感%N", client); - if (g_bSIPoolAvailable) - g_hSIPool.ReturnSIBot(client); - else KickClient(client, "You are worthless and was kicked by console"); - - return Plugin_Stop; - } - return Plugin_Continue; + if (IsClientInGame(client) && !IsClientInKickQueue(client) && IsFakeClient(client)) + { + Debug_Print("踢出特感%N",client); + KickClient(client, "You are worthless and was kicked by console"); + } + return Plugin_Continue; } // ********************* @@ -494,256 +477,265 @@ Action Timer_KickBot(Handle timer, int client) // ********************* public void OnGameFrame() { - // 根据情况动态调整 z_maxplayers_zombie 数值 - if (g_iSiLimit > g_hMaxPlayerZombies.IntValue) - CreateTimer(0.1, MaxSpecialsSet); - - if (g_iTeleportIndex <= 0 && g_iQueueIndex < g_iSiLimit) - { - int zombieclass = 0; - if (g_hAllChargerMode.BoolValue) - zombieclass = 6; - else if (g_hAllHunterMode.BoolValue) - zombieclass = 3; - else zombieclass = GetRandomInt(1, 6); - - if (zombieclass != 0 && MeetRequire(zombieclass) && !HasReachedLimit(zombieclass) && g_iQueueIndex < g_iSiLimit) - { - //这里增加一些boomer和spitter生成的判定,让boomer和spitter比较晚生成 - aSpawnQueue.Push(g_iQueueIndex); - aSpawnQueue.Set(g_iQueueIndex, zombieclass, 0, false); - g_ArraySIlimit[zombieclass - 1] -= 1; - g_iQueueIndex += 1; - Debug_Print("<刷特队列> 当前入队特感:%s,当前队列长度:%d,当前队列索引位置:%d", InfectedName[zombieclass], aSpawnQueue.Length, g_iQueueIndex); - } - } - - if (g_bIsLate) - { - /* - // 当nav存储长度超过特感生成上限时,删去第一个 - if (aSpawnNavList.Length > g_iSiLimit) - { - //Debug_Print(" 当前队列长度:%d, 超过特感上限,清除队列第一个元素", aSpawnNavList.Length); - aSpawnNavList.Erase(0); - } - */ - if (g_iTotalSINum < g_iSiLimit) - { - if (g_iTeleportIndex > 0) - { - g_iTargetSurvivor = GetTargetSurvivor(); - if (g_fTeleportDistance < g_fSpawnDistanceMax) - g_fTeleportDistance += 20.0; - - float fSpawnPos[3] = { 0.0 }; - if (GetSpawnPos(fSpawnPos, g_iTargetSurvivor, g_fTeleportDistance, true)) - { - int iZombieClass = aTeleportQueue.Get(0); - if (!(iZombieClass >= 1 && iZombieClass <= 6)) - { - Debug_Print("特感类型读取错误,读取的特感类型为:%d", iZombieClass); - aTeleportQueue.Erase(0); - g_iTeleportIndex -= 1; - return; - } - - if (SpawnInfected(fSpawnPos, g_fTeleportDistance, iZombieClass, true)) - { - g_iSINum[iZombieClass - 1] += 1; - g_iTotalSINum += 1; - if (aTeleportQueue.Length > 0 && g_iTeleportIndex > 0) - { - aTeleportQueue.Erase(0); - g_iTeleportIndex -= 1; - } - print_type(iZombieClass, g_fSpawnDistance, true); - } - else if (g_iTeleportIndex <= 0) - { - aTeleportQueue.Clear(); - g_iTeleportIndex = 0; - } - } - } - - Debug_Print("spawn_max:%d, tpidx:%d, queue_idx:%d", g_iSpawnMaxCount, g_iTeleportIndex, g_iQueueIndex); - //传送队列优先处理,防止普通刷特刷出来把特感数量刷满了 - if (g_iSpawnMaxCount > 0 && g_iTeleportIndex <= 0 && g_iQueueIndex > 0) - { - g_iTargetSurvivor = GetTargetSurvivor(); - if (g_fSpawnDistance < g_fSpawnDistanceMax) - g_fSpawnDistance += 5.0; - - float fSpawnPos[3] = { 0.0 }; - if (GetSpawnPos(fSpawnPos, g_iTargetSurvivor, g_fSpawnDistance)) - { - int iZombieClass = aSpawnQueue.Get(0); - if (SpawnInfected(fSpawnPos, g_fSpawnDistance, iZombieClass)) - { - g_iSpawnMaxCount -= 1; - g_iSINum[iZombieClass - 1] += 1; - g_iTotalSINum += 1; - if (aSpawnQueue.Length > 0 && g_iQueueIndex > 0) - { - aSpawnQueue.Erase(0); - g_iQueueIndex -= 1; - //刷出来之后要求特感激进进攻 - BypassAndExecuteCommand("nb_assault"); - } - print_type(iZombieClass, g_fSpawnDistance); - } - else - { - if (HasReachedLimit(iZombieClass)) - ReachedLimit(iZombieClass); - - if (g_iQueueIndex <= 0) - { - aSpawnQueue.Clear(); - g_iQueueIndex = 0; - } - } - } - } - } - } + // 根据情况动态调整 z_maxplayers_zombie 数值 + if (g_iSiLimit > g_hMaxPlayerZombies.IntValue) + { + CreateTimer(0.1, MaxSpecialsSet); + } + if (g_iTeleportIndex <= 0 && g_iQueueIndex < g_iSiLimit) + { + int zombieclass = 0; + if(g_hAllChargerMode.BoolValue){ + zombieclass = 6; + } + else if(g_hAllHunterMode.BoolValue){ + zombieclass = 3; + }else{ + zombieclass = GetRandomInt(1,6); + } + if (zombieclass != 0 && MeetRequire(zombieclass) && !HasReachedLimit(zombieclass) && g_iQueueIndex < g_iSiLimit) + { + //这里增加一些boomer和spitter生成的判定,让bommer和spitter比较晚生成 + aSpawnQueue.Push(g_iQueueIndex); + aSpawnQueue.Set(g_iQueueIndex, zombieclass, 0, false); + g_ArraySIlimit[zombieclass - 1] -= 1; + g_iQueueIndex += 1; + //Debug_Print("<刷特队列> 当前入队特感:%s,当前队列长度:%d,当前队列索引位置:%d", InfectedName[zombieclass], aSpawnQueue.Length, g_iQueueIndex); + } + } + if (g_bIsLate) + { + /* + // 当nav存储长度超过特感生成上限时,删去第一个 + if (aSpawnNavList.Length > g_iSiLimit) + { + //Debug_Print(" 当前队列长度:%d, 超过特感上限,清除队列第一个元素", aSpawnNavList.Length); + aSpawnNavList.Erase(0); + } + */ + if (g_iTotalSINum < g_iSiLimit) + { + if(g_iTeleportIndex > 0) + { + g_iTargetSurvivor = GetTargetSurvivor(); + if(g_fTeleportDistance < g_fSpawnDistanceMax) + { + g_fTeleportDistance += 20.0; + } + float fSpawnPos[3] = {0.0}; + if(GetSpawnPos(fSpawnPos, g_iTargetSurvivor, g_fTeleportDistance, true)) + { + int iZombieClass = aTeleportQueue.Get(0); + if(!(iZombieClass >= 1 && iZombieClass <= 6)) + { + Debug_Print("特感类型读取错误,读取的特感类型为:%d", iZombieClass); + aTeleportQueue.Erase(0); + g_iTeleportIndex -= 1; + return; + } + if(SpawnInfected(fSpawnPos, g_fTeleportDistance, iZombieClass, true)){ + g_iSINum[iZombieClass - 1] += 1; + g_iTotalSINum += 1; + if (aTeleportQueue.Length > 0 && g_iTeleportIndex > 0) + { + aTeleportQueue.Erase(0); + g_iTeleportIndex -= 1; + } + print_type(iZombieClass, g_fSpawnDistance, true); + } + else + { + if (g_iTeleportIndex <= 0) + { + aTeleportQueue.Clear(); + g_iTeleportIndex = 0; + } + } + } + } + //传送队列优先处理,防止普通刷特刷出来把特感数量刷满了 + if(g_iSpawnMaxCount > 0 && g_iTeleportIndex <= 0 && g_iQueueIndex > 0) + { + g_iTargetSurvivor = GetTargetSurvivor(); + if(g_fSpawnDistance < g_fSpawnDistanceMax) + { + g_fSpawnDistance += 5.0; + } + float fSpawnPos[3] = {0.0}; + if(GetSpawnPos(fSpawnPos, g_iTargetSurvivor, g_fSpawnDistance)) { + int iZombieClass = aSpawnQueue.Get(0); + if(SpawnInfected(fSpawnPos, g_fSpawnDistance, iZombieClass)){ + g_iSpawnMaxCount -= 1; + g_iSINum[iZombieClass - 1] += 1; + g_iTotalSINum += 1; + if (aSpawnQueue.Length > 0 && g_iQueueIndex > 0) + { + aSpawnQueue.Erase(0); + g_iQueueIndex -= 1; + //刷出来之后要求特感激进进攻 + BypassAndExecuteCommand("nb_assault"); + } + print_type(iZombieClass, g_fSpawnDistance); + } + else + { + if (HasReachedLimit(iZombieClass)) + { + ReachedLimit(iZombieClass); + } + if (g_iQueueIndex <= 0) + { + aSpawnQueue.Clear(); + g_iQueueIndex = 0; + } + } + } + } + } + } } -stock bool GetSpawnPos(float fSpawnPos[3], int TargetSurvivor, float SpawnDistance, bool IsTeleport = false) -{ - if (!IsValidClient(TargetSurvivor)) return false; - - float fSurvivorPos[3], fDirection[3], fEndPos[3], fMins[3], fMaxs[3]; - // 根据指定生还者坐标,拓展刷新范围 - GetClientEyePosition(TargetSurvivor, fSurvivorPos); - //增加高度,增加刷房顶的几率 - if (SpawnDistance < 500.0) - fMaxs[2] = fSurvivorPos[2] + 800.0; - else fMaxs[2] = fSurvivorPos[2] + SpawnDistance + 300.0; - - fMins[0] = fSurvivorPos[0] - SpawnDistance; - fMaxs[0] = fSurvivorPos[0] + SpawnDistance; - fMins[1] = fSurvivorPos[1] - SpawnDistance; - fMaxs[1] = fSurvivorPos[1] + SpawnDistance; - fMaxs[2] = fSurvivorPos[2] + SpawnDistance; - // 规定射线方向 - fDirection[0] = 90.0; - fDirection[1] = fDirection[2] = 0.0; - // 随机刷新位置 - fSpawnPos[0] = GetRandomFloat(fMins[0], fMaxs[0]); - fSpawnPos[1] = GetRandomFloat(fMins[1], fMaxs[1]); - fSpawnPos[2] = GetRandomFloat(fSurvivorPos[2], fMaxs[2]); - // 找位条件,可视,是否在有效 NavMesh,是否卡住,否则先会判断是否在有效 Mesh 与是否卡住导致某些位置刷不出特感 - int count2 = 0; - //生成的时候只能在有跑男情况下才特意生成到幸存者前方 - while (PlayerVisibleToSDK(fSpawnPos, IsTeleport) || !IsOnValidMesh(fSpawnPos) || IsPlayerStuck(fSpawnPos) || ((g_bPickRushMan || IsTeleport) && !Is_Pos_Ahead(fSpawnPos, g_iTargetSurvivor))) - { - count2++; - if (count2 > 20) - { - return false; - } - fSpawnPos[0] = GetRandomFloat(fMins[0], fMaxs[0]); - fSpawnPos[1] = GetRandomFloat(fMins[1], fMaxs[1]); - fSpawnPos[2] = GetRandomFloat(fSurvivorPos[2], fMaxs[2]); - TR_TraceRay(fSpawnPos, fDirection, MASK_PLAYERSOLID, RayType_Infinite); - if (TR_DidHit()) - { - TR_GetEndPosition(fEndPos); - fSpawnPos = fEndPos; - fSpawnPos[2] += NAV_MESH_HEIGHT; - } - } - return true; +stock bool GetSpawnPos(float fSpawnPos[3], int TargetSurvivor, float SpawnDistance, bool IsTeleport = false){ + if(IsValidClient(TargetSurvivor)){ + float fSurvivorPos[3] = {0.0}, fDirection[3] = {0.0}, fEndPos[3] = {0.0}, fMins[3] = {0.0}, fMaxs[3] = {0.0}; + // 根据指定生还者坐标,拓展刷新范围 + GetClientEyePosition(TargetSurvivor, fSurvivorPos); + //增加高度,增加刷房顶的几率 + if(SpawnDistance < 500.0) + { + fMaxs[2] = fSurvivorPos[2] + 800.0; + } + else + { + fMaxs[2] = fSurvivorPos[2] + SpawnDistance + 300.0; + } + fMins[0] = fSurvivorPos[0] - SpawnDistance; + fMaxs[0] = fSurvivorPos[0] + SpawnDistance; + fMins[1] = fSurvivorPos[1] - SpawnDistance; + fMaxs[1] = fSurvivorPos[1] + SpawnDistance; + fMaxs[2] = fSurvivorPos[2] + SpawnDistance; + // 规定射线方向 + fDirection[0] = 90.0; + fDirection[1] = fDirection[2] = 0.0; + // 随机刷新位置 + fSpawnPos[0] = GetRandomFloat(fMins[0], fMaxs[0]); + fSpawnPos[1] = GetRandomFloat(fMins[1], fMaxs[1]); + fSpawnPos[2] = GetRandomFloat(fSurvivorPos[2], fMaxs[2]); + // 找位条件,可视,是否在有效 NavMesh,是否卡住,否则先会判断是否在有效 Mesh 与是否卡住导致某些位置刷不出特感 + int count2=0; + //生成的时候只能在有跑男情况下才特意生成到幸存者前方 + while (PlayerVisibleToSDK(fSpawnPos, IsTeleport) || !IsOnValidMesh(fSpawnPos) || IsPlayerStuck(fSpawnPos) || ((g_bPickRushMan || IsTeleport) && !Is_Pos_Ahead(fSpawnPos, g_iTargetSurvivor))) + { + count2++; + if(count2 > 20) + { + return false; + } + fSpawnPos[0] = GetRandomFloat(fMins[0], fMaxs[0]); + fSpawnPos[1] = GetRandomFloat(fMins[1], fMaxs[1]); + fSpawnPos[2] = GetRandomFloat(fSurvivorPos[2], fMaxs[2]); + TR_TraceRay(fSpawnPos, fDirection, MASK_PLAYERSOLID, RayType_Infinite); + if(TR_DidHit()) + { + TR_GetEndPosition(fEndPos); + fSpawnPos = fEndPos; + fSpawnPos[2] += NAV_MESH_HEIGHT; + } + } + return true; + } + return false; } /* stock bool Is_Nav_already_token(Address nav) { - for(int i = 0; i < aSpawnNavList.Length; i++) - { - if(nav == aSpawnNavList.Get(i)) - return true; - } - return false; + for(int i = 0; i < aSpawnNavList.Length; i++) + { + if(nav == aSpawnNavList.Get(i)) + return true; + } + return false; } */ stock bool SpawnInfected(float fSpawnPos[3], float SpawnDistance, int iZombieClass, bool IsTeleport = false) { - float fSurvivorPos[3]; - Debug_Print("生还者看不到"); - // 生还数量为 4,循环 4 次,检测此位置到生还的距离是否小于 750 是则刷特,此处可以刷新 1 ~ g_iSiLimit 只特感,如果此处刷完,则上面的 SpawnSpecial 将不再刷特 - for (int count = 0; count < g_iSurvivorNum; count++) - { - int index = g_iSurvivors[count]; - //不是有效生还者不生成 - if (!IsValidSurvivor(index)) - continue; - - //生还者倒地或者挂边,也不生成 - if (IsClientIncapped(index)) - continue; - - //非跑男模式目标已满,跳过 - if (g_bTargetSystemAvailable && !g_bPickRushMan && IsClientReachLimit(index)) - continue; - - GetClientEyePosition(index, fSurvivorPos); - fSurvivorPos[2] -= 60.0; - //获取nav地址 - Address nav1 = L4D_GetNearestNavArea(fSpawnPos, 120.0, false, false, false, TEAM_INFECTED); - Address nav2 = L4D_GetNearestNavArea(fSurvivorPos, 120.0, false, false, false, TEAM_INFECTED); - - //这一段是对高处生成位置进行的补偿 - float distance; - if (IsTeleport) - distance = g_fTeleportDistance; - else - distance = g_fSpawnDistance; - - if (distance * (NORMALPOSMULT - 1) <= 250.0) - distance += 250.0; - else - distance *= NORMALPOSMULT; - - if (fSpawnPos[2] - fSurvivorPos[2] > HIGHERPOS) - distance += HIGHERPOSADDDISTANCE; - - // nav1 和 nav2 必须有网格相连的路,并且生成距离大于distance,增加不能是同nav网格的要求 - if (L4D2_NavAreaBuildPath(nav1, nav2, distance, TEAM_INFECTED, false) && GetVectorDistance(fSurvivorPos, fSpawnPos, true) >= Pow(g_fSpawnDistanceMin, 2.0) && nav1 != nav2) - { - if (iZombieClass > 0 && !HasReachedLimit(iZombieClass) && CheckSIOption(iZombieClass)) - { - if (IsTeleport && g_iTeleportIndex <= 0) - return false; - - if (!IsTeleport && g_iSpawnMaxCount <= 0) - return false; - - int entityindex; - if (g_bSIPoolAvailable) - entityindex = g_hSIPool.RequestSIBot(iZombieClass, fSpawnPos); - else entityindex = L4D2_SpawnSpecial(iZombieClass, fSpawnPos, view_as({ 0.0, 0.0, 0.0 })); - - Debug_Print("请求%d特感,生成:%d", iZombieClass, entityindex); - if (IsValidEntity(entityindex) && IsValidEdict(entityindex)) - { - // aSpawnNavList.Push(nav1); - // Debug_Print(" 当前入队nav:%d,当前队列长度:%d", nav1, aSpawnNavList.Length); - if (IsInfectedBot(entityindex) && IsPlayerAlive(entityindex)) - return true; - else - { - Debug_Print("生成错误"); - RemoveEntity(entityindex); - return false; - } - } - } - } - } - return false; + + float fSurvivorPos[3]; + //Debug_Print("生还者看不到"); + // 生还数量为 4,循环 4 次,检测此位置到生还的距离是否小于 750 是则刷特,此处可以刷新 1 ~ g_iSiLimit 只特感,如果此处刷完,则上面的 SpawnSpecial 将不再刷特 + for (int count = 0; count < g_iSurvivorNum; count++) + { + int index = g_iSurvivors[count]; + //不是有效生还者不生成 + if(!IsValidSurvivor(index)) + continue; + + //生还者倒地或者挂边,也不生成 + if(IsClientIncapped(index)){ + continue; + } + //非跑男模式目标已满,跳过 + if(g_bTargetSystemAvailable && !g_bPickRushMan && IsClientReachLimit(index)) + { + continue; + } + GetClientEyePosition(index, fSurvivorPos); + fSurvivorPos[2] -= 60.0; + //获取nav地址 + Address nav1 = L4D_GetNearestNavArea(fSpawnPos, 120.0, false, false, false, TEAM_INFECTED); + Address nav2 = L4D_GetNearestNavArea(fSurvivorPos, 120.0, false, false, false, TEAM_INFECTED); + + //这一段是对高处生成位置进行的补偿 + float distance; + if(IsTeleport) + { + distance = g_fTeleportDistance; + }else + { + distance = g_fSpawnDistance; + } + if(distance * (NORMALPOSMULT - 1) <= 250.0) + { + distance += 250.0; + } + else + { + distance *= NORMALPOSMULT; + } + if(fSpawnPos[2] - fSurvivorPos[2] > HIGHERPOS) + { + distance += HIGHERPOSADDDISTANCE; + } + //nav1 和 nav2 必须有网格相连的路,并且生成距离大于distance,增加不能是同nav网格的要求 + if (L4D2_NavAreaBuildPath(nav1, nav2, distance, TEAM_INFECTED, false) && GetVectorDistance(fSurvivorPos, fSpawnPos, true) >= Pow(g_fSpawnDistanceMin, 2.0) && nav1 != nav2) + { + if (iZombieClass > 0 && !HasReachedLimit(iZombieClass) && CheckSIOption(iZombieClass)) + { + if(IsTeleport && g_iTeleportIndex <= 0){ + return false; + } + if(!IsTeleport && g_iSpawnMaxCount <= 0){ + return false; + } + int entityindex = L4D2_SpawnSpecial(iZombieClass, fSpawnPos, view_as({0.0, 0.0, 0.0})); + if (IsValidEntity(entityindex) && IsValidEdict(entityindex)) + { + //aSpawnNavList.Push(nav1); + //Debug_Print(" 当前入队nav:%d,当前队列长度:%d", nav1, aSpawnNavList.Length); + if(IsInfectedBot(entityindex) && IsPlayerAlive(entityindex)) + return true; + else + { + Debug_Print("生成错误"); + RemoveEntity(entityindex); + return false; + } + } + } + } + } + return false; } // 当前在场的某种特感种类数量达到 Cvar 限制,但因为刷新一个特感,出队此元素,之后再入队相同特感元素,则会刷不出来,需要处理重复情况,如果队列长度大于 1 且索引大于 0,说明队列存在 @@ -751,238 +743,255 @@ stock bool SpawnInfected(float fSpawnPos[3], float SpawnDistance, int iZombieCla // 如:当前存在 2 个 Smoker 未死亡,Smoker 的 Cvar 限制为 2 ,这时入队一个 Smoker 元素,则会导致无法刷出特感 void ReachedLimit(int type) { - if (aSpawnQueue.Length > 1 && g_iQueueIndex > 0) - { - Debug_Print("%s上限已到,无法生成,且队列不为空,删除第一个队列元素", InfectedName[type]); - aSpawnQueue.Erase(0); - g_iQueueIndex -= 1; - } - else - for (int i = 1; i <= 6; i++) - if (CheckSIOption(i) && !HasReachedLimit(i)) - { - Debug_Print("%s上限已到,无法生成,当前队列为空,遍历1-6类型发现%s类型未满", InfectedName[type], InfectedName[i]); - aSpawnQueue.Set(0, i, 0, false); - } + if (aSpawnQueue.Length > 1 && g_iQueueIndex > 0) + { + Debug_Print("%s上限已到,无法生成,且队列不为空,删除第一个队列元素", InfectedName[type]); + aSpawnQueue.Erase(0); + g_iQueueIndex -= 1; + } + else + { + for (int i = 1; i <= 6; i++) + { + if (CheckSIOption(i) && !HasReachedLimit(i)) + { + Debug_Print("%s上限已到,无法生成,当前队列为空,遍历1-6类型发现%s类型未满", InfectedName[type], InfectedName[i]); + aSpawnQueue.Set(0, i, 0, false); + } + } + } } -int CheckSIOption(int type) -{ +public int CheckSIOption(int type){ switch (type) { case 1: + { return ENABLE_SMOKER & g_iEnableSIoption; + } case 2: + { return ENABLE_BOOMER & g_iEnableSIoption; + } case 3: + { return ENABLE_HUNTER & g_iEnableSIoption; + } case 4: + { return ENABLE_SPITTER & g_iEnableSIoption; + } case 5: + { return ENABLE_JOCKEY & g_iEnableSIoption; + } case 6: + { return ENABLE_CHARGER & g_iEnableSIoption; + } } return 0; } + // 当前某种特感数量是否达到 Convar 值限制 bool HasReachedLimit(int zombieclass) { - int count = 0; - static char convar[16]; - for (int infected = 1; infected <= MaxClients; infected++) - if (IsClientConnected(infected) && IsClientInGame(infected) && !IsPlayerAlive(infected) - && GetEntProp(infected, Prop_Send, "m_zombieClass") == zombieclass) - count += 1; - - if ((g_hAllChargerMode.BoolValue || g_hAllHunterMode.BoolValue) && count == g_iSiLimit) - return true; - else if ((g_hAllChargerMode.BoolValue || g_hAllHunterMode.BoolValue) && count < g_iSiLimit) - return false; - - FormatEx(convar, sizeof(convar), "z_%s_limit\0", InfectedName[zombieclass]); - // if (count == GetConVarInt(FindConVar(convar))) - // { - // return true; - // } - // else - // { - // return false; - // } - return count == GetConVarInt(FindConVar(convar)); -} - -void print_type(int iType, float SpawnDistance, bool Isteleport = false) -{ - if (iType >= 1 && iType <= 6) - { - Debug_Print(" %s生成一只%s,当前%s数量:%d,特感总数量 %d, 真实特感数量:%d, 找位最大单位距离:%f", Isteleport ? "传送" : "", InfectedName[iType], InfectedName[iType], g_iSINum[iType - 1], g_iTotalSINum, GetCurrentSINum(), SpawnDistance); - } -} - -// 初始 & 动态刷特时钟 -Action SpawnFirstInfected(Handle timer) -{ - if (!g_bIsLate) - { - g_bIsLate = true; - //首先触发一次刷特,然后每1s检测 - g_hCheckShouldSpawnOrNot = CreateTimer(1.0, CheckShouldSpawnOrNot, _, TIMER_REPEAT); - SpawnInfectedSettings(); - if (g_bTeleportSi) - g_hTeleHandle = CreateTimer(1.0, Timer_PositionSi, _, TIMER_REPEAT); - } - return Plugin_Stop; + int count = 0; char convar[16] = {'\0'}; + for (int infected = 1; infected <= MaxClients; infected++) + { + if (IsClientConnected(infected) && IsClientInGame(infected) && GetEntProp(infected, Prop_Send, "m_zombieClass") == zombieclass) + { + count += 1; + } + } + if((g_hAllChargerMode.BoolValue || g_hAllHunterMode.BoolValue) && count == g_iSiLimit){ + return true; + } + else if((g_hAllChargerMode.BoolValue || g_hAllHunterMode.BoolValue) && count < g_iSiLimit){ + return false; + } + FormatEx(convar, sizeof(convar), "z_%s_limit", InfectedName[zombieclass]); + if (count == GetConVarInt(FindConVar(convar))) + { + return true; + } + else + { + return false; + } } -Action SpawnNewInfected(Handle timer) -{ - SpawnInfectedSettings(); - g_hSpawnProcess = INVALID_HANDLE; - return Plugin_Stop; +stock void print_type(int iType, float SpawnDistance, bool Isteleport = false){ + if (iType >= 1 && iType <=6) + { + Debug_Print(" %s生成一只%s,当前%s数量:%d,特感总数量 %d, 真实特感数量:%d, 找位最大单位距离:%f", Isteleport?"传送":"", InfectedName[iType], InfectedName[iType], g_iSINum[iType -1], g_iTotalSINum, GetCurrentSINum(), SpawnDistance); + } } -void SpawnInfectedSettings() -{ - if (g_bIsLate) - { - g_iSurvivorNum = 0; - g_iLastSpawnTime = 0; - for (int client = 1; client <= MaxClients; client++) - if (IsValidSurvivor(client) && IsPlayerAlive(client)) - { - g_iSurvivors[g_iSurvivorNum] = client; - g_iSurvivorNum += 1; - } - - g_fSpawnDistance = g_fSpawnDistanceMin; - /* - //优化性能,每波刷新前清除aSpawnNavList队列中的值,但是如果刷特时间很短,这个优化估计起的作用不大 - if(g_iSpawnMaxCount == 0) - { - aSpawnNavList.Clear(); - } - */ - - g_iSpawnMaxCount += g_iSiLimit; - g_bShouldCheck = true; - g_iWaveTime++; - Debug_Print("开始第%d波刷特", g_iWaveTime); +// 初始 & 动态刷特时钟 +public Action SpawnFirstInfected(Handle timer) +{ + if (!g_bIsLate) + { + g_bIsLate = true; + //首先触发一次刷特,然后每1s检测 + g_hCheckShouldSpawnOrNot = CreateTimer(1.0, CheckShouldSpawnOrNot, _, TIMER_REPEAT); + SpawnInfectedSettings(); + if (g_bTeleportSi) + { + g_hTeleHandle = CreateTimer(1.0, Timer_PositionSi, _, TIMER_REPEAT); + } + } + return Plugin_Stop; +} + +public Action SpawnNewInfected(Handle timer) +{ + SpawnInfectedSettings(); + g_hSpawnProcess = INVALID_HANDLE; + return Plugin_Stop; +} + +public void SpawnInfectedSettings() +{ + if (g_bIsLate) + { + g_iSurvivorNum = 0; + g_iLastSpawnTime = 0; + for (int client = 1; client <= MaxClients; client++) + { + if (IsValidSurvivor(client) && IsPlayerAlive(client)) + { + g_iSurvivors[g_iSurvivorNum] = client; + g_iSurvivorNum += 1; + } + } + g_fSpawnDistance = g_fSpawnDistanceMin; + /* + //优化性能,每波刷新前清除aSpawnNavList队列中的值,但是如果刷特时间很短,这个优化估计起的作用不大 + if(g_iSpawnMaxCount == 0) + { + aSpawnNavList.Clear(); + } + */ + + g_iSpawnMaxCount += g_iSiLimit; + g_bShouldCheck = true; + g_iWaveTime++; + Debug_Print("开始第%d波刷特", g_iWaveTime); + + // 当一定时间内刷不出特感,触发时钟使 g_iSpawnMaxCount 超过 g_iSiLimit 值时,最多允许刷出 g_iSiLimit + 2 只特感,防止连续刷 2-3 波的情况 + if (g_iSiLimit < g_iSpawnMaxCount) + { + + g_iSpawnMaxCount = g_iSiLimit; + Debug_Print("当前特感数量达到上限"); + } - // 当一定时间内刷不出特感,触发时钟使 g_iSpawnMaxCount 超过 g_iSiLimit 值时,最多允许刷出 g_iSiLimit + 2 只特感,防止连续刷 2-3 波的情况 - if (g_iSiLimit < g_iSpawnMaxCount) - { - g_iSpawnMaxCount = g_iSiLimit; - Debug_Print("当前特感数量达到上限"); - } - } + } } public void OnUnpause() { - if (g_hSpawnProcess == INVALID_HANDLE) - { - Debug_Print("解除暂停,原先一波刷特进程已经在处理,下一波刷特是%.2f秒后", g_fUnpauseNextSpawnTime); - g_hSpawnProcess = CreateTimer(g_fUnpauseNextSpawnTime, SpawnNewInfected, _, TIMER_REPEAT); - } + if(g_hSpawnProcess == INVALID_HANDLE) + { + Debug_Print("解除暂停,原先一波刷特进程已经在处理,下一波刷特是%.2f秒后", g_fUnpauseNextSpawnTime); + g_hSpawnProcess = CreateTimer(g_fUnpauseNextSpawnTime, SpawnNewInfected, _, TIMER_REPEAT); + } } -Action CheckShouldSpawnOrNot(Handle timer) +public Action CheckShouldSpawnOrNot(Handle timer) { - if (IsInPause()) - { - Debug_Print("处于暂停状态,停止刷特"); - if (g_hSpawnProcess != INVALID_HANDLE) - { - g_fUnpauseNextSpawnTime = g_fSiInterval - (GetGameTime() - g_fLastSISpawnStartTime); - KillTimer(g_hSpawnProcess); - g_hSpawnProcess = INVALID_HANDLE; - } - return Plugin_Continue; - } - - g_iLastSpawnTime++; - if (!g_bIsLate) return Plugin_Stop; - if (!g_bShouldCheck && g_hSpawnProcess != INVALID_HANDLE) return Plugin_Continue; - if (FindConVar("survivor_limit").IntValue >= 2 && IsAnyTankOrAboveHalfSurvivorDownOrDied() && g_iLastSpawnTime < RoundToFloor(g_fSiInterval / 2)) return Plugin_Continue; - //防止0s情况下spitter无法快速踢出导致的特感越刷越少问题 - if (g_iEnableSIoption & ENABLE_SPITTER && g_iLastSpawnTime < 4 && !g_bSIPoolAvailable) // 使用 SIPool 后无此问题 - { - Debug_Print("因为可以刷spitter,所以最低4秒起刷,不然容易造成特感数量统计错误,特感生成不出来"); - return Plugin_Continue; - } - if (!g_bAutoSpawnTimeControl) - { - g_bShouldCheck = false; - if (g_iSpawnMaxCount == g_iSiLimit) - { - Debug_Print("固定增时系统因为等待刷特数量达到上限,暂停刷特, 总用时:%.1f秒", g_iLastSpawnTime + g_fSiInterval); - g_iLastSpawnTime = 0; - } - else - { - Debug_Print("固定增时系统开始新一波刷特, 总用时:%.1f秒", g_iLastSpawnTime + g_fSiInterval); - g_hSpawnProcess = CreateTimer(g_fSiInterval * 1.5, SpawnNewInfected, _, TIMER_REPEAT); - } - } - else if ((IsAllKillersDown() && g_iSpawnMaxCount == 0) || (g_iTotalSINum <= (RoundToFloor(g_iSiLimit / 4.0) + 1) && g_iSpawnMaxCount == 0) || (g_iLastSpawnTime >= g_fSiInterval * 0.5)) - { - g_bShouldCheck = false; - if (g_iSpawnMaxCount == g_iSiLimit) - { - Debug_Print("自动增时系统因为等待刷特数量达到上限,暂停刷特, 总用时:%.1f秒", g_iLastSpawnTime + g_fSiInterval); - g_iLastSpawnTime = 0; - } - else - { - Debug_Print("自动增时系统开始新一波刷特, 总用时:%.1f秒", g_iLastSpawnTime + g_fSiInterval); - g_hSpawnProcess = CreateTimer(g_fSiInterval, SpawnNewInfected, _, TIMER_REPEAT); - } - } - g_fLastSISpawnStartTime = GetGameTime(); - return Plugin_Continue; + if(IsInPause()) + { + Debug_Print("处于暂停状态,停止刷特"); + if(g_hSpawnProcess != INVALID_HANDLE) + { + g_fUnpauseNextSpawnTime = g_fSiInterval - (GetGameTime() - g_fLastSISpawnStartTime); + KillTimer(g_hSpawnProcess); + g_hSpawnProcess = INVALID_HANDLE; + } + return Plugin_Continue; + } + g_iLastSpawnTime ++; + if(!g_bIsLate) return Plugin_Stop; + if(!g_bShouldCheck && g_hSpawnProcess != INVALID_HANDLE) return Plugin_Continue; + if(FindConVar("survivor_limit").IntValue >= 2 && IsAnyTankOrAboveHalfSurvivorDownOrDied() && g_iLastSpawnTime < RoundToFloor(g_fSiInterval / 2)) return Plugin_Continue; + //防止0s情况下spitter无法快速踢出导致的特感越刷越少问题 + if(g_iEnableSIoption & ENABLE_SPITTER && g_iLastSpawnTime < 4) {Debug_Print("因为可以刷spitter,所以最低4秒起刷,不然容易造成特感数量统计错误,特感生成不出来");return Plugin_Continue;} + if(!g_bAutoSpawnTimeControl) + { + g_bShouldCheck = false; + if(g_iSpawnMaxCount == g_iSiLimit) + { + Debug_Print("固定增时系统因为等待刷特数量达到上限,暂停刷特, 总用时:%.1f秒", g_iLastSpawnTime + g_fSiInterval); + g_iLastSpawnTime = 0; + } + else + { + Debug_Print("固定增时系统开始新一波刷特, 总用时:%.1f秒", g_iLastSpawnTime + g_fSiInterval); + g_hSpawnProcess = CreateTimer(g_fSiInterval * 1.5, SpawnNewInfected, _, TIMER_REPEAT); + } + } + else + { + if((IsAllKillersDown() && g_iSpawnMaxCount == 0) || (g_iTotalSINum <= (RoundToFloor(g_iSiLimit / 4.0) + 1) && g_iSpawnMaxCount == 0) || (g_iLastSpawnTime >= g_fSiInterval * 0.5)) + { + g_bShouldCheck = false; + if(g_iSpawnMaxCount == g_iSiLimit) + { + Debug_Print("自动增时系统因为等待刷特数量达到上限,暂停刷特, 总用时:%.1f秒", g_iLastSpawnTime + g_fSiInterval); + g_iLastSpawnTime = 0; + } + else + { + Debug_Print("自动增时系统开始新一波刷特, 总用时:%.1f秒", g_iLastSpawnTime + g_fSiInterval); + g_hSpawnProcess = CreateTimer(g_fSiInterval, SpawnNewInfected, _, TIMER_REPEAT); + } + } + } + g_fLastSISpawnStartTime = GetGameTime(); + return Plugin_Continue; } //是否存在非克、舌头、口水、胖子存活 bool IsAllKillersDown() { - return (g_iSINum[view_as(ZC_CHARGER) - 1] - + g_iSINum[view_as(ZC_HUNTER) - 1] - + g_iSINum[view_as(ZC_JOCKEY)] - 1) - == 0; + return (g_iSINum[view_as(ZC_CHARGER) - 1] + g_iSINum[view_as(ZC_HUNTER) - 1] + g_iSINum[view_as(ZC_JOCKEY)] - 1) == 0; } -stock void BypassAndExecuteCommand(char[] strCommand) +stock void BypassAndExecuteCommand(char []strCommand) { - int flags = GetCommandFlags(strCommand); - SetCommandFlags(strCommand, flags & ~FCVAR_CHEAT); - FakeClientCommand(GetRandomSurvivor(), "%s", strCommand); - SetCommandFlags(strCommand, flags); + int flags = GetCommandFlags(strCommand); + SetCommandFlags(strCommand, flags & ~ FCVAR_CHEAT); + FakeClientCommand(GetRandomSurvivor(), "%s", strCommand); + SetCommandFlags(strCommand, flags); } // 开局重置特感状态 -Action SafeRoomReset(Handle timer) -{ - ResetStatus(); - return Plugin_Continue; -} - -void ResetStatus() -{ - g_iTotalSINum = 0; - for (int client = 1; client <= MaxClients; client++) - { - if (IsInfectedBot(client) && !IsPlayerAlive(client)) - { - g_iTeleCount[client] = 0; - int type = GetEntProp(client, Prop_Send, "m_zombieClass"); - g_iSINum[type - 1] += 1; - g_iTotalSINum += 1; - } - if (IsValidSurvivor(client) && !IsPlayerAlive(client)) - L4D_RespawnPlayer(client); - } +public Action SafeRoomReset(Handle timer) +{ + ResetStatus(); + return Plugin_Continue; +} + +public void ResetStatus(){ + g_iTotalSINum = 0; + for (int client = 1; client <= MaxClients; client++) + { + if (IsInfectedBot(client) && IsPlayerAlive(client)) + { + g_iTeleCount[client] = 0; + int type = GetEntProp(client, Prop_Send, "m_zombieClass"); + g_iSINum[type - 1] += 1; + g_iTotalSINum += 1; + } + if (IsValidSurvivor(client) && !IsPlayerAlive(client)) + { + L4D_RespawnPlayer(client); + } + } } // ********************* @@ -990,514 +999,549 @@ void ResetStatus() // ********************* bool IsInfectedBot(int client) { - // if (client > 0 && client <= MaxClients && IsClientInGame(client) && IsFakeClient(client) && GetClientTeam(client) == TEAM_INFECTED && GetEntProp(client, Prop_Send, "m_zombieClass") <= 6 && GetEntProp(client, Prop_Send, "m_zombieClass") >= 1) - // { - // return true; - // } - // else - // { - // return false; - // } - - return client > 0 && client <= MaxClients && IsClientInGame(client) && IsFakeClient(client) - && GetClientTeam(client) == TEAM_INFECTED && GetEntProp(client, Prop_Send, "m_zombieClass") <= 6 - && GetEntProp(client, Prop_Send, "m_zombieClass") >= 1; + if (client > 0 && client <= MaxClients && IsClientInGame(client) && IsFakeClient(client) && GetClientTeam(client) == TEAM_INFECTED && GetEntProp(client, Prop_Send, "m_zombieClass") <= 6 && GetEntProp(client, Prop_Send, "m_zombieClass") >=1) + { + return true; + } + else + { + return false; + } } bool IsOnValidMesh(float fReferencePos[3]) { - Address pNavArea = L4D2Direct_GetTerrorNavArea(fReferencePos); - // if (pNavArea != Address_Null && !(L4D_GetNavArea_SpawnAttributes(pNavArea) & CHECKPOINT)) - // { - // return true; - // } - // else - // { - // return false; - // } - - // 我真心建议这样写,可读性不比用if分支差,一个方法太长看着会很乱的 - return pNavArea != Address_Null && !(L4D_GetNavArea_SpawnAttributes(pNavArea) & CHECKPOINT); + Address pNavArea = L4D2Direct_GetTerrorNavArea(fReferencePos); + if (pNavArea != Address_Null && !(L4D_GetNavArea_SpawnAttributes(pNavArea) & CHECKPOINT)) + { + return true; + } + else + { + return false; + } } //判断该坐标是否可以看到生还或者距离小于g_fSpawnDistanceMin码,减少一层栈函数,增加实时性,单人模式增加2条射线模仿左右眼 stock bool PlayerVisibleTo(float targetposition[3], bool IsTeleport = false) { - float position[3], vAngles[3], vLookAt[3], spawnPos[3]; - for (int client = 1; client <= MaxClients; ++client) - { - if (IsClientConnected(client) && IsClientInGame(client) && IsValidSurvivor(client) && IsPlayerAlive(client)) - { - //传送的时候无视倒地或者挂边生还者的视线,检测到跑男时,也不关注被控生还者的视线 - if (IsTeleport && (IsClientIncapped(client) || (g_bPickRushMan && IsPinned(client)))) - if (!g_bIgnoreIncappedSurvivorSight) - { - int sum = 0; - float temp[3]; - for (int i = 0; i < MaxClients; i++) - if (i != client && IsValidSurvivor(i) && !IsClientIncapped(i)) - { - GetClientAbsOrigin(i, temp); - //倒地生还者INCAPSURVIVORCHECKDIS范围内已经没有正常生还者,掠过这个人的视线判断 - if (GetVectorDistance(temp, position, true) < Pow(INCAPSURVIVORCHECKDIS, 2.0)) - sum++; - } - - if (sum == 0) - { - Debug_Print("Teleport方法,目标位置已经不能被正常生还者所看到"); - continue; - } - else Debug_Print("Teleport方法,目标位置依旧能被正常生还者看到,sum为:%d", sum); - } - else continue; - - GetClientEyePosition(client, position); - // position[0] += 20; - if (GetVectorDistance(targetposition, position, true) < Pow(g_fSpawnDistanceMin, 2.0)) - return true; - - MakeVectorFromPoints(targetposition, position, vLookAt); - GetVectorAngles(vLookAt, vAngles); - Handle trace = TR_TraceRayFilterEx(targetposition, vAngles, MASK_VISIBLE, RayType_Infinite, TraceFilter, client); - if (TR_DidHit(trace)) - { - static float vStart[3]; - TR_GetEndPosition(vStart, trace); - delete trace; // 用完就 delete,不然迟早会忘 - if ((GetVectorDistance(targetposition, vStart, false) + 75.0) >= GetVectorDistance(position, targetposition)) - return true; - - // else // 都 return 了就别 else 了,一堆 大括号 + 缩进 看着眼疼 - // { - spawnPos = targetposition; - spawnPos[2] += 40.0; - MakeVectorFromPoints(spawnPos, position, vLookAt); - GetVectorAngles(vLookAt, vAngles); - Handle trace2 = TR_TraceRayFilterEx(spawnPos, vAngles, MASK_VISIBLE, RayType_Infinite, TraceFilter, client); - if (TR_DidHit(trace2)) - { - TR_GetEndPosition(vStart, trace2); - delete trace2; - if ((GetVectorDistance(spawnPos, vStart, false) + 75.0) >= GetVectorDistance(position, spawnPos)) - return true; - } - else delete trace2; - - return true; - // delete trace2; // 你都 return 了,还怎么delete??? - // } - } - else delete trace; - - return true; - // delete trace; // 你都 return 了,还怎么delete??? - } - } - return false; -} - -// thanks fdxx https://github.com/fdxx/l4d2_plugins/blob/main/l4d2_si_spawn_control.sp -stock bool PlayerVisibleToSDK(float targetposition[3], bool IsTeleport = false) -{ - static float fTargetPos[3]; - - float position[3]; - fTargetPos = targetposition; - fTargetPos[2] += 62.0; //眼睛位置 - - //计算该位置是不是和所有人都相隔大于g_fSpawnDistanceMax - int count = 0, skipcount = 0; - - for (int client = 1; client <= MaxClients; client++) - { - if (IsClientInGame(client) && GetClientTeam(client) == 2 && IsPlayerAlive(client)) - { - GetClientEyePosition(client, position); - //传送的时候无视倒地或者挂边生还者的视线,检测到跑男时,也不关注被控生还者的视线 - if (IsTeleport && (IsClientIncapped(client) || (g_bPickRushMan && IsPinned(client)))) - { - if (!g_bIgnoreIncappedSurvivorSight) - { - int sum = 0; - float temp[3]; - for (int i = 1; i <= MaxClients; i++) - if (i != client && IsValidSurvivor(i) && !IsClientIncapped(i)) - { - GetClientAbsOrigin(i, temp); - //倒地生还者500范围内已经没有正常生还者,掠过这个人的视线判断 - if (GetVectorDistance(temp, position, true) < Pow(INCAPSURVIVORCHECKDIS, 2.0)) - sum++; - } - - if (sum == 0) - { - Debug_Print("Teleport方法,目标位置已经不能被正常生还者所看到"); - skipcount++; - continue; - } - else Debug_Print("Teleport方法,目标位置依旧能被正常生还者看到,sum为:%d", sum); - } - else - { - skipcount++; - continue; - } - } - //太近直接返回看见 - if (GetVectorDistance(targetposition, position, true) < Pow(g_fSpawnDistanceMin, 2.0)) - return true; - - //太远直接返回没看见 - if (GetVectorDistance(targetposition, position, true) >= Pow(g_fSpawnDistanceMax, 2.0)) - { - count++; - if (count >= (g_iSurvivorNum - skipcount)) - return false; - } - if (L4D2_IsVisibleToPlayer(client, 2, 3, 0, targetposition)) - return true; - if (L4D2_IsVisibleToPlayer(client, 2, 3, 0, fTargetPos)) - return true; - } - } + float position[3], vAngles[3], vLookAt[3], spawnPos[3]; + for (int client = 1; client <= MaxClients; ++client) + { + if (IsClientConnected(client) && IsClientInGame(client) && IsValidSurvivor(client) && IsPlayerAlive(client)) + { + //传送的时候无视倒地或者挂边生还者的视线,检测到跑男时,也不关注被控生还者的视线 + if(IsTeleport && (IsClientIncapped(client) || (g_bPickRushMan && IsPinned(client)))){ + if(!g_bIgnoreIncappedSurvivorSight){ + int sum = 0; + float temp[3]; + for(int i = 0; i < MaxClients; i++){ + if(i != client && IsValidSurvivor(i) && !IsClientIncapped(i)){ + GetClientAbsOrigin(i, temp); + //倒地生还者INCAPSURVIVORCHECKDIS范围内已经没有正常生还者,掠过这个人的视线判断 + if(GetVectorDistance(temp, position, true) < Pow(INCAPSURVIVORCHECKDIS, 2.0)){ + sum ++; + } + } + } + if(sum == 0){ + Debug_Print("Teleport方法,目标位置已经不能被正常生还者所看到"); + continue; + }else{ + Debug_Print("Teleport方法,目标位置依旧能被正常生还者看到,sum为:%d", sum); + } + } + else{ + continue; + } + + } + GetClientEyePosition(client, position); + //position[0] += 20; + if(GetVectorDistance(targetposition, position, true) < Pow(g_fSpawnDistanceMin, 2.0)) + { + return true; + } + MakeVectorFromPoints(targetposition, position, vLookAt); + GetVectorAngles(vLookAt, vAngles); + Handle trace = TR_TraceRayFilterEx(targetposition, vAngles, MASK_VISIBLE, RayType_Infinite, TraceFilter, client); + if(TR_DidHit(trace)) + { + static float vStart[3]; + TR_GetEndPosition(vStart, trace); + if((GetVectorDistance(targetposition, vStart, false) + 75.0) >= GetVectorDistance(position, targetposition)) + { + return true; + } + else + { + spawnPos = targetposition; + spawnPos[2] += 40.0; + MakeVectorFromPoints(spawnPos, position, vLookAt); + GetVectorAngles(vLookAt, vAngles); + Handle trace2 = TR_TraceRayFilterEx(spawnPos, vAngles, MASK_VISIBLE, RayType_Infinite, TraceFilter, client); + if(TR_DidHit(trace2)) + { + TR_GetEndPosition(vStart, trace2); + if((GetVectorDistance(spawnPos, vStart, false) + 75.0) >= GetVectorDistance(position, spawnPos)) + return true; + } + else + { + return true; + } + delete trace2; + } + } + else + { + return true; + } + delete trace; + } + } + return false; +} + +//thanks fdxx https://github.com/fdxx/l4d2_plugins/blob/main/l4d2_si_spawn_control.sp +stock bool PlayerVisibleToSDK(float targetposition[3], bool IsTeleport = false){ + static float fTargetPos[3]; + + float position[3]; + fTargetPos = targetposition; + fTargetPos[2] += 62.0; //眼睛位置 + + //计算该位置是不是和所有人都相隔大于g_fSpawnDistanceMax + int count = 0, skipcount = 0; + + for (int client = 1; client <= MaxClients; client++) + { + if (IsClientInGame(client) && GetClientTeam(client) == 2 && IsPlayerAlive(client)) + { + GetClientEyePosition(client, position); + //传送的时候无视倒地或者挂边生还者的视线,检测到跑男时,也不关注被控生还者的视线 + if(IsTeleport && (IsClientIncapped(client) || (g_bPickRushMan && IsPinned(client)))){ + if(!g_bIgnoreIncappedSurvivorSight){ + int sum = 0; + float temp[3]; + for(int i = 1; i <= MaxClients; i++){ + if(i != client && IsValidSurvivor(i) && !IsClientIncapped(i)){ + GetClientAbsOrigin(i, temp); + //倒地生还者500范围内已经没有正常生还者,掠过这个人的视线判断 + if(GetVectorDistance(temp, position, true) < Pow(INCAPSURVIVORCHECKDIS, 2.0)){ + sum ++; + } + } + } + if(sum == 0){ + Debug_Print("Teleport方法,目标位置已经不能被正常生还者所看到"); + skipcount++; + continue; + }else{ + Debug_Print("Teleport方法,目标位置依旧能被正常生还者看到,sum为:%d", sum); + } + }else{ + skipcount++; + continue; + } + } + //太近直接返回看见 + if(GetVectorDistance(targetposition, position, true) < Pow(g_fSpawnDistanceMin, 2.0)) + { + return true; + } + //太远直接返回没看见 + if(GetVectorDistance(targetposition, position, true) >= Pow(g_fSpawnDistanceMax, 2.0)) + { + count++; + if(count >= (g_iSurvivorNum - skipcount)){ + return false; + } + + } + if (L4D2_IsVisibleToPlayer(client, 2, 3, 0, targetposition)) + { + return true; + } + if (L4D2_IsVisibleToPlayer(client, 2, 3, 0, fTargetPos)) + { + return true; + } + } + } - return false; + return false; } bool IsPlayerStuck(float fSpawnPos[3]) { - //似乎所有客户端的尺寸都一样 - static const float fClientMinSize[3] = { -16.0, -16.0, 0.0 }; - static const float fClientMaxSize[3] = { 16.0, 16.0, 72.0 }; + //似乎所有客户端的尺寸都一样 + static const float fClientMinSize[3] = {-16.0, -16.0, 0.0}; + static const float fClientMaxSize[3] = {16.0, 16.0, 72.0}; - static bool bHit; - static Handle hTrace; + static bool bHit; + static Handle hTrace; - hTrace = TR_TraceHullFilterEx(fSpawnPos, fSpawnPos, fClientMinSize, fClientMaxSize, MASK_PLAYERSOLID, TraceFilter_Stuck); - bHit = TR_DidHit(hTrace); + hTrace = TR_TraceHullFilterEx(fSpawnPos, fSpawnPos, fClientMinSize, fClientMaxSize, MASK_PLAYERSOLID, TraceFilter_Stuck); + bHit = TR_DidHit(hTrace); - delete hTrace; - return bHit; + delete hTrace; + return bHit; } stock bool TraceFilter_Stuck(int entity, int contentsMask) { - if (entity <= MaxClients || !IsValidEntity(entity)) - return false; - - static char sClassName[20]; - GetEntityClassname(entity, sClassName, sizeof(sClassName)); - if (strcmp(sClassName, "env_physics_blocker") == 0 && !EnvBlockType(entity)) - return false; - - return true; + if (entity <= MaxClients || !IsValidEntity(entity)) + { + return false; + } + else{ + static char sClassName[20]; + GetEntityClassname(entity, sClassName, sizeof(sClassName)); + if (strcmp(sClassName, "env_physics_blocker") == 0 && !EnvBlockType(entity)){ + return false; + } + } + return true; } -stock bool EnvBlockType(int entity) -{ - int BlockType = GetEntProp(entity, Prop_Data, "m_nBlockType"); - //阻拦ai infected - // if (BlockType == 1 || BlockType == 2) - // return false; - // else - // return true; - return !(BlockType == 1 || BlockType == 2); +stock bool EnvBlockType(int entity){ + int BlockType = GetEntProp(entity, Prop_Data, "m_nBlockType"); + //阻拦ai infected + if(BlockType == 1 || BlockType == 2){ + return false; + } + else{ + return true; + } } stock bool TraceFilter(int entity, int contentsMask) { - if (entity <= MaxClients || !IsValidEntity(entity)) - return false; - - static char sClassName[9]; - GetEntityClassname(entity, sClassName, sizeof(sClassName)); - if (strcmp(sClassName, "infected") == 0 || strcmp(sClassName, "witch") == 0) - return false; - - return true; + if (entity <= MaxClients || !IsValidEntity(entity)) + { + return false; + } + else + { + static char sClassName[9]; + GetEntityClassname(entity, sClassName, sizeof(sClassName)); + if (strcmp(sClassName, "infected") == 0 || strcmp(sClassName, "witch") == 0) + { + return false; + } + } + return true; } bool IsPinned(int client) { - if (!(IsValidSurvivor(client) && IsPlayerAlive(client))) return false; - - return GetEntPropEnt(client, Prop_Send, "m_tongueOwner") > 0 - || GetEntPropEnt(client, Prop_Send, "m_carryAttacker") > 0 - || GetEntPropEnt(client, Prop_Send, "m_jockeyAttacker") > 0 - || GetEntPropEnt(client, Prop_Send, "m_pounceAttacker") > 0 - || GetEntPropEnt(client, Prop_Send, "m_pummelAttacker") > 0; + bool bIsPinned = false; + if (IsValidSurvivor(client) && IsPlayerAlive(client)) + { + if(GetEntPropEnt(client, Prop_Send, "m_tongueOwner") > 0) bIsPinned = true; + if(GetEntPropEnt(client, Prop_Send, "m_pounceAttacker") > 0) bIsPinned = true; + if(GetEntPropEnt(client, Prop_Send, "m_carryAttacker") > 0) bIsPinned = true; + if(GetEntPropEnt(client, Prop_Send, "m_pummelAttacker") > 0) bIsPinned = true; + if(GetEntPropEnt(client, Prop_Send, "m_jockeyAttacker") > 0) bIsPinned = true; + } + return bIsPinned; } bool IsPinningSomeone(int client) { - bool bIsPinning = false; - if (IsInfectedBot(client)) - { - if (GetEntPropEnt(client, Prop_Send, "m_tongueVictim") > 0) bIsPinning = true; - if (GetEntPropEnt(client, Prop_Send, "m_jockeyVictim") > 0) bIsPinning = true; - if (GetEntPropEnt(client, Prop_Send, "m_pounceVictim") > 0) bIsPinning = true; - if (GetEntPropEnt(client, Prop_Send, "m_pummelVictim") > 0) bIsPinning = true; - if (GetEntPropEnt(client, Prop_Send, "m_carryVictim") > 0) bIsPinning = true; - } - return bIsPinning; + bool bIsPinning = false; + if (IsInfectedBot(client)) + { + if (GetEntPropEnt(client, Prop_Send, "m_tongueVictim") > 0) bIsPinning = true; + if (GetEntPropEnt(client, Prop_Send, "m_jockeyVictim") > 0) bIsPinning = true; + if (GetEntPropEnt(client, Prop_Send, "m_pounceVictim") > 0) bIsPinning = true; + if (GetEntPropEnt(client, Prop_Send, "m_pummelVictim") > 0) bIsPinning = true; + if (GetEntPropEnt(client, Prop_Send, "m_carryVictim") > 0) bIsPinning = true; + } + return bIsPinning; } bool CanBeTeleport(int client) { - if (IsInfectedBot(client) && IsClientInGame(client) && IsPlayerAlive(client) && GetEntProp(client, Prop_Send, "m_zombieClass") != ZC_TANK && !IsPinningSomeone(client)) - { - // 防止无声口水 (使用 SIPool 后无此问题) - if (!g_bSIPoolAvailable && IsSpitter(client) && GetGameTime() - g_fSpitterSpitTime[client] < SPIT_INTERVAL) - return false; - - if (GetClosetSurvivorDistance(client) < g_fSpawnDistanceMin) - return false; - - //舌头能力检查 - if (IsAiSmoker(client) && g_bSmokerAvailable && !IsSmokerCanUseAbility(client)) - return false; - - float fPos[3]; - GetClientAbsOrigin(client, fPos); - if (Is_Pos_Ahead(fPos)) - return false; - - return true; - } - - return false; + if (IsInfectedBot(client) && IsClientInGame(client)&& IsPlayerAlive(client) && GetEntProp(client, Prop_Send, "m_zombieClass") != ZC_TANK && !IsPinningSomeone(client)) + { + // 防止无声口水 + if(IsSpitter(client) && GetGameTime() - g_fSpitterSpitTime[client] < SPIT_INTERVAL) + { + return false; + } + + if(GetClosetSurvivorDistance(client) < g_fSpawnDistanceMin) + { + return false; + } + + //舌头能力检查 + if(IsAiSmoker(client) && g_bSmokerAvailable && !IsSmokerCanUseAbility(client)) + { + return false; + } + float fPos[3]; + GetClientAbsOrigin(client, fPos); + if(Is_Pos_Ahead(fPos)) + { + return false; + } + return true; + } + else + { + return false; + } } -// 5秒内以1s检测一次,5次没被看到,就可以踢出并加入传送队列 -Action Timer_PositionSi(Handle timer) +//5秒内以1s检测一次,5次没被看到,就可以踢出并加入传送队列 +public Action Timer_PositionSi(Handle timer) { - if (IsInPause()) - { - Debug_Print("处于暂停状态,停止传送检测"); - return Plugin_Continue; - } - - //每1s找一次跑男或者是否所有全被控 - if (CheckRushManAndAllPinned()) - return Plugin_Continue; - - for (int client = 1; client <= MaxClients; client++) - { - if (CanBeTeleport(client)) - { - float fSelfPos[3] = { 0.0 }; - GetClientEyePosition(client, fSelfPos); - if (!PlayerVisibleToSDK(fSelfPos, true)) - { - // 如果是跑男状态,只要1s没被看到后就能传送 - if ((g_iTeleCount[client] > g_iTeleportCheckTime || (g_bPickRushMan && g_iTeleCount[client] > 0))) - { - int type = GetInfectedClass(client); - if (type >= 1 && type <= 6) - { - if (g_iTeleportIndex == 0) - g_fTeleportDistance = g_fSpawnDistanceMin; - - aTeleportQueue.Push(type); - g_iTeleportIndex += 1; - Debug_Print("<传送队列> %N踢出,进入传送队列,当前 <传送队列> 队列长度:%d 队列索引:%d 当前记录特感总数为:%d , 真实数量为:%d", client, aTeleportQueue.Length, g_iTeleportIndex, g_iTotalSINum, GetCurrentSINum()); - //不再单独处理spitter防止无声口水,已经在canbeteleport处理 - if (g_iSINum[type - 1] > 0) g_iSINum[type - 1]--; - else g_iSINum[type - 1] = 0; - - if (g_iTotalSINum > 0) g_iTotalSINum--; - else g_iTotalSINum = 0; - - // KickClient(client, "传送刷特,踢出"); - if (g_bSIPoolAvailable) g_hSIPool.ReturnSIBot(client); - else KickClient(client, "传送刷特,踢出"); - - Debug_Print("当前 <传送队列> 队列长度:%d 队列索引:%d 当前记录特感总数为:%d , 真实数量为:%d", aTeleportQueue.Length, g_iTeleportIndex, g_iTotalSINum, GetCurrentSINum()); - g_iTeleCount[client] = 0; - } - } - g_iTeleCount[client] += 1; - } - else g_iTeleCount[client] = 0; - } - } - //每1s找一次攻击目标,主要用于检测跑男,正常情况ongameframe会调用找攻击目标 - g_iTargetSurvivor = GetTargetSurvivor(); - return Plugin_Continue; + if(IsInPause()) + { + Debug_Print("处于暂停状态,停止传送检测"); + return Plugin_Continue; + } + //每1s找一次跑男或者是否所有全被控 + if(CheckRushManAndAllPinned()) + { + return Plugin_Continue; + } + for (int client = 1; client <= MaxClients; client++) + { + if(CanBeTeleport(client)){ + float fSelfPos[3] = {0.0}; + GetClientEyePosition(client, fSelfPos); + if (!PlayerVisibleToSDK(fSelfPos, true)) + { + // 如果是跑男状态,只要1s没被看到后就能传送 + if ((g_iTeleCount[client] > g_iTeleportCheckTime || (g_bPickRushMan && g_iTeleCount[client] > 0))) + { + int type = GetInfectedClass(client); + if(type >= 1 && type <= 6){ + if(g_iTeleportIndex == 0) + { + g_fTeleportDistance = g_fSpawnDistanceMin; + } + aTeleportQueue.Push(type); + g_iTeleportIndex += 1; + Debug_Print("<传送队列> %N踢出,进入传送队列,当前 <传送队列> 队列长度:%d 队列索引:%d 当前记录特感总数为:%d , 真实数量为:%d", client, aTeleportQueue.Length, g_iTeleportIndex, g_iTotalSINum, GetCurrentSINum()); + //不再单独处理spitter防止无声口水,已经在canbeteleport处理 + if(g_iSINum[type - 1] > 0) + { + g_iSINum[type - 1] --; + } + else + { + g_iSINum[type - 1] = 0; + } + if(g_iTotalSINum > 0) + { + g_iTotalSINum --; + } + else + { + g_iTotalSINum = 0; + } + KickClient(client, "传送刷特,踢出"); + //Debug_Print("当前 <传送队列> 队列长度:%d 队列索引:%d 当前记录特感总数为:%d , 真实数量为:%d", aTeleportQueue.Length, g_iTeleportIndex, g_iTotalSINum, GetCurrentSINum()); + g_iTeleCount[client] = 0; + } + } + g_iTeleCount[client] += 1; + } + else{ + g_iTeleCount[client] = 0; + } + } + + } + //每1s找一次攻击目标,主要用于检测跑男,正常情况ongameframe会调用找攻击目标 + g_iTargetSurvivor = GetTargetSurvivor(); + return Plugin_Continue; } stock int GetCurrentSINum() { - int sum = 0; - for (int i = 0; i < MaxClients; i++) - if (IsInfectedBot(i) && !IsPlayerAlive(i)) - sum++; - - return sum; + int sum = 0; + for(int i = 0; i < MaxClients; i++){ + if(IsInfectedBot(i) && IsPlayerAlive(i)) + { + sum ++; + } + } + return sum; } stock bool IsSpitter(int client) { - // if (IsInfectedBot(client) && IsPlayerAlive(client) && GetEntProp(client, Prop_Send, "m_zombieClass") == ZC_SPITTER) - // { - // return true; - // } - // else - // { - // return false; - // } - return IsInfectedBot(client) && IsPlayerAlive(client) && (GetEntProp(client, Prop_Send, "m_zombieClass") == ZC_SPITTER); + if (IsInfectedBot(client) && IsPlayerAlive(client) && GetEntProp(client, Prop_Send, "m_zombieClass") == ZC_SPITTER) + { + return true; + } + else + { + return false; + } } // 跑男定义为距离所有生还者或者特感超过RushManDistance距离 bool CheckRushManAndAllPinned() { - bool TempRushMan = g_bPickRushMan; - int iSurvivors[8] = { 0 }, iSurvivorIndex = 0, PinnedNumber = 0; - int iInfecteds[MAXPLAYERS] = { 0 }, iInfectedIndex = 0; - float fInfectedssOrigin[MAXPLAYERS][3], fSurvivorsOrigin[8][3], OriginTemp[3]; - for (int client = 1; client <= MaxClients; client++) - { - if (IsValidSurvivor(client) && IsPlayerAlive(client)) - { - if (IsPinned(client) || IsClientIncapped(client)) - PinnedNumber++; - - GetClientAbsOrigin(client, OriginTemp); - if (iSurvivorIndex < 8) - { - fSurvivorsOrigin[iSurvivorIndex] = OriginTemp; - iSurvivors[iSurvivorIndex++] = client; - } - } - else if (IsInfectedBot(client) && IsPlayerAlive(client)) - { - iInfecteds[iInfectedIndex] = client; - GetClientAbsOrigin(client, OriginTemp); - fInfectedssOrigin[iInfectedIndex++] = OriginTemp; - } - } - - //一个人有什么跑男 - if (iSurvivorIndex == 1) - return false; - - int target = L4D_GetHighestFlowSurvivor(); - if (iSurvivorIndex >= 1 && IsValidClient(target)) - { - GetClientAbsOrigin(target, OriginTemp); - bool testSurvior = false; - if (iSurvivorIndex == 1) - testSurvior = true; - - for (int i = 0; i < iSurvivorIndex && !testSurvior; i++) - if (IsPinned(target) || IsClientIncapped(target) || (iSurvivors[i] != target && GetVectorDistance(fSurvivorsOrigin[i], OriginTemp, true) <= Pow(RushManDistance, 2.0))) - { - testSurvior = true; - break; - } - - if (!testSurvior || g_iTotalSINum < (g_iSiLimit / 2 + 1)) - { - g_bPickRushMan = false; - g_iRushManIndex = -1; - if (TempRushMan != g_bPickRushMan) - StartForward(g_bPickRushMan); - - return PinnedNumber == iSurvivorIndex; - } - else - for (int i = 0; i < iInfectedIndex; i++) - if (IsPinned(target) || IsClientIncapped(target) || (GetVectorDistance(fInfectedssOrigin[i], OriginTemp, true) <= Pow(RushManDistance, 2.0) * 1.3)) - { - g_bPickRushMan = false; - g_iRushManIndex = -1; - if (TempRushMan != g_bPickRushMan) - StartForward(g_bPickRushMan); - - return PinnedNumber == iSurvivorIndex; - } - - if (!testSurvior) - Debug_Print("跑男由于和其他正常生还者过远触发"); - else Debug_Print("跑男由于和特感过远触发"); - - g_bPickRushMan = true; - g_iRushManIndex = target; - if (TempRushMan != g_bPickRushMan) - StartForward(g_bPickRushMan); - } - return PinnedNumber == iSurvivorIndex; + bool TempRushMan = g_bPickRushMan; + int iSurvivors[8] = {0}, iSurvivorIndex = 0, PinnedNumber = 0; + int iInfecteds[MAXPLAYERS] = {0}, iInfectedIndex = 0; + float fInfectedssOrigin[MAXPLAYERS][3], fSurvivorsOrigin[8][3], OriginTemp[3]; + for (int client = 1; client <= MaxClients; client++) + { + if (IsValidSurvivor(client) && IsPlayerAlive(client)) + { + if(IsPinned(client) || IsClientIncapped(client)){ + PinnedNumber++; + } + GetClientAbsOrigin(client, OriginTemp); + if(iSurvivorIndex < 8) + { + fSurvivorsOrigin[iSurvivorIndex] = OriginTemp; + iSurvivors[iSurvivorIndex++] = client; + } + } + else if(IsInfectedBot(client) && IsPlayerAlive(client)) + { + iInfecteds[iInfectedIndex] = client; + GetClientAbsOrigin(client, OriginTemp); + fInfectedssOrigin[iInfectedIndex++] = OriginTemp; + } + } + if(iSurvivorIndex == 1) + { + //一个人有什么跑男 + return false; + } + int target = L4D_GetHighestFlowSurvivor(); + if (iSurvivorIndex >= 1 && IsValidClient(target)) + { + GetClientAbsOrigin(target, OriginTemp); + bool testSurvior = false; + if(iSurvivorIndex == 1) + { + testSurvior = true; + } + for(int i =0; i < iSurvivorIndex && !testSurvior; i++){ + if(IsPinned(target) || IsClientIncapped(target) || (iSurvivors[i] != target && GetVectorDistance(fSurvivorsOrigin[i], OriginTemp, true) <= Pow(RushManDistance, 2.0))) + { + testSurvior = true; + break; + } + } + if(!testSurvior || g_iTotalSINum < (g_iSiLimit / 2 + 1)) + { + g_bPickRushMan = false; + g_iRushManIndex = -1; + if(TempRushMan != g_bPickRushMan){ + StartForward(g_bPickRushMan); + } + return PinnedNumber == iSurvivorIndex; + } + else + { + for(int i =0; i < iInfectedIndex; i++) + { + if(IsPinned(target) || IsClientIncapped(target) || (GetVectorDistance(fInfectedssOrigin[i], OriginTemp, true) <= Pow(RushManDistance, 2.0) * 1.3)) + { + g_bPickRushMan = false; + g_iRushManIndex = -1; + if(TempRushMan != g_bPickRushMan){ + StartForward(g_bPickRushMan); + } + return PinnedNumber == iSurvivorIndex; + } + } + } + if(!testSurvior) + { + Debug_Print("跑男由于和其他正常生还者过远触发") ; + } + else + { + Debug_Print("跑男由于和特感过远触发") ; + } + g_bPickRushMan = true; + g_iRushManIndex = target; + if(TempRushMan != g_bPickRushMan){ + StartForward(g_bPickRushMan); + } + } + return PinnedNumber == iSurvivorIndex; } int GetTargetSurvivor() { - //如果有跑男,抓跑男 - if (g_bPickRushMan && IsValidSurvivor(g_iRushManIndex) && IsPlayerAlive(g_iRushManIndex) && !IsPinned(g_iRushManIndex)) - return g_iRushManIndex; - - //没有跑男,抓目标未满的生还者 - int iSurvivors[8] = { 0 }, iSurvivorIndex = 0; - for (int client = 1; client <= MaxClients; client++) - { - if (IsValidSurvivor(client) && IsPlayerAlive(client) && (!IsPinned(client) || !IsClientIncapped(client))) - { - g_bIsLate = true; - if (g_bTargetSystemAvailable && IsClientReachLimit(client)) - continue; - - if (iSurvivorIndex < 8) - { - iSurvivors[iSurvivorIndex] = client; - iSurvivorIndex += 1; - } - } - } - if (iSurvivorIndex > 0) - return iSurvivors[GetRandomInt(0, iSurvivorIndex - 1)]; - - return L4D_GetHighestFlowSurvivor(); + //如果有跑男,抓跑男 + if(g_bPickRushMan && IsValidSurvivor(g_iRushManIndex) && IsPlayerAlive(g_iRushManIndex) && !IsPinned(g_iRushManIndex)){ + return g_iRushManIndex; + } + //没有跑男,抓目标未满的生还者 + else + { + int iSurvivors[8] = {0} , iSurvivorIndex = 0; + for (int client = 1; client <= MaxClients; client++) + { + if (IsValidSurvivor(client) && IsPlayerAlive(client) && (!IsPinned(client) || !IsClientIncapped(client))) + { + g_bIsLate = true; + if(g_bTargetSystemAvailable && IsClientReachLimit(client)) + { + continue; + } + if (iSurvivorIndex < 8) + { + iSurvivors[iSurvivorIndex] = client; + iSurvivorIndex += 1; + } + } + } + if (iSurvivorIndex > 0) + { + return iSurvivors[GetRandomInt(0, iSurvivorIndex - 1)]; + } + else{ + return L4D_GetHighestFlowSurvivor(); + } + } } -void StartForward(bool IsRush) -{ - Debug_Print("跑男检测状态变化,发送forward"); - Call_StartForward(g_hRushManNotifyForward); //转发触发 - Call_PushCell(IsRush); //按顺序将参数push进forward传参列表里 - Call_Finish(); //转发结束 +void StartForward(bool IsRush){ + Debug_Print("跑男检测状态变化,发送forward"); + Call_StartForward(g_hRushManNotifyForward);//转发触发 + Call_PushCell(IsRush);//按顺序将参数push进forward传参列表里 + Call_Finish();//转发结束 } stock bool IsAiSmoker(int client) { - // if (client && client <= MaxClients && IsClientInGame(client) && IsPlayerAlive(client) && IsFakeClient(client) && GetClientTeam(client) == TEAM_INFECTED && GetEntProp(client, Prop_Send, "m_zombieClass") == 1 && GetEntProp(client, Prop_Send, "m_isGhost") != 1) - // { - // return true; - // } - // else - // { - // return false; - // } - - return client && client <= MaxClients && IsClientInGame(client) && IsPlayerAlive(client) - && IsFakeClient(client) && (GetClientTeam(client) == TEAM_INFECTED) - && (GetEntProp(client, Prop_Send, "m_zombieClass") == 1) && (GetEntProp(client, Prop_Send, "m_isGhost") != 1); + if (client && client <= MaxClients && IsClientInGame(client) && IsPlayerAlive(client) && IsFakeClient(client) && GetClientTeam(client) == TEAM_INFECTED && GetEntProp(client, Prop_Send, "m_zombieClass") == 1 && GetEntProp(client, Prop_Send, "m_isGhost") != 1) + { + return true; + } + else + { + return false; + } } stock bool IsAiTank(int client) { - // if (client && client <= MaxClients && IsClientInGame(client) && IsPlayerAlive(client) && IsFakeClient(client) && GetClientTeam(client) == TEAM_INFECTED && GetEntProp(client, Prop_Send, "m_zombieClass") == 8 && GetEntProp(client, Prop_Send, "m_isGhost") != 1) - // { - // return true; - // } - // else - // { - // return false; - // } - - return client && client <= MaxClients && IsClientInGame(client) && IsPlayerAlive(client) - && IsFakeClient(client) && (GetClientTeam(client) == TEAM_INFECTED) - && (GetEntProp(client, Prop_Send, "m_zombieClass") == 8) && (GetEntProp(client, Prop_Send, "m_isGhost") != 1); + if (client && client <= MaxClients && IsClientInGame(client) && IsPlayerAlive(client) && IsFakeClient(client) && GetClientTeam(client) == TEAM_INFECTED && GetEntProp(client, Prop_Send, "m_zombieClass") == 8 && GetEntProp(client, Prop_Send, "m_isGhost") != 1) + { + return true; + } + else + { + return false; + } } stock bool IsGhost(int client) @@ -1506,140 +1550,139 @@ stock bool IsGhost(int client) } //获取队列里Hunter和Charger数量 -stock int getArrayHunterAndChargetNum() -{ - int count = 0; - for (int i = 0; i < aSpawnQueue.Length; i++) - { - int type = aSpawnQueue.Get(i); - if (type == 3 || type == 6) - count++; - } - return count; -} - -//获取控制特感的数量 -stock int getArrayDominateSINum() -{ - int count = 0; - for (int i = 0; i < aSpawnQueue.Length; i++) - { - int type = aSpawnQueue.Get(i); - if (type != 2 || type == 4) - count++; - } - return count; +stock int getArrayHunterAndChargetNum(){ + int count = 0; + for(int i = 0; i < aSpawnQueue.Length; i++){ + int type = aSpawnQueue.Get(i); + if(type == 3 || type == 6){ + count++; + } + } + return count; +} +stock int getArrayDominateSINum(){ + int count = 0; + for(int i = 0; i < aSpawnQueue.Length; i++){ + int type = aSpawnQueue.Get(i); + if(type != 2 || type == 4){ + count++; + } + } + return count; } // 返回在场特感数量,根据 z_%s_limit 限制每种特感上限 bool MeetRequire(int iType) { - if (g_hAllChargerMode.BoolValue || g_hAllHunterMode.BoolValue) - return true; - - GetSiLimit(); - if (iType < 1 || iType > 6) return false; - switch (iType) - { - case 2: - if (CheckSIOption(iType) && (g_ArraySIlimit[iType - 1] > 0) && ((getArrayDominateSINum() > (g_iSiLimit / 4 + 1)) || (g_iQueueIndex >= g_iSiLimit - 2))) - return true; - case 4: - if (CheckSIOption(iType) && (g_ArraySIlimit[iType - 1] > 0) && ((getArrayHunterAndChargetNum() > (g_iSiLimit / 5 + 1) || (g_iQueueIndex >= g_iSiLimit - 2)))) - return true; - default: - if (CheckSIOption(iType) && (g_ArraySIlimit[iType - 1] > 0)) - return true; - } - return false; - // if (iType == 1) - // { - // if (CheckSIOption(iType) && (g_ArraySIlimit[iType - 1] > 0)) - // return true; - // } - // else if (iType == 2) - // { - // if (CheckSIOption(iType) && (g_ArraySIlimit[iType - 1] > 0) && ((getArrayDominateSINum() > (g_iSiLimit / 4 + 1)) || (g_iQueueIndex >= g_iSiLimit - 2))) - // return true; - // } - // else if (iType == 3) - // { - // if (CheckSIOption(iType) && (g_ArraySIlimit[iType - 1] > 0)) - // return true; - // } - // else if (iType == 4) - // { - // if (CheckSIOption(iType) && (g_ArraySIlimit[iType - 1] > 0) && ((getArrayHunterAndChargetNum() > (g_iSiLimit / 5 + 1) || (g_iQueueIndex >= g_iSiLimit - 2)))) - // return true; - // } - // else if (iType == 5) - // { - // if (CheckSIOption(iType) && (g_ArraySIlimit[iType - 1] > 0)) - // return true; - // } - // else if (iType == 6) - // { - // if (CheckSIOption(iType) && (g_ArraySIlimit[iType - 1] > 0)) - // return true; - // } - // return false; + if(g_hAllChargerMode.BoolValue || g_hAllHunterMode.BoolValue){ + return true; + } + GetSiLimit(); + if (iType == 1) + { + if (CheckSIOption(iType) && (g_ArraySIlimit[iType - 1] > 0)) + { + return true; + } + } + else if (iType == 2) + { + if (CheckSIOption(iType) && (g_ArraySIlimit[iType - 1] > 0) && ((getArrayDominateSINum() > (g_iSiLimit/4 +1)) || (g_iQueueIndex >= g_iSiLimit -2))) + { + return true; + } + } + else if (iType == 3) + { + if (CheckSIOption(iType) && (g_ArraySIlimit[iType - 1] > 0)) + { + return true; + } + } + else if (iType == 4) + { + if (CheckSIOption(iType) && (g_ArraySIlimit[iType - 1] > 0) && ((getArrayHunterAndChargetNum() > (g_iSiLimit/5 +1) || (g_iQueueIndex >= g_iSiLimit -2)))) + { + return true; + } + } + else if (iType == 5) + { + if (CheckSIOption(iType) && (g_ArraySIlimit[iType - 1] > 0)) + { + return true; + } + } + else if (iType == 6) + { + if (CheckSIOption(iType) && (g_ArraySIlimit[iType - 1] > 0)) + { + return true; + } + } + return false; } // 特感种类限制数组,刷完一波特感时重新读取 Cvar 数值,重置特感种类限制数量 void GetSiLimit() { - g_ArraySIlimit[0] = GetConVarInt(FindConVar("z_smoker_limit")); - g_ArraySIlimit[1] = GetConVarInt(FindConVar("z_boomer_limit")); - g_ArraySIlimit[2] = GetConVarInt(FindConVar("z_hunter_limit")); - g_ArraySIlimit[3] = GetConVarInt(FindConVar("z_spitter_limit")); - g_ArraySIlimit[4] = GetConVarInt(FindConVar("z_jockey_limit")); - g_ArraySIlimit[5] = GetConVarInt(FindConVar("z_charger_limit")); - //删除队列里已有元素 - for (int i = 0; i < aSpawnQueue.Length; i++) - { - int type = aSpawnQueue.Get(i); - if (type > 0 && type < 7) - { - if (g_ArraySIlimit[type - 1] > 0) - g_ArraySIlimit[type - 1]--; - else g_ArraySIlimit[type - 1] = 0; - } - } + g_ArraySIlimit[0] = GetConVarInt(FindConVar("z_smoker_limit")); + g_ArraySIlimit[1] = GetConVarInt(FindConVar("z_boomer_limit")); + g_ArraySIlimit[2] = GetConVarInt(FindConVar("z_hunter_limit")); + g_ArraySIlimit[3] = GetConVarInt(FindConVar("z_spitter_limit")); + g_ArraySIlimit[4] = GetConVarInt(FindConVar("z_jockey_limit")); + g_ArraySIlimit[5] = GetConVarInt(FindConVar("z_charger_limit")); + //删除队列里已有元素 + for(int i = 0; i < aSpawnQueue.Length; i++){ + int type = aSpawnQueue.Get(i); + if(type > 0 && type < 7){ + if(g_ArraySIlimit[type - 1] > 0){ + g_ArraySIlimit[type - 1]--; + } + else + { + g_ArraySIlimit[type - 1] = 0; + } + } + } } // 判断一个坐标是否在当前最高路程的生还者前面 bool Is_Pos_Ahead(float refpos[3], int target = -1) { - int pos_flow = 0, target_flow = 0; - Address pNowNav = L4D2Direct_GetTerrorNavArea(refpos); - if (pNowNav == Address_Null) - pNowNav = view_as
(L4D_GetNearestNavArea(refpos, 300.0)); - - pos_flow = Calculate_Flow(pNowNav); - if (target == -1) - target = L4D_GetHighestFlowSurvivor(); - - if (IsValidSurvivor(target)) - { - float targetpos[3] = { 0.0 }; - GetClientAbsOrigin(target, targetpos); - Address pTargetNav = L4D2Direct_GetTerrorNavArea(targetpos); - if (pTargetNav == Address_Null) - pTargetNav = view_as
(L4D_GetNearestNavArea(refpos, 300.0)); - - target_flow = Calculate_Flow(pTargetNav); - } - return view_as(pos_flow >= target_flow); + int pos_flow = 0, target_flow = 0; + Address pNowNav = L4D2Direct_GetTerrorNavArea(refpos); + if (pNowNav == Address_Null) + { + pNowNav = view_as
(L4D_GetNearestNavArea(refpos, 300.0)); + } + pos_flow = Calculate_Flow(pNowNav); + if(target == -1) + { + target = L4D_GetHighestFlowSurvivor(); + } + if (IsValidSurvivor(target)) + { + float targetpos[3] = {0.0}; + GetClientAbsOrigin(target, targetpos); + Address pTargetNav = L4D2Direct_GetTerrorNavArea(targetpos); + if (pTargetNav == Address_Null) + { + pTargetNav = view_as
(L4D_GetNearestNavArea(refpos, 300.0)); + } + target_flow = Calculate_Flow(pTargetNav); + } + return view_as(pos_flow >= target_flow); } - int Calculate_Flow(Address pNavArea) { - float now_nav_flow = L4D2Direct_GetTerrorNavAreaFlow(pNavArea) / L4D2Direct_GetMapMaxFlowDistance(); - float now_nav_promixity = now_nav_flow + g_hVsBossFlowBuffer.FloatValue / L4D2Direct_GetMapMaxFlowDistance(); - if (now_nav_promixity > 1.0) - now_nav_promixity = 1.0; - - return RoundToNearest(now_nav_promixity * 100.0); + float now_nav_flow = L4D2Direct_GetTerrorNavAreaFlow(pNavArea) / L4D2Direct_GetMapMaxFlowDistance(); + float now_nav_promixity = now_nav_flow + g_hVsBossFlowBuffer.FloatValue / L4D2Direct_GetMapMaxFlowDistance(); + if (now_nav_promixity > 1.0) + { + now_nav_promixity = 1.0; + } + return RoundToNearest(now_nav_promixity * 100.0); } // @key:需要调整的 key 值 @@ -1660,35 +1703,39 @@ public Action L4D_OnGetScriptValueInt(const char[] key, int &retVal) return Plugin_Continue; } -stock void Debug_Print(char[] format, any...) +stock void Debug_Print(char[] format, any ...) { -#if (DEBUG) - { - char sTime[32]; - FormatTime(sTime, sizeof(sTime), "%I-%M-%S", GetTime()); - char sBuffer[512]; - VFormat(sBuffer, sizeof(sBuffer), format, 2); - Format(sBuffer, sizeof(sBuffer), "[%s] %s: %s", "DEBUG", sTime, sBuffer); - // PrintToChatAll(sBuffer); - PrintToConsoleAll(sBuffer); - PrintToServer(sBuffer); - LogToFile(sLogFile, sBuffer); - } -#endif + #if (DEBUG) + { + char sTime[32]; + FormatTime(sTime, sizeof(sTime), "%I-%M-%S", GetTime()); + char sBuffer[512]; + VFormat(sBuffer, sizeof(sBuffer), format, 2); + Format(sBuffer, sizeof(sBuffer), "[%s] %s: %s", "DEBUG", sTime, sBuffer); + // PrintToChatAll(sBuffer); + PrintToConsoleAll(sBuffer); + PrintToServer(sBuffer); + LogToFile(sLogFile, sBuffer); + } + #endif } stock bool IsAnyTankOrAboveHalfSurvivorDownOrDied() { - int count = 0; - for (int i = 1; i <= MaxClients; i++) - { - if (IsAiTank(i)) - return true; - if (IsValidSurvivor(i) && (L4D_IsPlayerIncapacitated(i) || !IsPlayerAlive(i))) - count++; - } - if (count >= RoundToCeil(FindConVar("survivor_limit").IntValue / 2.0)) - return true; + int count = 0; + for(int i = 1; i <= MaxClients; i ++) + { + if(IsAiTank(i)) + return true; + if(IsValidSurvivor(i) && (L4D_IsPlayerIncapacitated(i) || !IsPlayerAlive(i))) + { + count ++; + } + } + if(count >= RoundToCeil(FindConVar("survivor_limit").IntValue / 2.0)) + { + return true; + } + return false; +} - return false; -} \ No newline at end of file diff --git a/addons/sourcemod/scripting/AnneHappy/si_pool.sp b/addons/sourcemod/scripting/AnneHappy/si_pool.sp deleted file mode 100644 index 39b18d1f5..000000000 --- a/addons/sourcemod/scripting/AnneHappy/si_pool.sp +++ /dev/null @@ -1,486 +0,0 @@ -/* - * @Author: 我是派蒙啊 - * @Last Modified by: 我是派蒙啊 - * @Create Date: 2024-02-17 11:15:10 - * @Last Modified time: 2024-03-26 13:39:14 - * @Github: https://github.com/Paimon-Kawaii - */ - -#pragma semicolon 1 -#pragma newdecls required - -#define DEBUG 0 - -#if DEBUG - #define LOGFILE "addons/sourcemod/logs/si_pool_log.txt" -#endif - -#define VERSION "2024.03.26#121" - -#define LIBRARY_NAME "si_pool" -#define GAMEDATA_FILE "si_pool" - -#include - -#include -#include - -#include - -public Plugin myinfo = -{ - name = "Special Infected Bot Client Pool", - author = "我是派蒙啊", - description = "A Client Pool for SI Bots, used to avoid lots of CreateFakeClient() operation", - version = VERSION, - url = "http://github.com/Paimon-Kawaii/L4D2-Plugins" -}; - -#define MAXSIZE MAXPLAYERS + 1 - -static char g_sZombieClass[][] = { - "Smoker", - "Boomer", - "Hunter", - "Spitter", - "Jockey", - "Charger", - "Witch", - "Tank", -}; - -#define DEAD 1 -void ResetDeadZombie(int client) -{ - SetStateTransition(client, STATE_ACTIVE); - SetEntProp(client, Prop_Send, "m_isGhost", true); - SetEntProp(client, Prop_Send, "deadflag", DEAD); - SetEntProp(client, Prop_Send, "m_lifeState", DEAD); - SetEntProp(client, Prop_Send, "m_iPlayerState", DEAD); - SetEntProp(client, Prop_Send, "m_zombieState", DEAD); - SetEntProp(client, Prop_Send, "m_iObserverMode", DEAD); - SetEntProp(client, Prop_Send, "movetype", MOVETYPE_NOCLIP); -} - -#define ALIVE 0 -#define FSOLID_NOT_STANDABLE 0x10 -void InitializeSpecial(int ent, const float vPos[3] = NULL_VECTOR, const float vAng[3] = NULL_VECTOR, bool bSpawn = false) -{ - if (bSpawn) DispatchSpawn(ent); - else RespawnPlayer(ent); - - if (GetClientTeam(ent) != TEAM_INFECTED) ChangeClientTeam(ent, TEAM_INFECTED); - SetEntProp(ent, Prop_Send, "m_usSolidFlags", FSOLID_NOT_STANDABLE); - SetEntProp(ent, Prop_Send, "movetype", MOVETYPE_WALK); - SetEntProp(ent, Prop_Send, "deadflag", ALIVE); - SetEntProp(ent, Prop_Send, "m_lifeState", ALIVE); - SetEntProp(ent, Prop_Send, "m_iObserverMode", ALIVE); - SetEntProp(ent, Prop_Send, "m_iPlayerState", ALIVE); - SetEntProp(ent, Prop_Send, "m_zombieState", ALIVE); - SetEntProp(ent, Prop_Send, "m_isGhost", false); - TeleportEntity(ent, vPos, vAng, NULL_VECTOR); -} - -#define ZC_COUNT 6 -Handle - // g_hSDK_CTerrorPlayer_SetClass, - g_hSDK_CBaseAbility_CreateForPlayer, - g_hSDK_CCSPlayer_State_Transition, - g_hSDK_CTerrorPlayer_RoundRespawn, - g_hSDK_NextBotCreatePlayerBot[ZC_COUNT]; - -static SIPool g_hSIPool; -static int g_iLastDeadTypeIdx = -1; -static int g_iPoolSize[ZC_COUNT] = { 0, ... }; -static int g_iPoolArray[ZC_COUNT][MAXSIZE] = { - {-1, ...}, - { -1, ...}, - { -1, ...}, - { -1, ...}, - { -1, ...}, - { -1, ...}, -}; - -public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) -{ - RegPluginLibrary(LIBRARY_NAME); - g_hSIPool = view_as(myself); - - return APLRes_Success; -} - -public void OnPluginStart() -{ - CreateNatives(); - - HookEvents(); - PrepareSDKCalls(); -} - -public bool OnClientConnect(int client) -{ - if (IsFakeClient(client)) return true; - if (g_iLastDeadTypeIdx == -1) return true; - - int size = g_iPoolSize[g_iLastDeadTypeIdx]; - if (size > 0) - { - KickClient(g_iPoolArray[g_iLastDeadTypeIdx][size - 1]); - OnPoolSizeChanged(size, size - 1, g_iLastDeadTypeIdx); - g_iPoolSize[g_iLastDeadTypeIdx]--; - } - - return true; -} - -void CreateNatives() -{ - CreateNative("SIPool.Instance", Native_SIPool_Instance_get); - - CreateNative("SIPool.RequestSIBot", Native_SIPool_RequestSIBot); - CreateNative("SIPool.ReturnSIBot", Native_SIPool_ReturnSIBot); -} - -any Native_SIPool_Instance_get(Handle plugin, int numParams) -{ - return g_hSIPool; -} - -any Native_SIPool_RequestSIBot(Handle plugin, int numParams) -{ - int zclass_idx = GetNativeCell(2) - 1; - int size = g_iPoolSize[zclass_idx]; - if (size < 1) - { -#if DEBUG - static bool log = false; - if (!log) - { - LogToFile(LOGFILE, "[SIPool] Pool empty or not sized !"); - LogToFile(LOGFILE, "[SIPool] SIPool will auto set size to 1 !"); - LogToFile(LOGFILE, "[SIPool] This log only showed once."); - log = true; - } -#endif - OnPoolSizeChanged(0, 1, zclass_idx); - g_iPoolSize[zclass_idx] = 1; - } - - int index = 1; - size = g_iPoolSize[zclass_idx]; - int bot = g_iPoolArray[zclass_idx][size - index]; -#if DEBUG - LogToFile(LOGFILE, "[SIPool] Start request (%s)pool, current size: %d", g_sZombieClass[zclass_idx], size); - LogToFile(LOGFILE, "[SIPool] %d is ghost:(%d), isalive:(%d)?", bot, IsGhost(bot), IsValidClient(bot) && IsPlayerAlive(bot)); -#endif - while (!(IsValidClient(bot) && IsFakeClient(bot) && !IsPlayerAlive(bot)) && ++index <= size) - { - bot = g_iPoolArray[zclass_idx][size - index]; -#if DEBUG - LogToFile(LOGFILE, "[SIPool] %d is ghost:(%d), isalive:(%d)?", bot, IsGhost(bot), IsValidClient(bot) && IsPlayerAlive(bot)); -#endif - } - if (index > size && !(IsValidClient(bot) && IsFakeClient(bot) && !IsPlayerAlive(bot))) - { -#if DEBUG - LogToFile(LOGFILE, "[SIPool] No SI available !"); -#endif - OnPoolSizeChanged(size, 0, zclass_idx); - g_iPoolSize[zclass_idx] = 0; - - return -1; - } - - static float origin[3], angle[3]; - bool bPos = !IsNativeParamNullVector(3), bAngle = !IsNativeParamNullVector(4); - if (bPos) GetNativeArray(3, origin, 3); - if (bAngle) GetNativeArray(4, angle, 3); - - if (bPos && bAngle) InitializeSpecial(bot, origin, angle); - else if (bPos) InitializeSpecial(bot, origin); - else if (bAngle) InitializeSpecial(bot, _, angle); - else InitializeSpecial(bot); - // SetClass(bot, zclass_idx + 1); - SetClientName(bot, g_sZombieClass[zclass_idx]); - - Event event = CreateEvent("player_spawn", true); - if (event != INVALID_HANDLE) - { - event.SetInt("userid", GetClientUserId(bot)); - event.Fire(); - } - - OnPoolSizeChanged(size, size - index, zclass_idx); - g_iPoolSize[zclass_idx] -= index; - -#if DEBUG - LogToFile(LOGFILE, "[SIPool] SI request: %d, type: %s", bot, g_sZombieClass[zclass_idx]); - LogToFile(LOGFILE, "[SIPool] Finish request (%s)pool, current size: %d", g_sZombieClass[zclass_idx], g_iPoolSize[zclass_idx]); -#endif - - return bot; -} - -any Native_SIPool_ReturnSIBot(Handle plugin, int numParams) -{ - int bot = GetNativeCell(2); - if (!(IsInfected(bot) && IsFakeClient(bot) && IsPlayerAlive(bot))) - { -#if DEBUG - LogToFile(LOGFILE, "[SIPool] SI is not available!"); -#endif - return false; - } - ForcePlayerSuicide(bot); - - return true; -} - -void OnPoolSizeChanged(int iOldPoolSize, int iNewPoolSize, int zclass_idx) -{ - if (GetClientCount(false) >= MaxClients && iOldPoolSize < iNewPoolSize) return; - -#if DEBUG - LogToFile(LOGFILE, "[SIPool] (%s)pool sized(%d -> %d)", g_sZombieClass[zclass_idx], iOldPoolSize, iNewPoolSize); -#endif - - bool add; - int idx_min, idx_max; - if (iOldPoolSize < iNewPoolSize) - { - idx_min = iOldPoolSize; - idx_max = iNewPoolSize; - add = true; - } - if (!add) return; - - for (int i = idx_min; i < idx_max; i++) - { - int bot = CreateSIBot(zclass_idx); - if (bot == -1) - { - int max_count_class = 0; - for (int v = 0, count = 0; v < ZC_COUNT; v++) - if (count < g_iPoolSize[v]) - { - count = g_iPoolSize[v]; - max_count_class = v; - } - KickClient(g_iPoolArray[max_count_class][g_iPoolSize[max_count_class]--], "Kicked because client full."); - - bot = CreateSIBot(zclass_idx); - if (bot == -1) - { - LogError("[SIPool] SI create failed for the unknow reason ?!"); - break; - } - } - g_iPoolArray[zclass_idx][i] = bot; - InitializeSpecial(bot, _, _, true); - ResetDeadZombie(bot); -#if DEBUG - LogToFile(LOGFILE, "[SIPool] SI create: %d, (%s)pool sized(%d -> %d)", bot, g_sZombieClass[zclass_idx], iOldPoolSize, iNewPoolSize); -#endif - } -} - -void HookEvents() -{ - HookEvent("player_death", Event_PlayerDeath); - HookEvent("round_start", Event_RoundStart); -} - -void Event_PlayerDeath(Event event, const char[] name, bool dontBroadcast) -{ - int client = GetClientOfUserId(event.GetInt("userid")); - if (!(IsInfected(client) && IsFakeClient(client)) || IsTank(client)) return; - - // Return bot; - ResetDeadZombie(client); - g_iLastDeadTypeIdx = GetZombieClass(client) - 1; - g_iPoolArray[g_iLastDeadTypeIdx][g_iPoolSize[g_iLastDeadTypeIdx]++] = client; - -#if DEBUG - LogToFile(LOGFILE, "[SIPool] SI dead: %d, (%s)pool sized(%d -> %d)", client, g_sZombieClass[g_iLastDeadTypeIdx], g_iPoolSize[g_iLastDeadTypeIdx] - 1, g_iPoolSize[g_iLastDeadTypeIdx]); - LogToFile(LOGFILE, "[SIPool] %d is ghost:(%d), isalive:(%d)?", client, IsGhost(client), IsPlayerAlive(client)); -#endif -} - -void Event_RoundStart(Event event, const char[] name, bool dontBroadcast) -{ - for (int i = 0; i < ZC_COUNT; i++) - { - g_iPoolSize[i] = 0; - for (int v = 0; v < MAXSIZE; v++) - g_iPoolArray[i][v] = -1; - } - g_iLastDeadTypeIdx = -1; -} - -void PrepareSDKCalls() -{ - GameData hGameData = new GameData(GAMEDATA_FILE); - Address pReplaceWithBot = hGameData.GetAddress("NextBotCreatePlayerBot.jumptable"); - if (pReplaceWithBot != Address_Null && LoadFromAddress(pReplaceWithBot, NumberType_Int8) == 0x68) - PrepWindowsCreateBotCalls(pReplaceWithBot); - else - PrepLinuxCreateBotCalls(hGameData); - - // StartPrepSDKCall(SDKCall_Player); - // if (PrepSDKCall_SetFromConf(hGameData, SDKConf_Signature, "CTerrorPlayer::SetClass")) - // { - // PrepSDKCall_AddParameter(SDKType_PlainOldData, SDKPass_Plain); - // g_hSDK_CTerrorPlayer_SetClass = EndPrepSDKCall(); - // if (g_hSDK_CTerrorPlayer_SetClass == null) - // LogError("Failed to create SDKCall: \"CTerrorPlayer::SetClass\""); - // } - // else LogError("Failed to find signature: \"CTerrorPlayer::SetClass\""); - - StartPrepSDKCall(SDKCall_Static); - if (PrepSDKCall_SetFromConf(hGameData, SDKConf_Signature, "CBaseAbility::CreateForPlayer")) - { - PrepSDKCall_AddParameter(SDKType_CBaseEntity, SDKPass_Pointer); - PrepSDKCall_SetReturnInfo(SDKType_CBaseEntity, SDKPass_Pointer); - g_hSDK_CBaseAbility_CreateForPlayer = EndPrepSDKCall(); - if (g_hSDK_CBaseAbility_CreateForPlayer == null) - LogError("Failed to create SDKCall: \"CBaseAbility::CreateForPlayer\""); - } - else LogError("Failed to find signature: \"CBaseAbility::CreateForPlayer\""); - - StartPrepSDKCall(SDKCall_Player); - if (PrepSDKCall_SetFromConf(hGameData, SDKConf_Signature, "CCSPlayer::State_Transition")) - { - PrepSDKCall_AddParameter(SDKType_PlainOldData, SDKPass_Plain); - g_hSDK_CCSPlayer_State_Transition = EndPrepSDKCall(); - if (g_hSDK_CCSPlayer_State_Transition == null) - LogError("Failed to create SDKCall: \"CCSPlayer::State_Transition\""); - } - else LogError("Failed to find signature: \"CCSPlayer::State_Transition\""); - - StartPrepSDKCall(SDKCall_Player); - if (PrepSDKCall_SetFromConf(hGameData, SDKConf_Signature, "CTerrorPlayer::RoundRespawn")) - { - g_hSDK_CTerrorPlayer_RoundRespawn = EndPrepSDKCall(); - if (g_hSDK_CTerrorPlayer_RoundRespawn == null) - LogError("Failed to create SDKCall: \"CTerrorPlayer::RoundRespawn\""); - } - else LogError("Failed to find signature: \"CTerrorPlayer::RoundRespawn\""); - - delete hGameData; -} - -// #define HUNTER_ADDR 0 -// #define JOCKEY_ADDR 12 -// #define SPITTER_ADDR 24 -// #define CHARGER_ADDR 36 -// #define SMOKER_ADDR 48 -// #define BOOMER_ADDR 60 -// #define TANK_ADDR 72 -static int g_iZombieAddr[ZC_COUNT] = { - 48, 60, 0, 24, 12, 36 -}; -void PrepWindowsCreateBotCalls(Address pBaseAddr) -{ -#if DEBUG - TestName(pBaseAddr); -#endif - for (int i = 0; i < ZC_COUNT; i++) - { - Address pJumpAddr = pBaseAddr + view_as
(g_iZombieAddr[i]); - Address pFuncRefAddr = pJumpAddr + view_as
(6); - int funcRelOffset = LoadFromAddress(pFuncRefAddr, NumberType_Int32); - Address pCallOffsetBase = pJumpAddr + view_as
(10); - Address pNextBotCreatePlayerBotTAddr = pCallOffsetBase + view_as
(funcRelOffset); - - StartPrepSDKCall(SDKCall_Static); - if (!PrepSDKCall_SetAddress(pNextBotCreatePlayerBotTAddr)) - SetFailState("Unable to find NextBotCreatePlayer address in memory."); - PrepSDKCall_AddParameter(SDKType_String, SDKPass_Pointer); - PrepSDKCall_SetReturnInfo(SDKType_CBasePlayer, SDKPass_Pointer); - g_hSDK_NextBotCreatePlayerBot[i] = EndPrepSDKCall(); - } -} - -void PrepLinuxCreateBotCalls(GameData hGameData = null) -{ - static char signature_name[32]; - for (int i = 0; i < ZC_COUNT; i++) - { - Format(signature_name, sizeof(signature_name), "NextBotCreatePlayerBot<%s>", g_sZombieClass[i]); - StartPrepSDKCall(SDKCall_Static); - if (!PrepSDKCall_SetFromConf(hGameData, SDKConf_Signature, signature_name)) - SetFailState("Failed to find signature: %s", signature_name); - PrepSDKCall_AddParameter(SDKType_String, SDKPass_Pointer); - PrepSDKCall_SetReturnInfo(SDKType_CBasePlayer, SDKPass_Pointer); - g_hSDK_NextBotCreatePlayerBot[i] = EndPrepSDKCall(); - } -} - -#if DEBUG -void TestName(Address pBaseAddr) -{ - for (int i = 0; i < 7; i++) - { - Address pCaseBase = pBaseAddr + view_as
(i * 12); - Address pSIStringAddr = view_as
(LoadFromAddress(pCaseBase + view_as
(1), NumberType_Int32)); - static char SIName[32]; - LoadStringFromAddress(pSIStringAddr, SIName, sizeof(SIName)); - LogToFile(LOGFILE, "[SIPool] Found \"%s\"(%d) in memory.", SIName, i); - } -} - -void LoadStringFromAddress(Address pAddr, char[] buffer, int maxlength) -{ - int i; - char val; - while (i < maxlength) - { - val = LoadFromAddress(pAddr + view_as
(i), NumberType_Int8); - if (val == 0) - { - buffer[i] = '\0'; - break; - } - buffer[i++] = val; - } - buffer[maxlength - 1] = '\0'; -} -#endif - -// #define ABILITY_TRYTIMES 3 -// void SetClass(int client, int zombieClass) -// { -// int weapon = GetPlayerWeaponSlot(client, 0); -// if (weapon != -1) -// { -// RemovePlayerItem(client, weapon); -// RemoveEntity(weapon); -// } - -// int ability = GetEntPropEnt(client, Prop_Send, "m_customAbility"); -// if (ability != -1) RemoveEntity(ability); -// ability = -1; - -// SDKCall(g_hSDK_CTerrorPlayer_SetClass, client, zombieClass); - -// for (int count = 0; count < ABILITY_TRYTIMES && ability == -1; count++) -// ability = SDKCall(g_hSDK_CBaseAbility_CreateForPlayer, client); - -// if (ability != -1) SetEntPropEnt(client, Prop_Send, "m_customAbility", ability); -// else LogToFile(LOGFILE, "[SIPool] Failed to create ability for %N after %d times tried.", client, ABILITY_TRYTIMES); -// } - -void SetStateTransition(int client, int state) -{ - SDKCall(g_hSDK_CCSPlayer_State_Transition, client, state); -} - -void RespawnPlayer(int client) -{ - SDKCall(g_hSDK_CTerrorPlayer_RoundRespawn, client); -} - -int CreateSIBot(int zclass_idx) -{ - return SDKCall(g_hSDK_NextBotCreatePlayerBot[zclass_idx], g_sZombieClass[zclass_idx]); -} \ No newline at end of file diff --git a/addons/sourcemod/scripting/include/paiutils.inc b/addons/sourcemod/scripting/include/paiutils.inc deleted file mode 100644 index 54f50eccb..000000000 --- a/addons/sourcemod/scripting/include/paiutils.inc +++ /dev/null @@ -1,889 +0,0 @@ -/* - * @Author: 派蒙 - * @Last Modified by: 我是派蒙啊 - * @Create Date: 2022-01-14 11:54:43 - * @Last Modified time: 2024-02-04 12:19:46 - * @Github: http://github.com/PaimonQwQ - */ - -/* #################################################################### * - Paimon Utils - Author: 我是派蒙啊 - Version: 2023.07.15 - * #################################################################### */ - -#if defined _paiutils_included - #endinput -#endif -#define _paiutils_included - -// #define int(%1) view_as(%1) -// #define float(%1) view_as(%1) -// #define bool(%1) view_as(%1) -// #define char(%1) view_as(%1) - -#define TEAM_SPECTATOR 1 -#define TEAM_SURVIVOR 2 -#define TEAM_INFECTED 3 - -#define ZC_SMOKER 1 -#define ZC_BOOMER 2 -#define ZC_HUNTER 3 -#define ZC_SPITTER 4 -#define ZC_JOCKEY 5 -#define ZC_CHARGER 6 -#define ZC_WITCH 7 -#define ZC_TANK 8 - -#define CMDBOT_ATTACK 0 -#define CMDBOT_MOVE 1 -#define CMDBOT_RETREAT 2 -#define CMDBOT_RESET 3 - -enum -{ - Team_Spectator = 1, - Team_Survivor, - Team_Infected -}; - -enum -{ - ZC_Smoker = 1, - ZC_Boomer, - ZC_Hunter, - ZC_Spitter, - ZC_Jockey, - ZC_Charger, - ZC_Witch, - ZC_Tank -}; - -// enum -// { -// PC_UpperCut = 40, -// PC_RightHook = 43, -// PC_LeftHook = 45, -// PC_PoundGround1 = 46, -// PC_PoundGround2 = 47, -// TH_UnderCut = 48, -// TH_OverHand = 49, -// TH_FromHip = 50, -// TH_OverHead = 51 -// }; - -enum -{ - CmdBot_Attack = 0, // Force the bot to attack a specific target, even bypassing CTerrorPlayer::SetSenseFlags (DirectorScript.BOT_CANT_SEE). - CmdBot_Move = 1, // Force the bot to move to a specific location, which then they will do it unconditionally without performing any other AI behaviours controlled by themselves. - // This means that Survivor Bots and most player-controllable Special Infected won't attack anything when commanded, but Common Infected still automatically attack enemies if they close in enough. - CmdBot_Retreat = 2, // Force the bot to retreat from a target entity. Only works when used on Survivor Bots, and if target is a Tank. - CmdBot_Reset = 3 // Removes the active bot command and lets the AI resume controlling the bot. -}; - -/** - * @brief Returns true if client is correct. - * - * @param client Client index. - * @return True if client is correct. False otherwise. - */ -stock bool IsValidClient(int client) -{ - return (0 < client <= MaxClients && IsClientConnected(client) && IsClientInGame(client)); -} - -/** - * @brief Returns true if client is a survivor. - * - * @param client Client index. - * @return True if client is a survivor. False otherwise. - */ -stock bool IsSurvivor(int client) -{ - return (IsValidClient(client) && GetClientTeam(client) == TEAM_SURVIVOR); -} - -/** - * @brief Returns true if client is a SI(Special Infected). - * - * @param client Client index. - * @return True if client is a SI. False otherwise. - */ -stock bool IsInfected(int client) -{ - return (IsValidClient(client) && GetClientTeam(client) == TEAM_INFECTED); -} - -/** - * @brief Returns true if client is a spectator. - * - * @param client Client index. - * @return True if client is a spectator. False otherwise. - */ -stock bool IsSpectator(int client) -{ - return (IsValidClient(client) && GetClientTeam(client) == TEAM_SPECTATOR); -} - -/** - * @brief Returns true if client is in ghost mode. - * - * @param client Client index. - * @return True if client is in ghost mode. False otherwise. - */ -stock bool IsGhost(int client) -{ - return (IsValidClient(client) && view_as(GetEntProp(client, Prop_Send, "m_isGhost"))); -} - -/** - * @brief Returns true if client is a tank. - * - * @param client Client index. - * @return True if client is a tank. False otherwise. - */ -stock bool IsTank(int client) -{ - return (IsValidClient(client) && GetZombieClass(client) == ZC_Tank); -} - -/** - * @brief Returns true if client is being pinned. - * - * @param client Client index. - * @return True if client is being pinned. False otherwise. - */ -stock bool IsSurvivorPinned(int client) -{ - return (IsSurvivor(client) && (GetEntPropEnt(client, Prop_Send, "m_tongueOwner") > 0 || // smoker grabbed - GetEntPropEnt(client, Prop_Send, "m_carryAttacker") > 0 || // hunter pounded - GetEntPropEnt(client, Prop_Send, "m_pounceAttacker") > 0 || // charger carred - GetEntPropEnt(client, Prop_Send, "m_pummelAttacker") > 0 || // charger pounded - GetEntPropEnt(client, Prop_Send, "m_jockeyAttacker") > 0)); // jockey ridden -} - -/** - * @brief Returns who is pinning a client. - * - * @param client Client index. - * @return Infected index if client is being pinned. -1 otherwise. - */ -stock int GetSurvivorPinner(int client) -{ - if (!IsSurvivor(client)) return -1; - - return (GetEntPropEnt(client, Prop_Send, "m_tongueOwner") > 0 ? // smoker grabbed - GetEntPropEnt(client, Prop_Send, "m_tongueOwner") - : GetEntPropEnt(client, Prop_Send, "m_carryAttacker") > 0 ? // hunter pounded - GetEntPropEnt(client, Prop_Send, "m_carryAttacker") - : GetEntPropEnt(client, Prop_Send, "m_pounceAttacker") > 0 ? // charger carred - GetEntPropEnt(client, Prop_Send, "m_pounceAttacker") - : GetEntPropEnt(client, Prop_Send, "m_pummelAttacker") > 0 ? // charger pounded - GetEntPropEnt(client, Prop_Send, "m_pummelAttacker") - : GetEntPropEnt(client, Prop_Send, "m_jockeyAttacker") > 0 ? // jockey ridden - GetEntPropEnt(client, Prop_Send, "m_jockeyAttacker") - : -1); -} - -/** - * @brief Returns survivor who is pinned by client. - * - * @param client Client index. - * @return Survivor index who is pinned by client. -1 otherwise. - */ -stock int GetPinningSurvivor(int client) -{ - if (!IsInfected(client)) return -1; - return (GetEntPropEnt(client, Prop_Send, "m_tongueVictim") > 0 ? // smoker grabbed - GetEntPropEnt(client, Prop_Send, "m_tongueVictim") - : GetEntPropEnt(client, Prop_Send, "m_pounceVictim") > 0 ? // hunter pounded - GetEntPropEnt(client, Prop_Send, "m_pounceVictim") - : GetEntPropEnt(client, Prop_Send, "m_carryVictim") > 0 ? // charger carred - GetEntPropEnt(client, Prop_Send, "m_carryVictim") - : GetEntPropEnt(client, Prop_Send, "m_pummelVictim") > 0 ? // charger pounded - GetEntPropEnt(client, Prop_Send, "m_pummelVictim") - : GetEntPropEnt(client, Prop_Send, "m_jockeyVictim") > 0 ? // jockey ridden - GetEntPropEnt(client, Prop_Send, "m_jockeyVictim") - : -1); -} - -/** - * @brief Returns true if client is pinning a survivor. - * - * @param client Client index. - * @return True if client is pinning a survivor. False otherwise. - */ -stock bool IsPinningSurvivor(int client) -{ - return (IsInfected(client) && (GetEntPropEnt(client, Prop_Send, "m_tongueVictim") > 0 || // smoker grabbing - GetEntPropEnt(client, Prop_Send, "m_pounceVictim") > 0 || // hunter pounding - GetEntPropEnt(client, Prop_Send, "m_carryVictim") > 0 || // charger carrying - GetEntPropEnt(client, Prop_Send, "m_pummelVictim") > 0 || // charger pounding - GetEntPropEnt(client, Prop_Send, "m_jockeyVictim") > 0)); // jockey riding -} - -/** - * @brief Returns who is aiming client. - * - * @param aimee Client who is aimed. - * @param team Team type to included. - * @param sawable Is target seen threats. - * @return Client index for who aimed aimee. -1 otherwise. - */ -stock int GetClientAimedBy(int aimee, int team = 0, bool sawable = true) -{ - int target = -1; - for (int i = 1; i <= MaxClients; i++) - { - if (!IsValidClient(i) || (team && GetClientTeam(i) != team) || !IsPlayerAlive(i) || IsPlayerIncap(i) || (sawable && !IsEntitySawThreats(i))) continue; - int aimed = GetClientAimTarget(i); - if (aimed != aimee) continue; - target = i; - break; - } - - return target; -} - -/** - * @brief Returns closest client who is aiming aimee. - * - * @param aimee Client who is aimed. - * @param team Team type to included. - * @param sawable Is target seen threats. - * @return Client index for closest client. -1 otherwise. - */ -stock int GetClosestClientAimer(int aimee, int team, bool sawable = true) -{ - int target = -1; - float dis = -1.0, pos[3], tarPos[3]; - GetClientAbsOrigin(aimee, tarPos); - for (int i = 1; i <= MaxClients; i++) - { - if (!IsValidClient(i) || (team && GetClientTeam(i) != team) || !IsPlayerAlive(i) || IsPlayerIncap(i) || (sawable && !IsEntitySawThreats(i))) continue; - int aimed = GetClientAimTarget(i); - if (aimed != aimee) continue; - - GetClientAbsOrigin(i, pos); - float tmp = GetVectorDistance(tarPos, pos, true); - if (dis <= tmp && dis != -1) continue; - - target = i; - dis = tmp; - } - - return target; -} - -/** - * @brief Returns closest client of target or origin. - * - * @param target Target entity to begin searching. - * @param origin Position to begin searching, no use if target is valid. - * @param targetTeam Team type to included. - * @param sawable Is target seen threats. - * @param notAimed Should client not aim target. - * @param isIncap Can target be incap. - * @param isGhost Is target a ghost. - * @return Client index for closest client. -1 otherwise. - */ -stock int GetClosestClient(int target = -1, const float origin[3], int targetTeam = 0, bool sawable = true, bool notAimed = false, bool isIncapable = false, bool isGhost = false) -{ - int result = -1; - float dis = -1.0, pos[3], tarPos[3]; - AddVectors(tarPos, origin, tarPos); - if (IsValidEntity(target)) GetEntPropVector(target, Prop_Send, "m_vecOrigin", tarPos); - for (int i = 1; i <= MaxClients; i++) - { - if (!IsValidClient(i) || !IsPlayerAlive(i) || (targetTeam && GetClientTeam(i) != targetTeam) || (notAimed && GetClientAimTarget(i) == target) || (sawable && !IsEntitySawThreats(i)) || (!isIncapable && IsPlayerIncap(i)) || (!isGhost && IsGhost(i))) continue; - - GetClientAbsOrigin(i, pos); - float tmp = GetVectorDistance(tarPos, pos, true); - if (dis <= tmp && dis != -1) continue; - - result = i; - dis = tmp; - } - - return result; -} - -/** - * @brief Returns closest zombies of target or origin. - * - * @param target Target entity to begin searching. - * @param origin Position to begin searching, no use if target is valid. - * @param includeWitch Is included witch. - * @param includeRock Is included rock. - * @return Entity index for closest zombies. -1 otherwise. - */ -stock int GetClosestZombie(int target = -1, const float origin[3], bool includeWitch = true, bool includeRock = true) -{ - int result = -1; - int entCnt = GetMaxEntities(); - float dis = -1.0, pos[3], tarPos[3]; - AddVectors(tarPos, origin, tarPos); - if (IsValidEntity(target)) GetEntPropVector(target, Prop_Send, "m_vecOrigin", tarPos); - for (int i = MaxClients + 1; i <= entCnt; i++) - { - if (!IsValidEntity(i) || IsValidClient(i)) continue; - char clsName[32]; - GetEntityClassname(i, clsName, sizeof(clsName)); - if (strcmp(clsName, "infected") && (includeWitch && strcmp(clsName, "witch")) && (includeRock && strcmp(clsName, "tank_rock"))) continue; - if (GetEntProp(i, Prop_Data, "m_lifeState") || !GetEntProp(i, Prop_Data, "m_iHealth")) continue; - - GetEntPropVector(i, Prop_Send, "m_vecOrigin", pos); - float tmp = GetVectorDistance(tarPos, pos, true); - if (dis <= tmp && dis != -1) continue; - - result = i; - dis = tmp; - } - - return result; -} - -/** - * @brief Returns true if client is incapacitated. - * - * @param client Client index. - * @return True if client is incapacitated. False otherwise. - */ -stock bool IsPlayerIncap(int client) -{ - return (IsValidClient(client) && view_as(GetEntProp(client, Prop_Send, "m_isIncapacitated"))); -} - -/** - * @brief Returns true if survivor team is full. - * - * @return True if survivor team is full. False otherwise. - */ -stock bool IsSurvivorTeamFull() -{ - for (int i = 1; i <= MaxClients; i++) - if (IsSurvivor(i) && IsPlayerAlive(i) && IsFakeClient(i)) - return false; - - return true; -} - -/** - * @brief Returns true if survivors are all pinnded. - * - * @return True if survivors are all pinnded. False otherwise. - */ -stock bool IsAllSurvivorPinned() -{ - for (int i = 1; i <= MaxClients; i++) - if (IsSurvivor(i) && IsPlayerAlive(i) && !(IsSurvivorPinned(i) || IsPlayerIncap(i))) - return false; - - return true; -} - -/** - * @brief Returns true if survivors are all incapped. - * - * @return True if survivora are all incapped. False otherwise. - */ -stock bool IsAllSurvivorIncapped() -{ - for (int i = 1; i <= MaxClients; i++) - if (IsSurvivor(i) && !IsPlayerIncap(i)) - return false; - - return true; -} - -/** - * @brief Returns true if player saw threats. - * - * @param entity Entity index. - * @return True if player saw threats. False otherwise. - */ -stock bool IsEntitySawThreats(int entity) -{ - return (IsValidEntity(entity) && view_as(GetEntProp(entity, Prop_Send, "m_hasVisibleThreats"))); -} - -/** - * @brief Returns true if any survivor alives. - * - * @return True if any survivor alives. False otherwise. - */ -stock bool HasSurvivorAlive() -{ - for (int i = 1; i <= MaxClients; i++) - if (IsSurvivor(i) && IsPlayerAlive(i)) - return true; - - return false; -} - -/** - * @brief Returns true if any SI(Special Infected) but not tank alives. - * - * @return True if any SI(Special Infected) but not tank alives. False otherwise. - */ -stock bool HasInfectedAlive(bool includeTank = false) -{ - for (int i = 1; i <= MaxClients; i++) - if (IsInfected(i) && IsPlayerAlive(i) && !IsPlayerIncap(i)) - { - if (!includeTank && IsTank(i)) continue; - return true; - } - - return false; -} - -/** - * @brief Get tank's frustration. - * - * @param tankClient Client index. - * @return Tank's frustration. -1 for false. - */ -stock int GetTankFrustration(int tankClient) -{ - return IsValidClient(tankClient) ? 100 - GetEntProp(tankClient, Prop_Send, "m_frustration") : -1; -} - -/** - * @brief Set tank's frustration. - * - * @param tankClient Client index. - * @param frustration Frustration value. - * @return True if succeed. False otherwise. - */ -stock bool SetTankFrustration(int tankClient, int frustration) -{ - if (!IsValidClient(tankClient)) return false; - if (frustration < 0 || frustration > 100) return false; - - SetEntProp(tankClient, Prop_Send, "m_frustration", 100 - frustration); - return true; -} - -/** - * @brief Get player's health. - * - * @param client Client index. - * @param temp_heal Client temp health. - * @param total_heal Client total health. - * @return Player's health. -1 for false. - */ -stock int GetPlayerHealth(int client, bool temp_heal = false, bool total_heal = false) -{ - if (!IsValidClient(client)) return -1; - if (!IsPlayerAlive(client)) return 0; - - if (total_heal) temp_heal = false; - if (!IsSurvivor(client)) temp_heal = total_heal = false; - - int temp = 0, health = GetEntProp(client, Prop_Send, "m_iHealth"); - if (temp_heal) temp = RoundToCeil(GetEntPropFloat(client, Prop_Send, "m_healthBuffer") - ((GetGameTime() - GetEntPropFloat(client, Prop_Send, "m_healthBufferTime")) * FindConVar("pain_pills_decay_rate").FloatValue)) - 1; - int result = !total_heal ? (temp_heal ? temp : health) : health + temp; - - return result; -} - -/** - * @brief Set player's health. - * - * @param client Client index. - * @param health Health value. - * @return True if succeed. False otherwise. - */ -stock bool SetPlayerHealth(int client, int health) -{ - if (!IsValidClient(client)) return false; - if (health < 0) return false; - - SetEntProp(client, Prop_Send, "m_iHealth", health); - return true; -} - -/** - * Returns whether player is on third strike. - * - * @param client Client index. - * @return True if on third strike, false otherwise. - * @error Invalid client index. - */ -stock bool IsPlayerOnThirdStrike(int client) -{ - return view_as(GetEntProp(client, Prop_Send, "m_bIsOnThirdStrike")); -} - -/** - * @brief Execute a command. - * - * @param client Client index. - * @param strCmd Command string. - * @param strPrm CmdParam string. - * @return True if succeed. False otherwise. - */ -stock bool ExecuteCommand(int client, const char[] strCmd, const char[] strPrm = "") -{ - if (!IsValidClient(client)) return false; - - int flags = GetCommandFlags(strCmd); - SetCommandFlags(strCmd, flags & (~FCVAR_CHEAT)); - FakeClientCommand(client, "%s %s", strCmd, strPrm); - SetCommandFlags(strCmd, flags); - - return true; -} - -/** - * @brief Get client SI(Special Infected) class. - * - * @param client Client index. - * @return SI class. -1 for false. - */ -stock int GetZombieClass(int client) -{ - if (!IsValidClient(client) || !IsInfected(client)) return -1; - - return GetEntProp(client, Prop_Send, "m_zombieClass"); -} - -/** - * @brief Get survivor count. - * - * @return Survivor count. - */ -stock int GetSurvivorCount() -{ - int count = 0; - for (int i = 1; i <= MaxClients; i++) - if (IsSurvivor(i)) - count++; - - return count; -} - -/** - * @brief Get SI(Special Infected) count. - * - * @return SI count. - */ -stock int GetSInfectedCount() -{ - int count = 0; - for (int i = 1; i <= MaxClients; i++) - if (IsInfected(i)) - count++; - - return count; -} - -/** - * @brief Get client count without special infected. - * - * @return Client count without special infected. - */ -stock int GetClientNoSICount() -{ - int count = 0; - for (int i = 1; i <= MaxClients; i++) - if (IsValidClient(i) && IsClientInGame(i) && !IsInfected(i)) - count++; - - return count; -} - -/** - * @brief Get player survivor count. - * - * @return Player survivor count. - */ -stock int GetSurvivorPlayerCount() -{ - int count = 0; - for (int i = 1; i <= MaxClients; i++) - if (IsSurvivor(i) && !IsFakeClient(i)) - count++; - - return count; -} - -/** - * @brief Get player SI(Special Infected) count. - * - * @return Player SI count. - */ -stock int GetInfectedPlayerCount() -{ - int count = 0; - for (int i = 1; i <= MaxClients; i++) - if (IsInfected(i) && !IsFakeClient(i)) - count++; - - return count; -} - -/** - * @brief Get alive survivor count. - * - * @return Alive survivor count. - */ -stock int GetAliveSurvivorCount() -{ - int count = 0; - for (int i = 1; i <= MaxClients; i++) - if (IsSurvivor(i) && IsPlayerAlive(i)) - count++; - - return count; -} - -/** - * @brief Get alive SI count. - * - * @return Alive SI count. - */ -stock int GetAliveInfectedCount() -{ - int count = 0; - for (int i = 1; i <= MaxClients; i++) - if (IsInfected(i) && IsPlayerAlive(i)) - count++; - - return count; -} - -/** - * @brief Delete client items - * - * @return No return. - */ -stock void DeleteInventoryItem(int client) -{ - if (!IsValidClient(client)) return; - - for (int s = 0; s < 5; s++) - { - int item = GetPlayerWeaponSlot(client, s); - if (item > 0) - RemovePlayerItem(client, item); - } -} - -/** - * @brief Uses the VScript "CommandABot" function to command a bot to attack, move, retreat or reset previous command - * - * @param entity The bot or infected to command - * @param target The Special Infected to target (used for types "CMDBOT_ATTACK" and "CMDBOT_RETREAT") - * @param type Type of command - * @param pos Move to this location (Used for type "CMDBOT_MOVE") - * - * @return Returns false when unable to perform, true otherwise. - */ -stock bool CommandABot(int entity, int target = 0, int type, float pos[3] = NULL_VECTOR) -{ - if (!IsValidClient(entity) || !IsFakeClient(entity)) return false; - char param[PLATFORM_MAX_PATH]; - switch (type) - { - case CMDBOT_ATTACK, CMDBOT_RETREAT: - Format(param, sizeof(param), "CommandABot({cmd = %d, bot = GetPlayerFromUserID(%i), target = GetPlayerFromUserID(%i)})", type, GetClientUserId(entity), GetClientUserId(target)); - case CMDBOT_MOVE: - Format(param, sizeof(param), "CommandABot({cmd = %d, pos = Vector(%f, %f, %f), bot = GetPlayerFromUserID(%i)})", type, pos[0], pos[1], pos[2], GetClientUserId(entity)); - case CMDBOT_RESET: - Format(param, sizeof(param), "CommandABot({cmd = %d, bot = GetPlayerFromUserID(%i)})", type, GetClientUserId(entity)); - - default: - return false; - } - - return ExecuteCommand(entity, "script", param); -} - -/** - * @brief Runs a specified VScript code and returns values from it. - * - * @param code The VScript code to execute. Maximum length seems to be 1006 characters. - * @param result Buffer to copy return data to. You can use StringToInt or StringToFloat if required. - * @param maxlength Maximum size of the result. - * - * @return True on success, false otherwise. - */ -#pragma deprecated This is nonachievement. -stock bool ExecuteVscript(char[] code, char[] result, int maxlength) -{ - int script = CreateEntityByName("logic_script"); - if (!IsValidEdict(script)) return false; - DispatchSpawn(script); - - if ((StrContains(code, "") | StrContains(code, "")) < 0) return false; - - char buffer[1024], put[] = "NetProps.SetPropString(%d, \"m_iName\", "; - Format(put, sizeof(put), put, script); - strcopy(buffer, sizeof(buffer), code); - ReplaceString(buffer, sizeof(buffer), "", put); - ReplaceString(buffer, sizeof(buffer), "", ");"); - SetVariantString(buffer); - PrintToChatAll("%s", buffer); - - AcceptEntityInput(script, "RunScriptCode"); - GetEntPropString(script, Prop_Data, "m_iName", result, maxlength); - - AcceptEntityInput(script, "Kill"); - RemoveEdict(script); - - return true; -} - -/** - * Sets custom ability cooldown of client. - * - * Note: Used for the Infected class abilities. - * - * @param client Client index. - * @param time How long before client can use their custom ability. - * - * @return True if set, false if no ability found. - */ -stock bool SetAbilityCooldown(int client, float time) -{ - int ability = GetEntPropEnt(client, Prop_Send, "m_customAbility"); - if (ability > 0 && IsValidEdict(ability)) - { - SetEntPropFloat(ability, Prop_Send, "m_duration", time); - SetEntPropFloat(ability, Prop_Send, "m_timestamp", GetGameTime() + time); - return true; - } - return false; -} - -/** - * Get entity abs origin. - * - * @param entity Entity index. - * @param origin Entity position. - */ -stock void GetEntityAbsOrigin(int entity, float origin[3]) -{ - if (!IsValidEntity(entity)) - ThrowError("Invalid entity index %d", entity); - float mins[3], maxs[3]; - GetEntPropVector(entity, Prop_Send, "m_vecOrigin", origin); - GetEntPropVector(entity, Prop_Send, "m_vecMins", mins); - GetEntPropVector(entity, Prop_Send, "m_vecMaxs", maxs); - - origin[0] += (mins[0] + maxs[0]) * 0.5; - origin[1] += (mins[1] + maxs[1]) * 0.5; - origin[2] += (mins[2] + maxs[2]) * 0.5; -} - -// stock float GetVectorMinDistanceByPlane(const float vec1[3], const float vec2[3], bool squared = false) -// { -// float zDis = FloatAbs(vec1[2] - vec2[2]); -// if(squared) zDis = Pow(zDis, 2.0); -// float xOyDis = GetVector2Distance(vec1, vec2, squared); - -// return zDis > xOyDis ? xOyDis : zDis; -// } - -/** - * Calculates the distance between two vectors(ignored z axis). - * - * @param vec1 First vector. - * @param vec2 Second vector. - * @param squared If true, the result will be squared (for optimization). - * @return Vector2D distance. - */ -stock float GetVector2Distance(const float vec1[3], const float vec2[3], bool squared = false) -{ - float vecA[3], vecB[3]; - CopyVector(vec1, vecA); - CopyVector(vec2, vecB); - vecA[2] = vecB[2] = 0.0; - - return GetVectorDistance(vecA, vecB, squared); -} - -stock void CopyVector(const float origin[3], float vector[3]) -{ - for (int i = 0; i < 3; i++) - vector[i] = origin[i]; -} - -stock float GetEntityDistance(int ent1, int ent2, bool squared = false) -{ - if (!IsValidEntity(ent1)) - ThrowError("Error: invalid entity index %d", ent1); - if (!IsValidEntity(ent2)) - ThrowError("Error: invalid entity index %d", ent2); - - float pos1[3], pos2[3]; - if (IsValidClient(ent1)) - GetClientAbsOrigin(ent1, pos1); - else GetEntityAbsOrigin(ent1, pos1); - if (IsValidClient(ent2)) - GetClientAbsOrigin(ent2, pos2); - else GetEntityAbsOrigin(ent2, pos2); - - return GetVectorDistance(pos1, pos2, squared); -} - -//== From https://forums.alliedmods.net/showthread.php?t=342637 -//== Author: LinLinLin -stock bool IsInClientView(int entity, int client) -{ - float entPos[3], playerPos[3], playerAng[3]; - float eye2entVector[3]; - float eye2fwdVector[3]; - char temp[16]; - GetClientInfo(client, "fov_desired", temp, sizeof(temp)); - int fov = StringToInt(temp); - - GetClientEyePosition(client, playerPos); - GetEntPropVector(entity, Prop_Data, "m_vecOrigin", entPos); - MakeVectorFromPoints(playerPos, entPos, eye2entVector); - - GetClientEyeAngles(client, playerAng); - GetAngleVectors(playerAng, eye2fwdVector, NULL_VECTOR, NULL_VECTOR); - - NormalizeVector(eye2entVector, eye2entVector); - NormalizeVector(eye2fwdVector, eye2fwdVector); - - float radian = ArcCosine(GetVectorDotProduct(eye2entVector, eye2fwdVector)); - /** - * let me explain how this degree radian. - * - * DotProduct = |a||b|cosθ, this is the vector theorem. - * we have normalize the vector so |a| = |b| = 1. - * in this case DotProduct = cosθ. - * ArcCosine() let us get the radian of θ. - * FOV is a degree, we need make it become radian. - */ - return radian <= DegToRad(fov + 0.0) / 2; -} - -stock bool IsFirstMap() -{ - int count, entity; - while ((entity = FindEntityByClassname(entity, "info_landmark")) > 0) - count++; - return count < 2 && FindEntityByClassname(0, "trigger_finale") == -1; -} - -stock bool IsIntro() -{ - int entity = FindEntityByClassname(0, "terror_gamerules"); - if (!IsValidEntity(entity)) return false; - - return view_as(GetEntProp(entity, Prop_Send, "m_bInIntro")); -} - -stock bool IsMissionFinal() -{ - int count, entity; - while ((entity = FindEntityByClassname(entity, "info_landmark")) > 0) - count++; - - return count < 2 && FindEntityByClassname(0, "trigger_finale") > 0; -} diff --git a/addons/sourcemod/scripting/include/si_pool.inc b/addons/sourcemod/scripting/include/si_pool.inc deleted file mode 100644 index 2b05baf95..000000000 --- a/addons/sourcemod/scripting/include/si_pool.inc +++ /dev/null @@ -1,90 +0,0 @@ -/* - * @Author: 我是派蒙啊 - * @Last Modified by: 我是派蒙啊 - * @Create Date: 2024-02-17 18:11:22 - * @Last Modified time: 2024-03-25 18:59:48 - * @Github: https://github.com/Paimon-Kawaii - */ - -#if defined _sipool_included_ - #endinput -#endif -#define _sipool_included_ - -public SharedPlugin __pl_sipool = { - name = "si_pool", - file = "si_pool.smx", -#if defined REQUIRE_PLUGIN - required = 1, -#else - required = 0, -#endif -}; - -// #define SIZABLE 0 - -public void __pl_sipool_SetNTVOptional() -{ - // MarkNativeAsOptional("SIPool.SIPool"); - MarkNativeAsOptional("SIPool.Instance"); - // MarkNativeAsOptional("SIPool.Size.get"); - -// #if SIZABLE -// MarkNativeAsOptional("SIPool.Narrow"); -// MarkNativeAsOptional("SIPool.Expand"); -// MarkNativeAsOptional("SIPool.Resize"); -// #endif - - MarkNativeAsOptional("SIPool.RequestSIBot"); - MarkNativeAsOptional("SIPool.ReturnSIBot"); -} - -// Provided by "BHaType": -// For the "L4D_State_Transition" native -// X -> Y (means X state will become Y state on next frame or some seconds later) -#define STATE_ACTIVE 0 -#define STATE_WELCOME 1 // -> STATE_PICKING_TEAM -#define STATE_PICKING_TEAM 2 -#define STATE_PICKINGCLASS 3 // -> STATE_ACTIVE -#define STATE_DEATH_ANIM 4 // -> STATE_DEATH_WAIT_FOR_KEY -#define STATE_DEATH_WAIT_FOR_KEY 5 // -> STATE_OBSERVER_MODE -#define STATE_OBSERVER_MODE 6 -#define STATE_WAITING_FOR_RESCUE 7 -#define STATE_GHOST 8 -#define STATE_INTRO_CAMERA 9 - -methodmap SIPool __nullable__ -{ - // public native SIPool(); - - public static native SIPool Instance(); - - // property int Size { - // public native get(); - // } - -// #if SIZABLE -// public native void Narrow(int size); -// public native void Expand(int size); -// public native void Resize(int size); -// #endif - - /** - * Request a Special infected Bot from SIPool - * - * @param zclass Zombie class - * @param origin Position to spawn - * @param angle Angle that zombie look at - * @return Client index - */ - public native int RequestSIBot(int zclass, const float origin[3] = NULL_VECTOR, const float angle[3] = NULL_VECTOR); - - /** - * Return a Special infected Bot to SIPool - * Bot will automatically return after dead - * - * @param client Zombie Bot to return - * @return True is success, false otherwise - */ - public native bool ReturnSIBot(int client); -} \ No newline at end of file diff --git a/cfg/cfgogl/allcharger/confogl_plugins.cfg b/cfg/cfgogl/allcharger/confogl_plugins.cfg index 84a679dca..7410fb0c8 100644 --- a/cfg/cfgogl/allcharger/confogl_plugins.cfg +++ b/cfg/cfgogl/allcharger/confogl_plugins.cfg @@ -32,7 +32,6 @@ sm plugins load optional/l4d2_skill_detect.smx //sm plugins load optional/AnneHappy/ai_spitter_new.smx sm plugins load optional/AnneHappy/ai_charger_2.smx //sm plugins load optional/AnneHappy/ai_boomer_new.smx -sm plugins load optional/AnneHappy/si_pool.smx sm plugins load optional/AnneHappy/infected_control.smx sm plugins load optional/AnneHappy/l4d_target_override.smx sm plugins load optional/AnneHappy/SI_Target_limit.smx diff --git a/cfg/cfgogl/alone/confogl_plugins.cfg b/cfg/cfgogl/alone/confogl_plugins.cfg index dc17b44b4..ab9d24070 100644 --- a/cfg/cfgogl/alone/confogl_plugins.cfg +++ b/cfg/cfgogl/alone/confogl_plugins.cfg @@ -37,7 +37,6 @@ sm plugins load optional/AnneHappy/l4d2_hunter_patch.smx sm plugins load optional/AnneHappy/ai_hunter_2.smx sm plugins load optional/AnneHappy/ai_jockey_2.smx sm plugins load optional/AnneHappy/ai_charger_2.smx -sm plugins load optional/AnneHappy/si_pool.smx sm plugins load optional/AnneHappy/infected_control.smx sm plugins load optional/AnneHappy/l4d_target_override.smx sm plugins load optional/AnneHappy/SI_Target_limit.smx diff --git a/cfg/cfgogl/annehappy/confogl_plugins.cfg b/cfg/cfgogl/annehappy/confogl_plugins.cfg index bf0d47d8f..8becb8977 100644 --- a/cfg/cfgogl/annehappy/confogl_plugins.cfg +++ b/cfg/cfgogl/annehappy/confogl_plugins.cfg @@ -34,7 +34,6 @@ sm plugins load optional/AnneHappy/ai_jockey_2.smx sm plugins load optional/AnneHappy/ai_spitter_2.smx sm plugins load optional/AnneHappy/ai_charger_2.smx sm plugins load optional/AnneHappy/ai_boomer_2.smx -sm plugins load optional/AnneHappy/si_pool.smx sm plugins load optional/AnneHappy/infected_control.smx sm plugins load optional/AnneHappy/l4d_target_override.smx sm plugins load optional/AnneHappy/SI_Target_limit.smx diff --git a/cfg/cfgogl/hunters/confogl_plugins.cfg b/cfg/cfgogl/hunters/confogl_plugins.cfg index 37eb6c047..1d35cbaec 100644 --- a/cfg/cfgogl/hunters/confogl_plugins.cfg +++ b/cfg/cfgogl/hunters/confogl_plugins.cfg @@ -33,7 +33,6 @@ sm plugins load optional/l4d2_skill_detect.smx //------------------------------------------- sm plugins load optional/AnneHappy/l4d2_hunter_patch.smx sm plugins load optional/AnneHappy/ai_hunter_2.smx -sm plugins load optional/AnneHappy/si_pool.smx sm plugins load optional/AnneHappy/infected_control.smx sm plugins load optional/AnneHappy/l4d_target_override.smx sm plugins load optional/AnneHappy/SI_Target_limit.smx diff --git a/cfg/cfgogl/witchparty/confogl_plugins.cfg b/cfg/cfgogl/witchparty/confogl_plugins.cfg index 63f347f17..45bf58fec 100644 --- a/cfg/cfgogl/witchparty/confogl_plugins.cfg +++ b/cfg/cfgogl/witchparty/confogl_plugins.cfg @@ -28,7 +28,6 @@ sm plugins load optional/l4d2_skill_detect.smx //------------------------------------------- sm plugins load optional/AnneHappy/l4d2_hunter_patch.smx sm plugins load optional/AnneHappy/ai_hunter_2.smx -sm plugins load optional/AnneHappy/si_pool.smx sm plugins load optional/AnneHappy/infected_control.smx sm plugins load optional/AnneHappy/l4d_target_override.smx sm plugins load optional/AnneHappy/SI_Target_limit.smx