Files
wnsrc/lua/weapons/arccw_base/sh_attach.lua
lifestorm df294d03aa Upload
2024-08-04 23:54:45 +03:00

1777 lines
49 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/
--]]
-- Used to prevent stack overflow. Set false here so luarefresh clears it
ArcCW.BuffStack = false
ArcCW.ConVar_BuffMults = {
["Mult_Damage"] = "arccw_mult_damage",
["Mult_DamageMin"] = "arccw_mult_damage",
["Mult_DamageNPC"] = "arccw_mult_npcdamage",
["Mult_HipDispersion"] = "arccw_mult_hipfire",
["Mult_ReloadTime"] = "arccw_mult_reloadtime",
["Mult_SightTime"] = "arccw_mult_sighttime",
["Mult_RPM"] = "arccw_mult_rpm",
["Mult_CycleTime"] = "arccw_mult_rpm",
["Mult_Range"] = "arccw_mult_range",
["Mult_Recoil"] = "arccw_mult_recoil",
["Mult_MoveDispersion"] = "arccw_mult_movedisp",
["Mult_AccuracyMOA"] = "arccw_mult_accuracy",
["Mult_Penetration"] = "arccw_mult_penetration",
["Mult_Sway"] = "arccw_mult_sway",
["Mult_MeleeDamage"] = "arccw_mult_meleedamage",
["Mult_MeleeTime"] = "arccw_mult_meleetime",
}
ArcCW.ConVar_BuffAdds = {
["Add_Sway"] = "arccw_add_sway",
}
ArcCW.ConVar_BuffOverrides = {
["Override_ShootWhileSprint"] = "arccw_mult_shootwhilesprinting"
}
SWEP.TickCache_Overrides = {}
SWEP.TickCache_Adds = {}
SWEP.TickCache_Mults = {}
SWEP.TickCache_Hooks = {}
SWEP.TickCache_IsShotgun = nil
SWEP.TickCache_Tick_Overrides = {}
SWEP.TickCache_Tick_Adds = {}
SWEP.TickCache_Tick_Mults = {}
SWEP.AttCache_Hooks = {}
-- debug: enable/disable modified caching
local MODIFIED_CACHE = true
-- print if a variable presumed to never change actually changes (this also happens right after attaching/detaching)
-- only works if MODIFIED_CACHE is false
local VERIFY_MODIFIED_CACHE = false
-- Conditions not listed are are presumed to never change; this is done for optimization purposes
SWEP.ModifiedCache = {}
function SWEP:RecalcAllBuffs()
self.TickCache_Overrides = {}
self.TickCache_Adds = {}
self.TickCache_Mults = {}
self.TickCache_Hooks = {}
self.TickCache_IsShotgun = nil
self.TickCache_Tick_Overrides = {}
self.TickCache_Tick_Adds = {}
self.TickCache_Tick_Mults = {}
self.ReferencePosCache = {}
self.AttCache_Hooks = {}
self.NextMalfunction = nil
-- for the customization page
if CLIENT then
self.Infos_Stats = nil
self.Infos_Ballistics = nil
self.Infos_Breakpoints = nil
end
-- this function is not always called right before AdjustAtts
--self.ModifiedCache = {}
end
function SWEP:GetIsShotgun()
if self.TickCache_IsShotgun == nil then
local shotgun = self:GetBuff_Override("Override_IsShotgun")
if shotgun != nil then
self.TickCache_IsShotgun = shotgun
end
local num = self.Num
if self.TickCache_IsShotgun == nil and num > 1 then self.TickCache_IsShotgun = true end
end
return self.TickCache_IsShotgun
end
function SWEP:GetIsManualAction()
local manual = self:GetBuff_Override("Override_ManualAction")
if manual != false then
manual = manual or self.ManualAction
end
-- A manual action gun CAN have automatic firemode, this is intended behavior!!!
-- It's used for slamfiring
--[[]
local mode = self:GetCurrentFiremode().Mode
if mode != 0 and mode != 1 then
return false
end
]]
return manual
end
-- ONE FUNCTION TO RULE THEM ALL
function SWEP:GetBuff(buff, defaultnil, defaultvar)
local stable = self:GetTable()
local result = stable[buff] or defaultvar
if !result and defaultnil then
result = nil
elseif !result then
result = 1
end
result = self:GetBuff_Override("Override_" .. buff, result)
if isnumber(result) then
result = self:GetBuff_Add("Add_" .. buff) + result
result = self:GetBuff_Mult("Mult_" .. buff) * result
end
return result
end
function SWEP:GetBuff_Stat(buff, slot)
local slottbl = self.Attachments[slot]
if !slottbl then return end
local atttbl = ArcCW.AttachmentTable[slottbl.Installed]
if !atttbl then return end
local num = slottbl.ToggleNum or 1
if atttbl.ToggleStats and atttbl.ToggleStats[num] and (atttbl.ToggleStats[num][buff] != nil) then
return atttbl.ToggleStats[num][buff]
else
return atttbl[buff]
end
end
function SWEP:GetBuff_Hook(buff, data, defaultnil)
-- call through hook function, args = data. return nil to do nothing. return false to prevent thing from happening.
if !self.AttCache_Hooks[buff] then
self.AttCache_Hooks[buff] = {}
for i, k in pairs(self.Attachments) do
if !k.Installed then continue end
local atttbl = ArcCW.AttachmentTable[k.Installed]
if !atttbl then continue end
if isfunction(atttbl[buff]) then
table.insert(self.AttCache_Hooks[buff], {atttbl[buff], atttbl[buff .. "_Priority"] or 0})
elseif atttbl.ToggleStats and k.ToggleNum and atttbl.ToggleStats[k.ToggleNum] and isfunction(atttbl.ToggleStats[k.ToggleNum][buff]) then
table.insert(self.AttCache_Hooks[buff], {atttbl.ToggleStats[k.ToggleNum][buff], atttbl.ToggleStats[k.ToggleNum][buff .. "_Priority"] or 0})
end
end
local cfm = self:GetCurrentFiremode()
if cfm and isfunction(cfm[buff]) then
table.insert(self.AttCache_Hooks[buff], {cfm[buff], cfm[buff .. "_Priority"] or 0})
end
for i, e in pairs(self:GetActiveElements()) do
local ele = self.AttachmentElements[e]
if ele and ele[buff] then
table.insert(self.AttCache_Hooks[buff], {ele[buff], ele[buff .. "_Priority"] or 0})
end
end
if isfunction(self:GetTable()[buff]) then
table.insert(self.AttCache_Hooks[buff], {self:GetTable()[buff], self:GetTable()[buff .. "_Priority"] or 0})
end
table.sort(self.AttCache_Hooks[buff], function(a, b) return a[2] >= b[2] end)shouldsort = true
end
local retvalue = nil
for i, k in ipairs(self.AttCache_Hooks[buff]) do
local ret = k[1](self, data)
if ret == false then
return
elseif ret != nil then
retvalue = ret
break
end
end
if retvalue then data = retvalue
elseif defaultnil then data = nil end
data = hook.Call(buff, nil, self, data) or data
return data
end
function SWEP:GetBuff_Override(buff, default)
local level = 0
local current = nil
local winningslot = nil
if MODIFIED_CACHE and !self.ModifiedCache[buff] then
-- ArcCW.ConVar_BuffOverrides[buff] isn't actually implemented??
if !ArcCW.BuffStack then
ArcCW.BuffStack = true
local out = (self:GetBuff_Hook("O_Hook_" .. buff, {buff = buff}) or {})
current = out.current or current
winningslot = out.winningslot or winningslot
ArcCW.BuffStack = false
end
return current or default, winningslot
end
if self.TickCache_Overrides[buff] then
current = self.TickCache_Overrides[buff][1]
winningslot = self.TickCache_Overrides[buff][2]
local data = {
buff = buff,
current = current,
winningslot = winningslot
}
if !ArcCW.BuffStack then
ArcCW.BuffStack = true
local out = (self:GetBuff_Hook("O_Hook_" .. buff, data) or {})
current = out.current or current
winningslot = out.winningslot or winningslot
ArcCW.BuffStack = false
end
if current == nil then
return default
else
return current, winningslot
end
end
for i, k in pairs(self.Attachments) do
if !k.Installed then continue end
local atttbl = ArcCW.AttachmentTable[k.Installed]
if !atttbl then continue end
if atttbl[buff] != nil then
local pri = atttbl[buff .. "_Priority"] or 1
if level == 0 or (pri > level) then
current = atttbl[buff]
level = pri
winningslot = i
end
end
if atttbl.ToggleStats and k.ToggleNum and atttbl.ToggleStats[k.ToggleNum] and atttbl.ToggleStats[k.ToggleNum][buff] then
local pri = atttbl.ToggleStats[k.ToggleNum][buff .. "_Priority"] or 1
if level == 0 or (pri > level) then
current = atttbl.ToggleStats[k.ToggleNum][buff]
level = pri
winningslot = i
end
end
end
if !ArcCW.BuffStack then
ArcCW.BuffStack = true
local cfm = self:GetCurrentFiremode()
if cfm and cfm[buff] != nil then
local pri = cfm[buff .. "_Priority"] or 1
if level == 0 or (pri > level) then
current = cfm[buff]
level = pri
end
end
ArcCW.BuffStack = false
end
if !ArcCW.BuffStack then
ArcCW.BuffStack = true
for i, e in pairs(self:GetActiveElements()) do
local ele = self.AttachmentElements[e]
if ele and ele[buff] != nil then
local pri = ele[buff .. "_Priority"] or 1
if level == 0 or (pri > level) then
current = ele[buff]
level = pri
winningslot = i
end
end
end
ArcCW.BuffStack = false
end
if self:GetTable()[buff] != nil then
local pri = self:GetTable()[buff .. "_Priority"] or 1
if level == 0 or (pri > level) then
current = self:GetTable()[buff]
level = pri
end
end
self.TickCache_Overrides[buff] = {current, winningslot}
if VERIFY_MODIFIED_CACHE and !self.ModifiedCache[buff] and current != nil then
print("ArcCW: Presumed non-changing buff '" .. buff .. "' is modified (" .. tostring(current) .. ")!")
end
local data = {
buff = buff,
current = current,
winningslot = winningslot
}
if !ArcCW.BuffStack then
ArcCW.BuffStack = true
current = (self:GetBuff_Hook("O_Hook_" .. buff, data) or {}).current or current
ArcCW.BuffStack = false
end
if current == nil then
current = default
end
return current, winningslot
end
function SWEP:GetBuff_Mult(buff)
local mult = 1
if MODIFIED_CACHE and !self.ModifiedCache[buff] then
if !ArcCW.BuffStack then
ArcCW.BuffStack = true
mult = (self:GetBuff_Hook("M_Hook_" .. buff, {buff = buff, mult = 1}) or {}).mult or mult
ArcCW.BuffStack = false
end
if ArcCW.ConVar_BuffMults[buff] then
if buff == "Mult_CycleTime" then
mult = mult / GetConVar(ArcCW.ConVar_BuffMults[buff]):GetFloat()
else
mult = mult * GetConVar(ArcCW.ConVar_BuffMults[buff]):GetFloat()
end
end
return mult
end
if self.TickCache_Mults[buff] then
mult = self.TickCache_Mults[buff]
local data = {
buff = buff,
mult = mult
}
if !ArcCW.BuffStack then
ArcCW.BuffStack = true
mult = (self:GetBuff_Hook("M_Hook_" .. buff, data) or {}).mult or mult
ArcCW.BuffStack = false
end
if ArcCW.ConVar_BuffMults[buff] then
if buff == "Mult_CycleTime" then
mult = mult / GetConVar(ArcCW.ConVar_BuffMults[buff]):GetFloat()
else
mult = mult * GetConVar(ArcCW.ConVar_BuffMults[buff]):GetFloat()
end
end
return mult
end
for i, k in pairs(self.Attachments) do
if !k.Installed then continue end
local atttbl = ArcCW.AttachmentTable[k.Installed]
if atttbl[buff] then
mult = mult * atttbl[buff]
end
if atttbl.ToggleStats and k.ToggleNum and atttbl.ToggleStats[k.ToggleNum] and atttbl.ToggleStats[k.ToggleNum][buff] then
mult = mult * atttbl.ToggleStats[k.ToggleNum][buff]
end
end
local cfm = self:GetCurrentFiremode()
if cfm and cfm[buff] then
mult = mult * cfm[buff]
end
if self:GetTable()[buff] then
mult = mult * self:GetTable()[buff]
end
for i, e in pairs(self:GetActiveElements()) do
local ele = self.AttachmentElements[e]
if ele and ele[buff] then
mult = mult * ele[buff]
end
end
self.TickCache_Mults[buff] = mult
if VERIFY_MODIFIED_CACHE and !self.ModifiedCache[buff] and mult != 1 then
print("ArcCW: Presumed non-changing buff '" .. buff .. "' is modified (" .. tostring(mult) .. ")!")
end
if ArcCW.ConVar_BuffMults[buff] then
if buff == "Mult_CycleTime" then
mult = mult / GetConVar(ArcCW.ConVar_BuffMults[buff]):GetFloat()
else
mult = mult * GetConVar(ArcCW.ConVar_BuffMults[buff]):GetFloat()
end
end
local data = {
buff = buff,
mult = mult
}
if !ArcCW.BuffStack then
ArcCW.BuffStack = true
mult = (self:GetBuff_Hook("M_Hook_" .. buff, data) or {}).mult or mult
ArcCW.BuffStack = false
end
return mult
end
function SWEP:GetBuff_Add(buff)
local add = 0
if MODIFIED_CACHE and !self.ModifiedCache[buff] then
if !ArcCW.BuffStack then
ArcCW.BuffStack = true
add = (self:GetBuff_Hook("A_Hook_" .. buff, {buff = buff, add = 0}) or {}).add or add
ArcCW.BuffStack = false
end
if ArcCW.ConVar_BuffAdds[buff] then
add = add + GetConVar(ArcCW.ConVar_BuffAdds[buff]):GetFloat()
end
return add
end
if self.TickCache_Adds[buff] then
add = self.TickCache_Adds[buff]
local data = {
buff = buff,
add = add
}
if !ArcCW.BuffStack then
ArcCW.BuffStack = true
add = (self:GetBuff_Hook("A_Hook_" .. buff, data) or {}).add or add
ArcCW.BuffStack = false
end
if ArcCW.ConVar_BuffAdds[buff] then
add = add + GetConVar(ArcCW.ConVar_BuffAdds[buff]):GetFloat()
end
return add
end
for i, k in pairs(self.Attachments) do
if !k.Installed then continue end
local atttbl = ArcCW.AttachmentTable[k.Installed]
if atttbl[buff] then
add = add + atttbl[buff]
end
if atttbl.ToggleStats and k.ToggleNum and atttbl.ToggleStats[k.ToggleNum] and atttbl.ToggleStats[k.ToggleNum][buff] then
add = add + atttbl.ToggleStats[k.ToggleNum][buff]
end
end
local cfm = self:GetCurrentFiremode()
if cfm and cfm[buff] then
add = add + cfm[buff]
end
for i, e in pairs(self:GetActiveElements()) do
local ele = self.AttachmentElements[e]
if ele and ele[buff] then
add = add + ele[buff]
end
end
self.TickCache_Adds[buff] = add
if VERIFY_MODIFIED_CACHE and !self.ModifiedCache[buff] and add != 0 then
print("ArcCW: Presumed non-changing buff '" .. buff .. "' is modified (" .. tostring(add) .. ")!")
end
if ArcCW.ConVar_BuffAdds[buff] then
add = add + GetConVar(ArcCW.ConVar_BuffAdds[buff]):GetFloat()
end
local data = {
buff = buff,
add = add
}
if !ArcCW.BuffStack then
ArcCW.BuffStack = true
add = (self:GetBuff_Hook("A_Hook_" .. buff, data) or {}).add or add
ArcCW.BuffStack = false
end
return add
end
SWEP.ActiveElementCache = nil
function SWEP:GetActiveElements(recache)
if self.ActiveElementCache and !recache then return self.ActiveElementCache end
if ArcCW.Overflow and self.ActiveElementCache then return self.ActiveElementCache end
local eles = {}
for _, i in pairs(self.Attachments) do
if !i.Installed then
if i.DefaultEles then
table.Add(eles, i.DefaultEles)
end
continue
end
if i.InstalledEles and i.Installed != i.EmptyFallback then
table.Add(eles, i.InstalledEles)
end
local atttbl = ArcCW.AttachmentTable[i.Installed]
if atttbl.ActivateElements then
table.Add(eles, atttbl.ActivateElements)
end
local num = i.ToggleNum or 1
if atttbl.ToggleStats and atttbl.ToggleStats[num] and (atttbl.ToggleStats[num]["ActivateElements"] != nil) then
table.Add(eles, atttbl.ToggleStats[num]["ActivateElements"])
--atttbl.ToggleStats[num][buff]
end
local slots = atttbl.Slot
if isstring(slots) then
slots = {slots}
end
table.Add(eles, slots or {})
table.insert(eles, i.Installed)
end
table.Add(eles, self.DefaultElements)
local mode = self:GetCurrentFiremode()
table.Add(eles, (mode or {}).ActivateElements or {})
local eles2 = {}
ArcCW.Overflow = true
for f, i in pairs(eles) do
local e = self.AttachmentElements[i]
if !e then continue end
if !self:CheckFlags(e.ExcludeFlags, e.RequireFlags) then continue end
local a = false
local c = 0
for g = f, table.Count(eles) do
if eles[g] == i then c = c + 1 end
if a then continue end
if c > 1 then a = true end
end
if a then continue end
table.insert(eles2, i)
end
table.Add(eles2, self:GetWeaponFlags())
ArcCW.Overflow = false
self.ActiveElementCache = eles2
return eles2
end
function SWEP:GetMuzzleDevice(wm)
local model = self.WM
local muzz = self.WMModel or self
if !wm then
model = self.VM
muzz = self:GetOwner():GetViewModel()
end
if model then
for _, ele in pairs(model) do
if ele.IsMuzzleDevice then
muzz = ele.Model or muzz
end
end
end
if self:GetInUBGL() then
local _, slot = self:GetBuff_Override("UBGL")
if wm then
muzz = (self.Attachments[slot].WMuzzleDeviceElement or {}).Model or muzz
else
muzz = (self.Attachments[slot].VMuzzleDeviceElement or {}).Model or muzz
end
end
return muzz
end
function SWEP:GetTracerOrigin()
local ow = self:GetOwner()
local wm = nil
local muzz = nil
if !ow:IsNPC() and !ow:IsNextBot() and ow:IsValid() then
wm = !ow:GetViewModel():IsValid() or ow:ShouldDrawLocalPlayer()
muzz = self:GetMuzzleDevice(wm)
end
if muzz and muzz:IsValid() then
local posang = muzz:GetAttachment(self:GetBuff_Override("Override_MuzzleEffectAttachment", self.MuzzleEffectAttachment) or 1)
if !posang then return muzz:GetPos() end
local pos = posang.Pos
return pos
end
end
function SWEP:CheckFlags(reject, need)
local flags
if ArcCW.Overflow then
flags = self:GetWeaponFlags()
else
flags = self:GetActiveElements()
end
reject = reject or {}
need = need or {}
if !istable(reject) then reject = {reject} end
if !istable(need) then need = {need} end
for _, i in pairs(reject) do
if table.HasValue(flags, i) then
return false
end
end
for _, i in pairs(need) do
if !table.HasValue(flags, i) then
return false
end
end
return true
end
function SWEP:GetWeaponFlags()
local flags = {}
if self.DefaultFlags then table.Add(flags, self.DefaultFlags) end
for id, i in pairs(self.Attachments) do
if !i.Installed then
if i.DefaultFlags then
table.Add(flags, i.DefaultFlags)
end
continue
end
local buff = self:GetBuff_Stat("GivesFlags", id)
if buff then
table.Add(flags, buff)
end
if i.GivesFlags then
table.Add(flags, i.GivesFlags)
end
local extras = {}
self:GetBuff_Hook("Hook_ExtraFlags", extras)
table.Add(flags, extras)
table.Add(flags, {i.Installed})
end
return flags
end
function SWEP:PlayerOwnsAtt(att)
local qty = ArcCW:PlayerGetAtts(self:GetOwner(), att)
return qty > 0
end
function SWEP:NetworkWeapon(sendto)
net.Start("arccw_networkatts")
net.WriteEntity(self) -- self entity
net.WriteUInt(table.Count(self.Attachments), 8)
for _, i in pairs(self.Attachments) do
if !i.Installed then net.WriteUInt(0, ArcCW.GetBitNecessity()) continue end
local atttbl = ArcCW.AttachmentTable[i.Installed]
local id = atttbl.ID
net.WriteUInt(id, ArcCW.GetBitNecessity())
if i.SlideAmount then
net.WriteFloat(i.SlidePos or 0.5)
end
if atttbl.ToggleStats then
net.WriteUInt(i.ToggleNum or 1, 8) -- look if you want more than 255 fucking toggle options you're insane and stupid just don't ok
end
-- if atttbl.ColorOptionsTable then
-- net.WriteUInt(i.ColorOptionIndex or 1, 8) -- look if you want more than 256 fucking color options you're insane and stupid and just don't ok
-- end
end
if sendto then
net.Send(sendto)
else
-- net.SendPVS(self:GetPos())
net.Broadcast()
end
end
function SWEP:SendDetail_ColorIndex(slot)
net.Start("arccw_colorindex")
net.WriteUInt(slot, 8)
net.WriteUInt(self.Attachments[slot].ColorOptionIndex)
net.SendToServer()
end
function SWEP:SendDetail_SlidePos(slot, hmm)
if !self.Attachments then return end
if !self.Attachments[slot].SlidePos then return end
net.Start("arccw_slidepos")
net.WriteUInt(slot, 8)
net.WriteFloat(self.Attachments[slot].SlidePos or 0.5)
net.SendToServer()
end
function SWEP:SendDetail_ToggleNum(slot, hmm)
if !self.Attachments or !self.Attachments[slot] then return end
if !self.Attachments[slot].ToggleNum then return end
net.Start("arccw_togglenum")
net.WriteUInt(slot, 8)
net.WriteUInt(self.Attachments[slot].ToggleNum or 1, 8)
net.SendToServer()
end
function SWEP:SendAllDetails()
for i, k in pairs(self.Attachments) do
self:SendDetail_SlidePos(i, true)
self:SendDetail_ToggleNum(i, true)
end
end
function SWEP:CountAttachments()
local total = 0
for _, i in pairs(self.Attachments) do
if i.Installed and !i.FreeSlot then
local ins = ArcCW.AttachmentTable[i.Installed]
if ins and !ins.IgnorePickX then
total = total + 1
end
end
end
return total
end
function SWEP:SetBodygroupTr(ind, bg)
self.Bodygroups[ind] = bg
end
function SWEP:RefreshBGs()
local vm
local vmm = self:GetBuff_Override("Override_VMMaterial") or self.VMMaterial or ""
local wmm = self:GetBuff_Override("Override_WMMaterial") or self.WMMaterial or ""
local vmc = self:GetBuff_Override("Override_VMColor") or self.VMColor or Color(255, 255, 255)
local wmc = self:GetBuff_Override("Override_WMColor") or self.WMColor or Color(255, 255, 255)
local vms = self:GetBuff_Override("Override_VMSkin") or self.DefaultSkin
local wms = self:GetBuff_Override("Override_WMSkin") or self.DefaultWMSkin
local vmp = self.DefaultPoseParams
local wmp = self.DefaultWMPoseParams
if self.MirrorVMWM then
wmm = vmm
wmc = vmc
wms = vms
wmp = vmp
end
if self:GetOwner():IsPlayer() then
vm = self:GetOwner():GetViewModel()
end
if vm and vm:IsValid() then
ArcCW.SetBodyGroups(vm, self.DefaultBodygroups)
vm:SetMaterial(vmm)
vm:SetColor(vmc)
vm:SetSkin(vms)
vmp["BaseClass"] = nil
for i, k in pairs(vmp) do
vm:SetPoseParameter(i, k)
end
end
self:SetMaterial(wmm)
self:SetColor(wmc)
self:SetSkin(wms)
if self.WMModel and self.WMModel:IsValid() then
ArcCW.SetBodyGroups(self.WMModel, self.MirrorVMWM and self.DefaultBodygroups or self.DefaultWMBodygroups)
self.WMModel:SetMaterial(wmm)
self.WMModel:SetColor(wmc)
self.WMModel:SetSkin(wms)
wmp["BaseClass"] = nil
for i, k in pairs(wmp) do
self.WMModel:SetPoseParameter(i, k)
end
end
local ae = self:GetActiveElements()
for _, e in pairs(ae) do
local ele = self.AttachmentElements[e]
if !ele then continue end
if ele.VMPoseParams and vm and IsValid(vm) then
ele.VMPoseParams["BaseClass"] = nil
for i, k in pairs(ele.VMPoseParams) do
vm:SetPoseParameter(i, k)
end
end
if self.WMModel and self.WMModel:IsValid() then
if self.MirrorVMWM and ele.VMPoseParams then
ele.VMPoseParams["BaseClass"] = nil
for i, k in pairs(ele.VMPoseParams) do
self.WMModel:SetPoseParameter(i, k)
end
end
if ele.WMPoseParams then
ele.WMPoseParams["BaseClass"] = nil
for i, k in pairs(ele.WMPoseParams) do
self.WMModel:SetPoseParameter(i, k)
end
end
end
if ele.VMSkin and vm and IsValid(vm) then
vm:SetSkin(ele.VMSkin)
end
if self.WMModel and self.WMModel:IsValid() then
if self.MirrorVMWM and ele.VMSkin then
self.WMModel:SetSkin(ele.VMSkin)
self:SetSkin(ele.VMSkin)
end
if ele.WMSkin then
self.WMModel:SetSkin(ele.WMSkin)
self:SetSkin(ele.WMSkin)
end
end
if ele.VMColor and vm and IsValid(vm) then
vm:SetColor(ele.VMColor)
end
if self.WMModel and self.WMModel:IsValid() then
if self.MirrorVMWM and ele.VMSkin then
self.WMModel:SetColor(ele.VMColor or color_white)
self:SetColor(ele.VMColor or color_white)
end
if ele.WMSkin then
self.WMModel:SetColor(ele.WMColor or color_white)
self:SetColor(ele.WMColor or color_white)
end
end
if ele.VMMaterial and vm and IsValid(vm) then
vm:SetMaterial(ele.VMMaterial)
end
if self.WMModel and self.WMModel:IsValid() then
if self.MirrorVMWM and ele.VMMaterial then
self.WMModel:SetMaterial(ele.VMMaterial)
self:SetMaterial(ele.VMMaterial)
end
if ele.WMMaterial then
self.WMModel:SetMaterial(ele.WMMaterial)
self:SetMaterial(ele.WMMaterial)
end
end
if ele.VMBodygroups then
for _, i in pairs(ele.VMBodygroups) do
if !i.ind or !i.bg then continue end
if vm and IsValid(vm) and vm:GetBodygroup(i.ind) != i.bg then
vm:SetBodygroup(i.ind, i.bg)
end
end
if self.MirrorVMWM then
for _, i in pairs(ele.VMBodygroups) do
if !i.ind or !i.bg then continue end
if self.WMModel and IsValid(self.WMModel) and self.WMModel:GetBodygroup(i.ind) != i.bg then
self.WMModel:SetBodygroup(i.ind, i.bg)
end
if self:GetBodygroup(i.ind) != i.bg then
self:SetBodygroup(i.ind, i.bg)
end
end
end
end
if ele.WMBodygroups then
for _, i in pairs(ele.WMBodygroups) do
if !i.ind or !i.bg then continue end
if self.WMModel and IsValid(self.WMModel) and self.WMModel:GetBodygroup(i.ind) != i.bg then
self.WMModel:SetBodygroup(i.ind, i.bg)
end
if self:GetBodygroup(i.ind) != i.bg then
self:SetBodygroup(i.ind, i.bg)
end
end
end
if ele.VMBoneMods then
for bone, i in pairs(ele.VMBoneMods) do
local boneind = vm:LookupBone(bone)
if !boneind then continue end
vm:ManipulateBonePosition(boneind, i)
end
if self.MirrorVMWM then
for bone, i in pairs(ele.VMBoneMods) do
if !(self.WMModel and self.WMModel:IsValid()) then break end
local boneind = self:LookupBone(bone)
if !boneind then continue end
self:ManipulateBonePosition(boneind, i)
end
end
end
if ele.WMBoneMods then
for bone, i in pairs(ele.WMBoneMods) do
if !(self.WMModel and self.WMModel:IsValid()) then break end
local boneind = self:LookupBone(bone)
if !boneind then continue end
self:ManipulateBonePosition(boneind, i)
end
end
if SERVER then
self:SetupShields()
end
end
local tpmdl = IsValid(self.WMModel) and self.WMModel or self
if IsValid(vm) then
for i = 0, (vm:GetNumBodyGroups()) do
if self.Bodygroups[i] then
vm:SetBodygroup(i, self.Bodygroups[i])
end
end
self:GetBuff_Hook("Hook_ModifyBodygroups", {vm = vm, eles = ae, wm = false})
end
for i = 0, (tpmdl:GetNumBodyGroups()) do
if self.Bodygroups[i] then
tpmdl:SetBodygroup(i, self.Bodygroups[i])
end
end
self:GetBuff_Hook("Hook_ModifyBodygroups", {vm = tpmdl, eles = ae, wm = true})
for slot, v in pairs(self.Attachments) do
if !v.Installed then continue end
local func = self:GetBuff_Stat("Hook_ModifyAttBodygroups", slot)
if func and v.VElement and IsValid(v.VElement.Model) and IsValid(vm) then
func(self, {vm = vm, element = v.VElement, slottbl = v, wm = false})
end
if func and v.WElement and IsValid(v.WElement.Model) then
func(self, {vm = tpmdl, element = v.WElement, slottbl = v, wm = true})
end
end
end
function SWEP:GetPickX()
return ArcCW.ConVars["atts_pickx"]:GetInt()
end
function SWEP:Attach(slot, attname, silent, noadjust)
silent = silent or false
local attslot = self.Attachments[slot]
if !attslot then return end
if attslot.Installed == attname then return end
if attslot.Internal then return end
-- Make an additional check to see if we can detach the current attachment
if attslot.Installed and !ArcCW:PlayerCanAttach(self:GetOwner(), self, attslot.Installed, slot, attname) then
if CLIENT and !silent then
surface.PlaySound("items/medshotno1.wav")
end
return
end
if !ArcCW:PlayerCanAttach(self:GetOwner(), self, attname, slot, false) then
if CLIENT and !silent then
surface.PlaySound("items/medshotno1.wav")
end
return
end
local pick = self:GetPickX()
if pick > 0 and self:CountAttachments() >= pick and !attslot.FreeSlot
and !attslot.Installed then
if CLIENT and !silent then
surface.PlaySound("items/medshotno1.wav")
end
return
end
local atttbl = ArcCW.AttachmentTable[attname]
if !atttbl then return end
if !ArcCW:SlotAcceptsAtt(attslot.Slot, self, attname) then return end
if !self:CheckFlags(atttbl.ExcludeFlags, atttbl.RequireFlags) then return end
if !self:PlayerOwnsAtt(attname) then return end
local max = atttbl.Max
if max then
local amt = 0
for i, k in pairs(self.Attachments) do
if k.Installed == attname then amt = amt + 1 end
end
if amt >= max then return end
end
if attslot.SlideAmount then
attslot.SlidePos = 0.5
end
if atttbl.MountPositionOverride then
attslot.SlidePos = atttbl.MountPositionOverride
end
if atttbl.AdditionalSights then
self.SightMagnifications = {}
end
if atttbl.ToggleStats then
attslot.ToggleNum = 1
end
attslot.ToggleLock = atttbl.ToggleLockDefault or false
if CLIENT then
-- we are asking to attach something
self:SendAllDetails()
net.Start("arccw_asktoattach")
net.WriteUInt(slot, 8)
net.WriteUInt(atttbl.ID, 24)
net.SendToServer()
if !silent then
surface.PlaySound(atttbl.AttachSound or "weapons/arccw/install.wav")
end
else
self:DetachAllMergeSlots(slot)
for i, k in pairs(self.Attachments) do
if table.HasValue(k.MergeSlots or {}, slot) then
self:DetachAllMergeSlots(i)
end
end
end
attslot.Installed = attname
if atttbl.Health then
attslot.HP = self:GetAttachmentMaxHP(slot)
end
if atttbl.ColorOptionsTable then
attslot.ColorOptionIndex = 1
end
ArcCW:PlayerTakeAtt(self:GetOwner(), attname)
--[[]
local fmt = self:GetBuff_Override("Override_Firemodes") or self.Firemodes
local fmi = self:GetFireMode()
if fmi > table.Count(fmt) then
self:SetFireMode(1)
end
]]
--self.UnReady = false
if SERVER then
self:NetworkWeapon()
self:SetupModel(false)
self:SetupModel(true)
ArcCW:PlayerSendAttInv(self:GetOwner())
if engine.ActiveGamemode() == "terrortown" then
self:TTT_PostAttachments()
end
else
self:SetupActiveSights()
self.LHIKAnimation = 0
self.LHIKAnimationStart = 0
self.LHIKAnimationTime = 0
self.LHIKDelta = {}
self.LHIKDeltaAng = {}
self.ViewModel_Hit = Vector(0, 0, 0)
if !silent then
self:SavePreset("autosave")
end
end
for s, i in pairs(self.Attachments) do
if !self:CheckFlags(i.ExcludeFlags, i.RequireFlags) then
self:Detach(s, true, true)
end
end
if !noadjust then
self:AdjustAtts()
end
if atttbl.UBGL then
local ubgl_ammo = self:GetBuff_Override("UBGL_Ammo")
local ubgl_clip = self:GetBuff_Override("UBGL_Capacity")
if self:GetOwner():IsPlayer() and ArcCW.ConVars["atts_ubglautoload"]:GetBool() and ubgl_ammo then
local amt = math.min(ubgl_clip - self:Clip2(), self:GetOwner():GetAmmoCount(ubgl_ammo))
self:SetClip2(self:Clip2() + amt)
self:GetOwner():RemoveAmmo(amt, ubgl_ammo)
end
end
self:RefreshBGs()
return true
end
function SWEP:DetachAllMergeSlots(slot, silent)
local slots = {slot}
table.Add(slots, (self.Attachments[slot] or {}).MergeSlots or {})
for _, i in pairs(slots) do
self:Detach(i, silent, nil, true)
end
end
function SWEP:Detach(slot, silent, noadjust, nocheck)
if !slot then return end
if !self.Attachments[slot] then return end
if !self.Attachments[slot].Installed then return end
if self.Attachments[slot].Internal then return end
if !nocheck and !ArcCW:PlayerCanAttach(self:GetOwner(), self, self.Attachments[slot].Installed, slot, true) then
if CLIENT and !silent then
surface.PlaySound("items/medshotno1.wav")
end
return
end
if self.Attachments[slot].Installed == self.Attachments[slot].EmptyFallback then
return
end
local previnstall = self.Attachments[slot].Installed
local atttbl = ArcCW.AttachmentTable[previnstall]
if atttbl.UBGL then
local clip = self:Clip2()
local ammo = atttbl.UBGL_Ammo or "smg1_grenade"
if SERVER and IsValid(self:GetOwner()) then
self:GetOwner():GiveAmmo(clip, ammo, true)
end
self:SetClip2(0)
self:DeselectUBGL()
end
if self.Attachments[slot].EmptyFallback then -- is this a good name
self.Attachments[slot].Installed = self.Attachments[slot].EmptyFallback
else
self.Attachments[slot].Installed = nil
end
if self.Attachments[slot].SubAtts then
for i, k in pairs(self.Attachments[slot].SubAtts) do
self:Detach(k, true, true)
end
end
if self:GetAttachmentHP(slot) >= self:GetAttachmentMaxHP(slot) then
ArcCW:PlayerGiveAtt(self:GetOwner(), previnstall)
end
if CLIENT then
self:SendAllDetails()
-- we are asking to detach something
net.Start("arccw_asktodetach")
net.WriteUInt(slot, 8)
net.SendToServer()
if !silent then
surface.PlaySound(atttbl.DetachSound or "weapons/arccw/uninstall.wav")
end
self:SetupActiveSights()
self.LHIKAnimation = 0
self.LHIKAnimationStart = 0
self.LHIKAnimationTime = 0
if !silent then
self:SavePreset("autosave")
end
else
self:NetworkWeapon()
self:SetupModel(false)
self:SetupModel(true)
ArcCW:PlayerSendAttInv(self:GetOwner())
if engine.ActiveGamemode() == "terrortown" then
self:TTT_PostAttachments()
end
end
self:RefreshBGs()
if !noadjust then
self:AdjustAtts()
end
return true
end
function SWEP:ToggleSlot(slot, num, silent, back)
local atttbl = ArcCW.AttachmentTable[self.Attachments[slot].Installed]
if !atttbl.ToggleStats then return end
local amt = 1
if back then amt = -1 end
if !num then
self.Attachments[slot].ToggleNum = (self.Attachments[slot].ToggleNum or 1) + amt
if self.Attachments[slot].ToggleNum > #atttbl.ToggleStats then
self.Attachments[slot].ToggleNum = 1
elseif self.Attachments[slot].ToggleNum < 1 then
self.Attachments[slot].ToggleNum = #atttbl.ToggleStats
end
else
self.Attachments[slot].ToggleNum = math.Clamp(num, 1, #catttbl.ToggleStats)
end
if CLIENT then
self:SendDetail_ToggleNum(slot)
self:SetupActiveSights()
elseif SERVER then
self:NetworkWeapon()
self:SetupModel(false)
self:SetupModel(true)
end
self:AdjustAtts()
for s, i in pairs(self.Attachments) do
if !self:CheckFlags(i.ExcludeFlags, i.RequireFlags) then
self:Detach(s, true)
end
end
self:RefreshBGs()
if CLIENT and !silent and self:GetBuff_Stat("ToggleSound", slot) != false then
surface.PlaySound(self:GetBuff_Stat("ToggleSound", slot) or (atttbl.ToggleStats[slot] or {}).ToggleSound or "weapons/arccw/firemode.wav")
end
end
function SWEP:AdjustAmmo(old_inf)
local new_inf = self:HasInfiniteAmmo()
local wpn = weapons.Get(self:GetClass())
local ammo = self:GetBuff_Override("Override_Ammo", wpn.Primary.Ammo)
local oldammo = self.OldAmmo or self.Primary.Ammo
if old_inf and (!new_inf or ammo != oldammo) then
self:SetClip1(0)
elseif (!old_inf and new_inf) or ammo != oldammo then
self:Unload()
end
self.Primary.Ammo = ammo
self.OldAmmo = self.Primary.Ammo
end
function SWEP:AdjustAtts()
local old_inf = self:HasInfiniteAmmo()
self:RecalcAllBuffs()
-- Recalculate active elements so dependencies aren't fucked
self.ActiveElementCache = nil
self:GetActiveElements(true)
self.ModifiedCache = {}
-- Tempoarily disable modified cache, since we're building it right now
MODIFIED_CACHE = false
for i, k in pairs(self.Attachments) do
if !k.Installed then continue end
local ok = true
if !ArcCW:SlotAcceptsAtt(k.Slot, self, k.Installed) then ok = false end
if ok and !self:CheckFlags(k.ExcludeFlags, k.RequireFlags) then ok = false end
local atttbl = ArcCW.AttachmentTable[k.Installed]
if !atttbl then continue end
if ok and !self:CheckFlags(atttbl.ExcludeFlags, atttbl.RequireFlags) then ok = false end
if !ok then
self:Detach(i, true)
continue
end
-- Cache all possible value modifiers
for var, v in pairs(atttbl) do
self.ModifiedCache[var] = true
if var == "ToggleStats" or var == "Override_Firemodes" then
for _, v2 in pairs(v) do
for var2, _ in pairs(v2) do
self.ModifiedCache[var2] = true
end
end
end
end
end
for _, e in pairs(self.AttachmentElements) do
if !istable(e) then continue end
for var, v in pairs(e) do
self.ModifiedCache[var] = true
end
end
for _, e in pairs(self.Firemodes) do
if !istable(e) then continue end
for var, v in pairs(e) do
self.ModifiedCache[var] = true
end
end
MODIFIED_CACHE = true
if SERVER then
local cs = self:GetCapacity() + self:GetChamberSize()
if self:Clip1() > cs and self:Clip1() != ArcCW.BottomlessMagicNumber then
local diff = self:Clip1() - cs
self:SetClip1(cs)
if self:GetOwner():IsValid() and !self:GetOwner():IsNPC() then
self:GetOwner():GiveAmmo(diff, self.Primary.Ammo, true)
end
end
else
local se = self:GetBuff_Override("Override_ShootEntity") or self.ShootEntity
if se then
local path = "arccw/weaponicons/" .. self:GetClass()
local mat = Material(path)
if !mat:IsError() then
local tex = mat:GetTexture("$basetexture")
local texpath = tex:GetName()
killicon.Add(se, texpath, Color(255, 255, 255))
end
end
end
local ubgl_ammo = self:GetBuff_Override("UBGL_Ammo")
local ubgl_clip = self:GetBuff_Override("UBGL_Capacity")
self.Secondary.ClipSize = ubgl_clip or -1
self.Secondary.Ammo = ubgl_ammo or "none"
--[[]
if ubgl_clip then
self.Secondary.ClipSize = ubgl_clip
if self:GetOwner():IsPlayer() and ArcCW.ConVars["atts_ubglautoload"]:GetBool() and ubgl_ammo then
local amt = math.min(ubgl_clip - self:Clip2(), self:GetOwner():GetAmmoCount(ubgl_ammo))
self:SetClip2(self:Clip2() + amt)
self:GetOwner():RemoveAmmo(amt, ubgl_ammo)
end
else
self.Secondary.ClipSize = -1
end
]]
self:RebuildSubSlots()
local fmt = self:GetBuff_Override("Override_Firemodes", self.Firemodes)
fmt["BaseClass"] = nil
local fmi = self:GetFireMode()
if !fmt[fmi] then self:SetFireMode(1) end
self:AdjustAmmo(old_inf)
end
function SWEP:GetAttachmentMaxHP(slot)
if !self.Attachments[slot] then return 100 end
if !self.Attachments[slot].Installed then return 100 end
local maxhp = 100
local atttbl = ArcCW.AttachmentTable[self.Attachments[slot].Installed]
if atttbl.Health then
maxhp = atttbl.Health
end
return maxhp
end
function SWEP:GetAttachmentHP(slot)
if !self.Attachments[slot] then return 100 end
if !self.Attachments[slot].Installed then return 100 end
if self.Attachments[slot].HP then return self.Attachments[slot].HP end
self.Attachments[slot].HP = self:GetAttachmentMaxHP(slot)
return self.Attachments[slot].HP
end
function SWEP:ApplyAttachmentShootDamage()
local any = false
for j, i in pairs(self.Attachments) do
if !i.Installed then continue end
local atttbl = ArcCW.AttachmentTable[i.Installed]
if !atttbl.Health then continue end
if atttbl.DamageOnShoot then
self:DamageAttachment(j, atttbl.DamageOnShoot)
any = true
end
end
if any then
self:SendAttHP()
end
end
function SWEP:DamageAttachment(slot, dmg)
if !self.Attachments[slot] then return end
if !self.Attachments[slot].Installed then return end
self.Attachments[slot].HP = self:GetAttachmentHP(slot) - dmg
if self:GetAttachmentHP(slot) <= 0 then
local atttbl = ArcCW.AttachmentTable[self.Attachments[slot].Installed]
if atttbl.Hook_AttDestroyed then
atttbl.Hook_AttDestroyed(self, {slot = slot, dmg = dmg})
end
self:Detach(slot, true)
end
end
function SWEP:SendAttHP()
net.Start("arccw_sendatthp")
for i, k in pairs(self.Attachments) do
if !k.Installed then continue end
local atttbl = ArcCW.AttachmentTable[k.Installed]
if atttbl.Health then
net.WriteBool(true)
net.WriteUInt(i, 8)
net.WriteFloat(self:GetAttachmentHP(i))
end
end
net.WriteBool(false)
net.Send(self:GetOwner())
end
-- local node = {b = {}, i = "" t = 0}
-- b: branches
-- i: installed
-- t: toggle
-- s: slide
-- h: hp
-- recursive function
-- gets a tree of all the attachments installed in subslots subordinate to a particular slot
function SWEP:GetSubSlotTree(i)
if !self.Attachments[i] then return nil end
if !self.Attachments[i].Installed then return nil end
if !self.Attachments[i].SubAtts then return
{
b = {},
i = self.Attachments[i].Installed,
t = self.Attachments[i].ToggleNum,
s = self.Attachments[i].SlidePos,
h = self.Attachments[i].Health}
end
local ss = {}
for j, k in pairs(self.Attachments[i].SubAtts) do
if k == i then continue end
local sst = self:GetSubSlotTree(k)
if sst then
ss[j] = sst
end
end
return {b = ss, i = self.Attachments[i].Installed}
end
function SWEP:SubSlotTreeReinstall(slot, subslottree)
for i, k in pairs(self.Attachments[slot].SubAtts or {}) do
-- i = index
-- k = slot
self.Attachments[k].Installed = subslottree[i].i
self.Attachments[k].ToggleNum = subslottree[i].t
self.Attachments[k].SlidePos = subslottree[i].s
self.Attachments[k].Health = subslottree[i].h
if subslottree.b[i] then
self:SubSlotTreeReinstall(i, subslottree.b[i])
end
end
end
function SWEP:RebuildSubSlots()
-- this function rebuilds the subslots while preserving installed attachment data
local subslottrees = {}
local baseatts = table.Count(weapons.Get(self:GetClass()).Attachments)
self.Attachments.BaseClass = nil
for i = 1, baseatts do
subslottrees[baseatts] = self:GetSubSlotTree(i)
end
-- remove all sub slots
for i, k in pairs(self.Attachments) do
if !isnumber(i) then continue end
if !istable(k) then continue end
if i > baseatts then
self.Attachments[i] = nil
else
self.Attachments[i].SubAtts = nil
end
end
self.SubSlotCount = 0
-- add the sub slots back
for i, k in pairs(self.Attachments) do
if !k.Installed then continue end
local att = ArcCW.AttachmentTable[k.Installed]
if !att then continue end
if !istable(k) then continue end
if att.SubSlots then
self:AddSubSlot(i, k.Installed)
end
end
-- add the sub slot data back
for i, k in pairs(subslottrees) do
self:SubSlotTreeReinstall(i, k)
end
end
function SWEP:AddSubSlot(i, attname)
local baseatts = table.Count(weapons.Get(self:GetClass()).Attachments)
local att = ArcCW.AttachmentTable[attname]
if att.SubSlots then
self.Attachments[i].SubAtts = {}
local og_slot = self.Attachments[i]
for ind, slot in pairs(att.SubSlots) do
if !istable(slot) then continue end
self.SubSlotCount = self.SubSlotCount + 1
local index = baseatts + self.SubSlotCount
self.Attachments[index] = slot
self.Attachments[index].Bone = og_slot.Bone
self.Attachments[index].WMBone = og_slot.WMBone
self.Attachments[index].ExtraSightDist = 0--self.Attachments[index].ExtraSightDist or og_slot.ExtraSightDist
self.Attachments[index].CorrectivePos = og_slot.CorrectivePos
self.Attachments[index].CorrectiveAng = og_slot.CorrectiveAng
og_slot.SubAtts[ind] = index
if slot.MergeSlots then
self.Attachments[index].MergeSlots = {}
for _, k2 in pairs(slot.MergeSlots) do
table.insert(self.Attachments[index].MergeSlots, k2 + index)
end
end
if slot.Offset then
self.Attachments[index].Offset = {
vpos = Vector(0, 0, 0),
vang = Angle(0, 0, 0),
wpos = Vector(0, 0, 0),
wang = Angle(0, 0, 0)
}
if slot.Offset.vang then
self.Attachments[index].Offset.vang = slot.Offset.vang + (og_slot.Offset.vang or Angle(0, 0, 0))
end
if slot.Offset.wang then
self.Attachments[index].Offset.wang = slot.Offset.wang + (og_slot.Offset.wang or Angle(0, 0, 0))
end
if slot.Offset.vpos then
self.Attachments[index].Offset.vpos = LocalToWorld(slot.Offset.vpos, self.Attachments[index].Offset.vang, og_slot.Offset.vpos, og_slot.Offset.vang or Angle(0, 0, 0))
end
if slot.Offset.wpos then
self.Attachments[index].Offset.wpos = LocalToWorld(slot.Offset.wpos, self.Attachments[index].Offset.wang, og_slot.Offset.wpos, og_slot.Offset.wang or Angle(0, 0, 0))
end
end
self.Attachments[index].SubAtts = {}
end
end
end
function SWEP:OnReloaded()
self:RecalcAllBuffs()
self:SetupActiveSights()
end