Files
wnsrc/gamemodes/darkrp/plugins/arccwbase/sh_plugin.lua
lifestorm 6a58f406b1 Upload
2024-08-04 23:54:45 +03:00

445 lines
14 KiB
Lua

--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
local PLUGIN = PLUGIN;
PLUGIN.name = "ArcCW Base";
PLUGIN.description = "Adds compatibility with ArcCW, integrating the weapons and attachments with helix items. Also links ArcCW SWEPs spread, recoil and sway to characters \"gun\" skill.";
PLUGIN.author = "Gr4Ss & LegAz";
if (!ArcCW) then return end
PLUGIN.attachmentTranslate = {}
for k, v in pairs(ArcCW.AttachmentTable) do
local ITEM = ix.item.Register(k, nil, false, nil, true)
ITEM.name = v.PrintName
ITEM.category = "Attachment (ArcCW)"
ITEM.model = "models/props_lab/box01a.mdl"
ITEM.description = "A box containing a "..v.PrintName.." attachment."
end
ix.util.Include("sh_config.lua") -- moving config into another plugin just for visuals @offlegaz
ix.util.Include("sv_plugin.lua")
ix.util.Include("cl_plugin.lua")
ix.config.Add("forceUseMagazineSystem", true, "Should magazines be mandatory for item weapons, or is it OK to fall back to the Source ammo pool?", nil, {
category = "general",
})
if (CLIENT) then
function ArcCW:PlayerTakeAtt(ply, att, amt) end
function ArcCW:PlayerGiveAtt(ply, att, amt) end
end
ix.config.Add("shootDbLevelReduction", 0, "How many DB to reduce the ARCCW sound levels by (this effects how far away it can be heard).", nil, {
data = {min = 1, max = 50, decimals = 1},
category = "general"
})
ix.config.Add("shootOtherDbLevelReduction", 50, "How many DB to reduce the ARCCW sound levels by (non gunshot sound effects).", nil, {
data = {min = 1, max = 100, decimals = 1},
category = "general"
})
ix.config.Add("shootOtherVolumeLevelReduction", 0.5, "How much to reduce the ARCCW sound levels by (non gunshot sound effects).", nil, {
data = {min = 0, max = 1, decimals = 3},
category = "general"
})
ix.config.Add("shootDbLevelReductionClose", 50, "How many DB to reduce the ARCCW sound levels by (this effects how far away close sounds can be heard).", nil, {
data = {min = 1, max = 100, decimals = 1},
category = "general"
})
ix.config.Add("shootVolumeReduction", 0, "How much to reduce the ARCCW sound levels by (this effects how far loud it is).", nil, {
data = {min = 0, max = 1, decimals = 3},
category = "general"
})
ix.config.Add("shootSoundDSP", 2, "Which DSP preset to use for close sounds (don't change this unless you know what you're doing!)", nil, {
data = {min = 0, max = 140, decimals = 0},
category = "general"
})
ix.config.Add("shootSoundDSPDistant", 31, "Which DSP preset to use for distant sounds (don't change this unless you know what you're doing!)", nil, {
data = {min = 0, max = 140, decimals = 0},
category = "general"
})
ix.config.Add("burnTime", 3, "How long someone should be ignited for after they've been lit up by an incendiary grenade", nil, {
data = {min = 0, max = 60, decimals = 1},
category = "general"
})
--[[
sound overrides for ArcCW
]]
local sndTbl = {} -- a table which we can use to queue up sounds asynchronously
local function networkAllPendingArcSounds()
while true do
for i, snd in ipairs(sndTbl) do
if (!snd.filter) then
snd.ent:EmitSound(snd.fsound, snd.lvl, 100, snd.vol, CHAN_STATIC, SND_NOFLAGS,
snd.useDSP and snd.dsp or 0)
else
local _snd = CreateSound(snd.ent, snd.fsound, snd.filter)
_snd:SetSoundLevel(snd.lvl)
if (snd.useDSP) then
_snd:SetDSP(snd.dsp or 0)
end
_snd:PlayEx(snd.vol, 255)
end
table.remove(sndTbl, i)
end
coroutine.yield()
end
end
local function insertArcSound(ent, fsound, filter, lvl, vol, dsp, useDSP)
sndTbl[#sndTbl + 1] = {
ent = ent,
fsound = fsound,
filter = filter,
lvl = lvl,
vol = vol,
dsp = dsp,
useDSP = useDSP
}
end
local sndCO = coroutine.create(networkAllPendingArcSounds)
if (SERVER) then
timer.Create("ixNetworkArcCWSounds", 0.1, 0, function()
-- gun sounds are kind of low-priority. improves S2K performance if we don't block the main thread with them
if (!sndCO or !coroutine.resume(sndCO)) then
-- there is something wrong with the table.Add
ErrorNoHalt("ArcCW sound coroutine failed to resume. Attempting to recreate coroutine.\n")
sndTbl = {}
sndCO = coroutine.create(networkAllPendingArcSounds)
end
end)
end
local function isShootSound(ent, fsound)
if (fsound == ent.ShootSound or fsound == ent.ShootSoundSilenced or fsound == ent.DistantShootSound) then
return true
end
return false
end
timer.Simple(1, function()
local tbl = weapons.GetStored("arccw_base") -- baller way of shoving our own shit code into arccw
-- ^^ make sure we're the last to load so the override actually works
function tbl:MyEmitSound(fsound, level, pitch, _vol, chan, useWorld)
if !fsound then return end
fsound = self:GetBuff_Hook("Hook_TranslateSound", fsound) or fsound
local client = self:GetOwner()
local weapon = client:GetActiveWeapon()
local useDSP = isShootSound(weapon, fsound) -- don't play DSP on reloading and equipping sfx
if istable(fsound) then fsound = self:TableRandom(fsound) end
if fsound and fsound != "" then
local lvl = math.Clamp(level or 150 - ix.config.Get("shootDbLevelReductionClose", 10), 60, 160)
local vol = _vol or 1 - ix.config.Get("shootVolumeReduction", 0.1)
local dsp = ix.config.Get("shootSoundDSP", 2)
local lvl2 = math.Clamp(level or 150 - ix.config.Get("shootDbLevelReduction", 60), 60, 160)
local vol2 = math.Clamp(_vol or 1 - ix.config.Get("shootVolumeReduction", 0.1) - 0.1, 0, 1)
local dsp2 = ix.config.Get("shootSoundDSPDistant", 31)
if (!useDSP) then
lvl = math.Clamp(lvl - ix.config.Get("shootOtherDbLevelReduction", 50), 60, 160)
vol = vol - ix.config.Get("shootOtherVolumeLevelReduction", 0.1)
end
if (CLIENT and client == LocalPlayer()) then
-- client plays their own sound
-- nevermind, we can't do this because i can't use CRecipientFilters to filter out the client serverside
-- nevermind, apparently they don't duplicate any more ?????????????
-- TODO: reimplement when that feature makes it to main gmod branch
weapon:EmitSound(fsound, level, pitch, vol, chan or CHAN_STATIC)
elseif (client and SERVER) then
-- filter for distant sounding gunshots
if (useDSP) then
local filter = RecipientFilter()
filter:AddAllPlayers()
filter:RemovePlayer(client)
filter:RemovePVS(client:GetPos())
insertArcSound(weapon, fsound, filter, lvl2, vol2, dsp2, true)
end
weapon:EmitSound(fsound, lvl, 100, vol, CHAN_STATIC, SND_NOFLAGS, dsp)
end
end
end
end)
function ArcCW:PlayerGetAtts(client, attachment)
if (!IsValid(client)) then return 0 end
if (GetConVar("arccw_attinv_free"):GetBool()) then return 999 end
if (attachment == "") then return 999 end
local attachmentData = ArcCW.AttachmentTable[attachment]
if (!attachmentData) then return 0 end
if (attachmentData.Free) then return 999 end
local character = client:GetCharacter()
if (!character) then return 0 end
if (!client:IsAdmin() and attachmentData.AdminOnly) then
return 0
end
if (attachmentData.InvAtt) then
attachment = attachmentData.InvAtt
end
local weapon = client:GetActiveWeapon()
if (SERVER and client.ixAttachAttempt and
client.ixAttachAttempt.attachment == attachment and
client.ixAttachAttempt.time + 1 > CurTime()) then
weapon = client.ixAttachAttempt.weapon
end
local amount = character:GetInventory():GetItemCount(PLUGIN.attachmentTranslate[attachment] or attachment)
for _, v in ipairs(weapon:GetNetVar("ixItemDefaultWeaponAtts", {})) do
if (v == attachment) then amount = amount + 1 end
end
if (SERVER and weapon.ixItem) then
for _, v in pairs(weapon.ixItem:GetData("WeaponAttachments", {})) do
if (v.attachment == attachment) then
amount = amount + 1
break
end
end
elseif (CLIENT and weapon:GetNetVar("ixItemID")) then
local item = ix.item.instances[weapon:GetNetVar("ixItemID")]
if (item) then
for _, v in pairs(item:GetData("WeaponAttachments", {})) do
if (v.attachment == attachment) then
amount = amount + 1
break
end
end
end
end
return amount
end
function PLUGIN:ArcCW_PlayerCanAttach(client, weapon, attachment, slot, detach)
client.ixAttachAttempt = nil
client.ixDeattachAttempt = nil
if (detach) then
client.ixDeattachAttempt = {
weapon = weapon,
attachment = attachment,
slot = slot
}
return
end
if (attachment == "") then return end
local weapon2 = client.ixAttachWeapon or client:GetActiveWeapon()
if (weapon2 != weapon) then return end
client.ixAttachAttempt = {
weapon = weapon,
attachment = attachment,
slot = slot,
time = CurTime()
}
for _, v in ipairs(weapon:GetNetVar("ixItemDefaultWeaponAtts", {})) do
if (v == attachment) then
client.ixAttachAttempt.mode = "default"
return true
end
end
if (weapon.ixItem) then
for _, v in pairs(weapon.ixItem:GetData("WeaponAttachments", {})) do
if (v.attachment == attachment) then
client.ixAttachAttempt.mode = "reattach"
return true
end
end
end
local character = client:GetCharacter()
if (character) then
local inventory = character:GetInventory()
if (self.attachmentTranslate[attachment]) then
if (!inventory:HasItem(self.attachmentTranslate[attachment])) then
return false
end
elseif (!inventory:HasItem(attachment)) then
return false
end
end
client.ixAttachAttempt.mode = "item"
return true
end
-- Give one ammo otherwise ArcCW doesn't let us reload
function PLUGIN:KeyPress(client, key)
if (key == IN_RELOAD) then
local weapon = client:GetActiveWeapon()
if (IsValid(weapon) and weapon:GetNetVar("ixItemID") and client:GetAmmoCount(weapon:GetPrimaryAmmoType()) == 0) then
if (client.ixPreReloadAmmoGiven) then
-- We still have 1 ammo from some previous attempt, lets remove this first!
client:SetAmmo(math.max(client:GetAmmoCount(client.ixPreReloadAmmoGiven) - 1, 0), client.ixPreReloadAmmoGiven)
end
client:SetAmmo(1, weapon:GetPrimaryAmmoType())
client.ixPreReloadAmmoGiven = weapon:GetPrimaryAmmoType()
end
end
end
-- Take the ammo again if it wasn't used
function PLUGIN:KeyRelease(client, key)
if (client.ixPreReloadAmmoGiven) then
client:SetAmmo(client:GetAmmoCount(client.ixPreReloadAmmoGiven) - 1, client.ixPreReloadAmmoGiven)
client.ixPreReloadAmmoGiven = nil
end
end
function PLUGIN:PlayerSwitchWeapon(client, oldWeapon, newWeapon)
if (client.ixPreReloadAmmoGiven) then
client:SetAmmo(client:GetAmmoCount(client.ixPreReloadAmmoGiven) - 1, client.ixPreReloadAmmoGiven)
client.ixPreReloadAmmoGiven = nil
end
end
local sortFunc = function(a, b)
local aAmmo, bAmmo = a:GetAmmo(), b:GetAmmo()
if (aAmmo != bAmmo) then
return aAmmo > bAmmo
else
return a:GetID() > b:GetID()
end
end
function PLUGIN:Hook_PreReload(weapon)
local client = weapon:GetOwner()
if (!IsValid(client) or client:IsNPC()) then return end
-- Take the 1 ammo given to let us get past the 'not zero ammo' check that happens before this hook
if (client.ixPreReloadAmmoGiven) then
client:SetAmmo(client:GetAmmoCount(client.ixPreReloadAmmoGiven) - 1, client.ixPreReloadAmmoGiven)
client.ixPreReloadAmmoGiven = nil
end
local character = client:GetCharacter()
if (!character) then return end
if (weapon:GetNetVar("ixItemID") == nil) then return end
local itemID = weapon:GetNetVar("ixItemID")
local item = ix.item.instances[itemID]
if (!item) then return end
local ammoID = weapon:GetPrimaryAmmoType()
local ammoName = string.lower(game.GetAmmoName(ammoID) or "")
if (ammoName == "") then return end
local ammoItems = {}
for k, v in ipairs(character:GetInventory():GetItemsByBase("base_arccwmag")) do
if (item.magazines and item.magazines[v.uniqueID] and v:GetAmmo() > 0) then
ammoItems[#ammoItems + 1] = v
end
end
table.sort(ammoItems, sortFunc)
local ammoItem = ammoItems[1]
if (!ammoItem) then return ix.config.Get("forceUseMagazineSystem") end
local chamber = math.Clamp(weapon:Clip1(), 0, weapon:GetChamberSize())
if (SERVER) then
if (weapon.ixItem:GetData("reloadMagazineItem")) then
self:RefundAmmoItem(weapon, character, weapon.ixItem:GetData("reloadMagazineItem"), weapon:Clip1() + client:GetAmmoCount(ammoID) - chamber, weapon.ixItem:GetData("reloadMagazineInvPos"))
weapon:SetClip1(chamber)
end
weapon.ixItem:SetData("reloadMagazineItem", ammoItem:GetID())
weapon.ixItem:SetData("reloadMagazineInvPos", {ammoItem.invID, ammoItem.gridX, ammoItem.gridY})
ammoItem:Transfer(nil, nil, nil, client, nil, true)
end
client:SetAmmo(ammoItem:GetAmmo(), ammoID)
end
function PLUGIN:Hook_ModDispersion(weapon, hip)
local weaponOwner = weapon:GetOwner()
if (weaponOwner:IsPlayer()) then
weaponOwner = weaponOwner:GetCharacter()
else
return
end
local skillLevel = weaponOwner:GetSkillLevel("guns")
local minSkillLevel, maxSkillLevel = ix.weapons:GetWeaponSkillRequired(weapon:GetClass())
if (skillLevel < minSkillLevel) then
return hip + math.ceil(hip * self.spreadMaxIncrease) * (1 - skillLevel / minSkillLevel)
end
skillLevel = math.min(skillLevel, maxSkillLevel)
return hip - math.floor(hip * self.spreadMaxDecrease) * ((skillLevel - minSkillLevel) / (maxSkillLevel - minSkillLevel))
end
function PLUGIN:Hook_ModifyRecoil(weapon, rec)
local weaponOwner = weapon:GetOwner()
if (weaponOwner:IsPlayer()) then
weaponOwner = weaponOwner:GetCharacter()
else
return
end
local skillLevel = weaponOwner:GetSkillLevel("guns")
local minSkillLevel, maxSkillLevel = ix.weapons:GetWeaponSkillRequired(weapon:GetClass())
local modifier
if (skillLevel < minSkillLevel) then
modifier = PLUGIN.recoilMaxIncrease * (1 - skillLevel / minSkillLevel)
else
skillLevel = math.min(skillLevel, maxSkillLevel)
modifier = -(PLUGIN.recoilMaxDecrease * ((skillLevel - minSkillLevel) / (maxSkillLevel - minSkillLevel)))
end
rec.Recoil = rec.Recoil + (rec.Recoil * modifier)
rec.RecoilSide = rec.RecoilSide + (rec.RecoilSide * modifier)
-- rec.VisualRecoilMul = rec.VisualRecoilMul + (rec.VisualRecoilMul * modifier)
return rec
end
function PLUGIN:O_Hook_Override_CanBash()
return false
end