add sborka
This commit is contained in:
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
|
||||
Reference in New Issue
Block a user