diff --git a/improvedvoicecommands/LICENSE b/improvedvoicecommands/LICENSE new file mode 100644 index 0000000..06e20eb --- /dev/null +++ b/improvedvoicecommands/LICENSE @@ -0,0 +1,7 @@ +Copyright 2022 DoopieWop + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/improvedvoicecommands/README.md b/improvedvoicecommands/README.md new file mode 100644 index 0000000..595093c --- /dev/null +++ b/improvedvoicecommands/README.md @@ -0,0 +1,33 @@ +# Improved Voice Commands + +## Description +This plugin enhances the default voice commands that come with Half-Life 2 Roleplay. It allows users to chain multiple voice commands together in one message. + +## Installation +:warning: **This plugin was made for the Half-Life 2 Roleplay schema!** The plugin might not work with other schemas. +- Download the folder +- Place it into the plugins folder inside the schema. +- Set up the in-game config to your desires. You can find the config options under the plugin name. + +## Features +- Chaining multiple voice commands together +- Radio support +- Dynamic sentence adaption (Experimental mode) +- Config options + +## Config Options +- Experimental Mode VC + - Adapts casing of voice commands according to the rest of the sentence + - Uses punctuation mark and casing for logic +- Radio VCAllow + - Determines if voice commands can be used on the radio. +- Radio VCClient Only + - Voice command playback on receivers will only be played for every receiver clientside, as such people around them won't hear the sounds. +- Radio VCVolume + - Determines the volume of the radio playback for receivers +- Separator VC + - The separator between voice commands. Can be anything, but it's best to choose a symbol, that isn't used often. Leave it empty if you want commands to be separated by spaces. This might not always work as expected for example with the voice command "10-8 Standing By" as "10-8" on its own is a voice command. + +### Developed By DoopieWop + +[My Steam Profile](https://steamcommunity.com/id/doopiewop/) \ No newline at end of file diff --git a/improvedvoicecommands/cl_plugin.lua b/improvedvoicecommands/cl_plugin.lua new file mode 100644 index 0000000..113dd57 --- /dev/null +++ b/improvedvoicecommands/cl_plugin.lua @@ -0,0 +1,5 @@ +netstream.Hook("PlayQueuedSound", function(entity, sounds, delay, spacing, volume, pitch) + entity = entity or LocalPlayer() + + ix.util.EmitQueuedSounds(entity, sounds, delay, spacing, volume, pitch) +end) \ No newline at end of file diff --git a/improvedvoicecommands/sh_plugin.lua b/improvedvoicecommands/sh_plugin.lua new file mode 100644 index 0000000..0c00f4d --- /dev/null +++ b/improvedvoicecommands/sh_plugin.lua @@ -0,0 +1,22 @@ +PLUGIN.name = "Improved Voice Commands" +PLUGIN.description = "Let's users string multiple voice commands together." +PLUGIN.author = "DoopieWop" +PLUGIN.schema = "HL2 RP" +PLUGIN.license = [[ +Copyright 2022 DoopieWop + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +]] + +ix.config.Add("experimentalModeVC", false, "This won't always work as expected!!! Adjusts voice command texts to \"fit\" to the rest of the text. If the text continues after a voice command it will remove the end symbol etc.", nil, {category = PLUGIN.name}) +ix.config.Add("separatorVC", "|", "Separator symbol between voice commands and text. Leave empty for using spaces as separator. (Using spaces might cause unforseen consequences, 10-8 Standing By will not be recognized, instead it will use the 10-8 voice command and then play the Standing By voice command.", nil, {category = PLUGIN.name}) +ix.config.Add("radioVCAllow", true, "Allow voice commands to be used on the radio. This will playback the voice commands on all the recevers.", nil, {category = PLUGIN.name}) +ix.config.Add("radioVCVolume", 60, "Sets the volume, radio voice commands are played back for receivers. This is lower than normal as it's coming through a radio.", nil, {category = PLUGIN.name, data = {min = 0, max = 200}}) +ix.config.Add("radioVCClientOnly", false, "If set to true, radio voice commands receivers will hear the voice commands only clientside, so nobody around them can hear it.", nil, {category = PLUGIN.name}) + +ix.util.Include("cl_plugin.lua") +ix.util.Include("sv_plugin.lua") \ No newline at end of file diff --git a/improvedvoicecommands/sv_plugin.lua b/improvedvoicecommands/sv_plugin.lua new file mode 100644 index 0000000..2c683fa --- /dev/null +++ b/improvedvoicecommands/sv_plugin.lua @@ -0,0 +1,179 @@ +-- I apologise for the amount of loops in all of this lol, also for the lack of comments, i tend to change my code a lot. +local PLUGIN = PLUGIN +PLUGIN.TempStored = PLUGIN.TempStored or {} + +-- if no separator then just seperate at spaces +local function GetVoiceCommands(text, class, separator) + local strings = string.Explode(separator or " ", text) + local finaltable = {} + local usedkeys = {} + + for k, v in ipairs(strings) do + if usedkeys[k] then continue end + + v = string.Trim(v) + + local info = Schema.voices.Get(class, v) + + if !info then + if !separator then + local combiner + local temp = {} + + for i = k, #strings do + combiner = combiner and combiner .. " " .. strings[i] or strings[i] + + info = Schema.voices.Get(class, combiner) + + temp[i] = true + + if info then + usedkeys = temp + break + end + end + end + end + table.insert(finaltable, !info and {text = v} or table.Copy(info)) + end + return finaltable +end + +local function ExperimentalFormatting(stringtabl) + local carry + -- carry like in mathematical equations :) + -- the point of the carry is to move question marks or exclamation marks to the end of the text + for k, v in ipairs(stringtabl) do + local before, after = stringtabl[k - 1] and k - 1, stringtabl[k + 1] and k + 1 + + -- if we are not a voice command, check if we have someone before us, cuz if we do and they are a voice command than only they can have the carry symbol set + if !v.sound then + if before and carry and stringtabl[before].sound and string.sub(stringtabl[before].text, #stringtabl[before].text, #stringtabl[before].text) != "," then + local text = stringtabl[before].text + stringtabl[before].text = string.SetChar(text, #text, carry) + carry = nil + end + -- we only want voice commands to be corrected + continue + end + + -- if there is a string before us adjust the casing of our first letter according to the before's symbol + if before then + local sub = string.sub(stringtabl[before].text, #stringtabl[before].text, #stringtabl[before].text) + local case = string.lower(string.sub(v.text, 1, 1)) + + if sub == "!" or sub == "." or sub == "?" then + case = string.upper(string.sub(v.text, 1, 1)) + end + + v.text = string.SetChar(v.text, 1, case) + end + + -- if there is a string after us adjust our symbol to their casing. if they are a vc always adjust to comma, if they are not, check if the message starts with a lower casing letter, indicating a conntinuation of the sentence + if after then + local firstletterafter = string.sub(stringtabl[after].text, 1, 1) + local endsub = string.sub(v.text, #v.text, #v.text) + + if stringtabl[after].sound or string.match(firstletterafter, "%l") then + if endsub == "!" or endsub == "." or endsub == "?" then + v.text = string.SetChar(v.text, #v.text, ",") + if stringtabl[after].sound and endsub != "." then + carry = carry == nil and endsub or carry + end + end + end + end + + -- we are a vc so we can also set the carry to us + if carry then + if !after then + v.text = string.SetChar(v.text, #v.text, carry) + carry = nil + continue + end + end + end + return stringtabl +end + +function Schema:PlayerMessageSend(speaker, chatType, text, anonymous, receivers, rawText) + local separator = ix.config.Get("separatorVC", nil) != "" and ix.config.Get("separatorVC", nil) or nil + + if chatType == "ic" or chatType == "w" or chatType == "y" or chatType == "dispatch" or (ix.config.Get("radioVCAllow", true) and chatType == "radio") then + local class = self.voices.GetClass(speaker) + + for k, v in pairs(class) do + local texts = GetVoiceCommands(rawText, v, separator) + local isGlobal = false + local completetext + local sounds = {} + if ix.config.Get("experimentalModeVC", false) == true then + texts = ExperimentalFormatting(texts) + end + for k2, v2 in ipairs(texts) do + if v2.sound then + if v2.global then + isGlobal = true + end + table.insert(sounds, v2.sound) + end + + local volume = isGlobal and 0 or 80 + if chatType == "w" then + volume = 60 + elseif chatType == "y" then + volume = 150 + end + + completetext = completetext and completetext .. " " .. v2.text or v2.text + + if k2 == #texts then + if table.IsEmpty(sounds) then break end + + if speaker:IsCombine() and !isGlobal then + speaker.bTypingBeep = nil + table.insert(sounds, "NPC_MetroPolice.Radio.Off") + end + + local _ = !isGlobal and ix.util.EmitQueuedSounds(speaker, sounds, nil, nil, volume) or netstream.Start(nil, "PlayQueuedSound", nil, sounds, nil, nil, volume) + + if chatType == "radio" then + volume = ix.config.Get("radioVCVolume", 60) + if ix.config.Get("radioVCClientOnly", false) == true then + netstream.Start(receivers, "PlayQueuedSound", nil, sounds, nil, nil, volume) + else + for k3, v3 in pairs(receivers) do + if v3 == speaker then continue end + ix.util.EmitQueuedSounds(v3, sounds, nil, nil, volume) + end + end + end + + text = completetext + + goto exit + end + end + end + + ::exit:: + + PLUGIN.TempStored[CurTime()] = text + + if speaker:IsCombine() then + if chatType != "radio" then + return string.format("<:: %s ::>", text) + end + end + return text + end + + -- this isnt optimal but it works + if chatType == "radio_eavesdrop" then + if PLUGIN.TempStored[CurTime()] then + text = PLUGIN.TempStored[CurTime()] + PLUGIN.TempStored[CurTime()] = nil + end + end + return text +end diff --git a/tposefixer.lua b/tposefixer.lua new file mode 100644 index 0000000..ff92b70 --- /dev/null +++ b/tposefixer.lua @@ -0,0 +1,104 @@ +local PLUGIN = PLUGIN + +PLUGIN.name = "T-Pose Fixer" +PLUGIN.author = "DoopieWop" +PLUGIN.description = "Attempts to fix T-Posing for models." +PLUGIN.license = [[ +MIT License + +Copyright (c) 2025 DoopieWop + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +]] +PLUGIN.cached = PLUGIN.cached or {} + +local translations = { + male_shared = "citizen_male", + female_shared = "citizen_female", + police_animations = "metrocop", + combine_soldier_anims = "overwatch", + vortigaunt_anims = "vortigaunt", + m_anm = "player", + f_anm = "player", +} + +local og = ix.anim.SetModelClass +function ix.anim.SetModelClass(model, class) + if (!ix.anim[class]) then return end + + PLUGIN.cached[model:lower()] = class + + og(model, class) +end + +local function UpdateAnimationTable(client) + local baseTable = ix.anim[client.ixAnimModelClass] or {} + + client.ixAnimTable = baseTable[client.ixAnimHoldType] + client.ixAnimGlide = baseTable["glide"] +end + +function PLUGIN:PlayerModelChanged(ply, model) + timer.Simple(0, function() + if not IsValid(ply) then + return + end + + model = model:lower() + + if not self.cached[model] then + local submodels = ply:GetSubModels() + for k, v in ipairs(submodels) do + local class = v.name:gsub(".*/([^/]+)%.%w+$", "%1"):lower() + if translations[class] then + ix.anim.SetModelClass(model, translations[class]) + break + end + end + end + + ply.ixAnimModelClass = ix.anim.GetModelClass(model) + + UpdateAnimationTable(ply) + end) + + return true +end + +function PLUGIN:OnReloaded() + for k, v in pairs(self.cached) do + ix.anim.SetModelClass(k, v) + end +end + +if SERVER then + util.AddNetworkString("TPoseFixerSync") + + function PLUGIN:PlayerInitialSpawn(client) + net.Start("TPoseFixerSync") + net.WriteTable(self.cached) + net.Send(client) + end +else + net.Receive("TPoseFixerSync", function() + for k, v in pairs(net.ReadTable()) do + ix.anim.SetModelClass(k, v) + end + end) +end