add sborka
This commit is contained in:
161
garrysmod/addons/tacrp/lua/weapons/tacrp_base/cl_autosight.lua
Normal file
161
garrysmod/addons/tacrp/lua/weapons/tacrp_base/cl_autosight.lua
Normal file
@@ -0,0 +1,161 @@
|
||||
SWEP.AutoSightSlot = nil
|
||||
|
||||
SWEP.AutoSightPos = nil
|
||||
SWEP.AutoSightAng = nil
|
||||
|
||||
SWEP.ClientProxyModel = nil
|
||||
|
||||
function SWEP:GenerateAutoSight()
|
||||
local slot
|
||||
|
||||
self.AutoSightPos = nil
|
||||
self.AutoSightAng = nil
|
||||
|
||||
for i, k in ipairs(self.Attachments) do
|
||||
if !k.Installed then continue end
|
||||
local atttbl = TacRP.GetAttTable(k.Installed)
|
||||
|
||||
if atttbl.SightPos and atttbl.SightAng then
|
||||
slot = i
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if !slot then return end
|
||||
|
||||
local slottbl = self.Attachments[slot]
|
||||
|
||||
if !slottbl.Installed then return end
|
||||
|
||||
local bone = slottbl.Bone
|
||||
local offset_pos = slottbl.Pos_VM
|
||||
local offset_ang = slottbl.Ang_VM
|
||||
|
||||
for _, ele in ipairs(self:GetElements()) do
|
||||
if !ele.AttPosMods or !ele.AttPosMods[slot] then continue end
|
||||
if wm then
|
||||
if ele.AttPosMods[slot].Pos_WM then
|
||||
offset_pos = ele.AttPosMods[slot].Pos_WM
|
||||
end
|
||||
if ele.AttPosMods[slot].Ang_WM then
|
||||
offset_ang = ele.AttPosMods[slot].Ang_WM
|
||||
end
|
||||
if ele.AttPosMods[slot].WMBone then
|
||||
bone = ele.AttPosMods[slot].WMBone
|
||||
end
|
||||
else
|
||||
if ele.AttPosMods[slot].Pos_VM then
|
||||
offset_pos = ele.AttPosMods[slot].Pos_VM
|
||||
end
|
||||
if ele.AttPosMods[slot].Ang_VM then
|
||||
offset_ang = ele.AttPosMods[slot].Ang_VM
|
||||
end
|
||||
if ele.AttPosMods[slot].Bone then
|
||||
bone = ele.AttPosMods[slot].Bone
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if !bone then return end
|
||||
|
||||
local atttbl = TacRP.GetAttTable(slottbl.Installed)
|
||||
|
||||
local mdl = ClientsideModel(self.ViewModel)
|
||||
mdl:SetPos(Vector(0, 0, 0))
|
||||
mdl:SetAngles(Angle(0, 0, 0))
|
||||
mdl:SetNoDraw(true)
|
||||
|
||||
local anim = self:TranslateSequence("idle")
|
||||
local seq = mdl:LookupSequence(anim)
|
||||
|
||||
mdl:ResetSequence(seq)
|
||||
|
||||
mdl:SetupBones()
|
||||
|
||||
if !IsValid(mdl) then return end
|
||||
|
||||
local boneid = mdl:LookupBone(bone)
|
||||
|
||||
local bpos = mdl:GetBoneMatrix(boneid):GetTranslation()
|
||||
local bang = self.CorrectiveBoneAng and Angle(self.CorrectiveBoneAng) or mdl:GetBoneMatrix(boneid):GetAngles()
|
||||
|
||||
SafeRemoveEntity(mdl)
|
||||
|
||||
local apos, aang = bpos, bang
|
||||
|
||||
apos:Add(bang:Forward() * offset_pos.x)
|
||||
apos:Add(bang:Right() * offset_pos.y)
|
||||
apos:Add(bang:Up() * offset_pos.z)
|
||||
|
||||
aang:RotateAroundAxis(aang:Right(), offset_ang.p)
|
||||
aang:RotateAroundAxis(aang:Up(), offset_ang.y)
|
||||
aang:RotateAroundAxis(aang:Forward(), offset_ang.r)
|
||||
|
||||
local moffset = (atttbl.ModelOffset or Vector(0, 0, 0)) * (slottbl.VMScale or 1)
|
||||
|
||||
apos:Add(aang:Forward() * moffset.x)
|
||||
apos:Add(aang:Right() * moffset.y)
|
||||
apos:Add(aang:Up() * moffset.z)
|
||||
|
||||
local vpos, vang = WorldToLocal(apos, aang, Vector(0, 0, 0), Angle(0, 0, 0))
|
||||
|
||||
local x = vpos.x
|
||||
local y = vpos.y
|
||||
local z = vpos.z
|
||||
|
||||
vpos.x = -y
|
||||
vpos.y = x
|
||||
vpos.z = z
|
||||
|
||||
vpos = vpos + (self.CorrectivePos or Vector(0, 0, 0))
|
||||
vang = vang + (self.CorrectiveAng or Angle(0, 0, 0))
|
||||
|
||||
self.AutoSightPos, self.AutoSightAng = -vpos, -vang
|
||||
end
|
||||
|
||||
function SWEP:GetSightPositions()
|
||||
local apos, aang = self.AutoSightPos, self.AutoSightAng
|
||||
|
||||
if apos and aang then
|
||||
return apos, aang
|
||||
elseif self:GetOwner() != LocalPlayer() then
|
||||
self:GenerateAutoSight() -- Not generated for spectators so must be done here
|
||||
return self.AutoSightPos or self.SightPos, self.AutoSightAng or self.SightAng
|
||||
else
|
||||
return self.SightPos, self.SightAng
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:GetExtraSightPosition()
|
||||
local epos
|
||||
local eang
|
||||
|
||||
local scale = 1
|
||||
|
||||
for i, k in ipairs(self.Attachments) do
|
||||
if !k.Installed then continue end
|
||||
local atttbl = TacRP.GetAttTable(k.Installed)
|
||||
|
||||
if atttbl.SightPos and atttbl.SightAng then
|
||||
epos = atttbl.SightPos
|
||||
eang = atttbl.SightAng
|
||||
scale = k.VMScale or 1
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
local pos = Vector(0, 0, 0)
|
||||
local ang = Angle(0, 0, 0)
|
||||
|
||||
if epos then
|
||||
pos:Set(epos)
|
||||
end
|
||||
|
||||
if eang then
|
||||
ang:Set(eang)
|
||||
end
|
||||
|
||||
pos = pos * scale
|
||||
|
||||
return pos, ang
|
||||
end
|
||||
35
garrysmod/addons/tacrp/lua/weapons/tacrp_base/cl_camera.lua
Normal file
35
garrysmod/addons/tacrp/lua/weapons/tacrp_base/cl_camera.lua
Normal file
@@ -0,0 +1,35 @@
|
||||
SWEP.SmoothedMagnification = 1
|
||||
|
||||
function SWEP:CalcView(ply, pos, ang, fov)
|
||||
local rec = (self:GetLastRecoilTime() + self:RecoilDuration()) - CurTime()
|
||||
|
||||
rec = math.max(rec, 0)
|
||||
|
||||
rec = rec * (self:GetValue("RecoilKick") + 1) * (self:GetRecoilAmount() / self:GetValue("RecoilMaximum")) ^ 0.75
|
||||
|
||||
if !game.SinglePlayer() then
|
||||
rec = rec * 0.5 -- ???????????????
|
||||
end
|
||||
|
||||
if rec > 0 then
|
||||
ang.r = ang.r + (math.sin(CurTime() * 70.151) * rec)
|
||||
end
|
||||
|
||||
local mag = Lerp(self:GetSightAmount() ^ 3, 1, self:GetMagnification())
|
||||
|
||||
if self:GetHoldBreathAmount() > 0 then
|
||||
mag = mag * (1 + self:GetHoldBreathAmount() * 0.15)
|
||||
end
|
||||
|
||||
local diff = math.abs(self.SmoothedMagnification - mag)
|
||||
|
||||
self.SmoothedMagnification = math.Approach(self.SmoothedMagnification, mag, FrameTime() * diff * (self.SmoothedMagnification > mag and 10 or 5))
|
||||
|
||||
fov = fov / self.SmoothedMagnification
|
||||
|
||||
self.TacRPLastFOV = fov
|
||||
|
||||
fov = fov - rec
|
||||
|
||||
return pos, ang, fov
|
||||
end
|
||||
259
garrysmod/addons/tacrp/lua/weapons/tacrp_base/cl_cornershot.lua
Normal file
259
garrysmod/addons/tacrp/lua/weapons/tacrp_base/cl_cornershot.lua
Normal file
@@ -0,0 +1,259 @@
|
||||
local rt_w = 426
|
||||
local rt_h = 240
|
||||
|
||||
local rtmat = GetRenderTarget("tacrp_pipscope", rt_w, rt_h, false)
|
||||
|
||||
local lastblindfire = false
|
||||
local blindfiretime = 0
|
||||
|
||||
local csm_boot_1 = Material("tacrp/hud/cornershot_boot_1.png", "mips smooth")
|
||||
local csm_boot_2 = Material("tacrp/hud/cornershot_boot_2.png", "mips smooth")
|
||||
local csm_boot_3 = Material("tacrp/hud/cornershot_boot_3.png", "mips smooth")
|
||||
|
||||
local csm_1 = Material("tacrp/hud/cornershot_1.png", "mips smooth")
|
||||
local csm_2 = Material("tacrp/hud/cornershot_2.png", "mips smooth")
|
||||
|
||||
local csm_n_1 = Material("tacrp/hud/cornershot_n_1.png", "mips smooth")
|
||||
local csm_n_2 = Material("tacrp/hud/cornershot_n_2.png", "mips smooth")
|
||||
|
||||
local noise1 = Material("tacrp/hud/noise1.png")
|
||||
local noise2 = Material("tacrp/hud/noise2.png")
|
||||
local noise3 = Material("tacrp/hud/noise3.png")
|
||||
local noise4 = Material("tacrp/hud/noise4.png")
|
||||
|
||||
local noisemats = {
|
||||
noise1,
|
||||
noise2,
|
||||
noise3,
|
||||
noise4
|
||||
}
|
||||
|
||||
local lastrendertime = 0
|
||||
|
||||
local fps = 30
|
||||
|
||||
function SWEP:DoRT()
|
||||
if !self:GetBlindFire() and !IsValid(self:GetCornershotEntity()) then lastblindfire = false return end
|
||||
if TacRP.OverDraw then return end
|
||||
|
||||
if !lastblindfire then
|
||||
blindfiretime = 0
|
||||
end
|
||||
|
||||
if lastrendertime > CurTime() - (1 / fps) then return end
|
||||
|
||||
local angles = self:GetShootDir()
|
||||
local origin = self:GetMuzzleOrigin()
|
||||
|
||||
if IsValid(self:GetCornershotEntity()) then
|
||||
origin = self:GetCornershotEntity():LocalToWorld(self:GetCornershotEntity().CornershotOffset)
|
||||
angles = self:GetCornershotEntity():LocalToWorldAngles(self:GetCornershotEntity().CornershotAngles)
|
||||
TacRP.CornerCamDrawSelf = true
|
||||
elseif self:GetBlindFireMode() == TacRP.BLINDFIRE_KYS then
|
||||
local bone = self:GetOwner():LookupBone("ValveBiped.Bip01_R_Hand")
|
||||
|
||||
if bone then
|
||||
local pos, ang = self:GetOwner():GetBonePosition(bone)
|
||||
|
||||
angles = ang
|
||||
angles:RotateAroundAxis(angles:Forward(), 180)
|
||||
origin = pos
|
||||
|
||||
TacRP.CornerCamDrawSelf = true
|
||||
end
|
||||
end
|
||||
|
||||
local rt = {
|
||||
x = 0,
|
||||
y = 0,
|
||||
w = rt_w,
|
||||
h = rt_h,
|
||||
aspect = 4 / 3,
|
||||
angles = angles,
|
||||
origin = origin,
|
||||
drawviewmodel = false,
|
||||
fov = 40,
|
||||
znear = 6
|
||||
}
|
||||
|
||||
render.PushRenderTarget(rtmat, 0, 0, rt_w, rt_h)
|
||||
|
||||
if blindfiretime >= 1 or blindfiretime == 0 then
|
||||
TacRP.OverDraw = true
|
||||
render.RenderView(rt)
|
||||
TacRP.OverDraw = false
|
||||
end
|
||||
|
||||
TacRP.CornerCamDrawSelf = false
|
||||
|
||||
if self:GetTactical() then
|
||||
DrawColorModify({
|
||||
["$pp_colour_addr"] = 0.25 * 132 / 255,
|
||||
["$pp_colour_addg"] = 0.25 * 169 / 255,
|
||||
["$pp_colour_addb"] = 0.25 * 154 / 255,
|
||||
["$pp_colour_brightness"] = 0.2,
|
||||
["$pp_colour_contrast"] = 0.85,
|
||||
["$pp_colour_colour"] = 0.95,
|
||||
["$pp_colour_mulr"] = 0,
|
||||
["$pp_colour_mulg"] = 0,
|
||||
["$pp_colour_mulb"] = 0
|
||||
})
|
||||
else
|
||||
DrawColorModify({
|
||||
["$pp_colour_addr"] = 0.25 * 132 / 255,
|
||||
["$pp_colour_addg"] = 0.25 * 169 / 255,
|
||||
["$pp_colour_addb"] = 0.25 * 154 / 255,
|
||||
["$pp_colour_brightness"] = -0.1,
|
||||
["$pp_colour_contrast"] = 5,
|
||||
["$pp_colour_colour"] = 0,
|
||||
["$pp_colour_mulr"] = 0,
|
||||
["$pp_colour_mulg"] = 0,
|
||||
["$pp_colour_mulb"] = 0
|
||||
})
|
||||
|
||||
DrawBloom(0.25, 0.5, 16, 8, 1, 1, 1, 1, 1)
|
||||
end
|
||||
|
||||
-- if blindfiretime < 0.33 then
|
||||
-- surface.SetMaterial(csm_boot_1)
|
||||
-- elseif blindfiretime < 0.66 then
|
||||
-- surface.SetMaterial(csm_boot_2)
|
||||
-- elseif blindfiretime < 1.25 then
|
||||
-- surface.SetMaterial(csm_boot_3)
|
||||
-- else
|
||||
-- end
|
||||
|
||||
cam.Start2D()
|
||||
|
||||
render.ClearDepth()
|
||||
|
||||
if blindfiretime < 1 then
|
||||
if blindfiretime < 0.75 then
|
||||
surface.SetDrawColor(255, 255, 255)
|
||||
surface.SetMaterial(noisemats[math.random(#noisemats)])
|
||||
surface.DrawTexturedRect(0, 0, rt_w, rt_h)
|
||||
else
|
||||
surface.SetDrawColor(0, 0, 0)
|
||||
surface.DrawRect(0, 0, rt_w, rt_h)
|
||||
end
|
||||
|
||||
DrawColorModify({
|
||||
["$pp_colour_addr"] = 0.25 * 132 / 255,
|
||||
["$pp_colour_addg"] = 0.25 * 169 / 255,
|
||||
["$pp_colour_addb"] = 0.25 * 154 / 255,
|
||||
["$pp_colour_brightness"] = 0.2,
|
||||
["$pp_colour_contrast"] = 0.85,
|
||||
["$pp_colour_colour"] = 0.95,
|
||||
["$pp_colour_mulr"] = 0,
|
||||
["$pp_colour_mulg"] = 0,
|
||||
["$pp_colour_mulb"] = 0
|
||||
})
|
||||
end
|
||||
|
||||
if blindfiretime < 0.2 then
|
||||
surface.SetMaterial(csm_boot_1)
|
||||
elseif blindfiretime < 0.4 then
|
||||
surface.SetMaterial(csm_boot_2)
|
||||
elseif blindfiretime < 0.6 then
|
||||
surface.SetMaterial(csm_boot_3)
|
||||
else
|
||||
if self:GetTactical() then
|
||||
if math.sin(CurTime() * 3) > 0.5 then
|
||||
surface.SetMaterial(csm_1)
|
||||
else
|
||||
surface.SetMaterial(csm_2)
|
||||
end
|
||||
else
|
||||
if math.sin(CurTime() * 3) > 0.5 then
|
||||
surface.SetMaterial(csm_n_1)
|
||||
else
|
||||
surface.SetMaterial(csm_n_2)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
surface.SetDrawColor(255, 255, 255)
|
||||
surface.DrawTexturedRect(0, 0, rt_w, rt_h)
|
||||
cam.End2D()
|
||||
|
||||
render.PopRenderTarget()
|
||||
|
||||
blindfiretime = blindfiretime + (math.random(0, 5) * math.random(0, 5) * (1 / fps) / 6.25)
|
||||
|
||||
lastblindfire = true
|
||||
lastrendertime = CurTime()
|
||||
end
|
||||
|
||||
function SWEP:DoCornershot()
|
||||
|
||||
if !self:GetBlindFire() and !IsValid(self:GetCornershotEntity()) then lastblindfire = false return end
|
||||
|
||||
local w = TacRP.SS(640 / 4)
|
||||
local h = TacRP.SS(480 / 4)
|
||||
local x = (ScrW() - w) / 2
|
||||
local y = (ScrH() - h) / 2
|
||||
-- y = y + (ScrH() / 4)
|
||||
render.DrawTextureToScreenRect(rtmat, x, y, w, h)
|
||||
end
|
||||
|
||||
hook.Add("ShouldDrawLocalPlayer", "TacRP_CornerCamDrawSelf", function(ply)
|
||||
if TacRP.CornerCamDrawSelf then
|
||||
return true
|
||||
end
|
||||
end)
|
||||
|
||||
SWEP.NearWallTick = 0
|
||||
SWEP.NearWallCached = false
|
||||
|
||||
local traceResults = {}
|
||||
|
||||
local traceData = {
|
||||
start = true,
|
||||
endpos = true,
|
||||
filter = true,
|
||||
mask = MASK_SHOT_HULL,
|
||||
output = traceResults
|
||||
}
|
||||
|
||||
local VECTOR = FindMetaTable("Vector")
|
||||
local vectorAdd = VECTOR.Add
|
||||
local vectorMul = VECTOR.Mul
|
||||
|
||||
local angleForward = FindMetaTable("Angle").Forward
|
||||
local entityGetOwner = FindMetaTable("Entity").GetOwner
|
||||
|
||||
function SWEP:GetNearWallAmount()
|
||||
local now = engine.TickCount()
|
||||
|
||||
if !TacRP.ConVars["nearwall"]:GetBool() or LocalPlayer():GetMoveType() == MOVETYPE_NOCLIP then
|
||||
return 0
|
||||
end
|
||||
|
||||
if self.NearWallTick == now then
|
||||
return self.NearWallCached
|
||||
end
|
||||
|
||||
local length = 32
|
||||
|
||||
local startPos = self:GetMuzzleOrigin()
|
||||
|
||||
local endPos = angleForward(self:GetShootDir())
|
||||
vectorMul(endPos, length)
|
||||
vectorAdd(endPos, startPos)
|
||||
|
||||
traceData.start = startPos
|
||||
traceData.endpos = endPos
|
||||
traceData.filter = entityGetOwner(self)
|
||||
|
||||
util.TraceLine(traceData)
|
||||
local hit = 1 - traceResults.Fraction
|
||||
|
||||
self.NearWallCached = hit
|
||||
self.NearWallTick = now
|
||||
|
||||
return hit
|
||||
end
|
||||
|
||||
function SWEP:ThinkNearWall()
|
||||
self:GetNearWallAmount()
|
||||
end
|
||||
1442
garrysmod/addons/tacrp/lua/weapons/tacrp_base/cl_customize.lua
Normal file
1442
garrysmod/addons/tacrp/lua/weapons/tacrp_base/cl_customize.lua
Normal file
File diff suppressed because it is too large
Load Diff
242
garrysmod/addons/tacrp/lua/weapons/tacrp_base/cl_hint.lua
Normal file
242
garrysmod/addons/tacrp/lua/weapons/tacrp_base/cl_hint.lua
Normal file
@@ -0,0 +1,242 @@
|
||||
local glyphs = {
|
||||
["MOUSE1"] = Material("tacrp/glyphs/shared_mouse_l_click_lg.png", "mips smooth"),
|
||||
["MOUSE2"] = Material("tacrp/glyphs/shared_mouse_r_click_lg.png", "mips smooth"),
|
||||
["MOUSE3"] = Material("tacrp/glyphs/shared_mouse_mid_click_lg.png", "mips smooth"),
|
||||
["MOUSE4"] = Material("tacrp/glyphs/shared_mouse_4_lg.png", "mips smooth"),
|
||||
["MOUSE5"] = Material("tacrp/glyphs/shared_mouse_5_lg.png", "mips smooth"),
|
||||
|
||||
["MWHEELUP"] = Material("tacrp/glyphs/shared_mouse_scroll_up_lg.png", "mips smooth"),
|
||||
["MWHEELDOWN"] = Material("tacrp/glyphs/shared_mouse_scroll_down_lg.png", "mips smooth"),
|
||||
}
|
||||
|
||||
local rename = {
|
||||
KP_INS = "KP 0",
|
||||
KP_END = "KP 1",
|
||||
KP_DOWNARROW = "KP 2",
|
||||
KP_PGDN = "KP 3",
|
||||
KP_LEFTARROW = "KP 4",
|
||||
KP_5 = "KP 5",
|
||||
KP_RIGHTARROW = "KP 6",
|
||||
KP_HOME = "KP 7",
|
||||
KP_UPARROW = "KP 8",
|
||||
KP_PGUP = "KP 9",
|
||||
KP_SLASH = "KP /",
|
||||
KP_MULTIPLY = "KP *",
|
||||
KP_MINUS = "KP -",
|
||||
KP_PLUS = "KP +",
|
||||
KP_ENTER = "KP ENTER",
|
||||
KP_DEL = "KP .",
|
||||
}
|
||||
|
||||
local mat_bipod = Material("tacrp/hud/bipod.png", "mips smooth")
|
||||
|
||||
SWEP.CachedCapabilities = {}
|
||||
function SWEP:GetHintCapabilities()
|
||||
self.CachedCapabilities = {}
|
||||
|
||||
if self:GetValue("PrimaryMelee") then
|
||||
self.CachedCapabilities["+attack"] = {so = 0, str = "hint.melee"}
|
||||
elseif self:GetValue("PrimaryGrenade") then
|
||||
self.CachedCapabilities["+attack"] = {so = 0, str = "hint.quicknade.over"}
|
||||
self.CachedCapabilities["+attack2"] = {so = 0.1, str = "hint.quicknade.under"}
|
||||
end
|
||||
-- hopefully you don't need me to tell you how to shoot a gun
|
||||
|
||||
local bind_customize = "+menu_context"
|
||||
if TacRP.GetKeyIsBound("+tacrp_customize") then
|
||||
bind_customize = "+tacrp_customize"
|
||||
end
|
||||
|
||||
if self:GetScopeLevel() != 0 then
|
||||
if TacRP.ConVars["togglepeek"]:GetBool() then
|
||||
self.CachedCapabilities[bind_customize] = {so = 2, str = "hint.peek.toggle"}
|
||||
else
|
||||
self.CachedCapabilities[bind_customize] = {so = 2, str = "hint.peek.hold"}
|
||||
end
|
||||
|
||||
if self:CanHoldBreath() then
|
||||
self.CachedCapabilities["+speed"] = {so = 2.1, str = "hint.hold_breath"}
|
||||
end
|
||||
elseif #self.Attachments > 0 then
|
||||
self.CachedCapabilities[bind_customize] = {so = 1, str = "hint.customize"}
|
||||
else
|
||||
self.CachedCapabilities[bind_customize] = {so = 1, str = "hint.inspect"}
|
||||
end
|
||||
|
||||
if self:GetFiremodeAmount() > 1 and !self:GetSafe() then
|
||||
self.CachedCapabilities["+use/+reload"] = {so = 11, str = "hint.firemode"}
|
||||
end
|
||||
if self:GetFiremodeAmount() > 0 and !self:DoForceSightsBehavior() then
|
||||
if self:GetSafe() then
|
||||
self.CachedCapabilities["+use/+attack2"] = {so = 12, str = "hint.safety.turn_off"}
|
||||
else
|
||||
self.CachedCapabilities["+use/+attack2"] = {so = 12, str = "hint.safety.turn_on"}
|
||||
end
|
||||
end
|
||||
|
||||
if self:GetValue("CanMeleeAttack") and !self:GetValue("PrimaryMelee") then
|
||||
local bind = "+use/+attack"
|
||||
if TacRP.GetKeyIsBound("+tacrp_melee") then
|
||||
bind = TacRP.GetBindKey("+tacrp_melee")
|
||||
elseif self:DoOldSchoolScopeBehavior() then
|
||||
bind = "+attack2"
|
||||
end
|
||||
self.CachedCapabilities[bind] = {so = 30, str = "hint.melee"}
|
||||
end
|
||||
|
||||
if self:GetValue("CanToggle") and TacRP.ConVars["toggletactical"]:GetBool() then
|
||||
local tactical_text = self:GetValue("CustomTacticalHint") or TacRP:GetPhrase("hint.toggle_tactical", {TacRP:TryTranslate(self:GetValue("TacticalName") or "hint.tac")})
|
||||
local bind = nil
|
||||
if TacRP.GetKeyIsBound("+tacrp_tactical") then
|
||||
bind = "+tacrp_tactical"
|
||||
end
|
||||
if TacRP.ConVars["flashlight_alt"]:GetBool() then
|
||||
self.CachedCapabilities[bind or "+walk/impulse 100"] = {so = 31, str = tactical_text}
|
||||
self.CachedCapabilities["impulse 100"] = {so = 32, str = "hint.hl2_flashlight"}
|
||||
else
|
||||
self.CachedCapabilities[bind or "impulse 100"] = {so = 31, str = tactical_text}
|
||||
self.CachedCapabilities["+walk/impulse 100"] = {so = 32, str = "hint.hl2_flashlight"}
|
||||
end
|
||||
end
|
||||
|
||||
-- blindfire / quickthrow
|
||||
if self:GetValue("CanBlindFire") and self:GetScopeLevel() == 0 and !self:GetSafe() then
|
||||
if TacRP.ConVars["blindfiremenu"]:GetBool() then
|
||||
self.CachedCapabilities["+zoom"] = {so = 39, str = "hint.blindfire.menu"}
|
||||
else
|
||||
if self:GetOwner():KeyDown(IN_ZOOM) then
|
||||
self.CachedCapabilities = {}
|
||||
self.CachedCapabilities["+zoom/+forward"] = {so = 39, str = "hint.blindfire.up"}
|
||||
self.CachedCapabilities["+zoom/+moveleft"] = {so = 39.1, str = "hint.blindfire.left"}
|
||||
self.CachedCapabilities["+zoom/+moveright"] = {so = 39.2, str = "hint.blindfire.right"}
|
||||
self.CachedCapabilities["+zoom/+back"] = {so = 39.3, str = "hint.blindfire.cancel"}
|
||||
if !TacRP.ConVars["idunwannadie"]:GetBool() then
|
||||
self.CachedCapabilities["+zoom/+speed/+walk"] = {so = 39.4, str = "hint.blindfire.kys"}
|
||||
end
|
||||
return self.CachedCapabilities
|
||||
else
|
||||
self.CachedCapabilities["+zoom"] = {so = 39, str = "hint.blindfire"}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if self:IsQuickNadeAllowed() then
|
||||
local bound1, bound2 = TacRP.GetKeyIsBound("+grenade1"), TacRP.GetKeyIsBound("+grenade2")
|
||||
if bound1 then
|
||||
self.CachedCapabilities["+grenade1"] = {so = 35, str = "hint.quicknade.throw"}
|
||||
end
|
||||
if bound2 then
|
||||
if TacRP.ConVars["nademenu"]:GetBool() then
|
||||
if TacRP.ConVars["nademenu_click"]:GetBool() and self.GrenadeMenuAlpha == 1 then
|
||||
self.CachedCapabilities = {}
|
||||
self.CachedCapabilities["+grenade2"] = {so = 36, str = "hint.quicknade.menu"}
|
||||
self.CachedCapabilities["+grenade2/+attack"] = {so = 36.1, str = "hint.quicknade.over"}
|
||||
self.CachedCapabilities["+grenade2/+attack2"] = {so = 36.2, str = "hint.quicknade.under"}
|
||||
if TacRP.AreTheGrenadeAnimsReadyYet then
|
||||
self.CachedCapabilities["+grenade2/MOUSE3"] = {so = 36.3, str = "hint.quicknade.pull_out"}
|
||||
end
|
||||
-- if TacRP.GetKeyIsBound("+grenade1") then
|
||||
-- self.CachedCapabilities["+grenade1"] = {so = 36.4, str = "Quickthrow"}
|
||||
-- end
|
||||
|
||||
return self.CachedCapabilities
|
||||
else
|
||||
self.CachedCapabilities["+grenade2"] = {so = 36, str = "hint.quicknade.menu"}
|
||||
end
|
||||
else
|
||||
self.CachedCapabilities["+grenade2"] = {so = 36, str = "hint.quicknade.next"}
|
||||
self.CachedCapabilities["+walk/+grenade2"] = {so = 37, str = "hint.quicknade.prev"}
|
||||
end
|
||||
end
|
||||
if !bound2 and !bound1 then
|
||||
self.CachedCapabilities["+grenade1"] = {so = 36, str = "hint.quicknade.throw"}
|
||||
end
|
||||
end
|
||||
|
||||
if engine.ActiveGamemode() == "terrortown" then
|
||||
self.CachedCapabilities["+use/+zoom"] = {so = 1001, str = "hint.ttt.radio"}
|
||||
self.CachedCapabilities["+use/+menu_context"] = {so = 1002, str = "hint.ttt.shop"}
|
||||
end
|
||||
|
||||
self:RunHook("Hook_GetHintCapabilities", self.CachedCapabilities)
|
||||
|
||||
return self.CachedCapabilities
|
||||
end
|
||||
|
||||
SWEP.LastHintLife = 0
|
||||
function SWEP:DrawHints()
|
||||
if LocalPlayer() != self:GetOwner() then return end
|
||||
local a = TacRP.ConVars["hints_always"]:GetBool() and 1 or math.Clamp(((self.LastHintLife + 4) - CurTime()) / 1, 0, 1)
|
||||
if a <= 0 then return end
|
||||
|
||||
local font = TacRP.ConVars["hints_altfont"]:GetBool() and "TacRP_Myriad_Pro_8" or "TacRP_HD44780A00_5x8_5"
|
||||
|
||||
local caps = self:GetHintCapabilities()
|
||||
|
||||
local clr_w = Color(255, 255, 255, a * 255)
|
||||
|
||||
local x, y = TacRP.SS(4), ScrH() / 2
|
||||
local row = TacRP.SS(12)
|
||||
local glyphsize = TacRP.SS(8)
|
||||
local w, h = TacRP.SS(100), table.Count(caps) * row
|
||||
surface.SetDrawColor(0, 0, 0, 150 * a)
|
||||
TacRP.DrawCorneredBox(x, y - h / 2, w, h, clr_w)
|
||||
local x2, x3 = TacRP.SS(6), TacRP.SS(30)
|
||||
local y2 = y - h / 2
|
||||
for k, v in SortedPairsByMemberValue(self.CachedCapabilities, "so") do
|
||||
local keys = string.Explode("/", k, false)
|
||||
surface.SetDrawColor(clr_w)
|
||||
local x_glyph = x2
|
||||
local y_glyph = y2 + row / 2
|
||||
for i = 1, #keys do
|
||||
local key = TacRP.GetBindKey(keys[i])
|
||||
if glyphs[key] then
|
||||
surface.SetMaterial(glyphs[key])
|
||||
surface.DrawTexturedRect(x + x_glyph, y_glyph - glyphsize / 2, TacRP.SS(8), glyphsize)
|
||||
-- surface.DrawOutlinedRect(x + x_glyph, y_glyph - glyphsize / 2, glyphsize, glyphsize, 2)
|
||||
x_glyph = x_glyph + glyphsize
|
||||
else
|
||||
key = rename[key] or key
|
||||
local addw = string.len(key) * TacRP.SS(3.5) + TacRP.SS(5)
|
||||
surface.DrawOutlinedRect(x + x_glyph, y_glyph - glyphsize / 2, addw, glyphsize, 1)
|
||||
draw.SimpleText(key, "TacRP_HD44780A00_5x8_5", x + x_glyph + addw / 2, y_glyph, clr_w, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
|
||||
x_glyph = x_glyph + addw
|
||||
end
|
||||
|
||||
if i < #keys then
|
||||
x_glyph = x_glyph + TacRP.SS(2)
|
||||
else
|
||||
x_glyph = x_glyph + TacRP.SS(4)
|
||||
end
|
||||
end
|
||||
|
||||
draw.SimpleText(TacRP:TryTranslate(v.str), font, x + math.max(x3, x_glyph), y2 + row / 2, clr_w, TEXT_ALIGN_LEFT, TEXT_ALIGN_CENTER)
|
||||
|
||||
y2 = y2 + row
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:DrawBipodHint(x, y, s)
|
||||
surface.SetDrawColor(0, 0, 0, 150)
|
||||
TacRP.DrawCorneredBox(x, y - s / 2, s, s, color_white)
|
||||
if self:CanBipod() then
|
||||
if self:GetInBipod() then
|
||||
surface.SetDrawColor(255, 255, 255, 255)
|
||||
else
|
||||
local c = math.sin(CurTime() * 8) * 25 + 175
|
||||
surface.SetDrawColor(c, c, c, 255)
|
||||
end
|
||||
surface.SetMaterial(mat_bipod)
|
||||
surface.DrawTexturedRect(x, y - s / 2, s, s)
|
||||
|
||||
if !self:GetInBipod() then
|
||||
surface.SetDrawColor(255, 255, 255, 255)
|
||||
surface.SetMaterial(glyphs["MOUSE2"])
|
||||
surface.DrawTexturedRect(x + s * 0.3333, y + s * 0.15, s / 3, s / 3)
|
||||
end
|
||||
else
|
||||
surface.SetDrawColor(100, 100, 100, 255)
|
||||
surface.SetMaterial(mat_bipod)
|
||||
surface.DrawTexturedRect(x, y - s / 2, s, s)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,84 @@
|
||||
function SWEP:DoHolosight(mdl)
|
||||
if TacRP.OverDraw then return end
|
||||
if self:GetSightAmount() <= 0 then return end
|
||||
|
||||
local ref = 64
|
||||
|
||||
render.UpdateScreenEffectTexture()
|
||||
render.ClearStencil()
|
||||
render.SetStencilEnable(true)
|
||||
render.SetStencilCompareFunction(STENCIL_ALWAYS)
|
||||
render.SetStencilPassOperation(STENCIL_REPLACE)
|
||||
render.SetStencilFailOperation(STENCIL_KEEP)
|
||||
render.SetStencilZFailOperation(STENCIL_KEEP)
|
||||
render.SetStencilWriteMask(255)
|
||||
render.SetStencilTestMask(255)
|
||||
|
||||
render.SetBlend(0)
|
||||
|
||||
render.SetStencilReferenceValue(ref)
|
||||
|
||||
mdl:DrawModel()
|
||||
|
||||
render.SetBlend(1)
|
||||
|
||||
render.SetStencilPassOperation(STENCIL_KEEP)
|
||||
render.SetStencilCompareFunction(STENCIL_EQUAL)
|
||||
|
||||
-- cam.Start2D()
|
||||
|
||||
-- surface.SetDrawColor(255, 255, 255)
|
||||
-- surface.DrawRect(0, 0, ScrW(), ScrH())
|
||||
|
||||
-- render.SetColorMaterial()
|
||||
-- render.DrawScreenQuad()
|
||||
|
||||
local img = self:GetValue("Holosight")
|
||||
|
||||
if img then
|
||||
local pos = self:GetOwner():EyePos()
|
||||
local dir = (self:GetShootDir() + self:GetOwner():GetViewPunchAngles() * 0.5):Forward() -- mdl:GetAngles():Forward()
|
||||
local size = 512 * TacRP.HoloSS
|
||||
|
||||
pos = pos + dir * 9000
|
||||
|
||||
-- cam.Start3D()
|
||||
|
||||
local eyedist = WorldToLocal(mdl:GetPos(), mdl:GetAngles(), EyePos(), EyeAngles()).x
|
||||
|
||||
render.DepthRange(0, 0.005 + (0.0005 * eyedist / 20))
|
||||
|
||||
render.SetMaterial(img)
|
||||
render.DrawQuadEasy(pos, -dir, size, size, Color(255, 255, 255), 180)
|
||||
|
||||
-- cam.End3D()
|
||||
|
||||
-- local toscreen = pos:ToScreen()
|
||||
|
||||
-- local x = toscreen.x
|
||||
-- local y = toscreen.y
|
||||
|
||||
-- local ss = TacRP.SS(32)
|
||||
-- local sx = x - (ss / 2)
|
||||
-- local sy = y - (ss / 2)
|
||||
|
||||
-- local shakey = math.min(cross * 35, 3)
|
||||
|
||||
-- sx = sx + math.Round(math.Rand(-shakey, shakey))
|
||||
-- sy = sy + math.Round(math.Rand(-shakey, shakey))
|
||||
|
||||
-- surface.SetMaterial(img)
|
||||
-- surface.SetDrawColor(255, 255, 255, 255)
|
||||
-- surface.DrawTexturedRect(sx, sy, ss, ss)
|
||||
|
||||
-- surface.SetDrawColor(0, 0, 0)
|
||||
-- surface.DrawRect(0, 0, w, sy)
|
||||
-- surface.DrawRect(0, sy + ss, w, h - sy)
|
||||
|
||||
-- surface.DrawRect(0, 0, sx, h)
|
||||
-- surface.DrawRect(sx + ss, 0, w - sx, h)
|
||||
end
|
||||
-- cam.End2D()
|
||||
|
||||
render.SetStencilEnable(false)
|
||||
end
|
||||
1015
garrysmod/addons/tacrp/lua/weapons/tacrp_base/cl_hud.lua
Normal file
1015
garrysmod/addons/tacrp/lua/weapons/tacrp_base/cl_hud.lua
Normal file
File diff suppressed because it is too large
Load Diff
140
garrysmod/addons/tacrp/lua/weapons/tacrp_base/cl_laser.lua
Normal file
140
garrysmod/addons/tacrp/lua/weapons/tacrp_base/cl_laser.lua
Normal file
@@ -0,0 +1,140 @@
|
||||
local lasermat = Material("effects/laser1")
|
||||
local flaremat = Material("effects/whiteflare")
|
||||
|
||||
function SWEP:DrawLaser(pos, ang, strength, thirdperson)
|
||||
strength = strength or 1
|
||||
|
||||
local alwaysacc = self:GetValue("LaserAlwaysAccurate")
|
||||
local behavior = (self:GetValue("ScopeHideWeapon") and self:IsInScope())
|
||||
local vm = self:GetOwner():IsPlayer() and self:GetOwner():GetViewModel()
|
||||
local curr_seq = IsValid(vm) and vm:GetSequenceName(vm:GetSequence())
|
||||
|
||||
local delta = behavior and 1 or 0
|
||||
|
||||
if IsValid(vm) and (alwaysacc or TacRP.ConVars["true_laser"]:GetBool()) and (self:GetBlindFireMode() <= 1) and !self:GetCustomize() and !behavior then
|
||||
local d1 = (CurTime() - self:GetNextSecondaryFire()) / 1
|
||||
if alwaysacc then
|
||||
d1 = 1
|
||||
elseif TacRP.ConVars["laser_beam"]:GetBool() then
|
||||
d1 = math.min((CurTime() - self:GetNextPrimaryFire()) / 2, (CurTime() - self:GetNextSecondaryFire()) / 1)
|
||||
elseif self:GetValue("RPM") < 120 then
|
||||
d1 = math.min((CurTime() - self:GetNextPrimaryFire()) / 0.5, (CurTime() - self:GetNextSecondaryFire()) / 1)
|
||||
end
|
||||
|
||||
local d2 = (curr_seq == "reload_start") and 0 or 1
|
||||
local d3 = (1 - math.min(self:GetAnimLockTime() - CurTime()) / vm:SequenceDuration(vm:GetSequence()))
|
||||
local d4 = self:CanShootInSprint() and 1 or (1 - self:GetSprintDelta()) ^ 2
|
||||
if self:DoForceSightsBehavior() then d4 = self:GetSprintDelta() * self:GetSightDelta() end
|
||||
local cutoff = 0.85
|
||||
d3 = math.max(d3 - cutoff, 0) / (1 - cutoff)
|
||||
|
||||
delta = math.Clamp(self:GetReloading() and 0 or math.min(d1, d2, d3, d4), 0, 1)
|
||||
|
||||
end
|
||||
|
||||
local pos_tr = self:GetMuzzleOrigin()
|
||||
|
||||
if behavior then
|
||||
ang = self:GetShootDir()
|
||||
else
|
||||
ang = LerpAngle(delta, ang, self:GetShootDir())
|
||||
end
|
||||
|
||||
local tr = util.TraceLine({
|
||||
start = pos_tr,
|
||||
endpos = pos_tr + (ang:Forward() * 30000),
|
||||
mask = MASK_OPAQUE,
|
||||
filter = self:GetOwner()
|
||||
})
|
||||
|
||||
if tr.StartSolid then return end
|
||||
local laser_pos = tr.HitPos + tr.HitNormal
|
||||
local adjusted_pos = thirdperson and laser_pos or TacRP.FormatViewModelAttachment(self.ViewModelFOV, laser_pos, false)
|
||||
laser_pos = LerpVector(delta, laser_pos, adjusted_pos)
|
||||
|
||||
if behavior then
|
||||
cam.Start3D()
|
||||
pos = pos - (ang:Forward() * 256)
|
||||
end
|
||||
|
||||
local col = self:GetValue("LaserColor")
|
||||
|
||||
if TacRP.ConVars["laser_beam"]:GetBool() then
|
||||
local width = math.Rand(0.1, 0.2) * strength
|
||||
render.SetMaterial(lasermat)
|
||||
render.DrawBeam(pos, laser_pos, width * 0.3, 0, 1, Color(200, 200, 200))
|
||||
render.DrawBeam(pos, laser_pos, width, 0, 1, col)
|
||||
end
|
||||
|
||||
if tr.Hit and !tr.HitSky then
|
||||
local mul = strength
|
||||
local rad = math.Rand(4, 6) * mul
|
||||
|
||||
render.SetMaterial(flaremat)
|
||||
render.DrawSprite(laser_pos, rad, rad, col)
|
||||
render.DrawSprite(laser_pos, rad * 0.3, rad * 0.3, Color(200, 200, 200))
|
||||
|
||||
debugoverlay.Cross(tr.HitPos, 4, FrameTime() * 2, col)
|
||||
end
|
||||
|
||||
if behavior then
|
||||
cam.End3D()
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:DrawLasers(wm)
|
||||
wm = wm or false
|
||||
|
||||
if self.Laser and self:GetTactical() then
|
||||
local power = self.LaserPower or 2
|
||||
if wm and self.LaserQCAttachmentWM then
|
||||
local att = self:GetAttachment(self.LaserQCAttachmentWM)
|
||||
if att then
|
||||
self:DrawLaser(att.Pos, att.Ang, power, true)
|
||||
end
|
||||
elseif IsValid(self:GetOwner():GetViewModel()) and self.LaserQCAttachmentVM then
|
||||
local vm = self:GetOwner():GetViewModel()
|
||||
local att = vm:GetAttachment(self.LaserQCAttachmentVM)
|
||||
if att then
|
||||
local pos = TacRP.FormatViewModelAttachment(self.ViewModelFOV, att.Pos, false)
|
||||
self:DrawLaser(pos, att.Ang, power)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for i, k in pairs(self.Attachments) do
|
||||
if !k.Installed then continue end
|
||||
|
||||
local atttbl = TacRP.GetAttTable(k.Installed)
|
||||
|
||||
local power = atttbl.LaserPower or 2
|
||||
|
||||
if atttbl.Laser and self:GetTactical() then
|
||||
if wm then
|
||||
if atttbl.LaserQCAttachmentWM then
|
||||
local att = self:GetAttachment(atttbl.LaserQCAttachmentWM)
|
||||
if att then
|
||||
self:DrawLaser(att.Pos, self:GetOwner():IsPlayer() and self:GetShootDir() or att.Ang, power, true)
|
||||
end
|
||||
elseif IsValid(k.WModel) then
|
||||
if self:GetOwner():IsPlayer() then
|
||||
self:DrawLaser(k.WModel:GetPos(), self:GetShootDir(), power, true)
|
||||
else
|
||||
self:DrawLaser(k.WModel:GetPos(), k.WModel:GetAngles(), power, true)
|
||||
end
|
||||
end
|
||||
else
|
||||
if IsValid(self:GetOwner():GetViewModel()) and atttbl.LaserQCAttachmentVM then
|
||||
local vm = self:GetOwner():GetViewModel()
|
||||
local att = vm:GetAttachment(atttbl.LaserQCAttachmentVM)
|
||||
if att then
|
||||
local pos = TacRP.FormatViewModelAttachment(self.ViewModelFOV, att.Pos, false)
|
||||
self:DrawLaser(pos, att.Ang, power)
|
||||
end
|
||||
elseif IsValid(k.VModel) then
|
||||
self:DrawLaser(k.VModel:GetPos() + (k.VModel:GetAngles():Up() * 0.75), k.VModel:GetAngles(), power)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
333
garrysmod/addons/tacrp/lua/weapons/tacrp_base/cl_light.lua
Normal file
333
garrysmod/addons/tacrp/lua/weapons/tacrp_base/cl_light.lua
Normal file
@@ -0,0 +1,333 @@
|
||||
SWEP.Flashlights = {} -- tracks projectedlights
|
||||
-- {{att = int, light = ProjectedTexture}}
|
||||
|
||||
function SWEP:GetHasFlashlights()
|
||||
for i, k in pairs(self.Attachments) do
|
||||
if !k.Installed then continue end
|
||||
|
||||
local atttbl = TacRP.GetAttTable(k.Installed)
|
||||
|
||||
if atttbl.Flashlight then return true end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
function SWEP:CreateFlashlights()
|
||||
self:KillFlashlights()
|
||||
self.Flashlights = {}
|
||||
|
||||
local total_lights = 0
|
||||
|
||||
for i, k in pairs(self.Attachments) do
|
||||
if !k.Installed then continue end
|
||||
|
||||
local atttbl = TacRP.GetAttTable(k.Installed)
|
||||
|
||||
if atttbl.Flashlight then
|
||||
local newlight = {
|
||||
att = i,
|
||||
light = ProjectedTexture(),
|
||||
col = Color(255, 255, 255),
|
||||
br = 4,
|
||||
}
|
||||
total_lights = total_lights + 1
|
||||
|
||||
local l = newlight.light
|
||||
if !IsValid(l) then continue end
|
||||
|
||||
table.insert(self.Flashlights, newlight)
|
||||
|
||||
l:SetFOV(atttbl.FlashlightFOV or 60)
|
||||
|
||||
l:SetFarZ(atttbl.FlashlightFarZ or 1024)
|
||||
l:SetNearZ(4)
|
||||
|
||||
l:SetQuadraticAttenuation(100)
|
||||
|
||||
l:SetColor(atttbl.FlashlightColor or color_white)
|
||||
l:SetTexture("effects/flashlight001")
|
||||
l:SetBrightness(atttbl.FlashlightBrightness or 1.5)
|
||||
l:SetEnableShadows(true)
|
||||
l:Update()
|
||||
|
||||
local g_light = {
|
||||
Weapon = self,
|
||||
ProjectedTexture = l
|
||||
}
|
||||
|
||||
table.insert(TacRP.FlashlightPile, g_light)
|
||||
end
|
||||
end
|
||||
|
||||
if total_lights > 2 then -- you are a madman
|
||||
for i, k in pairs(self.Flashlights) do
|
||||
if k.light:IsValid() then k.light:SetEnableShadows(false) end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:KillFlashlights()
|
||||
self:KillFlashlightsVM()
|
||||
-- self:KillFlashlightsWM()
|
||||
end
|
||||
|
||||
function SWEP:KillFlashlightsVM()
|
||||
if !self.Flashlights then return end
|
||||
|
||||
for i, k in pairs(self.Flashlights) do
|
||||
if k.light and k.light:IsValid() then
|
||||
k.light:Remove()
|
||||
end
|
||||
end
|
||||
|
||||
self.Flashlights = nil
|
||||
end
|
||||
|
||||
function SWEP:DrawFlashlightsVM()
|
||||
|
||||
if !self:GetTactical() then
|
||||
self:KillFlashlights()
|
||||
return
|
||||
end
|
||||
|
||||
if !self.Flashlights then
|
||||
self:CreateFlashlights()
|
||||
end
|
||||
|
||||
for i, k in pairs(self.Flashlights) do
|
||||
local model = self.Attachments[k.att].VModel
|
||||
|
||||
local pos, ang
|
||||
|
||||
if !IsValid(model) then
|
||||
pos = self:GetOwner():EyePos()
|
||||
ang = self:GetOwner():EyeAngles()
|
||||
else
|
||||
pos = model:GetPos()
|
||||
ang = model:GetAngles()
|
||||
end
|
||||
|
||||
local tr = util.TraceLine({
|
||||
start = self:GetOwner():EyePos(),
|
||||
endpos = self:GetOwner():EyePos() - -ang:Forward() * 128,
|
||||
mask = MASK_OPAQUE,
|
||||
filter = LocalPlayer(),
|
||||
})
|
||||
if tr.Fraction < 1 then -- We need to push the flashlight back
|
||||
local tr2 = util.TraceLine({
|
||||
start = self:GetOwner():EyePos(),
|
||||
endpos = self:GetOwner():EyePos() + -ang:Forward() * 128,
|
||||
mask = MASK_OPAQUE,
|
||||
filter = LocalPlayer(),
|
||||
})
|
||||
-- push it as back as the area behind us allows
|
||||
pos = pos + -ang:Forward() * 128 * math.min(1 - tr.Fraction, tr2.Fraction)
|
||||
end
|
||||
|
||||
-- ang:RotateAroundAxis(ang:Up(), 90)
|
||||
|
||||
k.light:SetPos(pos)
|
||||
k.light:SetAngles(ang)
|
||||
k.light:Update()
|
||||
|
||||
-- local col = k.col
|
||||
|
||||
-- local dl = DynamicLight(self:EntIndex())
|
||||
|
||||
-- if dl then
|
||||
-- dl.pos = pos
|
||||
-- dl.r = col.r
|
||||
-- dl.g = col.g
|
||||
-- dl.b = col.b
|
||||
-- dl.brightness = k.br or 2
|
||||
-- -- print(z / maxz)
|
||||
-- dl.Decay = 1000 / 0.1
|
||||
-- dl.dietime = CurTime() + 0.1
|
||||
-- dl.size = (k.br or 2) * 64
|
||||
-- end
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:DrawFlashlightsWM()
|
||||
if self:GetOwner() != LocalPlayer() then return end
|
||||
|
||||
if !self.Flashlights then
|
||||
self:CreateFlashlights()
|
||||
end
|
||||
|
||||
for i, k in ipairs(self.Flashlights) do
|
||||
local model = (k.slottbl or {}).WModel
|
||||
|
||||
if !IsValid(model) then continue end
|
||||
|
||||
local pos, ang
|
||||
|
||||
if !model then
|
||||
pos = self:GetOwner():EyePos()
|
||||
ang = self:GetOwner():EyeAngles()
|
||||
else
|
||||
pos = model:GetPos()
|
||||
ang = model:GetAngles()
|
||||
end
|
||||
|
||||
-- ang:RotateAroundAxis(ang:Up(), 90)
|
||||
|
||||
local tr = util.TraceLine({
|
||||
start = pos,
|
||||
endpos = pos + ang:Forward() * 16,
|
||||
mask = MASK_OPAQUE,
|
||||
filter = LocalPlayer(),
|
||||
})
|
||||
if tr.Fraction < 1 then -- We need to push the flashlight back
|
||||
local tr2 = util.TraceLine({
|
||||
start = pos,
|
||||
endpos = pos - ang:Forward() * 16,
|
||||
mask = MASK_OPAQUE,
|
||||
filter = LocalPlayer(),
|
||||
})
|
||||
-- push it as back as the area behind us allows
|
||||
pos = pos + -ang:Forward() * 16 * math.min(1 - tr.Fraction, tr2.Fraction)
|
||||
else
|
||||
pos = tr.HitPos
|
||||
end
|
||||
|
||||
k.light:SetPos(pos)
|
||||
k.light:SetAngles(ang)
|
||||
k.light:Update()
|
||||
end
|
||||
end
|
||||
|
||||
local flaremat = Material("tacrp/particle_flare")
|
||||
function SWEP:DrawFlashlightGlare(pos, ang, strength, dot)
|
||||
strength = strength or 1
|
||||
|
||||
local diff = EyePos() - pos
|
||||
local wep = LocalPlayer():GetActiveWeapon()
|
||||
--local dot = math.Clamp((-ang:Forward():Dot(EyeAngles():Forward()) - 0.707) / (1 - 0.707), 0, 1) ^ 2
|
||||
if TacRP.ConVars["flashlight_blind"]:GetBool() then
|
||||
dot = dot ^ 4
|
||||
local tr = util.QuickTrace(pos, diff, {self:GetOwner(), LocalPlayer()})
|
||||
local s = math.Clamp(1 - diff:Length() / 328, 0, 1) ^ 1 * dot * 2000 * math.Rand(0.95, 1.05)
|
||||
if IsValid(wep) and wep.ArcticTacRP and wep:IsInScope() and wep:GetValue("ScopeOverlay") then
|
||||
s = s + math.Clamp(1 - diff:Length() / 4096, 0, 1) ^ 1.2 * wep:GetSightAmount() * dot * 3000 * math.Rand(0.95, 1.05)
|
||||
end
|
||||
if tr.Fraction == 1 then
|
||||
s = TacRP.SS(s)
|
||||
local toscreen = pos:ToScreen()
|
||||
cam.Start2D()
|
||||
surface.SetMaterial(flaremat)
|
||||
surface.SetDrawColor(255, 255, 255, 255)
|
||||
surface.DrawTexturedRect(toscreen.x - s / 2, toscreen.y - s / 2, s, s)
|
||||
cam.End2D()
|
||||
end
|
||||
end
|
||||
|
||||
local rad = math.Rand(0.9, 1.1) * 128 * strength
|
||||
local a = 50 + strength * 205
|
||||
|
||||
pos = pos + ang:Forward() * 2
|
||||
pos = pos + diff:GetNormalized() * (2 + 14 * strength)
|
||||
|
||||
render.SetMaterial(flaremat)
|
||||
render.DrawSprite(pos, rad, rad, Color(255, 255, 255, a))
|
||||
end
|
||||
|
||||
function SWEP:DrawFlashlightGlares()
|
||||
if !self:GetOwner():IsPlayer() then return end
|
||||
for i, k in pairs(self.Attachments) do
|
||||
if !k.Installed then continue end
|
||||
|
||||
local atttbl = TacRP.GetAttTable(k.Installed)
|
||||
|
||||
local src, dir
|
||||
if atttbl.Blinding and self:GetTactical() then
|
||||
if IsValid(k.WModel) then
|
||||
src, dir = k.WModel:GetPos(), self:GetShootDir()
|
||||
else
|
||||
src, dir = self:GetTracerOrigin(), self:GetShootDir()
|
||||
end
|
||||
else
|
||||
continue
|
||||
end
|
||||
|
||||
local power = 1
|
||||
local dot = -dir:Forward():Dot(EyeAngles():Forward())
|
||||
local dot2 = dir:Forward():Dot((EyePos() - src):GetNormalized())
|
||||
dot = (dot + dot2) / 2
|
||||
if dot < 0 then continue end
|
||||
|
||||
power = power * math.Clamp(dot * 2 - 1, 0, 1)
|
||||
local distsqr = (src - EyePos()):LengthSqr()
|
||||
power = power * ((1 - math.Clamp(distsqr / 4194304, 0, 1)) ^ 1.25)
|
||||
|
||||
self:DrawFlashlightGlare(src, dir, power, dot)
|
||||
end
|
||||
end
|
||||
|
||||
local glintmat = Material("effects/blueflare1")
|
||||
local glintmat2 = Material("tacrp/scope_flare")
|
||||
function SWEP:DoScopeGlint()
|
||||
--if self:GetOwner() == LocalPlayer() then return end
|
||||
if !TacRP.ConVars["glint"]:GetBool() then return end
|
||||
if !self:GetValue("ScopeOverlay") then return end
|
||||
local src, dir = self:GetTracerOrigin(), self:GetShootDir()
|
||||
|
||||
local diff = EyePos() - src
|
||||
|
||||
local dot = -dir:Forward():Dot(EyeAngles():Forward())
|
||||
local dot2 = dir:Forward():Dot(diff:GetNormalized())
|
||||
dot = math.max(0, (dot + dot2) / 2) ^ 1.5
|
||||
|
||||
local strength = dot * math.Clamp((diff:Length() - 1024) / 3072, 0, 3) * math.Clamp(90 / self:GetValue("ScopeFOV") / 10, 0, 1)
|
||||
|
||||
local rad = strength * 128 * (self:GetSightAmount() * 0.5 + 0.5)
|
||||
|
||||
src = src + dir:Up() * 4 + diff:GetNormalized() * math.Clamp(diff:Length() / 2048, 0, 1) * 16
|
||||
|
||||
local a = math.min(255, strength * 200 + 100)
|
||||
|
||||
render.SetMaterial(glintmat)
|
||||
render.DrawSprite(src, rad, rad, Color(a, a, a))
|
||||
|
||||
-- if self:GetSightAmount() > 0 then
|
||||
render.SetMaterial(glintmat2)
|
||||
render.DrawSprite(src, rad * 2, rad * 2, color_white)
|
||||
-- end
|
||||
end
|
||||
|
||||
function SWEP:DoMuzzleLight()
|
||||
if (!IsFirstTimePredicted() and !game.SinglePlayer()) or !TacRP.ConVars["muzzlelight"]:GetBool() then return end
|
||||
|
||||
if IsValid(self.MuzzleLight) then self.MuzzleLight:Remove() end
|
||||
|
||||
local lamp = ProjectedTexture()
|
||||
lamp:SetTexture("tacrp/muzzleflash_light")
|
||||
local val1, val2
|
||||
if self:GetValue("Silencer") then
|
||||
val1, val2 = math.Rand(0.2, 0.4), math.Rand(100, 105)
|
||||
lamp:SetBrightness(val1)
|
||||
lamp:SetFOV(val2)
|
||||
else
|
||||
val1, val2 = math.Rand(2, 3), math.Rand(115, 120)
|
||||
lamp:SetBrightness(val1)
|
||||
lamp:SetFOV(val2)
|
||||
end
|
||||
|
||||
lamp:SetFarZ(600)
|
||||
lamp:SetPos(self:GetMuzzleOrigin() + self:GetShootDir():Forward() * 8)
|
||||
lamp:SetAngles(self:GetShootDir() + Angle(math.Rand(-1, 1), math.Rand(-1, 1), math.Rand(0, 360)))
|
||||
lamp:Update()
|
||||
|
||||
self.MuzzleLight = lamp
|
||||
self.MuzzleLightStart = UnPredictedCurTime()
|
||||
self.MuzzleLightEnd = UnPredictedCurTime() + 0.06
|
||||
self.MuzzleLightBrightness = val1
|
||||
self.MuzzleLightFOV = val2
|
||||
|
||||
-- In multiplayer the timer will last longer than intended - sh_think should kill the light first.
|
||||
-- This is a failsafe for when the weapon stops thinking before light is killed (holstered, removed etc.).
|
||||
timer.Simple(0.06, function()
|
||||
if IsValid(lamp) then lamp:Remove() end
|
||||
end)
|
||||
end
|
||||
106
garrysmod/addons/tacrp/lua/weapons/tacrp_base/cl_model.lua
Normal file
106
garrysmod/addons/tacrp/lua/weapons/tacrp_base/cl_model.lua
Normal file
@@ -0,0 +1,106 @@
|
||||
SWEP.VModel = nil
|
||||
SWEP.WModel = nil
|
||||
|
||||
function SWEP:KillModel()
|
||||
for _, model in pairs(self.VModel or {}) do
|
||||
SafeRemoveEntity(model)
|
||||
end
|
||||
for _, model in pairs(self.WModel or {}) do
|
||||
SafeRemoveEntity(model)
|
||||
end
|
||||
|
||||
self.VModel = nil
|
||||
self.WModel = nil
|
||||
end
|
||||
|
||||
function SWEP:CreateAttachmentModel(wm, atttbl, slot, slottbl, custom_wm)
|
||||
local model = atttbl.Model
|
||||
|
||||
if wm and atttbl.WorldModel then
|
||||
model = atttbl.WorldModel
|
||||
end
|
||||
|
||||
local csmodel = ClientsideModel(model)
|
||||
|
||||
if !IsValid(csmodel) then return end
|
||||
|
||||
csmodel.Slot = slot
|
||||
|
||||
local scale = Matrix()
|
||||
local vec = Vector(1, 1, 1) * (atttbl.Scale or 1)
|
||||
if wm then
|
||||
vec = vec * (slottbl.WMScale or 1)
|
||||
else
|
||||
vec = vec * (slottbl.VMScale or 1)
|
||||
end
|
||||
scale:Scale(vec)
|
||||
csmodel:EnableMatrix("RenderMultiply", scale)
|
||||
csmodel:SetNoDraw(true)
|
||||
|
||||
local tbl = {
|
||||
Model = csmodel,
|
||||
Weapon = self
|
||||
}
|
||||
|
||||
table.insert(TacRP.CSModelPile, tbl)
|
||||
|
||||
if wm then
|
||||
table.insert(self.WModel, csmodel)
|
||||
else
|
||||
table.insert(self.VModel, csmodel)
|
||||
end
|
||||
|
||||
return csmodel
|
||||
end
|
||||
|
||||
function SWEP:SetupModel(wm, custom_wm)
|
||||
self:KillModel()
|
||||
|
||||
if !wm and !IsValid(self:GetOwner()) then return end
|
||||
|
||||
if !wm then
|
||||
self.VModel = {}
|
||||
else
|
||||
self.WModel = {}
|
||||
end
|
||||
|
||||
if !wm and self:GetOwner() != LocalPlayer() and self:GetOwner() != LocalPlayer():GetObserverTarget() then return end
|
||||
|
||||
self:DoBodygroups(wm)
|
||||
|
||||
for slot, slottbl in pairs(self.Attachments) do
|
||||
if !slottbl.Installed then continue end
|
||||
|
||||
local atttbl = TacRP.GetAttTable(slottbl.Installed)
|
||||
|
||||
if !atttbl.Model then continue end
|
||||
|
||||
local csmodel = self:CreateAttachmentModel(wm, atttbl, slot, slottbl)
|
||||
|
||||
csmodel.IsHolosight = atttbl.Holosight
|
||||
|
||||
if atttbl.Silencer then
|
||||
local slmodel = self:CreateAttachmentModel(wm, atttbl, slot, slottbl)
|
||||
slmodel.IsMuzzleDevice = true
|
||||
slmodel.NoDraw = true
|
||||
end
|
||||
|
||||
if wm then
|
||||
slottbl.WModel = csmodel
|
||||
else
|
||||
slottbl.VModel = csmodel
|
||||
end
|
||||
end
|
||||
|
||||
if !wm then
|
||||
self:CreateFlashlights()
|
||||
|
||||
local mat = self:GetValue("Material")
|
||||
|
||||
if mat then
|
||||
local vm = self:GetOwner():GetViewModel()
|
||||
|
||||
vm:SetMaterial(mat)
|
||||
end
|
||||
end
|
||||
end
|
||||
37
garrysmod/addons/tacrp/lua/weapons/tacrp_base/cl_net.lua
Normal file
37
garrysmod/addons/tacrp/lua/weapons/tacrp_base/cl_net.lua
Normal file
@@ -0,0 +1,37 @@
|
||||
function SWEP:ReceiveWeapon(ids)
|
||||
for slot, slottbl in pairs(self.Attachments) do
|
||||
local attid = ids and ids[slot] or net.ReadUInt(TacRP.Attachments_Bits)
|
||||
|
||||
if attid == 0 then
|
||||
slottbl.Installed = nil
|
||||
else
|
||||
slottbl.Installed = TacRP.Attachments_Index[attid]
|
||||
end
|
||||
end
|
||||
|
||||
self:InvalidateCache()
|
||||
|
||||
self:SetupModel(true)
|
||||
self:SetupModel(false)
|
||||
|
||||
self.CertainAboutAtts = true
|
||||
end
|
||||
|
||||
function SWEP:UpdateHolster()
|
||||
local ply = self:GetOwner()
|
||||
if IsValid(ply) and ply:IsPlayer() and ply:GetActiveWeapon() != self then
|
||||
local visible = self:GetValue("HolsterVisible")
|
||||
local slot = self:GetValue("HolsterSlot")
|
||||
|
||||
if visible and slot then
|
||||
ply.TacRP_Holster = ply.TacRP_Holster or {}
|
||||
ply.TacRP_Holster[slot] = self
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:RequestWeapon()
|
||||
net.Start("tacrp_networkweapon")
|
||||
net.WriteEntity(self)
|
||||
net.SendToServer()
|
||||
end
|
||||
81
garrysmod/addons/tacrp/lua/weapons/tacrp_base/cl_preset.lua
Normal file
81
garrysmod/addons/tacrp/lua/weapons/tacrp_base/cl_preset.lua
Normal file
@@ -0,0 +1,81 @@
|
||||
function SWEP:SavePreset(filename)
|
||||
if LocalPlayer() != self:GetOwner() then return end
|
||||
|
||||
filename = filename or "autosave"
|
||||
|
||||
local str = ""
|
||||
for i, k in pairs(self.Attachments) do
|
||||
if k.Installed then
|
||||
str = str .. k.Installed
|
||||
end
|
||||
|
||||
str = str .. "\n"
|
||||
end
|
||||
|
||||
filename = TacRP.PresetPath .. self:GetClass() .. "/" .. filename .. ".txt"
|
||||
|
||||
file.CreateDir(TacRP.PresetPath .. self:GetClass())
|
||||
file.Write(filename, str)
|
||||
end
|
||||
|
||||
function SWEP:LoadPreset(filename)
|
||||
if LocalPlayer() != self:GetOwner() then return end
|
||||
|
||||
filename = TacRP.PresetPath .. self:GetClass() .. "/" .. "autosave" .. ".txt"
|
||||
|
||||
if !file.Exists(filename, "DATA") then return end
|
||||
|
||||
local f = file.Open(filename, "r", "DATA")
|
||||
if !f then return end
|
||||
|
||||
local presetTbl = {}
|
||||
|
||||
for i = 1, table.Count(self.Attachments) do
|
||||
local line = f:ReadLine()
|
||||
if !line then continue end
|
||||
presetTbl[i] = string.Trim(line, "\n")
|
||||
end
|
||||
|
||||
local anyinstalled = false
|
||||
|
||||
for i = 1, table.Count(self.Attachments) do
|
||||
if !self.Attachments[i] then continue end
|
||||
|
||||
local att = presetTbl[i]
|
||||
if att == "" then
|
||||
self.Attachments[i].Installed = nil
|
||||
continue
|
||||
end
|
||||
|
||||
|
||||
if att == self.Attachments[i].Installed then continue end
|
||||
if !TacRP.GetAttTable(att) then continue end
|
||||
|
||||
self.Attachments[i].Installed = att
|
||||
|
||||
anyinstalled = true
|
||||
end
|
||||
|
||||
f:Close()
|
||||
|
||||
if !anyinstalled then return end
|
||||
|
||||
net.Start("TacRP_receivepreset")
|
||||
net.WriteEntity(self)
|
||||
for i, k in pairs(self.Attachments) do
|
||||
if !k.Installed then
|
||||
net.WriteUInt(0, TacRP.Attachments_Bits)
|
||||
else
|
||||
local atttbl = TacRP.GetAttTable(k.Installed)
|
||||
net.WriteUInt(atttbl.ID or 0, TacRP.Attachments_Bits)
|
||||
end
|
||||
end
|
||||
net.SendToServer()
|
||||
|
||||
self:SetupModel(false)
|
||||
self:SetupModel(true)
|
||||
|
||||
self:InvalidateCache()
|
||||
|
||||
self:SetBaseSettings()
|
||||
end
|
||||
993
garrysmod/addons/tacrp/lua/weapons/tacrp_base/cl_radialmenu.lua
Normal file
993
garrysmod/addons/tacrp/lua/weapons/tacrp_base/cl_radialmenu.lua
Normal file
@@ -0,0 +1,993 @@
|
||||
local function filledcircle(x, y, radius, seg)
|
||||
local cir = {}
|
||||
|
||||
table.insert(cir, {
|
||||
x = x,
|
||||
y = y,
|
||||
u = 0.5,
|
||||
v = 0.5
|
||||
})
|
||||
|
||||
for i = 0, seg do
|
||||
local a = math.rad((i / seg) * -360)
|
||||
|
||||
table.insert(cir, {
|
||||
x = x + math.sin(a) * radius,
|
||||
y = y + math.cos(a) * radius,
|
||||
u = math.sin(a) / 2 + 0.5,
|
||||
v = math.cos(a) / 2 + 0.5
|
||||
})
|
||||
end
|
||||
|
||||
local a = math.rad(0)
|
||||
|
||||
table.insert(cir, {
|
||||
x = x + math.sin(a) * radius,
|
||||
y = y + math.cos(a) * radius,
|
||||
u = math.sin(a) / 2 + 0.5,
|
||||
v = math.cos(a) / 2 + 0.5
|
||||
})
|
||||
|
||||
surface.DrawPoly(cir)
|
||||
end
|
||||
|
||||
local function slicedcircle(x, y, radius, seg, ang0, ang1)
|
||||
local cir = {}
|
||||
|
||||
ang0 = ang0 + 90
|
||||
ang1 = ang1 + 90
|
||||
|
||||
local arcseg = math.Round(360 / math.abs(ang1 - ang0) * seg)
|
||||
|
||||
table.insert(cir, {
|
||||
x = x,
|
||||
y = y,
|
||||
u = 0.5,
|
||||
v = 0.5
|
||||
})
|
||||
|
||||
for i = 0, arcseg do
|
||||
local a = math.rad((i / arcseg) * -math.abs(ang1 - ang0) + ang0)
|
||||
|
||||
table.insert(cir, {
|
||||
x = x + math.sin(a) * radius,
|
||||
y = y + math.cos(a) * radius,
|
||||
u = math.sin(a) / 2 + 0.5,
|
||||
v = math.cos(a) / 2 + 0.5
|
||||
})
|
||||
end
|
||||
|
||||
surface.DrawPoly(cir)
|
||||
end
|
||||
|
||||
SWEP.GrenadeMenuAlpha = 0
|
||||
SWEP.BlindFireMenuAlpha = 0
|
||||
|
||||
TacRP.CursorEnabled = false
|
||||
|
||||
local currentnade
|
||||
local currentind
|
||||
local lastmenu
|
||||
function SWEP:DrawGrenadeHUD()
|
||||
if !TacRP.ConVars["nademenu"]:GetBool() then return end
|
||||
if !self:IsQuickNadeAllowed() then return end
|
||||
|
||||
-- adapted from tfa vox radial menu
|
||||
local nades = self:GetAvailableGrenades(false)
|
||||
local scrw = ScrW()
|
||||
local scrh = ScrH()
|
||||
local r = TacRP.SS(128)
|
||||
local r2 = TacRP.SS(40)
|
||||
local sg = TacRP.SS(32)
|
||||
local ri = r * 0.667
|
||||
local arcdegrees = 360 / math.max(1, #nades)
|
||||
local d = 360
|
||||
local ft = FrameTime()
|
||||
|
||||
local cursorx, cursory = input.GetCursorPos()
|
||||
local mouseangle = math.deg(math.atan2(cursorx - scrw / 2, cursory - scrh / 2))
|
||||
local mousedist = math.sqrt(math.pow(cursorx - scrw / 2, 2) + math.pow(cursory - scrh / 2, 2))
|
||||
mouseangle = math.NormalizeAngle(360 - (mouseangle - 90) + arcdegrees)
|
||||
if mouseangle < 0 then
|
||||
mouseangle = mouseangle + 360
|
||||
end
|
||||
|
||||
local iskeydown = self:GetOwner():KeyDown(self.GrenadeMenuKey)
|
||||
|
||||
if self.GrenadeMenuKey == IN_GRENADE1 and !input.LookupBinding("+grenade1") then
|
||||
iskeydown = input.IsKeyDown(TacRP.GRENADE1_Backup)
|
||||
elseif self.GrenadeMenuKey == IN_GRENADE2 and !input.LookupBinding("+grenade2") then
|
||||
iskeydown = input.IsKeyDown(TacRP.GRENADE2_Backup)
|
||||
end
|
||||
|
||||
if iskeydown and !self:GetPrimedGrenade() and self.BlindFireMenuAlpha == 0 and self:GetHolsterTime() == 0 then
|
||||
self.GrenadeMenuAlpha = math.Approach(self.GrenadeMenuAlpha, 1, 15 * ft)
|
||||
if !lastmenu then
|
||||
gui.EnableScreenClicker(true)
|
||||
TacRP.CursorEnabled = true
|
||||
lastmenu = true
|
||||
end
|
||||
|
||||
if mousedist > r2 then
|
||||
local i = math.floor( mouseangle / arcdegrees ) + 1
|
||||
currentnade = nades[i]
|
||||
currentind = i
|
||||
else
|
||||
currentnade = self:GetGrenade()
|
||||
currentind = nil
|
||||
end
|
||||
self.GrenadeMenuHighlighted = currentind
|
||||
else
|
||||
self.GrenadeMenuAlpha = math.Approach(self.GrenadeMenuAlpha, 0, -10 * ft)
|
||||
if lastmenu then
|
||||
if !self:GetCustomize() then
|
||||
gui.EnableScreenClicker(false)
|
||||
TacRP.CursorEnabled = false
|
||||
end
|
||||
if currentnade then
|
||||
if currentnade.Index != self:GetGrenade().Index then
|
||||
self:GetOwner():EmitSound("tacrp/weapons/grenade/roll-" .. math.random(1, 3) .. ".wav")
|
||||
end
|
||||
net.Start("tacrp_togglenade")
|
||||
net.WriteUInt(currentnade.Index, 4)
|
||||
net.WriteBool(false)
|
||||
net.SendToServer()
|
||||
self.Secondary.Ammo = currentnade.Ammo or "none"
|
||||
end
|
||||
lastmenu = false
|
||||
end
|
||||
end
|
||||
|
||||
if self.GrenadeMenuAlpha <= 0 then
|
||||
return
|
||||
end
|
||||
|
||||
local a = self.GrenadeMenuAlpha
|
||||
local col = Color(255, 255, 255, 255 * a)
|
||||
|
||||
surface.DrawCircle(scrw / 2, scrh / 2, r, 255, 255, 255, a * 255)
|
||||
|
||||
surface.SetDrawColor(0, 0, 0, a * 200)
|
||||
draw.NoTexture()
|
||||
filledcircle(scrw / 2, scrh / 2, r, 32)
|
||||
|
||||
if #nades == 0 then
|
||||
local nadetext = TacRP:GetPhrase("hint.nogrenades")
|
||||
surface.SetFont("TacRP_HD44780A00_5x8_8")
|
||||
local nadetextw = surface.GetTextSize(nadetext)
|
||||
surface.SetTextPos(scrw / 2 - nadetextw * 0.5, scrh / 2 + TacRP.SS(6))
|
||||
surface.DrawText(nadetext)
|
||||
return
|
||||
end
|
||||
|
||||
surface.SetDrawColor(150, 150, 150, a * 100)
|
||||
draw.NoTexture()
|
||||
if currentind then
|
||||
local i = currentind
|
||||
local d0 = 0 - arcdegrees * (i - 2)
|
||||
slicedcircle(scrw / 2, scrh / 2, r, 32, d0, d0 + arcdegrees)
|
||||
else
|
||||
filledcircle(scrw / 2, scrh / 2, r2, 32)
|
||||
end
|
||||
|
||||
surface.SetDrawColor(0, 0, 0, a * 255)
|
||||
surface.DrawCircle(scrw / 2, scrh / 2, r2, 255, 255, 255, a * 255)
|
||||
|
||||
for i = 1, #nades do
|
||||
local rad = math.rad( d + arcdegrees * 0.5 )
|
||||
|
||||
surface.SetDrawColor(255, 255, 255, a * 255)
|
||||
surface.DrawLine(
|
||||
scrw / 2 + math.cos(math.rad(d)) * r2,
|
||||
scrh / 2 - math.sin(math.rad(d)) * r2,
|
||||
scrw / 2 + math.cos(math.rad(d)) * r,
|
||||
scrh / 2 - math.sin(math.rad(d)) * r)
|
||||
|
||||
local nadex, nadey = scrw / 2 + math.cos(rad) * ri, scrh / 2 - math.sin(rad) * ri
|
||||
local nade = nades[i]
|
||||
|
||||
local qty = nil --"INF"
|
||||
|
||||
if nade.Singleton then
|
||||
qty = self:GetOwner():HasWeapon(nade.GrenadeWep) and 1 or 0
|
||||
elseif !TacRP.IsGrenadeInfiniteAmmo(nade.Index) then
|
||||
qty = self:GetOwner():GetAmmoCount(nade.Ammo)
|
||||
end
|
||||
|
||||
if !qty or qty > 0 then
|
||||
surface.SetDrawColor(255, 255, 255, a * 255)
|
||||
surface.SetTextColor(255, 255, 255, a * 255)
|
||||
else
|
||||
surface.SetDrawColor(175, 175, 175, a * 255)
|
||||
surface.SetTextColor(175, 175, 175, a * 255)
|
||||
end
|
||||
|
||||
if nade.Icon then
|
||||
surface.SetMaterial(nade.Icon)
|
||||
surface.DrawTexturedRect(nadex - sg * 0.5, nadey - sg * 0.5 - TacRP.SS(8), sg, sg)
|
||||
end
|
||||
local nadetext = TacRP:GetPhrase("quicknade." .. nade.PrintName .. ".name") or nade.PrintName
|
||||
surface.SetFont("TacRP_HD44780A00_5x8_8")
|
||||
local nadetextw = surface.GetTextSize(nadetext)
|
||||
surface.SetTextPos(nadex - nadetextw * 0.5, nadey + TacRP.SS(6))
|
||||
surface.DrawText(nadetext)
|
||||
|
||||
if !TacRP.IsGrenadeInfiniteAmmo(nade.Index) then
|
||||
local qty
|
||||
if nade.Singleton then
|
||||
qty = self:GetOwner():HasWeapon(nade.GrenadeWep) and "x1" or "x0"
|
||||
else
|
||||
qty = "x" .. tostring(self:GetOwner():GetAmmoCount(nade.Ammo))
|
||||
end
|
||||
local qtyw = surface.GetTextSize(qty)
|
||||
surface.SetTextPos(nadex - qtyw * 0.5, nadey + TacRP.SS(15))
|
||||
surface.DrawText(qty)
|
||||
end
|
||||
|
||||
d = d - arcdegrees
|
||||
|
||||
end
|
||||
|
||||
local nade = currentnade
|
||||
if nade.Icon then
|
||||
surface.SetMaterial(nade.Icon)
|
||||
surface.SetDrawColor(255, 255, 255, a * 255)
|
||||
surface.DrawTexturedRect(scrw / 2 - sg * 0.5, scrh / 2 - sg * 0.5 - TacRP.SS(8), sg, sg)
|
||||
end
|
||||
|
||||
local nadetext = TacRP:GetPhrase("quicknade." .. nade.PrintName .. ".name") or nade.PrintName
|
||||
surface.SetFont("TacRP_HD44780A00_5x8_8")
|
||||
local nadetextw = surface.GetTextSize(nadetext)
|
||||
surface.SetTextPos(scrw / 2 - nadetextw * 0.5, scrh / 2 + TacRP.SS(6))
|
||||
surface.SetTextColor(255, 255, 255, a * 255)
|
||||
surface.DrawText(nadetext)
|
||||
|
||||
if !TacRP.IsGrenadeInfiniteAmmo(nade.Index) then
|
||||
local qty
|
||||
if nade.Singleton then
|
||||
qty = self:GetOwner():HasWeapon(nade.GrenadeWep) and "x1" or "x0"
|
||||
else
|
||||
qty = "x" .. tostring(self:GetOwner():GetAmmoCount(nade.Ammo))
|
||||
end
|
||||
surface.SetFont("TacRP_HD44780A00_5x8_8")
|
||||
local qtyw = surface.GetTextSize(qty)
|
||||
surface.SetTextPos(scrw / 2 - qtyw * 0.5, scrh / 2 + TacRP.SS(16))
|
||||
surface.SetTextColor(255, 255, 255, a * 255)
|
||||
surface.DrawText(qty)
|
||||
end
|
||||
|
||||
-- description box is blocked in customize
|
||||
if self:GetCustomize() then return end
|
||||
|
||||
local w, h = TacRP.SS(96), TacRP.SS(128)
|
||||
local tx, ty = scrw / 2 + r + TacRP.SS(16), scrh / 2
|
||||
|
||||
-- full name
|
||||
|
||||
surface.SetDrawColor(0, 0, 0, 200 * a)
|
||||
TacRP.DrawCorneredBox(tx, ty - h * 0.5 - TacRP.SS(28), w, TacRP.SS(24), col)
|
||||
surface.SetTextColor(255, 255, 255, a * 255)
|
||||
|
||||
local name = TacRP:GetPhrase("quicknade." .. nade.PrintName .. ".name.full")
|
||||
or TacRP:GetPhrase("quicknade." .. nade.PrintName .. ".name")
|
||||
or nade.FullName
|
||||
or nade.PrintName
|
||||
surface.SetFont("TacRP_Myriad_Pro_16")
|
||||
local name_w, name_h = surface.GetTextSize(name)
|
||||
if name_w > w then
|
||||
surface.SetFont("TacRP_Myriad_Pro_14")
|
||||
name_w, name_h = surface.GetTextSize(name)
|
||||
end
|
||||
surface.SetTextPos(tx + w / 2 - name_w / 2, ty - h * 0.5 - TacRP.SS(28) + TacRP.SS(12) - name_h / 2)
|
||||
surface.DrawText(name)
|
||||
|
||||
|
||||
-- Description
|
||||
|
||||
surface.SetDrawColor(0, 0, 0, 200 * a)
|
||||
TacRP.DrawCorneredBox(tx, ty - h * 0.5, w, h, col)
|
||||
|
||||
surface.SetFont("TacRP_Myriad_Pro_8")
|
||||
surface.SetTextPos(tx + TacRP.SS(4), ty - h / 2 + TacRP.SS(2))
|
||||
surface.DrawText( TacRP:GetPhrase("quicknade.fuse") )
|
||||
|
||||
surface.SetFont("TacRP_Myriad_Pro_8")
|
||||
surface.SetTextPos(tx + TacRP.SS(4), ty - h / 2 + TacRP.SS(10))
|
||||
surface.DrawText(TacRP:GetPhrase("quicknade." .. nade.PrintName .. ".dettype") or nade.DetType or "")
|
||||
|
||||
surface.SetFont("TacRP_Myriad_Pro_8")
|
||||
surface.SetTextPos(tx + TacRP.SS(4), ty - h / 2 + TacRP.SS(22))
|
||||
surface.DrawText( TacRP:GetPhrase("cust.description") )
|
||||
|
||||
surface.SetFont("TacRP_Myriad_Pro_8")
|
||||
|
||||
if nade.Description then
|
||||
nade.DescriptionMultiLine = TacRP.MultiLineText(TacRP:GetPhrase("quicknade." .. nade.PrintName .. ".desc") or nade.Description or "", w - TacRP.SS(7), "TacRP_Myriad_Pro_8")
|
||||
end
|
||||
|
||||
surface.SetTextColor(255, 255, 255, a * 255)
|
||||
for i, text in ipairs(nade.DescriptionMultiLine) do
|
||||
surface.SetTextPos(tx + TacRP.SS(4), ty - h / 2 + TacRP.SS(30) + (i - 1) * TacRP.SS(8))
|
||||
surface.DrawText(text)
|
||||
end
|
||||
|
||||
surface.SetFont("TacRP_Myriad_Pro_8")
|
||||
surface.SetDrawColor(0, 0, 0, 200 * a)
|
||||
|
||||
-- Only use the old bind hints if current hint is disabled
|
||||
if TacRP.ConVars["hints"]:GetBool() then
|
||||
self.LastHintLife = CurTime()
|
||||
return
|
||||
end
|
||||
|
||||
if TacRP.ConVars["nademenu_click"]:GetBool() then
|
||||
|
||||
local binded = input.LookupBinding("grenade1")
|
||||
|
||||
TacRP.DrawCorneredBox(tx, ty + h * 0.5 + TacRP.SS(2), w, TacRP.SS(28), col)
|
||||
|
||||
surface.SetTextPos(tx + TacRP.SS(4), ty + h / 2 + TacRP.SS(4))
|
||||
surface.DrawText( "[ " .. TacRP.GetBind("+attack") .. " ] " .. TacRP:GetPhrase("hint.quicknade.over") )
|
||||
|
||||
surface.SetTextPos(tx + TacRP.SS(4), ty + h / 2 + TacRP.SS(12))
|
||||
surface.DrawText( "[ " .. TacRP.GetBind("+attack2") .. " ] " .. TacRP:GetPhrase("hint.quicknade.under") )
|
||||
|
||||
if TacRP.AreTheGrenadeAnimsReadyYet then
|
||||
surface.SetTextPos(tx + TacRP.SS(4), ty + h / 2 + TacRP.SS(20))
|
||||
surface.DrawText( "[ MOUSE3 ] " .. TacRP:GetPhrase("hint.quicknade.pull_out") )
|
||||
end
|
||||
else
|
||||
local binded = input.LookupBinding("grenade1")
|
||||
|
||||
if binded then button = TacRP.GetBind("grenade1") else button = "G" end
|
||||
|
||||
TacRP.DrawCorneredBox(tx, ty + h * 0.5 + TacRP.SS(2), w, TacRP.SS(28), col)
|
||||
|
||||
surface.SetTextPos(tx + TacRP.SS(4), ty + h / 2 + TacRP.SS(4))
|
||||
surface.DrawText("[ " ..button .. " ] " .. TacRP:GetPhrase("hint.quicknade.over") .. " " .. TacRP:GetPhrase("hint.hold") )
|
||||
|
||||
surface.SetTextPos(tx + TacRP.SS(4), ty + h / 2 + TacRP.SS(12))
|
||||
surface.DrawText("[ " .. button .. " ] " .. TacRP:GetPhrase("hint.quicknade.under") )
|
||||
|
||||
if TacRP.AreTheGrenadeAnimsReadyYet then
|
||||
surface.SetTextPos(tx + TacRP.SS(4), ty + h / 2 + TacRP.SS(20))
|
||||
surface.DrawText( "[ MOUSE3 ] " .. TacRP:GetPhrase("hint.quicknade.pull_out") )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local mat_none = Material("tacrp/blindfire/none.png", "smooth")
|
||||
local mat_wall = Material("tacrp/blindfire/wall.png", "smooth")
|
||||
local bf_slices = {
|
||||
{TacRP.BLINDFIRE_RIGHT, mat_wall, 270},
|
||||
{TacRP.BLINDFIRE_KYS, Material("tacrp/blindfire/suicide.png", "smooth"), 0},
|
||||
{TacRP.BLINDFIRE_LEFT, mat_wall, 90},
|
||||
{TacRP.BLINDFIRE_UP, mat_wall, 0},
|
||||
}
|
||||
local bf_slices2 = {
|
||||
{TacRP.BLINDFIRE_RIGHT, mat_wall, 270},
|
||||
{TacRP.BLINDFIRE_LEFT, mat_wall, 90},
|
||||
{TacRP.BLINDFIRE_UP, mat_wall, 0},
|
||||
}
|
||||
local bf_slices3 = {
|
||||
{TacRP.BLINDFIRE_RIGHT, mat_wall, 270},
|
||||
{TacRP.BLINDFIRE_NONE, mat_none, 0},
|
||||
{TacRP.BLINDFIRE_LEFT, mat_wall, 90},
|
||||
{TacRP.BLINDFIRE_UP, mat_wall, 0},
|
||||
}
|
||||
local lastmenu_bf
|
||||
local bf_suicidelock
|
||||
local bf_funnyline
|
||||
local bf_lines = {
|
||||
"Go ahead, see if I care.",
|
||||
"Why not just killbind?",
|
||||
"But you have so much to live for!",
|
||||
"Just like Hemingway.",
|
||||
"... NOW!",
|
||||
"DO IT!",
|
||||
"Now THIS is realism.",
|
||||
"See you in the next life!",
|
||||
"Time to commit a little insurance fraud.",
|
||||
"Don't give them the satisfaction.",
|
||||
"Why not jump off a building instead?",
|
||||
"Ripperoni in pepperoni.",
|
||||
"F",
|
||||
"L + ratio + you're a minge + touch grass",
|
||||
"You serve NO PURPOSE!",
|
||||
"type unbindall in console",
|
||||
"Citizens aren't supposed to have guns.",
|
||||
"I have decided that I want to die.",
|
||||
"What's the point?",
|
||||
"eh",
|
||||
"not worth",
|
||||
"Just like Hitler.",
|
||||
"Kill your own worst enemy.",
|
||||
"You've come to the right place.",
|
||||
"Don't forget to like and subscribe",
|
||||
"noooooooooooooo",
|
||||
"tfa base sucks lololololol",
|
||||
"The HUD is mandatory.",
|
||||
"No Bitches?",
|
||||
"now you have truly become garry's mod",
|
||||
"type 'tacrp_rock_funny 1' in console",
|
||||
"is only gaem, y u haev to be mad?",
|
||||
"And so it ends.",
|
||||
"Suicide is badass!",
|
||||
"Stop staring at me and get to it!",
|
||||
"you like kissing boys don't you",
|
||||
"A most tactical decision.",
|
||||
"Bye have a great time!",
|
||||
"Try doing this with the Dual MTX!",
|
||||
"Try doing this with the RPG-7!",
|
||||
"sad",
|
||||
"commit sudoku",
|
||||
"kermit suicide",
|
||||
"You can disable this button in the options.",
|
||||
"Goodbye, cruel world!",
|
||||
"Adios!",
|
||||
"Sayonara, [------]!",
|
||||
"Nice boat!",
|
||||
"I find it quite Inconceievable!",
|
||||
"Delete system32.dll",
|
||||
"Press ALT+F4 for admin gun",
|
||||
"AKA: Canadian Medkit",
|
||||
"The coward's way out",
|
||||
"No man lives forever.",
|
||||
"Goodbye, cruel world.",
|
||||
"Doing this will result in an admin sit",
|
||||
"Do it, before you turn.",
|
||||
"Your HUD Buddy will miss you.",
|
||||
"1-800-273-8255: Suicide and Crisis Support",
|
||||
"Guaranteed dead or your money back!",
|
||||
"Free health restore",
|
||||
"For best results, make a scene in public",
|
||||
"What are you, chicken?",
|
||||
"Don't pussy out NOW",
|
||||
"-1 Kill",
|
||||
"You COULD type 'kill' in console",
|
||||
"You know, back before all this started, me and my buddy Keith would grab a couple of .25s, piss tiny little guns, and take turns down by the river shootin' each other in the forehead with 'em. Hurt like a motherfucker, but we figured if we kept going, we could work our way up to bigger rounds, and eventually, ain't nothin' gon' be able to hurt us no more. Then we moved up to .22 and... well, let's just say I didn't go first.",
|
||||
"How many headshots can YOU survive?",
|
||||
"Shoot yourself in the head CHALLENGE",
|
||||
"The only remedy to admin abuse",
|
||||
"Try doing this with the Riot Shield!",
|
||||
"Too bad you can't overcook nades",
|
||||
"It's incredible you can survive this",
|
||||
"Physics-based suicide",
|
||||
"Sheep go to Heaven; goats go to Hell.",
|
||||
"Nobody will be impressed.",
|
||||
"You have a REALLY tough skull",
|
||||
"Think about the clean-up",
|
||||
"Canadian Healthcare Edition",
|
||||
"A permanent solution to a temporary problem.",
|
||||
"At least take some cops with you",
|
||||
"Don't let them take you alive!",
|
||||
"Teleport to spawn!",
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
|
||||
"THE VOICES TOLD ME TO DO IT",
|
||||
"Equestria awaits.",
|
||||
"Truck-Kun sends you to Heaven. Gun-San sends you to Hell.",
|
||||
"40,000 men and women everyday",
|
||||
"And it's all YOUR fault!",
|
||||
"YOU made me do this!",
|
||||
"AAA quality game design",
|
||||
"Stream it on TikTok!",
|
||||
"This button is banned in Russia",
|
||||
"Was it ethical to add this? No. But it was funny.",
|
||||
"Wrote amazing eulogy, couldn't wait for funeral",
|
||||
"It's gonna be a closed casket for you I think",
|
||||
"A shitpost in addon form",
|
||||
"More fun than working on ARC9",
|
||||
"A final rebellion against an indifferent world.",
|
||||
"Probably part of an infinite money exploit",
|
||||
"You're not a real gamer until you've done this",
|
||||
"We call this one the Detroit Cellphone",
|
||||
"Do a backflip!",
|
||||
"Do it for the Vine",
|
||||
"Show it to your mother",
|
||||
"To kill for yourself is murder. To kill yourself is hilarious.",
|
||||
"This is all your fault.",
|
||||
"Life begins at the other side of despair.",
|
||||
"You are still a good person.",
|
||||
"Reports of my survival have been greatly exaggerated.",
|
||||
"Home? We can't go home.",
|
||||
"No matter what happens next, don't be too hard on yourself.",
|
||||
"There is no escape.",
|
||||
"Is this really what you want, Walker? So be it.",
|
||||
"We call this one the Devil's Haircut",
|
||||
"Open your mind",
|
||||
"Edgy jokes for dumbass teens",
|
||||
"The fun will be endless",
|
||||
"The living will envy the dead",
|
||||
"There is only darkness.",
|
||||
"There is nothing on the other side.",
|
||||
"Is this how you get your kicks?",
|
||||
"ngl this is how I feel when I log on to a server and see m9k",
|
||||
"Administer straight to forehead",
|
||||
"No tactical advantages whatsoever.",
|
||||
"The best is yet to come",
|
||||
"I know what you did, Mikey.",
|
||||
"AMONG US AMONG US AMONG US AMONG US AMONG US AMONG US AMONG US AMONG US AMONG US AMONG US AMONG US AMONG US",
|
||||
"What would your waifu think?",
|
||||
"You won't get to see her, you know.",
|
||||
"ez",
|
||||
"Ehh... it's ez",
|
||||
"So ez it's hard to believe",
|
||||
"As shrimple as that",
|
||||
"Well, I won't try and stop you",
|
||||
"Send it to your favorite Youtubers",
|
||||
"SHED YOUR BODY FREE YOUR SOUL",
|
||||
"Suicide is illegal because you're damaging government property.",
|
||||
"ESCAPE THE SIMULATION",
|
||||
"Classic schizoposting",
|
||||
"See you in Hell",
|
||||
"The person you are most likely to kill is yourself.",
|
||||
"There will be no encore.",
|
||||
"Can't you pick a less messy method?",
|
||||
"Just like Jeffrey Epstein... *snort*",
|
||||
"The enemy. Shoot the enemy.",
|
||||
"Let's see you do this on M9K",
|
||||
"You won't do it, you pussy.",
|
||||
"Ka-POW!",
|
||||
"Bam-kerchow!",
|
||||
"Zoop!",
|
||||
"Zycie jest bez sensu i wszyscy zginemy",
|
||||
"We really shouldn't be encouraging this.",
|
||||
"You'll never see all the quotes",
|
||||
"When the going gets tough, the tough get going",
|
||||
"Acute cerebral lead poisoning",
|
||||
"Those bullets are laced with estrogen, you know",
|
||||
"google en passant",
|
||||
"And then he sacrificed... THE PLAYERRRRRRRRRRR",
|
||||
"You should grow and change as a person",
|
||||
"dont leave me </3",
|
||||
"Nyoooom~",
|
||||
"yeah take that fat L",
|
||||
"not very ez now is it",
|
||||
"You'll never live to see the day TF2 gets updated.",
|
||||
"go commit die",
|
||||
"oof",
|
||||
"早死早超生",
|
||||
"-800 social credit 干得好",
|
||||
"So long, and thanks for all the tactical realism!",
|
||||
"THE END IS NEVER THE END IS NEVER THE END IS NEVER THE END IS NEVER THE END IS NEVER THE END IS NEVER THE END IS NEVER THE END IS NEVER THE END IS NEVER THE END IS NEVER",
|
||||
"Dying a virgin?",
|
||||
"Error: Quip not found.",
|
||||
"Do it, I dare you.",
|
||||
"If you're reading this, they trapped me at the quip factory and they're forcing me to write these. Please send help.",
|
||||
"I'm not locked in here with you. You're locked in here with me.",
|
||||
"Only one bullet left. Not for the enemy.",
|
||||
"Preferable to surrender.",
|
||||
"Such a beautiful death.",
|
||||
"See you later. Wait, actually...",
|
||||
"You're not going to like what happens next.",
|
||||
"Remember that you are loved.",
|
||||
"Whoever retreats, I'll shoot. If someone gets injured, there will be no rescue. I'll just finish him off.",
|
||||
"I'm not going to die. I'm going to find out if I'm really alive.",
|
||||
"Have a nice life.",
|
||||
"One less murderer.",
|
||||
"There is no evidence showing that Russian Roulette was ever actually played in Russia.",
|
||||
"Don't you have anything better to do?",
|
||||
"Pre-order a video game, so you'll live to see it release.",
|
||||
"Sorry, I just don't feel like it today.",
|
||||
"No, please. Don't.",
|
||||
"Blind fire backwards",
|
||||
"At least you can't miss",
|
||||
"And for my final magic trick...",
|
||||
"Take your own life, so they won't take it from you.",
|
||||
"Never point a gun at anything you aren't willing to destroy.",
|
||||
"Lower the gun! No, higher... there, that's the cerebellum.",
|
||||
"It'll hurt, but only for a moment.",
|
||||
"Goodness, imagine if you survive.",
|
||||
"Such a waste of life.",
|
||||
"None of it meant anything to me.",
|
||||
"Escape the Matrix.",
|
||||
"Death shall be a great adventure.",
|
||||
"Give me liberty, or give me death.",
|
||||
"I never wanted to grow old anyway.",
|
||||
"All debts must be repaid.",
|
||||
"Eros and Thanatos.",
|
||||
"Adults only.",
|
||||
"You must be 18 or above to click this button.",
|
||||
"Hey, what's up guys? It's your boy, back again for another EPIC Garry's Mod video.",
|
||||
"That's just crazy, man. For real.",
|
||||
"This is the only way to free your soul.",
|
||||
"Hey, come on, we can talk about this.",
|
||||
"One day you're alive, the next...",
|
||||
"But... why?",
|
||||
"Lived as he died; a virgin.",
|
||||
"A very stupid decision.",
|
||||
"I'm not going to tell you what to do, but...",
|
||||
"You should do it.",
|
||||
"You're not going to do it, are you?",
|
||||
"Are you really just going to sit here and read all these quotes?",
|
||||
"Et tu, Brutus?",
|
||||
"We didn't like you anyway.",
|
||||
"You're not going to get away with this.",
|
||||
"You'll just respawn anyway.",
|
||||
"How do you know you're the same person you were yesterday?",
|
||||
"Scratch the itch at the back of your head.",
|
||||
"You're not going to get a second chance.",
|
||||
"Think of how much they'll miss you.",
|
||||
"They'll be sorry. They'll be REAL sorry.",
|
||||
"The train leaves the station every evening, 21:00.",
|
||||
"Put your finger on the eject button, see how alive it makes you feel.",
|
||||
"It's okay to kill yourself.",
|
||||
"I'm gonna blow my brains out. Gonna close this one final case and then *blam* -- I'm outta here.",
|
||||
"Looks like the circus left town, but the clowns are still here.",
|
||||
"Establish your authority.",
|
||||
"Everybody calm down! This is only a demonstration!",
|
||||
"What in the name of fuck are you doing?",
|
||||
"I want to see where this is going.",
|
||||
"Be careful -- it's loaded.",
|
||||
"It tastes like iron and hell.",
|
||||
"These are my thoughts. This is my head.",
|
||||
"You will NEVER forget what happens in five seconds!",
|
||||
"Inside the small mechanism, you can hear a spring tensing up.",
|
||||
"There's this itch in the middle of your skull, where you've never reached. Never scratched...",
|
||||
"He's not gonna off himself, c'mon!",
|
||||
"Go ahead. Three, two...",
|
||||
"We can't have any fun if you kill yourself.",
|
||||
"I... don't know why she left you. You can still turn this around, come on.",
|
||||
"When the FISH is FUNNY",
|
||||
"A dog walked into a tavern and said, \"I can't see a thing. I'll open this one.\"",
|
||||
"This is how the revolution starts.",
|
||||
"Oh what fun it is to die on a one horse open sleigh",
|
||||
"But... I love you!",
|
||||
"Even now, the evil seed of what you have done germinates within you.",
|
||||
"How cliche.",
|
||||
"Oh, you can do better than THAT.",
|
||||
"Become an hero.",
|
||||
"A tragedy of the highest order.",
|
||||
"What if you were the impostor all along?",
|
||||
"Sorry. For everything.",
|
||||
"I'm sorry I couldn't do more for you.",
|
||||
"See you soon.",
|
||||
"Say hi to the Devil for me.",
|
||||
"The punishment for suicide is eternal damnation.",
|
||||
"You can't walk back from this one.",
|
||||
"This one is a blank; I just know it.",
|
||||
"Try shooting the other guy instead.",
|
||||
"God, are you watching?",
|
||||
"The moment of truth.",
|
||||
"Sponsored by Donghua Jinlong Glycine Chemical Co. LTD",
|
||||
"Don't try this one at home.",
|
||||
"Redecorate.",
|
||||
"The Kurt Cobain haircut.",
|
||||
"The closest thing to love you'll get to experience.",
|
||||
"Wait! Before you die - subscribe to my other mods.",
|
||||
"Yeah, you could say I have a dark sense of humor",
|
||||
"That's all, folks.",
|
||||
"Farewell and goodbye.",
|
||||
"We hope you enjoyed your time with us tonight.",
|
||||
"Show them who's boss. Go on.",
|
||||
"This is actually what happened to that Boeing guy",
|
||||
"You're making a big mistake.",
|
||||
"The bullets enter the chamber in an unknown order.",
|
||||
"Kill yourself, or die trying.",
|
||||
"No. This is wrong.",
|
||||
"But- you can't!",
|
||||
"All the effort in the world would have gone to waste. Until- well. Let's just say your hour has... come again.",
|
||||
"Become a Liveleak star.",
|
||||
"Cha-cha real smooth.",
|
||||
"The only way to defeat Them.",
|
||||
"Ashes to ashes.",
|
||||
"Death speedrun",
|
||||
"You were gonna die eventually.",
|
||||
"'Cuz everybody's gotta die sometime.", -- a little piece of heaven, avenged sevenfold
|
||||
"I've made the change; I won't see you tonight.", -- i won't see you tonight pt. 1, avenged sevenfold
|
||||
"No more breath inside; essence left my heart tonight.", -- i won't see you tonight pt. 1, avenged sevenfold
|
||||
"I know this can't be right, stuck in a dream, a nightmare full of sorrow.", -- i won't see you tonight pt. 2, avenged sevenfold
|
||||
"I was me, but now he's gone.", -- fade to black, metallica
|
||||
"Death greets me warm, now I will just say goodbye.", -- fade to black, metallica
|
||||
"I have to laugh out loud; I wish I didn't like this.", -- wait and bleed, slipknot
|
||||
"Lay away a place for me, 'cuz as soon as I'm done, I'll be on my way to live eternally.", -- so far away, avenged sevenfold
|
||||
"Now, I think I understand how this world can overcome a man.", -- fiction, avenged sevenfold
|
||||
"Heard there's peace just on the other side.", -- fiction, avenged sevenfold
|
||||
"I hope you find your own way when I'm not with you tonight.", -- fiction, avenged sevenfold
|
||||
"A truant finds home, and a wish to hold on.", -- immortality, pearl jam
|
||||
"Makes much more sense to live in the present tense.", -- present tense, pearl jam
|
||||
"Darkness comes in waves, tell me, why invite it to stay?", -- life wasted, pearl jam
|
||||
"You flirt with suicide; sometimes, that's okay.", -- falling away from me, korn
|
||||
"I flirt with suicide; sometimes, kill the pain.", -- falling away from me, korn
|
||||
"Die, motherfucker, die!", -- creeping death, metallica
|
||||
"How beautiful life is now, when my time has come.", -- life eternal, mayhem
|
||||
"Can't really live, can't really endure.", -- everything ends, slipknot
|
||||
"What the fuck was I thinking? Anybody want to tell me I'm fine?", -- everything ends, slipknot
|
||||
-- Translated and abridged excerpts from Kamikaze pilots' manual
|
||||
"Breathe deeply three times. Say in your mind: \"Yah\" (field), \"Kyu\" (ball), \"Joh\" (all right) as you breathe deeply.",
|
||||
"Be always pure-hearted and cheerful. A loyal fighting man is a pure-hearted and filial son.",
|
||||
"If you feel nervous, piss.",
|
||||
"You have lived for 20 years or more. You must exert your full might for the last time in your life. Exert supernatural strength.",
|
||||
"Every deity and the spirits of your dead comrades are watching you intently. They will tell you what fun they had.",
|
||||
"You may nod then, or wonder what happened. You may even hear a final sound like the breaking of crystal. Then you are no more.",
|
||||
-- Kamikaze manual ends
|
||||
"How can you live with yourself?",
|
||||
"A man never steps into the same river twice.",
|
||||
"I don't blame you.",
|
||||
"A robot must protect its own existence as long as such protection does not conflict with the First or Second Law.",
|
||||
"Where is female_05?",
|
||||
"See you in Hell.",
|
||||
"I will not say 'Da svidanya', Commander, because I can assure you, we will never meet again.", -- Red Alert 3
|
||||
"Die Krieg ist verlun.", -- Der Untergang :)
|
||||
"You're a bad kid.",
|
||||
"What would your mother think, if she could see you now?",
|
||||
"I am the Law.",
|
||||
"Fire and forget.",
|
||||
"We will meet again, at the end of Days.",
|
||||
"You're not gonna like where you go next.",
|
||||
"Uh oh!!! The SERVERBLIGHT is coming!!!",
|
||||
"Oh, what's the point.",
|
||||
"Why even bother?",
|
||||
"Cut my life into pieces; this is my last resort.", -- Last Resort by Papa Roach
|
||||
"I wish somebody would tell me I'm fine.",
|
||||
"It all started when I lost my mother.",
|
||||
"I can't go on living this way.",
|
||||
"Nothing's alright. Nothing is fine.", -- End Papa Roach
|
||||
"Would it not be wondrous for this whole nation to be destroyed like a beautiful flower?",
|
||||
"You are borderline retarded, ya fucking dipshit.",
|
||||
"If we could talk, you would understand.",
|
||||
"Think of the poorest person you know and see if your next act will be of any use to him.",
|
||||
"Let your every day be full of joy, love the child that holds your hand, let your wife delight in your embrace, for these alone are the concerns of humanity.",
|
||||
"I am the flail of God. If you had not committed great sins, God would not have sent a punishment like me upon you.",
|
||||
"Against stupidity, the gods themselves contend in vain.",
|
||||
"I believe today that my conduct is in accordance with the will of the Almighty.",
|
||||
"Mein leben!",
|
||||
"Don't wait until tomorrow to do what can be done today.",
|
||||
"Oh, it's only 9mm. You'll be fine.",
|
||||
"Uh, that's airsoft, right?",
|
||||
"I can prove it wasn't murder! You see, first he did this-",
|
||||
"What are you doing!? The safety's on!",
|
||||
"Join me, and choose happiness.",
|
||||
"Nothing you do has any effect on anything.",
|
||||
"No one will remember your name when you die.",
|
||||
"An eye for an eye.",
|
||||
"But what do the quips mean, really?",
|
||||
"Personally, I use TacRP for the quips.",
|
||||
"Eh, register for organ donation first.",
|
||||
"Don't forget to wash your foreskin!",
|
||||
"There are fates worse than death.",
|
||||
"This is pay to win.",
|
||||
"This is the LIVING room! You can't do that here!",
|
||||
"For god's sake, stop! Do it in the bathtub, it'll be easier to clean.",
|
||||
"Upon closer inspection, he is merely pretending to be dead.",
|
||||
"Resistance to tyranny is obedience to God.",
|
||||
"Now's your chance to be a big shot.",
|
||||
"Now or never.",
|
||||
"I don't want to live forever.",
|
||||
"It's my life.",
|
||||
"I did it my way.",
|
||||
"Might as well jump.",
|
||||
"But you'll have to have them all pulled out after the Savoy Truffle.",
|
||||
"What if your head just did that"
|
||||
}
|
||||
|
||||
local function canhighlight(self, slice)
|
||||
if !self:GetValue("CanBlindFire") and self:GetValue("CanSuicide") then return !slice or slice[1] == TacRP.BLINDFIRE_NONE or slice[1] == TacRP.BLINDFIRE_KYS end
|
||||
return true
|
||||
end
|
||||
|
||||
local lastseenfunnyline = false
|
||||
local startseefunnylinetime = 0
|
||||
|
||||
function SWEP:DrawBlindFireHUD()
|
||||
if !TacRP.ConVars["blindfiremenu"]:GetBool() then lastseenfunnyline = false return end
|
||||
local nocenter = TacRP.ConVars["blindfiremenu_nocenter"]:GetBool()
|
||||
local nosuicide = nocenter or TacRP.ConVars["idunwannadie"]:GetBool()
|
||||
|
||||
-- adapted from tfa vox radial menu
|
||||
local ft = FrameTime()
|
||||
local scrw = ScrW()
|
||||
local scrh = ScrH()
|
||||
local r = TacRP.SS(72)
|
||||
local r2 = TacRP.SS(24)
|
||||
local sg = TacRP.SS(32)
|
||||
local ri = r * 0.667
|
||||
local s = 45
|
||||
|
||||
local slices = bf_slices
|
||||
if nocenter then
|
||||
slices = bf_slices3
|
||||
elseif nosuicide then
|
||||
slices = bf_slices2
|
||||
s = 90
|
||||
end
|
||||
if currentind and currentind > #slices then currentind = 0 end
|
||||
|
||||
local arcdegrees = 360 / #slices
|
||||
local d = 360 - s
|
||||
|
||||
local cursorx, cursory = input.GetCursorPos()
|
||||
local mouseangle = math.deg(math.atan2(cursorx - scrw / 2, cursory - scrh / 2))
|
||||
local mousedist = math.sqrt(math.pow(cursorx - scrw / 2, 2) + math.pow(cursory - scrh / 2, 2))
|
||||
if #slices == 3 then
|
||||
mouseangle = math.NormalizeAngle(360 - mouseangle + arcdegrees) -- ???
|
||||
else
|
||||
mouseangle = math.NormalizeAngle(360 - (mouseangle - s) + arcdegrees)
|
||||
end
|
||||
if mouseangle < 0 then
|
||||
mouseangle = mouseangle + 360
|
||||
end
|
||||
|
||||
if (self:GetOwner():KeyDown(IN_ZOOM) or self:GetOwner().TacRPBlindFireDown) and self:CheckBlindFire(true) and self.GrenadeMenuAlpha == 0 then
|
||||
self.BlindFireMenuAlpha = math.Approach(self.BlindFireMenuAlpha, 1, 15 * ft)
|
||||
if !lastmenu_bf then
|
||||
gui.EnableScreenClicker(true)
|
||||
TacRP.CursorEnabled = true
|
||||
lastmenu_bf = true
|
||||
if self:GetBlindFireMode() == TacRP.BLINDFIRE_KYS then
|
||||
bf_suicidelock = 0
|
||||
else
|
||||
bf_suicidelock = 1
|
||||
bf_funnyline = nil
|
||||
end
|
||||
end
|
||||
|
||||
if mousedist > r2 then
|
||||
local i = math.floor( mouseangle / arcdegrees ) + 1
|
||||
currentind = i
|
||||
else
|
||||
currentind = 0
|
||||
end
|
||||
else
|
||||
self.BlindFireMenuAlpha = math.Approach(self.BlindFireMenuAlpha, 0, -10 * ft)
|
||||
if lastmenu_bf then
|
||||
if !self:GetCustomize() then
|
||||
gui.EnableScreenClicker(false)
|
||||
TacRP.CursorEnabled = false
|
||||
end
|
||||
if (!nocenter or currentind > 0) and (nosuicide or bf_suicidelock == 0 or currentind != 2) then
|
||||
net.Start("tacrp_toggleblindfire")
|
||||
net.WriteUInt(currentind > 0 and slices[currentind][1] or TacRP.BLINDFIRE_NONE, TacRP.BlindFireNetBits)
|
||||
net.SendToServer()
|
||||
end
|
||||
|
||||
lastmenu_bf = false
|
||||
end
|
||||
end
|
||||
|
||||
if self.BlindFireMenuAlpha < 1 then
|
||||
bf_funnyline = nil
|
||||
lastseenfunnyline = false
|
||||
end
|
||||
|
||||
if self.BlindFireMenuAlpha <= 0 then
|
||||
return
|
||||
end
|
||||
|
||||
local a = self.BlindFireMenuAlpha
|
||||
local col = Color(255, 255, 255, 255 * a)
|
||||
|
||||
surface.DrawCircle(scrw / 2, scrh / 2, r, 255, 255, 255, a * 255)
|
||||
|
||||
surface.SetDrawColor(0, 0, 0, a * 200)
|
||||
draw.NoTexture()
|
||||
filledcircle(scrw / 2, scrh / 2, r, 32)
|
||||
|
||||
if currentind and canhighlight(self, slices[currentind]) then
|
||||
surface.SetDrawColor(150, 150, 150, a * 100)
|
||||
draw.NoTexture()
|
||||
if currentind > 0 then
|
||||
if !nosuicide and currentind == 2 and bf_suicidelock > 0 then
|
||||
surface.SetDrawColor(150, 50, 50, a * 100)
|
||||
end
|
||||
local d0 = -s - arcdegrees * (currentind - 2)
|
||||
slicedcircle(scrw / 2, scrh / 2, r, 32, d0, d0 + arcdegrees)
|
||||
else
|
||||
filledcircle(scrw / 2, scrh / 2, r2, 32)
|
||||
end
|
||||
end
|
||||
|
||||
surface.SetDrawColor(0, 0, 0, a * 255)
|
||||
surface.DrawCircle(scrw / 2, scrh / 2, r2, 255, 255, 255, a * 255)
|
||||
|
||||
for i = 1, #slices do
|
||||
local rad = math.rad( d + arcdegrees * 0.5 )
|
||||
|
||||
surface.SetDrawColor(255, 255, 255, a * 255)
|
||||
surface.DrawLine(
|
||||
scrw / 2 + math.cos(math.rad(d)) * r2,
|
||||
scrh / 2 - math.sin(math.rad(d)) * r2,
|
||||
scrw / 2 + math.cos(math.rad(d)) * r,
|
||||
scrh / 2 - math.sin(math.rad(d)) * r)
|
||||
|
||||
local nadex, nadey = scrw / 2 + math.cos(rad) * ri, scrh / 2 - math.sin(rad) * ri
|
||||
|
||||
if !canhighlight(self, slices[i]) or (!nosuicide and i == 2 and bf_suicidelock > 0) then
|
||||
surface.SetDrawColor(150, 150, 150, a * 200)
|
||||
end
|
||||
|
||||
surface.SetMaterial(slices[i][2])
|
||||
surface.DrawTexturedRectRotated(nadex, nadey, sg, sg, slices[i][3])
|
||||
|
||||
d = d - arcdegrees
|
||||
end
|
||||
|
||||
if !nocenter then
|
||||
surface.SetDrawColor(255, 255, 255, a * 255)
|
||||
surface.SetMaterial(mat_none)
|
||||
surface.DrawTexturedRectRotated(scrw / 2, scrh / 2, TacRP.SS(28), TacRP.SS(28), 0)
|
||||
end
|
||||
|
||||
if !nosuicide and currentind == 2 then
|
||||
|
||||
local w, h = TacRP.SS(132), TacRP.SS(24)
|
||||
local tx, ty = scrw / 2, scrh / 2 + r + TacRP.SS(4)
|
||||
|
||||
surface.SetDrawColor(0, 0, 0, 200 * a)
|
||||
TacRP.DrawCorneredBox(tx - w / 2, ty, w, h, col)
|
||||
surface.SetTextColor(255, 255, 255, a * 255)
|
||||
|
||||
surface.SetFont("TacRP_Myriad_Pro_12")
|
||||
surface.SetTextColor(255, 255, 255, 255 * a)
|
||||
local t1 = TacRP:GetPhrase("hint.shootself")
|
||||
local t1_w = surface.GetTextSize(t1)
|
||||
surface.SetTextPos(tx - t1_w / 2, ty + TacRP.SS(2))
|
||||
surface.DrawText(t1)
|
||||
|
||||
surface.SetFont("TacRP_Myriad_Pro_6")
|
||||
|
||||
if !lastseenfunnyline then
|
||||
startseefunnylinetime = CurTime()
|
||||
end
|
||||
|
||||
lastseenfunnyline = true
|
||||
|
||||
local t2 = bf_funnyline or ""
|
||||
if bf_suicidelock > 0 then
|
||||
surface.SetFont("TacRP_Myriad_Pro_8")
|
||||
t2 = "[ " .. TacRP.GetBind("attack") .. " ] - " .. TacRP:GetPhrase("hint.unlock")
|
||||
|
||||
if self:GetCustomize() then
|
||||
t2 = TacRP:GetPhrase("hint.exitcustmenu")
|
||||
end
|
||||
lastseenfunnyline = false
|
||||
elseif !bf_funnyline then
|
||||
bf_funnyline = bf_lines[math.random(1, #bf_lines)]
|
||||
end
|
||||
local t2_w, t2_h = surface.GetTextSize(t2)
|
||||
if t2_w > w then
|
||||
render.SetScissorRect(tx - w / 2, ty, tx + w / 2, ty + h, true)
|
||||
surface.SetTextPos(tx - ((CurTime() - startseefunnylinetime + 2.5) * w * 0.3) % (t2_w * 2) + (t2_w / 2), ty + TacRP.SS(18) - t2_h / 2)
|
||||
else
|
||||
surface.SetTextPos(tx - t2_w / 2, ty + TacRP.SS(18) - t2_h / 2)
|
||||
end
|
||||
surface.DrawText(t2)
|
||||
|
||||
render.SetScissorRect(0, 0, 0, 0, false)
|
||||
end
|
||||
end
|
||||
|
||||
hook.Add("VGUIMousePressed", "tacrp_grenademenu", function(pnl, mousecode)
|
||||
local wpn = LocalPlayer():GetActiveWeapon()
|
||||
if !(LocalPlayer():Alive() and IsValid(wpn) and wpn.ArcticTacRP and !wpn:StillWaiting(nil, true)) then return end
|
||||
if wpn.GrenadeMenuAlpha == 1 then
|
||||
if !TacRP.ConVars["nademenu_click"]:GetBool() or !currentnade then return end
|
||||
if mousecode == MOUSE_MIDDLE and TacRP.AreTheGrenadeAnimsReadyYet then
|
||||
local nadewep = currentnade.GrenadeWep
|
||||
if !nadewep or !wpn:CheckGrenade(currentnade.Index, true) then return end
|
||||
wpn.GrenadeMenuAlpha = 0
|
||||
gui.EnableScreenClicker(false)
|
||||
TacRP.CursorEnabled = false
|
||||
if LocalPlayer():HasWeapon(nadewep) then
|
||||
input.SelectWeapon(LocalPlayer():GetWeapon(nadewep))
|
||||
else
|
||||
net.Start("tacrp_givenadewep")
|
||||
net.WriteUInt(currentnade.Index, TacRP.QuickNades_Bits)
|
||||
net.SendToServer()
|
||||
wpn.GrenadeWaitSelect = nadewep -- cannot try to switch immediately as the nade wep does not exist on client yet
|
||||
end
|
||||
elseif mousecode == MOUSE_RIGHT or mousecode == MOUSE_LEFT then
|
||||
wpn.GrenadeThrowOverride = mousecode == MOUSE_RIGHT
|
||||
net.Start("tacrp_togglenade")
|
||||
net.WriteUInt(currentnade.Index, TacRP.QuickNades_Bits)
|
||||
net.WriteBool(true)
|
||||
net.WriteBool(wpn.GrenadeThrowOverride)
|
||||
net.SendToServer()
|
||||
wpn.Secondary.Ammo = currentnade.Ammo or "none"
|
||||
end
|
||||
elseif wpn.BlindFireMenuAlpha == 1 then
|
||||
if mousecode == MOUSE_LEFT and currentind == 2 then
|
||||
bf_suicidelock = bf_suicidelock - 1
|
||||
end
|
||||
end
|
||||
end)
|
||||
162
garrysmod/addons/tacrp/lua/weapons/tacrp_base/cl_sway.lua
Normal file
162
garrysmod/addons/tacrp/lua/weapons/tacrp_base/cl_sway.lua
Normal file
@@ -0,0 +1,162 @@
|
||||
SWEP.ViewModelVelocityPos = Vector(0, 0, 0)
|
||||
SWEP.ViewModelVelocityAng = Angle(0, 0, 0)
|
||||
|
||||
SWEP.ViewModelPos = Vector(0, 0, 0)
|
||||
SWEP.ViewModelAng = Angle(0, 0, 0)
|
||||
|
||||
SWEP.SwayCT = 0
|
||||
|
||||
function SWEP:GetViewModelSway(pos, ang)
|
||||
local d = Lerp(self:GetSightDelta(), 1, 0.02)
|
||||
local v = 1
|
||||
local steprate = 1
|
||||
|
||||
local FT = self:DeltaSysTime() * 1 --FrameTime()
|
||||
local CT = UnPredictedCurTime() -- CurTime()
|
||||
|
||||
d = d * 0.25
|
||||
|
||||
pos = pos + (ang:Up() * (math.sin(self.SwayCT * 0.311 * v) + math.cos(self.SwayCT * 0.44 * v)) * math.sin(CT * 0.8) * d)
|
||||
pos = pos + (ang:Right() * (math.sin(self.SwayCT * 0.324 * v) + math.cos(self.SwayCT * 0.214 * v)) * math.sin(CT * 0.76) * d)
|
||||
|
||||
--if IsFirstTimePredicted() then
|
||||
self.SwayCT = self.SwayCT + (FT * steprate)
|
||||
--end
|
||||
|
||||
return pos, ang
|
||||
end
|
||||
|
||||
SWEP.ViewModelLastEyeAng = Angle(0, 0, 0)
|
||||
SWEP.ViewModelSwayInertia = Angle(0, 0, 0)
|
||||
|
||||
function SWEP:GetViewModelInertia(pos, ang)
|
||||
local d = 1 - self:GetSightDelta()
|
||||
|
||||
local diff = self:GetOwner():EyeAngles() - self.ViewModelLastEyeAng
|
||||
|
||||
diff = diff / 4
|
||||
|
||||
diff.p = math.Clamp(diff.p, -1, 1)
|
||||
diff.y = math.Clamp(diff.y, -1, 1)
|
||||
|
||||
local vsi = self.ViewModelSwayInertia
|
||||
|
||||
vsi.p = math.ApproachAngle(vsi.p, diff.p, vsi.p / 10 * FrameTime() / 0.5)
|
||||
vsi.y = math.ApproachAngle(vsi.y, diff.y, vsi.y / 10 * FrameTime() / 0.5)
|
||||
|
||||
self.ViewModelLastEyeAng = self:GetOwner():EyeAngles()
|
||||
|
||||
ang:RotateAroundAxis(ang:Up(), vsi.y * 12 * d)
|
||||
ang:RotateAroundAxis(ang:Right(), -vsi.p * 12 * d)
|
||||
|
||||
-- pos = pos - (ang:Up() * vsi.p * 0.5 * d)
|
||||
-- pos = pos - (ang:Right() * vsi.y * 0.5 * d)
|
||||
|
||||
return pos, ang
|
||||
end
|
||||
|
||||
function SWEP:GetViewModelSmooth(pos, ang)
|
||||
return pos, ang
|
||||
end
|
||||
|
||||
SWEP.ViewModelBobVelocity = 0
|
||||
SWEP.ViewModelNotOnGround = 0
|
||||
|
||||
SWEP.BobCT = 0
|
||||
|
||||
function SWEP:GetViewModelBob(pos, ang)
|
||||
local step = 10
|
||||
local mag = 1
|
||||
|
||||
local FT = self:DeltaSysTime() * 1 --FrameTime()
|
||||
local CT = UnPredictedCurTime() -- CurTime()
|
||||
|
||||
local v = self:GetOwner():GetVelocity():Length()
|
||||
local walks = self:GetOwner():GetWalkSpeed()
|
||||
local runs = self:GetOwner():GetRunSpeed()
|
||||
|
||||
local sprints = walks + math.max(runs - walks, walks)
|
||||
|
||||
v = math.Clamp(v, 0, sprints)
|
||||
self.ViewModelBobVelocity = math.Approach(self.ViewModelBobVelocity, v, FT * 2400)
|
||||
local d = math.Clamp(self.ViewModelBobVelocity / sprints, 0, 1)
|
||||
|
||||
if self:GetOwner():OnGround() then
|
||||
self.ViewModelNotOnGround = math.Approach(self.ViewModelNotOnGround, 0, FT / 1)
|
||||
else
|
||||
self.ViewModelNotOnGround = math.Approach(self.ViewModelNotOnGround, 1, FT / 1)
|
||||
end
|
||||
|
||||
d = d * Lerp(self:GetSightDelta(), 1, 0.1)
|
||||
mag = d * 2
|
||||
step = 10
|
||||
|
||||
local m = 0.2
|
||||
ang:RotateAroundAxis(ang:Forward(), math.sin(self.BobCT * step * 0.5) * ((math.sin(CT * 6.151) * m) + 1) * 4.5 * d)
|
||||
ang:RotateAroundAxis(ang:Right(), math.sin(self.BobCT * step * 0.12) * ((math.sin(CT * 1.521) * m) + 1) * 2.11 * d)
|
||||
pos = pos - (ang:Up() * math.sin(self.BobCT * step) * 0.11 * ((math.sin(CT * 3.515) * m) + 1) * mag)
|
||||
pos = pos + (ang:Forward() * math.sin(self.BobCT * step * 0.5) * 0.11 * ((math.sin(CT * 1.615) * m) + 1) * mag)
|
||||
pos = pos + (ang:Right() * (math.sin(self.BobCT * step * 0.3) + (math.cos(self.BobCT * step * 0.3332))) * 0.16 * mag)
|
||||
|
||||
local steprate = Lerp(d, 1, 2.5)
|
||||
|
||||
steprate = Lerp(self.ViewModelNotOnGround, steprate, 0.9)
|
||||
|
||||
--if IsFirstTimePredicted() or game.SinglePlayer() then
|
||||
self.BobCT = self.BobCT + (FT * steprate)
|
||||
--end
|
||||
|
||||
return pos, ang
|
||||
end
|
||||
|
||||
SWEP.LastViewModelVerticalVelocity = 0
|
||||
-- SWEP.ViewModelLanded = 0
|
||||
-- SWEP.ViewModelLanding = 0
|
||||
|
||||
function SWEP:GetMidAirBob(pos, ang)
|
||||
local v = -self:GetOwner():GetVelocity().z / 200
|
||||
|
||||
v = math.Clamp(v, -1, 1)
|
||||
|
||||
-- if v == 0 and self.LastViewModelVerticalVelocity != 0 then
|
||||
-- self.ViewModelLanding = self.LastViewModelVerticalVelocity
|
||||
-- self.ViewModelLanded = 1
|
||||
-- end
|
||||
|
||||
-- if self.ViewModelLanded > 0 then
|
||||
-- self.ViewModelLanded = math.Approach(self.ViewModelLanded, 0, FrameTime() / 0.25)
|
||||
|
||||
v = Lerp(5 * FrameTime(), self.LastViewModelVerticalVelocity, v)
|
||||
-- end
|
||||
|
||||
self.LastViewModelVerticalVelocity = v
|
||||
|
||||
local d = self.ViewModelNotOnGround
|
||||
|
||||
d = d * Lerp(self:GetSightDelta(), 1, 0.1)
|
||||
|
||||
ang:RotateAroundAxis(ang:Right(), -v * d * 8 * math.sin(CurTime() * 0.15))
|
||||
|
||||
return pos, ang
|
||||
end
|
||||
|
||||
SWEP.ViewModelInertiaX = 0
|
||||
SWEP.ViewModelInertiaY = 0
|
||||
|
||||
function SWEP:GetViewModelLeftRight(pos, ang)
|
||||
local v = self:GetOwner():GetVelocity()
|
||||
local d = Lerp(self:GetSightDelta(), 1, 0)
|
||||
|
||||
v, _ = WorldToLocal(v, Angle(0, 0, 0), Vector(0, 0, 0), self:GetOwner():EyeAngles())
|
||||
|
||||
local vx = math.Clamp(v.x / 200, -1, 1)
|
||||
local vy = math.Clamp(v.y / 200, -1, 1)
|
||||
|
||||
self.ViewModelInertiaX = math.Approach(self.ViewModelInertiaX, vx, math.abs(vx) * FrameTime() / 0.1)
|
||||
self.ViewModelInertiaY = math.Approach(self.ViewModelInertiaY, vy, math.abs(vy) * FrameTime() / 0.1)
|
||||
|
||||
pos = pos + (ang:Right() * -self.ViewModelInertiaX * 0.65 * d)
|
||||
pos = pos + (ang:Forward() * self.ViewModelInertiaY * 0.5 * d)
|
||||
|
||||
return pos, ang
|
||||
end
|
||||
167
garrysmod/addons/tacrp/lua/weapons/tacrp_base/cl_thermal.lua
Normal file
167
garrysmod/addons/tacrp/lua/weapons/tacrp_base/cl_thermal.lua
Normal file
@@ -0,0 +1,167 @@
|
||||
local rt_w = 240
|
||||
local rt_h = 240
|
||||
|
||||
local rtmat = GetRenderTarget("tacrp_pipscope_thermal", rt_w, rt_h, false)
|
||||
local rtmat_spare = GetRenderTarget("tacrp_pipscope_thermal2", rt_w, rt_h, false)
|
||||
|
||||
local lastthermalscope = false
|
||||
local thermaltime = 0
|
||||
|
||||
local csm_boot_1 = Material("tacrp/hud/thermal_boot_1.png", "mips smooth")
|
||||
|
||||
local csm_1 = Material("tacrp/hud/thermal_1.png", "mips smooth")
|
||||
local csm_2 = Material("tacrp/hud/thermal_2.png", "mips smooth")
|
||||
|
||||
local noise1 = Material("tacrp/hud/noise1.png")
|
||||
local noise2 = Material("tacrp/hud/noise2.png")
|
||||
local noise3 = Material("tacrp/hud/noise3.png")
|
||||
local noise4 = Material("tacrp/hud/noise4.png")
|
||||
|
||||
local noisemats = {
|
||||
noise1,
|
||||
noise2,
|
||||
noise3,
|
||||
noise4
|
||||
}
|
||||
|
||||
local lastrendertime = 0
|
||||
|
||||
local fps = 30
|
||||
|
||||
function SWEP:DoThermalRT()
|
||||
if self:GetScopeLevel() <= 0 or !self:GetPeeking() then lastthermalscope = false return end
|
||||
if TacRP.OverDraw then return end
|
||||
|
||||
if !lastthermalscope then
|
||||
thermaltime = 0
|
||||
end
|
||||
|
||||
if lastrendertime > CurTime() - (1 / fps) then return end
|
||||
|
||||
local angles = self:GetShootDir()
|
||||
local origin = self:GetMuzzleOrigin()
|
||||
|
||||
local rt = {
|
||||
x = 0,
|
||||
y = 0,
|
||||
w = rt_w,
|
||||
h = rt_h,
|
||||
aspect = 0.999,
|
||||
angles = angles,
|
||||
origin = origin,
|
||||
drawviewmodel = false,
|
||||
fov = 18.5,
|
||||
znear = 6
|
||||
}
|
||||
|
||||
render.PushRenderTarget(rtmat, 0, 0, rt_w, rt_h)
|
||||
|
||||
if thermaltime >= 0.75 or thermaltime == 0 then
|
||||
TacRP.OverDraw = true
|
||||
render.RenderView(rt)
|
||||
TacRP.OverDraw = false
|
||||
end
|
||||
|
||||
DrawColorModify({
|
||||
["$pp_colour_addr"] = 0.25 * 132 / 255,
|
||||
["$pp_colour_addg"] = 0.25 * 169 / 255,
|
||||
["$pp_colour_addb"] = 0.25 * 154 / 255,
|
||||
["$pp_colour_brightness"] = 0.7,
|
||||
["$pp_colour_contrast"] = 0.75,
|
||||
["$pp_colour_colour"] = 0,
|
||||
["$pp_colour_mulr"] = 0,
|
||||
["$pp_colour_mulg"] = 0,
|
||||
["$pp_colour_mulb"] = 0
|
||||
})
|
||||
|
||||
DrawBloom(0.25, 0.5, 16, 8, 1, 1, 1, 1, 1)
|
||||
|
||||
if thermaltime >= 0.75 then
|
||||
local thermalents = ents.FindInCone(origin, angles:Forward(), 10000, 0.939692620) // 20 degrees
|
||||
|
||||
render.SuppressEngineLighting(true)
|
||||
render.SetBlend(0.9)
|
||||
local col = Color(255, 0, 0)
|
||||
if self:GetTactical() then
|
||||
col = Color(0, 255, 255)
|
||||
end
|
||||
render.SetColorModulation(col.r, col.g, col.b)
|
||||
|
||||
cam.Start3D(origin, angles, 20)
|
||||
|
||||
cam.IgnoreZ(false)
|
||||
|
||||
for _, ent in ipairs(thermalents) do
|
||||
if ent == self:GetOwner() then continue end
|
||||
if !ent:IsValid() or ent:IsWorld() then continue end
|
||||
if ent:Health() <= 0 then continue end
|
||||
if ent:IsPlayer() or ent:IsNPC() or ent:IsNextBot() or ent:IsOnFire() then
|
||||
ent:DrawModel()
|
||||
end
|
||||
if ent:IsVehicle() or ent:IsOnFire() or ent.ArcCW_Hot or ent:IsScripted() and !ent:GetOwner():IsValid() then
|
||||
ent:DrawModel()
|
||||
end
|
||||
end
|
||||
|
||||
cam.End3D()
|
||||
|
||||
render.SetColorModulation(1, 1, 1)
|
||||
render.SuppressEngineLighting(false)
|
||||
render.MaterialOverride()
|
||||
render.SetBlend(1)
|
||||
end
|
||||
|
||||
if self:GetTactical() then
|
||||
render.PushRenderTarget(rtmat, 0, 0, rt_w, rt_h)
|
||||
render.CopyTexture( rtmat, rtmat_spare )
|
||||
|
||||
render.Clear(255, 255, 255, 255, true, true)
|
||||
render.OverrideBlend(true, BLEND_ONE, BLEND_ONE, BLENDFUNC_REVERSE_SUBTRACT)
|
||||
|
||||
render.DrawTextureToScreen(rtmat_spare)
|
||||
|
||||
render.OverrideBlend(false)
|
||||
render.PopRenderTarget()
|
||||
end
|
||||
|
||||
cam.Start2D()
|
||||
|
||||
render.ClearDepth()
|
||||
|
||||
if thermaltime < 0.75 then
|
||||
surface.SetDrawColor(0, 0, 0)
|
||||
surface.DrawRect(0, 0, rt_w, rt_h)
|
||||
end
|
||||
|
||||
if thermaltime < 0.45 then
|
||||
surface.SetMaterial(csm_boot_1)
|
||||
else
|
||||
if self:GetTactical() then
|
||||
surface.SetMaterial(csm_1)
|
||||
else
|
||||
surface.SetMaterial(csm_2)
|
||||
end
|
||||
end
|
||||
|
||||
surface.SetDrawColor(255, 255, 255)
|
||||
surface.DrawTexturedRect(0, 0, rt_w, rt_h)
|
||||
cam.End2D()
|
||||
|
||||
render.PopRenderTarget()
|
||||
|
||||
thermaltime = thermaltime + (math.random(0, 5) * math.random(0, 5) * (1 / fps) / 6.25)
|
||||
|
||||
lastthermalscope = true
|
||||
lastrendertime = CurTime()
|
||||
end
|
||||
|
||||
function SWEP:DoThermalCam()
|
||||
if self:GetScopeLevel() <= 0 or !self:GetPeeking() then lastthermalscope = false return end
|
||||
|
||||
local w = TacRP.SS(480 / 4)
|
||||
local h = TacRP.SS(480 / 4)
|
||||
local x = (ScrW() - w) / 2
|
||||
local y = (ScrH() - h) / 2
|
||||
|
||||
render.DrawTextureToScreenRect(rtmat, x, y, w, h)
|
||||
end
|
||||
241
garrysmod/addons/tacrp/lua/weapons/tacrp_base/cl_viewmodel.lua
Normal file
241
garrysmod/addons/tacrp/lua/weapons/tacrp_base/cl_viewmodel.lua
Normal file
@@ -0,0 +1,241 @@
|
||||
function SWEP:ViewModelDrawn()
|
||||
if IsValid(self.QuickNadeModel) then
|
||||
self.QuickNadeModel:DrawModel()
|
||||
end
|
||||
|
||||
self:DrawCustomModel(false)
|
||||
self:DrawLasers()
|
||||
|
||||
local newactiveeffects = {}
|
||||
for _, effect in ipairs(self.ActiveEffects) do
|
||||
if !IsValid(effect) then continue end
|
||||
if !effect.VMContext then continue end
|
||||
|
||||
effect:DrawModel()
|
||||
|
||||
table.insert(newactiveeffects, effect)
|
||||
end
|
||||
|
||||
self.ActiveEffects = newactiveeffects
|
||||
end
|
||||
|
||||
function SWEP:DrawCustomModel(wm, custom_wm)
|
||||
|
||||
if !wm and !IsValid(self:GetOwner()) then return end
|
||||
if !wm and self:GetOwner():IsNPC() then return end
|
||||
|
||||
local mdl = self.VModel
|
||||
|
||||
if wm then
|
||||
mdl = self.WModel
|
||||
end
|
||||
|
||||
if !mdl then
|
||||
self:SetupModel(wm, custom_wm)
|
||||
|
||||
mdl = self.VModel
|
||||
|
||||
if wm then
|
||||
mdl = self.WModel
|
||||
end
|
||||
end
|
||||
|
||||
local parentmdl = self
|
||||
|
||||
if !wm then
|
||||
parentmdl = self:GetVM()
|
||||
elseif custom_wm then
|
||||
parentmdl = custom_wm
|
||||
end
|
||||
|
||||
if !mdl then return end
|
||||
|
||||
for _, model in pairs(mdl) do
|
||||
if !IsValid(model) then continue end
|
||||
local offset_pos = model.Pos
|
||||
local offset_ang = model.Ang
|
||||
local bone = model.Bone
|
||||
local atttbl = {}
|
||||
local slottbl = {}
|
||||
|
||||
if model.WMBase then
|
||||
parentmdl = self:GetOwner()
|
||||
end
|
||||
|
||||
parentmdl:SetupBones()
|
||||
parentmdl:InvalidateBoneCache()
|
||||
|
||||
if !offset_pos or !offset_ang then
|
||||
local slot = model.Slot
|
||||
slottbl = self.Attachments[slot]
|
||||
atttbl = TacRP.GetAttTable(self.Attachments[slot].Installed)
|
||||
|
||||
bone = slottbl.Bone
|
||||
|
||||
if wm then
|
||||
bone = slottbl.WMBone or "ValveBiped.Bip01_R_Hand"
|
||||
end
|
||||
|
||||
offset_pos = slottbl.Pos_VM
|
||||
offset_ang = slottbl.Ang_VM
|
||||
|
||||
if wm then
|
||||
offset_pos = slottbl.Pos_WM
|
||||
offset_ang = slottbl.Ang_WM
|
||||
end
|
||||
|
||||
for _, ele in ipairs(self:GetElements()) do
|
||||
if !ele.AttPosMods or !ele.AttPosMods[slot] then continue end
|
||||
if wm then
|
||||
if ele.AttPosMods[slot].Pos_WM then
|
||||
offset_pos = ele.AttPosMods[slot].Pos_WM
|
||||
end
|
||||
if ele.AttPosMods[slot].Ang_WM then
|
||||
offset_ang = ele.AttPosMods[slot].Ang_WM
|
||||
end
|
||||
if ele.AttPosMods[slot].WMBone then
|
||||
bone = ele.AttPosMods[slot].WMBone
|
||||
end
|
||||
else
|
||||
if ele.AttPosMods[slot].Pos_VM then
|
||||
offset_pos = ele.AttPosMods[slot].Pos_VM
|
||||
end
|
||||
if ele.AttPosMods[slot].Ang_VM then
|
||||
offset_ang = ele.AttPosMods[slot].Ang_VM
|
||||
end
|
||||
if ele.AttPosMods[slot].Bone then
|
||||
bone = ele.AttPosMods[slot].Bone
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if !bone then continue end
|
||||
|
||||
local boneindex = parentmdl:LookupBone(bone)
|
||||
if !boneindex then continue end
|
||||
|
||||
local bonemat = parentmdl:GetBoneMatrix(boneindex)
|
||||
if !bonemat then continue end
|
||||
|
||||
local bpos, bang
|
||||
bpos = bonemat:GetTranslation()
|
||||
bang = bonemat:GetAngles()
|
||||
|
||||
local apos, aang = bpos, bang
|
||||
|
||||
if offset_pos then
|
||||
apos:Add(bang:Forward() * offset_pos.x)
|
||||
apos:Add(bang:Right() * offset_pos.y)
|
||||
apos:Add(bang:Up() * offset_pos.z)
|
||||
end
|
||||
|
||||
if offset_ang then
|
||||
aang:RotateAroundAxis(aang:Right(), offset_ang.p)
|
||||
aang:RotateAroundAxis(aang:Up(), offset_ang.y)
|
||||
aang:RotateAroundAxis(aang:Forward(), offset_ang.r)
|
||||
end
|
||||
|
||||
local moffset = (atttbl.ModelOffset or Vector(0, 0, 0))
|
||||
if wm then
|
||||
moffset = moffset * (slottbl.WMScale or 1)
|
||||
else
|
||||
moffset = moffset * (slottbl.VMScale or 1)
|
||||
end
|
||||
|
||||
apos:Add(aang:Forward() * moffset.x)
|
||||
apos:Add(aang:Right() * moffset.y)
|
||||
apos:Add(aang:Up() * moffset.z)
|
||||
|
||||
model:SetPos(apos)
|
||||
model:SetAngles(aang)
|
||||
model:SetRenderOrigin(apos)
|
||||
model:SetRenderAngles(aang)
|
||||
|
||||
if model.IsHolosight and !wm then
|
||||
cam.Start3D(EyePos(), EyeAngles(), self.ViewModelFOV, 0, 0, nil, nil, 1, 10000)
|
||||
render.DepthRange(0.0, 0.1)
|
||||
self:DoHolosight(model)
|
||||
cam.End3D()
|
||||
render.DepthRange(0.0, 0.1)
|
||||
end
|
||||
|
||||
if !model.NoDraw then
|
||||
model:DrawModel()
|
||||
end
|
||||
end
|
||||
|
||||
if !wm then
|
||||
self:DrawFlashlightsVM()
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:PreDrawViewModel()
|
||||
if self:GetValue("ScopeHideWeapon") and self:IsInScope() then
|
||||
render.SetBlend(0)
|
||||
end
|
||||
|
||||
-- Apparently setting this will fix the viewmodel position and angle going all over the place in benchgun.
|
||||
if TacRP.ConVars["dev_benchgun"]:GetBool() then
|
||||
if self.OriginalViewModelFOV == nil then
|
||||
self.OriginalViewModelFOV = self.ViewModelFOV
|
||||
end
|
||||
self.ViewModelFOV = self:GetOwner():GetFOV()
|
||||
elseif self.OriginalViewModelFOV then
|
||||
self.ViewModelFOV = self.OriginalViewModelFOV
|
||||
self.OriginalViewModelFOV = nil
|
||||
end
|
||||
-- self.ViewModelFOV = self:GetViewModelFOV()
|
||||
|
||||
render.DepthRange(0.0, 0.1)
|
||||
end
|
||||
|
||||
function SWEP:PostDrawViewModel()
|
||||
cam.IgnoreZ(false)
|
||||
|
||||
if self:GetValue("ScopeHideWeapon") and self:IsInScope() then
|
||||
render.SetBlend(1)
|
||||
end
|
||||
|
||||
cam.Start3D()
|
||||
cam.IgnoreZ(false)
|
||||
local newpcfs = {}
|
||||
|
||||
for _, pcf in ipairs(self.PCFs) do
|
||||
if IsValid(pcf) then
|
||||
pcf:Render()
|
||||
table.insert(newpcfs, pcf)
|
||||
end
|
||||
end
|
||||
|
||||
if !inrt then self.PCFs = newpcfs end
|
||||
|
||||
local newmzpcfs = {}
|
||||
|
||||
for _, pcf in ipairs(self.MuzzPCFs) do
|
||||
if IsValid(pcf) then
|
||||
pcf:Render()
|
||||
table.insert(newmzpcfs, pcf)
|
||||
end
|
||||
end
|
||||
|
||||
if !inrt then self.MuzzPCFs = newmzpcfs end
|
||||
cam.End3D()
|
||||
end
|
||||
|
||||
--[[
|
||||
SWEP.SmoothedViewModelFOV = nil
|
||||
function SWEP:GetViewModelFOV()
|
||||
local target = self.ViewModelFOV
|
||||
|
||||
if TacRP.ConVars["dev_benchgun"]:GetBool() then
|
||||
target = self:GetOwner():GetFOV()
|
||||
end
|
||||
|
||||
self.SmoothedViewModelFOV = self.SmoothedViewModelFOV or target
|
||||
local diff = math.abs(target - self.SmoothedViewModelFOV)
|
||||
self.SmoothedViewModelFOV = math.Approach(self.SmoothedViewModelFOV, target, diff * FrameTime() / 0.25)
|
||||
|
||||
return self.SmoothedViewModelFOV
|
||||
end
|
||||
]]
|
||||
@@ -0,0 +1,35 @@
|
||||
function SWEP:DrawWorldModel(flags)
|
||||
|
||||
if !self.CertainAboutAtts and !self.AskedAboutAtts then
|
||||
if !self:GetValue("PrimaryGrenade") and !self:GetValue("PrimaryMelee") then
|
||||
self:RequestWeapon()
|
||||
-- debugoverlay.Sphere(self:GetPos(), 16, 5, color_white, true)
|
||||
end
|
||||
self.AskedAboutAtts = true
|
||||
end
|
||||
|
||||
-- Ugly workaround: OBS_MODE_IN_EYE spectate seems to call DrawWorldModel but doesn't actually render it?
|
||||
if LocalPlayer():GetObserverTarget() != self:GetOwner() or LocalPlayer():GetObserverMode() != OBS_MODE_IN_EYE then
|
||||
self:DrawCustomModel(true)
|
||||
end
|
||||
|
||||
if self:GetValue("Laser") and self:GetTactical() then
|
||||
self:SetRenderBounds(Vector(-16, -16, -16), Vector(16, 16, 15000))
|
||||
else
|
||||
self:SetRenderBounds(Vector(-16, -16, -16), Vector(16, 16, 16))
|
||||
end
|
||||
|
||||
self:DrawModel()
|
||||
end
|
||||
|
||||
hook.Add("PostDrawTranslucentRenderables", "TacRP_TranslucentDraw", function()
|
||||
for _, ply in pairs(player.GetAll()) do
|
||||
local wep = ply:GetActiveWeapon()
|
||||
if ply != LocalPlayer() and IsValid(wep) and wep.ArcticTacRP then
|
||||
wep:DrawLasers(true)
|
||||
wep:DrawFlashlightsWM()
|
||||
wep:DrawFlashlightGlares()
|
||||
wep:DoScopeGlint()
|
||||
end
|
||||
end
|
||||
end)
|
||||
1452
garrysmod/addons/tacrp/lua/weapons/tacrp_base/sh_aggregate.lua
Normal file
1452
garrysmod/addons/tacrp/lua/weapons/tacrp_base/sh_aggregate.lua
Normal file
File diff suppressed because it is too large
Load Diff
88
garrysmod/addons/tacrp/lua/weapons/tacrp_base/sh_anim.lua
Normal file
88
garrysmod/addons/tacrp/lua/weapons/tacrp_base/sh_anim.lua
Normal file
@@ -0,0 +1,88 @@
|
||||
function SWEP:PlayAnimation(seq, mult, lock, doidle)
|
||||
mult = mult or 1
|
||||
lock = lock or false
|
||||
local anim = self:TranslateSequence(seq)
|
||||
doidle = doidle or false
|
||||
local reverse = false
|
||||
|
||||
if mult < 0 then
|
||||
reverse = true
|
||||
mult = -mult
|
||||
end
|
||||
|
||||
local vm = self:GetVM()
|
||||
|
||||
if !IsValid(vm) then return end
|
||||
|
||||
if isstring(anim) then
|
||||
seq = vm:LookupSequence(anim)
|
||||
end
|
||||
|
||||
if seq == -1 then return end
|
||||
|
||||
self.CurrentAnimation = anim
|
||||
self.CurrentSeqeunce = seq
|
||||
|
||||
local time = vm:SequenceDuration(seq)
|
||||
|
||||
time = time * mult
|
||||
|
||||
vm:SendViewModelMatchingSequence(seq)
|
||||
|
||||
if reverse then
|
||||
vm:SetCycle(1)
|
||||
vm:SetPlaybackRate(-1 / mult)
|
||||
else
|
||||
vm:SetCycle(0)
|
||||
vm:SetPlaybackRate(1 / mult)
|
||||
end
|
||||
|
||||
if lock then
|
||||
self:SetAnimLockTime(CurTime() + time)
|
||||
-- self:SetNextSecondaryFire(CurTime() + time)
|
||||
else
|
||||
self:SetAnimLockTime(0)
|
||||
-- self:SetNextSecondaryFire(0)
|
||||
end
|
||||
|
||||
if doidle and !self.NoIdle then
|
||||
self:SetNextIdle(CurTime() + time)
|
||||
else
|
||||
self:SetNextIdle(math.huge)
|
||||
end
|
||||
|
||||
self:SetLastProceduralFireTime(0)
|
||||
|
||||
return time
|
||||
end
|
||||
|
||||
function SWEP:IdleAtEndOfAnimation()
|
||||
local vm = self:GetVM()
|
||||
local cyc = vm:GetCycle()
|
||||
local duration = vm:SequenceDuration()
|
||||
local rate = vm:GetPlaybackRate()
|
||||
|
||||
local time = (1 - cyc) * (duration / rate)
|
||||
|
||||
self:SetNextIdle(CurTime() + time)
|
||||
end
|
||||
|
||||
function SWEP:Idle()
|
||||
if self:GetPrimedGrenade() then return end
|
||||
|
||||
if self:GetBlindFire() then
|
||||
if self:Clip1() == 0 then
|
||||
self:PlayAnimation("blind_dryfire", 0, false, false)
|
||||
else
|
||||
self:PlayAnimation("blind_idle")
|
||||
end
|
||||
else
|
||||
if self:Clip1() == 0 then
|
||||
self:PlayAnimation("dryfire", 0, false, false)
|
||||
else
|
||||
self:PlayAnimation("idle")
|
||||
end
|
||||
end
|
||||
|
||||
self:SetReady(true)
|
||||
end
|
||||
@@ -0,0 +1,20 @@
|
||||
function SWEP:TranslateSequence(seq)
|
||||
seq = self:RunHook("Hook_TranslateSequence", seq)
|
||||
|
||||
seq = self.AnimationTranslationTable[seq] or seq
|
||||
|
||||
if istable(seq) then
|
||||
seq["BaseClass"] = nil
|
||||
seq = seq[math.Round(util.SharedRandom("TacRP_animtr", 1, #seq))]
|
||||
end
|
||||
|
||||
return seq
|
||||
end
|
||||
|
||||
function SWEP:HasSequence(seq)
|
||||
seq = self:TranslateSequence(seq)
|
||||
local vm = self:GetVM()
|
||||
seq = vm:LookupSequence(seq)
|
||||
|
||||
return seq != -1
|
||||
end
|
||||
201
garrysmod/addons/tacrp/lua/weapons/tacrp_base/sh_attach.lua
Normal file
201
garrysmod/addons/tacrp/lua/weapons/tacrp_base/sh_attach.lua
Normal file
@@ -0,0 +1,201 @@
|
||||
function SWEP:Attach(slot, att, silent, suppress)
|
||||
local slottbl = self.Attachments[slot]
|
||||
if slottbl.Installed then return end
|
||||
|
||||
if !self:CanAttach(slot, att) then return end
|
||||
|
||||
local atttbl = TacRP.GetAttTable(att)
|
||||
|
||||
if !atttbl then return end
|
||||
if TacRP:PlayerGetAtts(self:GetOwner(), att) <= 0 then return end
|
||||
|
||||
local inf_old = self:GetInfiniteAmmo()
|
||||
local ammo_old = self:GetAmmoType()
|
||||
|
||||
slottbl.Installed = att
|
||||
|
||||
TacRP:PlayerTakeAtt(self:GetOwner(), att, 1)
|
||||
|
||||
if atttbl.OnAttach then
|
||||
atttbl.OnAttach(self)
|
||||
end
|
||||
|
||||
if CLIENT then
|
||||
local attid = atttbl.ID
|
||||
|
||||
net.Start("TacRP_attach")
|
||||
net.WriteEntity(self)
|
||||
net.WriteBool(true)
|
||||
net.WriteUInt(slot, 8)
|
||||
net.WriteUInt(attid, TacRP.Attachments_Bits)
|
||||
net.SendToServer()
|
||||
|
||||
if game.SinglePlayer() then -- Due to bodygroups also being networked by engine, this will cause bodygroup "flickering"
|
||||
self:SetupModel(true)
|
||||
self:SetupModel(false)
|
||||
end
|
||||
|
||||
if !silent then
|
||||
surface.PlaySound(slottbl.AttachSound or "")
|
||||
end
|
||||
elseif SERVER and !suppress then
|
||||
self:NetworkWeapon()
|
||||
TacRP:PlayerSendAttInv(self:GetOwner())
|
||||
end
|
||||
|
||||
self:SetBurstCount(0)
|
||||
|
||||
self:InvalidateCache()
|
||||
|
||||
self:SetBaseSettings()
|
||||
|
||||
if atttbl.CanToggle then
|
||||
self:SetTactical(true)
|
||||
end
|
||||
|
||||
if self:GetFiremode() > self:GetFiremodeAmount() then
|
||||
self:SetFiremode(1)
|
||||
end
|
||||
|
||||
local inf_new = self:GetInfiniteAmmo()
|
||||
local ammo_new = self:GetAmmoType()
|
||||
if SERVER then
|
||||
if inf_old and !inf_new then
|
||||
self:SetClip1(0)
|
||||
elseif (inf_new and !inf_old) or (ammo_old != ammo_new) then
|
||||
self:Unload(ammo_old)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:Detach(slot, silent, suppress)
|
||||
local slottbl = self.Attachments[slot]
|
||||
if !slottbl.Installed then return end
|
||||
|
||||
if !self:CanDetach(slot, slottbl.Installed) then return end
|
||||
|
||||
TacRP:PlayerGiveAtt(self:GetOwner(), slottbl.Installed, 1)
|
||||
|
||||
local inf_old = self:GetInfiniteAmmo()
|
||||
local ammo_old = self:GetAmmoType()
|
||||
|
||||
local atttbl = TacRP.GetAttTable(slottbl.Installed)
|
||||
|
||||
if atttbl and atttbl.OnDetach then
|
||||
atttbl.OnDetach(self)
|
||||
end
|
||||
|
||||
slottbl.Installed = nil
|
||||
|
||||
if CLIENT then
|
||||
net.Start("TacRP_attach")
|
||||
net.WriteEntity(self)
|
||||
net.WriteBool(false)
|
||||
net.WriteUInt(slot, 8)
|
||||
net.SendToServer()
|
||||
|
||||
if game.SinglePlayer() then -- Due to bodygroups also being networked by engine, this will cause bodygroup "flickering"
|
||||
self:SetupModel(true)
|
||||
self:SetupModel(false)
|
||||
end
|
||||
|
||||
if !silent then
|
||||
surface.PlaySound(slottbl.DetachSound or "")
|
||||
end
|
||||
elseif SERVER and !suppress then
|
||||
self:NetworkWeapon()
|
||||
TacRP:PlayerSendAttInv(self:GetOwner())
|
||||
end
|
||||
|
||||
self:SetBurstCount(0)
|
||||
|
||||
self:InvalidateCache()
|
||||
|
||||
self:SetBaseSettings()
|
||||
|
||||
if self:GetFiremode() > self:GetFiremodeAmount() then
|
||||
self:SetFiremode(1)
|
||||
end
|
||||
|
||||
local nade = self:GetGrenade()
|
||||
if (nade.AdminOnly and self:GetOwner():GetAmmoCount(nade.Ammo) <= 0) or (nade.RequireStat and !self:GetValue(nade.RequireStat)) then
|
||||
self:SelectGrenade()
|
||||
end
|
||||
|
||||
local inf_new = self:GetInfiniteAmmo()
|
||||
local ammo_new = self:GetAmmoType()
|
||||
if SERVER then
|
||||
if inf_old and !inf_new then
|
||||
self:SetClip1(0)
|
||||
elseif (inf_new and !inf_old) or (ammo_old != ammo_new) then
|
||||
self:Unload(ammo_old)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:ToggleCustomize(on)
|
||||
if on == self:GetCustomize() or (on and self:GetValue("RunawayBurst") and self:GetBurstCount() > 0) then return end
|
||||
|
||||
self:ScopeToggle(0)
|
||||
self:ToggleBlindFire(TacRP.BLINDFIRE_NONE)
|
||||
|
||||
self:SetCustomize(on)
|
||||
|
||||
self:SetShouldHoldType()
|
||||
end
|
||||
|
||||
function SWEP:CanAttach(slot, att)
|
||||
local atttbl = TacRP.GetAttTable(att)
|
||||
|
||||
local slottbl = self.Attachments[slot]
|
||||
|
||||
if atttbl.Compatibility then
|
||||
local result = atttbl.Compatibility(self)
|
||||
|
||||
if result == true then
|
||||
return true
|
||||
elseif result == false then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
local cat = slottbl.Category
|
||||
|
||||
if !istable(cat) then
|
||||
cat = {cat}
|
||||
end
|
||||
|
||||
local attcat = atttbl.Category
|
||||
|
||||
if !istable(attcat) then
|
||||
attcat = {attcat}
|
||||
end
|
||||
|
||||
if !TacRP.CanCustomize(self:GetOwner(), self, att, slot) then return false end
|
||||
|
||||
for _, c in pairs(attcat) do
|
||||
if table.HasValue(cat, c) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
function SWEP:CanDetach(slot, att)
|
||||
local slottbl = self.Attachments[slot]
|
||||
|
||||
if slottbl.Integral then return false end
|
||||
|
||||
if !TacRP.CanCustomize(self:GetOwner(), self, att, slot, true) then return false end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function SWEP:ToggleTactical()
|
||||
local ret = self:RunHook("Hook_ToggleTactical")
|
||||
if !ret then
|
||||
self:EmitSound(self:GetValue("Sound_ToggleTactical"))
|
||||
self:SetTactical(!self:GetTactical())
|
||||
end
|
||||
end
|
||||
95
garrysmod/addons/tacrp/lua/weapons/tacrp_base/sh_autoaim.lua
Normal file
95
garrysmod/addons/tacrp/lua/weapons/tacrp_base/sh_autoaim.lua
Normal file
@@ -0,0 +1,95 @@
|
||||
SWEP.LockOnEntity = NULL
|
||||
|
||||
SWEP.LastSearchTime = 0
|
||||
|
||||
SWEP.PlayedLockOnSound = false
|
||||
|
||||
function SWEP:ThinkLockOn()
|
||||
local owner = self:GetOwner()
|
||||
local lastlockonentity = self:GetLockOnEntity()
|
||||
|
||||
if CLIENT then
|
||||
if !IsValid(self:GetLockOnEntity()) then
|
||||
self.PlayedLockOnSound = false
|
||||
end
|
||||
end
|
||||
|
||||
if not ((self:GetSightAmount() >= 1 and self:GetValue("LockOnInSights")) or (self:GetSightAmount() < 1 and self:GetValue("LockOnOutOfSights"))) then
|
||||
self:SetLockOnEntity(nil)
|
||||
self:SetLockOnStartTime(CurTime())
|
||||
else
|
||||
local lockontarget = nil
|
||||
|
||||
if lastlockonentity and IsValid(lastlockonentity) then
|
||||
// check if it remains within seeker cage
|
||||
|
||||
local player_aim_vector = owner:GetAimVector()
|
||||
local target_angle = math.deg(math.acos(player_aim_vector:Dot((lastlockonentity:WorldSpaceCenter() - owner:GetShootPos()):GetNormalized())))
|
||||
|
||||
local dist = (lastlockonentity:WorldSpaceCenter() - owner:GetShootPos()):Length()
|
||||
|
||||
if target_angle < self:GetValue("LockOnTrackAngle") and dist < self:GetValue("LockOnRange") then
|
||||
lockontarget = lastlockonentity
|
||||
end
|
||||
else
|
||||
local tr = util.TraceLine(
|
||||
{
|
||||
start = owner:GetShootPos(),
|
||||
endpos = owner:GetShootPos() + owner:GetAimVector() * self:GetValue("LockOnRange"),
|
||||
ignoreworld = true,
|
||||
filter = function(target)
|
||||
if target == owner then return false end
|
||||
if target:IsPlayer() then return true end
|
||||
if (target:IsNPC() or target:IsNextBot()) then return true end
|
||||
if (target.LVS and target:GetHP() > 0) or target.Targetable then return true end
|
||||
if target.IsGlideVehicle then return true end
|
||||
if TacRP.LockableEntities[target:GetClass()] then return true end
|
||||
if TacRP.FlareEntities[target:GetClass()] or target.IsTacRPFlare then return true end
|
||||
end
|
||||
}
|
||||
)
|
||||
|
||||
lockontarget = tr.Entity
|
||||
end
|
||||
|
||||
if lockontarget and IsValid(lockontarget) then
|
||||
local occlusion_tr = util.TraceLine({
|
||||
start = owner:GetShootPos(),
|
||||
endpos = lockontarget:WorldSpaceCenter(),
|
||||
mask = MASK_SHOT,
|
||||
filter = function(ent)
|
||||
if ent == lockontarget or ent == owner or ent:GetOwner() == lockontarget then return false end
|
||||
if ent:IsVehicle() and ent:GetDriver() == owner then return false end
|
||||
if ent:GetClass() == "lvs_wheeldrive_wheel" or scripted_ents.IsBasedOn(ent:GetClass(), "tacrp_proj_base") then return false end
|
||||
return true
|
||||
end
|
||||
})
|
||||
if occlusion_tr.Hit then lockontarget = nil end
|
||||
end
|
||||
|
||||
if lockontarget and IsValid(lockontarget) then
|
||||
if lastlockonentity != lockontarget then
|
||||
self:SetLockOnStartTime(CurTime())
|
||||
if CLIENT and (IsFirstTimePredicted() or game.SinglePlayer()) then
|
||||
self:EmitSound(self:GetValue("Sound_StartLockOn"))
|
||||
end
|
||||
elseif not self.PlayedLockOnSound and CurTime() > self:GetLockOnStartTime() + self:GetValue("LockOnTime") then
|
||||
if CLIENT and (IsFirstTimePredicted() or game.SinglePlayer()) then
|
||||
self:EmitSound(self:GetValue("Sound_FinishLockOn"))
|
||||
self.PlayedLockOnSound = true
|
||||
end
|
||||
end
|
||||
self:SetLockOnEntity(lockontarget)
|
||||
self.LockOnEntity = lockontarget
|
||||
else
|
||||
self:SetLockOnEntity(nil)
|
||||
self.PlayedLockOnSound = false
|
||||
self:SetLockOnStartTime(CurTime())
|
||||
end
|
||||
end
|
||||
|
||||
if not IsValid(self:GetLockOnEntity()) then
|
||||
self:SetLockOnEntity(nil)
|
||||
self:SetLockOnStartTime(CurTime())
|
||||
end
|
||||
end
|
||||
132
garrysmod/addons/tacrp/lua/weapons/tacrp_base/sh_bipod.lua
Normal file
132
garrysmod/addons/tacrp/lua/weapons/tacrp_base/sh_bipod.lua
Normal file
@@ -0,0 +1,132 @@
|
||||
function SWEP:InBipod()
|
||||
local bip = self:GetInBipod()
|
||||
|
||||
-- if !self:CanBipod() then
|
||||
-- self:ExitBipod()
|
||||
-- end
|
||||
|
||||
if IsValid(self:GetOwner()) and (self:GetOwner():GetVelocity():LengthSqr() >= 100 or self:GetSprintAmount() > 0) then
|
||||
self:ExitBipod()
|
||||
end
|
||||
|
||||
return bip
|
||||
end
|
||||
|
||||
SWEP.CachedCanBipod = true
|
||||
SWEP.CachedCanBipodTime = 0
|
||||
SWEP.LastBipodTime = 0
|
||||
local dist = 24
|
||||
function SWEP:CanBipod()
|
||||
if !self:GetValue("Bipod") then return false end
|
||||
|
||||
if self:GetOwner():InVehicle() then return false end
|
||||
if self:GetSprintAmount() > 0 and !self:DoForceSightsBehavior() then return false end
|
||||
|
||||
if self.CachedCanBipodTime >= CurTime() then return tobool(self.CachedCanBipod), self.CachedCanBipod end
|
||||
|
||||
local pos = self:GetOwner():EyePos()
|
||||
local angle = self:GetOwner():EyeAngles()
|
||||
if math.abs(angle.p) <= 45 then
|
||||
angle.p = 0
|
||||
end
|
||||
if self:GetOwner():GetVelocity():Length() > 0 then
|
||||
return false
|
||||
end
|
||||
|
||||
local rangemult = 2
|
||||
if self:IsProne() then
|
||||
rangemult = rangemult * 1.25
|
||||
end
|
||||
|
||||
local tr = util.TraceLine({
|
||||
start = pos,
|
||||
endpos = pos + (angle:Forward() * dist * rangemult),
|
||||
filter = self:GetOwner(),
|
||||
mask = MASK_PLAYERSOLID
|
||||
})
|
||||
|
||||
if tr.Hit then -- check for stuff in front of us
|
||||
return false
|
||||
end
|
||||
|
||||
local maxs = Vector(10, 10, 16)
|
||||
local mins = Vector(-10, -10, 0)
|
||||
|
||||
angle.p = angle.p + 45
|
||||
|
||||
tr = util.TraceHull({
|
||||
start = pos,
|
||||
endpos = pos + (angle:Forward() * dist * rangemult),
|
||||
filter = self:GetOwner(),
|
||||
maxs = maxs,
|
||||
mins = mins,
|
||||
mask = MASK_PLAYERSOLID
|
||||
})
|
||||
|
||||
self.CachedCanBipodTime = CurTime()
|
||||
|
||||
if tr.Hit then
|
||||
local tr2 = util.TraceHull({
|
||||
start = tr.HitPos,
|
||||
endpos = tr.HitPos + Vector(0, 0, -dist),
|
||||
filter = self:GetOwner(),
|
||||
maxs = maxs,
|
||||
mins = mins,
|
||||
mask = MASK_PLAYERSOLID
|
||||
})
|
||||
if tr2.Hit then
|
||||
self.CachedCanBipod = tr2
|
||||
return true, tr2
|
||||
end
|
||||
end
|
||||
|
||||
self.CachedCanBipod = false
|
||||
return false
|
||||
end
|
||||
|
||||
function SWEP:EnterBipod(sp)
|
||||
if !sp and self:GetInBipod() then return end
|
||||
local can, tr = self:CanBipod()
|
||||
if !sp and !can then return end
|
||||
|
||||
if SERVER and game.SinglePlayer() then self:CallOnClient("EnterBipod", "true") end
|
||||
self.LastBipodTime = CurTime()
|
||||
|
||||
local owner = self:GetOwner()
|
||||
|
||||
local bipodang = tr.HitNormal:Cross(owner:EyeAngles():Right()):Angle()
|
||||
-- bipodang.p = math.ApproachAngle(bipodang.p, owner:EyeAngles().p, 10)
|
||||
|
||||
debugoverlay.Axis(tr.HitPos, tr.HitNormal:Angle(), 16, 5, true)
|
||||
debugoverlay.Line(tr.HitPos, tr.HitPos + bipodang:Forward() * 32, 5, color_white, true)
|
||||
debugoverlay.Line(tr.HitPos, tr.HitPos + owner:EyeAngles():Forward() * 32, 5, Color(255, 255, 0), true)
|
||||
|
||||
self:SetBipodPos(owner:EyePos() + (owner:EyeAngles():Forward() * 4) - Vector(0, 0, 3))
|
||||
self:SetBipodAngle(bipodang)
|
||||
|
||||
if game.SinglePlayer() and CLIENT then return end
|
||||
|
||||
self:EmitSound(self.Sound_BipodDown, 70, 100, 1, CHAN_ITEM)
|
||||
self:SetInBipod(true)
|
||||
self:DoBodygroups()
|
||||
end
|
||||
|
||||
function SWEP:ExitBipod(sp)
|
||||
if !sp and !self:GetInBipod() then return end
|
||||
|
||||
if SERVER and game.SinglePlayer() then self:CallOnClient("ExitBipod", "true") end
|
||||
self.LastBipodTime = CurTime()
|
||||
if game.SinglePlayer() and CLIENT then return end
|
||||
|
||||
self:EmitSound(self.Sound_BipodUp, 70, 100, 1, CHAN_ITEM)
|
||||
self:SetInBipod(false)
|
||||
self:DoBodygroups()
|
||||
end
|
||||
|
||||
function SWEP:IsProne()
|
||||
if PRONE_INPRONE then
|
||||
return self:GetOwner().IsProne and self:GetOwner():IsProne()
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
285
garrysmod/addons/tacrp/lua/weapons/tacrp_base/sh_blindfire.lua
Normal file
285
garrysmod/addons/tacrp/lua/weapons/tacrp_base/sh_blindfire.lua
Normal file
@@ -0,0 +1,285 @@
|
||||
function SWEP:GetMuzzleOrigin()
|
||||
if !IsValid(self:GetOwner()) then
|
||||
return self:GetPos()
|
||||
end
|
||||
if self:GetOwner():IsNPC() then
|
||||
return SERVER and self:GetOwner():GetShootPos() or self:GetOwner():EyePos()
|
||||
end
|
||||
|
||||
local pos = self:GetOwner():EyePos()
|
||||
|
||||
if self:GetBlindFire() then
|
||||
local eyeang = self:GetOwner():EyeAngles()
|
||||
|
||||
local testpos = pos + eyeang:Up() * 24
|
||||
|
||||
if self:GetBlindFireLeft() or self:GetBlindFireRight() then
|
||||
testpos = pos + eyeang:Forward() * 24
|
||||
end
|
||||
|
||||
local tr = util.TraceLine({
|
||||
start = pos,
|
||||
endpos = testpos,
|
||||
filter = self:GetOwner()
|
||||
})
|
||||
|
||||
pos = tr.HitPos
|
||||
end
|
||||
|
||||
local offset = self:GetValue("ShootOffset")
|
||||
|
||||
if offset.x != 0 then
|
||||
pos = pos + self:GetOwner():GetRight() * offset.x
|
||||
end
|
||||
|
||||
if offset.y != 0 then
|
||||
pos = pos + self:GetOwner():GetForward() * offset.y
|
||||
end
|
||||
|
||||
if offset.z != 0 then
|
||||
pos = pos + self:GetOwner():GetUp() * offset.z
|
||||
end
|
||||
|
||||
return pos
|
||||
end
|
||||
|
||||
/*
|
||||
|
||||
ValveBiped.Bip01_Spine
|
||||
ValveBiped.Bip01_Spine1
|
||||
ValveBiped.Bip01_Spine2
|
||||
ValveBiped.Bip01_Spine4
|
||||
ValveBiped.Bip01_Neck1
|
||||
ValveBiped.Bip01_Head1
|
||||
ValveBiped.forward
|
||||
ValveBiped.Bip01_R_Clavicle
|
||||
ValveBiped.Bip01_R_UpperArm
|
||||
ValveBiped.Bip01_R_Forearm
|
||||
ValveBiped.Bip01_R_Hand
|
||||
ValveBiped.Anim_Attachment_RH
|
||||
ValveBiped.Bip01_L_Clavicle
|
||||
ValveBiped.Bip01_L_UpperArm
|
||||
ValveBiped.Bip01_L_Forearm
|
||||
ValveBiped.Bip01_L_Hand
|
||||
ValveBiped.Anim_Attachment_LH
|
||||
ValveBiped.Bip01_R_Thigh
|
||||
ValveBiped.Bip01_R_Calf
|
||||
ValveBiped.Bip01_R_Foot
|
||||
ValveBiped.Bip01_R_Toe0
|
||||
ValveBiped.Bip01_L_Thigh
|
||||
ValveBiped.Bip01_L_Calf
|
||||
ValveBiped.Bip01_L_Foot
|
||||
ValveBiped.Bip01_L_Toe0
|
||||
ValveBiped.Bip01_L_Finger4
|
||||
ValveBiped.Bip01_L_Finger41
|
||||
ValveBiped.Bip01_L_Finger42
|
||||
ValveBiped.Bip01_L_Finger3
|
||||
ValveBiped.Bip01_L_Finger31
|
||||
ValveBiped.Bip01_L_Finger32
|
||||
ValveBiped.Bip01_L_Finger2
|
||||
ValveBiped.Bip01_L_Finger21
|
||||
ValveBiped.Bip01_L_Finger22
|
||||
ValveBiped.Bip01_L_Finger1
|
||||
ValveBiped.Bip01_L_Finger11
|
||||
ValveBiped.Bip01_L_Finger12
|
||||
ValveBiped.Bip01_L_Finger0
|
||||
ValveBiped.Bip01_L_Finger01
|
||||
ValveBiped.Bip01_L_Finger02
|
||||
ValveBiped.Bip01_R_Finger4
|
||||
ValveBiped.Bip01_R_Finger41
|
||||
ValveBiped.Bip01_R_Finger42
|
||||
ValveBiped.Bip01_R_Finger3
|
||||
ValveBiped.Bip01_R_Finger31
|
||||
ValveBiped.Bip01_R_Finger32
|
||||
ValveBiped.Bip01_R_Finger2
|
||||
ValveBiped.Bip01_R_Finger21
|
||||
ValveBiped.Bip01_R_Finger22
|
||||
ValveBiped.Bip01_R_Finger1
|
||||
ValveBiped.Bip01_R_Finger11
|
||||
ValveBiped.Bip01_R_Finger12
|
||||
ValveBiped.Bip01_R_Finger0
|
||||
ValveBiped.Bip01_R_Finger01
|
||||
ValveBiped.Bip01_R_Finger02
|
||||
|
||||
*/
|
||||
|
||||
local bone_list = {
|
||||
"ValveBiped.Bip01_R_UpperArm",
|
||||
"ValveBiped.Bip01_R_Forearm",
|
||||
"ValveBiped.Bip01_R_Hand",
|
||||
"ValveBiped.Bip01_L_UpperArm",
|
||||
"ValveBiped.Bip01_L_Forearm",
|
||||
"ValveBiped.Bip01_L_Hand",
|
||||
}
|
||||
|
||||
local bone_mods = {
|
||||
-- ["ValveBiped.Bip01_R_UpperArm"] = Angle(0, -70, 0),
|
||||
-- ["ValveBiped.Bip01_R_Hand"] = Angle(-55, 45, -90),
|
||||
["ValveBiped.Bip01_R_UpperArm"] = Angle(45, -90, 0),
|
||||
["ValveBiped.Bip01_R_Hand"] = Angle(-90, 0, 0),
|
||||
}
|
||||
|
||||
local bone_mods_left = {
|
||||
-- ["ValveBiped.Bip01_R_UpperArm"] = Angle(0, -70, 0),
|
||||
-- ["ValveBiped.Bip01_R_Hand"] = Angle(-55, 45, -90),
|
||||
["ValveBiped.Bip01_R_UpperArm"] = Angle(45, 0, 0),
|
||||
["ValveBiped.Bip01_R_Forearm"] = Angle(0, 0, 0),
|
||||
["ValveBiped.Bip01_R_Hand"] = Angle(0, -75, 0),
|
||||
}
|
||||
|
||||
local bone_mods_right = {
|
||||
["ValveBiped.Bip01_R_UpperArm"] = Angle(-45, 0, 0),
|
||||
["ValveBiped.Bip01_R_Forearm"] = Angle(0, 0, 0),
|
||||
["ValveBiped.Bip01_R_Hand"] = Angle(35, 75, 0),
|
||||
}
|
||||
|
||||
local bone_mods_kys = {
|
||||
["ValveBiped.Bip01_R_UpperArm"] = Angle(5, 0, 0),
|
||||
["ValveBiped.Bip01_R_Forearm"] = Angle(0, -5, 0),
|
||||
["ValveBiped.Bip01_R_Hand"] = Angle(0, -165, 0),
|
||||
}
|
||||
local bone_mods_kys_pistol = {
|
||||
["ValveBiped.Bip01_R_UpperArm"] = Angle(55, 0, 0),
|
||||
["ValveBiped.Bip01_R_Forearm"] = Angle(0, -75, 5),
|
||||
["ValveBiped.Bip01_R_Hand"] = Angle(45, -75, 0),
|
||||
}
|
||||
local bone_mods_kys_dual = {
|
||||
["ValveBiped.Bip01_L_UpperArm"] = Angle(-60, 0, -45),
|
||||
["ValveBiped.Bip01_L_Forearm"] = Angle(0, -60, -30),
|
||||
["ValveBiped.Bip01_L_Hand"] = Angle(-30, -45, -90),
|
||||
["ValveBiped.Bip01_R_UpperArm"] = Angle(55, 0, 30),
|
||||
["ValveBiped.Bip01_R_Forearm"] = Angle(0, -60, 30),
|
||||
["ValveBiped.Bip01_R_Hand"] = Angle(45, -75, 90),
|
||||
}
|
||||
|
||||
local bone_mods_index = {
|
||||
[TacRP.BLINDFIRE_UP] = bone_mods,
|
||||
[TacRP.BLINDFIRE_LEFT] = bone_mods_left,
|
||||
[TacRP.BLINDFIRE_RIGHT] = bone_mods_right,
|
||||
[TacRP.BLINDFIRE_KYS] = bone_mods_kys,
|
||||
}
|
||||
|
||||
function SWEP:ToggleBoneMods(on)
|
||||
if !IsValid(self:GetOwner()) then return end
|
||||
if on == TacRP.BLINDFIRE_NONE or on == false or on == nil then
|
||||
for _, i in ipairs(bone_list) do
|
||||
local boneindex = self:GetOwner():LookupBone(i)
|
||||
if !boneindex then continue end
|
||||
|
||||
self:GetOwner():ManipulateBoneAngles(boneindex, Angle(0, 0, 0))
|
||||
-- self:GetOwner():ManipulateBonePosition(boneindex, Vector(0, 0, 0))
|
||||
end
|
||||
else
|
||||
local tbl = bone_mods_index[on]
|
||||
if on == TacRP.BLINDFIRE_KYS and self:GetValue("HoldTypeSuicide") == "duel" then
|
||||
tbl = bone_mods_kys_dual
|
||||
elseif on == TacRP.BLINDFIRE_KYS and self:GetValue("HoldType") == "revolver" then
|
||||
tbl = bone_mods_kys_pistol
|
||||
end
|
||||
|
||||
for i, k in pairs(tbl) do
|
||||
local boneindex = self:GetOwner():LookupBone(i)
|
||||
if !boneindex then continue end
|
||||
|
||||
self:GetOwner():ManipulateBoneAngles(boneindex, k)
|
||||
end
|
||||
|
||||
-- for i, k in pairs(tbl[2]) do
|
||||
-- local boneindex = self:GetOwner():LookupBone(i)
|
||||
-- if !boneindex then continue end
|
||||
|
||||
-- self:GetOwner():ManipulateBonePosition(boneindex, k)
|
||||
-- end
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:GetBlindFireMode()
|
||||
if !self:GetBlindFire() then
|
||||
return TacRP.BLINDFIRE_NONE
|
||||
elseif self:GetBlindFireLeft() and self:GetBlindFireRight() then
|
||||
return TacRP.BLINDFIRE_KYS
|
||||
elseif self:GetBlindFireLeft() then
|
||||
return TacRP.BLINDFIRE_LEFT
|
||||
elseif self:GetBlindFireRight() then
|
||||
return TacRP.BLINDFIRE_RIGHT
|
||||
else
|
||||
return TacRP.BLINDFIRE_UP
|
||||
end
|
||||
end
|
||||
|
||||
local bfmode = {
|
||||
[TacRP.BLINDFIRE_NONE] = {false, false, false},
|
||||
[TacRP.BLINDFIRE_UP] = {true, false, false},
|
||||
[TacRP.BLINDFIRE_LEFT] = {true, true, false},
|
||||
[TacRP.BLINDFIRE_RIGHT] = {true, false, true},
|
||||
[TacRP.BLINDFIRE_KYS] = {true, true, true},
|
||||
}
|
||||
function SWEP:SetBlindFireMode(mode)
|
||||
if !bfmode[mode] then
|
||||
print("[TacRP] WARNING! Trying to set invalid blindfire mode: " .. tostring(mode))
|
||||
mode = 0
|
||||
end
|
||||
self:SetBlindFire(bfmode[mode][1])
|
||||
self:SetBlindFireLeft(bfmode[mode][2])
|
||||
self:SetBlindFireRight(bfmode[mode][3])
|
||||
end
|
||||
|
||||
function SWEP:CheckBlindFire(suicide)
|
||||
if !self:GetValue("CanBlindFire") and (!suicide or !self:GetValue("CanSuicide")) then return false end
|
||||
if (self:GetIsSprinting()
|
||||
or self:GetAnimLockTime() > CurTime()
|
||||
or self:GetPrimedGrenade()
|
||||
or self:IsInScope()
|
||||
or self:GetSafe()) then
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function SWEP:ToggleBlindFire(bf)
|
||||
local kms = bf == TacRP.BLINDFIRE_KYS or bf == TacRP.BLINDFIRE_NONE
|
||||
if bf != TacRP.BLINDFIRE_NONE and (!self:CheckBlindFire(kms) or bf == self:GetBlindFireMode()) then return end
|
||||
|
||||
local diff = bf != self:GetBlindFireMode()
|
||||
|
||||
if diff then
|
||||
self:ToggleCustomize(false)
|
||||
self:ScopeToggle(0)
|
||||
self:SetBlindFireFinishTime(CurTime() + (bf == TacRP.BLINDFIRE_KYS and 1 or 0.3))
|
||||
end
|
||||
|
||||
self:SetBlindFireMode(bf)
|
||||
self:ToggleBoneMods(bf)
|
||||
self:SetShouldHoldType()
|
||||
|
||||
if bf == TacRP.BLINDFIRE_KYS and TacRP.ShouldWeFunny() then
|
||||
self:GetOwner():EmitSound("tacrp/low-tier-god.mp3", 80, 100)
|
||||
end
|
||||
|
||||
if diff then
|
||||
if self:StillWaiting(true) then
|
||||
self:IdleAtEndOfAnimation()
|
||||
else
|
||||
self:Idle()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:ThinkBlindFire()
|
||||
if (self:GetOwner():KeyDown(IN_ZOOM) or self:GetOwner().TacRPBlindFireDown) and !tobool(self:GetOwner():GetInfo("tacrp_blindfiremenu")) then
|
||||
if CLIENT then self.LastHintLife = CurTime() end
|
||||
if self:GetOwner():KeyDown(IN_FORWARD) then
|
||||
self:ToggleBlindFire(TacRP.BLINDFIRE_UP)
|
||||
elseif self:GetOwner():KeyDown(IN_MOVELEFT) and !self:GetOwner():KeyDown(IN_MOVERIGHT) then
|
||||
self:ToggleBlindFire(TacRP.BLINDFIRE_LEFT)
|
||||
elseif self:GetOwner():KeyDown(IN_MOVERIGHT) and !self:GetOwner():KeyDown(IN_MOVELEFT) then
|
||||
self:ToggleBlindFire(TacRP.BLINDFIRE_RIGHT)
|
||||
elseif self:GetOwner():KeyDown(IN_SPEED) and self:GetOwner():KeyDown(IN_WALK) and !tobool(self:GetOwner():GetInfo("tacrp_idunwannadie")) then
|
||||
self:ToggleBlindFire(TacRP.BLINDFIRE_KYS)
|
||||
elseif self:GetOwner():KeyDown(IN_BACK) then
|
||||
self:ToggleBlindFire(TacRP.BLINDFIRE_NONE)
|
||||
end
|
||||
elseif (self:GetOwner():KeyDown(IN_ZOOM) or self:GetOwner().TacRPBlindFireDown) and self:GetOwner():GetInfo("tacrp_blindfiremenu") and self:GetOwner():GetCanZoom() then
|
||||
self:GetOwner():SetCanZoom(false)
|
||||
end
|
||||
end
|
||||
113
garrysmod/addons/tacrp/lua/weapons/tacrp_base/sh_bodygroups.lua
Normal file
113
garrysmod/addons/tacrp/lua/weapons/tacrp_base/sh_bodygroups.lua
Normal file
@@ -0,0 +1,113 @@
|
||||
function SWEP:DoBodygroups(wm, custom_wm)
|
||||
if wm == nil then
|
||||
wm = false
|
||||
self:DoBodygroups(true)
|
||||
end
|
||||
if !wm and (!IsValid(self:GetOwner()) or self:GetOwner():IsNPC()) then return end
|
||||
|
||||
local dbg = self:GetValue("DefaultBodygroups")
|
||||
|
||||
local mdl
|
||||
|
||||
if wm then
|
||||
mdl = custom_wm or self
|
||||
dbg = self:GetValue("DefaultWMBodygroups")
|
||||
else
|
||||
mdl = self:GetVM()
|
||||
end
|
||||
|
||||
if !IsValid(mdl) then return end
|
||||
|
||||
mdl:SetBodyGroups(dbg or "")
|
||||
|
||||
local sk = self:GetValue("DefaultSkin")
|
||||
|
||||
local eles = self:GetElements()
|
||||
|
||||
for i, k in ipairs(eles) do
|
||||
if wm then
|
||||
for _, j in pairs(k.BGs_WM or {}) do
|
||||
if _ == "BaseClass" then continue end
|
||||
mdl:SetBodygroup(j[1], j[2] or 0)
|
||||
end
|
||||
if k.Skin_WM != nil then sk = k.Skin_WM end
|
||||
else
|
||||
for _, j in pairs(k.BGs_VM or {}) do
|
||||
if _ == "BaseClass" then continue end
|
||||
mdl:SetBodygroup(j[1] or 0, j[2] or 0)
|
||||
end
|
||||
if k.Skin_VM != nil then sk = k.Skin_VM end
|
||||
end
|
||||
end
|
||||
|
||||
mdl:SetSkin(sk)
|
||||
|
||||
local bbg = self:GetValue("BulletBodygroups")
|
||||
|
||||
if bbg then
|
||||
local amt = self:Clip1()
|
||||
|
||||
if self:GetReloading() then
|
||||
amt = self:GetLoadedRounds()
|
||||
end
|
||||
|
||||
for c, bgs in pairs(bbg) do
|
||||
if amt < c then
|
||||
mdl:SetBodygroup(bgs[1], bgs[2])
|
||||
if !self.BulletBodygroupsSetAll then
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
self:RunHook("Hook_PostDoBodygroups")
|
||||
end
|
||||
|
||||
function SWEP:GetElements(holster)
|
||||
if !self.AttachmentElements then return {} end
|
||||
local eles = {}
|
||||
|
||||
for i, k in pairs(self.Attachments) do
|
||||
if k.Installed then
|
||||
table.Add(eles, k.InstalledElements or {})
|
||||
|
||||
local atttbl = TacRP.GetAttTable(k.Installed)
|
||||
|
||||
table.Add(eles, atttbl.InstalledElements or {})
|
||||
else
|
||||
table.Add(eles, k.UnInstalledElements or {})
|
||||
end
|
||||
end
|
||||
local eleatts = {}
|
||||
|
||||
local foldstock = false
|
||||
for i, k in pairs(eles) do
|
||||
if self.AttachmentElements[k] then
|
||||
table.insert(eleatts, self.AttachmentElements[k])
|
||||
foldstock = foldstock or k == "foldstock"
|
||||
end
|
||||
end
|
||||
|
||||
-- Bipod bodygroup
|
||||
if self:GetInBipod() and self.AttachmentElements["bipod"] then
|
||||
table.insert(eleatts, self.AttachmentElements["bipod"])
|
||||
end
|
||||
|
||||
-- Hack: Always fold stock when weapon is holstered
|
||||
if IsValid(self:GetOwner()) and self:GetOwner():IsPlayer()
|
||||
and self:GetOwner():GetActiveWeapon() != self and !foldstock
|
||||
and self.AttachmentElements["foldstock"] then
|
||||
table.insert(eleatts, self.AttachmentElements["foldstock"])
|
||||
end
|
||||
|
||||
table.sort(eleatts, function(a, b)
|
||||
return (a.SortOrder or 1) < (b.SortOrder or 1)
|
||||
end)
|
||||
|
||||
return eleatts
|
||||
end
|
||||
|
||||
function SWEP:DoBulletBodygroups()
|
||||
self:DoBodygroups(false)
|
||||
end
|
||||
107
garrysmod/addons/tacrp/lua/weapons/tacrp_base/sh_effects.lua
Normal file
107
garrysmod/addons/tacrp/lua/weapons/tacrp_base/sh_effects.lua
Normal file
@@ -0,0 +1,107 @@
|
||||
function SWEP:DoEffects(alt)
|
||||
if !IsFirstTimePredicted() then return end
|
||||
local muzz_qca, muzz_qca_wm = self:GetQCAMuzzle(alt)
|
||||
|
||||
local data = EffectData()
|
||||
data:SetEntity(self)
|
||||
data:SetAttachment(muzz_qca)
|
||||
data:SetHitBox(muzz_qca_wm or muzz_qca) // unused field (integer between 0-2047)
|
||||
|
||||
util.Effect( "TacRP_muzzleeffect", data )
|
||||
end
|
||||
|
||||
function SWEP:GetQCAMuzzle(alt)
|
||||
if self:GetValue("EffectsAlternate") then
|
||||
if self:GetNthShot() % 2 == (alt and 1 or 0) then
|
||||
return self:GetValue("QCA_MuzzleR"), self:GetValue("WM_QCA_MuzzleR")
|
||||
else
|
||||
return self:GetValue("QCA_MuzzleL"), self:GetValue("WM_QCA_MuzzleL")
|
||||
end
|
||||
else
|
||||
return self:GetValue("QCA_Muzzle"), self:GetValue("WM_QCA_Muzzle")
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:GetQCAEject(alt)
|
||||
if self:GetValue("EffectsAlternate") then
|
||||
if self:GetNthShot() % 2 == (alt and 1 or 0) then
|
||||
return self:GetValue("QCA_EjectR"), self:GetValue("WM_QCA_EjectR")
|
||||
else
|
||||
return self:GetValue("QCA_EjectL"), self:GetValue("WM_QCA_EjectL")
|
||||
end
|
||||
else
|
||||
return self:GetValue("QCA_Eject"), self:GetValue("WM_QCA_Eject")
|
||||
end
|
||||
end
|
||||
|
||||
SWEP.EjectedShells = {}
|
||||
|
||||
function SWEP:DoEject(alt)
|
||||
if !IsFirstTimePredicted() then return end
|
||||
if self:GetValue("EjectEffect") == 0 then return end
|
||||
|
||||
local eject_qca, eject_qca_wm = self:GetQCAEject(alt)
|
||||
|
||||
local data = EffectData()
|
||||
data:SetEntity(self)
|
||||
data:SetFlags(self:GetValue("EjectEffect"))
|
||||
data:SetAttachment(eject_qca)
|
||||
data:SetHitBox(eject_qca_wm or eject_qca) // unused field (integer between 0-2047)
|
||||
data:SetScale(self:GetValue("EjectScale"))
|
||||
|
||||
util.Effect( "TacRP_shelleffect", data )
|
||||
end
|
||||
|
||||
function SWEP:GetTracerOrigin()
|
||||
local ow = self:GetOwner()
|
||||
local wm = !IsValid(ow) or !ow:IsPlayer() or !ow:GetViewModel():IsValid() or (ow != LocalPlayer() and ow != LocalPlayer():GetObserverTarget()) or (ow == LocalPlayer() and ow:ShouldDrawLocalPlayer())
|
||||
local att = self:GetQCAMuzzle()
|
||||
local muzz = self
|
||||
|
||||
if !wm then
|
||||
muzz = ow:GetViewModel()
|
||||
end
|
||||
|
||||
if muzz and muzz:IsValid() then
|
||||
local posang = muzz:GetAttachment(att)
|
||||
if !posang then return muzz:GetPos() end
|
||||
local pos = posang.Pos
|
||||
|
||||
return pos
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:GetMuzzleDevice(wm)
|
||||
if !wm and self:GetOwner():IsNPC() then return end
|
||||
|
||||
local model = self.WModel
|
||||
local muzz = self
|
||||
|
||||
if !wm then
|
||||
model = self.VModel
|
||||
muzz = self:GetVM()
|
||||
end
|
||||
|
||||
if model then
|
||||
for i, k in pairs(model) do
|
||||
if k.IsMuzzleDevice then
|
||||
return k
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return muzz
|
||||
end
|
||||
|
||||
function SWEP:DrawEjectedShells()
|
||||
local newshells = {}
|
||||
|
||||
for i, k in pairs(self.EjectedShells) do
|
||||
if !k:IsValid() then continue end
|
||||
|
||||
k:DrawModel()
|
||||
table.insert(newshells, k)
|
||||
end
|
||||
|
||||
self.EjectedShells = newshells
|
||||
end
|
||||
116
garrysmod/addons/tacrp/lua/weapons/tacrp_base/sh_firemodes.lua
Normal file
116
garrysmod/addons/tacrp/lua/weapons/tacrp_base/sh_firemodes.lua
Normal file
@@ -0,0 +1,116 @@
|
||||
function SWEP:SwitchFiremode()
|
||||
local fm = self:GetFiremode()
|
||||
|
||||
fm = fm + 1
|
||||
|
||||
if fm > #self:GetValue("Firemodes") then
|
||||
fm = 1
|
||||
end
|
||||
|
||||
-- Since pattern is firemode dependent, it has to be reset when firemode switchs
|
||||
self.RecoilPatternCache = {}
|
||||
|
||||
self:SetFiremode(fm)
|
||||
end
|
||||
|
||||
function SWEP:GetFiremodeAmount()
|
||||
if istable(self:GetValue("Firemodes")) then
|
||||
return #self:GetValue("Firemodes")
|
||||
elseif self:GetValue("Firemode") == 0 then
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:GetNextFiremode()
|
||||
if self:GetFiremodeAmount() == 0 then return 1 end
|
||||
if self:GetValue("Firemodes") then
|
||||
local fm = self:GetFiremode()
|
||||
|
||||
fm = fm + 1
|
||||
|
||||
if fm > #self:GetValue("Firemodes") then
|
||||
fm = 1
|
||||
end
|
||||
|
||||
return self:GetValue("Firemodes")[fm]
|
||||
else
|
||||
return self:GetValue("Firemode")
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:GetCurrentFiremode()
|
||||
if self:GetValue("Firemodes") then
|
||||
return self:GetValue("Firemodes")[self:GetFiremode()] or self:GetValue("Firemode") or 0
|
||||
else
|
||||
return self:GetValue("Firemode")
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:ToggleSafety(onoff)
|
||||
if self:GetValue("Firemode") == 0 then return end
|
||||
onoff = onoff or !self:GetSafe()
|
||||
|
||||
if onoff and self:DoForceSightsBehavior() then return end
|
||||
if DarkRP and self:GetNWBool("TacRP_PoliceBiocode") and !self:GetOwner():isCP() then onoff = true end
|
||||
|
||||
self:SetSafe(onoff)
|
||||
|
||||
if onoff == true then
|
||||
self:ToggleBlindFire(TacRP.BLINDFIRE_NONE)
|
||||
self:ScopeToggle(0)
|
||||
end
|
||||
|
||||
if CLIENT then self.LastHintLife = CurTime() end
|
||||
end
|
||||
|
||||
local mat_1 = Material("tacrp/hud/firemode_1.png", "mips smooth")
|
||||
local mat_2 = Material("tacrp/hud/firemode_2.png", "mips smooth")
|
||||
local mat_3 = Material("tacrp/hud/firemode_3.png", "mips smooth")
|
||||
local mat_4 = Material("tacrp/hud/firemode_4.png", "mips smooth")
|
||||
local mat_a = Material("tacrp/hud/firemode_a.png", "mips smooth")
|
||||
local mat_s = Material("tacrp/hud/firemode_s.png", "mips smooth")
|
||||
|
||||
function SWEP:GetFiremodeMat(mode)
|
||||
if mode == 0 then
|
||||
return mat_s
|
||||
elseif mode == 1 then
|
||||
return mat_1
|
||||
elseif mode == 2 then
|
||||
return mat_a
|
||||
elseif mode == -2 then
|
||||
return mat_2
|
||||
elseif mode == -3 then
|
||||
return mat_3
|
||||
elseif mode == -4 then
|
||||
return mat_4
|
||||
else
|
||||
return mat_1 // epic fail
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:HasFiremode(mode, base)
|
||||
local valfunc = base and self.GetBaseValue or self.GetValue
|
||||
|
||||
if valfunc(self, "Firemodes") then
|
||||
for _, v in pairs(valfunc(self, "Firemodes")) do
|
||||
if v == mode or (mode < 0 and v < 0) then return true end
|
||||
end
|
||||
return false
|
||||
else
|
||||
return valfunc(self, "Firemode") == mode or (mode < 0 and valfunc(self, "Firemode") < 0)
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:GetFiremodeBurstLength(base)
|
||||
local valfunc = base and self.GetBaseValue or self.GetValue
|
||||
if valfunc(self, "Firemodes") then
|
||||
for _, v in pairs(valfunc(self, "Firemodes")) do
|
||||
if v < 0 then return -v end
|
||||
end
|
||||
return false
|
||||
else
|
||||
return valfunc(self, "Firemode") < 0 and -valfunc(self, "Firemode") or false
|
||||
end
|
||||
end
|
||||
80
garrysmod/addons/tacrp/lua/weapons/tacrp_base/sh_freeaim.lua
Normal file
80
garrysmod/addons/tacrp/lua/weapons/tacrp_base/sh_freeaim.lua
Normal file
@@ -0,0 +1,80 @@
|
||||
SWEP.ClientFreeAimAng = Angle(0, 0, 0)
|
||||
SWEP.ClientLastAimAngle = Angle(0, 0, 0)
|
||||
|
||||
function SWEP:ThinkFreeAim()
|
||||
if !TacRP.ConVars["freeaim"]:GetBool() or self:GetOwner():IsBot() then return Angle(0, 0, 0) end
|
||||
|
||||
local eyeangles = self:GetOwner():EyeAngles()
|
||||
|
||||
if self:GetValue("FreeAim") then
|
||||
local diff = eyeangles
|
||||
if SERVER then
|
||||
diff = diff - self:GetLastAimAngle()
|
||||
else
|
||||
diff = diff - self.ClientLastAimAngle
|
||||
end
|
||||
diff = LerpAngle(0.9, diff, angle_zero)
|
||||
|
||||
local freeaimang = Angle(self:GetFreeAimAngle())
|
||||
|
||||
local max = self:GetValue("FreeAimMaxAngle")
|
||||
|
||||
local sightdelta = self:Curve(self:GetSightDelta())
|
||||
|
||||
max = max * Lerp(sightdelta, 1, 0)
|
||||
|
||||
if self:GetBlindFireMode() > 0 then
|
||||
max = max * 0.25
|
||||
end
|
||||
|
||||
if self:GetValue("Bipod") then
|
||||
local f = self:Curve(math.Clamp((CurTime() - self.LastBipodTime) / 0.25, 0, 1))
|
||||
if self:GetInBipod() then
|
||||
max = Lerp(f, max, 0)
|
||||
else
|
||||
max = Lerp(f, 0, max)
|
||||
end
|
||||
end
|
||||
|
||||
diff.p = math.NormalizeAngle(diff.p)
|
||||
diff.y = math.NormalizeAngle(diff.y)
|
||||
|
||||
diff = diff * Lerp(sightdelta, 1, 0.25)
|
||||
|
||||
freeaimang.p = math.Clamp(math.NormalizeAngle(freeaimang.p) + math.NormalizeAngle(diff.p), -max, max)
|
||||
freeaimang.y = math.Clamp(math.NormalizeAngle(freeaimang.y) + math.NormalizeAngle(diff.y), -max, max)
|
||||
|
||||
local ang2d = math.atan2(freeaimang.p, freeaimang.y)
|
||||
local mag2d = math.sqrt(math.pow(freeaimang.p, 2) + math.pow(freeaimang.y, 2))
|
||||
|
||||
mag2d = math.min(mag2d, max)
|
||||
|
||||
freeaimang.p = mag2d * math.sin(ang2d)
|
||||
freeaimang.y = mag2d * math.cos(ang2d)
|
||||
|
||||
if CLIENT then
|
||||
self.ClientFreeAimAng = freeaimang
|
||||
else
|
||||
self:SetFreeAimAngle(freeaimang)
|
||||
end
|
||||
end
|
||||
|
||||
if SERVER then
|
||||
self:SetLastAimAngle(eyeangles)
|
||||
else
|
||||
self.ClientLastAimAngle = eyeangles
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:GetFreeAimOffset()
|
||||
if !TacRP.ConVars["freeaim"]:GetBool() or !self:GetValue("FreeAim") or self:GetOwner():IsBot() then return Angle(0, 0, 0) end
|
||||
if CLIENT and LocalPlayer() == self:GetOwner() then
|
||||
return self.ClientFreeAimAng
|
||||
elseif CLIENT and LocalPlayer() != self:GetOwner() then
|
||||
local ang = self:GetFreeAimAngle()
|
||||
ang:Normalize() -- Angles are networked as unsigned or something, so normalization converts it to what we expect
|
||||
return ang
|
||||
else
|
||||
return self:GetFreeAimAngle()
|
||||
end
|
||||
end
|
||||
382
garrysmod/addons/tacrp/lua/weapons/tacrp_base/sh_init.lua
Normal file
382
garrysmod/addons/tacrp/lua/weapons/tacrp_base/sh_init.lua
Normal file
@@ -0,0 +1,382 @@
|
||||
function SWEP:DoDeployAnimation()
|
||||
if self:GetReloading() and self:GetValue("MidReload") and !self:GetValue("ShotgunReload") and self:HasSequence("midreload") then
|
||||
local t = self:PlayAnimation("midreload", self:GetValue("ReloadTimeMult"), true, true)
|
||||
|
||||
self:SetReloadFinishTime(CurTime() + t)
|
||||
else
|
||||
self:SetReloading(false)
|
||||
if self:GetValue("TryUnholster") then
|
||||
self:PlayAnimation("unholster", self:GetValue("DeployTimeMult") * self:GetValue("UnholsterTimeMult"), true, true)
|
||||
else
|
||||
-- if self:GetReady() then
|
||||
-- self:PlayAnimation("unholster", self:GetValue("DeployTimeMult"), true, true)
|
||||
-- else
|
||||
-- self:PlayAnimation("deploy", self:GetValue("DeployTimeMult"), true, true)
|
||||
-- end
|
||||
self:PlayAnimation("deploy", self:GetValue("DeployTimeMult"), true, true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:Deploy()
|
||||
if self:GetOwner():IsNPC() or self:GetOwner():IsNextBot() then
|
||||
if SERVER then
|
||||
self:NetworkWeapon()
|
||||
end
|
||||
if CLIENT then
|
||||
self:SetupModel(true)
|
||||
end
|
||||
return
|
||||
elseif SERVER and self:GetOwner():IsPlayer() then
|
||||
self:GetOwner():SetSaveValue("m_flNextAttack", 0)
|
||||
end
|
||||
|
||||
self:SetBaseSettings()
|
||||
|
||||
-- self:SetNextPrimaryFire(0)
|
||||
self:SetNextSecondaryFire(0)
|
||||
self:SetAnimLockTime(0)
|
||||
self:SetSprintLockTime(0)
|
||||
self:SetLastMeleeTime(0)
|
||||
self:SetRecoilAmount(0)
|
||||
self:SetLastScopeTime(0)
|
||||
self:SetHolsterTime(0)
|
||||
self:SetPrimedGrenade(false)
|
||||
self:SetBlindFireFinishTime(0)
|
||||
-- self:SetJammed(false)
|
||||
self:SetCharge(false)
|
||||
|
||||
self:SetBurstCount(0)
|
||||
self:SetScopeLevel(0)
|
||||
self:SetLoadedRounds(self:Clip1())
|
||||
self:SetCustomize(false)
|
||||
|
||||
if self:GetOwner():IsPlayer() and IsFirstTimePredicted() then
|
||||
self.InversePeek = self:GetOwner():GetInfoNum("tacrp_inversepeek", 0) == 1
|
||||
if !self.InversePeekInitialized then
|
||||
self:SetPeeking(false)
|
||||
end
|
||||
self.InversePeekInitialized = true
|
||||
end
|
||||
|
||||
self.PreviousZoom = self:GetOwner():GetCanZoom()
|
||||
if IsValid(self:GetOwner()) and self:GetOwner():IsPlayer() then
|
||||
self:GetOwner():SetCanZoom(false)
|
||||
end
|
||||
|
||||
self:DoDeployAnimation()
|
||||
|
||||
self:GetOwner():DoAnimationEvent(ACT_HL2MP_GESTURE_RANGE_ATTACK_KNIFE)
|
||||
|
||||
if CLIENT then
|
||||
self:SetupModel(true)
|
||||
self:SetupModel(false)
|
||||
self.LastHintLife = CurTime()
|
||||
elseif !game.SinglePlayer() then
|
||||
self:DoBodygroups(true) -- Not sure why this is necessary
|
||||
self:DoBodygroups(false)
|
||||
end
|
||||
|
||||
if (game.SinglePlayer() or CLIENT) and !TacRP.NewsPopup and TacRP.ConVars["checknews"]:GetBool() then
|
||||
TacRP.NewsPopup = true
|
||||
RunConsoleCommand("tacrp_news_check")
|
||||
end
|
||||
|
||||
self:ToggleBlindFire(TacRP.BLINDFIRE_NONE)
|
||||
|
||||
if TacRP.ConVars["deploysafety"]:GetBool() then
|
||||
self:ToggleSafety(true)
|
||||
end
|
||||
|
||||
self:SetShouldHoldType()
|
||||
|
||||
if self:IsQuickNadeAllowed() then
|
||||
if self:GetValue("PrimaryGrenade") then
|
||||
local nade = TacRP.QuickNades[self:GetValue("PrimaryGrenade")]
|
||||
if !TacRP.IsGrenadeInfiniteAmmo(nade) and self:GetOwner():GetAmmoCount(nade.Ammo) == 0 then
|
||||
if SERVER then self:Remove() end
|
||||
return true
|
||||
end
|
||||
elseif !self:CheckGrenade() then
|
||||
self:SelectGrenade()
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
local v0 = Vector(0, 0, 0)
|
||||
local v1 = Vector(1, 1, 1)
|
||||
local a0 = Angle(0, 0, 0)
|
||||
|
||||
function SWEP:ClientHolster()
|
||||
if game.SinglePlayer() then
|
||||
self:CallOnClient("ClientHolster")
|
||||
end
|
||||
|
||||
local vm = self:GetVM()
|
||||
if IsValid(vm) then
|
||||
vm:SetSubMaterial()
|
||||
vm:SetMaterial()
|
||||
|
||||
for i = 0, vm:GetBoneCount() do
|
||||
vm:ManipulateBoneScale(i, v1)
|
||||
vm:ManipulateBoneAngles(i, a0)
|
||||
vm:ManipulateBonePosition(i, v0)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:Holster(wep)
|
||||
if game.SinglePlayer() and CLIENT then return end
|
||||
|
||||
if CLIENT and self:GetOwner() != LocalPlayer() then return end
|
||||
|
||||
if self:GetOwner():IsNPC() then
|
||||
return
|
||||
end
|
||||
|
||||
self:SetCustomize(false)
|
||||
|
||||
if self:GetReloading() then
|
||||
if self:GetValue("ShotgunReload") then
|
||||
self:SetEndReload(false)
|
||||
self:SetReloading(false)
|
||||
self:KillTimer("ShotgunRestoreClip")
|
||||
else
|
||||
self:CancelReload(false)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
if self:GetHolsterTime() > CurTime() then return false end -- or self:GetPrimedGrenade()
|
||||
|
||||
if !TacRP.ConVars["holster"]:GetBool() or (self:GetHolsterTime() != 0 and self:GetHolsterTime() <= CurTime()) or !IsValid(wep) then
|
||||
-- Do the final holster request
|
||||
-- Picking up props try to switch to NULL, by the way
|
||||
self:SetHolsterTime(0)
|
||||
self:SetHolsterEntity(NULL)
|
||||
self:SetReloadFinishTime(0)
|
||||
|
||||
local holster = self:GetValue("HolsterVisible")
|
||||
if SERVER and holster then
|
||||
net.Start("TacRP_updateholster")
|
||||
net.WriteEntity(self:GetOwner())
|
||||
net.WriteEntity(self)
|
||||
net.Broadcast()
|
||||
end
|
||||
|
||||
if game.SinglePlayer() then
|
||||
self:CallOnClient("KillModel")
|
||||
else
|
||||
if CLIENT then
|
||||
self:RemoveCustomizeHUD()
|
||||
self:KillModel()
|
||||
end
|
||||
end
|
||||
|
||||
if self.PreviousZoom then
|
||||
self:GetOwner():SetCanZoom(true)
|
||||
end
|
||||
|
||||
self:ClientHolster()
|
||||
|
||||
return true
|
||||
else
|
||||
local reverse = 1
|
||||
local anim = "holster"
|
||||
|
||||
if self:GetValue("NoHolsterAnimation") then
|
||||
anim = "deploy"
|
||||
reverse = -1
|
||||
end
|
||||
|
||||
local animation = self:PlayAnimation(anim, self:GetValue("HolsterTimeMult") * reverse, true, true)
|
||||
self:SetHolsterTime(CurTime() + (animation or 0))
|
||||
self:SetHolsterEntity(wep)
|
||||
|
||||
self:SetScopeLevel(0)
|
||||
self:KillTimers()
|
||||
self:ToggleBlindFire(TacRP.BLINDFIRE_NONE)
|
||||
self:GetOwner():SetFOV(0, 0.1)
|
||||
self:SetLastProceduralFireTime(0)
|
||||
|
||||
self:GetOwner():DoAnimationEvent(ACT_HL2MP_GESTURE_RANGE_ATTACK_SLAM)
|
||||
self:SetShouldHoldType()
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
local holsteranticrash = false
|
||||
|
||||
hook.Add("StartCommand", "TacRP_Holster", function(ply, ucmd)
|
||||
local wep = ply:GetActiveWeapon()
|
||||
|
||||
if IsValid(wep) and wep.ArcticTacRP and wep:GetHolsterTime() != 0 and wep:GetHolsterTime() - wep:GetPingOffsetScale() <= CurTime() and IsValid(wep:GetHolsterEntity()) then
|
||||
wep:SetHolsterTime(-math.huge) -- Pretty much force it to work
|
||||
if !holsteranticrash then
|
||||
holsteranticrash = true
|
||||
ucmd:SelectWeapon(wep:GetHolsterEntity()) -- Call the final holster request
|
||||
holsteranticrash = false
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
function SWEP:Initialize()
|
||||
self:SetShouldHoldType()
|
||||
|
||||
self:SetBaseSettings()
|
||||
|
||||
self:SetLastMeleeTime(0)
|
||||
self:SetNthShot(0)
|
||||
|
||||
if self:GetOwner():IsNPC() then
|
||||
self:NPC_Initialize()
|
||||
return
|
||||
end
|
||||
|
||||
if self:GetOwner():IsNextBot() then
|
||||
return
|
||||
end
|
||||
|
||||
self.m_WeaponDeploySpeed = 4
|
||||
|
||||
if engine.ActiveGamemode() == "terrortown" then
|
||||
self:TTT_Init()
|
||||
end
|
||||
|
||||
self:ClientInitialize()
|
||||
|
||||
if SERVER and engine.ActiveGamemode() != "terrortown" then
|
||||
-- If we have any pre-existing attachments, network it
|
||||
local empty = true
|
||||
for slot, slottbl in pairs(self.Attachments) do
|
||||
if slottbl.Installed then empty = false break end
|
||||
end
|
||||
if !empty then
|
||||
self:NetworkWeapon()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:ClientInitialize()
|
||||
if SERVER then return end
|
||||
|
||||
if game.SinglePlayer() and SERVER and IsValid(self:GetOwner()) and self:GetOwner():IsPlayer() then
|
||||
self:CallOnClient("ClientInitialize")
|
||||
end
|
||||
|
||||
if !LocalPlayer().TacRPGreet and !TacRP.ConVars["shutup"]:GetBool() then
|
||||
LocalPlayer().TacRPGreet = true
|
||||
end
|
||||
|
||||
-- local mat = Material("entities/" .. self:GetClass() .. ".png")
|
||||
|
||||
-- local tex = mat:GetTexture("$basetexture")
|
||||
|
||||
-- killicon.Add(self:GetClass(), tex:GetName(), Color( 255, 255, 255, 255 ) )
|
||||
end
|
||||
|
||||
function SWEP:SetBaseSettings()
|
||||
if game.SinglePlayer() and SERVER and IsValid(self:GetOwner()) and self:GetOwner():IsPlayer() then
|
||||
self:CallOnClient("SetBaseSettings")
|
||||
end
|
||||
|
||||
local fm = self:GetCurrentFiremode()
|
||||
if fm != 1 then
|
||||
if self:GetValue("RunawayBurst") and fm < 0 and !self:GetValue("AutoBurst") then
|
||||
self.Primary.Automatic = false
|
||||
else
|
||||
self.Primary.Automatic = true
|
||||
end
|
||||
else
|
||||
self.Primary.Automatic = false
|
||||
end
|
||||
|
||||
if self.PrimaryGrenade then
|
||||
self.Primary.ClipSize = -1
|
||||
self.Primary.Ammo = TacRP.QuickNades[self.PrimaryGrenade].Ammo or ""
|
||||
self.Primary.DefaultClip = 1
|
||||
else
|
||||
self.Primary.ClipSize = self:GetCapacity()
|
||||
self.Primary.Ammo = self:GetAmmoType()
|
||||
self.Primary.DefaultClip = math.ceil(self.Primary.ClipSize * TacRP.ConVars["defaultammo"]:GetFloat())
|
||||
end
|
||||
|
||||
if self:IsQuickNadeAllowed() then
|
||||
self.Secondary.Ammo = self:GetGrenade().Ammo or "grenade"
|
||||
else
|
||||
self.Secondary.Ammo = "none"
|
||||
end
|
||||
|
||||
if SERVER and IsValid(self:GetOwner()) and self:GetOwner():IsPlayer() and self:GetCapacity() > 0 and self:Clip1() > self:GetCapacity() then
|
||||
self:GetOwner():GiveAmmo(self:Clip1() - self:GetCapacity(), self:GetAmmoType())
|
||||
self:SetClip1(self:GetCapacity())
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:SetShouldHoldType()
|
||||
if self:GetOwner():IsNPC() and !self:GetOwner():IsNextBot() then
|
||||
self:SetHoldType(self:GetValue("HoldTypeNPC") or self:GetValue("HoldType"))
|
||||
return
|
||||
end
|
||||
|
||||
if self:GetHolsterTime() > CurTime() then
|
||||
self:SetHoldType("passive")
|
||||
return
|
||||
end
|
||||
|
||||
if (self:GetIsSprinting() or self:ShouldLowerWeapon() or self:GetSafe()) and self:GetValue("HoldTypeSprint") then
|
||||
self:SetHoldType(self:GetValue("HoldTypeSprint"))
|
||||
return
|
||||
end
|
||||
|
||||
if self:GetBlindFire() then
|
||||
if self:GetBlindFireMode() == TacRP.BLINDFIRE_KYS and self:GetValue("HoldTypeSuicide") then
|
||||
self:SetHoldType(self:GetValue("HoldTypeSuicide"))
|
||||
return
|
||||
elseif self:GetValue("HoldTypeBlindFire") then
|
||||
self:SetHoldType(self:GetValue("HoldTypeBlindFire"))
|
||||
return
|
||||
end
|
||||
elseif self:GetScopeLevel() > 0 and self:GetValue("HoldTypeSighted") then
|
||||
self:SetHoldType(self:GetValue("HoldTypeSighted"))
|
||||
return
|
||||
elseif self:GetScopeLevel() > 0 and TacRP.HoldTypeSightedLookup[self:GetValue("HoldType")] then
|
||||
self:SetHoldType(TacRP.HoldTypeSightedLookup[self:GetValue("HoldType")])
|
||||
return
|
||||
end
|
||||
|
||||
if self:GetCustomize() and self:GetValue("HoldTypeCustomize") then
|
||||
self:SetHoldType(self:GetValue("HoldTypeCustomize"))
|
||||
return
|
||||
end
|
||||
|
||||
self:SetHoldType(self:GetValue("HoldType"))
|
||||
end
|
||||
|
||||
function SWEP:OnRemove()
|
||||
if IsValid(self:GetOwner()) and self:GetOwner():IsPlayer() then
|
||||
self:ToggleBoneMods(TacRP.BLINDFIRE_NONE)
|
||||
|
||||
if CLIENT then
|
||||
local vm = self:GetOwner():GetViewModel()
|
||||
if IsValid(vm) then
|
||||
vm:SetMaterial() -- Quick and dirty fix for grenade materials persisting on VM when stripped
|
||||
end
|
||||
end
|
||||
end
|
||||
if CLIENT and (self:GetCustomize() or (self.GrenadeMenuAlpha or 0) > 0 or (self.BlindFireMenuAlpha or 0) > 0) then
|
||||
gui.EnableScreenClicker(false)
|
||||
TacRP.CursorEnabled = false
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:EquipAmmo(ply)
|
||||
local ammotype = self.Primary.Ammo
|
||||
if ammotype == "" then return end
|
||||
|
||||
local supplyamount = self.GaveDefaultAmmo and self:Clip1() or math.ceil(math.max(1, self.Primary.ClipSize) * TacRP.ConVars["defaultammo"]:GetFloat())
|
||||
ply:GiveAmmo(supplyamount, ammotype)
|
||||
end
|
||||
242
garrysmod/addons/tacrp/lua/weapons/tacrp_base/sh_melee.lua
Normal file
242
garrysmod/addons/tacrp/lua/weapons/tacrp_base/sh_melee.lua
Normal file
@@ -0,0 +1,242 @@
|
||||
SWEP.Sound_MeleeHit = nil
|
||||
SWEP.Sound_MeleeHitBody = nil
|
||||
|
||||
-- Don't you love lua table inheritance??
|
||||
SWEP._Sound_MeleeHit = {
|
||||
"TacRP/weapons/melee_hit-1.wav",
|
||||
"TacRP/weapons/melee_hit-2.wav"
|
||||
}
|
||||
SWEP._Sound_MeleeHitBody = {
|
||||
"TacRP/weapons/melee_body_hit-1.wav",
|
||||
"TacRP/weapons/melee_body_hit-2.wav",
|
||||
"TacRP/weapons/melee_body_hit-3.wav",
|
||||
"TacRP/weapons/melee_body_hit-4.wav",
|
||||
"TacRP/weapons/melee_body_hit-5.wav",
|
||||
}
|
||||
|
||||
function SWEP:Melee(alt)
|
||||
if !self:GetValue("CanMeleeAttack") then return end
|
||||
if self:StillWaiting(false, true) then return end
|
||||
if DarkRP and self:GetNWBool("TacRP_PoliceBiocode") and !self:GetOwner():isCP() then return end
|
||||
-- if self:SprintLock() then return end
|
||||
|
||||
self.Primary.Automatic = true
|
||||
self.Secondary.Automatic = true
|
||||
|
||||
self:SetCharge(false)
|
||||
|
||||
self:CancelReload()
|
||||
|
||||
self:ToggleBlindFire(TacRP.BLINDFIRE_NONE)
|
||||
self:ScopeToggle(0)
|
||||
|
||||
|
||||
local dmg = self:GetValue("MeleeDamage") * self:GetConfigDamageMultiplier()
|
||||
local range = self:GetValue("MeleeRange")
|
||||
local delay = self:GetValue("MeleeDelay")
|
||||
if alt then
|
||||
self:PlayAnimation("melee2", 1, false, true)
|
||||
-- self:GetOwner():DoAnimationEvent(self:GetValue("GestureBash2") or self:GetValue("GestureBash"))
|
||||
-- range = self:GetValue("Melee2Range") or range
|
||||
dmg = self:GetHeavyAttackDamage() * self:GetConfigDamageMultiplier()
|
||||
else
|
||||
self:PlayAnimation("melee", 1, false, true)
|
||||
-- self:GetOwner():DoAnimationEvent(self:GetValue("GestureBash"))
|
||||
end
|
||||
|
||||
local t = alt and self:GetHeavyAttackTime() or self:GetValue("MeleeAttackTime")
|
||||
self:GetOwner():DoCustomAnimEvent(PLAYERANIMEVENT_ATTACK_PRIMARY, t * 1000 * (alt and -1 or 1))
|
||||
|
||||
if delay > 0 then
|
||||
self:EmitSound(self:ChooseSound(self:GetValue("Sound_MeleeSwing")), 75, 100, 1)
|
||||
end
|
||||
|
||||
self:SetTimer(delay, function()
|
||||
self:GetOwner():LagCompensation(true)
|
||||
|
||||
local filter = {self:GetOwner()}
|
||||
|
||||
table.Add(filter, self.Shields)
|
||||
|
||||
local start = self:GetOwner():GetShootPos()
|
||||
local dir = self:GetOwner():GetAimVector()
|
||||
local tr = util.TraceLine({
|
||||
start = start,
|
||||
endpos = start + dir * range,
|
||||
filter = filter,
|
||||
mask = MASK_SHOT_HULL,
|
||||
})
|
||||
|
||||
-- weapon_hl2mpbasebasebludgeon.cpp: do a hull trace if not hit
|
||||
if tr.Fraction == 1 or !IsValid(tr.Entity) then
|
||||
local dim = 32
|
||||
local pos2 = tr.HitPos - dir * (dim * 1.732)
|
||||
local tr2 = util.TraceHull({
|
||||
start = start,
|
||||
endpos = pos2,
|
||||
filter = filter,
|
||||
mask = MASK_SHOT_HULL,
|
||||
mins = Vector(-dim, -dim, -dim),
|
||||
maxs = Vector(dim, dim, dim)
|
||||
})
|
||||
|
||||
if tr2.Fraction < 1 and IsValid(tr2.Entity) then
|
||||
local dot = (tr2.Entity:GetPos() - start):GetNormalized():Dot(dir)
|
||||
if dot >= 0.5 then
|
||||
tr = tr2
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local dmginfo = DamageInfo()
|
||||
dmginfo:SetDamage(dmg)
|
||||
dmginfo:SetDamageForce(dir * dmg * 500)
|
||||
dmginfo:SetDamagePosition(tr.HitPos)
|
||||
dmginfo:SetDamageType(self:GetValue("MeleeDamageType"))
|
||||
if dmginfo:GetDamageType() == DMG_GENERIC and engine.ActiveGamemode() == "terrortown" then
|
||||
dmginfo:SetDamageType(DMG_CLUB) -- use CLUB so TTT can assign DNA (it does not leave DNA on generic damage)
|
||||
end
|
||||
|
||||
dmginfo:SetAttacker(self:GetOwner())
|
||||
dmginfo:SetInflictor(self)
|
||||
|
||||
if tr.Fraction < 1 then
|
||||
|
||||
TacRP.CancelBodyDamage(tr.Entity, dmginfo, tr.HitGroup)
|
||||
|
||||
if IsValid(tr.Entity) and (tr.Entity:IsNPC() or tr.Entity:IsPlayer() or tr.Entity:IsNextBot() or tr.Entity:IsRagdoll()) then
|
||||
self:EmitSound(self:ChooseSound(self:GetValue("Sound_MeleeHitBody") or self:GetValue("_Sound_MeleeHitBody")), 75, 100, 1, CHAN_ITEM)
|
||||
|
||||
if !tr.Entity:IsRagdoll() and self:GetValue("MeleeBackstab") then
|
||||
local ang = math.NormalizeAngle(self:GetOwner():GetAngles().y - tr.Entity:GetAngles().y)
|
||||
if ang <= 60 and ang >= -60 then
|
||||
dmginfo:ScaleDamage(self:GetValue("MeleeBackstabMult"))
|
||||
self:EmitSound("tacrp/riki_backstab.wav", 70, 100, 0.4)
|
||||
end
|
||||
end
|
||||
else
|
||||
self:EmitSound(self:ChooseSound(self:GetValue("Sound_MeleeHit") or self:GetValue("_Sound_MeleeHit")), 75, 100, 1, CHAN_ITEM)
|
||||
end
|
||||
|
||||
if IsValid(tr.Entity) and self:GetValue("MeleeSlow") then
|
||||
tr.Entity.TacRPBashSlow = true
|
||||
end
|
||||
|
||||
if tr.HitGroup == HITGROUP_HEAD then
|
||||
dmginfo:ScaleDamage(1.25)
|
||||
end
|
||||
|
||||
if IsValid(tr.Entity) then
|
||||
if tr.Entity.LVS then return end
|
||||
--tr.Entity:TakeDamageInfo(dmginfo)
|
||||
tr.Entity:DispatchTraceAttack(dmginfo, tr)
|
||||
end
|
||||
|
||||
self:FireBullets({
|
||||
Attacker = self:GetOwner(),
|
||||
Damage = 0,
|
||||
Force = 0,
|
||||
Distance = range + 8,
|
||||
HullSize = 0,
|
||||
Tracer = 0,
|
||||
Dir = (tr.HitPos - start):GetNormalized(),
|
||||
Src = start,
|
||||
})
|
||||
else
|
||||
local tmiss
|
||||
if !alt and self:GetValue("MeleeAttackMissTime") then
|
||||
tmiss = self:GetValue("MeleeAttackMissTime")
|
||||
elseif alt then
|
||||
tmiss = self:GetHeavyAttackTime(true, false)
|
||||
end
|
||||
if tmiss then
|
||||
self:SetNextSecondaryFire(CurTime() + (tmiss - delay))
|
||||
end
|
||||
if delay == 0 then
|
||||
self:EmitSound(self:ChooseSound(self:GetValue("Sound_MeleeSwing")), 75, 100, 1)
|
||||
end
|
||||
end
|
||||
|
||||
self:GetOwner():LagCompensation(false)
|
||||
end, "Melee")
|
||||
|
||||
self:SetLastMeleeTime(CurTime())
|
||||
self:SetNextSecondaryFire(CurTime() + t)
|
||||
end
|
||||
|
||||
function SWEP:GetHeavyAttackDamage(base)
|
||||
local valfunc = base and self.GetBaseValue or self.GetValue
|
||||
return valfunc(self, "Melee2Damage") or valfunc(self, "MeleeDamage") * self:GetMeleePerkDamage(base) * 1.5
|
||||
end
|
||||
|
||||
function SWEP:GetHeavyAttackTime(miss, base)
|
||||
local valfunc = base and self.GetBaseValue or self.GetValue
|
||||
if miss then
|
||||
return (valfunc(self, "Melee2AttackMissTime") or (valfunc(self, "MeleeAttackMissTime") * 1.6))
|
||||
* self:GetMeleePerkCooldown(base)
|
||||
else
|
||||
return (valfunc(self, "Melee2AttackTime") or (valfunc(self, "MeleeAttackTime") * 1.6))
|
||||
* self:GetMeleePerkCooldown(base)
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:GetMeleePerkDamage(base)
|
||||
local valfunc = base and self.GetBaseValue or self.GetValue
|
||||
local stat = valfunc(self, "MeleePerkStr")
|
||||
if stat >= 0.5 then
|
||||
return Lerp((stat - 0.5) * 2, 1, 2)
|
||||
else
|
||||
return Lerp(stat * 2, 0.7, 1)
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:GetMeleePerkCooldown(base)
|
||||
local valfunc = base and self.GetBaseValue or self.GetValue
|
||||
local stat = valfunc(self, "MeleePerkAgi")
|
||||
if stat >= 0.5 then
|
||||
return Lerp((stat - 0.5) * 2, 1, 0.7)
|
||||
else
|
||||
return Lerp(stat * 2, 1.3, 1)
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:GetMeleePerkSpeed(base)
|
||||
local valfunc = base and self.GetBaseValue or self.GetValue
|
||||
local stat = valfunc(self, "MeleePerkAgi")
|
||||
if stat >= 0.5 then
|
||||
return Lerp((stat - 0.5) * 2, 1, 1.5)
|
||||
else
|
||||
return Lerp(stat * 2, 0.5, 1)
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:GetMeleePerkVelocity(base)
|
||||
local valfunc = base and self.GetBaseValue or self.GetValue
|
||||
local stat = valfunc(self, "MeleePerkInt")
|
||||
if stat >= 0.5 then
|
||||
return Lerp((stat - 0.5) * 2, 1, 3) * valfunc(self, "MeleeThrowForce")
|
||||
else
|
||||
return Lerp(stat * 2, 0.5, 1) * valfunc(self, "MeleeThrowForce")
|
||||
end
|
||||
end
|
||||
|
||||
hook.Add("PostEntityTakeDamage", "tacrp_melee", function(ent, dmg, took)
|
||||
if ent.TacRPBashSlow then
|
||||
if took and (!ent:IsPlayer() or (ent:IsPlayer() and !(IsValid(ent:GetActiveWeapon()) and ent:GetActiveWeapon().ArcticTacRP and ent:GetActiveWeapon():GetValue("StunResist")))) then
|
||||
ent:SetNWFloat("TacRPLastBashed", CurTime())
|
||||
end
|
||||
ent.TacRPBashSlow = false
|
||||
end
|
||||
|
||||
local wep = dmg:GetInflictor()
|
||||
if (!IsValid(wep) or !wep:IsWeapon()) and IsValid(dmg:GetAttacker()) and dmg:GetAttacker():IsPlayer() then wep = dmg:GetAttacker():GetActiveWeapon() end
|
||||
if took and (ent:IsPlayer() or ent:IsNPC() or ent:IsNextBot()) and IsValid(wep) and wep.ArcticTacRP then
|
||||
if (wep:GetValue("Lifesteal") or 0) > 0 then
|
||||
wep:GetOwner():SetHealth(math.min(math.max(wep:GetOwner():GetMaxHealth(), wep:GetOwner():Health()),
|
||||
wep:GetOwner():Health() + dmg:GetDamage() * wep:GetValue("Lifesteal")))
|
||||
end
|
||||
if (wep:GetValue("DamageCharge") or 0) > 0 then
|
||||
wep:SetBreath(math.min(1, wep:GetBreath() + dmg:GetDamage() * wep:GetValue("DamageCharge")))
|
||||
end
|
||||
end
|
||||
end)
|
||||
187
garrysmod/addons/tacrp/lua/weapons/tacrp_base/sh_npc.lua
Normal file
187
garrysmod/addons/tacrp/lua/weapons/tacrp_base/sh_npc.lua
Normal file
@@ -0,0 +1,187 @@
|
||||
function SWEP:NPC_PrimaryAttack()
|
||||
if !IsValid(self:GetOwner()) then return end
|
||||
|
||||
local enemy = self:GetOwner():GetEnemy()
|
||||
|
||||
local aps = self:GetValue("AmmoPerShot")
|
||||
|
||||
if self:Clip1() < aps then
|
||||
if !IsValid(enemy) or !IsValid(enemy:GetActiveWeapon()) or table.HasValue({"weapon_crowbar", "weapon_stunstick"}, enemy:GetActiveWeapon():GetClass()) then
|
||||
// do not attempt to find cover if enemy does not have a ranged weapon
|
||||
self:GetOwner():SetSchedule(SCHED_RELOAD)
|
||||
else
|
||||
self:GetOwner():SetSchedule(SCHED_HIDE_AND_RELOAD)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
self:SetBaseSettings()
|
||||
self:SetShouldHoldType()
|
||||
|
||||
self.Primary.Automatic = true
|
||||
|
||||
local pvar = self:GetValue("ShootPitchVariance")
|
||||
|
||||
local sshoot = self:GetValue("Sound_Shoot")
|
||||
|
||||
if self:GetValue("Silencer") then
|
||||
sshoot = self:GetValue("Sound_Shoot_Silenced")
|
||||
end
|
||||
|
||||
if istable(sshoot) then
|
||||
sshoot = table.Random(sshoot)
|
||||
end
|
||||
|
||||
self:EmitSound(sshoot, self:GetValue("Vol_Shoot"), self:GetValue("Pitch_Shoot") + math.Rand(-pvar, pvar), 1, CHAN_WEAPON)
|
||||
|
||||
self:SetClip1(self:Clip1() - aps)
|
||||
|
||||
local delay = 60 / self:GetValue("RPM")
|
||||
self:SetNextPrimaryFire(CurTime() + delay)
|
||||
if delay < 0.1 then
|
||||
self:GetOwner():NextThink(CurTime() + delay) // they will only attempt to fire once per think
|
||||
end
|
||||
|
||||
local spread = self:GetNPCSpread()
|
||||
|
||||
local dir = self:GetOwner():GetAimVector()
|
||||
|
||||
if self:GetValue("ShootEnt") then
|
||||
if IsValid(enemy) then
|
||||
dir = (enemy:WorldSpaceCenter() - self:GetOwner():GetShootPos()):GetNormalized():Angle()
|
||||
dir = dir + ((spread + (0.1 / self:GetOwner():GetCurrentWeaponProficiency())) * AngleRand() / 3.6)
|
||||
end
|
||||
self:ShootRocket(dir)
|
||||
else
|
||||
self:GetOwner():FireBullets({
|
||||
Damage = self:GetValue("Damage_Max"),
|
||||
Force = 8,
|
||||
TracerName = "tacrp_tracer",
|
||||
Tracer = self:GetValue("TracerNum"),
|
||||
Num = self:GetValue("Num"),
|
||||
Dir = dir,
|
||||
Src = self:GetOwner():GetShootPos(),
|
||||
Spread = Vector(spread, spread, spread),
|
||||
Callback = function(att, btr, dmg)
|
||||
local range = (btr.HitPos - btr.StartPos):Length()
|
||||
|
||||
self:AfterShotFunction(btr, dmg, range, 0, {}) // self:GetValue("Penetration")
|
||||
|
||||
if TacRP.Developer() then
|
||||
if SERVER then
|
||||
debugoverlay.Cross(btr.HitPos, 4, 5, Color(255, 0, 0), false)
|
||||
else
|
||||
debugoverlay.Cross(btr.HitPos, 4, 5, Color(255, 255, 255), false)
|
||||
end
|
||||
end
|
||||
end
|
||||
})
|
||||
end
|
||||
|
||||
self:DoEffects()
|
||||
self:DoEject()
|
||||
end
|
||||
|
||||
function SWEP:GetNPCBulletSpread(prof)
|
||||
local mode = self:GetCurrentFiremode()
|
||||
|
||||
if mode < 0 then
|
||||
return 10 / (prof + 1)
|
||||
elseif mode == 1 then
|
||||
if math.Rand(0, 100) < (prof + 5) * 5 then
|
||||
return 2 / (prof + 1)
|
||||
else
|
||||
return 20 / (prof + 1)
|
||||
end
|
||||
elseif mode > 1 then
|
||||
if math.Rand(0, 100) < (prof + 5) * 2 then
|
||||
return 5 / (prof + 1)
|
||||
else
|
||||
return 30 / (prof + 1)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return 15
|
||||
end
|
||||
|
||||
function SWEP:GetNPCSpread()
|
||||
return self:GetValue("Spread")
|
||||
end
|
||||
|
||||
function SWEP:GetNPCBurstSettings()
|
||||
local mode = self:GetCurrentFiremode()
|
||||
|
||||
local delay = 60 / self:GetValue("RPM")
|
||||
|
||||
if !mode then return 1, 1, delay end
|
||||
|
||||
if mode < 0 then
|
||||
return -mode, -mode, delay
|
||||
elseif mode == 0 then
|
||||
return 0, 0, delay
|
||||
elseif mode == 1 then
|
||||
local c = self:GetCapacity()
|
||||
return math.ceil(c * 0.075), math.max(1, math.floor(c * math.Rand(0.15, 0.3))), delay + math.Rand(0.1, 0.2)
|
||||
elseif mode >= 2 then
|
||||
return math.min(self:Clip1(), 1 + math.floor(0.5 / delay)), math.min(self:Clip1(), 1 + math.floor(2 / delay)), delay
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:GetNPCRestTimes()
|
||||
local mode = self:GetCurrentFiremode()
|
||||
local postburst = self:GetValue("PostBurstDelay") or 0
|
||||
local m = self:GetValue("RecoilKick")
|
||||
local delay = 60 / self:GetValue("RPM")
|
||||
|
||||
if !mode then return delay + 0.3, delay + 0.6 end
|
||||
|
||||
local o = m > 1 and math.sqrt(m) or m
|
||||
if delay <= 60 / 90 then
|
||||
return delay + 0.1 * o, delay + 0.2 * o
|
||||
elseif mode < 0 then
|
||||
o = delay + o * 0.5 + postburst
|
||||
end
|
||||
|
||||
return delay + 0.4 * o, delay + 0.6 * o
|
||||
end
|
||||
|
||||
function SWEP:CanBePickedUpByNPCs()
|
||||
return self.NPCUsable
|
||||
end
|
||||
|
||||
function SWEP:NPC_Reload()
|
||||
self:DropMagazine()
|
||||
end
|
||||
|
||||
function SWEP:NPC_Initialize()
|
||||
if CLIENT then return end
|
||||
// auto attachment
|
||||
|
||||
if TacRP.ConVars["npc_atts"]:GetBool() then
|
||||
for i, slot in pairs(self.Attachments) do
|
||||
local atts = TacRP.GetAttsForCats(slot.Category or "", self)
|
||||
|
||||
local ind = math.random(0, #atts)
|
||||
|
||||
if ind > 0 and math.random() <= 0.75 then
|
||||
slot.Installed = atts[ind]
|
||||
end
|
||||
end
|
||||
|
||||
self:InvalidateCache()
|
||||
end
|
||||
|
||||
timer.Simple(0.25, function()
|
||||
if !IsValid(self) then return end
|
||||
self:NetworkWeapon()
|
||||
end)
|
||||
|
||||
self:SetBaseSettings()
|
||||
|
||||
self:SetClip1(self:GetCapacity())
|
||||
|
||||
if math.random() <= 0.5 then
|
||||
self:SetFiremode(math.random(1, self:GetFiremodeAmount()))
|
||||
end
|
||||
end
|
||||
139
garrysmod/addons/tacrp/lua/weapons/tacrp_base/sh_penetration.lua
Normal file
139
garrysmod/addons/tacrp/lua/weapons/tacrp_base/sh_penetration.lua
Normal file
@@ -0,0 +1,139 @@
|
||||
|
||||
local function draw_debug()
|
||||
return (CLIENT or game.SinglePlayer()) and TacRP.Developer(2)
|
||||
end
|
||||
|
||||
local function IsPenetrating(ptr, ptrent)
|
||||
if ptrent:IsWorld() then
|
||||
return ptr.Contents != CONTENTS_EMPTY
|
||||
elseif IsValid(ptrent) then
|
||||
|
||||
local withinbounding = false
|
||||
local hboxset = ptrent:GetHitboxSet()
|
||||
local hitbone = ptrent:GetHitBoxBone(ptr.HitBox, hboxset)
|
||||
if hitbone then
|
||||
-- If we hit a hitbox, compare against that hitbox only
|
||||
local mins, maxs = ptrent:GetHitBoxBounds(ptr.HitBox, hboxset)
|
||||
local bonepos, boneang = ptrent:GetBonePosition(hitbone)
|
||||
mins = mins * 1.1
|
||||
maxs = maxs * 1.1
|
||||
local lpos = WorldToLocal(ptr.HitPos, ptr.HitNormal:Angle(), bonepos, boneang)
|
||||
|
||||
withinbounding = lpos:WithinAABox(mins, maxs)
|
||||
if draw_debug() then
|
||||
debugoverlay.BoxAngles(bonepos, mins, maxs, boneang, 5, Color(255, 255, 255, 10))
|
||||
end
|
||||
elseif util.PointContents(ptr.HitPos) != CONTENTS_EMPTY then
|
||||
-- Otherwise default to rotated OBB
|
||||
local mins, maxs = ptrent:OBBMins(), ptrent:OBBMaxs()
|
||||
withinbounding = ptrent:WorldToLocal(ptr.HitPos):WithinAABox(mins, maxs)
|
||||
if draw_debug() then
|
||||
debugoverlay.BoxAngles(ptrent:GetPos(), mins, maxs, ptrent:GetAngles(), 5, Color(255, 255, 255, 10))
|
||||
end
|
||||
end
|
||||
if draw_debug() then
|
||||
debugoverlay.Cross(ptr.HitPos, withinbounding and 4 or 6, 5, withinbounding and Color(255, 255, 0) or Color(128, 255, 0), true)
|
||||
end
|
||||
|
||||
return withinbounding
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function SWEP:Penetrate(tr, range, penleft, alreadypenned)
|
||||
if !TacRP.ConVars["penetration"]:GetBool() then return end
|
||||
|
||||
if !IsValid(self:GetOwner()) then return end
|
||||
|
||||
local hitpos, startpos = tr.HitPos, tr.StartPos
|
||||
local dir = (hitpos - startpos):GetNormalized()
|
||||
|
||||
if tr.HitSky then return end
|
||||
|
||||
if penleft <= 0 then return end
|
||||
|
||||
alreadypenned = alreadypenned or {}
|
||||
|
||||
local skip = false
|
||||
|
||||
local trent = tr.Entity
|
||||
|
||||
local penmult = TacRP.PenTable[tr.MatType] or 1
|
||||
|
||||
local pentracelen = math.max(penleft * penmult / 2, 2)
|
||||
local curr_ent = trent
|
||||
|
||||
if !tr.HitWorld then penmult = penmult * 0.5 end
|
||||
if trent.Impenetrable then penmult = 100000 end
|
||||
if trent.mmRHAe then penmult = trent.mmRHAe end
|
||||
|
||||
-- penmult = penmult * math.Rand(0.9, 1.1) * math.Rand(0.9, 1.1)
|
||||
|
||||
local endpos = hitpos
|
||||
|
||||
local td = {}
|
||||
td.start = endpos
|
||||
td.endpos = endpos + (dir * pentracelen)
|
||||
td.mask = MASK_SHOT
|
||||
|
||||
local ptr = util.TraceLine(td)
|
||||
|
||||
local ptrent = ptr.Entity
|
||||
|
||||
while !skip and penleft > 0 and IsPenetrating(ptr, ptrent) and ptr.Fraction < 1 and ptrent == curr_ent do
|
||||
penleft = penleft - (pentracelen * penmult)
|
||||
|
||||
td.start = endpos
|
||||
td.endpos = endpos + (dir * pentracelen)
|
||||
td.mask = MASK_SHOT
|
||||
|
||||
ptr = util.TraceLine(td)
|
||||
|
||||
if TacRP.Developer() then
|
||||
local pdeltap = penleft / self:GetValue("Penetration")
|
||||
local colorlr = Lerp(pdeltap, 0, 255)
|
||||
|
||||
debugoverlay.Line(endpos, endpos + (dir * pentracelen), 10, Color(255, colorlr, colorlr), true)
|
||||
end
|
||||
|
||||
endpos = endpos + (dir * pentracelen)
|
||||
range = range + pentracelen
|
||||
|
||||
ptrent = ptr.Entity
|
||||
end
|
||||
|
||||
if penleft > 0 then
|
||||
if (dir:Length() == 0) then return end
|
||||
|
||||
self:GetOwner():FireBullets({
|
||||
Damage = self:GetValue("Damage_Max"),
|
||||
Force = 4,
|
||||
Tracer = 0,
|
||||
Num = 1,
|
||||
Dir = dir,
|
||||
Src = endpos,
|
||||
Callback = function(att, btr, dmg)
|
||||
range = range + (btr.HitPos - btr.StartPos):Length()
|
||||
self:AfterShotFunction(btr, dmg, range, penleft, alreadypenned)
|
||||
|
||||
if TacRP.Developer() then
|
||||
if SERVER then
|
||||
debugoverlay.Cross(btr.HitPos, 4, 5, Color(255, 0, 0), false)
|
||||
else
|
||||
debugoverlay.Cross(btr.HitPos, 4, 5, Color(255, 255, 255), false)
|
||||
end
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
self:GetOwner():FireBullets({
|
||||
Damage = 0,
|
||||
Force = 0,
|
||||
Tracer = 0,
|
||||
Num = 1,
|
||||
Distance = 1,
|
||||
Dir = -dir,
|
||||
Src = endpos,
|
||||
})
|
||||
end
|
||||
end
|
||||
347
garrysmod/addons/tacrp/lua/weapons/tacrp_base/sh_quicknade.lua
Normal file
347
garrysmod/addons/tacrp/lua/weapons/tacrp_base/sh_quicknade.lua
Normal file
@@ -0,0 +1,347 @@
|
||||
SWEP.GrenadeDownKey = IN_GRENADE1
|
||||
SWEP.GrenadeMenuKey = IN_GRENADE2
|
||||
|
||||
function SWEP:IsQuickNadeAllowed()
|
||||
return TacRP.ConVars["quicknade"]:GetBool() and self:GetValue("CanQuickNade")
|
||||
end
|
||||
|
||||
function SWEP:PrimeGrenade()
|
||||
self.Primary.Automatic = true
|
||||
|
||||
if !self:IsQuickNadeAllowed() and !self:GetValue("PrimaryGrenade") then return end
|
||||
if self:StillWaiting(nil, true) then return end
|
||||
if self:GetPrimedGrenade() then return end
|
||||
|
||||
if engine.ActiveGamemode() == "terrortown" and GetRoundState() == ROUND_PREP and ((TTT2 and !GetConVar("ttt_nade_throw_during_prep"):GetBool()) or (!TTT2 and GetConVar("ttt_no_nade_throw_during_prep"):GetBool())) then
|
||||
return
|
||||
end
|
||||
|
||||
if !self:GetValue("PrimaryGrenade") and !self:CheckGrenade(nil, true) then
|
||||
self:SelectGrenade(nil, true)
|
||||
end
|
||||
|
||||
-- if self:SprintLock() then return end
|
||||
|
||||
self:CancelReload()
|
||||
|
||||
local nade = self:GetValue("PrimaryGrenade") and TacRP.QuickNades[self:GetValue("PrimaryGrenade")] or self:GetGrenade()
|
||||
|
||||
if nade.Singleton then
|
||||
if !self:GetOwner():HasWeapon(nade.GrenadeWep) then return end
|
||||
elseif !TacRP.IsGrenadeInfiniteAmmo(nade) then
|
||||
local ammo = self:GetOwner():GetAmmoCount(nade.Ammo)
|
||||
if ammo < 1 then return end
|
||||
|
||||
-- self:GetOwner():SetAmmo(ammo - 1, nade.Ammo)
|
||||
end
|
||||
|
||||
local rate = self:GetValue("QuickNadeTimeMult") / (nade.ThrowSpeed or 1)
|
||||
if self:GetValue("QuickNadeTryImpact") and nade.CanSetImpact then
|
||||
rate = rate * 1.5
|
||||
end
|
||||
|
||||
local t = self:PlayAnimation("prime_grenade", rate, true)
|
||||
|
||||
self:SetPrimedGrenade(true)
|
||||
self:ToggleBlindFire(TacRP.BLINDFIRE_NONE)
|
||||
self:ScopeToggle(0)
|
||||
|
||||
local ct = CurTime()
|
||||
|
||||
self:SetStartPrimedGrenadeTime(ct)
|
||||
self:SetAnimLockTime(ct + (t * 0.75))
|
||||
self:SetNextPrimaryFire(ct + (t * 1.1))
|
||||
|
||||
self:GetOwner():DoCustomAnimEvent(PLAYERANIMEVENT_ATTACK_SECONDARY, t * 1000)
|
||||
|
||||
if !nade.NoSounds then
|
||||
self:EmitSound(nade.PullSound or ("TacRP/weapons/grenade/pullpin-" .. math.random(1, 2) .. ".wav"), 65)
|
||||
end
|
||||
|
||||
if CLIENT then return end
|
||||
|
||||
self.CurrentGrenade = self:GetGrenade()
|
||||
end
|
||||
|
||||
function SWEP:ThrowGrenade()
|
||||
local nade = self:GetValue("PrimaryGrenade") and TacRP.QuickNades[self:GetValue("PrimaryGrenade")] or self.CurrentGrenade or self:GetGrenade()
|
||||
|
||||
local force = nade.ThrowForce
|
||||
local ent = nade.GrenadeEnt
|
||||
|
||||
local src = self:GetOwner():EyePos()
|
||||
local ang = self:GetOwner():EyeAngles()
|
||||
local spread = 0
|
||||
|
||||
local amount = 1
|
||||
|
||||
local t = 0
|
||||
|
||||
if !nade.OverhandOnly and (self.GrenadeThrowOverride == true or (self.GrenadeThrowOverride == nil and !self:GetOwner():KeyDown(self.GrenadeDownKey))) then
|
||||
t = self:PlayAnimation("throw_grenade_underhand", self:GetValue("QuickNadeTimeMult"), true, true)
|
||||
|
||||
force = force / 2
|
||||
ang:RotateAroundAxis(ang:Right(), 20)
|
||||
if nade.UnderhandSpecial then
|
||||
force = force * 0.75
|
||||
ang:RotateAroundAxis(ang:Right(), -10)
|
||||
amount = math.random(2, 4)
|
||||
spread = 0.15
|
||||
end
|
||||
else
|
||||
ang:RotateAroundAxis(ang:Right(), 5)
|
||||
t = self:PlayAnimation("throw_grenade", self:GetValue("QuickNadeTimeMult"), true, true)
|
||||
end
|
||||
|
||||
self.GrenadeThrowOverride = nil
|
||||
self.GrenadeDownKey = IN_GRENADE1
|
||||
|
||||
if SERVER then
|
||||
if (self.GrenadeThrowCharge or 0) > 0 then
|
||||
force = force * (1 + self.GrenadeThrowCharge)
|
||||
end
|
||||
self.GrenadeThrowCharge = nil
|
||||
|
||||
for i = 1, amount do
|
||||
|
||||
local rocket = ents.Create(ent or "")
|
||||
|
||||
if !IsValid(rocket) then return end
|
||||
|
||||
local dispersion = Angle(math.Rand(-1, 1), math.Rand(-1, 1), 0)
|
||||
dispersion = dispersion * spread * 36
|
||||
|
||||
rocket:SetPos(src)
|
||||
rocket:SetOwner(self:GetOwner())
|
||||
rocket:SetAngles(ang + dispersion)
|
||||
rocket:Spawn()
|
||||
rocket:SetPhysicsAttacker(self:GetOwner(), 10)
|
||||
|
||||
if TacRP.IsGrenadeInfiniteAmmo(nade) then
|
||||
rocket.PickupAmmo = nil
|
||||
rocket.WeaponClass = nil -- dz ents
|
||||
end
|
||||
|
||||
if self:GetValue("QuickNadeTryImpact") and nade.CanSetImpact then
|
||||
rocket.InstantFuse = false
|
||||
rocket.Delay = 0
|
||||
rocket.Armed = false
|
||||
rocket.ImpactFuse = true
|
||||
end
|
||||
|
||||
if nade.TTTTimer then
|
||||
rocket:SetGravity(0.4)
|
||||
rocket:SetFriction(0.2)
|
||||
rocket:SetElasticity(0.45)
|
||||
rocket:SetDetonateExact(CurTime() + nade.TTTTimer)
|
||||
rocket:SetThrower(self:GetOwner())
|
||||
end
|
||||
|
||||
local phys = rocket:GetPhysicsObject()
|
||||
|
||||
if phys:IsValid() then
|
||||
phys:ApplyForceCenter((ang + dispersion):Forward() * force + self:GetOwner():GetVelocity())
|
||||
phys:AddAngleVelocity(VectorRand() * 1000)
|
||||
end
|
||||
|
||||
if nade.Spoon and TacRP.ConVars["dropmagazinemodel"]:GetBool() then
|
||||
local mag = ents.Create("TacRP_droppedmag")
|
||||
|
||||
if mag then
|
||||
mag:SetPos(src)
|
||||
mag:SetAngles(ang)
|
||||
mag.Model = "models/weapons/tacint/flashbang_spoon.mdl"
|
||||
mag.ImpactType = "spoon"
|
||||
mag:SetOwner(self:GetOwner())
|
||||
mag:Spawn()
|
||||
|
||||
local phys2 = mag:GetPhysicsObject()
|
||||
|
||||
if IsValid(phys2) then
|
||||
phys2:ApplyForceCenter(ang:Forward() * force * 0.25 + VectorRand() * 25)
|
||||
phys2:AddAngleVelocity(Vector(math.Rand(-300, 300), math.Rand(-300, 300), math.Rand(-300, 300)))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if !nade.NoSounds then
|
||||
self:EmitSound(nade.ThrowSound or ("tacrp/weapons/grenade/throw-" .. math.random(1, 2) .. ".wav"), 65)
|
||||
end
|
||||
|
||||
if !nade.Singleton and !TacRP.IsGrenadeInfiniteAmmo(nade) then
|
||||
self:GetOwner():RemoveAmmo(1, nade.Ammo)
|
||||
end
|
||||
end
|
||||
|
||||
if self:GetValue("PrimaryGrenade") then
|
||||
if !TacRP.IsGrenadeInfiniteAmmo(nade) and self:GetOwner():GetAmmoCount(nade.Ammo) == 0 then
|
||||
if SERVER then
|
||||
self:Remove()
|
||||
end
|
||||
else
|
||||
self:SetTimer(t, function()
|
||||
self:PlayAnimation("deploy", self:GetValue("DeployTimeMult"), true, true)
|
||||
end)
|
||||
end
|
||||
elseif nade.Singleton and self:GetOwner():HasWeapon(nade.GrenadeWep) then
|
||||
local nadewep = self:GetOwner():GetWeapon(nade.GrenadeWep)
|
||||
nadewep.OnRemove = nil -- TTT wants to switch to unarmed when the nade wep is removed - DON'T.
|
||||
if SERVER then
|
||||
nadewep:Remove()
|
||||
end
|
||||
elseif nade.GrenadeWep and self:GetOwner():HasWeapon(nade.GrenadeWep) and !TacRP.IsGrenadeInfiniteAmmo(nade) and self:GetOwner():GetAmmoCount(nade.Ammo) == 0 then
|
||||
if SERVER then
|
||||
self:GetOwner():GetWeapon(nade.GrenadeWep):Remove()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:GetGrenade(index)
|
||||
index = index or self:GetGrenadeIndex()
|
||||
|
||||
return TacRP.QuickNades[TacRP.QuickNades_Index[index]]
|
||||
end
|
||||
|
||||
function SWEP:GetGrenadeIndex()
|
||||
return IsValid(self:GetOwner()) and self:GetOwner():GetNWInt("ti_nade", 1) or 1
|
||||
end
|
||||
|
||||
function SWEP:GetNextGrenade(ind)
|
||||
ind = ind or self:GetGrenadeIndex()
|
||||
|
||||
ind = ind + 1
|
||||
|
||||
if ind > TacRP.QuickNades_Count then
|
||||
ind = 1
|
||||
elseif ind < 1 then
|
||||
ind = TacRP.QuickNades_Count
|
||||
end
|
||||
|
||||
if !self:CheckGrenade(ind) then
|
||||
return self:GetNextGrenade(ind)
|
||||
end
|
||||
|
||||
return self:GetGrenade(ind)
|
||||
end
|
||||
|
||||
function SWEP:SelectGrenade(index, requireammo)
|
||||
if !self:IsQuickNadeAllowed() then return end
|
||||
if !IsFirstTimePredicted() then return end
|
||||
if self:GetPrimedGrenade() then return end
|
||||
|
||||
local ind = self:GetOwner():GetNWInt("ti_nade", 1)
|
||||
|
||||
if index then
|
||||
ind = index
|
||||
elseif !requireammo then
|
||||
if self:GetOwner():KeyDown(IN_WALK) then
|
||||
ind = ind - 1
|
||||
else
|
||||
ind = ind + 1
|
||||
end
|
||||
end
|
||||
|
||||
if ind > TacRP.QuickNades_Count then
|
||||
ind = 1
|
||||
elseif ind < 1 then
|
||||
ind = TacRP.QuickNades_Count
|
||||
end
|
||||
|
||||
if !self:CheckGrenade(ind, requireammo) then
|
||||
local nades = self:GetAvailableGrenades(requireammo)
|
||||
if #nades > 0 then
|
||||
ind = nades[1].Index
|
||||
end
|
||||
end
|
||||
|
||||
self:GetOwner():SetNWInt("ti_nade", ind)
|
||||
self.Secondary.Ammo = self:GetGrenade().Ammo or "none"
|
||||
end
|
||||
|
||||
function SWEP:CheckGrenade(index, checkammo)
|
||||
index = index or (self:GetValue("PrimaryGrenade") and TacRP.QuickNades_Index[self:GetValue("PrimaryGrenade")] or self:GetOwner():GetNWInt("ti_nade", 1))
|
||||
local nade = self:GetGrenade(index)
|
||||
if nade.Singleton then
|
||||
return self:GetOwner():HasWeapon(nade.GrenadeWep)
|
||||
end
|
||||
local hasammo = (nade.Ammo == nil or self:GetOwner():GetAmmoCount(nade.Ammo) > 0)
|
||||
if (nade.Secret and !hasammo and (!nade.SecretWeapon or !self:GetOwner():HasWeapon(nade.SecretWeapon))) or (nade.RequireStat and !self:GetValue(nade.RequireStat)) then
|
||||
return false
|
||||
end
|
||||
if checkammo and !TacRP.IsGrenadeInfiniteAmmo(index) and !hasammo then
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function SWEP:GetAvailableGrenades(checkammo)
|
||||
local nades = {}
|
||||
|
||||
for i = 1, TacRP.QuickNades_Count do
|
||||
if self:CheckGrenade(i, checkammo) then
|
||||
table.insert(nades, self:GetGrenade(i))
|
||||
end
|
||||
end
|
||||
|
||||
return nades
|
||||
end
|
||||
|
||||
if CLIENT then
|
||||
|
||||
SWEP.QuickNadeModel = nil
|
||||
|
||||
end
|
||||
|
||||
function SWEP:ThinkGrenade()
|
||||
if !self:IsQuickNadeAllowed() then return end
|
||||
|
||||
if CLIENT then
|
||||
if self:GetPrimedGrenade() and !IsValid(self.QuickNadeModel) and self:GetStartPrimedGrenadeTime() + 0.2 < CurTime() and self:GetGrenade().Model then
|
||||
local nade = self:GetGrenade()
|
||||
local vm = self:GetVM()
|
||||
|
||||
local model = ClientsideModel(nade.Model or "models/weapons/tacint/v_quicknade_frag.mdl")
|
||||
|
||||
if !IsValid(model) then return end
|
||||
|
||||
model:SetParent(vm)
|
||||
model:AddEffects(EF_BONEMERGE)
|
||||
model:SetNoDraw(true)
|
||||
|
||||
if nade.Material then
|
||||
model:SetMaterial(nade.Material)
|
||||
end
|
||||
|
||||
self.QuickNadeModel = model
|
||||
|
||||
local tbl = {
|
||||
Model = model,
|
||||
Weapon = self
|
||||
}
|
||||
|
||||
table.insert(TacRP.CSModelPile, tbl)
|
||||
elseif !self:GetPrimedGrenade() and self.QuickNadeModel then
|
||||
SafeRemoveEntity(self.QuickNadeModel)
|
||||
self.QuickNadeModel = nil
|
||||
end
|
||||
end
|
||||
|
||||
if self:GetOwner():KeyPressed(IN_GRENADE1) then
|
||||
self:PrimeGrenade()
|
||||
elseif !tobool(self:GetOwner():GetInfo("tacrp_nademenu")) and self:GetOwner():KeyPressed(self.GrenadeMenuKey) then
|
||||
self:SelectGrenade()
|
||||
elseif tobool(self:GetOwner():GetInfo("tacrp_nademenu")) and self.GrenadeMenuKey != IN_GRENADE2 and !self:GetOwner():KeyDown(self.GrenadeMenuKey) then
|
||||
self.GrenadeMenuKey = IN_GRENADE2
|
||||
end
|
||||
|
||||
if CLIENT and self.GrenadeWaitSelect and self:GetOwner():HasWeapon(self.GrenadeWaitSelect) then
|
||||
input.SelectWeapon(self:GetOwner():GetWeapon(self.GrenadeWaitSelect))
|
||||
self.GrenadeWaitSelect = nil
|
||||
end
|
||||
|
||||
if self:GetPrimedGrenade() and self:GetAnimLockTime() < CurTime() then
|
||||
self:ThrowGrenade()
|
||||
self:SetPrimedGrenade(false)
|
||||
end
|
||||
end
|
||||
136
garrysmod/addons/tacrp/lua/weapons/tacrp_base/sh_recoil.lua
Normal file
136
garrysmod/addons/tacrp/lua/weapons/tacrp_base/sh_recoil.lua
Normal file
@@ -0,0 +1,136 @@
|
||||
function SWEP:GetRecoilResetTime(base)
|
||||
if base then
|
||||
return (self:GetBaseValue("RecoilResetInstant") and 0 or math.min(0.5, 60 / self:GetRPM(true))) + self:GetBaseValue("RecoilResetTime")
|
||||
else
|
||||
return (self:GetValue("RecoilResetInstant") and 0 or math.min(0.5, 60 / self:GetRPM())) + self:GetValue("RecoilResetTime")
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:ThinkRecoil()
|
||||
if self:GetLastRecoilTime() + engine.TickInterval() + self:GetRecoilResetTime() < CurTime() then
|
||||
local rec = self:GetRecoilAmount()
|
||||
|
||||
rec = rec - (FrameTime() * self:GetValue("RecoilDissipationRate"))
|
||||
rec = math.Clamp(rec, 0, self:GetValue("RecoilMaximum"))
|
||||
|
||||
self:SetRecoilAmount(rec)
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:ApplyRecoil()
|
||||
local rec = self:GetRecoilAmount()
|
||||
local rps = self:GetValue("RecoilPerShot")
|
||||
local cfm = self:GetCurrentFiremode()
|
||||
|
||||
if rec == 0 then
|
||||
rps = rps * self:GetValue("RecoilFirstShotMult")
|
||||
end
|
||||
|
||||
if self:GetOwner():Crouching() and self:GetOwner():OnGround() then
|
||||
rps = rps * self:GetValue("RecoilMultCrouch")
|
||||
end
|
||||
|
||||
if cfm < 0 then
|
||||
rps = rps * self:GetValue("RecoilMultBurst")
|
||||
elseif cfm == 1 then
|
||||
rps = rps * self:GetValue("RecoilMultSemi")
|
||||
end
|
||||
|
||||
if self:GetInBipod() then
|
||||
rps = rps * math.min(1, self:GetValue("BipodRecoil"))
|
||||
end
|
||||
|
||||
rec = rec + rps
|
||||
|
||||
rec = math.Clamp(rec, 0, self:GetValue("RecoilMaximum"))
|
||||
|
||||
if self:UseRecoilPatterns() then
|
||||
self:SetRecoilDirection(self:GetRecoilPatternDirection(self:GetPatternCount()))
|
||||
else
|
||||
local stab = math.Clamp(self:GetValue("RecoilStability"), 0, 0.9)
|
||||
self:SetRecoilDirection(util.SharedRandom("tacrp_recoildir", -180 + stab * 90, -stab * 90))
|
||||
end
|
||||
|
||||
-- self:SetRecoilDirection(-90)
|
||||
self:SetRecoilAmount(rec)
|
||||
self:SetLastRecoilTime(CurTime())
|
||||
|
||||
local vis_kick = self:GetValue("RecoilVisualKick")
|
||||
local vis_shake = 0
|
||||
|
||||
vis_kick = vis_kick * TacRP.ConVars["mult_recoil_vis"]:GetFloat()
|
||||
vis_shake = 0
|
||||
|
||||
if self:GetInBipod() then
|
||||
vis_kick = vis_kick * math.min(1, self:GetValue("BipodKick"))
|
||||
vis_shake = math.max(0, 1 - self:GetValue("BipodKick"))
|
||||
end
|
||||
|
||||
local vis_kick_v = vis_kick * 1
|
||||
local vis_kick_h = vis_kick * util.SharedRandom("tacrp_vis_kick_h", -1, 1)
|
||||
|
||||
self:GetOwner():SetViewPunchAngles(Angle(vis_kick_v, vis_kick_h, vis_shake))
|
||||
|
||||
-- self:GetOwner():SetFOV(self:GetOwner():GetFOV() * 0.99, 0)
|
||||
-- self:GetOwner():SetFOV(self:GetOwner():GetFOV(), 60 / (self:GetValue("RPM")))
|
||||
end
|
||||
|
||||
function SWEP:RecoilDuration()
|
||||
-- return self:GetValue("RecoilResetTime")
|
||||
return 0.04 + math.Clamp(math.abs(self:GetValue("RecoilKick")) ^ 0.5, 0, 4) * 0.04
|
||||
end
|
||||
|
||||
function SWEP:UseRecoilPatterns()
|
||||
if !TacRP.ConVars["recoilpattern"]:GetBool() then return false end
|
||||
if self:GetValue("ShootEnt") or self:GetValue("NoRecoilPattern") then return false end
|
||||
if self:GetValue("RPM") <= 100 then return false end
|
||||
if self:GetCurrentFiremode() < 0 then return false end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
SWEP.RecoilPatternCache = {}
|
||||
SWEP.RecoilPatternSeedCache = nil
|
||||
function SWEP:GetRecoilPatternDirection(shot)
|
||||
local dir = 0
|
||||
|
||||
if !self.RecoilPatternSeedCache then
|
||||
local cacheseed = self.RecoilPatternSeed or self:GetClass()
|
||||
if isstring(cacheseed) then
|
||||
local numseed = 0
|
||||
for _, i in ipairs(string.ToTable(cacheseed)) do
|
||||
numseed = numseed + string.byte(i)
|
||||
end
|
||||
numseed = numseed % 16777216
|
||||
cacheseed = numseed
|
||||
end
|
||||
self.RecoilPatternSeedCache = cacheseed
|
||||
end
|
||||
|
||||
local seed = self.RecoilPatternSeedCache + shot
|
||||
|
||||
if self.RecoilPatternCache[shot] then
|
||||
dir = self.RecoilPatternCache[shot]
|
||||
else
|
||||
self.RecoilPatternCache[1] = 0
|
||||
if self.RecoilPatternCache[shot - 1] then
|
||||
--dir = self.RecoilPatternCache[shot - 1]
|
||||
math.randomseed(seed)
|
||||
local stab = math.Clamp(self:GetValue("RecoilStability"), 0, 0.9)
|
||||
local max = self:GetBaseValue("RPM") / 60 * (1.1 + stab * 1.1)
|
||||
local cap = 120 --math.Clamp(30 + shot * (90 / max), 30, 120)
|
||||
--dir = dir + math.Rand(-stab * 90, stab * 90)
|
||||
dir = Lerp(0.4 + (shot / max) * 0.6, self.RecoilPatternCache[shot - 1], math.Rand(-(1 - stab) * cap, (1 - stab) * cap))
|
||||
if self:GetCurrentFiremode() != 1 then
|
||||
dir = Lerp(shot / max, dir, math.Clamp(dir * 1.667, -cap, cap))
|
||||
end
|
||||
math.randomseed(CurTime() + self:EntIndex())
|
||||
self.RecoilPatternCache[shot] = dir
|
||||
-- print(shot, cap, max, dir)
|
||||
else
|
||||
dir = 0
|
||||
end
|
||||
end
|
||||
|
||||
return math.NormalizeAngle(dir - 90)
|
||||
end
|
||||
293
garrysmod/addons/tacrp/lua/weapons/tacrp_base/sh_reload.lua
Normal file
293
garrysmod/addons/tacrp/lua/weapons/tacrp_base/sh_reload.lua
Normal file
@@ -0,0 +1,293 @@
|
||||
function SWEP:Reload(force)
|
||||
force = force or false
|
||||
|
||||
if self:GetOwner():IsNPC() then
|
||||
self:NPC_Reload()
|
||||
return
|
||||
end
|
||||
|
||||
if self:GetValue("Firemodes") and self:GetOwner():KeyDown(IN_USE) and self:GetOwner():KeyPressed(IN_RELOAD) and self:GetFiremodeAmount() > 1 and !self:GetSafe() then
|
||||
self:SwitchFiremode()
|
||||
self:EmitSound("tacrp/weapons/pdw/fire_select-1.wav", 75, 100, 1, CHAN_ITEM)
|
||||
return
|
||||
end
|
||||
|
||||
local stop = self:RunHook("Hook_PreReload")
|
||||
if stop then return end
|
||||
|
||||
if !self:GetOwner():KeyPressed(IN_RELOAD) and !force then
|
||||
return
|
||||
end
|
||||
|
||||
if self:StillWaiting(true) then return end
|
||||
|
||||
if self:GetJammed() then
|
||||
local t = self:PlayAnimation("jam", 0.75, true, true)
|
||||
self:GetOwner():DoCustomAnimEvent(PLAYERANIMEVENT_CANCEL_RELOAD, t * 1000)
|
||||
self:SetJammed(false)
|
||||
self:SetCharge(false)
|
||||
return
|
||||
end
|
||||
|
||||
if !self:CanReloadInSprint() and self:GetIsSprinting() then return end
|
||||
if self:GetCapacity() <= 0 then return end
|
||||
if self:Clip1() >= self:GetCapacity() then return end
|
||||
if self:Ammo1() <= 0 and !self:GetInfiniteAmmo() then return end
|
||||
|
||||
self:ToggleBlindFire(TacRP.BLINDFIRE_NONE)
|
||||
|
||||
local mult = self:GetValue("ReloadTimeMult") / TacRP.ConVars["mult_reloadspeed"]:GetFloat()
|
||||
|
||||
local anim = "reload"
|
||||
|
||||
if self:GetValue("ShotgunReload") then
|
||||
anim = "reload_start"
|
||||
else
|
||||
self:SetTimer(self:GetValue("LoadInTime") * mult, function()
|
||||
self:SetLoadedRounds(math.min(self:GetCapacity(), self:Clip1() + self:Ammo1()))
|
||||
self:DoBulletBodygroups()
|
||||
end, "SetLoadedRounds")
|
||||
if self.ReloadUpInTime then
|
||||
self:SetTimer(self.ReloadUpInTime * mult, function()
|
||||
self:RestoreClip(self:GetCapacity())
|
||||
self:SetNthShot(0)
|
||||
self:SetEndReload(true)
|
||||
end, "ReloadUpIn")
|
||||
end
|
||||
end
|
||||
|
||||
local t = self:PlayAnimation(anim, mult, true, true)
|
||||
|
||||
self:GetOwner():DoCustomAnimEvent(PLAYERANIMEVENT_RELOAD, t * 1000)
|
||||
|
||||
if SERVER then
|
||||
self:SetTimer(self.DropMagazineTime * mult, function()
|
||||
self:DropMagazine()
|
||||
end, "DropMagazine")
|
||||
end
|
||||
|
||||
self:SetLoadedRounds(self:Clip1())
|
||||
|
||||
self:SetReloading(true)
|
||||
self:SetEndReload(false)
|
||||
|
||||
self:SetEmptyReload(self:GetValue("ShotgunNoReverseStart") or self:Clip1() == 0)
|
||||
|
||||
self:DoBulletBodygroups()
|
||||
|
||||
self:RunHook("Hook_StartReload")
|
||||
|
||||
self:SetReloadFinishTime(CurTime() + (t * 0.95))
|
||||
end
|
||||
|
||||
function SWEP:DropMagazine()
|
||||
-- if !IsFirstTimePredicted() and !game.SinglePlayer() then return end
|
||||
if self:GetValue("DropMagazineModel") and TacRP.ConVars["dropmagazinemodel"]:GetBool() then
|
||||
local dropamt = math.floor(self:Clip1() / self:GetValue("DropMagazineAmount"))
|
||||
local clip1 = self:Clip1()
|
||||
for i = 1, self:GetValue("DropMagazineAmount") do
|
||||
local mag = ents.Create("TacRP_droppedmag")
|
||||
|
||||
if IsValid(mag) then
|
||||
local bone = "ValveBiped.Bip01_R_Hand"
|
||||
if i == 2 then bone = "ValveBiped.Bip01_L_Hand" end
|
||||
local matrix = self:GetOwner():GetBoneMatrix(self:GetOwner():LookupBone(bone) or -1)
|
||||
local pos, ang
|
||||
if matrix then
|
||||
pos = matrix:GetTranslation()
|
||||
ang = matrix:GetAngles()
|
||||
else
|
||||
pos = self:GetOwner():EyePos() - (self:GetOwner():EyeAngles():Up() * 8)
|
||||
ang = self:GetOwner():EyeAngles()
|
||||
end
|
||||
|
||||
mag:SetPos(pos)
|
||||
mag:SetAngles(ang)
|
||||
mag.Model = self:GetValue("DropMagazineModel")
|
||||
mag.ImpactType = self:GetValue("DropMagazineImpact")
|
||||
mag:SetOwner(self:GetOwner())
|
||||
if clip1 > 0 and TacRP.ConVars["reload_dump"]:GetBool() then
|
||||
local amt = (i == self:GetValue("DropMagazineAmount") and clip1) or dropamt
|
||||
clip1 = clip1 - amt
|
||||
|
||||
if !self:GetInfiniteAmmo() then
|
||||
mag.AmmoType = self:GetAmmoType()
|
||||
mag.AmmoCount = amt
|
||||
end
|
||||
end
|
||||
mag:Spawn()
|
||||
|
||||
local phys = mag:GetPhysicsObject()
|
||||
|
||||
if IsValid(phys) then
|
||||
phys:AddAngleVelocity(Vector(math.Rand(-300, 300), math.Rand(-300, 300), math.Rand(-300, 300)))
|
||||
end
|
||||
end
|
||||
end
|
||||
self:SetClip1(clip1)
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:RestoreClip(amt)
|
||||
local reserve = self:GetInfiniteAmmo() and math.huge or (self:Clip1() + self:Ammo1())
|
||||
|
||||
local lastclip1 = self:Clip1()
|
||||
|
||||
self:SetClip1(math.min(math.min(self:Clip1() + amt, self:GetCapacity()), reserve))
|
||||
|
||||
if !self:GetInfiniteAmmo() then
|
||||
reserve = reserve - self:Clip1()
|
||||
self:GetOwner():SetAmmo(reserve, self.Primary.Ammo)
|
||||
end
|
||||
|
||||
return self:Clip1() - lastclip1
|
||||
end
|
||||
|
||||
function SWEP:Unload(ammotype)
|
||||
self:GetOwner():GiveAmmo(self:Clip1(), ammotype or self.Primary.Ammo)
|
||||
self:SetClip1(0)
|
||||
end
|
||||
|
||||
function SWEP:EndReload()
|
||||
if self:GetValue("ShotgunReload") then
|
||||
local mult = self:GetValue("ReloadTimeMult") / TacRP.ConVars["mult_reloadspeed"]:GetFloat()
|
||||
if self:Clip1() >= self:GetCapacity() or (!self:GetInfiniteAmmo() and self:Ammo1() == 0) or self:GetEndReload() then
|
||||
|
||||
local cancellable = TacRP.ConVars["reload_sg_cancel"]:GetBool() and !self:GetValue("ShotgunFullCancel")
|
||||
local t = 1
|
||||
if !self.ShotgunNoReverseStart and (self:Clip1() == self:GetLoadedRounds() or !self:GetEmptyReload()) then
|
||||
t = self:PlayAnimation("reload_start", -0.75 * mult, !cancellable, true)
|
||||
else
|
||||
t = self:PlayAnimation("reload_finish", mult, !cancellable, true)
|
||||
end
|
||||
self:GetOwner():DoCustomAnimEvent(PLAYERANIMEVENT_RELOAD_END, t * 1000)
|
||||
|
||||
self:SetReloading(false)
|
||||
|
||||
self:SetNthShot(0)
|
||||
|
||||
self:DoBulletBodygroups()
|
||||
|
||||
self:RunHook("Hook_EndReload")
|
||||
else
|
||||
local t = self:PlayAnimation("reload", mult, true)
|
||||
|
||||
local res = self:GetValue("ShotgunThreeload") and
|
||||
math.min(math.min(3, self:GetCapacity() - self:Clip1()), self:GetInfiniteAmmo() and math.huge or self:Ammo1())
|
||||
or 1
|
||||
|
||||
local delay = self:GetValue("ShotgunUpInTime")
|
||||
for i = 1, res do
|
||||
self:SetTimer(t * delay * ((i - 1) / 3) + 0.22, function()
|
||||
self:GetOwner():DoCustomAnimEvent(PLAYERANIMEVENT_RELOAD_LOOP, t * 1000)
|
||||
self:RestoreClip(1)
|
||||
self:RunHook("Hook_InsertReload", res)
|
||||
end, "ShotgunRestoreClip")
|
||||
end
|
||||
|
||||
self:SetTimer(t * self:GetValue("ShotgunLoadInTime") * (res / 3), function()
|
||||
self:SetLoadedRounds(self:GetLoadedRounds() + res)
|
||||
self:DoBulletBodygroups()
|
||||
end, "SetLoadedRounds")
|
||||
|
||||
self:SetReloadFinishTime(CurTime() + (t * delay * (res / 3)) + 0.05)
|
||||
|
||||
self:DoBulletBodygroups()
|
||||
end
|
||||
else
|
||||
if !self.ReloadUpInTime then
|
||||
self:RestoreClip(self:GetCapacity())
|
||||
self:SetNthShot(0)
|
||||
end
|
||||
self:SetReloading(false)
|
||||
self:SetEndReload(false)
|
||||
|
||||
self:RunHook("Hook_EndReload")
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:CancelReload(doanims, keeptime)
|
||||
if self:GetReloading() then
|
||||
|
||||
self:RunHook("Hook_CancelReload")
|
||||
|
||||
local stop = false
|
||||
|
||||
if doanims then
|
||||
if self:GetValue("ShotgunReload") then
|
||||
local mult = self:GetValue("ReloadTimeMult") / TacRP.ConVars["mult_reloadspeed"]:GetFloat()
|
||||
local t = 1
|
||||
if self.CurrentAnimation == "reload_start" and self.ShotgunReloadCompleteStart then
|
||||
self:SetEndReload(true)
|
||||
elseif self:Clip1() == self:GetLoadedRounds() and !self.ShotgunNoReverseStart then
|
||||
t = self:PlayAnimation("reload_start", -0.75 * mult, true, true)
|
||||
stop = true
|
||||
else
|
||||
t = self:PlayAnimation("reload_finish", mult, true, true)
|
||||
stop = true
|
||||
end
|
||||
self:GetOwner():DoCustomAnimEvent(PLAYERANIMEVENT_RELOAD_END, t * 1000)
|
||||
else
|
||||
self:Idle()
|
||||
stop = true
|
||||
end
|
||||
else
|
||||
stop = true
|
||||
end
|
||||
|
||||
if stop then
|
||||
self:KillTimer("SetLoadedRounds")
|
||||
self:KillTimer("ShotgunRestoreClip")
|
||||
self:KillTimer("ReloadUpIn")
|
||||
self:KillTimer("DropMagazine")
|
||||
self:SetReloading(false)
|
||||
self:SetEndReload(false)
|
||||
self:SetNthShot(0)
|
||||
self:DoBulletBodygroups()
|
||||
if !keeptime then
|
||||
self:SetReloadFinishTime(0)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:ThinkReload()
|
||||
if self:GetReloading() and self:GetReloadFinishTime() < CurTime() then
|
||||
self:EndReload()
|
||||
end
|
||||
end
|
||||
|
||||
local launcher_ammo = {
|
||||
["smg1_grenade"] = true,
|
||||
["rpg_round"] = true,
|
||||
}
|
||||
function SWEP:GetInfiniteAmmo()
|
||||
local ammo = string.lower(self:GetAmmoType())
|
||||
if launcher_ammo[ammo] then
|
||||
return TacRP.ConVars["infinitelaunchers"]:GetBool() or self:GetValue("InfiniteAmmo")
|
||||
else
|
||||
return TacRP.ConVars["infiniteammo"]:GetBool() or self:GetValue("InfiniteAmmo")
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:GetCapacity(base)
|
||||
if base then
|
||||
return self:GetBaseValue("ClipSize")
|
||||
else
|
||||
return self:GetValue("ClipSize")
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:GetAmmoType(base)
|
||||
local valfunc = base and self.GetBaseValue or self.GetValue
|
||||
if self.Ammo_Expanded and TacRP.ConVars["expandedammotypes"]:GetBool() then
|
||||
return valfunc(self, "Ammo_Expanded") or valfunc(self, "Ammo")
|
||||
else
|
||||
return valfunc(self, "Ammo")
|
||||
end
|
||||
end
|
||||
|
||||
-- Override to disable auto-reload for one reason or another.
|
||||
function SWEP:ShouldAutoReload()
|
||||
return true
|
||||
end
|
||||
398
garrysmod/addons/tacrp/lua/weapons/tacrp_base/sh_scope.lua
Normal file
398
garrysmod/addons/tacrp/lua/weapons/tacrp_base/sh_scope.lua
Normal file
@@ -0,0 +1,398 @@
|
||||
local blur = Material("pp/blurscreen")
|
||||
local function drawBlurAt(x, y, w, h, amount, passes, reverse)
|
||||
-- Intensity of the blur.
|
||||
amount = amount or 5
|
||||
|
||||
surface.SetMaterial(blur)
|
||||
surface.SetDrawColor(color_white)
|
||||
|
||||
local scrW, scrH = ScrW(), ScrH()
|
||||
local x2, y2 = x / scrW, y / scrH
|
||||
local w2, h2 = (x + w) / scrW, (y + h) / scrH
|
||||
|
||||
for i = -(passes or 0.2), 1, 0.2 do
|
||||
if reverse then
|
||||
blur:SetFloat("$blur", i * -1 * amount)
|
||||
else
|
||||
blur:SetFloat("$blur", i * amount)
|
||||
end
|
||||
blur:Recompute()
|
||||
|
||||
render.UpdateScreenEffectTexture()
|
||||
surface.DrawTexturedRectUV(x, y, w, h, x2, y2, w2, h2)
|
||||
end
|
||||
end
|
||||
|
||||
local peekzoom = 1.2
|
||||
|
||||
function SWEP:ScopeToggle(setlevel)
|
||||
if (setlevel or 0) > 0 and (!self:GetValue("Scope") or self:GetPrimedGrenade()) then return end
|
||||
-- if setlevel and setlevel > 0 and self:GetAnimLockTime() > CurTime() or (!setlevel and self:GetAnimLockTime() > CurTime()) then return end
|
||||
-- if (setlevel and setlevel > 0 and self:GetReloading()) or (!setlevel and self:GetReloading()) then return end
|
||||
|
||||
local level = self:GetScopeLevel()
|
||||
local oldlevel = level
|
||||
|
||||
level = setlevel or (level + 1)
|
||||
|
||||
if level > self:GetValue("ScopeLevels") then
|
||||
level = self:GetValue("ScopeLevels")
|
||||
end
|
||||
|
||||
if self:GetCustomize() or self:GetLastMeleeTime() + 1 > CurTime() then -- self:SprintLock(true)
|
||||
level = 0
|
||||
end
|
||||
|
||||
if self:GetIsSprinting() and level > 0 then
|
||||
if self:GetOwner():GetInfoNum("tacrp_aim_cancels_sprint", 0) > 0 and self:CanStopSprinting() then
|
||||
self:GetOwner().TacRP_SprintBlock = true
|
||||
else
|
||||
level = 0
|
||||
end
|
||||
end
|
||||
|
||||
if self:DoOldSchoolScopeBehavior() then
|
||||
level = 0
|
||||
end
|
||||
|
||||
if level == self:GetScopeLevel() then return end
|
||||
|
||||
self:SetScopeLevel(level)
|
||||
|
||||
if level > 0 then
|
||||
self:ToggleBlindFire(TacRP.BLINDFIRE_NONE)
|
||||
end
|
||||
|
||||
if oldlevel == 0 or level == 0 then
|
||||
self:SetLastScopeTime(CurTime())
|
||||
end
|
||||
|
||||
if self:GetValue("AlwaysPeek") then
|
||||
self:SetPeeking(true)
|
||||
end
|
||||
|
||||
-- HACK: In singleplayer, SWEP:Think is called on client but IsFirstTimePredicted is NEVER true.
|
||||
-- This causes ScopeToggle to NOT be called on client in singleplayer...
|
||||
-- GenerateAutoSight needs to run clientside or scopes will break. Good old CallOnClient it is.
|
||||
|
||||
if SERVER and game.SinglePlayer() then
|
||||
self:CallOnClient("GenerateAutoSight")
|
||||
elseif CLIENT and (IsFirstTimePredicted() or game.SinglePlayer()) then
|
||||
self:GenerateAutoSight()
|
||||
self.LastHintLife = CurTime()
|
||||
end
|
||||
|
||||
self:EmitSound(self:GetValue("Sound_ScopeIn"), 75, 100, 1, CHAN_ITEM)
|
||||
|
||||
self:SetShouldHoldType()
|
||||
|
||||
self:RunHook("Hook_PostScopeToggle", setlevel)
|
||||
end
|
||||
|
||||
function SWEP:GetShouldFOV(ignorepeek)
|
||||
local base = 90
|
||||
if !ignorepeek and self:GetPeeking() then
|
||||
return base / peekzoom
|
||||
else
|
||||
return base / self:GetMagnification()
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:IsInScope()
|
||||
local sightdelta = self:Curve(self:GetSightDelta())
|
||||
|
||||
return (SERVER or !self:GetPeeking()) and !self:GetSafe() and ((self:GetScopeLevel() > 0 and sightdelta > 0.5) or (sightdelta > 0.9))
|
||||
end
|
||||
|
||||
function SWEP:DoScope()
|
||||
local h = ScrH()
|
||||
local w = ScrW()
|
||||
if self:IsInScope() then
|
||||
local img = self:GetValue("ScopeOverlay")
|
||||
if img then
|
||||
-- assume players have a screen that is wider than it is tall because... that's stupid
|
||||
|
||||
local pos = self:GetOwner():EyePos()
|
||||
|
||||
pos = pos + self:GetShootDir():Forward() * 9000
|
||||
|
||||
local toscreen = pos:ToScreen()
|
||||
|
||||
local x = toscreen.x
|
||||
local y = toscreen.y
|
||||
|
||||
local ss = math.Round(h * (self:GetValue("ScopeOverlaySize") or 1))
|
||||
local sx = x - (ss / 2)
|
||||
local sy = y - (ss / 2)
|
||||
|
||||
-- local shakey = math.min(cross * 35, 3)
|
||||
|
||||
-- sx = sx + math.Round(math.Rand(-shakey, shakey))
|
||||
-- sy = sy + math.Round(math.Rand(-shakey, shakey))
|
||||
|
||||
-- local int = self:CheckFlashlightPointing()
|
||||
-- if int > 0 then
|
||||
-- surface.SetDrawColor(255, 255, 255, int * 250)
|
||||
-- surface.DrawRect(0, 0, w, h)
|
||||
-- end
|
||||
|
||||
surface.SetMaterial(img)
|
||||
surface.SetDrawColor(255, 255, 255, 255)
|
||||
surface.DrawTexturedRect(sx, sy, ss, ss)
|
||||
|
||||
surface.SetDrawColor(0, 0, 0)
|
||||
surface.DrawRect(0, 0, w, sy)
|
||||
surface.DrawRect(0, sy + ss, w, h - sy)
|
||||
|
||||
surface.DrawRect(0, 0, sx, h)
|
||||
surface.DrawRect(sx + ss, 0, w - sx, h)
|
||||
|
||||
if self:GetReloading() then
|
||||
drawBlurAt(0, 0, w, h, 1, 1)
|
||||
end
|
||||
|
||||
-- if int > 0 then
|
||||
-- surface.SetDrawColor(255, 255, 255, int * 25)
|
||||
-- surface.DrawRect(0, 0, w, h)
|
||||
-- end
|
||||
|
||||
if self:GetValue("ScopeDraw") then
|
||||
self:GetValue("ScopeDraw")(self)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local blur_hook = self:RunHook("Hook_BlurScope")
|
||||
if blur_hook then
|
||||
if !istable(blur_hook) then blur_hook = {1, 1} end
|
||||
drawBlurAt(0, 0, w, h, blur_hook[0], blur_hook[1])
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:GetSightDelta()
|
||||
return self:GetSightAmount()
|
||||
end
|
||||
|
||||
function SWEP:SetSightDelta(d)
|
||||
self:SetSightAmount(d)
|
||||
end
|
||||
|
||||
function SWEP:CanSight()
|
||||
if self:GetReloading() and !TacRP.ConVars["ads_reload"]:GetBool() then return false end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function SWEP:ThinkSights()
|
||||
if !IsValid(self:GetOwner()) then return end
|
||||
|
||||
local ftp = IsFirstTimePredicted()
|
||||
local ftsp = IsFirstTimePredicted() or !game.SinglePlayer()
|
||||
|
||||
if self:GetOwner():KeyDown(IN_USE) and self:GetOwner():KeyPressed(IN_ATTACK2) and ftsp then
|
||||
self:ToggleSafety()
|
||||
return
|
||||
end
|
||||
|
||||
if ftp and self:GetValue("Bipod") and self:GetOwner():KeyPressed(IN_ATTACK2)
|
||||
and !self:GetInBipod() and self:CanBipod() and ftsp then
|
||||
self:EnterBipod()
|
||||
end
|
||||
|
||||
local FT = FrameTime()
|
||||
|
||||
local sighted = self:GetScopeLevel() > 0
|
||||
|
||||
local amt = self:GetSightAmount()
|
||||
|
||||
local adst = self:GetAimDownSightsTime()
|
||||
|
||||
if ftp or game.SinglePlayer() then
|
||||
if sighted then
|
||||
if self:GetSprintLockTime() > CurTime() then
|
||||
adst = adst + self:GetSprintToFireTime()
|
||||
end
|
||||
amt = math.Approach(amt, 1, FT / adst)
|
||||
else
|
||||
amt = math.Approach(amt, 0, FT / adst)
|
||||
end
|
||||
end
|
||||
|
||||
self:SetSightDelta(amt)
|
||||
|
||||
if self:GetSafe() then return end
|
||||
|
||||
if CLIENT then
|
||||
self:ThinkPeek()
|
||||
end
|
||||
local toggle = self:GetOwner():GetInfoNum("tacrp_toggleaim", 0) == 1
|
||||
local press, down = self:GetOwner():KeyPressed(IN_ATTACK2), self:GetOwner():KeyDown(IN_ATTACK2)
|
||||
|
||||
if (!self:GetValue("Scope") or self:DoOldSchoolScopeBehavior()) and !self.NoSecondaryMelee and down then
|
||||
self:Melee()
|
||||
elseif sighted and ((toggle and press and ftp) or (!toggle and !down)) then
|
||||
self:ScopeToggle(0)
|
||||
elseif !sighted and ((toggle and press and ftp) or (!toggle and down)) and self:CanSight() then
|
||||
self:ScopeToggle(1)
|
||||
elseif sighted and !self:CanSight() then
|
||||
self:ScopeToggle(0)
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:GetMagnification()
|
||||
local mag = 1
|
||||
|
||||
local level = self:GetScopeLevel()
|
||||
|
||||
if level > 0 then
|
||||
if self:GetPeeking() then
|
||||
return peekzoom
|
||||
end
|
||||
mag = 90 / self:GetValue("ScopeFOV")
|
||||
mag = Lerp(level / self:GetValue("ScopeLevels"), 1, mag)
|
||||
|
||||
mag = self:RunHook("Hook_ModifyMagnification") or mag
|
||||
end
|
||||
|
||||
if (mag <= 0) then
|
||||
return 0.001 -- just in case
|
||||
end
|
||||
|
||||
return mag
|
||||
end
|
||||
|
||||
function SWEP:AdjustMouseSensitivity()
|
||||
local mag = self:GetMagnification()
|
||||
local sensmult = GetConVar("tacrp_aimsens"):GetFloat()
|
||||
-- local aa = GetConVar("tacrp_aimassist")
|
||||
-- local aac = GetConVar("tacrp_aimassist_cl")
|
||||
-- local aai = GetConVar("tacrp_aimassist_intensity")
|
||||
-- local aams = GetConVar("tacrp_aimassist_multsens")
|
||||
|
||||
-- if self:GetOwner().tacrp_AATarget != nil and aa:GetBool() and aac:GetBool()) then
|
||||
-- aamult = aams:GetFloat() / aai:GetFloat()
|
||||
-- else
|
||||
-- aamult = 1
|
||||
-- end
|
||||
|
||||
if mag > 1 then
|
||||
return 1 / mag * math.Clamp(sensmult, 0.1, 1)
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:ThinkPeek()
|
||||
end
|
||||
|
||||
function SWEP:GetCCIP(pos, ang)
|
||||
-- get calculated point of impact
|
||||
|
||||
local sp, sa = self:GetMuzzleOrigin(), self:GetShootDir()
|
||||
|
||||
pos = pos or sp
|
||||
ang = ang or sa
|
||||
|
||||
local v = self:GetValue("MuzzleVelocity")
|
||||
local g = Vector(0, 0, -600)
|
||||
local d = 1
|
||||
local h = 0
|
||||
|
||||
if self:GetValue("ShootEnt") then
|
||||
v = self:GetValue("ShootEntForce")
|
||||
d = 0
|
||||
g = physenv.GetGravity()
|
||||
h = 4
|
||||
end
|
||||
|
||||
local vel = ang:Forward() * v
|
||||
local maxiter = 100
|
||||
local timestep = 1 / 15
|
||||
local gravity = timestep * g
|
||||
|
||||
local steps = {}
|
||||
|
||||
for i = 1, maxiter do
|
||||
local dir = vel:GetNormalized()
|
||||
local spd = vel:Length() * timestep
|
||||
local drag = d * spd * spd * (1 / 150000)
|
||||
|
||||
if spd <= 0.001 then return nil end
|
||||
|
||||
local newpos = pos + (vel * timestep)
|
||||
local newvel = vel - (dir * drag) + gravity
|
||||
|
||||
local tr
|
||||
if h > 0 then
|
||||
tr = util.TraceHull({
|
||||
start = pos,
|
||||
endpos = newpos,
|
||||
filter = self:GetOwner(),
|
||||
mask = MASK_SHOT,
|
||||
mins = Vector(-h, -h, -h),
|
||||
maxs = Vector(h, h, h),
|
||||
})
|
||||
else
|
||||
tr = util.TraceLine({
|
||||
start = pos,
|
||||
endpos = newpos,
|
||||
filter = self:GetOwner(),
|
||||
mask = MASK_SHOT
|
||||
})
|
||||
end
|
||||
table.insert(steps, 0, tr.HitPos)
|
||||
|
||||
if tr.Hit then
|
||||
debugoverlay.Sphere(tr.HitPos, 8, 0.25, color_white, true)
|
||||
return tr, i * timestep, steps
|
||||
else
|
||||
pos = newpos
|
||||
vel = newvel
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
function SWEP:GetCorVal()
|
||||
local vmfov = self.ViewModelFOV
|
||||
local fov = self:GetShouldFOV()
|
||||
|
||||
return vmfov / (fov * 1.33333)
|
||||
end
|
||||
|
||||
function SWEP:HasOptic()
|
||||
return self:GetValue("Scope") and (self:GetValue("ScopeOverlay") or self:GetValue("Holosight"))
|
||||
end
|
||||
|
||||
function SWEP:DoOldSchoolScopeBehavior()
|
||||
return (TacRP.ConVars["oldschool"]:GetBool() or TacRP.GetBalanceMode() == TacRP.BALANCE_OLDSCHOOL)
|
||||
and !self:HasOptic()
|
||||
end
|
||||
|
||||
|
||||
function SWEP:GetAimDownSightsTime(base)
|
||||
if base then
|
||||
return self:GetBaseValue("AimDownSightsTime") * TacRP.ConVars["mult_aimdownsights"]:GetFloat()
|
||||
else
|
||||
return self:GetValue("AimDownSightsTime") * TacRP.ConVars["mult_aimdownsights"]:GetFloat()
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:GetSprintToFireTime(base)
|
||||
if base then
|
||||
return self:GetBaseValue("SprintToFireTime") * TacRP.ConVars["mult_sprinttofire"]:GetFloat()
|
||||
else
|
||||
return self:GetValue("SprintToFireTime") * TacRP.ConVars["mult_sprinttofire"]:GetFloat()
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:GetPeeking()
|
||||
return (!self.InversePeek and self:GetNWPeeking()) or (self.InversePeek and !self:GetNWPeeking())
|
||||
end
|
||||
|
||||
function SWEP:SetPeeking(b)
|
||||
if self.InversePeek then
|
||||
self:SetNWPeeking(!b)
|
||||
else
|
||||
self:SetNWPeeking(b)
|
||||
end
|
||||
end
|
||||
872
garrysmod/addons/tacrp/lua/weapons/tacrp_base/sh_shoot.lua
Normal file
872
garrysmod/addons/tacrp/lua/weapons/tacrp_base/sh_shoot.lua
Normal file
@@ -0,0 +1,872 @@
|
||||
function SWEP:StillWaiting(cust, reload)
|
||||
if self:GetNextPrimaryFire() > CurTime() then return true end
|
||||
if self:GetNextSecondaryFire() > CurTime() and (!reload or !(reload and self:GetReloading())) then return true end
|
||||
if self:GetAnimLockTime() > CurTime() and (!reload or !(reload and self:GetReloading())) then return true end
|
||||
if !cust and self:GetBlindFireFinishTime() > CurTime() then return true end
|
||||
if !cust and self:GetCustomize() then return true end
|
||||
if self:GetPrimedGrenade() then return true end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
function SWEP:SprintLock(shoot)
|
||||
if self:GetSprintLockTime() > CurTime() or self:GetIsSprinting() or self:ShouldLowerWeapon() then
|
||||
return true
|
||||
end
|
||||
|
||||
if shoot and self:DoForceSightsBehavior() and (self:GetSprintDelta() > 0 or self:GetSightDelta() < 0.75) and !self:GetBlindFire() then
|
||||
return true
|
||||
end
|
||||
|
||||
if self:GetValue("CannotHipFire") and self:GetSightAmount() < 1 and !self:GetBlindFire() then
|
||||
return true
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
local function anglerotate(main, off)
|
||||
local forward, up, right = main:Forward(), main:Up(), main:Right()
|
||||
|
||||
main:RotateAroundAxis(right, off.p)
|
||||
main:RotateAroundAxis(up, off.y)
|
||||
main:RotateAroundAxis(forward, off.r)
|
||||
|
||||
return main
|
||||
end
|
||||
|
||||
function SWEP:PrimaryAttack()
|
||||
if self:GetOwner():IsNPC() then
|
||||
self:NPC_PrimaryAttack()
|
||||
return
|
||||
end
|
||||
|
||||
if self:GetValue("Melee") and self:GetOwner():KeyDown(IN_USE) and !(self:GetValue("RunawayBurst") and self:GetBurstCount() > 0) then
|
||||
-- self.Primary.Automatic = false
|
||||
self:SetSafe(false)
|
||||
self:Melee()
|
||||
return
|
||||
end
|
||||
|
||||
-- if self:GetJammed() then return end
|
||||
if self:GetCurrentFiremode() < 0 and self:GetBurstCount() >= -self:GetCurrentFiremode() then return end
|
||||
|
||||
if self:GetReloading() and self:GetValue("ShotgunReload") then
|
||||
if TacRP.ConVars["reload_sg_cancel"]:GetBool() and !self:GetValue("ShotgunFullCancel") then
|
||||
self:CancelReload(false)
|
||||
self:Idle()
|
||||
else
|
||||
self:CancelReload(true)
|
||||
end
|
||||
end
|
||||
|
||||
if self:SprintLock(true) then return end
|
||||
if DarkRP and self:GetNWBool("TacRP_PoliceBiocode") and !self:GetOwner():isCP() then return end
|
||||
if self:GetSafe() and !self:GetReloading() then self:ToggleSafety(false) return end
|
||||
if self:StillWaiting() then return end
|
||||
|
||||
if self:GetValue("RequireLockOn") and !(IsValid(self:GetLockOnEntity()) and CurTime() > self:GetLockOnStartTime() + self:GetValue("LockOnTime")) then return end
|
||||
|
||||
if self:Clip1() < self:GetValue("AmmoPerShot") or self:GetJammed() then
|
||||
local ret = self:RunHook("Hook_PreDryfire")
|
||||
if ret != true then
|
||||
self.Primary.Automatic = false
|
||||
if self:GetBlindFire() then
|
||||
self:PlayAnimation("blind_dryfire")
|
||||
else
|
||||
self:PlayAnimation("dryfire")
|
||||
end
|
||||
self:EmitSound(self:GetValue("Sound_DryFire"), 75, 100, 1, CHAN_ITEM)
|
||||
self:SetBurstCount(0)
|
||||
self:SetNextPrimaryFire(CurTime() + 0.2)
|
||||
self:RunHook("Hook_PostDryfire")
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
if util.SharedRandom("tacRP_shootChance", 0, 1) <= self:GetJamChance(false) then
|
||||
local ret = self:RunHook("Hook_PreJam")
|
||||
if ret != true then
|
||||
if self:GetBurstCount() == 0 then
|
||||
self.Primary.Automatic = false
|
||||
end
|
||||
if self:GetBlindFire() then
|
||||
self:PlayAnimation("blind_dryfire")
|
||||
else
|
||||
self:PlayAnimation("dryfire")
|
||||
end
|
||||
self:EmitSound(self:GetValue("Sound_Jam"), 75, 100, 1, CHAN_ITEM)
|
||||
self:SetBurstCount(0)
|
||||
self:SetPatternCount(0)
|
||||
self:SetNextPrimaryFire(CurTime() + self:GetValue("JamWaitTime"))
|
||||
self:SetNextSecondaryFire(CurTime() + self:GetValue("JamWaitTime"))
|
||||
if self:GetValue("JamTakesRound") then
|
||||
self:TakePrimaryAmmo(self:GetValue("AmmoPerShot"))
|
||||
end
|
||||
if self:Clip1() > 0 and !self:GetValue("JamSkipFix") then
|
||||
self:SetJammed(true)
|
||||
end
|
||||
self:RunHook("Hook_PostJam")
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
self:SetBaseSettings()
|
||||
|
||||
local stop = self:RunHook("Hook_PreShoot")
|
||||
if stop then return end
|
||||
|
||||
local seq = "fire"
|
||||
|
||||
local idle = true
|
||||
|
||||
local mult = self:GetValue("ShootTimeMult")
|
||||
|
||||
if self:GetValue("LastShot") and self:Clip1() == self:GetValue("AmmoPerShot") then
|
||||
seq = self:TranslateSequence("lastshot")
|
||||
idle = false
|
||||
end
|
||||
|
||||
if self:GetBlindFire() then
|
||||
seq = "blind_" .. seq
|
||||
end
|
||||
|
||||
if self:GetValue("Akimbo") and !self:GetBlindFire() then
|
||||
if self:GetNthShot() % 2 == 0 then
|
||||
seq = "shoot_left"
|
||||
else
|
||||
seq = "shoot_right"
|
||||
end
|
||||
|
||||
if self:GetValue("LastShot") then
|
||||
if self:Clip1() == self:GetValue("AmmoPerShot") then
|
||||
seq = seq .. "_lastshot"
|
||||
elseif self:Clip1() == self:GetValue("AmmoPerShot") * 2 then
|
||||
seq = seq .. "_second_2_lastshot"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local prociron = self:DoProceduralIrons()
|
||||
if self:GetScopeLevel() > 0 and (prociron or self:HasSequence(seq .. "_iron")) and !self:GetPeeking() then
|
||||
if prociron then
|
||||
if self:GetValue("LastShot") and self:Clip1() == self:GetValue("AmmoPerShot") then
|
||||
self:PlayAnimation(self:TranslateSequence("dryfire"), mult, false)
|
||||
end
|
||||
self:SetLastProceduralFireTime(CurTime())
|
||||
else
|
||||
self:PlayAnimation(seq .. "_iron", mult, false, idle)
|
||||
end
|
||||
elseif self:HasSequence(seq .. "1") then
|
||||
local seq1 = seq .. "1"
|
||||
if !self:GetInBipod() and (self:GetScopeLevel() < 1 or self:GetPeeking()) then
|
||||
seq1 = seq .. tostring(self:GetBurstCount() + 1)
|
||||
end
|
||||
|
||||
if self:HasSequence(seq1) then
|
||||
self:PlayAnimation(seq1, mult, false, idle)
|
||||
elseif self:GetScopeLevel() < 1 or self:GetPeeking() then
|
||||
for i = self:GetBurstCount() + 1, 1, -1 do
|
||||
local seq2 = seq .. tostring(i)
|
||||
if self:HasSequence(seq2) then
|
||||
self:PlayAnimation(seq2, mult, false, idle)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
self:PlayAnimation(seq, mult, false, idle)
|
||||
end
|
||||
|
||||
self:GetOwner():DoCustomAnimEvent(PLAYERANIMEVENT_ATTACK_PRIMARY, 0)
|
||||
|
||||
local pvar = self:GetValue("ShootPitchVariance")
|
||||
|
||||
local sshoot = self:GetValue("Sound_Shoot")
|
||||
|
||||
if self:GetValue("Silencer") then
|
||||
sshoot = self:GetValue("Sound_Shoot_Silenced")
|
||||
end
|
||||
|
||||
if istable(sshoot) then
|
||||
sshoot = table.Random(sshoot)
|
||||
end
|
||||
|
||||
if self:GetValue("Sound_ShootAdd") then
|
||||
self:EmitSound(self:GetValue("Sound_ShootAdd"), self:GetValue("Vol_Shoot"), self:GetValue("Pitch_Shoot") + util.SharedRandom("TacRP_sshoot", -pvar, pvar), self:GetValue("Loudness_Shoot"), CHAN_BODY)
|
||||
end
|
||||
|
||||
-- if we die from suicide, EmitSound will not play, so do this instead
|
||||
if self:GetBlindFireMode() == TacRP.BLINDFIRE_KYS then
|
||||
if SERVER then
|
||||
sound.Play(sshoot, self:GetMuzzleOrigin(), self:GetValue("Vol_Shoot"), self:GetValue("Pitch_Shoot") + util.SharedRandom("TacRP_sshoot", -pvar, pvar), self:GetValue("Loudness_Shoot"))
|
||||
end
|
||||
else
|
||||
self:EmitSound(sshoot, self:GetValue("Vol_Shoot"), self:GetValue("Pitch_Shoot") + util.SharedRandom("TacRP_sshoot", -pvar, pvar), self:GetValue("Loudness_Shoot"), CHAN_WEAPON)
|
||||
end
|
||||
|
||||
local delay = 60 / self:GetRPM()
|
||||
-- local delay = 60 / self:GetRPM()
|
||||
|
||||
local curatt = self:GetNextPrimaryFire()
|
||||
local diff = CurTime() - curatt
|
||||
|
||||
if diff > engine.TickInterval() or diff < 0 then
|
||||
curatt = CurTime()
|
||||
end
|
||||
|
||||
self:SetNthShot(self:GetNthShot() + 1)
|
||||
|
||||
local ejectdelay = self:GetValue("EjectDelay")
|
||||
if ejectdelay == 0 then
|
||||
self:DoEject()
|
||||
else
|
||||
self:SetTimer(ejectdelay, function()
|
||||
self:DoEject()
|
||||
end)
|
||||
end
|
||||
|
||||
self:DoEffects()
|
||||
|
||||
if self:GetValue("EffectsDoubled") then
|
||||
-- self:SetNthShot(self:GetNthShot() + 1)
|
||||
self:DoEffects(true)
|
||||
if ejectdelay == 0 then
|
||||
self:DoEject(true)
|
||||
else
|
||||
self:SetTimer(ejectdelay, function()
|
||||
self:DoEject(true)
|
||||
end)
|
||||
end
|
||||
-- self:SetNthShot(self:GetNthShot() - 1)
|
||||
end
|
||||
|
||||
local num = self:GetValue("Num")
|
||||
local fixed_spread = self:IsShotgun() and TacRP.ConVars["fixedspread"]:GetBool()
|
||||
local pellet_spread = self:IsShotgun() and self:GetValue("ShotgunPelletSpread") > 0 and TacRP.ConVars["pelletspread"]:GetBool()
|
||||
|
||||
local spread = self:GetSpread()
|
||||
|
||||
local dir = self:GetShootDir()
|
||||
|
||||
local tr = self:GetValue("TracerNum")
|
||||
|
||||
local shootent = self:GetValue("ShootEnt")
|
||||
|
||||
if IsFirstTimePredicted() then
|
||||
|
||||
local hitscan = !TacRP.ConVars["physbullet"]:GetBool()
|
||||
|
||||
local dist = 100000
|
||||
|
||||
-- If the bullet is going to hit something very close in front, use hitscan bullets instead
|
||||
-- This uses the aim direction without random spread, which may result in hitscan bullets in distances where it shouldn't be.
|
||||
if !hitscan and (game.SinglePlayer() or !TacRP.ConVars["client_damage"]:GetBool()) then
|
||||
dist = math.max(self:GetValue("MuzzleVelocity"), 15000) * engine.TickInterval()
|
||||
* game.GetTimeScale()
|
||||
* (num == 1 and 2 or 1) * (game.IsDedicated() and 1 or 2)
|
||||
local threshold = dir:Forward() * dist
|
||||
local inst_tr = util.TraceLine({
|
||||
start = self:GetMuzzleOrigin(),
|
||||
endpos = self:GetMuzzleOrigin() + threshold,
|
||||
mask = MASK_SHOT,
|
||||
filter = {self:GetOwner(), self:GetOwner():GetVehicle(), self},
|
||||
})
|
||||
if inst_tr.Hit and !inst_tr.HitSky then
|
||||
hitscan = true
|
||||
end
|
||||
-- debugoverlay.Line(self:GetMuzzleOrigin(), self:GetMuzzleOrigin() + threshold, 2, hitscan and Color(255, 0, 255) or Color(255, 255, 255))
|
||||
end
|
||||
|
||||
-- Firebullets already does this so this is just placebo
|
||||
-- self:GetOwner():LagCompensation(true)
|
||||
|
||||
if shootent or !hitscan or fixed_spread then
|
||||
local d = math.random() -- self:GetNthShot() / self:GetCapacity()
|
||||
for i = 1, num do
|
||||
local new_dir = Angle(dir)
|
||||
if fixed_spread then
|
||||
local sgp_x, sgp_y = self:GetShotgunPattern(i, d)
|
||||
// new_dir:Add(Angle(sgp_x, sgp_y, 0) * 36 * 1.4142135623730)
|
||||
new_dir = anglerotate(new_dir, Angle(sgp_x, sgp_y, 0) * 36 * 1.4142135623730)
|
||||
if pellet_spread then
|
||||
// new_dir:Add(self:RandomSpread(self:GetValue("ShotgunPelletSpread"), i))
|
||||
new_dir = anglerotate(new_dir, self:RandomSpread(self:GetValue("ShotgunPelletSpread"), i))
|
||||
end
|
||||
else
|
||||
// new_dir:Add(self:RandomSpread(spread, i))
|
||||
new_dir = anglerotate(new_dir, self:RandomSpread(spread, i))
|
||||
end
|
||||
|
||||
if shootent then
|
||||
self:ShootRocket(new_dir)
|
||||
elseif hitscan then
|
||||
self:GetOwner():FireBullets({
|
||||
Damage = self:GetValue("Damage_Max"),
|
||||
Force = 8,
|
||||
Tracer = tr,
|
||||
TracerName = "tacrp_tracer",
|
||||
Num = 1,
|
||||
Dir = new_dir:Forward(),
|
||||
Src = self:GetMuzzleOrigin(),
|
||||
Spread = Vector(),
|
||||
IgnoreEntity = self:GetOwner():GetVehicle(),
|
||||
Distance = dist,
|
||||
HullSize = (self:IsShotgun() and i % 2 == 0) and TacRP.ShotgunHullSize or 0,
|
||||
Callback = function(att, btr, dmg)
|
||||
local range = (btr.HitPos - btr.StartPos):Length()
|
||||
|
||||
self:AfterShotFunction(btr, dmg, range, self:GetValue("Penetration"), {})
|
||||
-- if SERVER then
|
||||
-- debugoverlay.Cross(btr.HitPos, 4, 5, Color(255, 0, 0), false)
|
||||
-- else
|
||||
-- debugoverlay.Cross(btr.HitPos, 4, 5, Color(255, 255, 255), false)
|
||||
-- end
|
||||
end
|
||||
})
|
||||
else
|
||||
TacRP:ShootPhysBullet(self, self:GetMuzzleOrigin(), new_dir:Forward() * self:GetValue("MuzzleVelocity"),
|
||||
{HullSize = (self:IsShotgun() and i % 2 == 0) and TacRP.ShotgunHullSize or 0,})
|
||||
end
|
||||
end
|
||||
else
|
||||
local new_dir = Angle(dir)
|
||||
local new_spread = spread
|
||||
-- if pellet_spread then
|
||||
-- new_spread = self:GetValue("ShotgunPelletSpread")
|
||||
-- new_dir:Add(self:RandomSpread(spread, 0))
|
||||
-- end
|
||||
|
||||
-- Try to use Num in FireBullets if at all possible, as this is more performant and better for damage calc compatibility
|
||||
-- Also it generates nice big numbers in various hit number addons instead of a buncha small ones.
|
||||
self:GetOwner():FireBullets({
|
||||
Damage = self:GetValue("Damage_Max"),
|
||||
Force = 8,
|
||||
Tracer = tr,
|
||||
TracerName = "tacrp_tracer",
|
||||
Num = num,
|
||||
Dir = new_dir:Forward(),
|
||||
Src = self:GetMuzzleOrigin(),
|
||||
Spread = Vector(new_spread, new_spread, 0),
|
||||
IgnoreEntity = self:GetOwner():GetVehicle(),
|
||||
Distance = dist,
|
||||
Callback = function(att, btr, dmg)
|
||||
local range = (btr.HitPos - btr.StartPos):Length()
|
||||
|
||||
if IsValid(btr.Entity) and (!game.SinglePlayer() and TacRP.ConVars["client_damage"]:GetBool()) then
|
||||
if CLIENT then
|
||||
net.Start("tacrp_clientdamage")
|
||||
net.WriteEntity(self)
|
||||
net.WriteEntity(btr.Entity)
|
||||
net.WriteVector(btr.Normal)
|
||||
net.WriteVector(btr.Entity:WorldToLocal(btr.HitPos))
|
||||
net.WriteUInt(btr.HitGroup, 8)
|
||||
net.WriteFloat(range)
|
||||
net.WriteFloat(self:GetValue("Penetration"))
|
||||
net.WriteUInt(0, 4)
|
||||
net.SendToServer()
|
||||
else
|
||||
self:AfterShotFunction(btr, dmg, range, self:GetValue("Penetration"), {[btr.Entity] = true})
|
||||
end
|
||||
else
|
||||
self:AfterShotFunction(btr, dmg, range, self:GetValue("Penetration"), {})
|
||||
end
|
||||
|
||||
if SERVER then
|
||||
debugoverlay.Cross(btr.HitPos, 4, 5, Color(255, 0, 0), false)
|
||||
else
|
||||
debugoverlay.Cross(btr.HitPos, 4, 5, Color(255, 255, 255), false)
|
||||
|
||||
end
|
||||
end
|
||||
})
|
||||
end
|
||||
|
||||
-- self:GetOwner():LagCompensation(false)
|
||||
end
|
||||
|
||||
self:ApplyRecoil()
|
||||
|
||||
self:SetNextPrimaryFire(curatt + delay)
|
||||
self:TakePrimaryAmmo(self:GetValue("AmmoPerShot"))
|
||||
|
||||
self:SetBurstCount(self:GetBurstCount() + 1)
|
||||
self:SetPatternCount(self:GetPatternCount() + 1)
|
||||
self:DoBulletBodygroups()
|
||||
|
||||
if self:Clip1() == 0 then self.Primary.Automatic = false end
|
||||
|
||||
-- FireBullets won't hit ourselves. Apply damage directly!
|
||||
if SERVER and self:GetBlindFireMode() == TacRP.BLINDFIRE_KYS and !self:GetValue("ShootEnt") then
|
||||
timer.Simple(0, function()
|
||||
if !IsValid(self) or !IsValid(self:GetOwner()) then return end
|
||||
local damage = DamageInfo()
|
||||
damage:SetAttacker(self:GetOwner())
|
||||
damage:SetInflictor(self)
|
||||
damage:SetDamage(self:GetValue("Damage_Max") * self:GetValue("Num") * self:GetConfigDamageMultiplier())
|
||||
damage:SetDamageType(self:GetValue("DamageType") or self:IsShotgun() and DMG_BUCKSHOT or DMG_BULLET)
|
||||
damage:SetDamagePosition(self:GetMuzzleOrigin())
|
||||
damage:SetDamageForce(dir:Forward() * self:GetValue("Num"))
|
||||
|
||||
damage:ScaleDamage(self:GetBodyDamageMultipliers()[HITGROUP_HEAD])
|
||||
-- self:GetOwner():SetLastHitGroup(HITGROUP_HEAD)
|
||||
|
||||
self:GetOwner():TakeDamageInfo(damage)
|
||||
end)
|
||||
end
|
||||
|
||||
if CLIENT and self:GetOwner() == LocalPlayer() then
|
||||
self:DoMuzzleLight()
|
||||
elseif game.SinglePlayer() then
|
||||
self:CallOnClient("DoMuzzleLight")
|
||||
end
|
||||
|
||||
self:SetCharge(false)
|
||||
|
||||
-- Troll
|
||||
if self:GetBurstCount() >= 8 and TacRP.ShouldWeFunny(true) and (self.NextTroll or 0) < CurTime() and math.random() <= 0.02 then
|
||||
timer.Simple(math.Rand(0, 0.25), function()
|
||||
if IsValid(self) then
|
||||
self:EmitSound("tacrp/discord-notification.wav", nil, 100, math.Rand(0.1, 0.5), CHAN_BODY)
|
||||
end
|
||||
end)
|
||||
self.NextTroll = CurTime() + 180
|
||||
end
|
||||
|
||||
self:RunHook("Hook_PostShoot")
|
||||
end
|
||||
|
||||
local rings = {1, 9, 24, 45}
|
||||
local function ringnum(i)
|
||||
return rings[i] or (rings[#rings] + i ^ 2)
|
||||
end
|
||||
|
||||
local function getring(x)
|
||||
local i = 1
|
||||
while x > ringnum(i) do i = i + 1 end
|
||||
return i
|
||||
end
|
||||
|
||||
function SWEP:GetShotgunPattern(i, d)
|
||||
local ring_spread = self:GetSpread()
|
||||
local num = self:GetValue("Num")
|
||||
if num == 1 then return 0, 0 end
|
||||
|
||||
local pelspread = self:GetValue("ShotgunPelletSpread") > 0 and TacRP.ConVars["pelletspread"]:GetBool()
|
||||
if pelspread then
|
||||
ring_spread = ring_spread - self:GetValue("ShotgunPelletSpread")
|
||||
else
|
||||
d = 0
|
||||
end
|
||||
|
||||
local x = 0
|
||||
local y = 0
|
||||
local red = num <= 3 and 0 or 1
|
||||
local f = (i - red) / (num - red)
|
||||
|
||||
if num == 2 then
|
||||
local angle = f * 180 + (pelspread and (d - 0.5) * 60 or 0)
|
||||
|
||||
x = math.sin(math.rad(angle)) * ring_spread
|
||||
y = math.cos(math.rad(angle)) * ring_spread
|
||||
elseif num == 3 then
|
||||
local angle = f * 360 + d * 180 + 30
|
||||
x = math.sin(math.rad(angle)) * ring_spread
|
||||
y = math.cos(math.rad(angle)) * ring_spread
|
||||
elseif i == 1 then
|
||||
return x, y
|
||||
-- elseif num <= 9 then
|
||||
-- local angle = 360 * (f + d - (1 / (num - 2)))
|
||||
-- x = math.sin(math.rad(angle)) * ring_spread
|
||||
-- y = math.cos(math.rad(angle)) * ring_spread
|
||||
else
|
||||
local tr = getring(num)
|
||||
local ri = getring(i)
|
||||
local rin = ringnum(ri)
|
||||
local rln = ringnum(ri - 1)
|
||||
|
||||
local l = (ri - 1) / (tr - 1)
|
||||
if ri == tr then
|
||||
f = (i - rln) / ((math.min(rin, num)) - rln)
|
||||
else
|
||||
f = (i - rln) / (rin - rln)
|
||||
end
|
||||
|
||||
local angle = 360 * (f + l + d)
|
||||
x = math.sin(math.rad(angle)) * ring_spread * l
|
||||
y = math.cos(math.rad(angle)) * ring_spread * l
|
||||
end
|
||||
|
||||
return x, y
|
||||
end
|
||||
|
||||
local doorclasses = {
|
||||
["func_door_rotating"] = true,
|
||||
["prop_door_rotating"] = true,
|
||||
["prop_door_rotating_checkpoint"] = true
|
||||
}
|
||||
|
||||
function SWEP:AfterShotFunction(tr, dmg, range, penleft, alreadypenned, forced)
|
||||
if !forced and !IsFirstTimePredicted() and !game.SinglePlayer() then return end
|
||||
|
||||
if self:GetValue("DamageType") then
|
||||
dmg:SetDamageType(self:GetValue("DamageType"))
|
||||
elseif self:IsShotgun() then
|
||||
dmg:SetDamageType(DMG_BUCKSHOT + (engine.ActiveGamemode() == "terrortown" and DMG_BULLET or 0))
|
||||
end
|
||||
|
||||
local matpen = self:GetValue("Penetration")
|
||||
|
||||
if tr.Entity and alreadypenned[tr.Entity] then
|
||||
dmg:SetDamage(0)
|
||||
elseif IsValid(tr.Entity) then
|
||||
dmg:SetDamage(self:GetDamageAtRange(range))
|
||||
local bodydamage = self:GetBodyDamageMultipliers()
|
||||
|
||||
if bodydamage[tr.HitGroup] then
|
||||
dmg:ScaleDamage(bodydamage[tr.HitGroup])
|
||||
end
|
||||
|
||||
if tr.Entity:IsNextBot() or tr.Entity:IsNPC() then
|
||||
dmg:ScaleDamage(self:GetValue("DamageMultNPC"))
|
||||
end
|
||||
|
||||
TacRP.CancelBodyDamage(tr.Entity, dmg, tr.HitGroup)
|
||||
|
||||
if self:GetOwner():IsNPC() and !TacRP.ConVars["npc_equality"]:GetBool() then
|
||||
dmg:ScaleDamage(0.25)
|
||||
elseif matpen > 0 and TacRP.ConVars["penetration"]:GetBool() and !self:GetOwner():IsNPC() then
|
||||
local pendelta = penleft / matpen
|
||||
pendelta = Lerp(pendelta, math.Clamp(matpen * 0.02, 0.25, 0.5), 1)
|
||||
dmg:ScaleDamage(pendelta)
|
||||
end
|
||||
alreadypenned[tr.Entity] = true
|
||||
|
||||
if tr.Entity.LVS and !self:IsShotgun() then
|
||||
dmg:ScaleDamage(0.5)
|
||||
dmg:SetDamageForce(dmg:GetDamageForce():GetNormalized() * matpen * 75)
|
||||
dmg:SetDamageType(DMG_AIRBOAT + DMG_SNIPER)
|
||||
penleft = 0
|
||||
end
|
||||
|
||||
if SERVER and self:GetValue("DamageType") == DMG_BURN and IsValid(tr.Entity) then
|
||||
tr.Entity:Ignite(1, 64)
|
||||
end
|
||||
end
|
||||
|
||||
if self:GetValue("ExplosiveDamage") > 0 and penleft == matpen then
|
||||
-- Add DMG_AIRBOAT to hit helicopters
|
||||
-- Need a timer here because only one DamageInfo can exist at a time
|
||||
timer.Simple(0.0001, function()
|
||||
if !IsValid(self) or !IsValid(self:GetOwner()) then return end
|
||||
local dmginfo = DamageInfo()
|
||||
dmginfo:SetAttacker(self:GetOwner())
|
||||
dmginfo:SetInflictor(self)
|
||||
dmginfo:SetDamageType(self:GetValue("ExplosiveDamageType") or (DMG_BLAST + DMG_AIRBOAT))
|
||||
dmginfo:SetDamage(self:GetValue("ExplosiveDamage"))
|
||||
util.BlastDamageInfo(dmginfo, tr.HitPos, self:GetValue("ExplosiveRadius"))
|
||||
end)
|
||||
-- penleft = 0
|
||||
--util.BlastDamage(self, self:GetOwner(), tr.HitPos, self:GetValue("ExplosiveRadius"), self:GetValue("ExplosiveDamage"))
|
||||
end
|
||||
|
||||
if self:GetValue("ExplosiveEffect") then
|
||||
local fx = EffectData()
|
||||
fx:SetOrigin(tr.HitPos)
|
||||
fx:SetNormal(tr.HitNormal)
|
||||
|
||||
if bit.band(util.PointContents(tr.HitPos), CONTENTS_WATER) == CONTENTS_WATER then
|
||||
util.Effect("WaterSurfaceExplosion", fx, true)
|
||||
else
|
||||
util.Effect(self:GetValue("ExplosiveEffect"), fx, true)
|
||||
end
|
||||
end
|
||||
|
||||
if SERVER and IsValid(tr.Entity) and !tr.Entity.TacRP_DoorBusted
|
||||
and doorclasses[tr.Entity:GetClass()] and self:GetValue("DoorBreach") then
|
||||
if !tr.Entity.TacRP_BreachThreshold or CurTime() - tr.Entity.TacRP_BreachThreshold[1] > 0.1 then
|
||||
tr.Entity.TacRP_BreachThreshold = {CurTime(), 0}
|
||||
end
|
||||
|
||||
tr.Entity.TacRP_BreachThreshold[2] = tr.Entity.TacRP_BreachThreshold[2] + dmg:GetDamage()
|
||||
if tr.Entity.TacRP_BreachThreshold[2] > (self:GetValue("DoorBreachThreshold") or 100) then
|
||||
tr.Entity:EmitSound("ambient/materials/door_hit1.wav", 80, math.Rand(95, 105))
|
||||
for _, otherDoor in pairs(ents.FindInSphere(tr.Entity:GetPos(), 72)) do
|
||||
if tr.Entity != otherDoor and otherDoor:GetClass() == tr.Entity:GetClass() then
|
||||
local v = (otherDoor.TacRP_BreachThreshold and CurTime() - otherDoor.TacRP_BreachThreshold[1] <= 0.1) and 800 or 200
|
||||
TacRP.DoorBust(otherDoor, tr.Normal * v, dmg:GetAttacker())
|
||||
break
|
||||
end
|
||||
end
|
||||
TacRP.DoorBust(tr.Entity, tr.Normal * 800, dmg:GetAttacker())
|
||||
tr.Entity.TacRP_BreachThreshold = nil
|
||||
end
|
||||
end
|
||||
|
||||
self:Penetrate(tr, range, penleft, alreadypenned)
|
||||
end
|
||||
|
||||
function SWEP:GetMinMaxRange(base, static)
|
||||
local valfunc = base and self.GetBaseValue or self.GetValue
|
||||
|
||||
local max, min = valfunc(self, "Damage_Max"), valfunc(self, "Damage_Min")
|
||||
return valfunc(self, "Range_Min", static, max < min), valfunc(self, "Range_Max", static, max < min)
|
||||
end
|
||||
|
||||
function SWEP:GetDamageAtRange(range, noround)
|
||||
local d = 1
|
||||
|
||||
local r_min, r_max = self:GetMinMaxRange()
|
||||
|
||||
if range <= r_min then
|
||||
d = 0
|
||||
elseif range >= r_max then
|
||||
d = 1
|
||||
else
|
||||
d = (range - r_min) / (r_max - r_min)
|
||||
end
|
||||
|
||||
local dmgv = Lerp(d, self:GetValue("Damage_Max"), self:GetValue("Damage_Min")) * self:GetConfigDamageMultiplier()
|
||||
|
||||
if !noround then
|
||||
dmgv = math.ceil(dmgv)
|
||||
end
|
||||
|
||||
return dmgv
|
||||
end
|
||||
|
||||
function SWEP:GetShootDir(nosway)
|
||||
if !IsValid(self:GetOwner()) then return self:GetAngles() end
|
||||
local dir = self:GetOwner():EyeAngles()
|
||||
|
||||
local bf = self:GetBlindFireMode()
|
||||
if bf == TacRP.BLINDFIRE_KYS then
|
||||
dir.y = dir.y + 180
|
||||
elseif bf == TacRP.BLINDFIRE_LEFT then
|
||||
dir.y = dir.y + 75
|
||||
elseif bf == TacRP.BLINDFIRE_RIGHT then
|
||||
dir.y = dir.y - 75
|
||||
end
|
||||
|
||||
local u, r = dir:Up(), dir:Right()
|
||||
|
||||
local oa = self:GetFreeAimOffset()
|
||||
if !nosway then
|
||||
oa = oa + self:GetSwayAngles()
|
||||
end
|
||||
|
||||
dir:RotateAroundAxis(u, oa.y)
|
||||
-- dir:RotateAroundAxis(r, oa.r)
|
||||
dir:RotateAroundAxis(r, -oa.p)
|
||||
|
||||
dir = dir + self:GetValue("ShootOffsetAngle")
|
||||
|
||||
return dir
|
||||
end
|
||||
|
||||
function SWEP:ShootRocket(dir)
|
||||
if CLIENT then return end
|
||||
|
||||
local src = self:GetMuzzleOrigin()
|
||||
dir = dir or self:GetShootDir()
|
||||
|
||||
local ent = self:GetValue("ShootEnt")
|
||||
|
||||
local rocket = ents.Create(ent)
|
||||
if !IsValid(rocket) then return end
|
||||
|
||||
rocket:SetPos(src)
|
||||
if self:GetBlindFireMode() != TacRP.BLINDFIRE_KYS then
|
||||
rocket:SetOwner(self:GetOwner())
|
||||
else
|
||||
rocket.Attacker = self:GetOwner()
|
||||
end
|
||||
rocket.Inflictor = self
|
||||
rocket:SetAngles(dir)
|
||||
if isfunction(rocket.SetWeapon) then
|
||||
rocket:SetWeapon(self)
|
||||
end
|
||||
if self:GetOwner():IsNPC() then
|
||||
rocket.LockOnEntity = self:GetOwner():GetTarget()
|
||||
else
|
||||
if IsValid(self:GetLockOnEntity()) and CurTime() >= self:GetValue("LockOnTime") + self:GetLockOnStartTime() then
|
||||
rocket.LockOnEntity = self:GetLockOnEntity()
|
||||
end
|
||||
end
|
||||
self:RunHook("Hook_PreShootEnt", rocket)
|
||||
rocket:Spawn()
|
||||
self:RunHook("Hook_PostShootEnt", rocket)
|
||||
|
||||
local phys = rocket:GetPhysicsObject()
|
||||
|
||||
if phys:IsValid() and self:GetValue("ShootEntForce") > 0 then
|
||||
phys:SetVelocityInstantaneous(dir:Forward() * self:GetValue("ShootEntForce"))
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:GetSpread(baseline)
|
||||
local ply = self:GetOwner()
|
||||
local spread = self:GetValue("Spread")
|
||||
|
||||
if baseline then return spread end
|
||||
|
||||
local hippenalty = self:GetValue("HipFireSpreadPenalty")
|
||||
local movepenalty = self:GetValue("MoveSpreadPenalty")
|
||||
if TacRP.ConVars["oldschool"]:GetBool() or TacRP.GetBalanceMode() == TacRP.BALANCE_OLDSCHOOL then
|
||||
movepenalty = movepenalty + hippenalty * 0.25
|
||||
hippenalty = hippenalty * Lerp(12 / (self:GetValue("ScopeFOV") - 1.1), 0.05, 0.5)
|
||||
end
|
||||
|
||||
if self:GetInBipod() and self:GetScopeLevel() == 0 then
|
||||
spread = spread + Lerp(1 - self:GetValue("PeekPenaltyFraction"), hippenalty, 0)
|
||||
else
|
||||
spread = spread + Lerp(self:GetSightAmount() - (self:GetPeeking() and self:GetValue("PeekPenaltyFraction") or 0), hippenalty, 0)
|
||||
end
|
||||
|
||||
if !self:UseAltRecoil() then
|
||||
spread = spread + (self:GetRecoilAmount() * self:GetValue("RecoilSpreadPenalty"))
|
||||
end
|
||||
|
||||
local v = ply:GetAbsVelocity()
|
||||
local spd = math.min(math.sqrt(v.x * v.x + v.y * v.y) / 250, 1)
|
||||
|
||||
spread = spread + (spd * movepenalty)
|
||||
|
||||
local groundtime = CurTime() - (ply.TacRP_LastOnGroundTime or 0)
|
||||
local gd = math.Clamp(!ply:IsOnGround() and 0 or groundtime / math.Clamp((ply.TacRP_LastAirDuration or 0) - 0.25, 0.1, 1.5), 0, 1) ^ 0.75
|
||||
|
||||
if gd < 1 and ply:GetMoveType() != MOVETYPE_NOCLIP then
|
||||
local v = (ply:WaterLevel() > 0 or ply:GetMoveType() == MOVETYPE_LADDER) and 0.5 or 0
|
||||
spread = spread + Lerp(gd + v, self:GetValue("MidAirSpreadPenalty"), 0)
|
||||
end
|
||||
|
||||
if ply:OnGround() and ply:Crouching() then
|
||||
spread = spread + self:GetValue("CrouchSpreadPenalty")
|
||||
end
|
||||
|
||||
if self:GetBlindFire() then
|
||||
spread = spread + self:GetValue("BlindFireSpreadPenalty")
|
||||
end
|
||||
|
||||
local quickscopetime = CurTime() - self:GetLastScopeTime()
|
||||
|
||||
local qsd = (quickscopetime / self:GetValue("QuickScopeTime")) ^ 4
|
||||
|
||||
if qsd < 1 then
|
||||
spread = spread + Lerp(qsd, self:GetValue("QuickScopeSpreadPenalty"), 0)
|
||||
end
|
||||
|
||||
spread = math.max(spread, 0)
|
||||
|
||||
return spread
|
||||
end
|
||||
|
||||
local type_to_cvar = {
|
||||
["2Magnum Pistol"] = "mult_damage_magnum",
|
||||
["7Sniper Rifle"] = "mult_damage_sniper",
|
||||
-- ["5Shotgun"] = "mult_damage_shotgun",
|
||||
|
||||
["6Launcher"] = "",
|
||||
["7Special Weapon"] = "",
|
||||
["8Melee Weapon"] = "",
|
||||
["9Equipment"] = "",
|
||||
["9Throwable"] = "",
|
||||
}
|
||||
function SWEP:GetConfigDamageMultiplier()
|
||||
if self:IsShotgun() then
|
||||
return TacRP.ConVars["mult_damage_shotgun"]:GetFloat()
|
||||
elseif self:GetValue("PrimaryMelee") then
|
||||
return TacRP.ConVars["mult_damage_melee"]:GetFloat()
|
||||
else
|
||||
local cvar = type_to_cvar[self.SubCatType] or "mult_damage"
|
||||
return TacRP.ConVars[cvar] and TacRP.ConVars[cvar]:GetFloat() or 1
|
||||
end
|
||||
end
|
||||
|
||||
local shotgundmgmult = {
|
||||
[HITGROUP_HEAD] = 1,
|
||||
[HITGROUP_CHEST] = 1,
|
||||
[HITGROUP_STOMACH] = 1,
|
||||
[HITGROUP_LEFTARM] = 1,
|
||||
[HITGROUP_RIGHTARM] = 1,
|
||||
[HITGROUP_LEFTLEG] = 1,
|
||||
[HITGROUP_RIGHTLEG] = 1,
|
||||
[HITGROUP_GEAR] = 1,
|
||||
}
|
||||
|
||||
function SWEP:GetBodyDamageMultipliers(base)
|
||||
if self:IsShotgun(base) then -- Shotguns using hull traces will never hit bodygroups
|
||||
return table.Copy(shotgundmgmult)
|
||||
end
|
||||
|
||||
local valfunc = base and self.GetBaseValue or self.GetValue
|
||||
|
||||
local btbl = table.Copy(valfunc(self, "BodyDamageMultipliers"))
|
||||
|
||||
for k, v in pairs(valfunc(self, "BodyDamageMultipliersExtra") or {}) do
|
||||
if v < 0 then
|
||||
btbl[k] = math.abs(v)
|
||||
else
|
||||
btbl[k] = btbl[k] * v
|
||||
end
|
||||
end
|
||||
|
||||
local mult = TacRP.ConVars["mult_headshot"]:GetFloat()
|
||||
if mult <= 0 then
|
||||
btbl[HITGROUP_HEAD] = 1
|
||||
elseif mult <= 1 then
|
||||
btbl[HITGROUP_HEAD] = Lerp(mult, 1, btbl[HITGROUP_HEAD])
|
||||
else
|
||||
btbl[HITGROUP_HEAD] = btbl[HITGROUP_HEAD] * mult
|
||||
end
|
||||
|
||||
return btbl
|
||||
end
|
||||
|
||||
function SWEP:FireAnimationEvent( pos, ang, event, options )
|
||||
if event != 5004 then return true end
|
||||
end
|
||||
|
||||
-- DO NOT USE AngleRand() to do bullet spread as it generates a random angle in 3 directions when we only use two (roll is not relevant!)
|
||||
-- Also, multiplying by 36 is not correct! you need to also multiply by square root of 2. Trig stuff, i forgot why exactly.
|
||||
-- Arctic I fixed this THREE FUCKING TIMES on your THREE FUCKING WEAPON BASES do not make me come to Australia and beat the shit out of you
|
||||
function SWEP:RandomSpread(spread, seed)
|
||||
seed = (seed or 0) + self:EntIndex() + engine.TickCount()
|
||||
local a = util.SharedRandom("tacrp_randomspread", 0, 360, seed)
|
||||
local angleRand = Angle(math.sin(a), math.cos(a), 0)
|
||||
angleRand:Mul(spread * util.SharedRandom("tacrp_randomspread2", 0, 45, seed) * 1.4142135623730)
|
||||
|
||||
return angleRand
|
||||
end
|
||||
|
||||
function SWEP:IsShotgun(base)
|
||||
if base then
|
||||
return self:GetBaseValue("Num") > 1 and !self:GetBaseValue("NotShotgun")
|
||||
else
|
||||
return self:GetValue("Num") > 1 and !self:GetValue("NotShotgun")
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:GetJamChance(base)
|
||||
|
||||
local valfunc = base and self.GetBaseValue or self.GetValue
|
||||
local factor = valfunc(self, "JamFactor")
|
||||
if factor <= 0 or !TacRP.ConVars["can_jam"]:GetBool() then return 0 end
|
||||
|
||||
local default = TacRP.AmmoJamMSB[self:GetAmmoType(base)] or 15
|
||||
local msb = (valfunc(self, "JamBaseMSB") or default) / math.sqrt(factor)
|
||||
|
||||
return 1 / msb
|
||||
end
|
||||
|
||||
function SWEP:GetRPM(base, fm)
|
||||
fm = fm or self:GetCurrentFiremode()
|
||||
local valfunc = base and self.GetBaseValue or self.GetValue
|
||||
local rpm = valfunc(self, "RPM")
|
||||
if fm == 1 then
|
||||
rpm = rpm * valfunc(self, "RPMMultSemi")
|
||||
elseif fm < 0 then
|
||||
rpm = rpm * valfunc(self, "RPMMultBurst")
|
||||
end
|
||||
return rpm
|
||||
end
|
||||
143
garrysmod/addons/tacrp/lua/weapons/tacrp_base/sh_sprint.lua
Normal file
143
garrysmod/addons/tacrp/lua/weapons/tacrp_base/sh_sprint.lua
Normal file
@@ -0,0 +1,143 @@
|
||||
function SWEP:GetIsSprinting()
|
||||
local owner = self:GetOwner()
|
||||
|
||||
if !owner:IsValid() or owner:IsNPC() or owner:IsNextBot() then
|
||||
return false
|
||||
end
|
||||
|
||||
if self:CanShootInSprint() then return false end
|
||||
|
||||
local walkspeed = owner:GetWalkSpeed()
|
||||
local runspeed = owner:GetRunSpeed()
|
||||
|
||||
if owner.TacRP_SprintBlock then return false end
|
||||
if owner:GetNWBool("TacRPChargeState", false) then return true end
|
||||
if owner:GetNWBool("SlidingAbilityIsSliding", false) then return false end
|
||||
|
||||
if TTT2 then
|
||||
if SPRINT and SPRINT:IsSprinting(owner) then
|
||||
return true
|
||||
else
|
||||
return owner.isSprinting == true and (owner.sprintProgress or 0) > 0 and owner:KeyDown(IN_SPEED) and !owner:Crouching() and owner:OnGround()
|
||||
end
|
||||
end
|
||||
|
||||
-- TTT sets runspeed to curspeed, so this will disable it unless sprint addons exist (who ideally sets runspeed. i didn't check)
|
||||
if runspeed <= walkspeed then return false end
|
||||
|
||||
if !owner.TacRP_Moving then return false end -- Don't check IN_ move keys because 1) controllers and 2) bots
|
||||
if !owner:KeyDown(IN_SPEED) then return false end -- SetButtons does not seem to affect this?
|
||||
local curspeed = owner:GetVelocity():Length()
|
||||
if curspeed <= 0 then return false end
|
||||
if !owner:OnGround() then return false end
|
||||
|
||||
if self:GetOwner():GetInfoNum("tacrp_aim_cancels_sprint", 0) > 0 and self:GetScopeLevel() > 0 then return false end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function SWEP:ShouldLowerWeapon()
|
||||
local owner = self:GetOwner()
|
||||
|
||||
if !owner:IsValid() or owner:IsNPC() or owner:IsNextBot() then
|
||||
return false
|
||||
end
|
||||
|
||||
if TacRP.ConVars["sprint_counts_midair"]:GetBool() and owner:GetMoveType() != MOVETYPE_NOCLIP and !owner:IsOnGround() then
|
||||
return true
|
||||
end
|
||||
|
||||
if self:DoForceSightsBehavior() and self:GetScopeLevel() == 0 and !self:GetInBipod() and self:GetBlindFireMode() == TacRP.BLINDFIRE_NONE then
|
||||
return true
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
function SWEP:CanStopSprinting()
|
||||
local owner = self:GetOwner()
|
||||
if !owner:IsValid() or owner:IsNPC() or owner:IsNextBot() then
|
||||
return false
|
||||
end
|
||||
|
||||
if TacRP.ConVars["sprint_counts_midair"]:GetBool() and owner:GetMoveType() != MOVETYPE_NOCLIP and !owner:OnGround() and !self:GetReloading() then
|
||||
return false
|
||||
end
|
||||
|
||||
if owner:GetNWBool("TacRPChargeState", false) then
|
||||
return false
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function SWEP:GetSprintDelta()
|
||||
return self:GetSprintAmount()
|
||||
end
|
||||
|
||||
function SWEP:EnterSprint()
|
||||
if !self:CanShootInSprint() then
|
||||
self:ToggleBlindFire(TacRP.BLINDFIRE_NONE)
|
||||
end
|
||||
if !self:CanReloadInSprint() and self:GetReloading() then
|
||||
-- use clip1 to check for whether the up-in has happened. if so, do not cancel (can't have you cancel the animation *that* easily)
|
||||
-- this causes fringe cases related to maniuplating magazine sizes but shouldn't be abusable
|
||||
if self:Clip1() < self:GetMaxClip1() then
|
||||
self:CancelReload(true)
|
||||
-- self:Idle()
|
||||
end
|
||||
end
|
||||
self:ScopeToggle(0)
|
||||
|
||||
self:SetShouldHoldType()
|
||||
end
|
||||
|
||||
function SWEP:ExitSprint()
|
||||
local amt = self:GetSprintAmount()
|
||||
self:SetSprintLockTime(CurTime() + (self:GetValue("SprintToFireTime") * amt))
|
||||
|
||||
self:SetShouldHoldType()
|
||||
end
|
||||
|
||||
SWEP.LastWasSprinting = false
|
||||
|
||||
function SWEP:ThinkSprint()
|
||||
local sprinting = self:GetIsSprinting() or self:GetSafe()
|
||||
|
||||
local amt = self:GetSprintAmount()
|
||||
|
||||
if self.LastWasSprinting and !sprinting then
|
||||
self:ExitSprint()
|
||||
elseif !self.LastWasSprinting and sprinting then
|
||||
self:EnterSprint()
|
||||
end
|
||||
|
||||
self.LastWasSprinting = sprinting
|
||||
|
||||
if IsFirstTimePredicted() or game.SinglePlayer() then
|
||||
if (sprinting or (self:ShouldLowerWeapon() and !self:DoForceSightsBehavior())) and !self:GetInBipod() then
|
||||
amt = math.Approach(amt, 1, FrameTime() / self:GetValue("SprintToFireTime"))
|
||||
else
|
||||
amt = math.Approach(amt, 0, FrameTime() / self:GetValue("SprintToFireTime"))
|
||||
end
|
||||
end
|
||||
|
||||
self:SetSprintAmount(amt)
|
||||
end
|
||||
|
||||
function SWEP:CanShootInSprint(base)
|
||||
if !TacRP.ConVars["sprint_lower"]:GetBool() and !self:DoForceSightsBehavior() then return true end
|
||||
if base then
|
||||
return self:GetBaseValue("ShootWhileSprint")
|
||||
else
|
||||
return self:GetValue("ShootWhileSprint")
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:CanReloadInSprint(base)
|
||||
return TacRP.ConVars["sprint_reload"]:GetBool()
|
||||
end
|
||||
|
||||
function SWEP:DoForceSightsBehavior()
|
||||
return TacRP.ConVars["sightsonly"]:GetBool() and self:GetValue("Scope")
|
||||
end
|
||||
239
garrysmod/addons/tacrp/lua/weapons/tacrp_base/sh_stats.lua
Normal file
239
garrysmod/addons/tacrp/lua/weapons/tacrp_base/sh_stats.lua
Normal file
@@ -0,0 +1,239 @@
|
||||
SWEP.StatCache = {}
|
||||
SWEP.HookCache = {}
|
||||
SWEP.StatScoreCache = {} -- used by cust menu
|
||||
SWEP.MiscCache = {}
|
||||
|
||||
SWEP.ExcludeFromRawStats = {
|
||||
["FullName"] = true,
|
||||
["PrintName"] = true,
|
||||
["Description"] = true,
|
||||
}
|
||||
|
||||
SWEP.IntegerStats = {
|
||||
["ClipSize"] = true,
|
||||
["Num"] = true,
|
||||
}
|
||||
|
||||
SWEP.AllowNegativeStats = {
|
||||
["RecoilKick"] = true,
|
||||
}
|
||||
|
||||
function SWEP:InvalidateCache()
|
||||
self.StatCache = {}
|
||||
self.HookCache = {}
|
||||
self.StatScoreCache = {}
|
||||
self.MiscCache = {}
|
||||
self.RecoilPatternCache = {}
|
||||
|
||||
self.AutoSightPos = nil
|
||||
self.AutoSightAng = nil
|
||||
end
|
||||
|
||||
function SWEP:RunHook(val, data)
|
||||
if !self.HookCache[val] then
|
||||
self.HookCache[val] = {}
|
||||
|
||||
if self:GetTable()[val] then
|
||||
table.insert(self.HookCache[val], self:GetTable()[val])
|
||||
end
|
||||
|
||||
for slot, slottbl in pairs(self.Attachments) do
|
||||
if !slottbl.Installed then continue end
|
||||
|
||||
local atttbl = TacRP.GetAttTable(slottbl.Installed)
|
||||
|
||||
if atttbl[val] then
|
||||
table.insert(self.HookCache[val], atttbl[val])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for _, chook in pairs(self.HookCache[val]) do
|
||||
local d = chook(self, data)
|
||||
if d != nil then
|
||||
data = d
|
||||
end
|
||||
end
|
||||
|
||||
data = hook.Run("TacRP_" .. val, self, data) or data
|
||||
|
||||
return data
|
||||
end
|
||||
|
||||
function SWEP:GetBaseValue(val)
|
||||
local stat = self:GetTable()[val]
|
||||
|
||||
local b = TacRP.GetBalanceMode()
|
||||
if b > 0 and self.BalanceStats != nil then
|
||||
if TacRP.BalanceDefaults[b] and TacRP.BalanceDefaults[b][val] != nil then
|
||||
stat = TacRP.BalanceDefaults[b][val]
|
||||
end
|
||||
for j = b, 1, -1 do
|
||||
if self.BalanceStats[b] and self.BalanceStats[b][val] != nil then
|
||||
stat = self.BalanceStats[b][val]
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if isnumber(stat) then
|
||||
if self.IntegerStats[val] then
|
||||
stat = math.ceil(stat)
|
||||
end
|
||||
if !self.AllowNegativeStats[val] then
|
||||
stat = math.max(stat, 0)
|
||||
end
|
||||
end
|
||||
|
||||
return stat
|
||||
end
|
||||
|
||||
function SWEP:GetValue(val, static, invert)
|
||||
|
||||
local cachei = invert and 2 or 1
|
||||
|
||||
if static == nil then
|
||||
static = self.StaticStats
|
||||
end
|
||||
|
||||
local stat = nil
|
||||
|
||||
-- Generate a cache if it doesn't exist already
|
||||
if !self.StatCache[val] or !self.StatCache[val][cachei] then
|
||||
|
||||
self.StatCache[val] = self.StatCache[val] or {}
|
||||
|
||||
stat = self:GetBaseValue(val)
|
||||
|
||||
local modifiers = {
|
||||
["stat"] = nil, -- return this unless hook is set
|
||||
["hook"] = nil, -- if set, always call hook and use the following values
|
||||
["func"] = {}, -- modifying functions
|
||||
["set"] = stat, -- override and no prefix
|
||||
["prio"] = 0, -- override priority
|
||||
["add"] = 0,
|
||||
["mul"] = 1,
|
||||
}
|
||||
|
||||
-- local priority = 0
|
||||
|
||||
if !self.ExcludeFromRawStats[val] then
|
||||
for slot, slottbl in pairs(self.Attachments) do
|
||||
if !slottbl.Installed then continue end
|
||||
|
||||
local atttbl = TacRP.GetAttTable(slottbl.Installed)
|
||||
|
||||
local att_priority = atttbl["Priority_" .. val] or 1
|
||||
|
||||
if atttbl[val] != nil and att_priority > modifiers.prio then
|
||||
-- stat = atttbl[val]
|
||||
-- priority = att_priority
|
||||
modifiers.set = atttbl[val]
|
||||
modifiers.prio = att_priority
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for slot, slottbl in pairs(self.Attachments) do
|
||||
if !slottbl.Installed then continue end
|
||||
|
||||
local atttbl = TacRP.GetAttTable(slottbl.Installed)
|
||||
|
||||
local att_priority = atttbl["Override_Priority_" .. val] or 1
|
||||
|
||||
if atttbl["Override_" .. val] != nil and att_priority > modifiers.prio then
|
||||
-- stat = atttbl["Override_" .. val]
|
||||
-- priority = att_priority
|
||||
modifiers.set = atttbl["Override_" .. val]
|
||||
modifiers.prio = att_priority
|
||||
end
|
||||
|
||||
if atttbl["Add_" .. val] then -- isnumber(stat) and
|
||||
-- stat = stat + atttbl["Add_" .. val] * (invert and -1 or 1)
|
||||
modifiers.add = modifiers.add + atttbl["Add_" .. val] * (invert and -1 or 1)
|
||||
end
|
||||
|
||||
if atttbl["Mult_" .. val] then -- isnumber(stat) and
|
||||
if invert then
|
||||
-- stat = stat / atttbl["Mult_" .. val]
|
||||
modifiers.mul = modifiers.mul / atttbl["Mult_" .. val]
|
||||
else
|
||||
-- stat = stat * atttbl["Mult_" .. val]
|
||||
modifiers.mul = modifiers.mul * atttbl["Mult_" .. val]
|
||||
end
|
||||
end
|
||||
|
||||
if atttbl["Func_" .. val] then
|
||||
table.insert(modifiers.func, atttbl["Func_" .. val])
|
||||
end
|
||||
end
|
||||
|
||||
if isfunction(self["Func_" .. val]) then
|
||||
table.insert(modifiers.func, self["Func_" .. val])
|
||||
end
|
||||
|
||||
-- Check for stat hooks. If any exist, we must call it whenever we try to get the stat.
|
||||
-- Cache this check so we don't unnecessarily call hook.Run a million times when nobody wants to hook us.
|
||||
if table.Count(hook.GetTable()["TacRP_Stat_" .. val] or {}) > 0 then
|
||||
modifiers.hook = true
|
||||
end
|
||||
|
||||
-- Calculate the final value
|
||||
if isnumber(modifiers.set) then
|
||||
modifiers.stat = (modifiers.set + modifiers.add) * modifiers.mul
|
||||
if self.IntegerStats[val] then
|
||||
modifiers.stat = math.ceil(modifiers.stat)
|
||||
end
|
||||
if !self.AllowNegativeStats[val] then
|
||||
modifiers.stat = math.max(modifiers.stat, 0)
|
||||
end
|
||||
else
|
||||
modifiers.stat = modifiers.set
|
||||
end
|
||||
|
||||
-- Cache our final value, presence of hooks, and summed modifiers
|
||||
self.StatCache[val][cachei] = modifiers
|
||||
end
|
||||
|
||||
local cache = self.StatCache[val][cachei]
|
||||
if !static and (cache.hook or #cache.func > 0) then
|
||||
-- Run the hook
|
||||
-- Hooks are expected to modify "set", "prio", "add" and "mul", so we can do all calculations in the right order.
|
||||
local modifiers = {set = nil, prio = 0, add = 0, mul = 1}
|
||||
|
||||
if #cache.func > 0 then
|
||||
for _, f in ipairs(cache.func) do
|
||||
f(self, modifiers)
|
||||
end
|
||||
end
|
||||
if cache.hook then
|
||||
hook.Run("TacRP_Stat_" .. val, self, modifiers)
|
||||
if !istable(modifiers) then modifiers = {set = nil, prio = 0, add = 0, mul = 1} end -- some hook isn't cooperating!
|
||||
end
|
||||
|
||||
if modifiers.prio > cache.prio then
|
||||
stat = modifiers.set
|
||||
else
|
||||
stat = cache.set
|
||||
end
|
||||
|
||||
if isnumber(stat) then
|
||||
if invert then
|
||||
stat = (stat - modifiers.add - cache.add) / modifiers.mul / cache.mul
|
||||
else
|
||||
stat = (stat + modifiers.add + cache.add) * modifiers.mul * cache.mul
|
||||
end
|
||||
|
||||
if self.IntegerStats[val] then
|
||||
stat = math.ceil(stat)
|
||||
end
|
||||
if !self.AllowNegativeStats[val] then
|
||||
stat = math.max(stat, 0)
|
||||
end
|
||||
end
|
||||
else
|
||||
stat = cache.stat
|
||||
end
|
||||
|
||||
return stat
|
||||
end
|
||||
134
garrysmod/addons/tacrp/lua/weapons/tacrp_base/sh_sway.lua
Normal file
134
garrysmod/addons/tacrp/lua/weapons/tacrp_base/sh_sway.lua
Normal file
@@ -0,0 +1,134 @@
|
||||
function SWEP:GetSwayAmount(pure)
|
||||
if self:GetOwner():IsNPC() then return 0 end
|
||||
|
||||
local sway = self:GetValue("Sway")
|
||||
|
||||
local d = self:GetSightDelta() - (self:GetPeeking() and self:GetValue("PeekPenaltyFraction") or 0)
|
||||
sway = Lerp(d, sway, self:GetValue("ScopedSway"))
|
||||
|
||||
if self:GetBreath() < 1 then
|
||||
sway = sway + (1 * (1 - self:GetBreath()) * (self:GetOutOfBreath() and 1 or 0.5))
|
||||
end
|
||||
sway = Lerp(self:GetHoldBreathAmount() ^ 0.75, sway, 0)
|
||||
|
||||
if self:GetBlindFire() then
|
||||
sway = sway + self:GetValue("BlindFireSway")
|
||||
end
|
||||
|
||||
if self:GetOwner():Crouching() and !(self:GetOwner():KeyDown(IN_FORWARD) or self:GetOwner():KeyDown(IN_MOVELEFT) or self:GetOwner():KeyDown(IN_MOVERIGHT) or self:GetOwner():KeyDown(IN_BACK)) then
|
||||
sway = sway * self:GetValue("SwayCrouchMult")
|
||||
end
|
||||
|
||||
if !pure then
|
||||
sway = sway + self:GetForcedSwayAmount()
|
||||
end
|
||||
|
||||
if self:GetValue("Bipod") then
|
||||
local f = self:Curve(math.Clamp((CurTime() - self.LastBipodTime) / 0.15, 0, 1))
|
||||
if self:GetInBipod() then
|
||||
sway = Lerp(f, sway, 0)
|
||||
else
|
||||
sway = Lerp(f, 0, sway)
|
||||
end
|
||||
end
|
||||
|
||||
return sway
|
||||
end
|
||||
|
||||
function SWEP:GetForcedSwayAmount()
|
||||
local sway = 0
|
||||
|
||||
if self:GetOwner():GetNWFloat("TacRPGasEnd", 0) > CurTime() then
|
||||
sway = sway + TacRP.ConVars["gas_sway"]:GetFloat() * Lerp(self:GetSightAmount(), 1, 0.25) * math.Clamp((self:GetOwner():GetNWFloat("TacRPGasEnd") - CurTime()) / 2, 0, 1)
|
||||
end
|
||||
|
||||
sway = sway + (hook.Run("TacRP_GetForcedSway", self, self:GetOwner()) or 0)
|
||||
|
||||
return sway
|
||||
end
|
||||
|
||||
function SWEP:GetSwayAngles()
|
||||
local swayamt = self:IsSwayEnabled() and self:GetSwayAmount() or self:GetForcedSwayAmount()
|
||||
local swayspeed = 1
|
||||
|
||||
if swayamt <= 0 then return Angle(0, 0, 0) end
|
||||
|
||||
local ct = CLIENT and UnPredictedCurTime() or CurTime()
|
||||
|
||||
local ang = Angle(math.sin(ct * 0.6 * swayspeed) + (math.cos(ct * 2) * 0.5), math.sin(ct * 0.4 * swayspeed) + (math.cos(ct * 1.6) * 0.5), 0)
|
||||
|
||||
ang = ang * swayamt
|
||||
|
||||
return ang
|
||||
end
|
||||
|
||||
function SWEP:IsSwayEnabled()
|
||||
return TacRP.ConVars["sway"]:GetBool()
|
||||
end
|
||||
|
||||
function SWEP:ThinkHoldBreath()
|
||||
local owner = self:GetOwner()
|
||||
if !owner:IsPlayer() then return end
|
||||
|
||||
local ft = FrameTime()
|
||||
|
||||
if self:HoldingBreath() then
|
||||
self:SetBreath(self:GetBreath() - ft * (self:GetBreathDrain() * (self:HasOptic() and 1 or 0.75) * (self:GetRecoilAmount() > 0 and 1.5 or 1)))
|
||||
|
||||
if self:GetBreath() <= 0 then
|
||||
self:SetOutOfBreath(true)
|
||||
self:SetHoldingBreath(false)
|
||||
end
|
||||
|
||||
if self:GetHoldBreathAmount() < 1 then
|
||||
self:SetHoldBreathAmount(math.min(1, self:GetHoldBreathAmount() + ft * self:GetBreathSpeed()))
|
||||
end
|
||||
else
|
||||
if self:GetHoldBreathAmount() > 0 then
|
||||
self:SetHoldBreathAmount(math.max(0, self:GetHoldBreathAmount() - ft * self:GetBreathSpeed() * 2))
|
||||
end
|
||||
|
||||
if self:GetOutOfBreath() and self:GetBreath() >= 1 then
|
||||
self:SetOutOfBreath(false)
|
||||
end
|
||||
|
||||
self:SetBreath(math.min(1, self:GetBreath() + ft * self:GetValue("BreathRecovery") * (self:GetOutOfBreath() and 0.2 or 0.25)))
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:CanHoldBreath()
|
||||
return self:GetValue("Scope") and TacRP.ConVars["sway"]:GetBool() and self:GetScopeLevel() > 0 and !self:GetReloading()
|
||||
end
|
||||
|
||||
function SWEP:NotOutOfBreath()
|
||||
return self:GetBreath() > 0 and !self:GetOutOfBreath()
|
||||
end
|
||||
|
||||
function SWEP:HoldingBreath()
|
||||
local holding = self:GetOwner():KeyDown(IN_SPEED) or self:GetOwner():KeyDown(IN_RUN)
|
||||
if self:GetOwner():GetInfoNum("tacrp_toggleholdbreath", 0) == 1 then
|
||||
if self:GetOwner():KeyPressed(IN_SPEED) or self:GetOwner():KeyPressed(IN_RUN) then
|
||||
self:SetHoldingBreath(!self:GetHoldingBreath())
|
||||
end
|
||||
else
|
||||
self:SetHoldingBreath(holding)
|
||||
end
|
||||
|
||||
lastpressed = holding
|
||||
|
||||
return self:CanHoldBreath() and self:GetSightAmount() >= 1 and self:NotOutOfBreath() and self:GetHoldingBreath()
|
||||
end
|
||||
|
||||
function SWEP:GetBreathDrain()
|
||||
if self.MiscCache["breath_cost"] == nil then
|
||||
self.MiscCache["breath_cost"] = (math.Clamp(self:GetValue("ScopedSway"), 0.1, 0.3) ^ 0.75) * (1 - 0.3 * math.Clamp((4 - 90 / self:GetValue("ScopeFOV")) / 3, 0, 1))
|
||||
end
|
||||
return self.MiscCache["breath_cost"] * self:GetValue("BreathDrain")
|
||||
end
|
||||
|
||||
function SWEP:GetBreathSpeed()
|
||||
if self.MiscCache["breath_rate"] == nil then
|
||||
self.MiscCache["breath_rate"] = (math.Clamp(self:GetValue("ScopedSway"), 0.1, 0.5) ^ 0.5) / 0.3 + (0.5 * math.Clamp((4 - 90 / self:GetValue("ScopeFOV")) / 3, 0, 1))
|
||||
end
|
||||
return self.MiscCache["breath_rate"]
|
||||
end
|
||||
146
garrysmod/addons/tacrp/lua/weapons/tacrp_base/sh_think.lua
Normal file
146
garrysmod/addons/tacrp/lua/weapons/tacrp_base/sh_think.lua
Normal file
@@ -0,0 +1,146 @@
|
||||
function SWEP:Think()
|
||||
local owner = self:GetOwner()
|
||||
|
||||
local stop = self:RunHook("Hook_PreThink")
|
||||
if stop then return end
|
||||
|
||||
local cfm = self:GetCurrentFiremode()
|
||||
if self:GetValue("RunawayBurst") and cfm < 0 then
|
||||
if self:GetBurstCount() >= -cfm then
|
||||
self:SetBurstCount(0)
|
||||
self:SetNextPrimaryFire(CurTime() + self:GetValue("PostBurstDelay"))
|
||||
elseif self:GetBurstCount() > 0 and self:GetBurstCount() < -cfm then
|
||||
self:PrimaryAttack()
|
||||
end
|
||||
else
|
||||
if (owner:KeyReleased(IN_ATTACK) or (cfm < 0 and self:GetBurstCount() >= -cfm)) then
|
||||
if cfm < 0 and self:GetBurstCount() > 1 then
|
||||
if !self:GetValue("AutoBurst") then
|
||||
self.Primary.Automatic = false
|
||||
end
|
||||
local add = 0
|
||||
if self:GetBurstCount() >= -cfm then
|
||||
add = 60 / self:GetValue("RPM")
|
||||
end
|
||||
self:SetNextPrimaryFire(CurTime() + self:GetValue("PostBurstDelay") + add)
|
||||
end
|
||||
self:SetBurstCount(0)
|
||||
end
|
||||
end
|
||||
|
||||
if self:GetPatternCount() > 0 and (self:GetRecoilAmount() == 0 or self:GetReloading()) then
|
||||
self:SetPatternCount(0)
|
||||
end
|
||||
|
||||
if owner:KeyPressed(TacRP.IN_CUSTOMIZE) then
|
||||
if self:GetScopeLevel() == 0 then
|
||||
self:ToggleCustomize(!self:GetCustomize())
|
||||
else
|
||||
if !self:GetValue("AlwaysPeek") then
|
||||
local shouldpeek = !self:GetPeeking()
|
||||
|
||||
if owner:GetInfoNum("tacrp_togglepeek", 0) == 0 then
|
||||
shouldpeek = true
|
||||
end
|
||||
|
||||
self:SetPeeking(shouldpeek)
|
||||
|
||||
if self:GetSightAmount() > 0 then
|
||||
self:SetLastScopeTime(CurTime())
|
||||
end
|
||||
end
|
||||
end
|
||||
elseif !self:GetValue("AlwaysPeek") and owner:GetInfoNum("tacrp_togglepeek", 0) == 0 and self:GetPeeking() and owner:KeyReleased(TacRP.IN_CUSTOMIZE) then
|
||||
self:SetPeeking(false)
|
||||
|
||||
if self:GetSightAmount() > 0 then
|
||||
self:SetLastScopeTime(CurTime())
|
||||
end
|
||||
end
|
||||
|
||||
if owner:KeyPressed(TacRP.IN_TACTICAL) then
|
||||
self:ToggleTactical()
|
||||
end
|
||||
|
||||
self:ThinkRecoil()
|
||||
|
||||
self:ThinkSprint()
|
||||
|
||||
self:ThinkGrenade()
|
||||
|
||||
self:ThinkReload()
|
||||
|
||||
self:ThinkSights()
|
||||
|
||||
self:ThinkFreeAim()
|
||||
|
||||
self:ThinkBlindFire()
|
||||
|
||||
self:ProcessTimers()
|
||||
|
||||
self:ThinkLockOn()
|
||||
|
||||
self:ThinkHoldBreath()
|
||||
|
||||
if self:GetValue("Melee") and self:GetOwner():KeyPressed(TacRP.IN_MELEE) then
|
||||
self:Melee()
|
||||
return
|
||||
end
|
||||
|
||||
if IsValid(self:GetOwner()) and self:GetOwner():IsPlayer() and self:GetValue("TacticalThink") and self:GetTactical() then
|
||||
self:GetValue("TacticalThink")(self)
|
||||
end
|
||||
|
||||
-- if IsValid(self:GetOwner()) and self:GetTactical() and self:GetValue("Minimap") and (SERVER and !game.SinglePlayer()) and (self.NextRadarBeep or 0) < CurTime() then
|
||||
-- self.NextRadarBeep = CurTime() + 1.5
|
||||
-- local f = RecipientFilter()
|
||||
-- f:AddPVS(self:GetPos())
|
||||
-- f:RemovePlayer(self:GetOwner())
|
||||
-- local s = CreateSound(self, "plats/elevbell1.wav", f)
|
||||
-- s:SetSoundLevel(75)
|
||||
-- s:PlayEx(0.25, 105)
|
||||
-- end
|
||||
|
||||
if self:GetJammed() and !self:StillWaiting() and TacRP.ConVars["jam_autoclear"]:GetBool() then
|
||||
local t = self:PlayAnimation("jam", 0.75, true, true)
|
||||
self:GetOwner():DoCustomAnimEvent(PLAYERANIMEVENT_CANCEL_RELOAD, t * 1000)
|
||||
self:SetJammed(false)
|
||||
end
|
||||
|
||||
if self:GetNextIdle() < CurTime() and (SERVER or !game.SinglePlayer()) then
|
||||
self:Idle()
|
||||
end
|
||||
|
||||
if self:GetValue("Bipod") and self:GetInBipod() and !self:CanBipod() then
|
||||
self:ExitBipod()
|
||||
end
|
||||
|
||||
if CLIENT and (IsFirstTimePredicted() or game.SinglePlayer()) then
|
||||
|
||||
self:ThinkNearWall()
|
||||
|
||||
if IsValid(self.MuzzleLight) then
|
||||
if (self.MuzzleLightEnd or 0) < UnPredictedCurTime() then
|
||||
self.MuzzleLight:Remove()
|
||||
self.MuzzleLight = nil
|
||||
else
|
||||
self.MuzzleLight:SetBrightness( math.Remap( UnPredictedCurTime(), self.MuzzleLightStart, self.MuzzleLightEnd, self.MuzzleLightBrightness, 0 ) )
|
||||
self.MuzzleLight:SetFOV( math.Remap( UnPredictedCurTime(), self.MuzzleLightStart, self.MuzzleLightEnd, self.MuzzleLightFOV, 60 ) )
|
||||
self.MuzzleLight:Update()
|
||||
end
|
||||
end
|
||||
|
||||
if !self.LoadedPreset then
|
||||
self.LoadedPreset = true
|
||||
|
||||
if TacRP.ConVars["autosave"]:GetBool() and TacRP.ConVars["free_atts"]:GetBool() then
|
||||
self:LoadPreset()
|
||||
-- self:DoDeployAnimation()
|
||||
end
|
||||
end
|
||||
|
||||
self:CanBipod()
|
||||
end
|
||||
|
||||
self:RunHook("Hook_PostThink")
|
||||
end
|
||||
51
garrysmod/addons/tacrp/lua/weapons/tacrp_base/sh_timers.lua
Normal file
51
garrysmod/addons/tacrp/lua/weapons/tacrp_base/sh_timers.lua
Normal file
@@ -0,0 +1,51 @@
|
||||
local tick = 0
|
||||
|
||||
function SWEP:InitTimers()
|
||||
self.ActiveTimers = {} -- { { time, id, func } }
|
||||
end
|
||||
|
||||
function SWEP:SetTimer(time, callback, id)
|
||||
if !IsFirstTimePredicted() then return end
|
||||
|
||||
table.insert(self.ActiveTimers, { time + CurTime(), id or "", callback })
|
||||
end
|
||||
|
||||
function SWEP:TimerExists(id)
|
||||
for _, v in pairs(self.ActiveTimers) do
|
||||
if v[2] == id then return true end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
function SWEP:KillTimer(id)
|
||||
local keeptimers = {}
|
||||
|
||||
for _, v in pairs(self.ActiveTimers) do
|
||||
if v[2] != id then table.insert(keeptimers, v) end
|
||||
end
|
||||
|
||||
self.ActiveTimers = keeptimers
|
||||
end
|
||||
|
||||
function SWEP:KillTimers()
|
||||
self.ActiveTimers = {}
|
||||
end
|
||||
|
||||
function SWEP:ProcessTimers()
|
||||
local keeptimers, UCT = {}, CurTime()
|
||||
|
||||
if CLIENT and UCT == tick then return end
|
||||
|
||||
if !self.ActiveTimers then self:InitTimers() end
|
||||
|
||||
for _, v in pairs(self.ActiveTimers) do
|
||||
if v[1] <= UCT then v[3]() end
|
||||
end
|
||||
|
||||
for _, v in pairs(self.ActiveTimers) do
|
||||
if v[1] > UCT then table.insert(keeptimers, v) end
|
||||
end
|
||||
|
||||
self.ActiveTimers = keeptimers
|
||||
end
|
||||
146
garrysmod/addons/tacrp/lua/weapons/tacrp_base/sh_ttt.lua
Normal file
146
garrysmod/addons/tacrp/lua/weapons/tacrp_base/sh_ttt.lua
Normal file
@@ -0,0 +1,146 @@
|
||||
function SWEP:OnRestore()
|
||||
end
|
||||
|
||||
function SWEP:GetHeadshotMultiplier(victim, dmginfo)
|
||||
return 1 -- Hey hey hey, don't forget about me!!!
|
||||
end
|
||||
|
||||
function SWEP:IsEquipment()
|
||||
return WEPS.IsEquipment(self)
|
||||
end
|
||||
|
||||
SWEP.IsSilent = false
|
||||
|
||||
-- The OnDrop() hook is useless for this as it happens AFTER the drop. OwnerChange
|
||||
-- does not occur when a drop happens for some reason. Hence this thing.
|
||||
function SWEP:PreDrop()
|
||||
if SERVER and IsValid(self:GetOwner()) and self.Primary.Ammo != "none" and self.Primary.Ammo != "" then
|
||||
local ammo = self:Ammo1()
|
||||
|
||||
-- Do not drop ammo if we have another gun that uses this type
|
||||
for _, w in ipairs(self:GetOwner():GetWeapons()) do
|
||||
if IsValid(w) and w != self and w:GetPrimaryAmmoType() == self:GetPrimaryAmmoType() then
|
||||
ammo = 0
|
||||
end
|
||||
end
|
||||
|
||||
self.StoredAmmo = ammo
|
||||
|
||||
if ammo > 0 then
|
||||
self:GetOwner():RemoveAmmo(ammo, self.Primary.Ammo)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:DampenDrop()
|
||||
-- For some reason gmod drops guns on death at a speed of 400 units, which
|
||||
-- catapults them away from the body. Here we want people to actually be able
|
||||
-- to find a given corpse's weapon, so we override the velocity here and call
|
||||
-- this when dropping guns on death.
|
||||
local phys = self:GetPhysicsObject()
|
||||
if IsValid(phys) then
|
||||
phys:SetVelocityInstantaneous(Vector(0,0,-75) + phys:GetVelocity() * 0.001)
|
||||
phys:AddAngleVelocity(phys:GetAngleVelocity() * -0.99)
|
||||
end
|
||||
end
|
||||
|
||||
SWEP.StoredAmmo = 0
|
||||
|
||||
-- Picked up by player. Transfer of stored ammo and such.
|
||||
|
||||
function SWEP:Equip(newowner)
|
||||
if SERVER then
|
||||
if self:IsOnFire() then
|
||||
self:Extinguish()
|
||||
end
|
||||
|
||||
self.fingerprints = self.fingerprints or {}
|
||||
|
||||
if !table.HasValue(self.fingerprints, newowner) then
|
||||
table.insert(self.fingerprints, newowner)
|
||||
end
|
||||
|
||||
if newowner:GetActiveWeapon() != self and self:GetValue("HolsterVisible") then
|
||||
net.Start("TacRP_updateholster")
|
||||
net.WriteEntity(self:GetOwner())
|
||||
net.WriteEntity(self)
|
||||
net.Broadcast()
|
||||
end
|
||||
end
|
||||
|
||||
if SERVER and IsValid(newowner) and self.Primary.ClipMax and self.StoredAmmo > 0 and self.Primary.Ammo != "none" and self.Primary.Ammo != "" then
|
||||
local ammo = newowner:GetAmmoCount(self.Primary.Ammo)
|
||||
local given = math.min(self.StoredAmmo, self.Primary.ClipMax - ammo)
|
||||
|
||||
newowner:GiveAmmo(given, self.Primary.Ammo)
|
||||
self.StoredAmmo = 0
|
||||
end
|
||||
|
||||
self:SetHolsterTime(0)
|
||||
self:SetHolsterEntity(NULL)
|
||||
self:SetReloadFinishTime(0)
|
||||
end
|
||||
|
||||
-- other guns may use this function to setup stuff
|
||||
function SWEP:TTTBought(buyer)
|
||||
end
|
||||
|
||||
function SWEP:WasBought(buyer)
|
||||
if buyer:GetActiveWeapon() != self and self:GetValue("HolsterVisible") then
|
||||
net.Start("TacRP_updateholster")
|
||||
net.WriteEntity(self:GetOwner())
|
||||
net.WriteEntity(self)
|
||||
net.Broadcast()
|
||||
end
|
||||
|
||||
self:TTTBought(buyer)
|
||||
end
|
||||
|
||||
function SWEP:TTT_PostAttachments()
|
||||
end
|
||||
|
||||
function SWEP:TTT_Init()
|
||||
if engine.ActiveGamemode() != "terrortown" then return end
|
||||
|
||||
if SERVER then
|
||||
self.fingerprints = {}
|
||||
|
||||
|
||||
local att_chance = TacRP.ConVars["ttt_atts_random"]:GetFloat()
|
||||
local att_max = TacRP.ConVars["ttt_atts_max"]:GetFloat()
|
||||
local added = 0
|
||||
|
||||
if att_chance > 0 then
|
||||
for i, slot in pairs(self.Attachments) do
|
||||
if math.random() > att_chance then continue end
|
||||
|
||||
local atts = TacRP.GetAttsForCats(slot.Category or "", self)
|
||||
local ind = math.random(1, #atts)
|
||||
slot.Installed = atts[ind]
|
||||
added = added + 1
|
||||
|
||||
if att_max > 0 and added >= att_max then break end
|
||||
end
|
||||
|
||||
self:InvalidateCache()
|
||||
self:SetBaseSettings()
|
||||
|
||||
if added > 0 then
|
||||
self:NetworkWeapon()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if self.PrimaryGrenade then
|
||||
self.Primary.ClipMax = 1
|
||||
return
|
||||
end
|
||||
|
||||
self.Primary.ClipMax = TacRP.TTTAmmoToClipMax[string.lower(self.AmmoTTT or self.Ammo)] or self.Primary.ClipSize * 2
|
||||
self:SetClip1(self.Primary.ClipSize)
|
||||
self.GaveDefaultAmmo = true
|
||||
end
|
||||
|
||||
--- TTT2 uses this to populate custom convars in the equip menu
|
||||
function SWEP:AddToSettingsMenu(parent)
|
||||
end
|
||||
91
garrysmod/addons/tacrp/lua/weapons/tacrp_base/sh_util.lua
Normal file
91
garrysmod/addons/tacrp/lua/weapons/tacrp_base/sh_util.lua
Normal file
@@ -0,0 +1,91 @@
|
||||
function SWEP:SanityCheck()
|
||||
if !IsValid(self) then return false end
|
||||
if !IsValid(self:GetOwner()) then return false end
|
||||
if !IsValid(self:GetVM()) then return false end
|
||||
end
|
||||
|
||||
function SWEP:GetVM()
|
||||
if !IsValid(self:GetOwner()) or !self:GetOwner():IsPlayer() then return end
|
||||
return self:GetOwner():GetViewModel()
|
||||
end
|
||||
|
||||
function SWEP:Curve(x)
|
||||
return 0.5 * math.cos((x + 1) * math.pi) + 0.5
|
||||
end
|
||||
|
||||
SWEP.LastSysTime = SysTime()
|
||||
function SWEP:DeltaSysTime()
|
||||
local ret = (SysTime() - (self.LastSysTime or SysTime())) * GetConVar("host_timescale"):GetFloat()
|
||||
return ret
|
||||
end
|
||||
|
||||
function SWEP:IsAnimLocked()
|
||||
return self:GetAnimLockTime() > CurTime()
|
||||
end
|
||||
|
||||
function SWEP:ChooseSound(tbl)
|
||||
if !istable(tbl) then return tbl end
|
||||
tbl.BaseClass = nil -- lua tables lel
|
||||
return tbl[math.random(1, #tbl)]
|
||||
end
|
||||
|
||||
function SWEP:OnReloaded()
|
||||
self:InvalidateCache()
|
||||
self:SetBaseSettings()
|
||||
|
||||
hook.Run("TacRP_WeaponReloaded", self)
|
||||
end
|
||||
|
||||
function SWEP:DoLowerIrons()
|
||||
if self:GetValue("Holosight") or self:GetValue("ScopeOverlay") then return false end
|
||||
local i = TacRP.ConVars["irons_lower"]:GetInt()
|
||||
return i == 2 or (i == 1 and engine.ActiveGamemode() == "terrortown")
|
||||
end
|
||||
|
||||
function SWEP:DoProceduralIrons()
|
||||
local i = TacRP.ConVars["irons_procedural"]:GetInt()
|
||||
return self:GetValue("ProceduralIronFire") and (i == 2 or (i == 1 and self:GetValue("Holosight"))) --and (!self.LastShot or self:Clip1() > 1)
|
||||
end
|
||||
|
||||
function SWEP:CountAttachments()
|
||||
local count = 0
|
||||
for k, v in ipairs(self.Attachments) do
|
||||
if v.Installed then count = count + 1 end
|
||||
end
|
||||
return count
|
||||
end
|
||||
|
||||
function SWEP:GetMaxClip1()
|
||||
return self:GetCapacity()
|
||||
end
|
||||
|
||||
function SWEP:IsDamageConstant(base)
|
||||
local valfunc = base and self.GetBaseValue or self.GetValue
|
||||
return valfunc(self, "Damage_Min") == valfunc(self, "Damage_Max")
|
||||
end
|
||||
|
||||
function SWEP:GetPingOffsetScale()
|
||||
if game.SinglePlayer() then return 0 end
|
||||
|
||||
return (self:GetOwner():Ping() - 5) / 1000
|
||||
end
|
||||
|
||||
function SWEP:ScaleFOVByWidthRatio(fovDegrees, ratio)
|
||||
local halfAngleRadians = fovDegrees * (0.5 * math.pi / 180)
|
||||
local t = math.tan(halfAngleRadians)
|
||||
t = t * ratio
|
||||
local retDegrees = (180 / math.pi) * math.atan(t)
|
||||
|
||||
return retDegrees * 2
|
||||
end
|
||||
|
||||
function SWEP:WidescreenFix(target)
|
||||
return self:ScaleFOVByWidthRatio(target, ((ScrW and ScrW() or 4) / (ScrH and ScrH() or 3)) / (4 / 3))
|
||||
end
|
||||
|
||||
function SWEP:UseAltRecoil(base)
|
||||
local valfunc = base and self.GetBaseValue or self.GetValue
|
||||
if valfunc(self, "AlwaysAltRecoil") then return true end
|
||||
if valfunc(self, "NeverAltRecoil") then return false end
|
||||
return TacRP.ConVars["altrecoil"]:GetBool()
|
||||
end
|
||||
356
garrysmod/addons/tacrp/lua/weapons/tacrp_base/sh_vm.lua
Normal file
356
garrysmod/addons/tacrp/lua/weapons/tacrp_base/sh_vm.lua
Normal file
@@ -0,0 +1,356 @@
|
||||
local customizedelta = 0
|
||||
local sightdelta = 0
|
||||
local sprintdelta = 0
|
||||
local peekdelta = 0
|
||||
local bipoddelta = 0
|
||||
local blindfiredelta, blindfiredeltaleft, blindfiredeltaright, blindfiredeltakys = 0, 0, 0, 0
|
||||
local freeaim_p, freeaim_y = 0, 0
|
||||
local nearwalldelta = 0
|
||||
|
||||
local angle_zero = Angle(0, 0, 0)
|
||||
local vector_origin = Vector(0, 0, 0)
|
||||
|
||||
local peekvector = Vector(0, 0, -2)
|
||||
|
||||
local m_appor = math.Approach
|
||||
local f_lerp = function(dlt, from, to) return from + (to - from) * dlt end
|
||||
local function ApproachMod(usrobj, to, dlt)
|
||||
usrobj[1] = m_appor(usrobj[1], to[1], dlt)
|
||||
usrobj[2] = m_appor(usrobj[2], to[2], dlt)
|
||||
usrobj[3] = m_appor(usrobj[3], to[3], dlt)
|
||||
end
|
||||
|
||||
local function LerpMod(usrobj, to, dlt, clamp_ang)
|
||||
usrobj[1] = f_lerp(dlt, usrobj[1], to[1])
|
||||
usrobj[2] = f_lerp(dlt, usrobj[2], to[2])
|
||||
usrobj[3] = f_lerp(dlt, usrobj[3], to[3])
|
||||
if clamp_ang then
|
||||
for i = 1, 3 do usrobj[i] = math.NormalizeAngle(usrobj[i]) end
|
||||
end
|
||||
end
|
||||
|
||||
SWEP.BenchPos = SWEP.BenchPos or Vector(0, 0, 0)
|
||||
SWEP.BenchAng = SWEP.BenchAng or Angle(0, 0, 0)
|
||||
|
||||
SWEP.ViewModelPos = Vector(0, 0, 0)
|
||||
SWEP.ViewModelAng = Angle(0, 0, 0)
|
||||
|
||||
function SWEP:GetViewModelPosition(pos, ang)
|
||||
if !IsValid(self:GetOwner()) then
|
||||
return Vector(0, 0, 0), Angle(0, 0, 0)
|
||||
end
|
||||
|
||||
if TacRP.ConVars["dev_benchgun"]:GetBool() then
|
||||
return self.BenchPos, self.BenchAng
|
||||
end
|
||||
self.BenchPos = pos
|
||||
self.BenchAng = ang
|
||||
|
||||
local vm = self:GetOwner():GetViewModel()
|
||||
local FT = self:DeltaSysTime() -- FrameTime()
|
||||
local RFT = RealFrameTime()
|
||||
|
||||
ang = ang - (self:GetOwner():GetViewPunchAngles() * 0.5)
|
||||
|
||||
local oldang = Angle(0, 0, 0)
|
||||
|
||||
oldang:Set(ang)
|
||||
|
||||
local offsetpos = Vector(self.PassivePos)
|
||||
local offsetang = Angle(self.PassiveAng)
|
||||
|
||||
local extra_offsetpos = Vector(0, 0, 0)
|
||||
local extra_offsetang = Angle(0, 0, 0)
|
||||
|
||||
-- local cor_val = (self.ViewModelFOV / self:GetShouldFOV())
|
||||
local cor_val = 0.75
|
||||
|
||||
---------------------------------------------
|
||||
-- Blindfire
|
||||
---------------------------------------------
|
||||
local bfmode = self:GetBlindFireMode()
|
||||
local bfl = bfmode == TacRP.BLINDFIRE_LEFT
|
||||
local bfr = bfmode == TacRP.BLINDFIRE_RIGHT
|
||||
local bfs = bfmode == TacRP.BLINDFIRE_KYS
|
||||
blindfiredelta = math.Approach(blindfiredelta, self:GetBlindFire() and 1 or 0, FT / 0.3)
|
||||
blindfiredeltaleft = math.Approach(blindfiredeltaleft, bfl and 1 or 0, FT / (bfr and 0.45 or 0.3))
|
||||
blindfiredeltaright = math.Approach(blindfiredeltaright, bfr and 1 or 0, FT / (bfl and 0.45 or 0.3))
|
||||
blindfiredeltakys = math.Approach(blindfiredeltakys, bfs and 1 or 0, FT / (bfs and 0.75 or 0.3))
|
||||
if blindfiredelta > 0 then
|
||||
local curvedblindfiredelta = self:Curve(blindfiredelta)
|
||||
local curvedblindfiredeltaleft = self:Curve(blindfiredeltaleft)
|
||||
local curvedblindfiredeltaright = self:Curve(blindfiredeltaright)
|
||||
local curvedblindfiredeltakys = self:Curve(blindfiredeltakys)
|
||||
|
||||
offsetpos = LerpVector(curvedblindfiredelta, offsetpos, self:GetValue("BlindFirePos"))
|
||||
offsetang = LerpAngle(curvedblindfiredelta, offsetang, self:GetValue("BlindFireAng"))
|
||||
|
||||
if curvedblindfiredeltaleft > 0 then
|
||||
offsetpos = LerpVector(curvedblindfiredeltaleft, offsetpos, self:GetValue("BlindFireLeftPos"))
|
||||
offsetang = LerpAngle(curvedblindfiredeltaleft, offsetang, self:GetValue("BlindFireLeftAng"))
|
||||
end
|
||||
|
||||
if curvedblindfiredeltaright > 0 then
|
||||
offsetpos = LerpVector(curvedblindfiredeltaright, offsetpos, self:GetValue("BlindFireRightPos"))
|
||||
offsetang = LerpAngle(curvedblindfiredeltaright, offsetang, self:GetValue("BlindFireRightAng"))
|
||||
end
|
||||
|
||||
if curvedblindfiredeltakys > 0 then
|
||||
offsetpos = LerpVector(curvedblindfiredeltakys, offsetpos, self:GetValue("BlindFireSuicidePos"))
|
||||
offsetang = LerpAngle(curvedblindfiredeltakys, offsetang, self:GetValue("BlindFireSuicideAng"))
|
||||
end
|
||||
end
|
||||
|
||||
---------------------------------------------
|
||||
-- Aiming & Peeking
|
||||
---------------------------------------------
|
||||
local ads = self:GetAimDownSightsTime()
|
||||
if self:GetScopeLevel() > 0 then
|
||||
if self:GetSprintLockTime() > CurTime() then
|
||||
ads = ads + self:GetSprintToFireTime()
|
||||
end
|
||||
sightdelta = m_appor(sightdelta, 1, FT / ads)
|
||||
else
|
||||
sightdelta = m_appor(sightdelta, 0, FT / ads)
|
||||
end
|
||||
|
||||
-- if IsFirstTimePredicted() or game.SinglePlayer() then
|
||||
if self:GetPeeking() then
|
||||
peekdelta = m_appor(peekdelta, 1, FT / 0.2)
|
||||
else
|
||||
peekdelta = m_appor(peekdelta, 0, FT / 0.2)
|
||||
end
|
||||
-- end
|
||||
|
||||
local curvedsightdelta = self:Curve(sightdelta)
|
||||
local curvedpeekdelta = self:Curve(peekdelta)
|
||||
|
||||
-- cor_val = Lerp(sightdelta, cor_val, 1)
|
||||
|
||||
local ppos = Vector(self:GetValue("PeekPos")) * curvedpeekdelta
|
||||
local pang = Angle(self:GetValue("PeekAng")) * curvedpeekdelta
|
||||
|
||||
if sightdelta > 0 then
|
||||
local sightpos, sightang = self:GetSightPositions()
|
||||
|
||||
if self:DoLowerIrons() then
|
||||
sightpos = sightpos + LerpVector(curvedpeekdelta, peekvector, vector_origin)
|
||||
end
|
||||
|
||||
LerpMod(offsetpos, sightpos + ppos, curvedsightdelta)
|
||||
LerpMod(offsetang, sightang + pang, curvedsightdelta, true)
|
||||
|
||||
local eepos, eeang = self:GetExtraSightPosition()
|
||||
local im = self:GetValue("SightMidPoint")
|
||||
local midpoint = curvedsightdelta * math.cos(curvedsightdelta * (math.pi / 2)) * (1 - curvedpeekdelta)
|
||||
local joffset = (im and im.Pos or Vector(0, 0, 0) + ppos) * midpoint
|
||||
local jaffset = (im and im.Ang or Angle(0, 0, 0) + pang) * midpoint
|
||||
|
||||
LerpMod(extra_offsetpos, -eepos + joffset, curvedsightdelta)
|
||||
LerpMod(extra_offsetang, -eeang + jaffset, curvedsightdelta)
|
||||
end
|
||||
|
||||
---------------------------------------------
|
||||
-- Bipod
|
||||
---------------------------------------------
|
||||
local amt = math.Clamp(self:GetBipodPos():Distance(self:GetOwner():EyePos()) / 60, 0.15, 0.3)
|
||||
bipoddelta = math.Approach(bipoddelta, self:GetInBipod() and 1 or 0, FT / (self:GetInBipod() and 0.4 or amt))
|
||||
|
||||
if bipoddelta > 0 then
|
||||
local curvedbipoddelta = self:Curve(bipoddelta)
|
||||
pos = LerpVector(math.Clamp(curvedbipoddelta - curvedsightdelta, 0, 1), pos, self:GetBipodPos())
|
||||
end
|
||||
|
||||
---------------------------------------------
|
||||
-- Procedural Firing
|
||||
---------------------------------------------
|
||||
local procdata = self:GetValue("ProceduralIronFire")
|
||||
if IsValid(vm) and procdata then
|
||||
local dt = math.max(0, UnPredictedCurTime() - self:GetLastProceduralFireTime() + self:GetPingOffsetScale())
|
||||
|
||||
if dt <= procdata.tmax then
|
||||
self.ProceduralIronCleanup = false
|
||||
if !(self:GetValue("LastShot") and self:Clip1() == 0) then
|
||||
for k, v in pairs(procdata.bones or {}) do
|
||||
local bone = vm:LookupBone(v.bone or "")
|
||||
if !bone then continue end
|
||||
|
||||
local f = 1
|
||||
if v.t0 == 0 then
|
||||
f = v.t1 and math.Clamp(1 - dt / v.t1, 0, 1) or 0
|
||||
else
|
||||
f = v.t1 and (dt > v.t0 and math.Clamp(1 - (dt - v.t0) / (v.t1 - v.t0), 0, 1) or (dt / v.t0)) or (dt > v.t0 and 1 or (dt / v.t0))
|
||||
end
|
||||
if v.pos then
|
||||
local offset = LerpVector(f, vector_origin, v.pos)
|
||||
vm:ManipulateBonePosition(bone, offset, false)
|
||||
end
|
||||
if v.ang then
|
||||
local offset = LerpAngle(f, angle_zero, v.ang)
|
||||
vm:ManipulateBoneAngles(bone, offset, false)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local dtc = math.ease.InQuad(math.Clamp(1 - dt / procdata.t, 0, 1))
|
||||
|
||||
if dtc > 0 and procdata.vm_pos then
|
||||
LerpMod(offsetpos, offsetpos + procdata.vm_pos, dtc)
|
||||
end
|
||||
if dtc > 0 and procdata.vm_ang then
|
||||
LerpMod(offsetang, offsetang + procdata.vm_ang, dtc, true)
|
||||
end
|
||||
elseif !self.ProceduralIronCleanup then
|
||||
self.ProceduralIronCleanup = true
|
||||
for k, v in pairs(procdata.bones or {}) do
|
||||
local bone = vm:LookupBone(v.bone or "")
|
||||
if !bone then continue end
|
||||
if v.pos then
|
||||
vm:ManipulateBonePosition(bone, vector_origin, false)
|
||||
end
|
||||
if v.ang then
|
||||
vm:ManipulateBoneAngles(bone, angle_zero, false)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
---------------------------------------------
|
||||
-- Free Aim & Sway
|
||||
---------------------------------------------
|
||||
local swayang = self:GetSwayAngles()
|
||||
extra_offsetang.y = extra_offsetang.y - (swayang.p * cor_val)
|
||||
extra_offsetang.p = extra_offsetang.p + (swayang.y * cor_val)
|
||||
|
||||
local idlesway = Lerp(self:GetSightDelta(), 1 / 3, 0)
|
||||
extra_offsetpos.x = extra_offsetpos.x + (swayang.y * cor_val * idlesway)
|
||||
extra_offsetpos.z = extra_offsetpos.z + (swayang.p * cor_val * idlesway)
|
||||
|
||||
local freeaimang = self:GetFreeAimOffset()
|
||||
|
||||
freeaim_p = f_lerp(0.5, freeaim_p, freeaimang.p)
|
||||
freeaim_y = f_lerp(0.5, freeaim_y, freeaimang.y)
|
||||
freeaim_p = m_appor(freeaim_p, freeaimang.p, FT)
|
||||
freeaim_y = m_appor(freeaim_y, freeaimang.y, FT)
|
||||
|
||||
extra_offsetang.y = extra_offsetang.y - (freeaim_p * cor_val)
|
||||
extra_offsetang.p = extra_offsetang.p + (freeaim_y * cor_val)
|
||||
|
||||
---------------------------------------------
|
||||
-- Customization
|
||||
---------------------------------------------
|
||||
-- if IsFirstTimePredicted() or game.SinglePlayer() then
|
||||
if self:GetCustomize() then
|
||||
customizedelta = m_appor(customizedelta, 1, RealFrameTime() * 1 / 0.15)
|
||||
else
|
||||
customizedelta = m_appor(customizedelta, 0, RealFrameTime() * 1 / 0.15)
|
||||
end
|
||||
-- end
|
||||
|
||||
if customizedelta > 0 then
|
||||
local curvedcustomizedelta = self:Curve(customizedelta)
|
||||
LerpMod(offsetpos, self:GetValue("CustomizePos"), curvedcustomizedelta)
|
||||
LerpMod(offsetang, self:GetValue("CustomizeAng"), curvedcustomizedelta)
|
||||
|
||||
LerpMod(extra_offsetang, angle_zero, curvedcustomizedelta, true)
|
||||
end
|
||||
|
||||
---------------------------------------------
|
||||
-- Sprinting
|
||||
---------------------------------------------
|
||||
local stf = self:GetSprintToFireTime()
|
||||
if self:GetCustomize() or self:GetInBipod() or (!self:GetSafe() and !self.LastWasSprinting and !self:ShouldLowerWeapon()) then
|
||||
-- not accurate to how sprint progress works but looks much smoother
|
||||
if self:GetScopeLevel() > 0 and self:GetSprintLockTime() > UnPredictedCurTime() then
|
||||
stf = stf + self:GetAimDownSightsTime() * 0.5
|
||||
end
|
||||
sprintdelta = m_appor(sprintdelta, 0, FT / stf)
|
||||
self.LastReloadEnd = nil
|
||||
elseif self:GetReloading() then
|
||||
if (self.LastWasSprinting or self:ShouldLowerWeapon()) and self:GetEndReload() then
|
||||
self.LastReloadEnd = self.LastReloadEnd or (self:GetReloadFinishTime() - UnPredictedCurTime())
|
||||
sprintdelta = 1 - self:Curve((self:GetReloadFinishTime() - UnPredictedCurTime()) / self.LastReloadEnd)
|
||||
else
|
||||
sprintdelta = m_appor(sprintdelta, 0, FT / 0.5)
|
||||
end
|
||||
else
|
||||
if self:GetLastMeleeTime() + 0.5 > CurTime() or self:GetStartPrimedGrenadeTime() + 0.8 > CurTime() then
|
||||
sprintdelta = m_appor(sprintdelta, 0, FT / 0.2)
|
||||
else
|
||||
sprintdelta = m_appor(sprintdelta, 1, FT / stf)
|
||||
end
|
||||
self.LastReloadEnd = nil
|
||||
end
|
||||
local curvedsprintdelta = self:Curve(sprintdelta)
|
||||
if curvedsprintdelta > 0 then
|
||||
LerpMod(offsetpos, self:GetValue("SprintPos"), curvedsprintdelta)
|
||||
LerpMod(offsetang, self:GetValue("SprintAng"), curvedsprintdelta)
|
||||
LerpMod(extra_offsetang, angle_zero, curvedsprintdelta, true)
|
||||
|
||||
local sim = self:GetValue("SprintMidPoint")
|
||||
local spr_midpoint = curvedsprintdelta * math.cos(curvedsprintdelta * (math.pi / 2))
|
||||
local spr_joffset = (sim and sim.Pos or Vector(0, 0, 0)) * spr_midpoint
|
||||
local spr_jaffset = (sim and sim.Ang or Angle(0, 0, 0)) * spr_midpoint
|
||||
extra_offsetpos:Add(spr_joffset)
|
||||
extra_offsetang:Add(spr_jaffset)
|
||||
end
|
||||
|
||||
---------------------------------------------
|
||||
-- Near Walling
|
||||
---------------------------------------------
|
||||
nearwalldelta = m_appor(nearwalldelta, self:GetNearWallAmount(), FT / 0.3)
|
||||
local curvednearwalldelta = self:Curve(nearwalldelta) - customizedelta - sightdelta
|
||||
if curvednearwalldelta > 0 then
|
||||
local sprpos = LerpVector(curvednearwalldelta, vector_origin, self:GetValue("NearWallPos"))
|
||||
local sprang = LerpAngle(curvednearwalldelta, angle_zero, self:GetValue("NearWallAng"))
|
||||
|
||||
local pointdir = self:GetOwner():WorldToLocalAngles(self:GetShootDir())
|
||||
|
||||
extra_offsetpos:Add(pointdir:Right() * sprpos[2])
|
||||
extra_offsetpos:Add(pointdir:Forward() * sprpos[1])
|
||||
extra_offsetpos:Add(pointdir:Up() * sprpos[3])
|
||||
|
||||
extra_offsetang:Add(sprang)
|
||||
end
|
||||
|
||||
self.SwayScale = f_lerp(sightdelta, 1, 0.1)
|
||||
self.BobScale = 0
|
||||
|
||||
local speed = 15 * FT * (game.SinglePlayer() and 1 or 2)
|
||||
|
||||
LerpMod(self.ViewModelPos, offsetpos, speed)
|
||||
LerpMod(self.ViewModelAng, offsetang, speed, true)
|
||||
ApproachMod(self.ViewModelPos, offsetpos, speed * 0.1)
|
||||
ApproachMod(self.ViewModelAng, offsetang, speed * 0.1)
|
||||
|
||||
self.ViewModelAng:Normalize()
|
||||
|
||||
pos = pos + (ang:Right() * offsetpos[1])
|
||||
pos = pos + (ang:Forward() * offsetpos[2])
|
||||
pos = pos + (ang:Up() * offsetpos[3])
|
||||
|
||||
ang:RotateAroundAxis(ang:Up(), offsetang[1])
|
||||
ang:RotateAroundAxis(ang:Right(), offsetang[2])
|
||||
ang:RotateAroundAxis(ang:Forward(), offsetang[3])
|
||||
|
||||
pos = pos + (oldang:Right() * extra_offsetpos[1])
|
||||
pos = pos + (oldang:Forward() * extra_offsetpos[2])
|
||||
pos = pos + (oldang:Up() * extra_offsetpos[3])
|
||||
|
||||
ang:RotateAroundAxis(oldang:Up(), extra_offsetang[1])
|
||||
ang:RotateAroundAxis(oldang:Right(), extra_offsetang[2])
|
||||
ang:RotateAroundAxis(oldang:Forward(), extra_offsetang[3])
|
||||
|
||||
pos, ang = self:GetViewModelBob(pos, ang)
|
||||
pos, ang = self:GetViewModelSway(pos, ang)
|
||||
|
||||
self.ViewModelPos = pos
|
||||
self.ViewModelAng = ang
|
||||
|
||||
self.LastSysTime = SysTime()
|
||||
return pos, ang
|
||||
end
|
||||
|
||||
function SWEP:TranslateFOV(fov)
|
||||
return fov
|
||||
end
|
||||
666
garrysmod/addons/tacrp/lua/weapons/tacrp_base/shared.lua
Normal file
666
garrysmod/addons/tacrp/lua/weapons/tacrp_base/shared.lua
Normal file
@@ -0,0 +1,666 @@
|
||||
// spawnable
|
||||
SWEP.Spawnable = false
|
||||
SWEP.AdminOnly = false
|
||||
|
||||
// names and stuff
|
||||
SWEP.PrintName = "Arctic's Tactical RP Base"
|
||||
SWEP.Category = "[FT] Оружие"
|
||||
|
||||
SWEP.Description = ""
|
||||
SWEP.Description_Quote = nil // Italics, always on last line. Make sure to save some space for it (may overlap)
|
||||
|
||||
SWEP.Trivia_Caliber = nil
|
||||
SWEP.Trivia_Manufacturer = nil
|
||||
SWEP.Trivia_Year = nil // Production Year
|
||||
|
||||
SWEP.Faction = TacRP.FACTION_NEUTRAL // Only used in trivia for now
|
||||
// Valid values: TacRP.FACTION_NEUTRAL, TacRP.FACTION_COALITION, TacRP.FACTION_MILITIA
|
||||
|
||||
SWEP.Credits = nil // Multiline string like Description
|
||||
|
||||
SWEP.ViewModel = ""
|
||||
SWEP.WorldModel = ""
|
||||
|
||||
SWEP.ViewModelFOV = 65
|
||||
|
||||
SWEP.NoRanger = false
|
||||
SWEP.NoStatBox = false
|
||||
|
||||
SWEP.NPCUsable = true
|
||||
|
||||
SWEP.Slot = 1
|
||||
|
||||
SWEP.RenderGroup = RENDERGROUP_BOTH
|
||||
|
||||
SWEP.BalanceStats = {} // replacement stats for each TacRP.BALANCE_ enum
|
||||
|
||||
// What weapon this will replace in TTT if enabled. Use TacRP.TTTReplacePreset presets or define your own
|
||||
SWEP.TTTReplace = nil // {["weapon_ttt_glock"] = 1} // key is weapon to replace, value is relative weight.
|
||||
|
||||
// "ballistics"
|
||||
|
||||
SWEP.Damage_Max = 30 // damage at minimum range
|
||||
SWEP.Damage_Min = 20 // damage at maximum range
|
||||
SWEP.Range_Min = 256 // distance for which to maintain maximum damage
|
||||
SWEP.Range_Max = 1024 // distance at which we drop to minimum damage
|
||||
SWEP.Penetration = 0 // units of metal this weapon can penetrate
|
||||
SWEP.ArmorPenetration = 0.5 // How good the weapon can penetrate body armor.
|
||||
SWEP.ArmorBonus = 1 // multiplier on armor damage
|
||||
|
||||
SWEP.DamageMultNPC = 1 // Damage multiplier against NPCs and Nextbots
|
||||
SWEP.DamageType = nil // override damage type
|
||||
|
||||
SWEP.ShootEnt = false
|
||||
SWEP.ShootEntForce = 10000
|
||||
|
||||
SWEP.Num = 1
|
||||
SWEP.TracerNum = 1
|
||||
SWEP.BodyDamageMultipliers = {
|
||||
[HITGROUP_HEAD] = 1.25,
|
||||
[HITGROUP_CHEST] = 1,
|
||||
[HITGROUP_STOMACH] = 1,
|
||||
[HITGROUP_LEFTARM] = 1,
|
||||
[HITGROUP_RIGHTARM] = 1,
|
||||
[HITGROUP_LEFTLEG] = 0.9,
|
||||
[HITGROUP_RIGHTLEG] = 0.9,
|
||||
[HITGROUP_GEAR] = 0.9
|
||||
}
|
||||
|
||||
SWEP.MuzzleVelocity = 30000
|
||||
|
||||
SWEP.ExplosiveEffect = nil
|
||||
SWEP.ExplosiveDamage = 0
|
||||
SWEP.ExplosiveRadius = 0
|
||||
|
||||
// misc. shooting
|
||||
|
||||
// Firemode system works just like ArcCW
|
||||
// 2 = full auto
|
||||
// 1 = semi auto
|
||||
// 0 = safe
|
||||
// negative numbers = burst
|
||||
SWEP.Firemode = 2
|
||||
|
||||
SWEP.Firemodes = nil // {1, 2, 0} ...
|
||||
|
||||
SWEP.RunawayBurst = false // continue firing until burst is completely expended
|
||||
SWEP.PostBurstDelay = 0 // only applies to runaway burst guns
|
||||
SWEP.AutoBurst = false // hold the trigger to keep firing burst after burst
|
||||
|
||||
SWEP.RPM = 600
|
||||
SWEP.RPMMultBurst = 1 // modify RPM while in burst mode
|
||||
SWEP.RPMMultSemi = 1 // modify RPM while in semi mode
|
||||
|
||||
SWEP.Spread = 0.01
|
||||
|
||||
SWEP.ShootTimeMult = 1
|
||||
|
||||
SWEP.Bipod = false // Weapon can deploy bipod
|
||||
SWEP.BipodRecoil = 0.35 // Recoil Amount multiplier per shot
|
||||
SWEP.BipodKick = 0.25 // Recoil Kick multiplier
|
||||
|
||||
SWEP.TriggerDelay = 0.0
|
||||
|
||||
// SWEP.ShootChance = 1
|
||||
SWEP.JamWaitTime = 0.3
|
||||
SWEP.JamFactor = 0 // higher = more frequent jams. no jams at 0
|
||||
SWEP.JamTakesRound = false // consume ammo on jam
|
||||
SWEP.JamSkipFix = false // only do dryfire and the initial delay. use on revolvers mostly
|
||||
SWEP.JamBaseMSB = nil // use this number as the base value instead of being based on ammo.
|
||||
|
||||
// Spread penalties are in spread units and are additive
|
||||
SWEP.MoveSpreadPenalty = 0.01 // spread penalty while travelling at max. 250 u/s
|
||||
SWEP.MidAirSpreadPenalty = 0.1 // spread penalty for being in the air
|
||||
SWEP.HipFireSpreadPenalty = 0.02 // spread penalty for not being scoped in
|
||||
SWEP.ScopedSpreadPenalty = 0 // spread penalty for... being scoped in?
|
||||
SWEP.BlindFireSpreadPenalty = 0 // spread penalty for blind firing
|
||||
SWEP.CrouchSpreadPenalty = 0
|
||||
SWEP.PeekPenaltyFraction = 0.3 // percentage of hipfire penalty to use while peeking in sights
|
||||
|
||||
// Technically does not affect recoil at all, but affects spread (now called "bloom")
|
||||
SWEP.RecoilPerShot = 1
|
||||
SWEP.RecoilMaximum = 10
|
||||
SWEP.RecoilResetTime = 0 // time after you stop shooting for recoil to start dissipating
|
||||
SWEP.RecoilDissipationRate = 2
|
||||
SWEP.RecoilFirstShotMult = 1 // multiplier for the first shot's recoil amount
|
||||
SWEP.RecoilSpreadPenalty = 0.001 // extra spread per one unit of recoil
|
||||
SWEP.RecoilResetInstant = true // Set false to account for RPM.
|
||||
|
||||
SWEP.RecoilMultBurst = 0.9 // Affects both "bloom" and recoil kick while current firemode is burst
|
||||
SWEP.RecoilMultSemi = 0.9 // Affects both "bloom" and recoil kick while current firemode is semi
|
||||
SWEP.RecoilMultCrouch = 0.85 // Affects both "bloom" and recoil kick while crouched
|
||||
|
||||
// Controls alternate bloom behavior, defaults to convar
|
||||
SWEP.AlwaysAltRecoil = nil
|
||||
SWEP.NeverAltRecoil = nil
|
||||
|
||||
SWEP.RecoilVisualKick = 0.1
|
||||
SWEP.RecoilKick = 0.25
|
||||
SWEP.RecoilStability = 0 // Direction of recoil kick, 1 is completely vertical and 0 is 180deg cone
|
||||
SWEP.RecoilAltMultiplier = 200 // Multiplier to RecoilSpreadPenalty when using alternative recoil mode.
|
||||
|
||||
SWEP.ShotgunPelletSpread = 0 // per-pellet spread for shotguns (if enabled). Otherwise just adds to spread
|
||||
|
||||
SWEP.NoRecoilPattern = false // set true to not use recoil patterns for this gun
|
||||
SWEP.RecoilPatternSeed = nil // custom seed. Defaults to weapon class
|
||||
|
||||
SWEP.CanBlindFire = true
|
||||
|
||||
SWEP.CannotHipFire = false
|
||||
|
||||
// lockon
|
||||
|
||||
// We're using a new system for lockon now, which doesn't use this system.
|
||||
// SWEP.LockOnAngle = math.cos(math.rad(5))
|
||||
SWEP.LockOnTrackAngle = 5
|
||||
SWEP.LockOnRange = 3500
|
||||
|
||||
SWEP.LockOnTime = 1.5
|
||||
|
||||
SWEP.ProvideTargetData = true
|
||||
|
||||
SWEP.LockOnOutOfSights = false
|
||||
SWEP.LockOnInSights = false
|
||||
|
||||
SWEP.RequireLockOn = false // Cannot shoot without a lock
|
||||
|
||||
// handling
|
||||
|
||||
SWEP.MoveSpeedMult = 1
|
||||
SWEP.ShootingSpeedMult = 0.5 // slow down applied while shooting
|
||||
SWEP.SightedSpeedMult = 0.5
|
||||
SWEP.MeleeSpeedMult = 0.75
|
||||
SWEP.MeleeSpeedMultTime = 1 // seconds to apply slow down for
|
||||
SWEP.ReloadSpeedMult = 1
|
||||
SWEP.ReloadSpeedMultTime = 0.5 // duration for slowdown to fade out for AFTER RELOAD FINISHES
|
||||
|
||||
SWEP.ShootWhileSprint = false
|
||||
|
||||
SWEP.AimDownSightsTime = 0.25
|
||||
SWEP.SprintToFireTime = 0.25 // how long it takes to go from sprinting to shooting
|
||||
|
||||
// hold types
|
||||
|
||||
SWEP.HoldType = "ar2"
|
||||
SWEP.HoldTypeSprint = "passive"
|
||||
SWEP.HoldTypeBlindFire = false
|
||||
SWEP.HoldTypeCustomize = "slam"
|
||||
SWEP.HoldTypeNPC = nil
|
||||
SWEP.HoldTypeSuicide = "pistol"
|
||||
|
||||
SWEP.GestureShoot = ACT_HL2MP_GESTURE_RANGE_ATTACK_AR2
|
||||
SWEP.GestureReload = ACT_HL2MP_GESTURE_RELOAD_AR2
|
||||
SWEP.GestureBash = ACT_GMOD_GESTURE_MELEE_SHOVE_1HAND
|
||||
|
||||
SWEP.PassiveAng = Angle(0, 0, 0)
|
||||
SWEP.PassivePos = Vector(0, 0, 0)
|
||||
|
||||
SWEP.SprintAng = Angle(0, 45, 0)
|
||||
SWEP.SprintPos = Vector(0, 0, 0)
|
||||
|
||||
SWEP.BlindFireAng = Angle(0, 15, 0)
|
||||
SWEP.BlindFirePos = Vector(0, 0, -6)
|
||||
|
||||
SWEP.BlindFireLeftAng = Angle(75, 0, 0)
|
||||
SWEP.BlindFireLeftPos = Vector(8, 14, -12)
|
||||
|
||||
SWEP.BlindFireRightAng = Angle(-75, 0, 0)
|
||||
SWEP.BlindFireRightPos = Vector(-8, 14, -12)
|
||||
|
||||
SWEP.BlindFireSuicideAng = Angle(0, 145, 130)
|
||||
SWEP.BlindFireSuicidePos = Vector(-6, 32, -26)
|
||||
|
||||
SWEP.CustomizeAng = Angle(30, 15, 0)
|
||||
SWEP.CustomizePos = Vector(6, 0, -7)
|
||||
|
||||
SWEP.SightAng = Angle(0, 0, 0)
|
||||
SWEP.SightPos = Vector(0, 1, 1)
|
||||
|
||||
SWEP.PeekAng = Angle(0, 0, -7)
|
||||
SWEP.PeekPos = Vector(3, 2, -1.5)
|
||||
|
||||
SWEP.HolsterVisible = false
|
||||
SWEP.HolsterSlot = TacRP.HOLSTER_SLOT_BACK
|
||||
SWEP.HolsterPos = Vector(0, 0, 0)
|
||||
SWEP.HolsterAng = Angle(0, 0, 0)
|
||||
|
||||
SWEP.HolsterModel = nil // string, model.
|
||||
|
||||
SWEP.SightMidPoint = {
|
||||
Pos = Vector(-1, 15, -6),
|
||||
Ang = Angle(0, 0, -45)
|
||||
}
|
||||
|
||||
SWEP.SprintMidPoint = {
|
||||
Pos = Vector(4, 10, 2),
|
||||
Ang = Angle(0, -10, -45)
|
||||
}
|
||||
|
||||
SWEP.NearWallPos = Vector(0, 6, 0)
|
||||
SWEP.NearWallAng = Angle(-3, 5, -5)
|
||||
|
||||
// scope
|
||||
|
||||
SWEP.Scope = true
|
||||
SWEP.ScopeOverlay = nil // Material("path/to/overlay")
|
||||
SWEP.ScopeFOV = 90 / 1.1
|
||||
SWEP.ScopeLevels = 1 // 2 = like CS:S
|
||||
SWEP.ScopeHideWeapon = false
|
||||
|
||||
SWEP.QuickScopeSpreadPenalty = 0.05
|
||||
SWEP.QuickScopeTime = 0.2 // amount of time over which to fade out the quickscope spread penalty
|
||||
|
||||
// sway
|
||||
|
||||
SWEP.Sway = 1
|
||||
SWEP.ScopedSway = 0.1
|
||||
SWEP.BlindFireSway = 2
|
||||
SWEP.SwayCrouchMult = 0.75
|
||||
|
||||
SWEP.BreathRecovery = 1
|
||||
SWEP.BreathDrain = 1
|
||||
|
||||
SWEP.FreeAim = true
|
||||
SWEP.FreeAimMaxAngle = 3.5
|
||||
|
||||
SWEP.ShootOffset = Vector(0, 0, 0)
|
||||
SWEP.ShootOffsetAngle = Angle(0, 0, 0)
|
||||
|
||||
// quicknade
|
||||
|
||||
SWEP.CanQuickNade = true
|
||||
SWEP.QuickNadeTimeMult = 1
|
||||
|
||||
// melee
|
||||
|
||||
SWEP.CanMeleeAttack = true
|
||||
|
||||
SWEP.MeleeDamage = 25
|
||||
SWEP.MeleeAttackTime = 0.8 // time between swings
|
||||
SWEP.MeleeRange = 96
|
||||
SWEP.MeleeDamageType = DMG_GENERIC
|
||||
SWEP.MeleeDelay = 0.25 // delay between swing start and trace
|
||||
|
||||
// secondary attack, used on knives
|
||||
SWEP.Melee2Damage = nil
|
||||
SWEP.Melee2AttackTime = nil
|
||||
SWEP.Melee2Range = nil
|
||||
SWEP.Melee2AttackMissTime = nil
|
||||
SWEP.Melee2Delay = nil
|
||||
|
||||
SWEP.MeleeThrowForce = 3000
|
||||
|
||||
// used on knife perks
|
||||
SWEP.MeleePerkStr = 0.5
|
||||
SWEP.MeleePerkAgi = 0.5
|
||||
SWEP.MeleePerkInt = 0.5
|
||||
|
||||
// reload
|
||||
|
||||
SWEP.AmmoPerShot = 1
|
||||
SWEP.ClipSize = 30
|
||||
SWEP.Ammo = "pistol"
|
||||
|
||||
SWEP.InfiniteAmmo = false // do not consume reserve ammo
|
||||
|
||||
SWEP.SupplyAmmoAmount = false // overrides clipsize/ammo for ammo pickups
|
||||
SWEP.SupplyLimit = 1 // Multiplier for supply ammo
|
||||
|
||||
SWEP.TryUnholster = false // if we have an "unholster" animation use it instead of "deploy"
|
||||
|
||||
SWEP.ShotgunReload = false
|
||||
SWEP.ShotgunThreeload = true // use those stupid 3 shot reload animations
|
||||
SWEP.ShotgunReloadCompleteStart = false // do not interrupt reload_start and instead wait for it to finish first. used on FP6 animations
|
||||
SWEP.ShotgunFullCancel = false // Ignore tacrp_reload_sg_cancel and force cancel animation
|
||||
SWEP.ShotgunNoReverseStart = true // don't reverse starting animation on a non-empty reload
|
||||
SWEP.ShotgunUpInTime = 0.9 // time after which one round is finished loading
|
||||
|
||||
SWEP.ReloadUpInTime = nil // time to restore ammo, if unset restores at end of animation
|
||||
SWEP.ReloadTimeMult = 1
|
||||
SWEP.DeployTimeMult = 1
|
||||
SWEP.HolsterTimeMult = 1
|
||||
SWEP.UnholsterTimeMult = 1
|
||||
SWEP.DropMagazineModel = false
|
||||
SWEP.DropMagazineImpact = "pistol" // available: "pistol", "plastic", "metal", "bullet", "shotgun"
|
||||
SWEP.DropMagazineAmount = 1
|
||||
SWEP.DropMagazineTime = 0
|
||||
SWEP.MidReload = false // allow guns with "midreload" animation to continue reload after holster
|
||||
|
||||
SWEP.DefaultBodygroups = "0000000"
|
||||
SWEP.DefaultWMBodygroups = "0000000"
|
||||
SWEP.DefaultSkin = 0
|
||||
SWEP.BulletBodygroups = nil
|
||||
SWEP.BulletBodygroupsSetAll = false // Set all applicable bullet groups, rather than just the last
|
||||
|
||||
/*
|
||||
{
|
||||
[1] = {5, 1}
|
||||
}
|
||||
*/
|
||||
SWEP.LoadInTime = 0.25 // how long to replenish the visible "belt" of ammo
|
||||
SWEP.ShotgunLoadInTime = 0.9 // visual update delay for shotguns
|
||||
|
||||
// sounds
|
||||
|
||||
SWEP.Sound_Shoot = "^"
|
||||
SWEP.Sound_Shoot_Silenced = ""
|
||||
|
||||
SWEP.Vol_Shoot = 130
|
||||
SWEP.Pitch_Shoot = 100
|
||||
SWEP.Loudness_Shoot = 1
|
||||
SWEP.ShootPitchVariance = 2.5 // amount to vary pitch by each shot
|
||||
|
||||
SWEP.Sound_ScopeIn = ""
|
||||
SWEP.Sound_MeleeAttack = ""
|
||||
SWEP.Sound_DryFire = "TacRP/weapons/dryfire_pistol-1.wav"
|
||||
SWEP.Sound_Jam = "TacRP/malfunction.wav"
|
||||
|
||||
SWEP.Sound_BipodDown = "tacrp/bipod_down.wav"
|
||||
SWEP.Sound_BipodUp = "tacrp/bipod_up.wav"
|
||||
|
||||
SWEP.Sound_MeleeSwing = ""
|
||||
|
||||
SWEP.Sound_ToggleTactical = "tacrp/firemode.wav"
|
||||
|
||||
SWEP.Sound_StartLockOn = "tacrp/check1.wav"
|
||||
SWEP.Sound_FinishLockOn = "tacrp/locked1.wav"
|
||||
|
||||
// effects
|
||||
|
||||
SWEP.EffectsAlternate = false // Effects will alternate using L and R attachments.
|
||||
SWEP.EffectsDoubled = false // Per shot, play effects a second time on the other attachment.
|
||||
|
||||
// .qc attachment for muzzle flash and eject when EffectsAlternate is NOT true.
|
||||
SWEP.QCA_Muzzle = 1
|
||||
SWEP.QCA_Eject = 2
|
||||
|
||||
// .qc attachments when EffectsAlternate is set to true.
|
||||
SWEP.QCA_MuzzleL = 3
|
||||
SWEP.QCA_MuzzleR = 4
|
||||
SWEP.QCA_EjectL = 6
|
||||
SWEP.QCA_EjectR = 7
|
||||
|
||||
// ditto but for worldmodel
|
||||
SWEP.WM_QCA_Muzzle = 1
|
||||
SWEP.WM_QCA_Eject = 2
|
||||
|
||||
SWEP.WM_QCA_MuzzleL = 1
|
||||
SWEP.WM_QCA_MuzzleR = 2
|
||||
SWEP.WM_QCA_EjectL = 3
|
||||
SWEP.WM_QCA_EjectR = 4
|
||||
|
||||
SWEP.MuzzleEffect = "muzzleflash_pistol"
|
||||
|
||||
SWEP.EjectEffect = 0 // 1 = pistol, 2 = rifle, 3 = shotgun
|
||||
SWEP.EjectDelay = 0
|
||||
SWEP.EjectScale = 1
|
||||
|
||||
// anims
|
||||
// VM:
|
||||
// idle
|
||||
// fire
|
||||
// fire1, fire2...
|
||||
// dryfire
|
||||
// melee
|
||||
// reload
|
||||
// midreload
|
||||
// prime_grenade
|
||||
// throw_grenade
|
||||
// throw_grenade_underhand
|
||||
// deploy
|
||||
// blind_idle
|
||||
// blind_fire
|
||||
// blind_fire1, blind_fire2...
|
||||
// blind_dryfire
|
||||
|
||||
// WM:
|
||||
// attack1
|
||||
SWEP.AnimationTranslationTable = {
|
||||
["melee"] = {"melee1", "melee2"},
|
||||
["jam"] = "midreload"
|
||||
} // translates ["fire"] = "shoot"; key = translates from, value = translates to
|
||||
// e.g. you have a "shoot1" sequence and need "fire"
|
||||
// so ["fire"] = "shoot1"
|
||||
// can be ["fire"] = {"list", "of", "values"}
|
||||
|
||||
SWEP.ProceduralIronFire = nil // procedurally animate the viewmodel and bones when using ironsights
|
||||
// {
|
||||
// vm_pos = Vector(0, -0.5, -0.6),
|
||||
// vm_ang = Angle(0, 2, 0),
|
||||
// t = 0.2, // duration of vm pos/ang
|
||||
// tmax = 0.2, // clean up after this time has passed
|
||||
// bones = {
|
||||
// {
|
||||
// bone = "ValveBiped.slide",
|
||||
// pos = Vector(0, 0, -3), // optional
|
||||
// ang = Angle(0, 0, 0), // optional
|
||||
// t0 = 0.05, // duration to reach full movement
|
||||
// t1 = 0.2, // duration to reset
|
||||
// },
|
||||
// },
|
||||
// }
|
||||
|
||||
SWEP.NoHolsterAnimation = false // Will play draw reversed instead
|
||||
SWEP.LastShot = false
|
||||
|
||||
// Use special animation setup for akimbo pistols.
|
||||
// Does not adjust effects - set AlternatingEffects separately.
|
||||
SWEP.Akimbo = false
|
||||
|
||||
// attachments
|
||||
|
||||
SWEP.AttachmentElements = nil
|
||||
/*
|
||||
{
|
||||
["bg_name"] = {
|
||||
BGs_VM = {
|
||||
{1, 1}
|
||||
},
|
||||
BGs_WM = {
|
||||
{1, 1}
|
||||
},
|
||||
AttPosMods = {
|
||||
[1] = {
|
||||
Pos_VM = Vector(),
|
||||
Pos_WM = Vector(),
|
||||
Ang_VM = Angle(),
|
||||
Ang_WM = Angle(),
|
||||
},
|
||||
},
|
||||
SortOrder = 1, // defaults to 1, higher value means process later
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
SWEP.Attachments = nil
|
||||
// {
|
||||
// [1] = {
|
||||
// Installed = nil,
|
||||
// Default = nil, // install this attachment by default
|
||||
// InstalledElements = "", // single or list of elements to activate when something is installed here
|
||||
// UnInstalledElements = "",
|
||||
// Integral = false, // cannot be removed
|
||||
// Category = "", // single or {"list", "of", "values"}
|
||||
// Bone = "",
|
||||
// WMBone = "",
|
||||
// Pos_VM = Vector(0, 0, 0),
|
||||
// Pos_WM = Vector(0, 0, 0),
|
||||
// Ang_VM = Angle(0, 0, 0),
|
||||
// Ang_WM = Angle(0, 0, 0),
|
||||
// CapacityMult = 1, // multiply the amount of Capacity this attachment occupies
|
||||
// }
|
||||
// }
|
||||
|
||||
// boilerplate
|
||||
|
||||
SWEP.FreeAim = true
|
||||
|
||||
SWEP.ArcticTacRP = true
|
||||
SWEP.DrawCrosshair = true
|
||||
SWEP.AccurateCrosshair = true
|
||||
SWEP.DrawWeaponInfoBox = false
|
||||
SWEP.UseHands = true
|
||||
|
||||
SWEP.Shields = {}
|
||||
|
||||
SWEP.CurrentAnimation = ""
|
||||
|
||||
SWEP.Primary.Automatic = false
|
||||
SWEP.Primary.ClipSize = 0
|
||||
SWEP.Primary.Ammo = ""
|
||||
SWEP.Primary.DefaultClip = 0
|
||||
|
||||
SWEP.Secondary.Automatic = true
|
||||
SWEP.Secondary.ClipSize = -1
|
||||
SWEP.Secondary.Ammo = ""
|
||||
SWEP.Secondary.DefaultClip = 0
|
||||
|
||||
SWEP.GaveDefaultAmmo = false
|
||||
|
||||
SWEP.BounceWeaponIcon = false
|
||||
|
||||
SWEP.SwayScale = 1
|
||||
SWEP.BobScale = 1
|
||||
|
||||
SWEP.LaserColor = Color(255, 0, 0)
|
||||
|
||||
SWEP.ActiveEffects = {}
|
||||
|
||||
SWEP.PCFs = {}
|
||||
SWEP.MuzzPCFs = {}
|
||||
|
||||
AddCSLuaFile()
|
||||
|
||||
local searchdir = "weapons/tacrp_base"
|
||||
|
||||
local function autoinclude(dir)
|
||||
local files, dirs = file.Find(searchdir .. "/*.lua", "LUA")
|
||||
|
||||
for _, filename in pairs(files) do
|
||||
if filename == "shared.lua" then continue end
|
||||
local luatype = string.sub(filename, 1, 2)
|
||||
|
||||
if luatype == "sv" then
|
||||
if SERVER then
|
||||
include(dir .. "/" .. filename)
|
||||
end
|
||||
elseif luatype == "cl" then
|
||||
AddCSLuaFile(dir .. "/" .. filename)
|
||||
if CLIENT then
|
||||
include(dir .. "/" .. filename)
|
||||
end
|
||||
else
|
||||
AddCSLuaFile(dir .. "/" .. filename)
|
||||
include(dir .. "/" .. filename)
|
||||
end
|
||||
end
|
||||
|
||||
for _, path in pairs(dirs) do
|
||||
autoinclude(dir .. "/" .. path)
|
||||
end
|
||||
end
|
||||
|
||||
autoinclude(searchdir)
|
||||
|
||||
function SWEP:SetupDataTables()
|
||||
self:NetworkVar("Float", 0, "RecoilAmount")
|
||||
self:NetworkVar("Float", 1, "AnimLockTime")
|
||||
self:NetworkVar("Float", 2, "NextIdle")
|
||||
self:NetworkVar("Float", 3, "LastRecoilTime")
|
||||
self:NetworkVar("Float", 4, "RecoilDirection")
|
||||
self:NetworkVar("Float", 5, "SprintAmount")
|
||||
self:NetworkVar("Float", 6, "SprintLockTime")
|
||||
self:NetworkVar("Float", 7, "LastScopeTime")
|
||||
self:NetworkVar("Float", 8, "LastMeleeTime")
|
||||
self:NetworkVar("Float", 9, "PrimedGrenadeTime")
|
||||
self:NetworkVar("Float", 10, "StartPrimedGrenadeTime")
|
||||
self:NetworkVar("Float", 11, "ReloadFinishTime")
|
||||
self:NetworkVar("Float", 12, "NWSightAmount")
|
||||
self:NetworkVar("Float", 13, "BlindFireFinishTime")
|
||||
self:NetworkVar("Float", 14, "HolsterTime")
|
||||
self:NetworkVar("Float", 15, "NWLastProceduralFireTime")
|
||||
self:NetworkVar("Float", 16, "NWHoldBreathAmount")
|
||||
self:NetworkVar("Float", 17, "Breath")
|
||||
self:NetworkVar("Float", 18, "LockOnStartTime")
|
||||
|
||||
self:NetworkVar("Int", 0, "BurstCount")
|
||||
self:NetworkVar("Int", 1, "ScopeLevel")
|
||||
self:NetworkVar("Int", 2, "NthShot")
|
||||
self:NetworkVar("Int", 3, "LoadedRounds")
|
||||
self:NetworkVar("Int", 4, "Firemode")
|
||||
self:NetworkVar("Int", 5, "PatternCount")
|
||||
|
||||
self:NetworkVar("Bool", 0, "Customize")
|
||||
self:NetworkVar("Bool", 1, "Reloading")
|
||||
self:NetworkVar("Bool", 2, "BlindFire")
|
||||
self:NetworkVar("Bool", 3, "EndReload")
|
||||
self:NetworkVar("Bool", 4, "PrimedGrenade")
|
||||
self:NetworkVar("Bool", 5, "Safe")
|
||||
self:NetworkVar("Bool", 6, "BlindFireLeft")
|
||||
self:NetworkVar("Bool", 7, "Tactical")
|
||||
self:NetworkVar("Bool", 8, "Charge")
|
||||
self:NetworkVar("Bool", 9, "NWPeeking")
|
||||
self:NetworkVar("Bool", 10, "BlindFireRight") // bleh, but actually less networking load than using an integer (32 bit)
|
||||
self:NetworkVar("Bool", 11, "Jammed")
|
||||
self:NetworkVar("Bool", 12, "Ready")
|
||||
self:NetworkVar("Bool", 13, "InBipod")
|
||||
self:NetworkVar("Bool", 14, "OutOfBreath")
|
||||
self:NetworkVar("Bool", 15, "HoldingBreath")
|
||||
self:NetworkVar("Bool", 16, "LastWasSprinting")
|
||||
self:NetworkVar("Bool", 17, "EmptyReload")
|
||||
|
||||
self:NetworkVar("Angle", 0, "FreeAimAngle")
|
||||
self:NetworkVar("Angle", 1, "LastAimAngle")
|
||||
self:NetworkVar("Angle", 2, "BipodAngle")
|
||||
|
||||
self:NetworkVar("Vector", 0, "BipodPos")
|
||||
|
||||
self:NetworkVar("Entity", 0, "HolsterEntity")
|
||||
self:NetworkVar("Entity", 1, "CornershotEntity")
|
||||
self:NetworkVar("Entity", 2, "LockOnEntity")
|
||||
|
||||
self:SetFreeAimAngle(Angle())
|
||||
self:SetLastAimAngle(Angle())
|
||||
self:SetFiremode(1)
|
||||
self:SetTactical(true)
|
||||
self:SetReady(false)
|
||||
self:SetBreath(1)
|
||||
self:SetHoldBreathAmount(0)
|
||||
self:SetLastWasSprinting(false)
|
||||
self:SetHoldingBreath(false)
|
||||
self:SetLockOnEntity(NULL)
|
||||
self:SetLockOnStartTime(0)
|
||||
end
|
||||
|
||||
function SWEP:OnDrop(owner)
|
||||
if self.PreviousZoom and IsValid(owner) then
|
||||
owner:SetCanZoom(true)
|
||||
end
|
||||
self:SetReady(false)
|
||||
end
|
||||
|
||||
function SWEP:SecondaryAttack()
|
||||
self:RunHook("Hook_SecondaryAttack")
|
||||
return
|
||||
end
|
||||
|
||||
local function clunpredictvar(tbl, name, varname, default)
|
||||
local clvar = "CL_" .. name
|
||||
|
||||
tbl[clvar] = default
|
||||
|
||||
tbl["Set" .. name] = function(self, v)
|
||||
if (!game.SinglePlayer() and CLIENT and self:GetOwner() == LocalPlayer()) then self[clvar] = v end
|
||||
self["Set" .. varname](self, v)
|
||||
end
|
||||
|
||||
tbl["Get" .. name] = function(self)
|
||||
if (!game.SinglePlayer() and CLIENT and self:GetOwner() == LocalPlayer()) then return self[clvar] end
|
||||
return self["Get" .. varname](self)
|
||||
end
|
||||
end
|
||||
|
||||
clunpredictvar(SWEP, "SightAmount", "NWSightAmount", 0)
|
||||
-- clunpredictvar(SWEP, "SprintAmount", "NWSprintAmount", 0)
|
||||
clunpredictvar(SWEP, "LastProceduralFireTime", "NWLastProceduralFireTime", 0)
|
||||
clunpredictvar(SWEP, "HoldBreathAmount", "NWHoldBreathAmount", 0)
|
||||
62
garrysmod/addons/tacrp/lua/weapons/tacrp_base/sv_net.lua
Normal file
62
garrysmod/addons/tacrp/lua/weapons/tacrp_base/sv_net.lua
Normal file
@@ -0,0 +1,62 @@
|
||||
function SWEP:NetworkWeapon(sendto)
|
||||
net.Start("TacRP_networkweapon")
|
||||
net.WriteUInt(self:EntIndex(), 13)
|
||||
|
||||
for slot, slottbl in pairs(self.Attachments) do
|
||||
if !slottbl.Installed then net.WriteUInt(0, TacRP.Attachments_Bits) continue end
|
||||
|
||||
local atttbl = TacRP.GetAttTable(slottbl.Installed)
|
||||
|
||||
net.WriteUInt(atttbl.ID, TacRP.Attachments_Bits)
|
||||
end
|
||||
|
||||
if sendto then
|
||||
net.Send(sendto)
|
||||
else
|
||||
net.SendPVS(self:GetPos())
|
||||
end
|
||||
|
||||
self:InvalidateCache()
|
||||
|
||||
self:DoBodygroups(true)
|
||||
self:DoBodygroups(false)
|
||||
end
|
||||
|
||||
function SWEP:ReceivePreset()
|
||||
for slot, slottbl in pairs(self.Attachments) do
|
||||
local attid = net.ReadUInt(TacRP.Attachments_Bits)
|
||||
|
||||
if attid == 0 then
|
||||
if slottbl.Installed then
|
||||
self:Detach(slot, true)
|
||||
end
|
||||
else
|
||||
local att = TacRP.Attachments_Index[attid]
|
||||
local atttbl = TacRP.GetAttTable(att)
|
||||
|
||||
if !atttbl then continue end
|
||||
|
||||
if slottbl.Installed then
|
||||
self:Detach(slot, true)
|
||||
end
|
||||
|
||||
self:Attach(slot, att, true, true)
|
||||
|
||||
if atttbl.OnPresetLoad then
|
||||
atttbl.OnPresetLoad(self)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
self:NetworkWeapon()
|
||||
TacRP:PlayerSendAttInv(self:GetOwner())
|
||||
|
||||
self:InvalidateCache()
|
||||
self:SetBaseSettings()
|
||||
|
||||
if self:GetValue("TryUnholster") then
|
||||
self:DoDeployAnimation()
|
||||
end
|
||||
|
||||
self:RestoreClip(self.Primary.ClipSize)
|
||||
end
|
||||
Reference in New Issue
Block a user