From 3c7c16d929904817c2c2999a56f671008483a62c Mon Sep 17 00:00:00 2001 From: x07x08 <88050465+x07x08@users.noreply.github.com> Date: Fri, 13 Jan 2023 17:58:02 +0200 Subject: [PATCH] Update 1.3.0 Separated the main taunts plugin and the unusual taunts feature (#1) Small changes --- addons/sourcemod/plugins/TF2EconTaunts.smx | Bin 15655 -> 6535 bytes .../plugins/TF2EconUnusualTaunts.smx | Bin 0 -> 13284 bytes addons/sourcemod/scripting/TF2EconTaunts.sp | 754 ++---------------- .../scripting/TF2EconUnusualTaunts.sp | 684 ++++++++++++++++ 4 files changed, 735 insertions(+), 703 deletions(-) create mode 100644 addons/sourcemod/plugins/TF2EconUnusualTaunts.smx create mode 100644 addons/sourcemod/scripting/TF2EconUnusualTaunts.sp diff --git a/addons/sourcemod/plugins/TF2EconTaunts.smx b/addons/sourcemod/plugins/TF2EconTaunts.smx index cb401ef4075f79ffbec964253edaae863aadc214..780643ec7c758ccf11605a4c1fb915aff747bbdd 100644 GIT binary patch delta 6397 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 I)QTYe9~1AAB>(^b literal 15655 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 +#include + #define PLUGIN_NAME "[TF2] Econ Taunts" #define PLUGIN_AUTHOR "x07x08" -#define PLUGIN_DESCRIPTION "A simple taunt menu plugin" -#define PLUGIN_VERSION "1.2.0" +#define PLUGIN_DESCRIPTION "A simple taunts menu plugin" +#define PLUGIN_VERSION "1.3.0" #define PLUGIN_URL "https://github.com/x07x08/TF2-Econ-Taunts" #define DEFINDEX_UNDEFINED 65535 -#define EF_BONEMERGE (1 << 0) -#define EF_BRIGHTLIGHT (1 << 1) -#define EF_DIMLIGHT (1 << 2) -#define EF_NOINTERP (1 << 3) -#define EF_NOSHADOW (1 << 4) -#define EF_NODRAW (1 << 5) -#define EF_NORECEIVESHADOW (1 << 6) -#define EF_BONEMERGE_FASTCULL (1 << 7) -#define EF_ITEM_BLINK (1 << 8) -#define EF_PARENT_ANIMATES (1 << 9) - -#include -#include -#include - -Handle g_hPlayTaunt; -StringMap g_hTokensMap; -int g_iClientParticleIndex [MAXPLAYERS + 1]; -int g_iClientParticleEntity[MAXPLAYERS + 1] = {-1, ...}; -bool g_bClientShouldSee [MAXPLAYERS + 1]; -ArrayList g_hUnusualTauntsList; -ConVar g_hCvarVariation; -ConVar g_hCvarDefaultTaunts; - -enum ParticleAttachmentType -{ - PATTACH_ABSORIGIN = 0, // Create at absorigin, but don't follow - PATTACH_ABSORIGIN_FOLLOW, // Create at absorigin, and update to follow the entity - PATTACH_CUSTOMORIGIN, // Create at a custom origin, but don't follow - PATTACH_POINT, // Create on attachment point, but don't follow - PATTACH_POINT_FOLLOW, // Create on attachment point, and update to follow the entity - PATTACH_WORLDORIGIN, // Used for control points that don't attach to an entity - PATTACH_ROOTBONE_FOLLOW // Create at the root bone of the entity, and update to follow -}; - -enum struct UnusualTauntConfig -{ - int ParticleIndex; - float RefireInterval; - bool UseParticleSystem; - bool Disabled; -} - public Plugin myinfo = { name = PLUGIN_NAME, @@ -63,101 +23,8 @@ public Plugin myinfo = public void OnPluginStart() { - Handle hConf = LoadGameConfigFile("tf2.tauntem"); - - if (hConf == null) - { - SetFailState("Unable to load gamedata/tf2.tauntem.txt."); - } - - StartPrepSDKCall(SDKCall_Player); - PrepSDKCall_SetFromConf(hConf, SDKConf_Signature, "CTFPlayer::PlayTauntSceneFromItem"); - PrepSDKCall_AddParameter(SDKType_PlainOldData, SDKPass_Plain); - PrepSDKCall_SetReturnInfo(SDKType_Bool, SDKPass_Plain); - g_hPlayTaunt = EndPrepSDKCall(); - - if (g_hPlayTaunt == null) - { - SetFailState("Unable to initialize call to CTFPlayer::PlayTauntSceneFromItem."); - } - - delete hConf; - RegConsoleCmd("sm_taunt", CmdTaunt); - RegConsoleCmd("sm_unusualtaunt", CmdUnusualTauntsMenu); - RegConsoleCmd("sm_utaunt", CmdUnusualTauntsMenu); - RegConsoleCmd("sm_taunts", CmdTaunt); - RegConsoleCmd("sm_unusualtaunts", CmdUnusualTauntsMenu); - RegConsoleCmd("sm_utaunts", CmdUnusualTauntsMenu); - - RegAdminCmd("sm_refreshtaunts", CmdRefreshConfig, ADMFLAG_CONFIG, "Reloads the taunts configuration file"); - - g_hCvarVariation = CreateConVar("sm_econtaunts_refire", "0.05", "Time variation between particle restarts.", _, true, 0.0); - g_hCvarDefaultTaunts = CreateConVar("sm_econtaunts_defaulttaunts", "0", "Add unusuals to default taunts?", _, true, 0.0, true, 1.0); - - // OnClientPutInServer makes the particle spawn at [0, 0, 0], no idea why. - HookEvent("player_team", OnPlayerTeam); - - g_hTokensMap = ParseLanguage("english"); -} - -public void OnClientDisconnect(int iClient) -{ - g_iClientParticleIndex[iClient] = 0; - g_iClientParticleEntity[iClient] = -1; - g_bClientShouldSee[iClient] = false; -} - -public void OnClientConnected(int iClient) -{ - g_iClientParticleIndex[iClient] = 0; - g_iClientParticleEntity[iClient] = -1; - g_bClientShouldSee[iClient] = false; -} - -public void OnMapStart() -{ - ParseTauntConfig(); -} - -public void OnPlayerTeam(Event hEvent, char[] strEventName, bool bDontBroadcast) -{ - int iClient = GetClientOfUserId(hEvent.GetInt("userid")); - - char strEffectName[PLATFORM_MAX_PATH]; - int iParticleIndex, iParticleEntity; - - if (hEvent.GetInt("oldteam") == 0 && !g_bClientShouldSee[iClient]) - { - for (int iIndex = 1; iIndex <= MaxClients; iIndex++) - { - iParticleIndex = g_iClientParticleIndex[iIndex]; - iParticleEntity = EntRefToEntIndex(g_iClientParticleEntity[iIndex]); - - UnusualTauntConfig Taunt; - bool bParticleSystem = false; - - int iUnusualConfigIndex = g_hUnusualTauntsList == null ? -1 : g_hUnusualTauntsList.FindValue(iParticleIndex, 0); - - if (iUnusualConfigIndex != -1) - { - g_hUnusualTauntsList.GetArray(iUnusualConfigIndex, Taunt); - bParticleSystem = Taunt.UseParticleSystem; - } - - if (iParticleEntity == -1 || !iParticleIndex || bParticleSystem || (Taunt.RefireInterval > 0)) - { - continue; - } - - TF2Econ_GetParticleAttributeSystemName(iParticleIndex, strEffectName, sizeof(strEffectName)); - CreateTempParticle(strEffectName, _, _, _, iParticleEntity, PATTACH_ABSORIGIN_FOLLOW, _, _); - TE_SendToClient(iClient); - } - - g_bClientShouldSee[iClient] = true; - } } public Action CmdTaunt(int iClient, int iArgs) @@ -165,6 +32,7 @@ public Action CmdTaunt(int iClient, int iArgs) if (iClient == 0) { ReplyToCommand(iClient, "Command is in-game only."); + return Plugin_Handled; } @@ -184,147 +52,52 @@ public Action CmdTaunt(int iClient, int iArgs) { ReplyToCommand(iClient, "[SM] Invalid taunt index"); } - } - else - { - Menu hMenu = new Menu(TauntMenuHandler); - hMenu.SetTitle("Taunts :"); - - ArrayList hTauntList = TF2Econ_GetItemList(FilterTaunts, TF2_GetPlayerClass(iClient)); - int iTauntListSize = hTauntList.Length; - - char strTauntName[64]; - - for (int iEntry = 0; iEntry < iTauntListSize; iEntry++) - { - int iTauntIndex = hTauntList.Get(iEntry); - IntToString(iTauntIndex, strTauntIndex, sizeof(strTauntIndex)); - - TF2Econ_GetItemName(iTauntIndex, strTauntName, sizeof(strTauntName)); - - Format(strTauntName, sizeof(strTauntName), "%s (%i)", strTauntName, iTauntIndex); - - hMenu.AddItem(strTauntIndex, strTauntName, ITEMDRAW_DEFAULT); - } - - hMenu.Display(iClient, MENU_TIME_FOREVER); - delete hTauntList; + return Plugin_Handled; } - return Plugin_Handled; -} - -public int TauntMenuHandler(Menu hMenu, MenuAction iMenuActions, int iParam1, int iParam2) -{ - switch (iMenuActions) - { - case MenuAction_Select : - { - char strTauntIndex[16]; - hMenu.GetItem(iParam2, strTauntIndex, sizeof(strTauntIndex)); - - int iTauntIndex = StringToInt(strTauntIndex); - PlayTaunt(iParam1, iTauntIndex); - } - - case MenuAction_End : - { - delete hMenu; - } - } + Menu hMenu = new Menu(TauntMenuHandler); + hMenu.SetTitle("Taunts :"); - return 0; -} - -public bool FilterTaunts(int iItemDefIndex, TFClassType iClass) -{ - return TF2Econ_GetItemLoadoutSlot(iItemDefIndex, iClass) == TF2Econ_TranslateLoadoutSlotNameToIndex("taunt"); -} - -public Action CmdUnusualTauntsMenu(int iClient, int iArgs) -{ - if (iClient == 0) + static ArrayList hTauntsList = null; + + if (hTauntsList == null) { - ReplyToCommand(iClient, "Command is in-game only."); - return Plugin_Handled; + hTauntsList = TF2Econ_GetItemList(FilterTaunts, TF2_GetPlayerClass(iClient)); } - Menu hMenu = new Menu(UnusualTauntsMenuHandler); - - hMenu.SetTitle("Unusual taunt particles :"); - hMenu.AddItem("0", "No effect", ITEMDRAW_DEFAULT); - - ArrayList hUnusualsList = TF2Econ_GetParticleAttributeList(ParticleSet_TauntUnusualEffects); - int iUnusualsListSize = hUnusualsList.Length; + int iTauntListSize = hTauntsList.Length; - char strUnusualIndex[16]; - char strUnusualName[64]; - char strLocalizedName[64]; + char strTauntName[64]; - for (int iEntry = 0; iEntry < iUnusualsListSize; iEntry++) + for (int iEntry = 0; iEntry < iTauntListSize; iEntry++) { - UnusualTauntConfig UnusualTaunt; - bool bUseUnusual = false; - - int iUnusualIndex = hUnusualsList.Get(iEntry); - IntToString(iUnusualIndex, strUnusualIndex, sizeof(strUnusualIndex)); - - FormatEx(strUnusualName, sizeof(strUnusualName), "Attrib_Particle%i", iUnusualIndex); - - if (LocalizeToken(strUnusualName, strLocalizedName, sizeof(strLocalizedName))) - { - Format(strLocalizedName, sizeof(strLocalizedName), "%s (%i)", strLocalizedName, iUnusualIndex); - } + int iTauntIndex = hTauntsList.Get(iEntry); + IntToString(iTauntIndex, strTauntIndex, sizeof(strTauntIndex)); - int iUnusualConfigIndex = g_hUnusualTauntsList == null ? -1 : g_hUnusualTauntsList.FindValue(iUnusualIndex, 0); + TF2Econ_GetItemName(iTauntIndex, strTauntName, sizeof(strTauntName)); - if (iUnusualConfigIndex != -1) - { - g_hUnusualTauntsList.GetArray(iUnusualConfigIndex, UnusualTaunt); - bUseUnusual = UnusualTaunt.Disabled; - } + Format(strTauntName, sizeof(strTauntName), "%s (%i)", strTauntName, iTauntIndex); - hMenu.AddItem(strUnusualIndex, strLocalizedName, bUseUnusual ? ITEMDRAW_DISABLED : ITEMDRAW_DEFAULT); + hMenu.AddItem(strTauntIndex, strTauntName, ITEMDRAW_DEFAULT); } hMenu.Display(iClient, MENU_TIME_FOREVER); - delete hUnusualsList; - return Plugin_Handled; } -public Action CmdRefreshConfig(int iClient, int iArgs) -{ - ParseTauntConfig(); - - ReplyToCommand(iClient, "[SM] Successfully refreshed the taunts configuration file"); - - return Plugin_Handled; -} - -public int UnusualTauntsMenuHandler(Menu hMenu, MenuAction iMenuActions, int iParam1, int iParam2) +public int TauntMenuHandler(Menu hMenu, MenuAction iMenuActions, int iParam1, int iParam2) { switch (iMenuActions) { case MenuAction_Select : { - char strUnusualIndex[16]; - char strLocalizedName[64]; - hMenu.GetItem(iParam2, strUnusualIndex, sizeof(strUnusualIndex), _, strLocalizedName, sizeof(strLocalizedName)); - - int iUnusualIndex = StringToInt(strUnusualIndex); - g_iClientParticleIndex[iParam1] = iUnusualIndex; + char strTauntIndex[16]; + hMenu.GetItem(iParam2, strTauntIndex, sizeof(strTauntIndex)); - if (iUnusualIndex != 0) - { - ReplyToCommand(iParam1, "[SM] Successfully applied \"%s\" on your taunts", strLocalizedName); - } - else - { - ReplyToCommand(iParam1, "[SM] Successfully removed the current effect from your taunts"); - } + int iTauntIndex = StringToInt(strTauntIndex); + PlayTaunt(iParam1, iTauntIndex); } case MenuAction_End : @@ -336,6 +109,11 @@ public int UnusualTauntsMenuHandler(Menu hMenu, MenuAction iMenuActions, int iPa return 0; } +public bool FilterTaunts(int iItemDefIndex, TFClassType iClass) +{ + return TF2Econ_GetItemLoadoutSlot(iItemDefIndex, iClass) == TF2Econ_TranslateLoadoutSlotNameToIndex("taunt"); +} + bool PlayTaunt(int iClient, int iTauntIndex) { int iEntity = MakeCEIVEnt(iClient, iTauntIndex); @@ -371,7 +149,26 @@ bool PlayTaunt(int iClient, int iTauntIndex) pEconItemView += view_as
(iCEIVOffset); - if (!SDKCall(g_hPlayTaunt, iClient, pEconItemView)) + static Handle hPlayTaunt = null; + + if (hPlayTaunt == null) + { + GameData hConf = new GameData("tf2.tauntem"); + + if (hConf == null) SetFailState("Unable to load gamedata/tf2.tauntem.txt."); + + StartPrepSDKCall(SDKCall_Player); + PrepSDKCall_SetFromConf(hConf, SDKConf_Signature, "CTFPlayer::PlayTauntSceneFromItem"); + PrepSDKCall_AddParameter(SDKType_PlainOldData, SDKPass_Plain); + PrepSDKCall_SetReturnInfo(SDKType_Bool, SDKPass_Plain); + hPlayTaunt = EndPrepSDKCall(); + + if (hPlayTaunt == null) SetFailState("Unable to initialize call to CTFPlayer::PlayTauntSceneFromItem."); + + delete hConf; + } + + if (!SDKCall(hPlayTaunt, iClient, pEconItemView)) { ReplyToCommand(iClient, "[SM] Couldn't play taunt"); @@ -417,452 +214,3 @@ stock bool IsValidAddress(Address pAddress) { return pAddress != Address_Null; } - -void ParseTauntConfig() -{ - delete g_hUnusualTauntsList; - - char strFilePath[PLATFORM_MAX_PATH]; - BuildPath(Path_SM, strFilePath, sizeof(strFilePath), "configs/econtaunts/taunts.cfg"); - - if (!FileExists(strFilePath, true)) return; - - KeyValues kvTauntConfig = new KeyValues("EconTaunts"); - - if (kvTauntConfig.ImportFromFile(strFilePath) && kvTauntConfig.GotoFirstSubKey()) - { - g_hUnusualTauntsList = new ArrayList(sizeof(UnusualTauntConfig)); - - do - { - ParseUnusualTaunt(kvTauntConfig); - } - while (kvTauntConfig.GotoNextKey()); - } - - delete kvTauntConfig; -} - -void ParseUnusualTaunt(KeyValues kvConfig) -{ - kvConfig.GotoFirstSubKey(); - - char strUnusualTauntIndex[16]; - int iTauntIndex; - - do - { - if (!kvConfig.GetSectionName(strUnusualTauntIndex, sizeof(strUnusualTauntIndex)) || - !StringToIntEx(strUnusualTauntIndex, iTauntIndex)) continue; - - if (IsUnusualTauntAdded(iTauntIndex)) continue; - - UnusualTauntConfig UnusualTaunt; - - UnusualTaunt.ParticleIndex = iTauntIndex; - UnusualTaunt.RefireInterval = kvConfig.GetFloat("refire interval", 0.0); - UnusualTaunt.Disabled = !!kvConfig.GetNum("disabled", 0); - UnusualTaunt.UseParticleSystem = !!kvConfig.GetNum("use particle system", 0); - - g_hUnusualTauntsList.PushArray(UnusualTaunt); - } - while (kvConfig.GotoNextKey()); - - kvConfig.GoBack(); -} - -bool IsUnusualTauntAdded(int iTauntIndex) -{ - UnusualTauntConfig UnusualTaunt; - - int iArrayIndex = g_hUnusualTauntsList.FindValue(iTauntIndex, 0); - - if ((iArrayIndex != -1) && (g_hUnusualTauntsList.GetArray(iArrayIndex, UnusualTaunt) > 0)) - { - LogMessage("Taunt Index : %i found twice, skipping.", UnusualTaunt.ParticleIndex); - - return true; - } - - return false; -} - -public void TF2_OnConditionAdded(int iClient, TFCond iCondition) -{ - if (iCondition != TFCond_Taunting) return; - - // https://forums.alliedmods.net/showthread.php?p=2359106 - if (!g_hCvarDefaultTaunts.BoolValue && (GetEntProp(iClient, Prop_Send, "m_iTauntItemDefIndex") == -1)) return; - - int iParticleIndex = g_iClientParticleIndex[iClient]; - - if (iParticleIndex == 0) return; - - UnusualTauntConfig UnusualTaunt; - bool bParticleSystem = false; - - int iUnusualConfigIndex = g_hUnusualTauntsList == null ? -1 : g_hUnusualTauntsList.FindValue(iParticleIndex, 0); - - if (iUnusualConfigIndex != -1) - { - g_hUnusualTauntsList.GetArray(iUnusualConfigIndex, UnusualTaunt); - bParticleSystem = UnusualTaunt.UseParticleSystem; - } - - int iParticleEntity = CreateAttachedParticle(iClient, iParticleIndex); - - if (!(bParticleSystem ? IsValidEdict(iParticleEntity) : IsValidEntity(iParticleEntity))) return; - - if (UnusualTaunt.RefireInterval > 0 && !bParticleSystem) - { - DataPack hTauntDataPack = new DataPack(); - hTauntDataPack.WriteCell(GetClientUserId(iClient)); - hTauntDataPack.WriteCell(iParticleIndex); - hTauntDataPack.WriteCell(EntIndexToEntRef(iParticleEntity)); - - CreateTimer(UnusualTaunt.RefireInterval, RefireTauntParticle, hTauntDataPack, TIMER_REPEAT | TIMER_FLAG_NO_MAPCHANGE | TIMER_DATA_HNDL_CLOSE); - } - - g_iClientParticleEntity[iClient] = EntIndexToEntRef(iParticleEntity); -} - -public Action RefireTauntParticle(Handle hTimer, DataPack hTauntDataPack) -{ - hTauntDataPack.Reset(); - - int iClient = GetClientOfUserId(hTauntDataPack.ReadCell()); - int iParticleIndex = hTauntDataPack.ReadCell(); - int iParticleEntity = EntRefToEntIndex(hTauntDataPack.ReadCell()); - - if (!iParticleIndex || iParticleEntity == -1 || !iClient || !IsClientInGame(iClient)) - { - return Plugin_Stop; - } - - char strEffectName[PLATFORM_MAX_PATH]; - - // https://forums.alliedmods.net/showthread.php?t=235329 - SetVariantString("ParticleEffectStop"); - AcceptEntityInput(iParticleEntity, "DispatchEffect"); - - TF2Econ_GetParticleAttributeSystemName(iParticleIndex, strEffectName, sizeof(strEffectName)); - CreateTempParticle(strEffectName, _, _, _, iParticleEntity, PATTACH_ABSORIGIN_FOLLOW, _, _); - TE_SendToAll(); - - return Plugin_Continue; -} - -public void TF2_OnConditionRemoved(int iClient, TFCond iCondition) -{ - if (iCondition != TFCond_Taunting) return; - - int iParticleEntity = EntRefToEntIndex(g_iClientParticleEntity[iClient]); - - if (iParticleEntity == -1) return; - - char strEntityClassname[64]; GetEntityClassname(iParticleEntity, strEntityClassname, sizeof(strEntityClassname)); - - if (StrEqual(strEntityClassname, "info_particle_system")) - { - if (IsValidEdict(iParticleEntity)) - { - RemoveEdict(iParticleEntity); - g_iClientParticleEntity[iClient] = -1; - } - } - else - { - if (IsValidEntity(iParticleEntity)) - { - RemoveEntity(iParticleEntity); - g_iClientParticleEntity[iClient] = -1; - } - } -} - -/* - I couldn't get unusual taunts working with attributes so I did this. - If anyone knows how to get them working with attributes, please tell how. -*/ - -stock int CreateAttachedParticle(int iClient, int iParticleIndex) -{ - UnusualTauntConfig UnusualTaunt; - bool bParticleSystem = false; - - int iUnusualConfigIndex = g_hUnusualTauntsList == null ? -1 : g_hUnusualTauntsList.FindValue(iParticleIndex, 0); - - if (iUnusualConfigIndex != -1) - { - g_hUnusualTauntsList.GetArray(iUnusualConfigIndex, UnusualTaunt); - bParticleSystem = UnusualTaunt.UseParticleSystem; - } - - // I just guessed it, nothing else - int iEntity = CreateEntityByName(bParticleSystem ? "info_particle_system" : "tf_wearable"); - - if (!(bParticleSystem ? IsValidEdict(iEntity) : IsValidEntity(iEntity))) return iEntity; - - float fPosition[3]; - GetEntPropVector(iClient, Prop_Send, "m_vecOrigin", fPosition); - TeleportEntity(iEntity, fPosition, NULL_VECTOR, NULL_VECTOR); - - char strEffectName[PLATFORM_MAX_PATH]; - - if (!TF2Econ_GetParticleAttributeSystemName(iParticleIndex, strEffectName, sizeof(strEffectName))) - { - LogError("Failed to get the system name of the particle attribute index. Removing entity."); - - bParticleSystem ? RemoveEdict(iEntity) : RemoveEntity(iEntity); - - return -1; - } - - if (!bParticleSystem) - { - DispatchSpawn(iEntity); - - // EF_BONEMERGE_FASTCULL moves the entity in an undesired position (in the middle of the client) - // Also setting all these netprops before dispatching spawn resets them for some reason - SetEntProp(iEntity, Prop_Send, "m_fEffects", EF_BONEMERGE | EF_NOSHADOW | EF_PARENT_ANIMATES | EF_NODRAW | EF_NORECEIVESHADOW); - SetEntProp(iEntity, Prop_Send, "m_CollisionGroup", 0); // COLLISION_GROUP_NONE - SetEntProp(iEntity, Prop_Send, "m_usSolidFlags", 0x0004); // FSOLID_NOT_SOLID - SetEntProp(iEntity, Prop_Send, "m_nSolidType", 0); // SOLID_NONE - SetEntProp(iEntity, Prop_Send, "m_bValidatedAttachedEntity", 1); // Visibility - - char strModelName[PLATFORM_MAX_PATH]; - GetEntPropString(iClient, Prop_Data, "m_ModelName", strModelName, sizeof(strModelName)); - SetEntityModel(iEntity, strModelName); - - SetVariantString("!activator"); - AcceptEntityInput(iEntity, "SetParent", iClient, iEntity); - - CreateTempParticle(strEffectName, fPosition, _, _, iEntity, PATTACH_ABSORIGIN_FOLLOW, _, _); - - TE_SendToAll(); - } - else - { - DispatchKeyValue(iEntity, "effect_name", strEffectName); - DispatchSpawn(iEntity); - ActivateEntity(iEntity); - - SetVariantString("!activator"); - AcceptEntityInput(iEntity, "SetParent", iClient, iEntity); - - AcceptEntityInput(iEntity, "Start"); - - if (UnusualTaunt.RefireInterval > 0) - { - char strBuffer[64]; - FormatEx(strBuffer, sizeof(strBuffer), "OnUser1 !self:Stop::%f:-1", UnusualTaunt.RefireInterval); - SetVariantString(strBuffer); - AcceptEntityInput(iEntity, "AddOutput"); - FormatEx(strBuffer, sizeof(strBuffer), "OnUser1 !self:Start::%f:-1", UnusualTaunt.RefireInterval + GetConVarFloat(g_hCvarVariation)); - SetVariantString(strBuffer); - AcceptEntityInput(iEntity, "AddOutput"); - FormatEx(strBuffer, sizeof(strBuffer), "OnUser1 !self:FireUser1::%f:-1", UnusualTaunt.RefireInterval); - SetVariantString(strBuffer); - AcceptEntityInput(iEntity, "AddOutput"); - AcceptEntityInput(iEntity, "FireUser1"); - } - } - - return iEntity; -} - -stock void CreateTempParticle(const char[] strParticle, - const float vecOrigin[3] = NULL_VECTOR, - const float vecStart[3] = NULL_VECTOR, - const float vecAngles[3] = NULL_VECTOR, - int iEntity = -1, - ParticleAttachmentType AttachmentType = PATTACH_ABSORIGIN, - int iAttachmentPoint = -1, - bool bResetParticles = false) -{ - int iParticleTable, iParticleIndex; - - iParticleTable = FindStringTable("ParticleEffectNames"); - - if (iParticleTable == INVALID_STRING_TABLE) - { - ThrowError("Could not find string table: ParticleEffectNames"); - } - - iParticleIndex = FindStringIndex(iParticleTable, strParticle); - - if (iParticleIndex == INVALID_STRING_INDEX) - { - LogError("Could not find particle index: %s. Trying to precache it now.", strParticle); - - iParticleIndex = GetPrecachedParticle(strParticle); - - if (iParticleIndex == INVALID_STRING_INDEX) - { - ThrowError("Could not find particle index: %s", strParticle); - } - } - - TE_Start("TFParticleEffect"); - TE_WriteFloat("m_vecOrigin[0]", vecOrigin[0]); - TE_WriteFloat("m_vecOrigin[1]", vecOrigin[1]); - TE_WriteFloat("m_vecOrigin[2]", vecOrigin[2]); - TE_WriteFloat("m_vecStart[0]", vecStart[0]); - TE_WriteFloat("m_vecStart[1]", vecStart[1]); - TE_WriteFloat("m_vecStart[2]", vecStart[2]); - TE_WriteVector("m_vecAngles", vecAngles); - TE_WriteNum("m_iParticleSystemIndex", iParticleIndex); - - if (iEntity != -1) - { - TE_WriteNum("entindex", iEntity); - } - - if (AttachmentType != PATTACH_ABSORIGIN) - { - TE_WriteNum("m_iAttachType", view_as(AttachmentType)); - } - - if (iAttachmentPoint != -1) - { - TE_WriteNum("m_iAttachmentPointIndex", iAttachmentPoint); - } - - TE_WriteNum("m_bResetParticles", bResetParticles ? 1 : 0); -} - -stock int GetPrecachedParticle(const char[] strParticleSystem) -{ - static int iParticleEffectNames = INVALID_STRING_TABLE; - - if ((iParticleEffectNames == INVALID_STRING_TABLE) && - ((iParticleEffectNames = FindStringTable("ParticleEffectNames")) == INVALID_STRING_TABLE)) - { - return INVALID_STRING_INDEX; - } - - int iParticleIndex = FindStringIndex(iParticleEffectNames, strParticleSystem); - - if (iParticleIndex == INVALID_STRING_INDEX) - { - int iNumStrings = GetStringTableNumStrings(iParticleEffectNames); - - if (iNumStrings >= GetStringTableMaxStrings(iParticleEffectNames)) - { - return INVALID_STRING_INDEX; - } - - AddToStringTable(iParticleEffectNames, strParticleSystem); - iParticleIndex = iNumStrings; - } - - return iParticleIndex; -} - -/* - The following localization functions are taken from - https://github.com/DoctorMcKay/sourcemod-plugins/blob/master/scripting/enhanced_items.sp -*/ - -bool LocalizeToken(const char[] strToken, char[] strOutput, int strMaxLen) -{ - if(g_hTokensMap == null) - { - LogError("Unable to localize token for server language!"); - - return false; - } - else - { - return g_hTokensMap.GetString(strToken, strOutput, strMaxLen); - } -} - -StringMap ParseLanguage(const char[] strLanguage) -{ - char strFilename[64]; - Format(strFilename, sizeof(strFilename), "resource/tf_%s.txt", strLanguage); - File hFile = OpenFile(strFilename, "r"); - - if(hFile == null) - { - return null; - } - - // The localization files are encoded in UCS-2, breaking all of our available parsing options - // We have to go byte-by-byte then line-by-line :( - - // This parser isn't perfect since some values span multiple lines, but since we're only interested in single-line values, this is sufficient - - StringMap hLang = new StringMap(); - hLang.SetString("__name__", strLanguage); - - int iData, i = 0; - char strLine[2048]; - - while(ReadFileCell(hFile, iData, 2) == 1) - { - if(iData < 0x80) - { - // It's a single-byte character - strLine[i++] = iData; - - if(iData == '\n') - { - strLine[i] = '\0'; - HandleLangLine(strLine, hLang); - i = 0; - } - } - else if(iData < 0x800) - { - // It's a two-byte character - strLine[i++] = (iData >> 6) | 0xC0; - strLine[i++] = (iData & 0x3F) | 0x80; - } - else if(iData < 0xFFFF && iData >= 0xD800 && iData <= 0xDFFF) - { - strLine[i++] = (iData >> 12) | 0xE0; - strLine[i++] = ((iData >> 6) & 0x3F) | 0x80; - strLine[i++] = (iData & 0x3F) | 0x80; - } - else if(iData >= 0x10000 && iData < 0x10FFFF) - { - strLine[i++] = (iData >> 18) | 0xF0; - strLine[i++] = ((iData >> 12) & 0x3F) | 0x80; - strLine[i++] = ((iData >> 6) & 0x3F) | 0x80; - strLine[i++] = (iData & 0x3F) | 0x80; - } - } - - delete hFile; - - return hLang; -} - -void HandleLangLine(char[] strLine, StringMap hLang) -{ - TrimString(strLine); - - if(strLine[0] != '"') - { - // Not a line containing at least one quoted string - return; - } - - char strToken[128], strValue[1024]; - int iPos = BreakString(strLine, strToken, sizeof(strToken)); - - if(iPos == -1) - { - // This line doesn't have two quoted strings - return; - } - - BreakString(strLine[iPos], strValue, sizeof(strValue)); - - if (StrContains(strToken, "Attrib_Particle") != -1) // Only particles should be added - { - hLang.SetString(strToken, strValue); - } -} diff --git a/addons/sourcemod/scripting/TF2EconUnusualTaunts.sp b/addons/sourcemod/scripting/TF2EconUnusualTaunts.sp new file mode 100644 index 0000000..3b886ff --- /dev/null +++ b/addons/sourcemod/scripting/TF2EconUnusualTaunts.sp @@ -0,0 +1,684 @@ +#pragma semicolon 1 +#pragma newdecls required + +#include +#include +#include + +#define PLUGIN_NAME "[TF2] Econ Unusual Taunts" +#define PLUGIN_AUTHOR "x07x08" +#define PLUGIN_DESCRIPTION "A simple unusual taunts menu plugin" +#define PLUGIN_VERSION "1.3.0" +#define PLUGIN_URL "https://github.com/x07x08/TF2-Econ-Taunts" + +#define EF_BONEMERGE (1 << 0) +#define EF_BRIGHTLIGHT (1 << 1) +#define EF_DIMLIGHT (1 << 2) +#define EF_NOINTERP (1 << 3) +#define EF_NOSHADOW (1 << 4) +#define EF_NODRAW (1 << 5) +#define EF_NORECEIVESHADOW (1 << 6) +#define EF_BONEMERGE_FASTCULL (1 << 7) +#define EF_ITEM_BLINK (1 << 8) +#define EF_PARENT_ANIMATES (1 << 9) + +StringMap g_hTokensMap; +int g_iClientParticleIndex [MAXPLAYERS + 1]; +int g_iClientParticleEntity[MAXPLAYERS + 1] = {-1, ...}; +bool g_bClientShouldSee [MAXPLAYERS + 1]; +ArrayList g_hUnusualTauntsList; +ConVar g_hCvarVariation; +ConVar g_hCvarDefaultTaunts; + +enum ParticleAttachmentType +{ + PATTACH_ABSORIGIN = 0, // Create at absorigin, but don't follow + PATTACH_ABSORIGIN_FOLLOW, // Create at absorigin, and update to follow the entity + PATTACH_CUSTOMORIGIN, // Create at a custom origin, but don't follow + PATTACH_POINT, // Create on attachment point, but don't follow + PATTACH_POINT_FOLLOW, // Create on attachment point, and update to follow the entity + PATTACH_WORLDORIGIN, // Used for control points that don't attach to an entity + PATTACH_ROOTBONE_FOLLOW // Create at the root bone of the entity, and update to follow +}; + +enum struct UnusualTauntConfig +{ + int ParticleIndex; + float RefireInterval; + bool UseParticleSystem; + bool Disabled; +} + +public Plugin myinfo = +{ + name = PLUGIN_NAME, + author = PLUGIN_AUTHOR, + description = PLUGIN_DESCRIPTION, + version = PLUGIN_VERSION, + url = PLUGIN_URL +}; + +public void OnPluginStart() +{ + RegConsoleCmd("sm_unusualtaunt", CmdUnusualTauntsMenu); + RegConsoleCmd("sm_utaunt", CmdUnusualTauntsMenu); + + RegConsoleCmd("sm_unusualtaunts", CmdUnusualTauntsMenu); + RegConsoleCmd("sm_utaunts", CmdUnusualTauntsMenu); + + RegAdminCmd("sm_refreshtaunts", CmdRefreshConfig, ADMFLAG_CONFIG, "Reloads the taunts configuration file"); + + g_hCvarVariation = CreateConVar("sm_econtaunts_refire", "0.05", "Time variation between particle restarts.", _, true, 0.0); + g_hCvarDefaultTaunts = CreateConVar("sm_econtaunts_defaulttaunts", "0", "Add unusuals to default taunts?", _, true, 0.0, true, 1.0); + + // OnClientPutInServer makes the particle spawn at [0, 0, 0], no idea why. + HookEvent("player_team", OnPlayerTeam); + + g_hTokensMap = ParseLanguage("english"); +} + +public void OnClientDisconnect(int iClient) +{ + g_iClientParticleIndex[iClient] = 0; + g_iClientParticleEntity[iClient] = -1; + g_bClientShouldSee[iClient] = false; +} + +public void OnClientConnected(int iClient) +{ + g_iClientParticleIndex[iClient] = 0; + g_iClientParticleEntity[iClient] = -1; + g_bClientShouldSee[iClient] = false; +} + +public void OnMapStart() +{ + ParseTauntConfig(); +} + +public void OnPlayerTeam(Event hEvent, char[] strEventName, bool bDontBroadcast) +{ + int iClient = GetClientOfUserId(hEvent.GetInt("userid")); + + char strEffectName[PLATFORM_MAX_PATH]; + int iParticleIndex, iParticleEntity; + + if (hEvent.GetInt("oldteam") == 0 && !g_bClientShouldSee[iClient]) + { + for (int iIndex = 1; iIndex <= MaxClients; iIndex++) + { + iParticleIndex = g_iClientParticleIndex[iIndex]; + iParticleEntity = EntRefToEntIndex(g_iClientParticleEntity[iIndex]); + + UnusualTauntConfig Taunt; + bool bParticleSystem = false; + + int iUnusualConfigIndex = g_hUnusualTauntsList == null ? -1 : g_hUnusualTauntsList.FindValue(iParticleIndex, 0); + + if (iUnusualConfigIndex != -1) + { + g_hUnusualTauntsList.GetArray(iUnusualConfigIndex, Taunt); + bParticleSystem = Taunt.UseParticleSystem; + } + + if (iParticleEntity == -1 || !iParticleIndex || bParticleSystem || (Taunt.RefireInterval > 0)) + { + continue; + } + + TF2Econ_GetParticleAttributeSystemName(iParticleIndex, strEffectName, sizeof(strEffectName)); + CreateTempParticle(strEffectName, _, _, _, iParticleEntity, PATTACH_ABSORIGIN_FOLLOW, _, _); + TE_SendToClient(iClient); + } + + g_bClientShouldSee[iClient] = true; + } +} + +public Action CmdUnusualTauntsMenu(int iClient, int iArgs) +{ + if (iClient == 0) + { + ReplyToCommand(iClient, "Command is in-game only."); + + return Plugin_Handled; + } + + Menu hMenu = new Menu(UnusualTauntsMenuHandler); + + hMenu.SetTitle("Unusual taunt particles :"); + hMenu.AddItem("0", "No effect", ITEMDRAW_DEFAULT); + + static ArrayList hUnusualsList = null; + + if (hUnusualsList == null) + { + hUnusualsList = TF2Econ_GetParticleAttributeList(ParticleSet_TauntUnusualEffects); + } + + int iUnusualsListSize = hUnusualsList.Length; + + char strUnusualIndex[16]; + char strUnusualName[64]; + char strLocalizedName[64]; + + for (int iEntry = 0; iEntry < iUnusualsListSize; iEntry++) + { + UnusualTauntConfig UnusualTaunt; + bool bUseUnusual = false; + + int iUnusualIndex = hUnusualsList.Get(iEntry); + IntToString(iUnusualIndex, strUnusualIndex, sizeof(strUnusualIndex)); + + FormatEx(strUnusualName, sizeof(strUnusualName), "Attrib_Particle%i", iUnusualIndex); + + if (LocalizeToken(strUnusualName, strLocalizedName, sizeof(strLocalizedName))) + { + Format(strLocalizedName, sizeof(strLocalizedName), "%s (%i)", strLocalizedName, iUnusualIndex); + } + + int iUnusualConfigIndex = g_hUnusualTauntsList == null ? -1 : g_hUnusualTauntsList.FindValue(iUnusualIndex, 0); + + if (iUnusualConfigIndex != -1) + { + g_hUnusualTauntsList.GetArray(iUnusualConfigIndex, UnusualTaunt); + bUseUnusual = UnusualTaunt.Disabled; + } + + hMenu.AddItem(strUnusualIndex, strLocalizedName, bUseUnusual ? ITEMDRAW_DISABLED : ITEMDRAW_DEFAULT); + } + + hMenu.Display(iClient, MENU_TIME_FOREVER); + + return Plugin_Handled; +} + +public int UnusualTauntsMenuHandler(Menu hMenu, MenuAction iMenuActions, int iParam1, int iParam2) +{ + switch (iMenuActions) + { + case MenuAction_Select : + { + char strUnusualIndex[16]; + char strLocalizedName[64]; + hMenu.GetItem(iParam2, strUnusualIndex, sizeof(strUnusualIndex), _, strLocalizedName, sizeof(strLocalizedName)); + + int iUnusualIndex = StringToInt(strUnusualIndex); + g_iClientParticleIndex[iParam1] = iUnusualIndex; + + if (iUnusualIndex != 0) + { + ReplyToCommand(iParam1, "[SM] Successfully applied \"%s\" on your taunts", strLocalizedName); + } + else + { + ReplyToCommand(iParam1, "[SM] Successfully removed the current effect from your taunts"); + } + } + + case MenuAction_End : + { + delete hMenu; + } + } + + return 0; +} + +public Action CmdRefreshConfig(int iClient, int iArgs) +{ + ParseTauntConfig(); + + ReplyToCommand(iClient, "[SM] Successfully refreshed the taunts configuration file"); + + return Plugin_Handled; +} + +void ParseTauntConfig() +{ + delete g_hUnusualTauntsList; + + char strFilePath[PLATFORM_MAX_PATH]; + BuildPath(Path_SM, strFilePath, sizeof(strFilePath), "configs/econtaunts/taunts.cfg"); + + if (!FileExists(strFilePath, true)) return; + + KeyValues kvTauntConfig = new KeyValues("EconTaunts"); + + if (kvTauntConfig.ImportFromFile(strFilePath) && kvTauntConfig.GotoFirstSubKey()) + { + g_hUnusualTauntsList = new ArrayList(sizeof(UnusualTauntConfig)); + + do + { + ParseUnusualTaunt(kvTauntConfig); + } + while (kvTauntConfig.GotoNextKey()); + } + + delete kvTauntConfig; +} + +void ParseUnusualTaunt(KeyValues kvConfig) +{ + kvConfig.GotoFirstSubKey(); + + char strUnusualTauntIndex[16]; + int iTauntIndex; + + do + { + if (!kvConfig.GetSectionName(strUnusualTauntIndex, sizeof(strUnusualTauntIndex)) || + !StringToIntEx(strUnusualTauntIndex, iTauntIndex)) continue; + + if (IsUnusualTauntAdded(iTauntIndex)) continue; + + UnusualTauntConfig UnusualTaunt; + + UnusualTaunt.ParticleIndex = iTauntIndex; + UnusualTaunt.RefireInterval = kvConfig.GetFloat("refire interval", 0.0); + UnusualTaunt.Disabled = !!kvConfig.GetNum("disabled", 0); + UnusualTaunt.UseParticleSystem = !!kvConfig.GetNum("use particle system", 0); + + g_hUnusualTauntsList.PushArray(UnusualTaunt); + } + while (kvConfig.GotoNextKey()); + + kvConfig.GoBack(); +} + +bool IsUnusualTauntAdded(int iTauntIndex) +{ + UnusualTauntConfig UnusualTaunt; + + int iArrayIndex = g_hUnusualTauntsList.FindValue(iTauntIndex, 0); + + if ((iArrayIndex != -1) && (g_hUnusualTauntsList.GetArray(iArrayIndex, UnusualTaunt) > 0)) + { + LogMessage("Taunt Index : %i found twice, skipping.", UnusualTaunt.ParticleIndex); + + return true; + } + + return false; +} + +public void TF2_OnConditionAdded(int iClient, TFCond iCondition) +{ + if (iCondition != TFCond_Taunting) return; + + // https://forums.alliedmods.net/showthread.php?p=2359106 + if (!g_hCvarDefaultTaunts.BoolValue && (GetEntProp(iClient, Prop_Send, "m_iTauntItemDefIndex") == -1)) return; + + int iParticleIndex = g_iClientParticleIndex[iClient]; + + if (iParticleIndex == 0) return; + + UnusualTauntConfig UnusualTaunt; + bool bParticleSystem = false; + + int iUnusualConfigIndex = g_hUnusualTauntsList == null ? -1 : g_hUnusualTauntsList.FindValue(iParticleIndex, 0); + + if (iUnusualConfigIndex != -1) + { + g_hUnusualTauntsList.GetArray(iUnusualConfigIndex, UnusualTaunt); + bParticleSystem = UnusualTaunt.UseParticleSystem; + } + + int iParticleEntity = CreateAttachedParticle(iClient, iParticleIndex); + + if (!(bParticleSystem ? IsValidEdict(iParticleEntity) : IsValidEntity(iParticleEntity))) return; + + if (UnusualTaunt.RefireInterval > 0 && !bParticleSystem) + { + DataPack hTauntDataPack = new DataPack(); + hTauntDataPack.WriteCell(GetClientUserId(iClient)); + hTauntDataPack.WriteCell(iParticleIndex); + hTauntDataPack.WriteCell(EntIndexToEntRef(iParticleEntity)); + + CreateTimer(UnusualTaunt.RefireInterval, RefireTauntParticle, hTauntDataPack, TIMER_REPEAT | TIMER_FLAG_NO_MAPCHANGE | TIMER_DATA_HNDL_CLOSE); + } + + g_iClientParticleEntity[iClient] = EntIndexToEntRef(iParticleEntity); +} + +public Action RefireTauntParticle(Handle hTimer, DataPack hTauntDataPack) +{ + hTauntDataPack.Reset(); + + int iClient = GetClientOfUserId(hTauntDataPack.ReadCell()); + int iParticleIndex = hTauntDataPack.ReadCell(); + int iParticleEntity = EntRefToEntIndex(hTauntDataPack.ReadCell()); + + if (!iParticleIndex || iParticleEntity == -1 || !iClient || !IsClientInGame(iClient)) + { + return Plugin_Stop; + } + + char strEffectName[PLATFORM_MAX_PATH]; + + // https://forums.alliedmods.net/showthread.php?t=235329 + SetVariantString("ParticleEffectStop"); + AcceptEntityInput(iParticleEntity, "DispatchEffect"); + + TF2Econ_GetParticleAttributeSystemName(iParticleIndex, strEffectName, sizeof(strEffectName)); + CreateTempParticle(strEffectName, _, _, _, iParticleEntity, PATTACH_ABSORIGIN_FOLLOW, _, _); + TE_SendToAll(); + + return Plugin_Continue; +} + +public void TF2_OnConditionRemoved(int iClient, TFCond iCondition) +{ + if (iCondition != TFCond_Taunting) return; + + int iParticleEntity = EntRefToEntIndex(g_iClientParticleEntity[iClient]); + + if (iParticleEntity == -1) return; + + char strEntityClassname[64]; GetEntityClassname(iParticleEntity, strEntityClassname, sizeof(strEntityClassname)); + + if (StrEqual(strEntityClassname, "info_particle_system")) + { + if (IsValidEdict(iParticleEntity)) + { + RemoveEdict(iParticleEntity); + g_iClientParticleEntity[iClient] = -1; + } + } + else + { + if (IsValidEntity(iParticleEntity)) + { + RemoveEntity(iParticleEntity); + g_iClientParticleEntity[iClient] = -1; + } + } +} + +/* + I couldn't get unusual taunts working with attributes so I did this. + If anyone knows how to get them working with attributes, please tell how. +*/ + +stock int CreateAttachedParticle(int iClient, int iParticleIndex) +{ + UnusualTauntConfig UnusualTaunt; + bool bParticleSystem = false; + + int iUnusualConfigIndex = g_hUnusualTauntsList == null ? -1 : g_hUnusualTauntsList.FindValue(iParticleIndex, 0); + + if (iUnusualConfigIndex != -1) + { + g_hUnusualTauntsList.GetArray(iUnusualConfigIndex, UnusualTaunt); + bParticleSystem = UnusualTaunt.UseParticleSystem; + } + + // I just guessed it, nothing else + int iEntity = CreateEntityByName(bParticleSystem ? "info_particle_system" : "tf_wearable"); + + if (!(bParticleSystem ? IsValidEdict(iEntity) : IsValidEntity(iEntity))) return iEntity; + + float fPosition[3]; + GetEntPropVector(iClient, Prop_Send, "m_vecOrigin", fPosition); + TeleportEntity(iEntity, fPosition, NULL_VECTOR, NULL_VECTOR); + + char strEffectName[PLATFORM_MAX_PATH]; + + if (!TF2Econ_GetParticleAttributeSystemName(iParticleIndex, strEffectName, sizeof(strEffectName))) + { + LogError("Failed to get the system name of the particle attribute index. Removing entity."); + + bParticleSystem ? RemoveEdict(iEntity) : RemoveEntity(iEntity); + + return -1; + } + + if (!bParticleSystem) + { + DispatchSpawn(iEntity); + + // EF_BONEMERGE_FASTCULL moves the entity in an undesired position (in the middle of the client) + // Also setting all these netprops before dispatching spawn resets them for some reason + SetEntProp(iEntity, Prop_Send, "m_fEffects", EF_BONEMERGE | EF_NOSHADOW | EF_PARENT_ANIMATES | EF_NODRAW | EF_NORECEIVESHADOW); + SetEntProp(iEntity, Prop_Send, "m_CollisionGroup", 0); // COLLISION_GROUP_NONE + SetEntProp(iEntity, Prop_Send, "m_usSolidFlags", 0x0004); // FSOLID_NOT_SOLID + SetEntProp(iEntity, Prop_Send, "m_nSolidType", 0); // SOLID_NONE + SetEntProp(iEntity, Prop_Send, "m_bValidatedAttachedEntity", 1); // Visibility + + char strModelName[PLATFORM_MAX_PATH]; + GetEntPropString(iClient, Prop_Data, "m_ModelName", strModelName, sizeof(strModelName)); + SetEntityModel(iEntity, strModelName); + + SetVariantString("!activator"); + AcceptEntityInput(iEntity, "SetParent", iClient, iEntity); + + CreateTempParticle(strEffectName, fPosition, _, _, iEntity, PATTACH_ABSORIGIN_FOLLOW, _, _); + + TE_SendToAll(); + } + else + { + DispatchKeyValue(iEntity, "effect_name", strEffectName); + DispatchSpawn(iEntity); + ActivateEntity(iEntity); + + SetVariantString("!activator"); + AcceptEntityInput(iEntity, "SetParent", iClient, iEntity); + + AcceptEntityInput(iEntity, "Start"); + + if (UnusualTaunt.RefireInterval > 0) + { + char strBuffer[64]; + FormatEx(strBuffer, sizeof(strBuffer), "OnUser1 !self:Stop::%f:-1", UnusualTaunt.RefireInterval); + SetVariantString(strBuffer); + AcceptEntityInput(iEntity, "AddOutput"); + FormatEx(strBuffer, sizeof(strBuffer), "OnUser1 !self:Start::%f:-1", UnusualTaunt.RefireInterval + GetConVarFloat(g_hCvarVariation)); + SetVariantString(strBuffer); + AcceptEntityInput(iEntity, "AddOutput"); + FormatEx(strBuffer, sizeof(strBuffer), "OnUser1 !self:FireUser1::%f:-1", UnusualTaunt.RefireInterval); + SetVariantString(strBuffer); + AcceptEntityInput(iEntity, "AddOutput"); + AcceptEntityInput(iEntity, "FireUser1"); + } + } + + return iEntity; +} + +stock void CreateTempParticle(const char[] strParticle, + const float vecOrigin[3] = NULL_VECTOR, + const float vecStart[3] = NULL_VECTOR, + const float vecAngles[3] = NULL_VECTOR, + int iEntity = -1, + ParticleAttachmentType AttachmentType = PATTACH_ABSORIGIN, + int iAttachmentPoint = -1, + bool bResetParticles = false) +{ + int iParticleTable, iParticleIndex; + + iParticleTable = FindStringTable("ParticleEffectNames"); + + if (iParticleTable == INVALID_STRING_TABLE) + { + ThrowError("Could not find string table: ParticleEffectNames"); + } + + iParticleIndex = FindStringIndex(iParticleTable, strParticle); + + if (iParticleIndex == INVALID_STRING_INDEX) + { + LogError("Could not find particle index: %s. Trying to precache it now.", strParticle); + + iParticleIndex = GetPrecachedParticle(strParticle); + + if (iParticleIndex == INVALID_STRING_INDEX) + { + ThrowError("Could not find particle index: %s", strParticle); + } + } + + TE_Start("TFParticleEffect"); + TE_WriteFloat("m_vecOrigin[0]", vecOrigin[0]); + TE_WriteFloat("m_vecOrigin[1]", vecOrigin[1]); + TE_WriteFloat("m_vecOrigin[2]", vecOrigin[2]); + TE_WriteFloat("m_vecStart[0]", vecStart[0]); + TE_WriteFloat("m_vecStart[1]", vecStart[1]); + TE_WriteFloat("m_vecStart[2]", vecStart[2]); + TE_WriteVector("m_vecAngles", vecAngles); + TE_WriteNum("m_iParticleSystemIndex", iParticleIndex); + + if (iEntity != -1) + { + TE_WriteNum("entindex", iEntity); + } + + if (AttachmentType != PATTACH_ABSORIGIN) + { + TE_WriteNum("m_iAttachType", view_as(AttachmentType)); + } + + if (iAttachmentPoint != -1) + { + TE_WriteNum("m_iAttachmentPointIndex", iAttachmentPoint); + } + + TE_WriteNum("m_bResetParticles", bResetParticles ? 1 : 0); +} + +stock int GetPrecachedParticle(const char[] strParticleSystem) +{ + static int iParticleEffectNames = INVALID_STRING_TABLE; + + if ((iParticleEffectNames == INVALID_STRING_TABLE) && + ((iParticleEffectNames = FindStringTable("ParticleEffectNames")) == INVALID_STRING_TABLE)) + { + return INVALID_STRING_INDEX; + } + + int iParticleIndex = FindStringIndex(iParticleEffectNames, strParticleSystem); + + if (iParticleIndex == INVALID_STRING_INDEX) + { + int iNumStrings = GetStringTableNumStrings(iParticleEffectNames); + + if (iNumStrings >= GetStringTableMaxStrings(iParticleEffectNames)) + { + return INVALID_STRING_INDEX; + } + + AddToStringTable(iParticleEffectNames, strParticleSystem); + iParticleIndex = iNumStrings; + } + + return iParticleIndex; +} + +/* + The following localization functions are taken from + https://github.com/DoctorMcKay/sourcemod-plugins/blob/master/scripting/enhanced_items.sp +*/ + +bool LocalizeToken(const char[] strToken, char[] strOutput, int strMaxLen) +{ + if(g_hTokensMap == null) + { + LogError("Unable to localize token for server language!"); + + return false; + } + else + { + return g_hTokensMap.GetString(strToken, strOutput, strMaxLen); + } +} + +StringMap ParseLanguage(const char[] strLanguage) +{ + char strFilename[64]; + Format(strFilename, sizeof(strFilename), "resource/tf_%s.txt", strLanguage); + File hFile = OpenFile(strFilename, "r"); + + if(hFile == null) + { + return null; + } + + // The localization files are encoded in UCS-2, breaking all of our available parsing options + // We have to go byte-by-byte then line-by-line :( + + // This parser isn't perfect since some values span multiple lines, but since we're only interested in single-line values, this is sufficient + + StringMap hLang = new StringMap(); + hLang.SetString("__name__", strLanguage); + + int iData, i = 0; + char strLine[2048]; + + while(ReadFileCell(hFile, iData, 2) == 1) + { + if(iData < 0x80) + { + // It's a single-byte character + strLine[i++] = iData; + + if(iData == '\n') + { + strLine[i] = '\0'; + HandleLangLine(strLine, hLang); + i = 0; + } + } + else if(iData < 0x800) + { + // It's a two-byte character + strLine[i++] = (iData >> 6) | 0xC0; + strLine[i++] = (iData & 0x3F) | 0x80; + } + else if(iData < 0xFFFF && iData >= 0xD800 && iData <= 0xDFFF) + { + strLine[i++] = (iData >> 12) | 0xE0; + strLine[i++] = ((iData >> 6) & 0x3F) | 0x80; + strLine[i++] = (iData & 0x3F) | 0x80; + } + else if(iData >= 0x10000 && iData < 0x10FFFF) + { + strLine[i++] = (iData >> 18) | 0xF0; + strLine[i++] = ((iData >> 12) & 0x3F) | 0x80; + strLine[i++] = ((iData >> 6) & 0x3F) | 0x80; + strLine[i++] = (iData & 0x3F) | 0x80; + } + } + + delete hFile; + + return hLang; +} + +void HandleLangLine(char[] strLine, StringMap hLang) +{ + TrimString(strLine); + + if(strLine[0] != '"') + { + // Not a line containing at least one quoted string + return; + } + + char strToken[128], strValue[1024]; + int iPos = BreakString(strLine, strToken, sizeof(strToken)); + + if(iPos == -1) + { + // This line doesn't have two quoted strings + return; + } + + BreakString(strLine[iPos], strValue, sizeof(strValue)); + + if (StrContains(strToken, "Attrib_Particle") != -1) // Only particles should be added + { + hLang.SetString(strToken, strValue); + } +}