diff --git a/.gitignore b/.gitignore index d871326..61db84e 100644 --- a/.gitignore +++ b/.gitignore @@ -2,5 +2,6 @@ /release/* !/release/plugin.bat !/release/vpk.bat -!/release/addoninfo.txt +!/release/*-addoninfo.txt + /plugin/*-build diff --git a/README.md b/README.md index 4db817a..96e9426 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ [使用说明](https://lin515.com/archives/182.html) -[创意工坊](https://steamcommunity.com/sharedfiles/filedetails/?id=2587952986) \ No newline at end of file +[创意工坊](https://steamcommunity.com/sharedfiles/filedetails/?id=2587952986) + +![Rena](doc/0Rena.png) \ No newline at end of file diff --git a/doc/0Rena.png b/doc/0Rena.png new file mode 100644 index 0000000..3477803 Binary files /dev/null and b/doc/0Rena.png differ diff --git a/plugin/CMakeLists.txt b/plugin/CMakeLists.txt index f00c387..dbf9581 100644 --- a/plugin/CMakeLists.txt +++ b/plugin/CMakeLists.txt @@ -12,73 +12,74 @@ set(HL2PUB ${HL2SDKL4D2}/public) set(HL2GAME ${HL2SDKL4D2}/game) if(WIN32) - link_directories(${HL2SDKL4D2}/lib/public) - set(LINK_LIBRARY - tier0 - vstdlib - tier1 - tier2 - tier3 - mathlib - legacy_stdio_definitions # vc++2015及以上需链接此库 - ) - add_compile_options(/wd4819) # 禁止字符集警告 - # 静态链接到CRT库 - string(REPLACE "/MD" "/MT" CMAKE_CXX_FLAGS_DEBUG ${CMAKE_CXX_FLAGS_DEBUG}) - string(REPLACE "/MD" "/MT" CMAKE_CXX_FLAGS_RELEASE ${CMAKE_CXX_FLAGS_RELEASE}) - string(REPLACE "/MD" "/MT" CMAKE_CXX_FLAGS_MINSIZEREL ${CMAKE_CXX_FLAGS_MINSIZEREL}) - string(REPLACE "/MD" "/MT" CMAKE_CXX_FLAGS_RELWITHDEBINFO ${CMAKE_CXX_FLAGS_RELWITHDEBINFO}) - set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /NODEFAULTLIB:libc") - set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /NODEFAULTLIB:libcd") - # set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /NODEFAULTLIB:libcmt") # 如果动态链接到CRT库,需要忽略libcmt + link_directories(${HL2SDKL4D2}/lib/public) + set(LINK_LIBRARY + tier0 + vstdlib + tier1 + tier2 + tier3 + mathlib + legacy_stdio_definitions # vc++2015及以上需链接此库 + ) + add_compile_options(/wd4819) # 禁止字符集警告 + # 静态链接到CRT库 + string(REPLACE "/MD" "/MT" CMAKE_CXX_FLAGS_DEBUG ${CMAKE_CXX_FLAGS_DEBUG}) + string(REPLACE "/MD" "/MT" CMAKE_CXX_FLAGS_RELEASE ${CMAKE_CXX_FLAGS_RELEASE}) + string(REPLACE "/MD" "/MT" CMAKE_CXX_FLAGS_MINSIZEREL ${CMAKE_CXX_FLAGS_MINSIZEREL}) + string(REPLACE "/MD" "/MT" CMAKE_CXX_FLAGS_RELWITHDEBINFO ${CMAKE_CXX_FLAGS_RELWITHDEBINFO}) + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /NODEFAULTLIB:libc") + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /NODEFAULTLIB:libcd") + # set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /NODEFAULTLIB:libcmt") # 如果动态链接到CRT库,需要忽略libcmt elseif(UNIX) - link_directories(${L4D2BIN}) # 不要设置到 ${HL2SDKL4D2}/lib/linux .so需使用服务器最新 - get_filename_component(ABSLIB ${HL2SDKL4D2}/lib/linux ABSOLUTE) - set(LINK_LIBRARY - libtier0_srv.so - libvstdlib_srv.so - ${ABSLIB}/tier1_i486.a - ${ABSLIB}/tier2_i486.a - ${ABSLIB}/tier3_i486.a - ${ABSLIB}/mathlib_i486.a - ) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_LINUX -DL4D2 -m32 \ - -Dstricmp=strcasecmp -D_stricmp=strcasecmp -D_strnicmp=strncasecmp \ - -Dstrnicmp=strncasecmp -D_snprintf=snprintf -D_vsnprintf=vsnprintf \ - -D_alloca=alloca -Dstrcmpi=strcasecmp -Wall -Wno-switch \ - -Wno-error=uninitialized -Wno-unused -Wno-error=delete-non-virtual-dtor \ - -mfpmath=sse -msse -DHAVE_STDINT_H \ - -Wno-non-virtual-dtor -fno-rtti -fno-threadsafe-statics \ - -Wno-overloaded-virtual -fpermissive \ - -m32 -ldl -lm -static-libgcc -static-libstdc++ -shared" - ) - set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -funroll-loops -pipe -fno-strict-aliasing") - set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${CMAKE_BUILD_TYPE}") + link_directories(${L4D2BIN}) # 不要设置到 ${HL2SDKL4D2}/lib/linux .so需使用服务器最新 + get_filename_component(ABSLIB ${HL2SDKL4D2}/lib/linux ABSOLUTE) + set(LINK_LIBRARY + libtier0_srv.so + libvstdlib_srv.so + ${ABSLIB}/tier1_i486.a + ${ABSLIB}/tier2_i486.a + ${ABSLIB}/tier3_i486.a + ${ABSLIB}/mathlib_i486.a + ) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_LINUX -DL4D2 -m32 \ + -Dstricmp=strcasecmp -D_stricmp=strcasecmp -D_strnicmp=strncasecmp \ + -Dstrnicmp=strncasecmp -D_snprintf=snprintf -D_vsnprintf=vsnprintf \ + -D_alloca=alloca -Dstrcmpi=strcasecmp -Wall -Wno-switch \ + -Wno-error=uninitialized -Wno-unused -Wno-error=delete-non-virtual-dtor \ + -mfpmath=sse -msse -DHAVE_STDINT_H \ + -Wno-non-virtual-dtor -fno-rtti -fno-threadsafe-statics \ + -Wno-overloaded-virtual -fpermissive \ + -m32 -ldl -lm -static-libgcc -static-libstdc++ -shared" + ) + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -funroll-loops -pipe -fno-strict-aliasing") + set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${CMAKE_BUILD_TYPE}") endif() # 创建库 add_library(LinGe_VScripts SHARED) if(UNIX) - set_target_properties(LinGe_VScripts PROPERTIES PREFIX "") # 去掉输出文件名的lib前缀 + set_target_properties(LinGe_VScripts PROPERTIES PREFIX "") # 去掉输出文件名的lib前缀 endif() target_sources(LinGe_VScripts PRIVATE - LinGe_VScripts.cpp - sdkapi/MemoryUtils/MemoryUtils.cpp - sdkapi/sdkapi.cpp - sdkapi/signature.cpp + LinGe_VScripts.cpp + sdkapi/MemoryUtils/MemoryUtils.cpp + sdkapi/sdkapi.cpp + sdkapi/signature.cpp ) target_include_directories(LinGe_VScripts PRIVATE - ${HL2PUB} - ${HL2PUB}/tier0 - ${HL2PUB}/tier1 - ${HL2PUB}/tier2 - ${HL2PUB}/tier3 - ${HL2GAME}/shared - ${HL2GAME}/server - ${HL2GAME}/client + ${HL2PUB} + ${HL2PUB}/tier0 + ${HL2PUB}/tier1 + ${HL2PUB}/tier2 + ${HL2PUB}/tier3 + ${HL2GAME}/shared + ${HL2GAME}/server + ${HL2GAME}/client + ${HL2SDKL4D2}/common ) target_link_libraries(LinGe_VScripts PRIVATE ${LINK_LIBRARY}) \ No newline at end of file diff --git a/plugin/LinGe_VScripts.cpp b/plugin/LinGe_VScripts.cpp index 3272298..0607803 100644 --- a/plugin/LinGe_VScripts.cpp +++ b/plugin/LinGe_VScripts.cpp @@ -14,15 +14,6 @@ * * You should have received a copy of the GNU General Public License along with * this program. If not, see . - * - * As a special exception, AlliedModders LLC gives you permission to link the - * code of this program (as well as its derivative works) to "Half-Life 2," the - * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software - * by the Valve Corporation. You must obey the GNU General Public License in - * all respects for all other code used. Additionally, AlliedModders LLC grants - * this exception to all derivative works. AlliedModders LLC defines further - * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), - * or . */ #include #include @@ -34,14 +25,14 @@ LinGe_VScripts plugin; EXPOSE_SINGLE_INTERFACE_GLOBALVAR(LinGe_VScripts, IServerPluginCallbacks, INTERFACEVERSION_ISERVERPLUGINCALLBACKS, plugin); // ConVar -ConVar *cv_pSvMaxplayers = nullptr; -ConVar cv_time("linge_time", "", FCVAR_PRINTABLEONLY, "Server system time."); -ConVar cv_format("linge_time_format", "%Y-%m-%d %H:%M:%S", FCVAR_SERVER_CAN_EXECUTE, "linge_time format string, see also:https://www.runoob.com/cprogramming/c-function-strftime.html", false, 0.0, false, 0.0, LinGe_VScripts::OnTimeFormatChanged); +ConVar cv_vscriptReturn("linge_vscript_return", "", FCVAR_HIDDEN|FCVAR_SPONLY, "Return VScript values."); +ConVar cv_lookPing("linge_look_ping", "1", FCVAR_NOTIFY, "PlayerPing is executed when the 'vocalize smartlook' command is issued.", true, 0.0, true, 1.0, LinGe_VScripts::OnLookPingChanged); +ConVar cv_time("linge_time", "", FCVAR_PRINTABLEONLY|FCVAR_SPONLY, "Server system time."); +ConVar cv_format("linge_time_format", "%Y-%m-%d %H:%M:%S", FCVAR_NONE, "linge_time format string, see also:https://www.runoob.com/cprogramming/c-function-strftime.html", false, 0.0, false, 0.0, LinGe_VScripts::OnTimeFormatChanged); char g_sTimeFormat[50] = "%Y-%m-%d %H:%M:%S"; +bool g_bLookPing = true; -LinGe_VScripts::LinGe_VScripts() : - m_iMaxClients(0), - m_bIsFristStart(true) +LinGe_VScripts::LinGe_VScripts() : m_iClientCommandIndex(0), m_bPlayerPingLoaded(false) { } LinGe_VScripts::~LinGe_VScripts() {} @@ -52,11 +43,13 @@ bool LinGe_VScripts::Load(CreateInterfaceFn interfaceFactory, CreateInterfaceFn ConVar_Register(); - _Msg("Loaded.\n"); + PL_Msg("Loaded.\n"); return true; } void LinGe_VScripts::Unload(void) { + SDKAPI::iGameEventManager->RemoveListener(this); + ConVar_Unregister(); SDKAPI::UnInitialize(); } @@ -81,47 +74,71 @@ void LinGe_VScripts::GameFrame(bool simulating) } } -void LinGe_VScripts::ServerActivate(edict_t *pEdictList, int edictCount, int clientMax) +void LinGe_VScripts::LevelInit(char const *pMapName) { + SDKAPI::iGameEventManager->AddListener(this, "round_start", true); +} +void LinGe_VScripts::LevelShutdown(void) { + SDKAPI::iGameEventManager->RemoveListener(this); +} + +void LinGe_VScripts::FireGameEvent(IGameEvent * event) { - m_iMaxClients = clientMax; - // sv_maxplayers 参数是 l4dtoolz 插件创建的,其通过 metamod 加载 - // 本插件一定比 l4dtoolz 先载入,所以不能在插件载入时就去获取 sv_maxplayers - if (m_bIsFristStart) + const char *name = event->GetName(); + if (Q_stricmp(name, "round_start") == 0) { - cv_pSvMaxplayers = SDKAPI::iCvar->FindVar("sv_maxplayers"); - if (cv_pSvMaxplayers) + // 验证根表下是否存在 LinPlayerPing + SDKAPI::L4D2_RunScript("Convars.SetValue(\"linge_vscript_return\", getroottable().rawin(\"LinPlayerPing\"));"); + if (cv_vscriptReturn.GetBool()) { - // 保存原有 callback,然后安装自己的 callback - // 直接访问 m_fnChangeCallback 需要修改 hl2sdk-l4d2/public/tier1/convar.h - // 将 m_fnChangeCallback 声明为 public - SvMaxplayersCallback = cv_pSvMaxplayers->m_fnChangeCallback; - cv_pSvMaxplayers->InstallChangeCallback(LinGe_VScripts::OnSvMaxplayersChanged); + m_bPlayerPingLoaded = true; + PL_DevMsg("LinPlayerPing found.\n"); + } + else + { + m_bPlayerPingLoaded = false; + PL_DevMsg("LinPlayerPing not found.\n"); } - m_bIsFristStart = false; } } -// sv_maxplayers 发生改变 -FnChangeCallback_t LinGe_VScripts::SvMaxplayersCallback = nullptr; -void LinGe_VScripts::OnSvMaxplayersChanged(IConVar *var, const char *pOldValue, float flOldValue) +void LinGe_VScripts::ServerActivate(edict_t *pEdictList, int edictCount, int clientMax) {} + +// 监测 vocalize smartlook +PLUGIN_RESULT LinGe_VScripts::ClientCommand(edict_t *pEntity, const CCommand &args) { - SvMaxplayersCallback(var, pOldValue, flOldValue); - if (!SDKAPI::L4D2_RunScript("::LinGe.Base.UpdateMaxplayers()")) - _Warning("L4D2_RunScript ::LinGe.Base.UpdateMaxplayers() Failed\n"); + if ( !pEntity || pEntity->IsFree() ) + return PLUGIN_CONTINUE; + if (m_bPlayerPingLoaded && g_bLookPing) + { + if ( Q_stricmp(args[0], "vocalize") == 0 + && Q_stricmp(args[1], "smartlook") == 0 + && (args.ArgC() == 2 || Q_stricmp(args[2], "auto") != 0)) + // 游戏自动让人物发出该指令时,第三个参数会为 auto + { + IPlayerInfo *player = SDKAPI::iPlayerInfoManager->GetPlayerInfo(pEntity); + if (player->IsPlayer() && player->GetTeamIndex() == 2 + && !player->IsFakeClient() && !player->IsDead()) + { + PL_DevMsg("%s PlayerPing\n", player->GetName()); + SDKAPI::L4D2_RunScript("::LinPlayerPing(%d);", player->GetUserID()); + } + } + } + + return PLUGIN_CONTINUE; } // 插件控制台变量发生改变 +void LinGe_VScripts::OnLookPingChanged(IConVar *var, const char *pOldValue, float flOldValue) +{ + g_bLookPing = cv_lookPing.GetBool(); +} void LinGe_VScripts::OnTimeFormatChanged(IConVar *var, const char *pOldValue, float flOldValue) { strncpy(g_sTimeFormat, cv_format.GetString(), sizeof(g_sTimeFormat) - 1); g_sTimeFormat[sizeof(g_sTimeFormat) - 1] = '\0'; } -PLUGIN_RESULT LinGe_VScripts::ClientCommand(edict_t *pEntity, const CCommand &args) { - return PLUGIN_CONTINUE; -} -void LinGe_VScripts::LevelInit(char const *pMapName) {} -void LinGe_VScripts::LevelShutdown(void) {} void LinGe_VScripts::Pause(void) {} void LinGe_VScripts::UnPause(void) {} void LinGe_VScripts::ClientActive(edict_t *pEntity) {} @@ -129,7 +146,9 @@ void LinGe_VScripts::ClientDisconnect(edict_t *pEntity) {} void LinGe_VScripts::OnQueryCvarValueFinished(QueryCvarCookie_t iCookie, edict_t *pPlayerEntity, EQueryCvarValueStatus eStatus, const char *pCvarName, const char *pCvarValue) {} void LinGe_VScripts::ClientPutInServer(edict_t *pEntity, const char *playername) {} -void LinGe_VScripts::SetCommandClient(int index) {} +void LinGe_VScripts::SetCommandClient(int index) { + m_iClientCommandIndex = index; +} void LinGe_VScripts::ClientSettingsChanged(edict_t *pEdict) {} PLUGIN_RESULT LinGe_VScripts::ClientConnect(bool *bAllowConnect, edict_t *pEntity, const char *pszName, const char *pszAddress, char *reject, int maxrejectlen) { return PLUGIN_CONTINUE; @@ -138,4 +157,4 @@ PLUGIN_RESULT LinGe_VScripts::NetworkIDValidated(const char *pszUserName, const return PLUGIN_CONTINUE; } void LinGe_VScripts::OnEdictAllocated(edict_t *edict) {} -void LinGe_VScripts::OnEdictFreed(const edict_t *edict) {} +void LinGe_VScripts::OnEdictFreed(const edict_t *edict) {} \ No newline at end of file diff --git a/plugin/LinGe_VScripts.h b/plugin/LinGe_VScripts.h index 755b638..561ed48 100644 --- a/plugin/LinGe_VScripts.h +++ b/plugin/LinGe_VScripts.h @@ -14,27 +14,20 @@ * * You should have received a copy of the GNU General Public License along with * this program. If not, see . - * - * As a special exception, AlliedModders LLC gives you permission to link the - * code of this program (as well as its derivative works) to "Half-Life 2," the - * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software - * by the Valve Corporation. You must obey the GNU General Public License in - * all respects for all other code used. Additionally, AlliedModders LLC grants - * this exception to all derivative works. AlliedModders LLC defines further - * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), - * or . */ #pragma once #include +#include #include #define PLNAME "LinGe_VScripts" -#define PLVER "v2.6" +#define PLVER "v2.7.0" -#define _Msg(format, ...) Msg(PLNAME ": " format, ## __VA_ARGS__) -#define _Warning(format, ...) Warning(PLNAME ": " format, ## __VA_ARGS__) -#define _Error(format, ...) Error(PLNAME ": " format, ## __VA_ARGS__) +#define PL_Msg(format, ...) Msg(PLNAME ": " format, ## __VA_ARGS__) +#define PL_Warning(format, ...) Warning(PLNAME ": " format, ## __VA_ARGS__) +#define PL_Error(format, ...) Error(PLNAME ": " format, ## __VA_ARGS__) +#define PL_DevMsg(format, ...) DevMsg(PLNAME ": " format, ## __VA_ARGS__) -class LinGe_VScripts : public IServerPluginCallbacks +class LinGe_VScripts : public IServerPluginCallbacks, public IGameEventListener2 { public: LinGe_VScripts(); @@ -45,7 +38,7 @@ class LinGe_VScripts : public IServerPluginCallbacks virtual void Unload(void); virtual void Pause(void); virtual void UnPause(void); - virtual const char *GetPluginDescription(void); + virtual const char * GetPluginDescription(void); virtual void LevelInit(char const *pMapName); virtual void ServerActivate(edict_t *pEdictList, int edictCount, int clientMax); virtual void GameFrame(bool simulating); @@ -64,19 +57,16 @@ class LinGe_VScripts : public IServerPluginCallbacks virtual void OnEdictAllocated(edict_t *edict); virtual void OnEdictFreed(const edict_t *edict); -public: - static void OnSvMaxplayersChanged(IConVar *var, const char *pOldValue, float flOldValue); - static void OnTimeFormatChanged(IConVar *var, const char *pOldValue, float flOldValue); - -public: - static FnChangeCallback_t SvMaxplayersCallback; - - int m_iMaxClients; + // IGameEventListener Interface + virtual void FireGameEvent( IGameEvent * event ); + virtual int GetEventDebugID() { return EVENT_DEBUG_ID_INIT; } + virtual int GetCommandIndex() { return m_iClientCommandIndex; } -protected: - bool m_bIsFristStart; -}; +private: + int m_iClientCommandIndex; + bool m_bPlayerPingLoaded; -extern ConVar *cv_pSvMaxplayers; -extern ConVar cv_time; -extern ConVar cv_format; +public: + static void OnLookPingChanged(IConVar *var, const char *pOldValue, float flOldValue); + static void OnTimeFormatChanged(IConVar *var, const char *pOldValue, float flOldValue); +}; \ No newline at end of file diff --git a/plugin/sdkapi/sdkapi.cpp b/plugin/sdkapi/sdkapi.cpp index adaed28..3157e50 100644 --- a/plugin/sdkapi/sdkapi.cpp +++ b/plugin/sdkapi/sdkapi.cpp @@ -14,15 +14,6 @@ * * You should have received a copy of the GNU General Public License along with * this program. If not, see . - * - * As a special exception, AlliedModders LLC gives you permission to link the - * code of this program (as well as its derivative works) to "Half-Life 2," the - * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software - * by the Valve Corporation. You must obey the GNU General Public License in - * all respects for all other code used. Additionally, AlliedModders LLC grants - * this exception to all derivative works. AlliedModders LLC defines further - * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), - * or . */ #include "sdkapi.h" #include "signature.h" @@ -39,11 +30,22 @@ namespace SDKAPI { IServerGameDLL *iServerGameDLL = nullptr; IServerPluginHelpers *iServerPluginHelpers = nullptr; IGameEventManager2 *iGameEventManager = nullptr; + CGlobalVars *pGlobals = nullptr; + FCGlobalEntityList *gEntList = nullptr; - MemoryUtils *mu_engine = nullptr; - MemoryUtils *mu_server = nullptr; + static MemoryUtils *mu_engine = nullptr; + static MemoryUtils *mu_server = nullptr; - FCGlobalEntityList *gEntList = nullptr; + namespace ServerSigFunc { + FINDENTITYBYCLASSNAME CBaseEntity_FindEntityByClassname = nullptr; + + void Initialize() + { + CBaseEntity_FindEntityByClassname = mu_server->FindSignature(Sig_FindEntityByClassname); + if (!CBaseEntity_FindEntityByClassname) + SDKAPI_Warning("FindEntityByClassname signature not found!\n"); + } + } void Initialize(CreateInterfaceFn interfaceFactory, CreateInterfaceFn gameServerFactory) { @@ -92,17 +94,17 @@ namespace SDKAPI { else ServerSigFunc::Initialize(); - // 初始化 pEntList 获取方法参考 sourcemod/core/HalfLife2.cpp + // 初始化 pEntList 方法参考 sourcemod/core/HalfLife2.cpp // Win32下是通过LevelShutdown函数地址再加上偏移量获得pEntityList(指向gEntList的指针)的地址 // Linux下是直接通过符号查找获得gEntList的地址 if (mu_server->IsAvailable()) { void *ptr = mu_server->FindSignature(Sig_gEntList); if (!ptr) - SDKAPI_Warning("gEntList signature not found!"); -#ifdef WIN32 + SDKAPI_Warning("gEntList signature not found!\n"); + #ifdef WIN32 ptr = *(reinterpret_cast((char *)ptr + Offset_gEntList_windows)); -#endif + #endif gEntList = reinterpret_cast(ptr); } } @@ -117,11 +119,17 @@ namespace SDKAPI { // 通过向实体 logic_script 发送实体输入执行 vscripts 脚本代码 // 代码参考 Silver https://forums.alliedmods.net/showthread.php?p=2657025 - // 控制台指令script具有相同的功能 不过script是cheats指令 - // 并且据Silvers所说script指令似乎存在内存泄漏 所以通过实体来执行代码更优一点 - bool L4D2_RunScript(const char *sCode) + bool L4D2_RunScript(const char *_Format, ...) { + // 处理 _Format + static char buffer[8192]; + va_list arg_list; + va_start(arg_list, _Format); + vsnprintf(buffer, sizeof(buffer), _Format, arg_list); + va_end(arg_list); + static variant_t var; + // pScriptLogic 必须每次执行时查找 因为每局游戏它应该都会变 CBaseEntity *pScriptLogic = gEntList->FindEntityByClassname(nullptr, "logic_script"); edict_t *edict = iServerGameEnts->BaseEntityToEdict(pScriptLogic); if (!edict || edict->IsFree()) @@ -131,19 +139,8 @@ namespace SDKAPI { return false; iServerTools->DispatchSpawn(pScriptLogic); } - castable_string_t str(sCode); + castable_string_t str(buffer); var.SetString(str); return reinterpret_cast(pScriptLogic)->AcceptInput("RunScriptCode", nullptr, nullptr, var, 0); } - - namespace ServerSigFunc { - FINDENTITYBYCLASSNAME CBaseEntity_FindEntityByClassname = nullptr; - - void Initialize() - { - CBaseEntity_FindEntityByClassname = mu_server->FindSignature(Sig_FindEntityByClassname); - if (!CBaseEntity_FindEntityByClassname) - SDKAPI_Warning("FindEntityByClassname signature not found!"); - } - } -} +} // namespace SDKAPI \ No newline at end of file diff --git a/plugin/sdkapi/sdkapi.h b/plugin/sdkapi/sdkapi.h index ea04b59..9c792d0 100644 --- a/plugin/sdkapi/sdkapi.h +++ b/plugin/sdkapi/sdkapi.h @@ -14,15 +14,6 @@ * * You should have received a copy of the GNU General Public License along with * this program. If not, see . - * - * As a special exception, AlliedModders LLC gives you permission to link the - * code of this program (as well as its derivative works) to "Half-Life 2," the - * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software - * by the Valve Corporation. You must obey the GNU General Public License in - * all respects for all other code used. Additionally, AlliedModders LLC grants - * this exception to all derivative works. AlliedModders LLC defines further - * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), - * or . */ #pragma once #include @@ -43,9 +34,11 @@ #define SDKAPI_Error(format, ...) Error("SDKAPI: " format, ## __VA_ARGS__) namespace SDKAPI { + // 伪 SDK 类前置声明 class FCGlobalEntityList; + class FCBaseEntity; - // Interface + // SDK API 接口 extern ICvar *iCvar; extern IPlayerInfoManager *iPlayerInfoManager; extern IServerGameEnts *iServerGameEnts; @@ -54,24 +47,44 @@ namespace SDKAPI { extern IServerGameDLL *iServerGameDLL; extern IServerPluginHelpers *iServerPluginHelpers; extern IGameEventManager2 *iGameEventManager; - - extern MemoryUtils *mu_engine; - extern MemoryUtils *mu_server; - + extern CGlobalVars *pGlobals; extern FCGlobalEntityList *gEntList; + namespace ServerSigFunc { + typedef CBaseEntity *(__thiscall *FINDENTITYBYCLASSNAME)(void *, CBaseEntity *, const char *); + extern FINDENTITYBYCLASSNAME CBaseEntity_FindEntityByClassname; + } + + // 全局函数 void Initialize(CreateInterfaceFn interfaceFactory, CreateInterfaceFn gameServerFactory); void UnInitialize(); // 通过向实体 logic_script 发送实体输入执行 vscripts 脚本代码 - bool L4D2_RunScript(const char *sCode); + bool L4D2_RunScript(const char *_Format, ...); - // 签名函数 - namespace ServerSigFunc { - typedef CBaseEntity *(__thiscall *FINDENTITYBYCLASSNAME)(void *, CBaseEntity *, const char *); - extern FINDENTITYBYCLASSNAME CBaseEntity_FindEntityByClassname; + inline int IndexOfEdict(const edict_t *pEdict) + { + return (int)(pEdict - pGlobals->pEdicts); + } + inline edict_t *PEntityOfEntIndex(int iEntIndex) + { + if (iEntIndex >= 0 && iEntIndex < pGlobals->maxEntities) + { + return (edict_t *)(pGlobals->pEdicts + iEntIndex); + } + return nullptr; + } - void Initialize(); + // 通过UserID获取到实体 + inline edict_t *GetEntityFromUserID(int userid) + { + for (int i=0; imaxEntities; i++) + { + edict_t *pEntity = PEntityOfEntIndex(i); + if (iVEngineServer->GetPlayerUserId(pEntity) == userid) + return pEntity; + } + return nullptr; } // 伪SDK类,用于方便调用一些函数 @@ -83,7 +96,10 @@ namespace SDKAPI { if (ServerSigFunc::CBaseEntity_FindEntityByClassname) return ServerSigFunc::CBaseEntity_FindEntityByClassname(this, pStartEntity, szName); else - throw "FindEntityByClassname function pointer is nullptr!"; + { + SDKAPI_Error("FindEntityByClassname function pointer is nullptr!"); + return nullptr; + } } }; @@ -93,7 +109,7 @@ namespace SDKAPI { typedef bool(__thiscall *ACCEPTINPUT)(void *, const char *, CBaseEntity *, CBaseEntity *, variant_t, int); public: - inline bool AcceptInput(const char *szInputName, CBaseEntity *pActivator, CBaseEntity *pCaller, variant_t Value, int outputID) + inline bool AcceptInput(const char *szInputName, CBaseEntity *pActivator, CBaseEntity *pCaller, variant_t Value, int outputID=0) { return GetVirtualFunction(this, VTI_AcceptInput) (this, szInputName, pActivator, pCaller, Value, outputID); diff --git a/plugin/sdkapi/signature.cpp b/plugin/sdkapi/signature.cpp index 6b92b01..a1d3025 100644 --- a/plugin/sdkapi/signature.cpp +++ b/plugin/sdkapi/signature.cpp @@ -14,15 +14,6 @@ * * You should have received a copy of the GNU General Public License along with * this program. If not, see . - * - * As a special exception, AlliedModders LLC gives you permission to link the - * code of this program (as well as its derivative works) to "Half-Life 2," the - * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software - * by the Valve Corporation. You must obey the GNU General Public License in - * all respects for all other code used. Additionally, AlliedModders LLC grants - * this exception to all derivative works. AlliedModders LLC defines further - * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), - * or . */ // ļкǩƫƵַ sourcemod #include "signature.h" diff --git a/plugin/sdkapi/signature.h b/plugin/sdkapi/signature.h index 8142fb0..2c3e80c 100644 --- a/plugin/sdkapi/signature.h +++ b/plugin/sdkapi/signature.h @@ -14,15 +14,6 @@ * * You should have received a copy of the GNU General Public License along with * this program. If not, see . - * - * As a special exception, AlliedModders LLC gives you permission to link the - * code of this program (as well as its derivative works) to "Half-Life 2," the - * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software - * by the Valve Corporation. You must obey the GNU General Public License in - * all respects for all other code used. Additionally, AlliedModders LLC grants - * this exception to all derivative works. AlliedModders LLC defines further - * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), - * or . */ #pragma once #include "MemoryUtils/MemoryUtils.h" diff --git a/release/addoninfo.txt b/release/Base-addoninfo.txt similarity index 52% rename from release/addoninfo.txt rename to release/Base-addoninfo.txt index 31b43d8..8fb15f8 100644 --- a/release/addoninfo.txt +++ b/release/Base-addoninfo.txt @@ -1,10 +1,9 @@ "AddonInfo" { addonSteamAppID 550 - addontitle "LinGe VScripts" - addonDescription "击杀统计、时间显示、多特、友伤提示等等" + addontitle "LinGe VScripts 基础库" + addonDescription "LinGe VScripts 脚本基础库" addonauthor "LinGe" - addonversion "2.0" addonURL0 "https://github.com/Lin515/L4D2_LinGe_VScripts" addonContent_Script 1 diff --git a/release/HUD-addoninfo.txt b/release/HUD-addoninfo.txt new file mode 100644 index 0000000..6a1332c --- /dev/null +++ b/release/HUD-addoninfo.txt @@ -0,0 +1,10 @@ +"AddonInfo" +{ + addonSteamAppID 550 + addontitle "击杀与伤害统计、友伤提示、时间显示" + addonDescription "必须安装 LinGe VScripts Base 基础库" + addonauthor "LinGe" + addonURL0 "https://github.com/Lin515/L4D2_LinGe_VScripts" + + addonContent_Script 1 +} \ No newline at end of file diff --git a/release/Hint-addoninfo.txt b/release/Hint-addoninfo.txt new file mode 100644 index 0000000..088b251 --- /dev/null +++ b/release/Hint-addoninfo.txt @@ -0,0 +1,10 @@ +"AddonInfo" +{ + addonSteamAppID 550 + addontitle "玩家状态提示与物品标记提示" + addonDescription "必须安装 LinGe VScripts Base 基础库。使用 bind t +alt1 绑定快捷键可以标记物品" + addonauthor "LinGe" + addonURL0 "https://github.com/Lin515/L4D2_LinGe_VScripts" + + addonContent_Script 1 +} \ No newline at end of file diff --git a/release/LinGe_VScripts-addoninfo.txt b/release/LinGe_VScripts-addoninfo.txt new file mode 100644 index 0000000..e6c3b76 --- /dev/null +++ b/release/LinGe_VScripts-addoninfo.txt @@ -0,0 +1,10 @@ +"AddonInfo" +{ + addonSteamAppID 550 + addontitle "LinGe VScripts 全功能" + addonDescription "击杀统计、多特、玩家状态提示等等" + addonauthor "LinGe" + addonURL0 "https://github.com/Lin515/L4D2_LinGe_VScripts" + + addonContent_Script 1 +} \ No newline at end of file diff --git a/release/MoreSI-addoninfo.txt b/release/MoreSI-addoninfo.txt new file mode 100644 index 0000000..86a4d3d --- /dev/null +++ b/release/MoreSI-addoninfo.txt @@ -0,0 +1,10 @@ +"AddonInfo" +{ + addonSteamAppID 550 + addontitle "多特控制:修改特感生成数量、种类与刷新时间" + addonDescription "必须安装 LinGe VScripts Base 基础库" + addonauthor "LinGe" + addonURL0 "https://github.com/Lin515/L4D2_LinGe_VScripts" + + addonContent_Script 1 +} \ No newline at end of file diff --git a/release/vpk.bat b/release/vpk.bat index 6e8d2a5..555a7b7 100644 --- a/release/vpk.bat +++ b/release/vpk.bat @@ -1,24 +1,63 @@ @echo off set main=..\vscripts -set nutdir=nut_vpk +set all=LinGe_VScripts +set Base=Base +set HUD=HUD +set MoreSI=MoreSI +set Hint=Hint +set zs=zs set vpk="D:\Program Files (x86)\Steam\steamapps\common\Left 4 Dead 2\bin\vpk.exe" -mkdir %nutdir%\scripts\vscripts\LinGe -mkdir %nutdir%\scripts\vscripts\VSLib +:: 全套 +rd /s /q %all% +mkdir %all%\scripts\vscripts\LinGe +mkdir %all%\scripts\vscripts\VSLib +copy %all%-addoninfo.txt %all%\addoninfo.txt +copy %main%\LinGe %all%\scripts\vscripts\LinGe +copy %main%\VSLib %all%\scripts\vscripts\VSLib +copy %main%\VSLib.nut %all%\scripts\vscripts\VSLib.nut +copy %main%\director_base_addon.nut %all%\scripts\vscripts\director_base_addon.nut +copy %main%\scriptedmode_addon.nut %all%\scripts\vscripts\scriptedmode_addon.nut +%vpk% %all% -:: 复制 addoninfo.txt -copy addoninfo.txt %nutdir%\addoninfo.txt +::Base +rd /s /q %Base% +mkdir %Base%\scripts\vscripts\LinGe +mkdir %Base%\scripts\vscripts\VSLib +copy %Base%-addoninfo.txt %Base%\addoninfo.txt +copy %main%\LinGe\Base.nut %Base%\scripts\vscripts\LinGe\Base.nut +copy %main%\VSLib %Base%\scripts\vscripts\VSLib +copy %main%\VSLib.nut %Base%\scripts\vscripts\VSLib.nut +copy %main%\director_base_addon.nut %Base%\scripts\vscripts\director_base_addon.nut +copy %main%\scriptedmode_addon.nut %Base%\scripts\vscripts\scriptedmode_addon.nut +%vpk% %Base% -:: 复制 nut 文件 -copy %main%\LinGe %nutdir%\scripts\vscripts\LinGe -copy %main%\VSLib %nutdir%\scripts\vscripts\VSLib -copy %main%\VSLib.nut %nutdir%\scripts\vscripts\VSLib.nut -copy %main%\director_base_addon.nut %nutdir%\scripts\vscripts\director_base_addon.nut -copy %main%\scriptedmode_addon.nut %nutdir%\scripts\vscripts\scriptedmode_addon.nut +::HUD +rd /s /q %HUD% +mkdir %HUD%\scripts\vscripts\LinGe +copy %HUD%-addoninfo.txt %HUD%\addoninfo.txt +copy %main%\LinGe\HUD.nut %HUD%\scripts\vscripts\LinGe\HUD.nut +%vpk% %HUD% -:: 打包vpk -%vpk% %nutdir% -del LinGe_VScripts.vpk -rename %nutdir%.vpk LinGe_VScripts.vpk +::MoreSI +rd /s /q %MoreSI% +mkdir %MoreSI%\scripts\vscripts\LinGe +copy %MoreSI%-addoninfo.txt %MoreSI%\addoninfo.txt +copy %main%\LinGe\MoreSI.nut %MoreSI%\scripts\vscripts\LinGe\MoreSI.nut +%vpk% %MoreSI% + +::Hint +rd /s /q %Hint% +mkdir %Hint%\scripts\vscripts\LinGe +copy %Hint%-addoninfo.txt %Hint%\addoninfo.txt +copy %main%\LinGe\Hint.nut %Hint%\scripts\vscripts\LinGe\Hint.nut +%vpk% %Hint% + +::zs +rd /s /q %zs% +mkdir %zs%\scripts\vscripts\LinGe +copy %zs%-addoninfo.txt %zs%\addoninfo.txt +copy %main%\LinGe\zs.nut %zs%\scripts\vscripts\LinGe\zs.nut +%vpk% %zs% pause \ No newline at end of file diff --git a/release/zs-addoninfo.txt b/release/zs-addoninfo.txt new file mode 100644 index 0000000..ee3d574 --- /dev/null +++ b/release/zs-addoninfo.txt @@ -0,0 +1,10 @@ +"AddonInfo" +{ + addonSteamAppID 550 + addontitle "自杀指令 !zs" + addonDescription "必须安装 LinGe VScripts Base 基础库。仅战役模式下可用" + addonauthor "LinGe" + addonURL0 "https://github.com/Lin515/L4D2_LinGe_VScripts" + + addonContent_Script 1 +} \ No newline at end of file diff --git a/vscripts/LinGe/Base.nut b/vscripts/LinGe/Base.nut index 10bab48..d96e1b2 100644 --- a/vscripts/LinGe/Base.nut +++ b/vscripts/LinGe/Base.nut @@ -4,8 +4,6 @@ // L4D2 EMS/Appendix:HUD:https://developer.valvesoftware.com/wiki/L4D2_EMS/Appendix:_HUD // L4D2 Events:https://wiki.alliedmods.net/Left_4_Dead_2_Events // 以及VSLib与admin_system的脚本源码 -printl("[LinGe] 脚本功能集正在载入"); - printl("[LinGe] Base 正在载入"); ::LinGe <- {}; ::LinGe.Debug <- false; @@ -278,7 +276,7 @@ printl("[LinGe] 当前服务器端口 " + ::LinGe.hostport); } // 如果source中某个key在dest中也存在,则将其赋值给dest中的key -::LinGe.Merge <- function (dest, source) +::LinGe.Merge <- function (dest, source, typeMatch=true) { if ("table" == typeof dest && "table" == typeof source) { @@ -288,8 +286,15 @@ printl("[LinGe] 当前服务器端口 " + ::LinGe.hostport); { // 如果指定key也是table,则进行递归 if ("table" == typeof dest[key] - && "table" == typeof val ) + && "table" == typeof val) ::LinGe.Merge(dest[key], val); + else if (typeof dest[key] != typeof val) + { + if (!typeMatch) + dest[key] = val; + else if (typeof dest[key] == "bool" && typeof val == "integer") // 争对某些情况下 bool 被转换成了 integer + dest[key] = (val!=0); + } else dest[key] = val; } @@ -468,10 +473,9 @@ class ::LinGe.ConfigManager if (!table.rawin(tableName)) throw "未找到表"; local fromFile = ::VSLib.FileIO.LoadTable(filePath); - if (null != fromFile) + if (null != fromFile && fromFile.rawin(tableName)) { - if (fromFile.rawin(tableName)) - ::LinGe.Merge(table[tableName], fromFile[tableName]); + ::LinGe.Merge(table[tableName], fromFile[tableName]); } Save(tableName); // 保持文件配置和已载入配置的一致性 } @@ -501,7 +505,10 @@ class ::LinGe.ConfigManager // 保存所有表 function SaveAll() { - ::VSLib.FileIO.SaveTable(filePath, table); + local fromFile = ::VSLib.FileIO.LoadTable(filePath); + foreach (k, v in table) + fromFile.rawset(k, v); + ::VSLib.FileIO.SaveTable(filePath, fromFile); } }; @@ -842,12 +849,29 @@ if (null == ::LinGe.Admin.adminslist) ::LinGe.Admin.Cmd_saveconfig <- function (player, args) { - ::LinGe.Config.SaveAll(); - ClientPrint(player, 3, "\x04已保存当前功能设定为默认设定\n"); - ClientPrint(player, 3, "\x04配置文件: \x05 left4dead2/ems/" + FILE_CONFIG + ".tbl\n"); + if (args.len() == 1) + { + ::LinGe.Config.SaveAll(); + ClientPrint(player, 3, "\x04已保存当前功能设定为默认设定\n"); + ClientPrint(player, 3, "\x04配置文件: \x05 left4dead2/ems/" + FILE_CONFIG + ".tbl"); + } + else if (args.len() == 2) + { + foreach (name, tbl in ::LinGe.Config.table) + { + if (name.tolower() == args[1]) + { + ::LinGe.Config.Save(name); + ClientPrint(player, 3, "\x04已保存当前功能设定为默认设定: \x05" + name); + ClientPrint(player, 3, "\x04配置文件: \x05 left4dead2/ems/" + FILE_CONFIG + ".tbl"); + return; + } + } + ClientPrint(player, 3, "\x04未找到 \x05" + args[1]); + } } ::LinCmdAdd("saveconfig", ::LinGe.Admin.Cmd_saveconfig, ::LinGe.Admin); -::LinCmdAdd("save", ::LinGe.Admin.Cmd_saveconfig, ::LinGe.Admin, "保存所有配置到配置文件"); +::LinCmdAdd("save", ::LinGe.Admin.Cmd_saveconfig, ::LinGe.Admin, "保存配置到配置文件"); ::LinGe.Admin.Cmd_lshelp <- function (player, args) { diff --git a/vscripts/LinGe/HUD.nut b/vscripts/LinGe/HUD.nut index b466151..6abe18f 100644 --- a/vscripts/LinGe/HUD.nut +++ b/vscripts/LinGe/HUD.nut @@ -7,10 +7,10 @@ printl("[LinGe] HUD 正在载入"); time = true, players = true, hostname = true, + versusNoHUDRank = true, // 对抗模式是否永远不显示击杀排行 playersStyle = 0, }, hurt = { - versusNoHUDRank = true, // 对抗模式是否不显示HUD击杀排行 HUDRank = 3, // HUD排行榜最多显示多少人,范围0~8 设置为0则关闭排行显示 rankTitle = "特感/丧尸击杀:", rankStyle = "{ksi}/{kci}", @@ -149,7 +149,7 @@ for (local i=1; i<9; i++) else HUD_table.Fields.hostname.flags = HUD_table.Fields.hostname.flags | HUD_FLAG_NOTVISIBLE; - if (::LinGe.isVersus && Config.hurt.versusNoHUDRank) + if (::LinGe.isVersus && Config.HUDShow.versusNoHUDRank) { for (i=0; i<9; i++) HUD_table.Fields["rank"+i].flags = HUD_table.Fields["rank"+i].flags | HUD_FLAG_NOTVISIBLE; @@ -160,10 +160,6 @@ for (local i=1; i<9; i++) Config.hurt.HUDRank = 8; else if (Config.hurt.HUDRank <= 0) Config.hurt.HUDRank = -1; - if (Config.hurt.HUDRank > 0) - ::VSLib.Timers.AddTimerByName("UpdateRankHUD", 1.0, true, ::LinGe.HUD.UpdateRankHUD); - else - ::VSLib.Timers.RemoveTimerByName("UpdateRankHUD"); for (i=0; i<=Config.hurt.HUDRank; i++) // 去掉所有排行榜数据HUD的隐藏属性 HUD_table.Fields["rank"+i].flags = HUD_table.Fields["rank"+i].flags & (~HUD_FLAG_NOTVISIBLE); @@ -179,9 +175,14 @@ for (local i=1; i<9; i++) UpdateRankHUD(); } -::LinGe.HUD.Timer_UpdateTime <- function (params) +local isExistTime = false; +::LinGe.HUD.Timer_HUD <- function (params) { - HUD_table.Fields.time.dataval = Convars.GetStr("linge_time"); + if (isExistTime) + HUD_table.Fields.time.dataval = Convars.GetStr("linge_time"); + if (Config.hurt.HUDRank > 0) + UpdateRankHUD(); + ::LinGe.Base.UpdateMaxplayers(); // 如果存在更新则会触发 maxplayers_changed }.bindenv(::LinGe.HUD); // 事件:回合开始 @@ -199,18 +200,18 @@ for (local i=1; i<9; i++) // 如果linge_time变量不存在则显示回合时间 if (null == Convars.GetStr("linge_time")) { -// if (!("special" in HUD_table.Fields.time)) - HUD_table.Fields.time.special <- HUD_SPECIAL_ROUNDTIME; + isExistTime = false; + HUD_table.Fields.time.special <- HUD_SPECIAL_ROUNDTIME; } else { -// if (!("dataval" in HUD_table.Fields.time)) - HUD_table.Fields.time.dataval <- ""; - ::VSLib.Timers.AddTimerByName("Timer_UpdateTime", 1.0, true, Timer_UpdateTime); + isExistTime = true; + HUD_table.Fields.time.dataval <- ""; } ApplyAutoHurtPrint(); ApplyConfigHUD(); + ::VSLib.Timers.AddTimerByName("Timer_HUD", 1.0, true, Timer_HUD); } ::LinEventHook("OnGameEvent_round_start", ::LinGe.HUD.OnGameEvent_round_start, ::LinGe.HUD); @@ -248,34 +249,34 @@ for (local i=1; i<9; i++) if (0 == params.type) // 伤害类型为0 return; - local attacker = GetPlayerFromUserID(params.attacker); // 获得攻击者实体 - if (null == attacker) // 攻击者无效 - return; + local attacker = GetPlayerFromUserID(params.attacker); // 获得攻击者实体 + if (null == attacker) // 攻击者无效 + return; if (!attacker.IsSurvivor()) // 攻击者不是生还者 return; // 获取被攻击者实体 - local victim = GetPlayerFromUserID(params.userid); + local victim = GetPlayerFromUserID(params.userid); local vctHp = victim.GetHealth(); local dmg = params.dmg_health; - // 如果被攻击者是生还者则统计友伤数据 + // 如果被攻击者是生还者则统计友伤数据 if (victim.IsSurvivor()) { if (victim.IsDying() || victim.IsDead()) return; - else if (vctHp < 0) // 致死伤害事件发生时,victim.IsDead()还不会为真,但血量会<0 - { + else if (vctHp < 0) // 致死伤害事件发生时,victim.IsDead()还不会为真,但血量会<0 + { // 如果是本次伤害致其死亡,则 生命值 + 伤害值 > 0 if (vctHp + dmg <= 0) return; } - else if (victim.IsIncapacitated()) - { - // 如果是本次伤害致其倒地,则其当前血量+伤害量=300 + else if (victim.IsIncapacitated()) + { + // 如果是本次伤害致其倒地,则其当前血量+伤害量=300 // 如果不是,则说明攻击时已经倒地,则不统计本次友伤 - if (vctHp + dmg != 300) + if (vctHp + dmg != 300) return; - } + } // 若不是对自己造成的伤害,则计入累计统计 if (attacker != victim) @@ -290,7 +291,8 @@ for (local i=1; i<9; i++) local key = params.attacker + "_" + params.userid; if (!tempTeamHurt.rawin(key)) { - tempTeamHurt[key] <- { dmg=0, attacker=attacker, victim=victim, isDead=false, isIncap=false }; + tempTeamHurt[key] <- { dmg=0, attacker=attacker, atkName=attacker.GetPlayerName(), + victim=victim, vctName=victim.GetPlayerName(), isDead=false, isIncap=false }; } tempTeamHurt[key].dmg += dmg; // 友伤发生后,0.5秒内同一人若未再对同一人造成友伤,则输出其造成的伤害 @@ -326,8 +328,8 @@ for (local i=1; i<9; i++) ::LinGe.HUD.Timer_PrintTeamHurt <- function (key) { local info = tempTeamHurt[key]; - local atkName = info.attacker.GetPlayerName(); - local vctName = info.victim.GetPlayerName(); + local atkName = info.atkName; + local vctName = info.vctName; local text = ""; if (Config.hurt.teamHurtInfo == 1) @@ -362,7 +364,8 @@ for (local i=1; i<9; i++) text += ",并且死亡"; else if (info.isIncap) text += ",并且倒地"; - ClientPrint(info.attacker, 3, text); + if (info.attacker.IsValid()) + ClientPrint(info.attacker, 3, text); } else { @@ -372,14 +375,16 @@ for (local i=1; i<9; i++) text += ",并且杀死了他"; else if (info.isIncap) text += ",并且击倒了他"; - ClientPrint(info.attacker, 3, text); + if (info.attacker.IsValid()) + ClientPrint(info.attacker, 3, text); text = "\x03" + atkName + "\x04 对你造成了 \x03" + info.dmg + "\x04 点伤害"; if (info.isDead) text += ",并且杀死了你"; else if (info.isIncap) text += ",并且打倒了你"; - ClientPrint(info.victim, 3, text); + if (info.victim.IsValid()) + ClientPrint(info.victim, 3, text); } } tempTeamHurt.rawdelete(key); @@ -389,18 +394,18 @@ for (local i=1; i<9; i++) // 虽然是player_death 但小丧尸和witch死亡也会触发该事件 ::LinGe.HUD.OnGameEvent_player_death <- function (params) { - local dier = 0; // 死者ID - local dierEntity = null; // 死者实体 + local dier = 0; // 死者ID + local dierEntity = null; // 死者实体 local attacker = 0; // 攻击者ID local attackerEntity = null; // 攻击者实体 - if (params.victimname == "Infected" || params.victimname == "Witch") - { - // witch 和 小丧尸 不属于玩家可控制实体 无userid - dier = params.entityid; - } - else - dier = params.userid; + if (params.victimname == "Infected" || params.victimname == "Witch") + { + // witch 和 小丧尸 不属于玩家可控制实体 无userid + dier = params.entityid; + } + else + dier = params.userid; if (dier == 0) return; @@ -459,12 +464,6 @@ for (local i=1; i<9; i++) } ::LinEventHook("OnGameEvent_player_incapacitated", ::LinGe.HUD.OnGameEvent_player_incapacitated, ::LinGe.HUD); -::LinGe.HUD.maxplayers_changed <- function (params) -{ - UpdatePlayerHUD(); -} -::LinEventHook("maxplayers_changed", ::LinGe.HUD.maxplayers_changed, ::LinGe.HUD); - ::LinGe.HUD.OnGameEvent_hostname_changed <- function (params) { HUD_table.Fields.hostname.dataval = params.hostname; @@ -564,7 +563,7 @@ local reHudCmd = regexp("^(all|time|players|hostname)$"); return; } } - else if (3 == args.len()) + else if (3 == args.len() && args[1] == "rank") { Config.hurt.HUDRank = ::LinGe.TryStringToInt(args[2]); ApplyConfigHUD(); @@ -587,7 +586,7 @@ local reHudCmd = regexp("^(all|time|players|hostname)$"); ::LinCmdAdd("rank", ::LinGe.HUD.Cmd_rank, ::LinGe.HUD); // 更新玩家信息HUD -::LinGe.HUD.UpdatePlayerHUD <- function () +::LinGe.HUD.UpdatePlayerHUD <- function (params=null) { local playerText = ""; local style = Config.HUDShow.playersStyle; @@ -617,12 +616,13 @@ local reHudCmd = regexp("^(all|time|players|hostname)$"); HUD_table.Fields.players.dataval = playerText; } +::LinEventHook("maxplayers_changed", ::LinGe.HUD.UpdatePlayerHUD, ::LinGe.HUD); ::LinGe.HUD.UpdateRankHUD <- function (params=null) { if (Config.hurt.HUDRank < 1) return; - if (::LinGe.isVersus && Config.hurt.versusNoHUDRank) + if (::LinGe.isVersus && Config.HUDShow.versusNoHUDRank) return; // 如果不想改变 ::pyinfo.survivorIdx 的顺序 这里应使用 clone 克隆数组 diff --git a/vscripts/LinGe/Hint.nut b/vscripts/LinGe/Hint.nut index c0dd6a3..358d92a 100644 --- a/vscripts/LinGe/Hint.nut +++ b/vscripts/LinGe/Hint.nut @@ -1,20 +1,23 @@ // https://developer.valvesoftware.com/wiki/Env_instructor_hint:zh-cn - +// 该功能必须开启 游戏菜单-选项-多人联机-游戏提示 // 只在非对抗模式下加载此功能 if (!::LinGe.isVersus) { -printl("[LinGe] Hint 正在载入"); +printl("[LinGe] 标记提示 正在载入"); ::LinGe.Hint <- {}; ::LinGe.Hint.Config <- { limit = 4, // 队友状态与普通标记提示的总数量上限,若设置为<=0则彻底关闭提示系统 offscreenShow = true, // 提示在画面之外是否也要显示 - friend = { // 队友需要帮助时出现提示 包括 倒地、挂边、黑白、被控 - duration = 15, // 提示持续时间,若<=0则彻底关闭所有队友需要帮助的提示 - dominateDelay = 2, // 被控延迟,玩家被控多少秒后才会出现提示,若设置为0则无延迟立即显示 <0则不显示被控 + help = { // 队友需要帮助时出现提示 包括 倒地、挂边、黑白、被控 + duration = 12, // 提示持续时间,若<=0则彻底关闭所有队友需要帮助的提示 + dominateDelay = 0, // 被控延迟,玩家被控多少秒后才会出现提示,若设置为0则无延迟立即显示 + // 若<0,则不会自动提示玩家被控,但是玩家可以用按键自己发出呼救 + noShowSelf = true // 自己的倒地被控等状态不会显示给自己 }, - normal = { - duration = 15, // 提示持续时间,若<=0则彻底关闭所有普通类型的提示 + ping = { + duration = 8, // 玩家用按键发出信号的持续时间,若<=0则禁止玩家发出信号 + emptySpace = true, // 可以标记到什么都没有的位置 }, deadHint = true, // 死亡时不会出现标记提示,不过会在聊天窗输出提示 若设置为 false 则关闭该提示 // 该提示对BOT不生效 @@ -23,18 +26,34 @@ printl("[LinGe] Hint 正在载入"); ::LinGe.Config.Add("Hint", ::LinGe.Hint.Config); // ::LinGe.Cache.Hint_Config <- ::LinGe.Hint.Config; -// 生还者玩家死亡时输出提示 +// 特感或玩家死亡后消除其身上的提示 ::LinGe.Hint.OnGameEvent_player_death <- function (params) { - if (!params.rawin("userid")) + local dier = 0; // 死者ID + local dierEntity = null; + if (params.victimname == "Witch") + { + dier = params.entityid; + dierEntity = Ent(dier); + } + else if (params.victimname == "Infected") + return; + else + { + dier = params.userid; + dierEntity = GetPlayerFromUserID(dier); + } + if (dier == 0) return; - local dier = params.userid; // 死者ID - local dierEntity = GetPlayerFromUserID(dier); - if (dierEntity && dierEntity.IsSurvivor()) + if (dierEntity) { if (this.rawin("EndHint")) EndHint(dierEntity); + + // 生还者玩家死亡时输出提示 + if (::LinGe.GetPlayerTeam(dierEntity) != 2) + return; // 自杀时伤害类型为0 if (params.type == 0) return; @@ -53,13 +72,12 @@ printl("[LinGe] Hint 正在载入"); if (::LinGe.Hint.Config.limit > 0) { // 当前提示列表 包含三个键 -// key=level 信息提示等级,等级越高越重要 +// key=level 信息提示等级,等级越高越重要 若为-1级,则该提示不占用CurrentHint // key=ent value为env_instructor_hint实体 // key=targetname` value为目标实体名 local CurrentHint = []; getconsttable()["HELP_ICON"] <- "icon_shield"; - -if (::LinGe.Hint.Config.friend.duration > 0) { +getconsttable()["NONE_ICON"] <- "__NONE__"; // 事件:玩家倒地 ::LinGe.Hint.OnGameEvent_player_incapacitated <- function (params) @@ -70,22 +88,35 @@ if (::LinGe.Hint.Config.friend.duration > 0) { local player = GetPlayerFromUserID(params.userid); if (player.IsSurvivor()) { - EndHint(player); + if (Config.help.dominateDelay >= 0 && null != player.GetSpecialInfectedDominatingMe()) // 如果处于被控状态则先不提示倒地 + return; + // 摔死时会有短暂的倒地 所以延迟0.1s再判断是否处于倒地 VSLib.Timers.AddTimerByName(params.userid, 0.1, false, ShowPlayerIncap, player); } } -::LinEventHook("OnGameEvent_player_incapacitated", ::LinGe.Hint.OnGameEvent_player_incapacitated, ::LinGe.Hint); +if (::LinGe.Hint.Config.help.duration > 0) + ::LinEventHook("OnGameEvent_player_incapacitated", ::LinGe.Hint.OnGameEvent_player_incapacitated, ::LinGe.Hint); ::LinGe.Hint.ShowPlayerIncap <- function (player) { - if (player.IsIncapacitated()) + if (Config.help.duration <= 0) + return; + if (!player.IsValid() || !player.IsIncapacitated()) + return; + // 倒地状态提示 + local idx = ::pyinfo.survivorIdx.find(player.GetEntityIndex()); + if (null == idx) + return; + if (Config.help.noShowSelf) { - // 倒地状态提示 local showTo = clone ::pyinfo.survivorIdx; - if (::LinGe.RemoveInArray(player.GetEntityIndex(), showTo) != null) - { - ShowHint(player.GetPlayerName() + "倒地了", 2, player, - showTo, Config.friend.duration, HELP_ICON); - } + showTo.remove(idx); + ShowHint(player.GetPlayerName() + "倒地了", 1, player, + showTo, Config.help.duration, "icon_reviving"); + } + else + { + ShowHint(player.GetPlayerName() + "倒地了", 1, player, + null, Config.help.duration, "icon_reviving"); } }.bindenv(::LinGe.Hint); @@ -97,18 +128,31 @@ if (::LinGe.Hint.Config.friend.duration > 0) { local player = GetPlayerFromUserID(params.userid); ShowPlayerLedge(player); } -::LinEventHook("OnGameEvent_player_ledge_grab", ::LinGe.Hint.OnGameEvent_player_ledge_grab, ::LinGe.Hint); +if (::LinGe.Hint.Config.help.duration > 0) + ::LinEventHook("OnGameEvent_player_ledge_grab", ::LinGe.Hint.OnGameEvent_player_ledge_grab, ::LinGe.Hint); ::LinGe.Hint.ShowPlayerLedge <- function (player) { - local showTo = clone ::pyinfo.survivorIdx; - if (::LinGe.RemoveInArray(player.GetEntityIndex(), showTo) != null) + if (Config.help.duration <= 0) + return; + // 挂边提示 + local idx = ::pyinfo.survivorIdx.find(player.GetEntityIndex()); + if (null == idx) + return; + if (Config.help.noShowSelf) + { + local showTo = clone ::pyinfo.survivorIdx; + showTo.remove(idx); + ShowHint("帮助" + player.GetPlayerName(), 2, player, + showTo, Config.help.duration, "icon_reviving"); + } + else { ShowHint("帮助" + player.GetPlayerName(), 2, player, - showTo, Config.friend.duration, HELP_ICON); + null, Config.help.duration, "icon_reviving"); } } -// 成功救助队友 (倒地拉起、挂边拉起、治疗都会触发该事件) +// 成功救助队友 (倒地拉起、挂边拉起都会触发该事件) ::LinGe.Hint.OnGameEvent_revive_success <- function (params) { if (!params.rawin("subject")) @@ -121,73 +165,106 @@ if (::LinGe.Hint.Config.friend.duration > 0) { if (::LinGe.GetReviveCount(player) >= 2) ShowPlayerDying(player); } -::LinEventHook("OnGameEvent_revive_success", ::LinGe.Hint.OnGameEvent_revive_success, ::LinGe.Hint); +if (::LinGe.Hint.Config.help.duration > 0) + ::LinEventHook("OnGameEvent_revive_success", ::LinGe.Hint.OnGameEvent_revive_success, ::LinGe.Hint); + +// 成功完成治疗 +::LinGe.Hint.OnGameEvent_heal_success <- function (params) +{ + if (!params.rawin("subject")) + return; + local player = GetPlayerFromUserID(params.subject); + if (!player.IsSurvivor()) + return; + EndHint(player); +} +if (::LinGe.Hint.Config.help.duration > 0) + ::LinEventHook("OnGameEvent_heal_success", ::LinGe.Hint.OnGameEvent_heal_success, ::LinGe.Hint); + ::LinGe.Hint.ShowPlayerDying <- function (player) { + if (Config.help.duration <= 0) + return; // 黑白状态提示 - local showTo = ::pyinfo.survivorIdx; - if (::LinGe.RemoveInArray(player.GetEntityIndex(), showTo) != null) + local idx = ::pyinfo.survivorIdx.find(player.GetEntityIndex()); + if (null == idx) + return; + if (Config.help.noShowSelf) + { + local showTo = clone ::pyinfo.survivorIdx; + showTo.remove(idx); + ShowHint(player.GetPlayerName() + "濒死", 1, player, + showTo, Config.help.duration, "icon_medkit"); + } + else { ShowHint(player.GetPlayerName() + "濒死", 1, player, - showTo, Config.friend.duration, HELP_ICON); + null, Config.help.duration, "icon_medkit"); } } // 玩家被控 -if (::LinGe.Hint.Config.friend.dominateDelay >= 0) { ::LinGe.Hint.PlayerBeDominating <- function (params) { if (!params.rawin("victim")) return; local player = GetPlayerFromUserID(params.victim); - if (Config.friend.dominateDelay > 0) + if (Config.help.dominateDelay > 0) ::VSLib.Timers.AddTimerByName(::LinGe.GetEntityTargetname(player), - Config.friend.dominateDelay, false, ShowPlayerBeDominating, player); + Config.help.dominateDelay, false, ShowPlayerBeDominating, player); else ShowPlayerBeDominating(player); } -::LinEventHook("OnGameEvent_lunge_pounce", ::LinGe.Hint.PlayerBeDominating, ::LinGe.Hint); // Hunter -::LinEventHook("OnGameEvent_tongue_grab", ::LinGe.Hint.PlayerBeDominating, ::LinGe.Hint); // Smoker -::LinEventHook("OnGameEvent_charger_pummel_start", ::LinGe.Hint.PlayerBeDominating, ::LinGe.Hint); // Charger -::LinEventHook("OnGameEvent_jockey_ride", ::LinGe.Hint.PlayerBeDominating, ::LinGe.Hint); // Jockey - -// 被控解除 -::LinGe.Hint.PlayerDominateEnd <- function (params) +if (::LinGe.Hint.Config.help.duration > 0 && ::LinGe.Hint.Config.help.dominateDelay >= 0) { - if (!params.rawin("victim")) - return; - local player = GetPlayerFromUserID(params.victim); - EndHint(player); + ::LinEventHook("OnGameEvent_lunge_pounce", ::LinGe.Hint.PlayerBeDominating, ::LinGe.Hint); // Hunter + ::LinEventHook("OnGameEvent_tongue_grab", ::LinGe.Hint.PlayerBeDominating, ::LinGe.Hint); // Smoker + ::LinEventHook("OnGameEvent_charger_pummel_start", ::LinGe.Hint.PlayerBeDominating, ::LinGe.Hint); // Charger + ::LinEventHook("OnGameEvent_jockey_ride", ::LinGe.Hint.PlayerBeDominating, ::LinGe.Hint); // Jockey } -::LinEventHook("OnGameEvent_pounce_stopped", ::LinGe.Hint.PlayerDominateEnd, ::LinGe.Hint); -::LinEventHook("OnGameEvent_tongue_release", ::LinGe.Hint.PlayerDominateEnd, ::LinGe.Hint); -::LinEventHook("OnGameEvent_charger_pummel_end", ::LinGe.Hint.PlayerDominateEnd, ::LinGe.Hint); -::LinEventHook("OnGameEvent_jockey_ride_end", ::LinGe.Hint.PlayerDominateEnd, ::LinGe.Hint); ::LinGe.Hint.ShowPlayerBeDominating <- function (player) { - local showTo = clone ::pyinfo.survivorIdx; - if (::LinGe.RemoveInArray(player.GetEntityIndex(), showTo) != null) + if (Config.help.duration <= 0) + return; + if (!player.IsValid() || ::LinGe.GetPlayerTeam(player) != 2 + || player.GetSpecialInfectedDominatingMe()==null) + return; + local idx = ::pyinfo.survivorIdx.find(player.GetEntityIndex()); + if (null == idx) + return; + if (Config.help.noShowSelf) { + local showTo = clone ::pyinfo.survivorIdx; + showTo.remove(idx); ShowHint(player.GetPlayerName() + "被控了", 3, player, - showTo, Config.friend.duration, HELP_ICON); + showTo, Config.help.duration, "icon_blank"); } -} - -} // if (::LinGe.Hint.Config.friend.dominateDelay >= 0) { + else + { + ShowHint(player.GetPlayerName() + "被控了", 3, player, + null, Config.help.duration, "icon_blank"); + } +}.bindenv(::LinGe.Hint); -// 玩家队伍变更,当某生还者出现队伍变更时,重置其身上的提示状态 -::LinGe.Hint.OnGameEvent_player_team <- function (params) +// 被控解除 +::LinGe.Hint.PlayerDominateEnd <- function (params) { - if (!params.rawin("userid")) + if (!params.rawin("victim") || params.victim == 0) return; - local player = GetPlayerFromUserID(params.userid); - - // 如果是离开生还者队伍,则移除其身上当前的提示 - if (2 == params.oldteam) + local player = GetPlayerFromUserID(params.victim); + if (::LinGe.IsAlive(player) && player.IsIncapacitated()) + ShowPlayerIncap(player); + else EndHint(player); } -::LinEventHook("OnGameEvent_player_team", ::LinGe.Hint.OnGameEvent_player_team, ::LinGe.Hint); +if (::LinGe.Hint.Config.help.duration > 0) +{ + ::LinEventHook("OnGameEvent_pounce_stopped", ::LinGe.Hint.PlayerDominateEnd, ::LinGe.Hint); + ::LinEventHook("OnGameEvent_tongue_release", ::LinGe.Hint.PlayerDominateEnd, ::LinGe.Hint); + ::LinEventHook("OnGameEvent_charger_pummel_end", ::LinGe.Hint.PlayerDominateEnd, ::LinGe.Hint); + ::LinEventHook("OnGameEvent_jockey_ride_end", ::LinGe.Hint.PlayerDominateEnd, ::LinGe.Hint); +} // BOT与玩家的交换,将状态进行转移 // BOT取代玩家 @@ -198,10 +275,11 @@ if (::LinGe.Hint.Config.friend.dominateDelay >= 0) { if (::LinGe.GetPlayerTeam(bot) == 2 && FindHintIndex(player)!=null) { ::VSLib.Timers.AddTimerByName(::LinGe.GetEntityTargetname(bot), - 0.1, false, Timer_CheckPlayer, bot); + 0.1, false, CheckSurvivor, bot); } } -::LinEventHook("OnGameEvent_player_bot_replace", ::LinGe.Hint.OnGameEvent_player_bot_replace, ::LinGe.Hint); +if (::LinGe.Hint.Config.help.duration > 0) + ::LinEventHook("OnGameEvent_player_bot_replace", ::LinGe.Hint.OnGameEvent_player_bot_replace, ::LinGe.Hint); // 玩家取代BOT ::LinGe.Hint.OnGameEvent_bot_player_replace <- function (params) @@ -211,31 +289,72 @@ if (::LinGe.Hint.Config.friend.dominateDelay >= 0) { if (::LinGe.GetPlayerTeam(player) == 2 && FindHintIndex(bot)!=null) { ::VSLib.Timers.AddTimerByName(::LinGe.GetEntityTargetname(player), - 0.1, false, Timer_CheckPlayer, player); + 0.1, false, CheckSurvivor, player); } } -::LinEventHook("OnGameEvent_bot_player_replace", ::LinGe.Hint.OnGameEvent_bot_player_replace, ::LinGe.Hint); +if (::LinGe.Hint.Config.help.duration > 0) + ::LinEventHook("OnGameEvent_bot_player_replace", ::LinGe.Hint.OnGameEvent_bot_player_replace, ::LinGe.Hint); -::LinGe.Hint.Timer_CheckPlayer <- function (player) +// 检查并在生还者身上出现状态提示,若其状态一切正常则返回true +::LinGe.Hint.CheckSurvivor <- function (player) { - if (!::LinGe.IsAlive(player)) - return; + if (!player.IsValid() || ::LinGe.GetPlayerTeam(player) != 2 + || !::LinGe.IsAlive(player)) + return false; + + if (player.GetSpecialInfectedDominatingMe()) + ShowPlayerBeDominating(player); else if (player.IsIncapacitated()) ShowPlayerIncap(player); else if (player.IsHangingFromLedge()) ShowPlayerLedge(player); - else if (player.GetSpecialInfectedDominatingMe()) - ShowPlayerBeDominating(player); else if (::LinGe.GetReviveCount(player) >= 2) ShowPlayerDying(player); + else + return true; + return false; }.bindenv(::LinGe.Hint); -} // if (::LinGe.Hint.Config.friend.duration > 0) +// 按键监控 +::LinGe.Hint.buttonState <- {}; +::LinGe.Hint.OnGameEvent_round_start <- function (params) +{ + foreach (val in ::pyinfo.survivorIdx) + { + local player = PlayerInstanceFromIndex(val); + if (player.GetNetworkIDString() != "BOT") + buttonState.rawset(val, 0); + } +} +::LinEventHook("OnGameEvent_round_start", ::LinGe.Hint.OnGameEvent_round_start, ::LinGe.Hint); + +// 玩家队伍变更,当某生还者出现队伍变更时,重置其身上的提示状态 +::LinGe.Hint.OnGameEvent_player_team <- function (params) +{ + if (!params.rawin("userid")) + return; + local player = GetPlayerFromUserID(params.userid); + + // 如果是离开生还者队伍,则移除其身上当前的提示 + if (2 == params.oldteam) + { + EndHint(player); + if (player.GetNetworkIDString() != "BOT") + buttonState.rawdelete(player.GetEntityIndex()); + } + else if (2 == params.team) + { + if (player.GetNetworkIDString() != "BOT") + buttonState.rawset(player.GetEntityIndex(), 0); + } +} +::LinEventHook("OnGameEvent_player_team", ::LinGe.Hint.OnGameEvent_player_team, ::LinGe.Hint); ::LinGe.Hint.ShowHint <- function ( text, level=0, target = "", showTo = null, duration = 0.0, icon = "icon_tip", color = "255 255 255") { - ::LinGe.DebugPrintl("ShowHint : " + text); + if (typeof showTo == "array" && showTo.len() == 0) + return; if (typeof target == "instance") target = ::LinGe.GetEntityTargetname(target); EndHint(target); // 不允许同一目标上存在两个提示 @@ -251,6 +370,7 @@ if (::LinGe.Hint.Config.friend.dominateDelay >= 0) { hint_caption = text.tostring(), hint_color = color, hint_forcecaption = "1", + hint_suppress_rest = icon==NONE_ICON ? "1" : "0", // 是否关闭图标 hint_icon_offscreen = icon, hint_icon_offset = "0", hint_icon_onscreen = icon, @@ -273,11 +393,11 @@ if (::LinGe.Hint.Config.friend.dominateDelay >= 0) { if (!ent) { printl("[LinGe] 创建 env_instructor_hint 实体失败"); - return; + return false; } ent.ValidateScriptScope(); - if (null == showTo || showTo.len() == 0) + if (null == showTo) { DoEntFire("!self", "ShowHint", "", 0, null, ent); } @@ -285,8 +405,12 @@ if (::LinGe.Hint.Config.friend.dominateDelay >= 0) { { foreach (val in showTo) { - DoEntFire("!self", "ShowHint", "", 0, PlayerInstanceFromIndex(val), ent); - ::LinGe.DebugPrintl("显示给 " + PlayerInstanceFromIndex(val).GetPlayerName()); + if (typeof val == "integer") + DoEntFire("!self", "ShowHint", "", 0, PlayerInstanceFromIndex(val), ent); + else if (typeof val == "instance") + DoEntFire("!self", "ShowHint", "", 0, val, ent); + else + throw "参数类型非法"; } } CurrentHint.push({ level=level, ent=ent, targetname=target }); @@ -343,32 +467,482 @@ if (::LinGe.Hint.Config.friend.dominateDelay >= 0) { CurrentHint.remove(idx); }.bindenv(::LinGe.Hint); -// 清理一个空位出来 若空位不足,则返回 false +// 保证至少一个空位。若空位不足,则清理一个同级或更低级别的信息(优先清理最低级、最早出现的提示) +// 若无可清理的空位,返回 false ::LinGe.Hint.AtLeastOne <- function (level) { - // 如果已经没有空位,则尝试清理一个出来 - if (CurrentHint.len() >= Config.limit) + if (level < 0) // level < 0 表示该提示无视上限 + return true; + if (CurrentHint.len() < Config.limit) + return true; + + local minLevel = level + 1; + local i = null, count = 0; + foreach (idx, val in CurrentHint) { - // 清理掉一个满足 <= level 的最低等级提示 - local minLevel = level + 1; - local i = null; - foreach (idx, val in CurrentHint) + if (val.level >= 0) { + count++; if (val.level < minLevel) { minLevel = val.level; i = idx; } } - if (i) + } + // 如果 level >= 0 的项目小于限制数量 则可以不清理 + if (count < Config.limit) + return true; + + if (i != null) + { + EndHint(i); + return true; + } + else + return false; +} + +// 实现玩家使用按键发出信号 +// 以下用了很多 @samisalreadytaken 的 Contextual Ping System 标记系统 MOD 里的代码 +// https://steamcommunity.com/sharedfiles/filedetails/?id=2638628508 +// https://github.com/samisalreadytaken/vscripts/blob/master/left4dead2/ping_system.nut +// 所有可拾取/使用的物品名称 不一定只是武器 +local weaponName = { + oxygentank = "氧气罐", + propanetank = "煤气罐", + fireworkcrate = "烟花", + gnome = "侏儒", + cola_bottles = "可乐", + gascan = "汽油桶", + + first_aid_kit = "医疗包", + pain_pills = "止疼药", + adrenaline = "肾上腺素", + defibrillator = "电击器", + + upgradepack_explosive = "高爆弹药包", + upgradepack_incendiary = "燃烧弹药包", + laser_sight = "激光瞄准", + + pipe_bomb = "土制炸弹", + molotov = "燃烧瓶", + vomitjar = "胆汁", + + ammo = "弹药", + melee = "近战武器", + baseball_bat = "棒球棒", + fireaxe = "斧头", + crowbar = "撬棍", + cricket_bat = "板球拍", + electric_guitar = "电吉他", + frying_pan = "平底锅", + golfclub = "高尔夫球棒", + katana = "武士刀", + knife = "小刀", + machete = "砍刀", + pitchfork = "干草叉", + shovel = "铲子", + tonfa = "警棍", + riotshield = "防爆盾", + chainsaw = "电锯", + + pistol = "手枪", + pistol_magnum = "马格南", + shotgun_chrome = "霰弹枪", + pumpshotgun = "霰弹枪", + autoshotgun = "自动霰弹枪", + shotgun_spas = "自动霰弹枪", + grenade_launcher = "榴弹", + smg = "SMG冲锋枪", + smg_mp5 = "MP5冲锋枪", + smg_silenced = "MAC冲锋枪", + rifle = "M16步枪", + rifle_ak47 = "AK47步枪", + rifle_desert = "SCAR步枪", + rifle_sg552 = "SG552步枪", + rifle_m60 = "M60机枪", + sniper_scout = "SCOUT狙击枪", + sniper_awp = "AWP狙击枪", + hunting_rifle = "猎枪", + sniper_military = "连发狙击枪", +}; + +local weaponIcon = { // 特定物品可以显示特定图标 + oxygentank = "icon_interact", + propanetank = "icon_interact", + fireworkcrate = "icon_interact", + gnome = "icon_interact", + cola_bottles = "icon_cola_bottles", + gascan = "icon_gas_can", + + first_aid_kit = "icon_equip_medkit", + pain_pills = "icon_equip_pills", + adrenaline = "icon_equip_adrenaline", + defibrillator = "icon_defibrillator", + + // upgradepack_explosive = "linge_upgradepack_explosive", + // upgradepack_incendiary = "linge_upgradepack_incendiary", + upgradepack_explosive = "icon_interact", + upgradepack_incendiary = "icon_interact", + laser_sight = "icon_laser_sight", + + pipe_bomb = "icon_equip_pipebomb", + molotov = "icon_equip_molotov", + // vomitjar = "linge_equip_vomitjar", + vomitjar = "icon_interact", + + ammo = "icon_equip_ammopack", + melee = "icon_interact", + baseball_bat = "icon_baseball_bat", + fireaxe = "icon_fireaxe", + crowbar = "icon_crowbar", + cricket_bat = "icon_cricket_bat", + electric_guitar = "icon_guitar", + frying_pan = "icon_frying_pan", + golfclub = "icon_interact", + katana = "icon_katana", + knife = "icon_knife", + machete = "icon_machete", + pitchfork = "icon_interact", + shovel = "icon_interact", + tonfa = "icon_tonfa", + riotshield = "icon_interact", + chainsaw = "icon_chainsaw", + + // 还有一个双枪的标志 icon_equip_dualpistols 不过懒得写这个判断了 + pistol = "icon_equip_pistol", + pistol_magnum = "icon_pistol", + shotgun_chrome = "icon_equip_chromeshotgun", + pumpshotgun = "icon_equip_pumpshotgun", + autoshotgun = "icon_equip_autoshotgun", + shotgun_spas = "icon_equip_spasshotgun", + grenade_launcher = "icon_equip_grenadelauncher", + smg = "icon_equip_uzi", + // smg_mp5 = "linge_smg_mp5", + smg_mp5 = "icon_interact", + smg_silenced = "icon_equip_silencedsmg", + rifle = "icon_equip_machinegun", + // rifle_ak47 = "linge_rifle_ak47", + rifle_ak47 = "icon_interact", + // rifle_desert = "icon_equip_desertrifle", + rifle_desert = "icon_interact", + // rifle_sg552 = "linge_rifle_sg552", + rifle_sg552 = "icon_interact", + rifle_m60 = "icon_interact", + // sniper_scout = "linge_sniper_scout", + // sniper_awp = "linge_sniper_awp", + sniper_scout = "icon_interact", + sniper_awp = "icon_interact", + hunting_rifle = "icon_equip_rifle", + sniper_military = "icon_equip_militarysniper", +}; + +local weaponModelPath = { + oxygentank = "models/props_equipment/oxygentank01.mdl", + propanetank = "models/props_junk/propanecanister001a.mdl", + cola_bottles = "models/w_models/weapons/w_cola.mdl", + gnome = "models/props_junk/gnome.mdl", + fireworkcrate = "models/props_junk/explosive_box001.mdl", + gascan = "models/props_junk/gascan001a.mdl", + + first_aid_kit = "models/w_models/weapons/w_eq_Medkit.mdl", + pain_pills = "models/w_models/weapons/w_eq_painpills.mdl", + adrenaline = "models/w_models/weapons/w_eq_adrenaline.mdl", + defibrillator = "models/w_models/weapons/w_eq_defibrillator.mdl", + + upgradepack_explosive = "models/w_models/weapons/w_eq_explosive_ammopack.mdl", + upgradepack_incendiary = "models/w_models/weapons/w_eq_incendiary_ammopack.mdl", + + molotov = "models/w_models/weapons/w_eq_molotov.mdl", + pipe_bomb = "models/w_models/weapons/w_eq_pipebomb.mdl", + vomitjar = "models/w_models/weapons/w_eq_bile_flask.mdl", + + // ammo = "models/props/terror/ammo_stack.mdl", + // ammo = "models/props_unique/spawn_apartment/coffeeammo.mdl", + baseball_bat = "models/weapons/melee/w_bat.mdl", + fireaxe = "models/weapons/melee/w_fireaxe.mdl", + crowbar = "models/weapons/melee/w_crowbar.mdl", + cricket_bat = "models/weapons/melee/w_cricket_bat.mdl", + electric_guitar = "models/weapons/melee/w_electric_guitar.mdl", + frying_pan = "models/weapons/melee/w_frying_pan.mdl", + golfclub = "models/weapons/melee/w_golfclub.mdl", + katana = "models/weapons/melee/w_katana.mdl", + knife = "models/w_models/weapons/w_knife_t.mdl", + machete = "models/weapons/melee/w_machete.mdl", + pitchfork = "models/weapons/melee/w_pitchfork.mdl", + shovel = "models/weapons/melee/w_shovel.mdl", + tonfa = "models/weapons/melee/w_tonfa.mdl", + riotshield = "models/weapons/melee/w_riotshield.mdl", + chainsaw = "models/w_models/weapons/w_chainsaw.mdl", + + pistol = "models/w_models/weapons/w_pistol_B.mdl", + pistol_magnum = "models/w_models/weapons/w_desert_eagle.mdl", + shotgun_chrome = "models/w_models/weapons/w_pumpshotgun_A.mdl", + pumpshotgun = "models/w_models/weapons/w_shotgun.mdl", + autoshotgun = "models/w_models/weapons/w_autoshot_m4super.mdl", + shotgun_spas = "models/w_models/weapons/w_shotgun_spas.mdl", + grenade_launcher = "models/w_models/weapons/w_grenade_launcher.mdl", + smg = "models/w_models/weapons/w_smg_uzi.mdl", + smg_mp5 = "models/w_models/weapons/w_smg_mp5.mdl", + smg_silenced = "models/w_models/weapons/w_smg_a.mdl", + rifle = "models/w_models/weapons/w_rifle_m16a2.mdl", + rifle_ak47 = "models/w_models/weapons/w_rifle_ak47.mdl", + rifle_desert = "models/w_models/weapons/w_desert_rifle.mdl", + rifle_sg552 = "models/w_models/weapons/w_rifle_sg552.mdl", + rifle_m60 = "models/w_models/weapons/w_m60.mdl", + sniper_scout = "models/w_models/weapons/w_sniper_scout.mdl", + sniper_awp = "models/w_models/weapons/w_sniper_awp.mdl", + hunting_rifle = "models/w_models/weapons/w_sniper_mini14.mdl", + sniper_military = "models/w_models/weapons/w_sniper_military.mdl", +}; + +local weaponEntity = {}; +local weaponSpawn = {}; +local weaponModel = {}; +foreach ( k, v in weaponName ) + weaponEntity[ "weapon_" + k ] <- k; // 会有不少不存在的实体名 不过无所谓 +foreach ( k, v in weaponName ) + weaponSpawn[ "weapon_" + k + "_spawn" ] <- k; +foreach ( k, v in weaponModelPath ) + weaponModel[v] <- k; + +local zombieType = ["Smoker", "Boomer", "Hunter", "Spitter", + "Jockey", "Charger", "Witch", "Tank"]; + +const IN_ALT1 = 0x4000; +const IN_ALT2 = 0x8000; +const MAX_COORD_FLOAT = 16384.0; +const MAX_TRACE_LENGTH = 56755.840862417; + +// 按键监测 +::LinGe.Hint.ButtonScanFunc <- function () +{ + if (!("LinGe" in getroottable())) + return; + + foreach (key, val in buttonState) + { + local player = PlayerInstanceFromIndex(key); + if (!::LinGe.IsAlive(player)) + continue; + // 判断绑定的按键是否松开 + local curPressed = player.GetButtonMask() & IN_ALT1; + if ( curPressed != val) + { + buttonState[key] = curPressed; + if (!curPressed) // 松开触发 + ::LinGe.Hint.PlayerPing(player); + } + } +} + +// 玩家使用按键发出信号 +::LinGe.Hint.PlayerPing <- function (player) +{ + if (Config.ping.duration <= 0) + return; + if (typeof player == "integer") + player = GetPlayerFromUserID(player); + if (typeof player != "instance") + throw "player 无效"; + if (Config.help.duration > 0) + { + if (player.GetSpecialInfectedDominatingMe()) + { + ShowPlayerBeDominating(player); + ClientPrint(player, 3, "\x05已发出被控求救信号"); + } + // 如果玩家处于虚弱状态,则发出求救信号 + else if (player.IsIncapacitated()) + { + ShowPlayerIncap(player); + ClientPrint(player, 3, "\x05已发出倒地求救信号"); + } + else if (player.IsHangingFromLedge()) + { + ShowPlayerLedge(player); + ClientPrint(player, 3, "\x05已发出挂边求救信号"); + } + else + PingTrace(player); + } + // else + // PingTrace(player); +}.bindenv(::LinGe.Hint); +if (::LinGe.Hint.Config.ping.duration > 0) + ::LinPlayerPing <- ::LinGe.Hint.PlayerPing.weakref(); // 这是留给插件调用的 +else + getroottable().rawdelete("LinPlayerPing"); + +// 通过玩家视野进行光线追踪查找实体 +::LinGe.Hint.PingTrace <- function (player) +{ + local eyePos = player.EyePosition(); + local tr = { + start = eyePos, + end = eyePos + player.EyeAngles().Forward().Scale( MAX_TRACE_LENGTH ), + ignore = player, + mask = MASK_SHOT_HULL & (~CONTENTS_WINDOW), + }; + + TraceLine(tr); + PingEntity(player, tr.enthit, tr.pos); +} + +// 标记到实体 +::LinGe.Hint.PingEntity <- function (player, pEnt, vecPingPos = null) +{ + local szClassname = pEnt.GetClassname(); + + switch ( szClassname ) + { + case "player": + if (::LinGe.GetPlayerTeam(pEnt) == 3) // 如果是特感 + { + local type = pEnt.GetZombieType(); + if (type == 8) + ShowHint("Tank!", 1, pEnt, null, Config.ping.duration, "icon_alert_red"); + else if (type > 0 && type < 8) // 除Tank外其余特感不显示图标,因为容易遮挡住特感 + ShowHint(zombieType[type-1] + "!", 1, pEnt, null, Config.ping.duration, NONE_ICON); + } + else if (::LinGe.GetPlayerTeam(pEnt) == 2) + { + if (Config.help.duration <= 0 || CheckSurvivor(pEnt)) + { + // 如果不允许玩家状态标记,或者队友是健康的,则单独给发出标记的玩家提示血量 + ShowHint("当前血量:" + ceil(pEnt.GetHealth() + pEnt.GetHealthBuffer()), -1, pEnt, [player], 2.0, NONE_ICON); + } + } + break; + case "witch": + ShowHint("当心Witch!", 1, pEnt, null, Config.ping.duration, NONE_ICON); // 为了避免影响视线,对Witch的标记不显示图标 + break; +// case "infected": // 小僵尸 +// break; + case "prop_physics": + local model = pEnt.GetModelName(); + if (weaponModel.rawin(model)) + ShowHint(weaponName[weaponModel[model]], 0, pEnt, null, Config.ping.duration, weaponIcon[weaponModel[model]]); + break; + case "prop_dynamic": // 很多地方都会有这种实体,所以用了一个稍微有点意义不明的提示( + ShowHint("这里!", 0, pEnt, null, Config.ping.duration, "icon_run"); + break; + case "prop_health_cabinet": // 医疗箱 + if ( NetProps.GetPropInt( pEnt, "m_isUsed" ) == 1 ) { - EndHint(i); - return true; + // 如果医疗箱已经打开了则追踪里面的物体 + local tr = { + start = vecPingPos, + end = vecPingPos + player.EyeAngles().Forward().Scale( MAX_COORD_FLOAT ), + ignore = pEnt, + mask = MASK_SHOT_HULL & (~CONTENTS_WINDOW), + }; + TraceLine(tr); + + if ( tr.enthit.GetEntityIndex() != 0 ) + { + PingEntity( player, tr.enthit, tr.pos); + return; + } + } + ShowHint("医疗箱", 0, pEnt, null, Config.ping.duration, "icon_interact"); + break; + case "prop_car_alarm": + if (!NetProps.GetPropInt( pEnt, "m_bDisabled" ) ) + ShowHint("注意警报!", 0, pEnt, null, Config.ping.duration, "icon_alert_red"); + else + ShowHint("警报不会触发", -1, pEnt, [player], 2, "icon_tip"); + break; + case "prop_door_rotating": + ShowHint("走这里吧", 0, pEnt, null, Config.ping.duration, "icon_door"); + break; + case "prop_door_rotating_checkpoint": + ShowHint("安全屋", 0, pEnt, null, Config.ping.duration, "icon_door"); + break; + // case "prop_fuel_barrel": + // break; + case "upgrade_ammo_explosive": + ShowHint("高爆弹药", 0, pEnt, null, Config.ping.duration, "icon_explosive_ammo"); + break; + case "upgrade_ammo_incendiary": + ShowHint("燃烧弹药", 0, pEnt, null, Config.ping.duration, "icon_incendiary_ammo"); + break; + case "upgrade_laser_sight": + ShowHint("激光瞄准", 0, pEnt, null, Config.ping.duration, "icon_laser_sight"); + break; + // case "worldspawn": + // break; + // Partial matches and undefined entities + default: + // All weapons go through here + if (szClassname.find("weapon") == 0 ) + { + local model = pEnt.GetModelName(); + if (weaponModel.rawin(model)) + ShowHint(weaponName[weaponModel[model]], 0, pEnt, null, Config.ping.duration, weaponIcon[weaponModel[model]]); + else if (weaponEntity.rawin(szClassname)) + ShowHint(weaponName[weaponEntity[szClassname]], 0, pEnt, null, Config.ping.duration, weaponIcon[weaponEntity[szClassname]]); + else if (weaponSpawn.rawin(szClassname)) + ShowHint(weaponName[weaponSpawn[szClassname]], 0, pEnt, null, Config.ping.duration, weaponIcon[weaponSpawn[szClassname]]); } else - return false; + { + // 有些实体模型比较小,准星没对准很容易标记不到 + local weapon = null; + while ( weapon = Entities.FindByClassnameWithin(weapon, "weapon_*", vecPingPos, 15.0) ) + { + if ( weapon.IsValid() && weapon.GetMoveParent() == null) // 查找到的实体必须是有效且无主的 + { + PingEntity(player, weapon, weapon.GetLocalOrigin()); + return; + } + } + // 如果查找不到就标记普通路径点 + if (Config.ping.emptySpace) + { + local entInfo = SetInfoTarget(vecPingPos); + if (entInfo) + { + ShowHint("这里!", -1, entInfo, null, Config.ping.duration, "icon_run"); + } + } + } + break; } - return true; +} + +// 创建路径点标记实体 +local infoTargetEnt = null; +::LinGe.Hint.SetInfoTarget <- function (origin) +{ + if (null == infoTargetEnt) + infoTargetEnt = SpawnEntityFromTable("info_target_instructor_hint", { targetname = "LinGe_Hint_infoTarget" }); + if (null == infoTargetEnt) + { + printl("[LinGe] 无法创建 info_target_instructor_hint"); + return null; + } + + infoTargetEnt.SetLocalOrigin(origin); + return infoTargetEnt; +} + +// 启用按键监控 +if (!::LinGe.Hint.rawin("_buttonScaner") && ::LinGe.Hint.Config.ping.duration > 0) +{ + ::LinGe.Hint._buttonScaner <- SpawnEntityFromTable("info_target", { targetname = "LinGe_Hint_buttonScan" }); + if (::LinGe.Hint._buttonScaner != null) + { + ::LinGe.Hint._buttonScaner.ValidateScriptScope(); + local scrScope = ::LinGe.Hint._buttonScaner.GetScriptScope(); + scrScope.buttonState <- ::LinGe.Hint.buttonState; + scrScope["ButtonScanFunc"] <- ::LinGe.Hint.ButtonScanFunc; + AddThinkToEnt(::LinGe.Hint._buttonScaner, "ButtonScanFunc"); + // printl("[LinGe] 按键监视器已创建"); + } + else + throw "无法创建按键监视器"; } } // if (::LinGe.Hint.Config.limit > 0) { diff --git a/vscripts/LinGe/MoreSI.nut b/vscripts/LinGe/MoreSI.nut index ec6dad9..c3db51c 100644 --- a/vscripts/LinGe/MoreSI.nut +++ b/vscripts/LinGe/MoreSI.nut @@ -10,7 +10,7 @@ local sitypelist = ["Boomer", "Spitter", "Smoker", "Hunter", "Charger", "Jockey" siauto = 0, // 每1名生还者增加多少特感。在基础特感数量上增加,为0则不自动增加 sitime = 15, // 特感刷新间隔 若设定为 < 0 则单独关闭特感刷新时间控制 sionly = [], // 只允许生成哪些特感,若数组为空则不限制 - noci = false // 是否清除小僵尸 + sinoci = false // 是否清除小僵尸 }; ::LinGe.Config.Add("MoreSI", ::LinGe.MoreSI.Config); ::LinGe.Cache.MoreSI_Cache <- ::LinGe.MoreSI.Config; @@ -24,7 +24,7 @@ local _enabled = ::LinGe.MoreSI.Config.enabled; // 此时 enabled 的值为配 // 判断哪些控制处于开启 local ctrlNum = (Config.sibase >= 0); local ctrlTime = (Config.sitime >= 0); - local ctrlNoci = Config.noci; + local ctrlNoci = Config.sinoci; Checksionly(); local ctrlType = ( Config.sionly.len() > 0 ); @@ -162,7 +162,7 @@ local _enabled = ::LinGe.MoreSI.Config.enabled; // 此时 enabled 的值为配 text += "刷新控制为\x03 关闭"; ClientPrint(null, 3, text); - if (Config.sionly.len() > 0 || Config.noci) + if (Config.sionly.len() > 0 || Config.sinoci) { text = "\x04多特控制:" if (Config.sionly.len() > 0) @@ -174,7 +174,7 @@ local _enabled = ::LinGe.MoreSI.Config.enabled; // 此时 enabled 的值为配 } else text += "限制特感生成 \x03关闭"; - if (Config.noci) + if (Config.sinoci) text += "\x04,无小僵尸 \x03开启"; else text += "\x04,无小僵尸 \x03关闭"; @@ -282,9 +282,9 @@ local _enabled = ::LinGe.MoreSI.Config.enabled; // 此时 enabled 的值为配 { noci = args[4]; if ("on" == noci) - Config.noci = true; + Config.sinoci = true; else if ("off" == noci) - Config.noci = false; + Config.sinoci = false; else noci = -2; } @@ -452,12 +452,12 @@ local _enabled = ::LinGe.MoreSI.Config.enabled; // 此时 enabled 的值为配 if (args.len() == 2) { if (args[1] == "on") - Config.noci = true; + Config.sinoci = true; else if (args[1] == "off") - Config.noci = false; + Config.sinoci = false; ExecConfig(); } - if (Config.noci) + if (Config.sinoci) ClientPrint(null, 3, "\x04多特控制:无小僵尸 \x03开启"); else ClientPrint(null, 3, "\x04多特控制:无小僵尸 \x03关闭"); diff --git a/vscripts/LinGe/Server.nut b/vscripts/LinGe/Server.nut deleted file mode 100644 index 711d8e0..0000000 --- a/vscripts/LinGe/Server.nut +++ /dev/null @@ -1,52 +0,0 @@ -printl("[LinGe] Server 正在载入"); -::LinGe.Server <- {}; - -// 服务器控制 附加功能脚本 -::LinGe.Server.Config <- { - zs = { - enabled = true, - hint = true, - incap = "我不想变成魔女,这个世界还有许多我想守护的东西", - noIncap = "灵魂宝石会孕育出魔女的话,大家不就只有去死了吗!" - } -}; -::LinGe.Config.Add("Server", ::LinGe.Server.Config); -::LinGe.Cache.Server_Config <- ::LinGe.Server.Config; - -if ("coop" == g_BaseMode && ::LinGe.Server.Config.zs.enabled) { - -// !zs 自杀指令 -::LinGe.Server.Cmd_zs <- function (player, args) -{ - if (args.len() == 1) - { - if (!player.IsSurvivor()) - return; - - local vplayer = ::VSLib.Player(player); - if (!vplayer.IsPlayerEntityValid()) - return; - if (!vplayer.IsAlive() || vplayer.IsDead()) - return; - - local isIncapacitated = vplayer.IsIncapacitated(); - vplayer.Kill(); - if (Config.zs.hint) // 如果开启了自杀后提示 - { - if (!vplayer.IsAlive() || vplayer.IsDead()) // 可能不准 - { - if (isIncapacitated) - { - Say(player, "\x03" + Config.zs.incap, false); - } - else - { - Say(player, "\x03" + Config.zs.noIncap, false); - } - } - } - } -} -::LinCmdAdd("zs", ::LinGe.Server.Cmd_zs, ::LinGe.Server, "自杀指令", false); - -} // if ("coop" == g_BaseMode) { \ No newline at end of file diff --git a/vscripts/LinGe/zs.nut b/vscripts/LinGe/zs.nut new file mode 100644 index 0000000..d9c9051 --- /dev/null +++ b/vscripts/LinGe/zs.nut @@ -0,0 +1,49 @@ +::LinGe.zs <- {}; + +// 服务器控制 附加功能脚本 +::LinGe.zs.Config <- { + enabled = true, + hint = true, + incap = "我不想变成魔女,这个世界还有许多我想守护的东西", + noIncap = "灵魂宝石会孕育出魔女的话,大家不就只有去死了吗!" +}; +::LinGe.Config.Add("zs", ::LinGe.zs.Config); +//::LinGe.Cache.zs_Config <- ::LinGe.zs.Config; + +if ("coop" == g_BaseMode && ::LinGe.zs.Config.enabled) { +printl("[LinGe] 自杀指令 正在载入"); +// !zs 自杀指令 +::LinGe.zs.Cmd_zs <- function (player, args) +{ + if (args.len() == 1) + { + if (::LinGe.GetPlayerTeam(player) != 2) + return; + + local vplayer = ::VSLib.Player(player); + if (!vplayer.IsPlayerEntityValid()) + return; + if (!vplayer.IsAlive() || vplayer.IsDead()) + return; + + local isIncapacitated = vplayer.IsIncapacitated(); + vplayer.Kill(); + if (Config.hint) // 如果开启了自杀后提示 + { + if (!vplayer.IsAlive() || vplayer.IsDead()) // 可能不准 + { + if (isIncapacitated) + { + Say(player, "\x03" + Config.incap, false); + } + else + { + Say(player, "\x03" + Config.noIncap, false); + } + } + } + } +} +::LinCmdAdd("zs", ::LinGe.zs.Cmd_zs, ::LinGe.zs, "自杀指令", false); + +} \ No newline at end of file diff --git a/vscripts/VSLib/easylogic.nut b/vscripts/VSLib/easylogic.nut index 9e180a7..98d5bb3 100644 --- a/vscripts/VSLib/easylogic.nut +++ b/vscripts/VSLib/easylogic.nut @@ -645,7 +645,8 @@ if ( !("MutationState" in g_ModeScript) ) ::VSLib.GlobalCache <- {}; // Attempt read from session - ::VSLib.GlobalCache <- ::VSLib.Utils.DeserializeIdxTable(::VSLib.GlobalCacheSession); + if (::VSLib.rawin("GlobalCacheSession")) + ::VSLib.GlobalCache <- ::VSLib.Utils.DeserializeIdxTable(::VSLib.GlobalCacheSession); if (::VSLib.GlobalCache == null) ::VSLib.GlobalCache <- {}; diff --git a/vscripts/director_base_addon.nut b/vscripts/director_base_addon.nut index 5c6f138..8ebc86b 100644 --- a/vscripts/director_base_addon.nut +++ b/vscripts/director_base_addon.nut @@ -6,5 +6,5 @@ IncludeScript("LinGe/Base"); // 必须 基础库 注册事件函数,实现了 // 请不要更改必须库的载入顺序,否则将无法正确载入脚本 IncludeScript("LinGe/HUD"); // 可选 HUD 击杀与伤害统计 友伤提示等 IncludeScript("LinGe/MoreSI"); // 可选 简易的多特控制 -IncludeScript("LinGe/Server"); // 可选 服务器控制功能 +IncludeScript("LinGe/zs"); // 可选 自杀指令 IncludeScript("LinGe/Hint"); // 可选 玩家状态提示与物品标记功能 \ No newline at end of file diff --git a/vscripts/mod_textures.txt b/vscripts/mod_textures.txt new file mode 100644 index 0000000..573e7b3 --- /dev/null +++ b/vscripts/mod_textures.txt @@ -0,0 +1,1897 @@ +// 这个文件是从 pak01_dir.vpk 解包出来的 位于 scripts 文件夹下 +// 标记系统 MOD 用到了 materials\vgui\hud\iconsheet1-3.vtf 这三个文件里的图标 +// 在查找的过程中发现这个文件里缺少部分图标的坐标,还有存在错误坐标的 +// 所以我进行了修补,添加修改的内容均放在最后。我将其打包到 vpk 中 +// 然后在游戏中测试,结果我新增的键并不能生效 +// 但是原本就有、经过我修改坐标的 "icon_equip_desertrifle" 还是生效了 +// 具体原因我不清楚,只能暂时先不用这个修改后的方案了 +"sprites/640_hud" +{ + TextureData + { + "whiteAdditive" + { + "file" "vgui/white_additive" + "x" "0" + "y" "0" + "width" "1" + "height" "1" + } + + "d_headshot" + { + "file" "sprites/headshot_icon" + "x" "0" + "y" "0" + "width" "64" + "height" "64" + } + + "d_skull_cs" + { + "file" "sprites/death_icon" + "x" "0" + "y" "0" + "width" "64" + "height" "64" + } + + "pain_up" + { + "file" "sprites/640_pain_up" + "x" "0" + "y" "0" + "width" "128" + "height" "48" + } + + "pain_down" + { + "file" "sprites/640_pain_down" + "x" "0" + "y" "0" + "width" "128" + "height" "48" + } + + "pain_left" + { + "file" "sprites/640_pain_left" + "x" "0" + "y" "0" + "width" "48" + "height" "128" + } + + "pain_right" + { + "file" "sprites/640_pain_right" + "x" "0" + "y" "0" + "width" "48" + "height" "128" + } + + // Side voice bars + "voice_player" + { + "font" "L4D_Icons" + "character" "V" + } + + // Local player HUD element + "voice_self" + { + "font" "L4D_Icons_large" + "character" "V" + } + + // Teammates above their HUD elements + "voice_teammate" + { + "font" "L4D_Icons_medium" + "character" "V" + } + + "number_0" + { + "font" "Icons" + "character" "0" + } + + "number_1" + { + "font" "Icons" + "character" "1" + } + + "number_2" + { + "font" "Icons" + "character" "2" + } + + "number_3" + { + "font" "Icons" + "character" "3" + } + + "number_4" + { + "font" "Icons" + "character" "4" + } + + "number_5" + { + "font" "Icons" + "character" "5" + } + + "number_6" + { + "font" "Icons" + "character" "6" + } + + "number_7" + { + "font" "Icons" + "character" "7" + } + + "number_8" + { + "font" "Icons" + "character" "8" + } + + "number_9" + { + "font" "Icons" + "character" "9" + } + + + // TERROR: ---------------------------------------------------- + "SkullIcon" + { + "file" "sprites/skull_icon" + "x" "0" + "y" "0" + "width" "32" + "height" "32" + } + "s_panel_healing_mini_prog" + { + "file" "vgui/s_panel_healing_mini_prog" + "x" "0" + "y" "0" + "width" "0" + "height" "0" + } + "rating_0_stars" + { + "file" "vgui/ratingstars" + "x" "0" + "y" "105" + "width" "128" + "height" "21" + } + "rating_1_stars" + { + "file" "vgui/ratingstars" + "x" "0" + "y" "84" + "width" "128" + "height" "21" + } + "rating_2_stars" + { + "file" "vgui/ratingstars" + "x" "0" + "y" "63" + "width" "128" + "height" "21" + } + "rating_3_stars" + { + "file" "vgui/ratingstars" + "x" "0" + "y" "42" + "width" "128" + "height" "21" + } + "rating_4_stars" + { + "file" "vgui/ratingstars" + "x" "0" + "y" "21" + "width" "128" + "height" "21" + } + "rating_5_stars" + { + "file" "vgui/ratingstars" + "x" "0" + "y" "0" + "width" "128" + "height" "21" + } + "Boomer" + { + "file" "vgui/tipgraphic" + "x" "512" + "y" "64" + "width" "64" + "height" "64" + } + "Tank" + { + "file" "vgui/tipgraphic" + "x" "448" + "y" "64" + "width" "64" + "height" "64" + } + "Hunter" + { + "file" "vgui/tipgraphic" + "x" "512" + "y" "0" + "width" "64" + "height" "64" + } + "Smoker" + { + "file" "vgui/tipgraphic" + "x" "448" + "y" "0" + "width" "64" + "height" "64" + } + "InfectedGroup" + { + "file" "vgui/pz_groupshot" + "x" "0" + "y" "0" + "width" "256" + "height" "256" + } + "Biker" + { + "file" "vgui/checkpointheroes" + "x" "0" + "y" "0" + "width" "256" + "height" "256" + } + "TeenGirl" + { + "file" "vgui/checkpointheroes" + "x" "256" + "y" "0" + "width" "256" + "height" "256" + } + "Manager" + { + "file" "vgui/checkpointheroes" + "x" "0" + "y" "256" + "width" "256" + "height" "256" + } + "NamVet" + { + "file" "vgui/checkpointheroes" + "x" "256" + "y" "256" + "width" "256" + "height" "256" + } + + + + "Unknown" // sometimes the transition stats fails to figure out which survivor to show and tries to show "Unknown" + { + "file" "vgui/checkpointheroes" + "x" "256" + "y" "0" + "width" "256" + "height" "256" + } + "teengirl_outline" + { + "file" "vgui/groupoutline" + "x" "0" + "y" "0" + "width" "64" + "height" "128" + } + "namvet_outline" + { + "file" "vgui/groupoutline" + "x" "64" + "y" "0" + "width" "64" + "height" "128" + } + "biker_outline" + { + "file" "vgui/groupoutline" + "x" "128" + "y" "0" + "width" "64" + "height" "128" + } + "manager_outline" + { + "file" "vgui/groupoutline" + "x" "192" + "y" "0" + "width" "64" + "height" "128" + } + "clock_1" + { + "file" "vgui/checkpointclock" + "x" "0" + "y" "0" + "width" "64" + "height" "64" + } + +// L4D Tips + // Row 1 + "tips_spitter" + { + "file" "vgui/tipgraphic" + "x" "192" + "y" "0" + "width" "64" + "height" "64" + } + "tip_tank_incap" + { + "file" "vgui/tipgraphic" + "x" "256" + "y" "0" + "width" "64" + "height" "64" + } + "tip_shove" + { + "file" "vgui/tipgraphic" + "x" "320" + "y" "64" + "width" "64" + "height" "64" + } + "tip_witch" + { + "file" "vgui/tipgraphic" + "x" "384" + "y" "0" + "width" "64" + "height" "64" + } + "tips_smoker" + { + "file" "vgui/tipgraphic" + "x" "448" + "y" "0" + "width" "64" + "height" "64" + } + "tips_hunter" + { + "file" "vgui/tipgraphic" + "x" "512" + "y" "0" + "width" "64" + "height" "64" + } + "tip_heal" + { + "file" "vgui/tipgraphic" + "x" "576" + "y" "64" + "width" "64" + "height" "64" + } + "tip_doorbreak" + { + "file" "vgui/tipgraphic" + "x" "640" + "y" "0" + "width" "64" + "height" "64" + } + "tip_items" + { + "file" "vgui/tipgraphic" + "x" "704" + "y" "64" + "width" "64" + "height" "64" + } + "tip_boomer_bile" + { + "file" "vgui/tipgraphic" + "x" "768" + "y" "0" + "width" "64" + "height" "64" + } + "tip_upgradepack" + { + "file" "vgui/tipgraphic" + "x" "832" + "y" "0" + "width" "64" + "height" "64" + } + "tip_lasersight" + { + "file" "vgui/tipgraphic" + "x" "896" + "y" "0" + "width" "64" + "height" "64" + } + "tip_melee" + { + "file" "vgui/tipgraphic" + "x" "960" + "y" "0" + "width" "64" + "height" "64" + } + + // Row 2 + "tips_charger" + { + "file" "vgui/tipgraphic" + "x" "192" + "y" "64" + "width" "64" + "height" "64" + } + "tips_jockey" + { + "file" "vgui/tipgraphic" + "x" "256" + "y" "64" + "width" "64" + "height" "64" + } + "tip_crouch" + { + "file" "vgui/tipgraphic" + "x" "320" + "y" "64" + "width" "64" + "height" "64" + } + "tip_revive" + { + "file" "vgui/tipgraphic" + "x" "384" + "y" "64" + "width" "64" + "height" "64" + } + "tips_tank" + { + "file" "vgui/tipgraphic" + "x" "448" + "y" "64" + "width" "64" + "height" "64" + } + "tips_boomer" + { + "file" "vgui/tipgraphic" + "x" "512" + "y" "64" + "width" "64" + "height" "64" + } + "tip_pipebomb" + { + "file" "vgui/tipgraphic" + "x" "576" + "y" "64" + "width" "64" + "height" "64" + } + "tip_checkpoint" + { + "file" "vgui/tipgraphic" + "x" "640" + "y" "64" + "width" "64" + "height" "64" + } + "tip_shotguns" + { + "file" "vgui/tipgraphic" + "x" "704" + "y" "64" + "width" "64" + "height" "64" + } + "tip_explosive_ammo" + { + "file" "vgui/tipgraphic" + "x" "768" + "y" "64" + "width" "64" + "height" "64" + } + "tip_incendiary_ammo" + { + "file" "vgui/tipgraphic" + "x" "832" + "y" "64" + "width" "64" + "height" "64" + } + "tip_syringe" + { + "file" "vgui/tipgraphic" + "x" "896" + "y" "64" + "width" "64" + "height" "64" + } + "tip_grenadelauncher" + { + "file" "vgui/tipgraphic" + "x" "960" + "y" "64" + "width" "64" + "height" "64" + } + + //Row 3 + "tip_boomer_vomit" + { + "file" "vgui/tipgraphic" + "x" "0" + "y" "128" + "width" "128" + "height" "128" + } + "tip_boomer_swipe" + { + "file" "vgui/tipgraphic" + "x" "128" + "y" "128" + "width" "128" + "height" "128" + } + "tip_hunter_pounce" + { + "file" "vgui/tipgraphic" + "x" "256" + "y" "128" + "width" "128" + "height" "128" + } + "tip_hunter_swipe" + { + "file" "vgui/tipgraphic" + "x" "384" + "y" "128" + "width" "128" + "height" "128" + } + "tip_smoker_pull" + { + "file" "vgui/tipgraphic" + "x" "512" + "y" "128" + "width" "128" + "height" "128" + } + "tip_smoker_swipe" + { + "file" "vgui/tipgraphic" + "x" "640" + "y" "128" + "width" "128" + "height" "128" + } + "tip_jockey_pounce" + { + "file" "vgui/tipgraphic" + "x" "768" + "y" "128" + "width" "128" + "height" "128" + } + "tip_jockey_swipe" + { + "file" "vgui/tipgraphic" + "x" "896" + "y" "128" + "width" "128" + "height" "128" + } + // Row 4 + "tip_charger_charge" + { + "file" "vgui/tipgraphic" + "x" "0" + "y" "256" + "width" "128" + "height" "128" + } + "tip_charger_pummel" + { + "file" "vgui/tipgraphic" + "x" "128" + "y" "256" + "width" "128" + "height" "128" + } + "tip_spitter_spit" + { + "file" "vgui/tipgraphic" + "x" "256" + "y" "256" + "width" "128" + "height" "128" + } + "tip_spitter_claw" + { + "file" "vgui/tipgraphic" + "x" "384" + "y" "256" + "width" "128" + "height" "128" + } + +// Bottom of Tipgraphic file +// Infected Panels (find a place to spawn) + "tip_hunter" + { + "file" "vgui/tipgraphic" + "x" "832" + "y" "448" + "width" "192" + "height" "192" + } + "tip_spitter" + { + "file" "vgui/tipgraphic" + "x" "256" + "y" "640" + "width" "192" + "height" "192" + } + "tip_jockey" + { + "file" "vgui/tipgraphic" + "x" "256" + "y" "832" + "width" "192" + "height" "192" + } + "tip_charger" + { + "file" "vgui/tipgraphic" + "x" "256" + "y" "448" + "width" "192" + "height" "192" + } + "tip_boomer" + { + "file" "vgui/tipgraphic" + "x" "64" + "y" "832" + "width" "192" + "height" "192" + } + "tip_tank" + { + "file" "vgui/tipgraphic" + "x" "64" + "y" "640" + "width" "192" + "height" "192" + } + "tip_smoker" + { + "file" "vgui/tipgraphic" + "x" "832" + "y" "832" + "width" "192" + "height" "192" + } +// StatScreenIcons + "Stat_Most_Tank_Dmg" + { + "file" "vgui/tipgraphic" + "x" "64" + "y" "640" + "width" "192" + "height" "192" + } + "Stat_Most_Special_Kills" + { + "file" "vgui/tipgraphic" + "x" "448" + "y" "448" + "width" "192" + "height" "192" + } + "Stat_Most_Melee_Kills" + { + "file" "vgui/tipgraphic" + "x" "448" + "y" "832" + "width" "192" + "height" "192" + } + "Stat_Most_Infected_Kills" + { + "file" "vgui/tipgraphic" + "x" "640" + "y" "640" + "width" "192" + "height" "192" + } + "Stat_Most_Headshots" + { + "file" "vgui/tipgraphic" + "x" "448" + "y" "640" + "width" "192" + "height" "192" + } + "Stat_Most_Witch_Dmg" + { + "file" "vgui/tipgraphic" + "x" "832" + "y" "640" + "width" "192" + "height" "192" + } +// Row 8 +// StatScreenIcons + "Stat_vs_Most_Damage_Done" + { + "file" "vgui/tipgraphic" + "x" "640" + "y" "448" + "width" "192" + "height" "192" + } + "Stat_vs_Most_Hunter_Pounces" + { + "file" "vgui/tipgraphic" + "x" "832" + "y" "448" + "width" "192" + "height" "192" + } + "Stat_vs_Most_Smoker_Pulls" + { + "file" "vgui/tipgraphic" + "x" "832" + "y" "832" + "width" "192" + "height" "192" + } + "Stat_vs_Most_Vomit_Hit" + { + "file" "vgui/tipgraphic" + "x" "64" + "y" "832" + "width" "192" + "height" "192" + } + "Stat_vs_Most_Spit_Dmg" + { + "file" "vgui/tipgraphic" + "x" "256" + "y" "640" + "width" "192" + "height" "192" + } + "Stat_vs_Most_Jockey_Rides" + { + "file" "vgui/tipgraphic" + "x" "256" + "y" "832" + "width" "192" + "height" "192" + } + "Stat_vs_Most_Damage_As_Tank" + { + "file" "vgui/tipgraphic" + "x" "64" + "y" "640" + "width" "192" + "height" "192" + } + "Stat_vs_Most_Damage_As_Charger" + { + "file" "vgui/tipgraphic" + "x" "256" + "y" "448" + "width" "192" + "height" "192" + } + +// Iconsheet +// Row 1 + "icon_shield" + { + "file" "vgui/hud/iconsheet" + "x" "0" + "y" "0" + "width" "64" + "height" "64" + } + "icon_run" + { + "file" "vgui/hud/iconsheet" + "x" "64" + "y" "0" + "width" "64" + "height" "64" + } + "icon_noMedKit" + { + "file" "vgui/hud/iconsheet" + "x" "128" + "y" "0" + "width" "64" + "height" "64" + } + "icon_no" + { + "file" "vgui/hud/iconsheet" + "x" "192" + "y" "0" + "width" "64" + "height" "64" + } + "icon_mouseRight" + { + "file" "vgui/hud/iconsheet" + "x" "256" + "y" "0" + "width" "64" + "height" "64" + } + "icon_mouseLeft" + { + "file" "vgui/hud/iconsheet" + "x" "320" + "y" "0" + "width" "64" + "height" "64" + } + "icon_medkit" + { + "file" "vgui/hud/iconsheet" + "x" "384" + "y" "0" + "width" "64" + "height" "64" + } + "icon_interact" + { + "file" "vgui/hud/iconsheet" + "x" "448" + "y" "0" + "width" "64" + "height" "64" + } + +// Row 2 + "icon_door" + { + "file" "vgui/hud/iconsheet" + "x" "0" + "y" "64" + "width" "64" + "height" "64" + } + "icon_button" + { + "file" "vgui/hud/iconsheet" + "x" "64" + "y" "64" + "width" "64" + "height" "64" + } + "icon_blank" + { + "file" "vgui/hud/iconsheet" + "x" "128" + "y" "64" + "width" "64" + "height" "64" + } + "icon_arrow_up" + { + "file" "vgui/hud/iconsheet" + "x" "192" + "y" "64" + "width" "64" + "height" "64" + } + "icon_arrow_right" + { + "file" "vgui/hud/iconsheet" + "x" "256" + "y" "64" + "width" "64" + "height" "64" + } + "icon_arrow_plain" + { + "file" "vgui/hud/iconsheet" + "x" "320" + "y" "64" + "width" "64" + "height" "64" + } + "icon_alert_red" + { + "file" "vgui/hud/iconsheet" + "x" "384" + "y" "64" + "width" "64" + "height" "64" + } + "icon_tip" + { + "file" "vgui/hud/iconsheet" + "x" "448" + "y" "64" + "width" "64" + "height" "64" + } + +// Row 3 + "icon_key_right" + { + "file" "vgui/hud/iconsheet" + "x" "0" + "y" "128" + "width" "64" + "height" "64" + } + "icon_key_left" + { + "file" "vgui/hud/iconsheet" + "x" "64" + "y" "128" + "width" "64" + "height" "64" + } + "icon_key_down" + { + "file" "vgui/hud/iconsheet" + "x" "128" + "y" "128" + "width" "64" + "height" "64" + } + "icon_key_up" + { + "file" "vgui/hud/iconsheet" + "x" "192" + "y" "128" + "width" "64" + "height" "64" + } + "icon_mouseWheel_up" + { + "file" "vgui/hud/iconsheet" + "x" "256" + "y" "128" + "width" "64" + "height" "64" + } + "icon_mouseWheel_down" + { + "file" "vgui/hud/iconsheet" + "x" "320" + "y" "128" + "width" "64" + "height" "64" + } + "icon_alert" + { + "file" "vgui/hud/iconsheet" + "x" "384" + "y" "128" + "width" "64" + "height" "64" + } + "icon_key_generic" + { + "file" "vgui/hud/iconsheet" + "x" "448" + "y" "128" + "width" "64" + "height" "64" + } + +// Row 4 + "icon_key_wide" + { + "file" "vgui/hud/iconsheet" + "x" "0" + "y" "192" + "width" "128" + "height" "64" + } + "icon_info" + { + "file" "vgui/hud/iconsheet" + "x" "128" + "y" "192" + "width" "64" + "height" "64" + } + "icon_reviving" + { + "file" "vgui/hud/iconsheet" + "x" "192" + "y" "192" + "width" "64" + "height" "64" + } + "icon_mouseThree" + { + "file" "vgui/hud/iconsheet" + "x" "256" + "y" "192" + "width" "64" + "height" "64" + } + "icon_skull" + { + "file" "vgui/hud/iconsheet" + "x" "320" + "y" "192" + "width" "64" + "height" "64" + } + "icon_arrow_plain_white_dn" + { + "file" "vgui/hud/iconsheet" + "x" "384" + "y" "192" + "width" "64" + "height" "64" + } + "icon_arrow_plain_white_up" + { + "file" "vgui/hud/iconsheet" + "x" "448" + "y" "192" + "width" "64" + "height" "64" + } +// Row 5 + "icon_painpills" + { + "file" "vgui/hud/iconsheet" + "x" "0" + "y" "256" + "width" "64" + "height" "64" + } + "icon_healing" + { + "file" "vgui/hud/iconsheet" + "x" "128" + "y" "320" + "width" "64" + "height" "64" + } + "icon_pipebomb" + { + "file" "vgui/hud/iconsheet" + "x" "128" + "y" "256" + "width" "64" + "height" "64" + } + "icon_molotov" + { + "file" "vgui/hud/iconsheet" + "x" "192" + "y" "256" + "width" "64" + "height" "64" + } + "icon_incendiary_ammo" + { + "file" "vgui/hud/iconsheet" + "x" "256" + "y" "448" + "width" "64" + "height" "64" + } + "icon_explosive_ammo" + { + "file" "vgui/hud/iconsheet2" + "x" "448" + "y" "128" + "width" "64" + "height" "64" + } + "icon_laser_sight" + { + "file" "vgui/hud/iconsheet2" + "x" "448" + "y" "64" + "width" "64" + "height" "64" + } + "icon_pistol" + { + "file" "vgui/hud/iconsheet" + "x" "320" + "y" "256" + "width" "64" + "height" "64" + } + "icon_ammo" + { + "file" "vgui/hud/iconsheet" + "x" "256" + "y" "256" + "width" "64" + "height" "64" + } + "icon_adrenaline" + { + "file" "vgui/hud/iconsheet2" + "x" "384" + "y" "0" + "width" "64" + "height" "64" + } + "icon_ammopack" + { + "file" "vgui/hud/iconsheet2" + "x" "448" + "y" "64" + "width" "64" + "height" "64" + } + "icon_defibrillator" + { + "file" "vgui/hud/iconsheet2" + "x" "192" + "y" "256" + "width" "64" + "height" "64" + } + "icon_gas_can" + { + "file" "vgui/hud/iconsheet2" + "x" "448" + "y" "256" + "width" "64" + "height" "64" + } + "icon_equip_flashlight" + { + "file" "vgui/hud/iconsheet2" + "x" "64" + "y" "128" + "width" "64" + "height" "64" + } + "icon_equip_flashlight_active" + { + "file" "vgui/hud/iconsheet" + "x" "384" + "y" "256" + "width" "64" + "height" "64" + } + "icon_equip_pipebomb" + { + "file" "vgui/hud/iconsheet" + "x" "448" + "y" "256" + "width" "64" + "height" "64" + } +// Row 6 + "icon_equip_molotov" + { + "file" "vgui/hud/iconsheet" + "x" "0" + "y" "320" + "width" "64" + "height" "64" + } + "icon_equip_pills" + { + "file" "vgui/hud/iconsheet" + "x" "64" + "y" "320" + "width" "64" + "height" "64" + } + "icon_equip_medkit" + { + "file" "vgui/hud/iconsheet" + "x" "128" + "y" "320" + "width" "64" + "height" "64" + } + "icon_equip_dualpistols" + { + "file" "vgui/hud/iconsheet" + "x" "192" + "y" "320" + "width" "64" + "height" "64" + } + "icon_equip_pistol" + { + "file" "vgui/hud/iconsheet" + "x" "256" + "y" "320" + "width" "64" + "height" "64" + } + "icon_equip_pumpshotgun" + { + "file" "vgui/hud/iconsheet" + "x" "320" + "y" "320" + "width" "192" + "height" "64" + } + "icon_equip_adrenaline" + { + "file" "vgui/hud/iconsheet2" + "x" "384" + "y" "0" + "width" "64" + "height" "64" + } + "icon_equip_ammopack" + { + "file" "vgui/hud/iconsheet2" + "x" "128" + "y" "128" + "width" "64" + "height" "64" + } +// Row 7 + "icon_equip_uzi" + { + "file" "vgui/hud/iconsheet" + "x" "0" + "y" "384" + "width" "128" + "height" "64" + } + "icon_equip_machinegun" + { + "file" "vgui/hud/iconsheet" + "x" "128" + "y" "384" + "width" "192" + "height" "64" + } + "icon_equip_rifle" + { + "file" "vgui/hud/iconsheet" + "x" "320" + "y" "384" + "width" "192" + "height" "64" + } +// Row 8 + "icon_equip_autoshotgun" + { + "file" "vgui/hud/iconsheet" + "x" "0" + "y" "448" + "width" "192" + "height" "64" + } + "zombie_team_common" + { + "file" "vgui/hud/iconsheet" + "x" "256" + "y" "448" + "width" "64" + "height" "64" + } + "icon_dpad" + { + "file" "vgui/hud/iconsheet" + "x" "192" + "y" "448" + "width" "64" + "height" "64" + } + "icon_upgrade" + { + "file" "vgui/hud/iconsheet" + "x" "320" + "y" "448" + "width" "64" + "height" "64" + } + "icon_bullet" + { + "file" "vgui/hud/iconsheet" + "x" "384" + "y" "448" + "width" "64" + "height" "64" + } + "icon_equip_molotov_small" + { + "file" "vgui/hud/iconsheet" + "x" "448" + "y" "448" + "width" "32" + "height" "32" + } + "icon_equip_ammopack_small" + { + "file" "vgui/hud/iconsheet" + "x" "480" + "y" "448" + "width" "32" + "height" "32" + } + "icon_equip_adrenaline_small" + { + "file" "vgui/hud/iconsheet" + "x" "448" + "y" "480" + "width" "32" + "height" "32" + } + +// Iconsheet 2 + "icon_bronze_medal" + { + "file" "vgui/hud/iconsheet2" + "x" "0" + "y" "0" + "width" "64" + "height" "64" + } + "icon_silver_medal" + { + "file" "vgui/hud/iconsheet2" + "x" "64" + "y" "0" + "width" "64" + "height" "64" + } + "icon_gold_medal" + { + "file" "vgui/hud/iconsheet2" + "x" "128" + "y" "0" + "width" "64" + "height" "64" + } + "icon_player_record" + { + "file" "vgui/hud/iconsheet2" + "x" "192" + "y" "0" + "width" "64" + "height" "64" + } + "icon_world_record" + { + "file" "vgui/hud/iconsheet2" + "x" "0" + "y" "64" + "width" "64" + "height" "64" + } + "icon_bronze_medal_small" + { + "file" "vgui/hud/iconsheet2" + "x" "64" + "y" "64" + "width" "32" + "height" "32" + } + "icon_silver_medal_small" + { + "file" "vgui/hud/iconsheet2" + "x" "96" + "y" "64" + "width" "32" + "height" "32" + } + "icon_gold_medal_small" + { + "file" "vgui/hud/iconsheet2" + "x" "128" + "y" "64" + "width" "32" + "height" "32" + } + "icon_player_record_small" + { + "file" "vgui/hud/iconsheet2" + "x" "192" + "y" "64" + "width" "32" + "height" "32" + } + "icon_team_record_small" + { + "file" "vgui/hud/iconsheet2" + "x" "192" + "y" "64" + "width" "32" + "height" "32" + } + "icon_world_record_small" + { + "file" "vgui/hud/iconsheet2" + "x" "192" + "y" "64" + "width" "32" + "height" "32" + } + "icon_360_controller_1" + { + "file" "vgui/hud/iconsheet2" + "x" "64" + "y" "96" + "width" "32" + "height" "32" + } + "icon_360_controller_2" + { + "file" "vgui/hud/iconsheet2" + "x" "96" + "y" "96" + "width" "32" + "height" "32" + } + "icon_360_controller_3" + { + "file" "vgui/hud/iconsheet2" + "x" "128" + "y" "96" + "width" "32" + "height" "32" + } + "icon_360_controller_4" + { + "file" "vgui/hud/iconsheet2" + "x" "160" + "y" "96" + "width" "32" + "height" "32" + } + "icon_equip_pipebomb_small" + { + "file" "vgui/hud/iconsheet2" + "x" "224" + "y" "64" + "width" "32" + "height" "32" + } + "icon_equip_medkit_small" + { + "file" "vgui/hud/iconsheet2" + "x" "192" + "y" "96" + "width" "32" + "height" "32" + } + "icon_equip_pills_small" + { + "file" "vgui/hud/iconsheet2" + "x" "224" + "y" "96" + "width" "32" + "height" "32" + } + "icon_shotgun_shell" + { + "file" "vgui/hud/iconsheet2" + "x" "0" + "y" "128" + "width" "64" + "height" "64" + } + "icon_grenade" + { + "file" "vgui/hud/iconsheet2" + "x" "384" + "y" "128" + "width" "64" + "height" "64" + } + // "icon_equip_desertrifle" + // { + // "file" "vgui/hud/iconsheet2" + // "x" "128" // X 坐标错误,应为 192 + // "y" "128" + // "width" "128" // 宽度错误,应为 192 + // "height" "64" + // } + "icon_equip_militarysniper" + { + "file" "vgui/hud/iconsheet2" + "x" "0" + "y" "192" + "width" "192" + "height" "64" + } + "icon_equip_silencedsmg" + { + "file" "vgui/hud/iconsheet2" + "x" "256" + "y" "0" + "width" "128" + "height" "64" + } + "icon_equip_spasshotgun" + { + "file" "vgui/hud/iconsheet2" + "x" "256" + "y" "64" + "width" "128" + "height" "64" + } + "icon_equip_chromeshotgun" + { + "file" "vgui/hud/iconsheet2" + "x" "192" + "y" "192" + "width" "192" + "height" "64" + } + "icon_equip_grenadelauncher" + { + "file" "vgui/hud/iconsheet2" + "x" "0" + "y" "256" + "width" "191" + "height" "64" + } + "icon_fireaxe" + { + "file" "vgui/hud/iconsheet2" + "x" "0" + "y" "320" + "width" "128" + "height" "64" + } + "icon_baseball_bat" + { + "file" "vgui/hud/iconsheet2" + "x" "128" + "y" "320" + "width" "128" + "height" "64" + } + "icon_frying_pan" + { + "file" "vgui/hud/iconsheet2" + "x" "256" + "y" "320" + "width" "128" + "height" "64" + } + "icon_machete" + { + "file" "vgui/hud/iconsheet2" + "x" "384" + "y" "320" + "width" "128" + "height" "64" + } + "icon_cricket_bat" + { + "file" "vgui/hud/iconsheet2" + "x" "0" + "y" "384" + "width" "128" + "height" "64" + } + "icon_tonfa" + { + "file" "vgui/hud/iconsheet2" + "x" "0" + "y" "448" + "width" "128" + "height" "64" + } + "icon_katana" + { + "file" "vgui/hud/iconsheet2" + "x" "128" + "y" "448" + "width" "128" + "height" "64" + } + "icon_crowbar" + { + "file" "vgui/hud/iconsheet2" + "x" "256" + "y" "448" + "width" "128" + "height" "64" + } + "icon_chainsaw" + { + "file" "vgui/hud/iconsheet2" + "x" "384" + "y" "384" + "width" "128" + "height" "64" + } + "icon_guitar" + { + "file" "vgui/hud/iconsheet2" + "x" "256" + "y" "256" + "width" "128" + "height" "64" + } + "icon_deagle" + { + "file" "vgui/hud/iconsheet" + "x" "320" + "y" "256" + "width" "64" + "height" "64" + } + "icon_cola_bottles" + { + "file" "vgui/hud/iconsheet2" + "x" "384" + "y" "64" + "width" "64" + "height" "64" + } + "icon_knife" + { + "file" "vgui/hud/iconsheet3" + "x" "392" + "y" "0" + "width" "128" + "height" "64" + } + + "itempickup_background" + { + "file" "vgui/hud/iconsheet2" + "x" "448" + "y" "0" + "width" "64" + "height" "64" + } + + "rounded_background" + { + "file" "vgui/hud/ScalablePanel_bgBlack_outlineGrey_sm" + "x" "0" + "y" "192" + "width" "32" + "height" "32" + } + + "rounded_background_noborder" + { + "file" "vgui/hud/ScalablePanel_bgMidGrey" + "x" "0" + "y" "0" + "width" "128" + "height" "128" + } + + "rounded_background_glow" + { + "file" "vgui/hud/ScalablePanel_bgMidGrey_glow" + "x" "0" + "y" "0" + "width" "128" + "height" "128" + } + + "rounded_background_noborder_green" + { + "file" "vgui/hud/ScalablePanel_bgMidGrey_outlineGrey" + "x" "0" + "y" "0" + "width" "128" + "height" "128" + } + + "rounded_background_glow_green" + { + "file" "vgui/hud/ScalablePanel_bgMidGrey_outlineGreen_glow" + "x" "0" + "y" "0" + "width" "128" + "height" "128" + } + + "zombie_team_hunter" + { + "file" "vgui/hud/ZombieTeamImages" + "x" "0" + "y" "0" + "width" "64" + "height" "64" + } + "zombie_team_smoker" + { + "file" "vgui/hud/ZombieTeamImages" + "x" "64" + "y" "0" + "width" "64" + "height" "64" + } + "zombie_team_boomer" + { + "file" "vgui/hud/ZombieTeamImages" + "x" "0" + "y" "64" + "width" "64" + "height" "64" + } + "zombie_team_tank" + { + "file" "vgui/hud/ZombieTeamImages" + "x" "64" + "y" "64" + "width" "64" + "height" "64" + } + "zombie_team_jockey" + { + "file" "vgui/hud/ZombieTeamImages" + "x" "0" + "y" "128" + "width" "64" + "height" "64" + } + "zombie_team_spitter" + { + "file" "vgui/hud/ZombieTeamImages" + "x" "64" + "y" "128" + "width" "64" + "height" "64" + } + "zombie_team_hunter_ghost" + { + "file" "vgui/hud/GhostZombieTeamImages" + "x" "0" + "y" "0" + "width" "64" + "height" "64" + } + "zombie_team_smoker_ghost" + { + "file" "vgui/hud/GhostZombieTeamImages" + "x" "64" + "y" "0" + "width" "64" + "height" "64" + } + "zombie_team_boomer_ghost" + { + "file" "vgui/hud/GhostZombieTeamImages" + "x" "0" + "y" "64" + "width" "64" + "height" "64" + } + "zombie_team_tank_ghost" + { + "file" "vgui/hud/GhostZombieTeamImages" + "x" "64" + "y" "64" + "width" "64" + "height" "64" + } + + // LinGe + "icon_equip_desertrifle" // SCAR + { + "file" "vgui/hud/iconsheet2" + "x" "192" + "y" "128" + "width" "192" + "height" "64" + } + "linge_upgradepack_explosive" // 高爆弹药包 + { + "file" "vgui/hud/iconsheet2" + "x" "384" + "y" "448" + "width" "64" + "height" "64" + } + "linge_upgradepack_incendiary" // 燃烧弹药包 + { + "file" "vgui/hud/iconsheet2" + "x" "448" + "y" "448" + "width" "64" + "height" "64" + } + "linge_equip_vomitjar" // 胆汁 + { + "file" "vgui/hud/iconsheet2" + "x" "384" + "y" "192" + "width" "64" + "height" "64" + } + "linge_rifle_ak47" // AK47 + { + "file" "vgui/hud/iconsheet2" + "x" "128" + "y" "384" + "width" "192" + "height" "64" + } + "linge_smg_mp5" // MP5 + { + "file" "vgui/hud/iconsheet3" + "x" "256" + "y" "0" + "width" "128" + "height" "64" + } + "linge_rifle_sg552" // SG552 + { + "file" "vgui/hud/iconsheet3" + "x" "0" + "y" "0" + "width" "192" + "height" "64" + } + + "linge_sniper_scout" // Scout + { + "file" "vgui/hud/iconsheet3" + "x" "0" + "y" "64" + "width" "192" + "height" "64" + } + "linge_sniper_awp" // AWP + { + "file" "vgui/hud/iconsheet3" + "x" "192" + "y" "64" + "width" "192" + "height" "64" + } + } +} diff --git a/vscripts/mod_textures.txt.bak b/vscripts/mod_textures.txt.bak new file mode 100644 index 0000000..740000d --- /dev/null +++ b/vscripts/mod_textures.txt.bak @@ -0,0 +1,1816 @@ +"sprites/640_hud" +{ + TextureData + { + "whiteAdditive" + { + "file" "vgui/white_additive" + "x" "0" + "y" "0" + "width" "1" + "height" "1" + } + + "d_headshot" + { + "file" "sprites/headshot_icon" + "x" "0" + "y" "0" + "width" "64" + "height" "64" + } + + "d_skull_cs" + { + "file" "sprites/death_icon" + "x" "0" + "y" "0" + "width" "64" + "height" "64" + } + + "pain_up" + { + "file" "sprites/640_pain_up" + "x" "0" + "y" "0" + "width" "128" + "height" "48" + } + + "pain_down" + { + "file" "sprites/640_pain_down" + "x" "0" + "y" "0" + "width" "128" + "height" "48" + } + + "pain_left" + { + "file" "sprites/640_pain_left" + "x" "0" + "y" "0" + "width" "48" + "height" "128" + } + + "pain_right" + { + "file" "sprites/640_pain_right" + "x" "0" + "y" "0" + "width" "48" + "height" "128" + } + + // Side voice bars + "voice_player" + { + "font" "L4D_Icons" + "character" "V" + } + + // Local player HUD element + "voice_self" + { + "font" "L4D_Icons_large" + "character" "V" + } + + // Teammates above their HUD elements + "voice_teammate" + { + "font" "L4D_Icons_medium" + "character" "V" + } + + "number_0" + { + "font" "Icons" + "character" "0" + } + + "number_1" + { + "font" "Icons" + "character" "1" + } + + "number_2" + { + "font" "Icons" + "character" "2" + } + + "number_3" + { + "font" "Icons" + "character" "3" + } + + "number_4" + { + "font" "Icons" + "character" "4" + } + + "number_5" + { + "font" "Icons" + "character" "5" + } + + "number_6" + { + "font" "Icons" + "character" "6" + } + + "number_7" + { + "font" "Icons" + "character" "7" + } + + "number_8" + { + "font" "Icons" + "character" "8" + } + + "number_9" + { + "font" "Icons" + "character" "9" + } + + + // TERROR: ---------------------------------------------------- + "SkullIcon" + { + "file" "sprites/skull_icon" + "x" "0" + "y" "0" + "width" "32" + "height" "32" + } + "s_panel_healing_mini_prog" + { + "file" "vgui/s_panel_healing_mini_prog" + "x" "0" + "y" "0" + "width" "0" + "height" "0" + } + "rating_0_stars" + { + "file" "vgui/ratingstars" + "x" "0" + "y" "105" + "width" "128" + "height" "21" + } + "rating_1_stars" + { + "file" "vgui/ratingstars" + "x" "0" + "y" "84" + "width" "128" + "height" "21" + } + "rating_2_stars" + { + "file" "vgui/ratingstars" + "x" "0" + "y" "63" + "width" "128" + "height" "21" + } + "rating_3_stars" + { + "file" "vgui/ratingstars" + "x" "0" + "y" "42" + "width" "128" + "height" "21" + } + "rating_4_stars" + { + "file" "vgui/ratingstars" + "x" "0" + "y" "21" + "width" "128" + "height" "21" + } + "rating_5_stars" + { + "file" "vgui/ratingstars" + "x" "0" + "y" "0" + "width" "128" + "height" "21" + } + "Boomer" + { + "file" "vgui/tipgraphic" + "x" "512" + "y" "64" + "width" "64" + "height" "64" + } + "Tank" + { + "file" "vgui/tipgraphic" + "x" "448" + "y" "64" + "width" "64" + "height" "64" + } + "Hunter" + { + "file" "vgui/tipgraphic" + "x" "512" + "y" "0" + "width" "64" + "height" "64" + } + "Smoker" + { + "file" "vgui/tipgraphic" + "x" "448" + "y" "0" + "width" "64" + "height" "64" + } + "InfectedGroup" + { + "file" "vgui/pz_groupshot" + "x" "0" + "y" "0" + "width" "256" + "height" "256" + } + "Biker" + { + "file" "vgui/checkpointheroes" + "x" "0" + "y" "0" + "width" "256" + "height" "256" + } + "TeenGirl" + { + "file" "vgui/checkpointheroes" + "x" "256" + "y" "0" + "width" "256" + "height" "256" + } + "Manager" + { + "file" "vgui/checkpointheroes" + "x" "0" + "y" "256" + "width" "256" + "height" "256" + } + "NamVet" + { + "file" "vgui/checkpointheroes" + "x" "256" + "y" "256" + "width" "256" + "height" "256" + } + + + + "Unknown" // sometimes the transition stats fails to figure out which survivor to show and tries to show "Unknown" + { + "file" "vgui/checkpointheroes" + "x" "256" + "y" "0" + "width" "256" + "height" "256" + } + "teengirl_outline" + { + "file" "vgui/groupoutline" + "x" "0" + "y" "0" + "width" "64" + "height" "128" + } + "namvet_outline" + { + "file" "vgui/groupoutline" + "x" "64" + "y" "0" + "width" "64" + "height" "128" + } + "biker_outline" + { + "file" "vgui/groupoutline" + "x" "128" + "y" "0" + "width" "64" + "height" "128" + } + "manager_outline" + { + "file" "vgui/groupoutline" + "x" "192" + "y" "0" + "width" "64" + "height" "128" + } + "clock_1" + { + "file" "vgui/checkpointclock" + "x" "0" + "y" "0" + "width" "64" + "height" "64" + } + +// L4D Tips + // Row 1 + "tips_spitter" + { + "file" "vgui/tipgraphic" + "x" "192" + "y" "0" + "width" "64" + "height" "64" + } + "tip_tank_incap" + { + "file" "vgui/tipgraphic" + "x" "256" + "y" "0" + "width" "64" + "height" "64" + } + "tip_shove" + { + "file" "vgui/tipgraphic" + "x" "320" + "y" "64" + "width" "64" + "height" "64" + } + "tip_witch" + { + "file" "vgui/tipgraphic" + "x" "384" + "y" "0" + "width" "64" + "height" "64" + } + "tips_smoker" + { + "file" "vgui/tipgraphic" + "x" "448" + "y" "0" + "width" "64" + "height" "64" + } + "tips_hunter" + { + "file" "vgui/tipgraphic" + "x" "512" + "y" "0" + "width" "64" + "height" "64" + } + "tip_heal" + { + "file" "vgui/tipgraphic" + "x" "576" + "y" "64" + "width" "64" + "height" "64" + } + "tip_doorbreak" + { + "file" "vgui/tipgraphic" + "x" "640" + "y" "0" + "width" "64" + "height" "64" + } + "tip_items" + { + "file" "vgui/tipgraphic" + "x" "704" + "y" "64" + "width" "64" + "height" "64" + } + "tip_boomer_bile" + { + "file" "vgui/tipgraphic" + "x" "768" + "y" "0" + "width" "64" + "height" "64" + } + "tip_upgradepack" + { + "file" "vgui/tipgraphic" + "x" "832" + "y" "0" + "width" "64" + "height" "64" + } + "tip_lasersight" + { + "file" "vgui/tipgraphic" + "x" "896" + "y" "0" + "width" "64" + "height" "64" + } + "tip_melee" + { + "file" "vgui/tipgraphic" + "x" "960" + "y" "0" + "width" "64" + "height" "64" + } + + // Row 2 + "tips_charger" + { + "file" "vgui/tipgraphic" + "x" "192" + "y" "64" + "width" "64" + "height" "64" + } + "tips_jockey" + { + "file" "vgui/tipgraphic" + "x" "256" + "y" "64" + "width" "64" + "height" "64" + } + "tip_crouch" + { + "file" "vgui/tipgraphic" + "x" "320" + "y" "64" + "width" "64" + "height" "64" + } + "tip_revive" + { + "file" "vgui/tipgraphic" + "x" "384" + "y" "64" + "width" "64" + "height" "64" + } + "tips_tank" + { + "file" "vgui/tipgraphic" + "x" "448" + "y" "64" + "width" "64" + "height" "64" + } + "tips_boomer" + { + "file" "vgui/tipgraphic" + "x" "512" + "y" "64" + "width" "64" + "height" "64" + } + "tip_pipebomb" + { + "file" "vgui/tipgraphic" + "x" "576" + "y" "64" + "width" "64" + "height" "64" + } + "tip_checkpoint" + { + "file" "vgui/tipgraphic" + "x" "640" + "y" "64" + "width" "64" + "height" "64" + } + "tip_shotguns" + { + "file" "vgui/tipgraphic" + "x" "704" + "y" "64" + "width" "64" + "height" "64" + } + "tip_explosive_ammo" + { + "file" "vgui/tipgraphic" + "x" "768" + "y" "64" + "width" "64" + "height" "64" + } + "tip_incendiary_ammo" + { + "file" "vgui/tipgraphic" + "x" "832" + "y" "64" + "width" "64" + "height" "64" + } + "tip_syringe" + { + "file" "vgui/tipgraphic" + "x" "896" + "y" "64" + "width" "64" + "height" "64" + } + "tip_grenadelauncher" + { + "file" "vgui/tipgraphic" + "x" "960" + "y" "64" + "width" "64" + "height" "64" + } + + //Row 3 + "tip_boomer_vomit" + { + "file" "vgui/tipgraphic" + "x" "0" + "y" "128" + "width" "128" + "height" "128" + } + "tip_boomer_swipe" + { + "file" "vgui/tipgraphic" + "x" "128" + "y" "128" + "width" "128" + "height" "128" + } + "tip_hunter_pounce" + { + "file" "vgui/tipgraphic" + "x" "256" + "y" "128" + "width" "128" + "height" "128" + } + "tip_hunter_swipe" + { + "file" "vgui/tipgraphic" + "x" "384" + "y" "128" + "width" "128" + "height" "128" + } + "tip_smoker_pull" + { + "file" "vgui/tipgraphic" + "x" "512" + "y" "128" + "width" "128" + "height" "128" + } + "tip_smoker_swipe" + { + "file" "vgui/tipgraphic" + "x" "640" + "y" "128" + "width" "128" + "height" "128" + } + "tip_jockey_pounce" + { + "file" "vgui/tipgraphic" + "x" "768" + "y" "128" + "width" "128" + "height" "128" + } + "tip_jockey_swipe" + { + "file" "vgui/tipgraphic" + "x" "896" + "y" "128" + "width" "128" + "height" "128" + } + // Row 4 + "tip_charger_charge" + { + "file" "vgui/tipgraphic" + "x" "0" + "y" "256" + "width" "128" + "height" "128" + } + "tip_charger_pummel" + { + "file" "vgui/tipgraphic" + "x" "128" + "y" "256" + "width" "128" + "height" "128" + } + "tip_spitter_spit" + { + "file" "vgui/tipgraphic" + "x" "256" + "y" "256" + "width" "128" + "height" "128" + } + "tip_spitter_claw" + { + "file" "vgui/tipgraphic" + "x" "384" + "y" "256" + "width" "128" + "height" "128" + } + +// Bottom of Tipgraphic file +// Infected Panels (find a place to spawn) + "tip_hunter" + { + "file" "vgui/tipgraphic" + "x" "832" + "y" "448" + "width" "192" + "height" "192" + } + "tip_spitter" + { + "file" "vgui/tipgraphic" + "x" "256" + "y" "640" + "width" "192" + "height" "192" + } + "tip_jockey" + { + "file" "vgui/tipgraphic" + "x" "256" + "y" "832" + "width" "192" + "height" "192" + } + "tip_charger" + { + "file" "vgui/tipgraphic" + "x" "256" + "y" "448" + "width" "192" + "height" "192" + } + "tip_boomer" + { + "file" "vgui/tipgraphic" + "x" "64" + "y" "832" + "width" "192" + "height" "192" + } + "tip_tank" + { + "file" "vgui/tipgraphic" + "x" "64" + "y" "640" + "width" "192" + "height" "192" + } + "tip_smoker" + { + "file" "vgui/tipgraphic" + "x" "832" + "y" "832" + "width" "192" + "height" "192" + } +// StatScreenIcons + "Stat_Most_Tank_Dmg" + { + "file" "vgui/tipgraphic" + "x" "64" + "y" "640" + "width" "192" + "height" "192" + } + "Stat_Most_Special_Kills" + { + "file" "vgui/tipgraphic" + "x" "448" + "y" "448" + "width" "192" + "height" "192" + } + "Stat_Most_Melee_Kills" + { + "file" "vgui/tipgraphic" + "x" "448" + "y" "832" + "width" "192" + "height" "192" + } + "Stat_Most_Infected_Kills" + { + "file" "vgui/tipgraphic" + "x" "640" + "y" "640" + "width" "192" + "height" "192" + } + "Stat_Most_Headshots" + { + "file" "vgui/tipgraphic" + "x" "448" + "y" "640" + "width" "192" + "height" "192" + } + "Stat_Most_Witch_Dmg" + { + "file" "vgui/tipgraphic" + "x" "832" + "y" "640" + "width" "192" + "height" "192" + } +// Row 8 +// StatScreenIcons + "Stat_vs_Most_Damage_Done" + { + "file" "vgui/tipgraphic" + "x" "640" + "y" "448" + "width" "192" + "height" "192" + } + "Stat_vs_Most_Hunter_Pounces" + { + "file" "vgui/tipgraphic" + "x" "832" + "y" "448" + "width" "192" + "height" "192" + } + "Stat_vs_Most_Smoker_Pulls" + { + "file" "vgui/tipgraphic" + "x" "832" + "y" "832" + "width" "192" + "height" "192" + } + "Stat_vs_Most_Vomit_Hit" + { + "file" "vgui/tipgraphic" + "x" "64" + "y" "832" + "width" "192" + "height" "192" + } + "Stat_vs_Most_Spit_Dmg" + { + "file" "vgui/tipgraphic" + "x" "256" + "y" "640" + "width" "192" + "height" "192" + } + "Stat_vs_Most_Jockey_Rides" + { + "file" "vgui/tipgraphic" + "x" "256" + "y" "832" + "width" "192" + "height" "192" + } + "Stat_vs_Most_Damage_As_Tank" + { + "file" "vgui/tipgraphic" + "x" "64" + "y" "640" + "width" "192" + "height" "192" + } + "Stat_vs_Most_Damage_As_Charger" + { + "file" "vgui/tipgraphic" + "x" "256" + "y" "448" + "width" "192" + "height" "192" + } + +// Iconsheet +// Row 1 + "icon_shield" + { + "file" "vgui/hud/iconsheet" + "x" "0" + "y" "0" + "width" "64" + "height" "64" + } + "icon_run" + { + "file" "vgui/hud/iconsheet" + "x" "64" + "y" "0" + "width" "64" + "height" "64" + } + "icon_noMedKit" + { + "file" "vgui/hud/iconsheet" + "x" "128" + "y" "0" + "width" "64" + "height" "64" + } + "icon_no" + { + "file" "vgui/hud/iconsheet" + "x" "192" + "y" "0" + "width" "64" + "height" "64" + } + "icon_mouseRight" + { + "file" "vgui/hud/iconsheet" + "x" "256" + "y" "0" + "width" "64" + "height" "64" + } + "icon_mouseLeft" + { + "file" "vgui/hud/iconsheet" + "x" "320" + "y" "0" + "width" "64" + "height" "64" + } + "icon_medkit" + { + "file" "vgui/hud/iconsheet" + "x" "384" + "y" "0" + "width" "64" + "height" "64" + } + "icon_interact" + { + "file" "vgui/hud/iconsheet" + "x" "448" + "y" "0" + "width" "64" + "height" "64" + } + +// Row 2 + "icon_door" + { + "file" "vgui/hud/iconsheet" + "x" "0" + "y" "64" + "width" "64" + "height" "64" + } + "icon_button" + { + "file" "vgui/hud/iconsheet" + "x" "64" + "y" "64" + "width" "64" + "height" "64" + } + "icon_blank" + { + "file" "vgui/hud/iconsheet" + "x" "128" + "y" "64" + "width" "64" + "height" "64" + } + "icon_arrow_up" + { + "file" "vgui/hud/iconsheet" + "x" "192" + "y" "64" + "width" "64" + "height" "64" + } + "icon_arrow_right" + { + "file" "vgui/hud/iconsheet" + "x" "256" + "y" "64" + "width" "64" + "height" "64" + } + "icon_arrow_plain" + { + "file" "vgui/hud/iconsheet" + "x" "320" + "y" "64" + "width" "64" + "height" "64" + } + "icon_alert_red" + { + "file" "vgui/hud/iconsheet" + "x" "384" + "y" "64" + "width" "64" + "height" "64" + } + "icon_tip" + { + "file" "vgui/hud/iconsheet" + "x" "448" + "y" "64" + "width" "64" + "height" "64" + } + +// Row 3 + "icon_key_right" + { + "file" "vgui/hud/iconsheet" + "x" "0" + "y" "128" + "width" "64" + "height" "64" + } + "icon_key_left" + { + "file" "vgui/hud/iconsheet" + "x" "64" + "y" "128" + "width" "64" + "height" "64" + } + "icon_key_down" + { + "file" "vgui/hud/iconsheet" + "x" "128" + "y" "128" + "width" "64" + "height" "64" + } + "icon_key_up" + { + "file" "vgui/hud/iconsheet" + "x" "192" + "y" "128" + "width" "64" + "height" "64" + } + "icon_mouseWheel_up" + { + "file" "vgui/hud/iconsheet" + "x" "256" + "y" "128" + "width" "64" + "height" "64" + } + "icon_mouseWheel_down" + { + "file" "vgui/hud/iconsheet" + "x" "320" + "y" "128" + "width" "64" + "height" "64" + } + "icon_alert" + { + "file" "vgui/hud/iconsheet" + "x" "384" + "y" "128" + "width" "64" + "height" "64" + } + "icon_key_generic" + { + "file" "vgui/hud/iconsheet" + "x" "448" + "y" "128" + "width" "64" + "height" "64" + } + +// Row 4 + "icon_key_wide" + { + "file" "vgui/hud/iconsheet" + "x" "0" + "y" "192" + "width" "128" + "height" "64" + } + "icon_info" + { + "file" "vgui/hud/iconsheet" + "x" "128" + "y" "192" + "width" "64" + "height" "64" + } + "icon_reviving" + { + "file" "vgui/hud/iconsheet" + "x" "192" + "y" "192" + "width" "64" + "height" "64" + } + "icon_mouseThree" + { + "file" "vgui/hud/iconsheet" + "x" "256" + "y" "192" + "width" "64" + "height" "64" + } + "icon_skull" + { + "file" "vgui/hud/iconsheet" + "x" "320" + "y" "192" + "width" "64" + "height" "64" + } + "icon_arrow_plain_white_dn" + { + "file" "vgui/hud/iconsheet" + "x" "384" + "y" "192" + "width" "64" + "height" "64" + } + "icon_arrow_plain_white_up" + { + "file" "vgui/hud/iconsheet" + "x" "448" + "y" "192" + "width" "64" + "height" "64" + } +// Row 5 + "icon_painpills" + { + "file" "vgui/hud/iconsheet" + "x" "0" + "y" "256" + "width" "64" + "height" "64" + } + "icon_healing" + { + "file" "vgui/hud/iconsheet" + "x" "128" + "y" "320" + "width" "64" + "height" "64" + } + "icon_pipebomb" + { + "file" "vgui/hud/iconsheet" + "x" "128" + "y" "256" + "width" "64" + "height" "64" + } + "icon_molotov" + { + "file" "vgui/hud/iconsheet" + "x" "192" + "y" "256" + "width" "64" + "height" "64" + } + "icon_incendiary_ammo" + { + "file" "vgui/hud/iconsheet" + "x" "256" + "y" "448" + "width" "64" + "height" "64" + } + "icon_explosive_ammo" + { + "file" "vgui/hud/iconsheet2" + "x" "448" + "y" "128" + "width" "64" + "height" "64" + } + "icon_laser_sight" + { + "file" "vgui/hud/iconsheet2" + "x" "448" + "y" "64" + "width" "64" + "height" "64" + } + "icon_pistol" + { + "file" "vgui/hud/iconsheet" + "x" "320" + "y" "256" + "width" "64" + "height" "64" + } + "icon_ammo" + { + "file" "vgui/hud/iconsheet" + "x" "256" + "y" "256" + "width" "64" + "height" "64" + } + "icon_adrenaline" + { + "file" "vgui/hud/iconsheet2" + "x" "384" + "y" "0" + "width" "64" + "height" "64" + } + "icon_ammopack" + { + "file" "vgui/hud/iconsheet2" + "x" "448" + "y" "64" + "width" "64" + "height" "64" + } + "icon_defibrillator" + { + "file" "vgui/hud/iconsheet2" + "x" "192" + "y" "256" + "width" "64" + "height" "64" + } + "icon_gas_can" + { + "file" "vgui/hud/iconsheet2" + "x" "448" + "y" "256" + "width" "64" + "height" "64" + } + "icon_equip_flashlight" + { + "file" "vgui/hud/iconsheet2" + "x" "64" + "y" "128" + "width" "64" + "height" "64" + } + "icon_equip_flashlight_active" + { + "file" "vgui/hud/iconsheet" + "x" "384" + "y" "256" + "width" "64" + "height" "64" + } + "icon_equip_pipebomb" + { + "file" "vgui/hud/iconsheet" + "x" "448" + "y" "256" + "width" "64" + "height" "64" + } +// Row 6 + "icon_equip_molotov" + { + "file" "vgui/hud/iconsheet" + "x" "0" + "y" "320" + "width" "64" + "height" "64" + } + "icon_equip_pills" + { + "file" "vgui/hud/iconsheet" + "x" "64" + "y" "320" + "width" "64" + "height" "64" + } + "icon_equip_medkit" + { + "file" "vgui/hud/iconsheet" + "x" "128" + "y" "320" + "width" "64" + "height" "64" + } + "icon_equip_dualpistols" + { + "file" "vgui/hud/iconsheet" + "x" "192" + "y" "320" + "width" "64" + "height" "64" + } + "icon_equip_pistol" + { + "file" "vgui/hud/iconsheet" + "x" "256" + "y" "320" + "width" "64" + "height" "64" + } + "icon_equip_pumpshotgun" + { + "file" "vgui/hud/iconsheet" + "x" "320" + "y" "320" + "width" "192" + "height" "64" + } + "icon_equip_adrenaline" + { + "file" "vgui/hud/iconsheet2" + "x" "384" + "y" "0" + "width" "64" + "height" "64" + } + "icon_equip_ammopack" + { + "file" "vgui/hud/iconsheet2" + "x" "128" + "y" "128" + "width" "64" + "height" "64" + } +// Row 7 + "icon_equip_uzi" + { + "file" "vgui/hud/iconsheet" + "x" "0" + "y" "384" + "width" "128" + "height" "64" + } + "icon_equip_machinegun" + { + "file" "vgui/hud/iconsheet" + "x" "128" + "y" "384" + "width" "192" + "height" "64" + } + "icon_equip_rifle" + { + "file" "vgui/hud/iconsheet" + "x" "320" + "y" "384" + "width" "192" + "height" "64" + } +// Row 8 + "icon_equip_autoshotgun" + { + "file" "vgui/hud/iconsheet" + "x" "0" + "y" "448" + "width" "192" + "height" "64" + } + "zombie_team_common" + { + "file" "vgui/hud/iconsheet" + "x" "256" + "y" "448" + "width" "64" + "height" "64" + } + "icon_dpad" + { + "file" "vgui/hud/iconsheet" + "x" "192" + "y" "448" + "width" "64" + "height" "64" + } + "icon_upgrade" + { + "file" "vgui/hud/iconsheet" + "x" "320" + "y" "448" + "width" "64" + "height" "64" + } + "icon_bullet" + { + "file" "vgui/hud/iconsheet" + "x" "384" + "y" "448" + "width" "64" + "height" "64" + } + "icon_equip_molotov_small" + { + "file" "vgui/hud/iconsheet" + "x" "448" + "y" "448" + "width" "32" + "height" "32" + } + "icon_equip_ammopack_small" + { + "file" "vgui/hud/iconsheet" + "x" "480" + "y" "448" + "width" "32" + "height" "32" + } + "icon_equip_adrenaline_small" + { + "file" "vgui/hud/iconsheet" + "x" "448" + "y" "480" + "width" "32" + "height" "32" + } + +// Iconsheet 2 + "icon_bronze_medal" + { + "file" "vgui/hud/iconsheet2" + "x" "0" + "y" "0" + "width" "64" + "height" "64" + } + "icon_silver_medal" + { + "file" "vgui/hud/iconsheet2" + "x" "64" + "y" "0" + "width" "64" + "height" "64" + } + "icon_gold_medal" + { + "file" "vgui/hud/iconsheet2" + "x" "128" + "y" "0" + "width" "64" + "height" "64" + } + "icon_player_record" + { + "file" "vgui/hud/iconsheet2" + "x" "192" + "y" "0" + "width" "64" + "height" "64" + } + "icon_world_record" + { + "file" "vgui/hud/iconsheet2" + "x" "0" + "y" "64" + "width" "64" + "height" "64" + } + "icon_bronze_medal_small" + { + "file" "vgui/hud/iconsheet2" + "x" "64" + "y" "64" + "width" "32" + "height" "32" + } + "icon_silver_medal_small" + { + "file" "vgui/hud/iconsheet2" + "x" "96" + "y" "64" + "width" "32" + "height" "32" + } + "icon_gold_medal_small" + { + "file" "vgui/hud/iconsheet2" + "x" "128" + "y" "64" + "width" "32" + "height" "32" + } + "icon_player_record_small" + { + "file" "vgui/hud/iconsheet2" + "x" "192" + "y" "64" + "width" "32" + "height" "32" + } + "icon_team_record_small" + { + "file" "vgui/hud/iconsheet2" + "x" "192" + "y" "64" + "width" "32" + "height" "32" + } + "icon_world_record_small" + { + "file" "vgui/hud/iconsheet2" + "x" "192" + "y" "64" + "width" "32" + "height" "32" + } + "icon_360_controller_1" + { + "file" "vgui/hud/iconsheet2" + "x" "64" + "y" "96" + "width" "32" + "height" "32" + } + "icon_360_controller_2" + { + "file" "vgui/hud/iconsheet2" + "x" "96" + "y" "96" + "width" "32" + "height" "32" + } + "icon_360_controller_3" + { + "file" "vgui/hud/iconsheet2" + "x" "128" + "y" "96" + "width" "32" + "height" "32" + } + "icon_360_controller_4" + { + "file" "vgui/hud/iconsheet2" + "x" "160" + "y" "96" + "width" "32" + "height" "32" + } + "icon_equip_pipebomb_small" + { + "file" "vgui/hud/iconsheet2" + "x" "224" + "y" "64" + "width" "32" + "height" "32" + } + "icon_equip_medkit_small" + { + "file" "vgui/hud/iconsheet2" + "x" "192" + "y" "96" + "width" "32" + "height" "32" + } + "icon_equip_pills_small" + { + "file" "vgui/hud/iconsheet2" + "x" "224" + "y" "96" + "width" "32" + "height" "32" + } + "icon_shotgun_shell" + { + "file" "vgui/hud/iconsheet2" + "x" "0" + "y" "128" + "width" "64" + "height" "64" + } + "icon_grenade" + { + "file" "vgui/hud/iconsheet2" + "x" "384" + "y" "128" + "width" "64" + "height" "64" + } + "icon_equip_desertrifle" + { + "file" "vgui/hud/iconsheet2" + "x" "128" + "y" "128" + "width" "128" + "height" "64" + } + "icon_equip_militarysniper" + { + "file" "vgui/hud/iconsheet2" + "x" "0" + "y" "192" + "width" "192" + "height" "64" + } + "icon_equip_silencedsmg" + { + "file" "vgui/hud/iconsheet2" + "x" "256" + "y" "0" + "width" "128" + "height" "64" + } + "icon_equip_spasshotgun" + { + "file" "vgui/hud/iconsheet2" + "x" "256" + "y" "64" + "width" "128" + "height" "64" + } + "icon_equip_chromeshotgun" + { + "file" "vgui/hud/iconsheet2" + "x" "192" + "y" "192" + "width" "192" + "height" "64" + } + "icon_equip_grenadelauncher" + { + "file" "vgui/hud/iconsheet2" + "x" "0" + "y" "256" + "width" "191" + "height" "64" + } + "icon_fireaxe" + { + "file" "vgui/hud/iconsheet2" + "x" "0" + "y" "320" + "width" "128" + "height" "64" + } + "icon_baseball_bat" + { + "file" "vgui/hud/iconsheet2" + "x" "128" + "y" "320" + "width" "128" + "height" "64" + } + "icon_frying_pan" + { + "file" "vgui/hud/iconsheet2" + "x" "256" + "y" "320" + "width" "128" + "height" "64" + } + "icon_machete" + { + "file" "vgui/hud/iconsheet2" + "x" "384" + "y" "320" + "width" "128" + "height" "64" + } + "icon_cricket_bat" + { + "file" "vgui/hud/iconsheet2" + "x" "0" + "y" "384" + "width" "128" + "height" "64" + } + "icon_tonfa" + { + "file" "vgui/hud/iconsheet2" + "x" "0" + "y" "448" + "width" "128" + "height" "64" + } + "icon_katana" + { + "file" "vgui/hud/iconsheet2" + "x" "128" + "y" "448" + "width" "128" + "height" "64" + } + "icon_crowbar" + { + "file" "vgui/hud/iconsheet2" + "x" "256" + "y" "448" + "width" "128" + "height" "64" + } + "icon_chainsaw" + { + "file" "vgui/hud/iconsheet2" + "x" "384" + "y" "384" + "width" "128" + "height" "64" + } + "icon_guitar" + { + "file" "vgui/hud/iconsheet2" + "x" "256" + "y" "256" + "width" "128" + "height" "64" + } + "icon_deagle" + { + "file" "vgui/hud/iconsheet" + "x" "320" + "y" "256" + "width" "64" + "height" "64" + } + "icon_cola_bottles" + { + "file" "vgui/hud/iconsheet2" + "x" "384" + "y" "64" + "width" "64" + "height" "64" + } + "icon_knife" + { + "file" "vgui/hud/iconsheet3" + "x" "392" + "y" "0" + "width" "128" + "height" "64" + } + + "itempickup_background" + { + "file" "vgui/hud/iconsheet2" + "x" "448" + "y" "0" + "width" "64" + "height" "64" + } + + "rounded_background" + { + "file" "vgui/hud/ScalablePanel_bgBlack_outlineGrey_sm" + "x" "0" + "y" "192" + "width" "32" + "height" "32" + } + + "rounded_background_noborder" + { + "file" "vgui/hud/ScalablePanel_bgMidGrey" + "x" "0" + "y" "0" + "width" "128" + "height" "128" + } + + "rounded_background_glow" + { + "file" "vgui/hud/ScalablePanel_bgMidGrey_glow" + "x" "0" + "y" "0" + "width" "128" + "height" "128" + } + + "rounded_background_noborder_green" + { + "file" "vgui/hud/ScalablePanel_bgMidGrey_outlineGrey" + "x" "0" + "y" "0" + "width" "128" + "height" "128" + } + + "rounded_background_glow_green" + { + "file" "vgui/hud/ScalablePanel_bgMidGrey_outlineGreen_glow" + "x" "0" + "y" "0" + "width" "128" + "height" "128" + } + + "zombie_team_hunter" + { + "file" "vgui/hud/ZombieTeamImages" + "x" "0" + "y" "0" + "width" "64" + "height" "64" + } + "zombie_team_smoker" + { + "file" "vgui/hud/ZombieTeamImages" + "x" "64" + "y" "0" + "width" "64" + "height" "64" + } + "zombie_team_boomer" + { + "file" "vgui/hud/ZombieTeamImages" + "x" "0" + "y" "64" + "width" "64" + "height" "64" + } + "zombie_team_tank" + { + "file" "vgui/hud/ZombieTeamImages" + "x" "64" + "y" "64" + "width" "64" + "height" "64" + } + "zombie_team_jockey" + { + "file" "vgui/hud/ZombieTeamImages" + "x" "0" + "y" "128" + "width" "64" + "height" "64" + } + "zombie_team_spitter" + { + "file" "vgui/hud/ZombieTeamImages" + "x" "64" + "y" "128" + "width" "64" + "height" "64" + } + "zombie_team_hunter_ghost" + { + "file" "vgui/hud/GhostZombieTeamImages" + "x" "0" + "y" "0" + "width" "64" + "height" "64" + } + "zombie_team_smoker_ghost" + { + "file" "vgui/hud/GhostZombieTeamImages" + "x" "64" + "y" "0" + "width" "64" + "height" "64" + } + "zombie_team_boomer_ghost" + { + "file" "vgui/hud/GhostZombieTeamImages" + "x" "0" + "y" "64" + "width" "64" + "height" "64" + } + "zombie_team_tank_ghost" + { + "file" "vgui/hud/GhostZombieTeamImages" + "x" "64" + "y" "64" + "width" "64" + "height" "64" + } + + } +}