mirror of
https://github.com/lifestorm/wnsrc.git
synced 2025-12-16 21:33:46 +03:00
569 lines
16 KiB
Lua
569 lines
16 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/
|
|
--]]
|
|
|
|
ArcCW.AttachmentBlacklistTable = ArcCW.AttachmentBlacklistTable or {}
|
|
|
|
function ArcCW:PlayerCanAttach(ply, wep, attname, slot, detach)
|
|
-- The global variable takes priority over everything
|
|
if !ArcCW.EnableCustomization then return false end
|
|
|
|
-- Attach and Detach require a player (usually the owner)
|
|
if !IsValid(ply) then return false end
|
|
|
|
-- Spectators taking off your attachments is funny, but also cursed
|
|
if wep:GetOwner() != ply then return false end
|
|
|
|
-- Allow hooks to block or force allow attachment usage
|
|
local ret = hook.Run("ArcCW_PlayerCanAttach", ply, wep, attname, slot, detach)
|
|
|
|
if ret == nil and engine.ActiveGamemode() == "terrortown" then
|
|
local mode = ArcCW.ConVars["ttt_customizemode"]:GetInt()
|
|
if mode == 1 and !ply.ArcCW_AllowCustomize then return false
|
|
elseif mode == 2 and !ply.ArcCW_AllowCustomize and GetRoundState() == ROUND_ACTIVE then return false
|
|
elseif mode == 3 and !ply.ArcCW_AllowCustomize and !ply:IsActiveTraitor() and !ply:IsActiveDetective() then return false end
|
|
elseif ret == nil and ArcCW.ConVars["enable_customization"]:GetInt() <= 0 then
|
|
return false
|
|
end
|
|
|
|
return (ret == nil and true) or ret
|
|
end
|
|
|
|
function ArcCW:GetAttsForSlot(slot, wep, random)
|
|
local ret = {}
|
|
|
|
for id, atttbl in pairs(ArcCW.AttachmentTable) do
|
|
|
|
if !ArcCW:SlotAcceptsAtt(slot, wep, id) then continue end
|
|
if random and (atttbl.NoRandom or (atttbl.RandomWeight or 1) <= 0) then continue end
|
|
|
|
table.insert(ret, id)
|
|
end
|
|
|
|
return ret
|
|
end
|
|
|
|
function ArcCW:GetAttList(name, filter)
|
|
if self.AttachmentCachedLists[name] then return self.AttachmentCachedLists[name] end
|
|
self.AttachmentCachedLists[name] = {}
|
|
for k, v in pairs(self.AttachmentTable) do
|
|
local k2, v2 = filter(k, v)
|
|
if k2 then
|
|
self.AttachmentCachedLists[name][k2] = v2
|
|
end
|
|
end
|
|
return self.AttachmentCachedLists[name]
|
|
end
|
|
|
|
local function weighted_random(tbl, amt)
|
|
amt = amt or 1
|
|
local max = 0
|
|
for k, v in pairs(tbl) do max = max + v end
|
|
local ret = {}
|
|
for i = 1, amt do
|
|
local rng = math.random() * max
|
|
for k, v in pairs(tbl) do
|
|
rng = rng - v
|
|
if rng <= 0 then
|
|
ret[k] = (ret[k] or 0) + 1
|
|
break
|
|
end
|
|
end
|
|
end
|
|
return ret
|
|
end
|
|
|
|
function ArcCW:RollRandomAttachment(all, wep, slot)
|
|
for k, v in pairs(self:RollRandomAttachments(1, all, wep, slot)) do return k end
|
|
end
|
|
|
|
function ArcCW:RollRandomAttachments(amt, all, wep, slot)
|
|
if wep == nil then
|
|
-- cache the list results and randomly get one
|
|
local tbl = self:GetAttList("random" .. (all and "_all" or ""), function(k, v)
|
|
if ((!v.Free and !v.InvAtt) or all) and !v.NoRandom and (v.RandomWeight or 1) >= 0 then
|
|
return k, v.RandomWeight or 1
|
|
end
|
|
end)
|
|
return weighted_random(tbl, amt)
|
|
else
|
|
-- can't cache this because it is weapon-dependent
|
|
local tbl = {}
|
|
for id, atttbl in pairs(ArcCW.AttachmentTable) do
|
|
if ((!atttbl.Free and !atttbl.InvAtt) or all) and (atttbl.NoRandom or (atttbl.RandomWeight or 1) <= 0) then continue end
|
|
if !wep:CheckFlags(atttbl.ExcludeFlags, atttbl.RequireFlags) then continue end
|
|
if slot != nil and !ArcCW:SlotAcceptsAtt(slot.Slot, wep, id) then continue end
|
|
tbl[id] = atttbl.RandomWeight or 1
|
|
end
|
|
return weighted_random(tbl, amt)
|
|
end
|
|
end
|
|
|
|
function ArcCW:SlotAcceptsAtt(slot, wep, att)
|
|
local slots = {}
|
|
|
|
if isstring(slot) then
|
|
slots[slot] = true
|
|
elseif istable(slot) then
|
|
for _, i in pairs(slot) do
|
|
slots[i] = true
|
|
end
|
|
end
|
|
|
|
local atttbl = ArcCW.AttachmentTable[att]
|
|
if !atttbl then return false end
|
|
|
|
if atttbl.Hidden or atttbl.Blacklisted or ArcCW.AttachmentBlacklistTable[att] then return false end
|
|
|
|
local Owner = wep.GetOwner and wep:GetOwner()
|
|
if (atttbl.NotForNPC or atttbl.NotForNPCs) and Owner and Owner:IsNPC() then
|
|
return false
|
|
end
|
|
if atttbl.AdminOnly and IsValid(Owner) and !(Owner:IsPlayer() and Owner:IsAdmin()) then return false end
|
|
|
|
if wep.RejectAttachments and wep.RejectAttachments[att] then return false end
|
|
|
|
if isstring(atttbl.Slot) then
|
|
if !slots[atttbl.Slot] then return false end
|
|
elseif istable(atttbl.Slot) then
|
|
local yeah = false
|
|
|
|
for _, i in pairs(atttbl.Slot) do
|
|
if slots[i] then
|
|
yeah = true
|
|
break
|
|
end
|
|
end
|
|
|
|
if !yeah then
|
|
return false
|
|
end
|
|
end
|
|
|
|
if wep and atttbl.Hook_Compatible then
|
|
local compat = atttbl.Hook_Compatible(wep, {slot = slot, att = att})
|
|
if compat == true then
|
|
return true
|
|
elseif compat == false then
|
|
return false
|
|
end
|
|
end
|
|
|
|
return true
|
|
end
|
|
|
|
function ArcCW:WeaponAcceptsAtt(wep, att)
|
|
if wep.ArcCW and wep.Attachments then
|
|
local tbl = {}
|
|
for i, v in pairs(wep.Attachments) do
|
|
table.insert(tbl, i)
|
|
end
|
|
return ArcCW:SlotAcceptsAtt(wep, wep, att)
|
|
end
|
|
return false
|
|
end
|
|
|
|
function ArcCW:PlayerGetAtts(ply, att)
|
|
if !IsValid(ply) then return 0 end
|
|
if ArcCW.ConVars["attinv_free"]:GetBool() then return 999 end
|
|
|
|
if att == "" then return 999 end
|
|
|
|
local atttbl = ArcCW.AttachmentTable[att]
|
|
|
|
if !atttbl then return 0 end
|
|
|
|
if atttbl.Free then return 999 end
|
|
|
|
if !IsValid(ply) then return 0 end
|
|
|
|
if !ply:IsAdmin() and atttbl.AdminOnly then
|
|
return 0
|
|
end
|
|
|
|
if atttbl.InvAtt then att = atttbl.InvAtt end
|
|
|
|
if !ply.ArcCW_AttInv then return 0 end
|
|
|
|
if !ply.ArcCW_AttInv[att] then return 0 end
|
|
|
|
return ply.ArcCW_AttInv[att]
|
|
end
|
|
|
|
function ArcCW:PlayerGiveAtt(ply, att, amt)
|
|
amt = amt or 1
|
|
|
|
if !IsValid(ply) then return end
|
|
|
|
if !ply.ArcCW_AttInv then
|
|
ply.ArcCW_AttInv = {}
|
|
end
|
|
|
|
local atttbl = ArcCW.AttachmentTable[att]
|
|
|
|
if !atttbl then print("Invalid att " .. att) return end
|
|
if atttbl.Free then return end -- You can't give a free attachment, silly
|
|
if atttbl.AdminOnly and !(ply:IsPlayer() and ply:IsAdmin()) then return false end
|
|
|
|
if atttbl.InvAtt then att = atttbl.InvAtt end
|
|
|
|
if ArcCW.ConVars["attinv_lockmode"]:GetBool() then
|
|
if ply.ArcCW_AttInv[att] == 1 then return end
|
|
ply.ArcCW_AttInv[att] = 1
|
|
else
|
|
ply.ArcCW_AttInv[att] = (ply.ArcCW_AttInv[att] or 0) + amt
|
|
end
|
|
end
|
|
|
|
function ArcCW:PlayerTakeAtt(ply, att, amt)
|
|
amt = amt or 1
|
|
|
|
if ArcCW.ConVars["attinv_lockmode"]:GetBool() then return end
|
|
|
|
if !IsValid(ply) then return end
|
|
|
|
if !ply.ArcCW_AttInv then
|
|
ply.ArcCW_AttInv = {}
|
|
end
|
|
|
|
local atttbl = ArcCW.AttachmentTable[att]
|
|
if !atttbl or atttbl.Free then return end
|
|
|
|
if atttbl.InvAtt then att = atttbl.InvAtt end
|
|
|
|
ply.ArcCW_AttInv[att] = ply.ArcCW_AttInv[att] or 0
|
|
|
|
if ply.ArcCW_AttInv[att] < amt then
|
|
return false
|
|
end
|
|
|
|
ply.ArcCW_AttInv[att] = ply.ArcCW_AttInv[att] - amt
|
|
if ply.ArcCW_AttInv[att] <= 0 then
|
|
ply.ArcCW_AttInv[att] = nil
|
|
end
|
|
return true
|
|
end
|
|
|
|
if CLIENT then
|
|
|
|
local function postsetup(wpn)
|
|
if wpn.SetupModel then
|
|
wpn:SetupModel(true)
|
|
if wpn:GetOwner() == LocalPlayer() then
|
|
wpn:SetupModel(false)
|
|
end
|
|
wpn:AdjustAtts()
|
|
else
|
|
timer.Simple(0.1, function()
|
|
postsetup(wpn)
|
|
end)
|
|
end
|
|
end
|
|
|
|
net.Receive("arccw_networkatts", function(len, ply)
|
|
local wpn = net.ReadEntity()
|
|
if !IsValid(wpn) then return end
|
|
if !wpn.ArcCW then return end
|
|
|
|
local attnum = net.ReadUInt(8)
|
|
wpn.Attachments = wpn.Attachments or {}
|
|
wpn.SubSlotCount = 0
|
|
|
|
for i = 1, attnum do
|
|
local attid = net.ReadUInt(ArcCW.GetBitNecessity())
|
|
|
|
wpn.Attachments[i] = wpn.Attachments[i] or {}
|
|
|
|
if attid == 0 then
|
|
if !istable(wpn.Attachments[i]) then continue end
|
|
wpn.Attachments[i].Installed = nil
|
|
continue
|
|
end
|
|
|
|
local att = ArcCW.AttachmentIDTable[attid]
|
|
wpn.Attachments[i].Installed = att
|
|
|
|
if wpn.Attachments[i].SlideAmount then
|
|
wpn.Attachments[i].SlidePos = net.ReadFloat()
|
|
end
|
|
|
|
if ArcCW.AttachmentTable[att].ToggleStats then
|
|
wpn.Attachments[i].ToggleNum = net.ReadUInt(8)
|
|
end
|
|
|
|
wpn:AddSubSlot(i, att)
|
|
end
|
|
|
|
wpn.CertainAboutAtts = true
|
|
|
|
postsetup(wpn)
|
|
end)
|
|
|
|
net.Receive("arccw_sendattinv", function(len, ply)
|
|
if !IsValid(LocalPlayer()) then return end -- This might be called before we are valid
|
|
LocalPlayer().ArcCW_AttInv = {}
|
|
|
|
local count = net.ReadUInt(32)
|
|
|
|
for i = 1, count do
|
|
local attid = net.ReadUInt(ArcCW.GetBitNecessity())
|
|
local acount = net.ReadUInt(32)
|
|
|
|
local att = ArcCW.AttachmentIDTable[attid]
|
|
|
|
LocalPlayer().ArcCW_AttInv[att] = acount
|
|
end
|
|
|
|
-- This function will not exist until initialized (by having an ArcCW weapon exist)!
|
|
-- It also obviously needs menu2 open
|
|
if ArcCW.InvHUD_FormAttachmentSelect and IsValid(ArcCW.InvHUD) and IsValid(ArcCW.InvHUD_Menu2) then
|
|
ArcCW.InvHUD_FormAttachmentSelect()
|
|
end
|
|
end)
|
|
|
|
net.Receive("arccw_sendatthp", function(len, ply)
|
|
local wpn = LocalPlayer():GetActiveWeapon()
|
|
|
|
while net.ReadBool() do
|
|
local slot = net.ReadUInt(8)
|
|
local hp = net.ReadFloat()
|
|
|
|
wpn.Attachments[slot].HP = hp
|
|
end
|
|
end)
|
|
|
|
elseif SERVER then
|
|
|
|
hook.Add("PlayerDeath", "ArcCW_DeathAttInv", function(ply)
|
|
ply.ArcCW_AttInv = ply.ArcCW_AttInv or {}
|
|
if !table.IsEmpty(ply.ArcCW_AttInv)
|
|
and ArcCW.ConVars["attinv_loseondie"]:GetInt() >= 2
|
|
and !ArcCW.ConVars["attinv_free"]:GetBool() then
|
|
local boxEnt = ents.Create("arccw_att_dropped")
|
|
boxEnt:SetPos(ply:GetPos() + Vector(0, 0, 4))
|
|
boxEnt.GiveAttachments = ply.ArcCW_AttInv
|
|
boxEnt:Spawn()
|
|
boxEnt:SetNWString("boxname", ply:GetName() .. "'s Death Box")
|
|
local count = 0
|
|
for i, v in pairs(boxEnt.GiveAttachments) do count = count + v end
|
|
boxEnt:SetNWInt("boxcount", count)
|
|
end
|
|
end)
|
|
|
|
hook.Add("PlayerSpawn", "ArcCW_SpawnAttInv", function(ply, trans)
|
|
if trans then return end
|
|
|
|
if ArcCW.ConVars["attinv_loseondie"]:GetInt() >= 1 then
|
|
ply.ArcCW_AttInv = {}
|
|
end
|
|
local amt = ArcCW.ConVars["attinv_giveonspawn"]:GetInt()
|
|
if amt > 0 then
|
|
local giv = ArcCW:RollRandomAttachments(amt)
|
|
for k, v in pairs(giv) do
|
|
ArcCW:PlayerGiveAtt(ply, k, v)
|
|
end
|
|
end
|
|
ArcCW:PlayerSendAttInv(ply)
|
|
end)
|
|
|
|
net.Receive("arccw_rqwpnnet", function(len, ply)
|
|
local wpn = net.ReadEntity()
|
|
|
|
if !wpn.ArcCW then return end
|
|
|
|
wpn:RecalcAllBuffs()
|
|
wpn:NetworkWeapon(ply)
|
|
end)
|
|
|
|
net.Receive("arccw_slidepos", function(len, ply)
|
|
local wpn = ply:GetActiveWeapon()
|
|
|
|
local slot = net.ReadUInt(8)
|
|
local pos = net.ReadFloat()
|
|
|
|
if !wpn.ArcCW then return end
|
|
|
|
if !wpn.Attachments[slot] then return end
|
|
|
|
wpn.Attachments[slot].SlidePos = pos
|
|
end)
|
|
|
|
|
|
net.Receive("arccw_togglenum", function(len, ply)
|
|
local wpn = ply:GetActiveWeapon()
|
|
|
|
local slot = net.ReadUInt(8)
|
|
local num = net.ReadUInt(8)
|
|
|
|
if !wpn.ArcCW then return end
|
|
|
|
if !wpn.Attachments[slot] then return end
|
|
|
|
wpn.Attachments[slot].ToggleNum = num
|
|
|
|
wpn:AdjustAtts()
|
|
wpn:NetworkWeapon()
|
|
wpn:SetupModel(false)
|
|
wpn:SetupModel(true)
|
|
end)
|
|
|
|
|
|
net.Receive("arccw_asktoattach", function(len, ply)
|
|
local wpn = ply:GetActiveWeapon()
|
|
|
|
local slot = net.ReadUInt(8)
|
|
local attid = net.ReadUInt(24)
|
|
|
|
local att = ArcCW.AttachmentIDTable[attid]
|
|
|
|
if !wpn.ArcCW then return end
|
|
if !wpn.Attachments[slot] then return end
|
|
if !att then return end
|
|
|
|
wpn:Attach(slot, att)
|
|
end)
|
|
|
|
net.Receive("arccw_asktodetach", function(len, ply)
|
|
local wpn = ply:GetActiveWeapon()
|
|
|
|
local slot = net.ReadUInt(8)
|
|
|
|
if !wpn.ArcCW then return end
|
|
if !wpn.Attachments[slot] then return end
|
|
|
|
wpn:Detach(slot)
|
|
end)
|
|
|
|
net.Receive("arccw_asktodrop", function(len, ply)
|
|
|
|
local attid = net.ReadUInt(24)
|
|
local att = ArcCW.AttachmentIDTable[attid]
|
|
|
|
if ArcCW.ConVars["attinv_free"]:GetBool() then return end
|
|
if ArcCW.ConVars["attinv_lockmode"]:GetBool() then return end
|
|
if ArcCW.ConVars["enable_customization"]:GetInt() < 0 then return end
|
|
if !ArcCW.ConVars["enable_dropping"]:GetBool() then return end
|
|
|
|
if !att then return end
|
|
|
|
local atttbl = ArcCW.AttachmentTable[att]
|
|
|
|
if !atttbl then return end
|
|
if atttbl.Free then return end
|
|
if ArcCW:PlayerGetAtts(ply, att) < 1 then return end
|
|
|
|
-- better to do it like this in case you don't want to generate the attachment entities
|
|
local ent = ents.Create("arccw_att_base")
|
|
if !IsValid(ent) then return end
|
|
ent:SetPos(ply:EyePos() + ply:EyeAngles():Forward() * 32)
|
|
|
|
ent:SetNWInt("attid", attid)
|
|
|
|
ent.GiveAttachments = {[att] = 1}
|
|
ent.Model = atttbl.DroppedModel or atttbl.Model or "models/Items/BoxSRounds.mdl"
|
|
ent.Icon = atttbl.Icon
|
|
ent.PrintName = atttbl.PrintName or att
|
|
|
|
ent:Spawn()
|
|
timer.Simple(0, function()
|
|
local phys = ent:GetPhysicsObject()
|
|
if phys:IsValid() then
|
|
phys:SetVelocity(ply:EyeAngles():Forward() * 32 * math.max(phys:GetMass(), 4))
|
|
end
|
|
end)
|
|
ArcCW:PlayerTakeAtt(ply, att, 1)
|
|
ArcCW:PlayerSendAttInv(ply)
|
|
end)
|
|
|
|
if SERVER then
|
|
net.Receive("arccw_applypreset", function(len, ply)
|
|
local wpn = net.ReadEntity()
|
|
|
|
if wpn:GetOwner() != ply or !wpn.ArcCW then return end
|
|
if ply.ArcCW_DisableAutosave or ply.ArcCW_Sandbox_RandomAtts then
|
|
ply.ArcCW_Sandbox_RandomAtts = nil
|
|
return
|
|
end
|
|
|
|
for k, v in pairs(wpn.Attachments) do
|
|
wpn:Detach(k, true, true)
|
|
end
|
|
|
|
wpn.Attachments.BaseClass = nil -- AGHHHHHHHHHH
|
|
for k, v in SortedPairs(wpn.Attachments) do
|
|
local attid = net.ReadUInt(ArcCW.GetBitNecessity())
|
|
|
|
local attname = ArcCW.AttachmentIDTable[attid or 0] or ""
|
|
local atttbl = ArcCW.AttachmentTable[attname]
|
|
if !atttbl then continue end
|
|
|
|
wpn:Attach(k, attname, true, true)
|
|
|
|
if net.ReadBool() then
|
|
v.SlidePos = net.ReadFloat()
|
|
v.SlidePos = atttbl.MountPositionOverride or v.SlidePos
|
|
else
|
|
v.SlidePos = 0.5
|
|
end
|
|
|
|
if atttbl.ToggleStats then
|
|
v.ToggleNum = math.Clamp(net.ReadUInt(8), 1, #atttbl.ToggleStats)
|
|
else
|
|
v.ToggleNum = 1
|
|
end
|
|
end
|
|
|
|
wpn:AdjustAtts()
|
|
wpn:RefreshBGs()
|
|
|
|
if ply.ArcCW_Sandbox_FirstSpawn then
|
|
-- Curiously, RestoreAmmo has a sync delay only in singleplayer
|
|
ply.ArcCW_Sandbox_FirstSpawn = nil
|
|
wpn:RestoreAmmo()
|
|
end
|
|
|
|
wpn:NetworkWeapon()
|
|
wpn:SetupModel(false)
|
|
wpn:SetupModel(true)
|
|
|
|
net.Start("arccw_applypreset")
|
|
net.WriteEntity(wpn)
|
|
net.Send(ply)
|
|
end)
|
|
else
|
|
net.Receive("arccw_applypreset", function()
|
|
local wpn = net.ReadEntity()
|
|
if !IsValid(wpn) then return end
|
|
wpn:SavePreset("autosave")
|
|
end)
|
|
end
|
|
|
|
function ArcCW:PlayerSendAttInv(ply)
|
|
if ArcCW.ConVars["attinv_free"]:GetBool() then return end
|
|
|
|
if !IsValid(ply) then return end
|
|
|
|
if !ply.ArcCW_AttInv then return end
|
|
|
|
net.Start("arccw_sendattinv")
|
|
|
|
net.WriteUInt(table.Count(ply.ArcCW_AttInv), 32)
|
|
|
|
for att, count in pairs(ply.ArcCW_AttInv) do
|
|
local atttbl = ArcCW.AttachmentTable[att]
|
|
local attid = atttbl.ID
|
|
net.WriteUInt(attid, ArcCW.GetBitNecessity())
|
|
net.WriteUInt(count, 32)
|
|
end
|
|
|
|
net.Send(ply)
|
|
end
|
|
|
|
end
|