517 lines
16 KiB
Lua
517 lines
16 KiB
Lua
AddCSLuaFile()
|
|
|
|
if SERVER then
|
|
include("sv_commands.lua")
|
|
include("sh_commands.lua")
|
|
SWEP.Weight = 5
|
|
SWEP.AutoSwitchTo = false
|
|
SWEP.AutoSwitchFrom = false
|
|
end
|
|
|
|
if CLIENT then
|
|
SWEP.DrawAmmo = true
|
|
SWEP.DrawCrosshair = false
|
|
SWEP.ViewModelFOV = 82
|
|
SWEP.ViewModelFlip = false
|
|
SWEP.CSMuzzleFlashes = true
|
|
|
|
-- This is the font that's used to draw the death icons
|
|
surface.CreateFont("CSKillIcons", {
|
|
size = ScreenScale(30),
|
|
weight = 500,
|
|
antialias = true,
|
|
shadow = true,
|
|
font = "csd"
|
|
})
|
|
surface.CreateFont("CSSelectIcons", {
|
|
size = ScreenScale(60),
|
|
weight = 500,
|
|
antialias = true,
|
|
shadow = true,
|
|
font = "csd"
|
|
})
|
|
end
|
|
|
|
SWEP.Base = "weapon_base"
|
|
|
|
SWEP.Author = "DarkRP Developers"
|
|
SWEP.Contact = ""
|
|
SWEP.Purpose = ""
|
|
SWEP.Instructions = ""
|
|
|
|
SWEP.Spawnable = false
|
|
SWEP.AdminOnly = false
|
|
SWEP.UseHands = true
|
|
|
|
SWEP.HoldType = "normal"
|
|
SWEP.LoweredHoldType = "normal"
|
|
|
|
SWEP.Primary.Sound = Sound("Weapon_AK47.Single")
|
|
SWEP.Primary.Recoil = 1.5
|
|
SWEP.Primary.Damage = 40
|
|
SWEP.Primary.NumShots = 1
|
|
SWEP.Primary.Cone = 0.02
|
|
SWEP.Primary.Delay = 0.15
|
|
|
|
SWEP.Primary.ClipSize = -1
|
|
SWEP.Primary.DefaultClip = -1
|
|
SWEP.Primary.Automatic = false
|
|
SWEP.Primary.Ammo = "none"
|
|
|
|
SWEP.Secondary.ClipSize = -1
|
|
SWEP.Secondary.DefaultClip = -1
|
|
SWEP.Secondary.Automatic = false
|
|
SWEP.Secondary.Ammo = "none"
|
|
|
|
SWEP.MultiMode = false
|
|
|
|
SWEP.DarkRPBased = true
|
|
|
|
function SWEP:SetIronsights(b)
|
|
if (b ~= self:GetIronsights()) then
|
|
self:SetIronsightsPredicted(b)
|
|
self:SetIronsightsTime(CurTime())
|
|
if GAMEMODE.Config.ironshoot then
|
|
self:SetHoldType(b and self.HoldType or self.LoweredHoldType)
|
|
end
|
|
if CLIENT then
|
|
self:CalcViewModel()
|
|
end
|
|
end
|
|
end
|
|
|
|
function SWEP:GetIronsights()
|
|
return self:GetIronsightsPredicted()
|
|
end
|
|
|
|
--- Dummy functions that will be replaced when SetupDataTables runs. These are
|
|
--- here for when that does not happen (due to e.g. stacking base classes)
|
|
function SWEP:GetIronsightsTime() return -1 end
|
|
function SWEP:SetIronsightsTime() end
|
|
function SWEP:GetIronsightsPredicted() return false end
|
|
function SWEP:SetIronsightsPredicted() end
|
|
|
|
function SWEP:SetupDataTables()
|
|
self:NetworkVar("Bool", 0, "IronsightsPredicted")
|
|
self:NetworkVar("Float", 0, "IronsightsTime")
|
|
self:NetworkVar("Bool", 1, "Reloading")
|
|
self:NetworkVar("Float", 1, "LastPrimaryAttack")
|
|
self:NetworkVar("Float", 2, "ReloadEndTime")
|
|
self:NetworkVar("Float", 3, "BurstTime")
|
|
self:NetworkVar("Int", 0, "BurstBulletNum")
|
|
self:NetworkVar("Int", 1, "TotalUsedMagCount")
|
|
self:NetworkVar("String", 0, "FireMode")
|
|
self:NetworkVar("Entity", 0, "LastOwner")
|
|
end
|
|
|
|
function SWEP:Initialize()
|
|
if CLIENT and IsValid(self:GetOwner()) then
|
|
local vm = self:GetOwner():GetViewModel()
|
|
self:ResetDarkRPBones(vm)
|
|
end
|
|
|
|
self:SetHoldType(GAMEMODE.Config.ironshoot and self.LoweredHoldType or self.HoldType)
|
|
if SERVER then
|
|
self:SetNPCMinBurst(30)
|
|
self:SetNPCMaxBurst(30)
|
|
self:SetNPCFireRate(0.01)
|
|
end
|
|
|
|
self:SetFireMode(self.Primary.Automatic and "auto" or "semi")
|
|
end
|
|
|
|
function SWEP:Deploy()
|
|
self:SetHoldType(GAMEMODE.Config.ironshoot and self.LoweredHoldType or self.HoldType)
|
|
self:SetIronsights(false)
|
|
self:SetReloading(false)
|
|
self:SetReloadEndTime(0)
|
|
self:SetBurstTime(0)
|
|
self:SetBurstBulletNum(0)
|
|
|
|
return true
|
|
end
|
|
|
|
function SWEP:Holster()
|
|
self:SetIronsights(false)
|
|
self:SetReloading(false)
|
|
self:SetReloadEndTime(0)
|
|
self:SetBurstTime(0)
|
|
self:SetBurstBulletNum(0)
|
|
|
|
if CLIENT then self.hasShot = false end
|
|
|
|
if not IsValid(self:GetOwner()) then return true end
|
|
if CLIENT then
|
|
local vm = self:GetOwner():GetViewModel()
|
|
self:ResetDarkRPBones(vm)
|
|
end
|
|
|
|
return true
|
|
end
|
|
|
|
function SWEP:OnRemove()
|
|
if CLIENT and IsValid(self:GetOwner()) then
|
|
local vm = self:GetOwner():GetViewModel()
|
|
self:ResetDarkRPBones(vm)
|
|
end
|
|
end
|
|
|
|
function SWEP:OwnerChanged()
|
|
if IsValid(self:GetOwner()) then self:SetLastOwner(self:GetOwner()) end
|
|
end
|
|
|
|
function SWEP:PrimaryAttack()
|
|
self.Primary.Automatic = self:GetFireMode() == "auto"
|
|
|
|
if self:GetBurstBulletNum() > 0 and CurTime() < self:GetBurstTime() then return end
|
|
|
|
local Owner = self:GetOwner()
|
|
|
|
if not IsValid(Owner) then return end
|
|
|
|
if self.MultiMode and Owner:KeyDown(IN_USE) then
|
|
if self:GetFireMode() == "semi" then
|
|
self:SetFireMode("burst")
|
|
self.Primary.Automatic = false
|
|
Owner:PrintMessage(HUD_PRINTCENTER, DarkRP.getPhrase("switched_burst"))
|
|
elseif self:GetFireMode() == "burst" then
|
|
self:SetFireMode("auto")
|
|
self.Primary.Automatic = true
|
|
Owner:PrintMessage(HUD_PRINTCENTER, DarkRP.getPhrase("switched_fully_auto"))
|
|
elseif self:GetFireMode() == "auto" then
|
|
self:SetFireMode("semi")
|
|
self.Primary.Automatic = false
|
|
Owner:PrintMessage(HUD_PRINTCENTER, DarkRP.getPhrase("switched_semi_auto"))
|
|
end
|
|
self:SetNextPrimaryFire(CurTime() + 0.5)
|
|
self:SetNextSecondaryFire(CurTime() + 0.5)
|
|
return
|
|
end
|
|
|
|
if self:GetFireMode() ~= "burst" then
|
|
self:SetNextPrimaryFire(CurTime() + self.Primary.Delay)
|
|
end
|
|
|
|
self:SetNextSecondaryFire(CurTime() + self.Primary.Delay)
|
|
|
|
if self:Clip1() <= 0 then
|
|
self:EmitSound("weapons/clipempty_rifle.wav")
|
|
self:SetNextPrimaryFire(CurTime() + 2)
|
|
return
|
|
end
|
|
|
|
if not self:CanPrimaryAttack() then self:SetIronsights(false) return end
|
|
if not self:GetIronsights() and GAMEMODE.Config.ironshoot then return end
|
|
-- Play shoot sound
|
|
self:EmitSound(self.Primary.Sound)
|
|
|
|
-- Shoot the bullet
|
|
self:CSShootBullet(self.Primary.Damage, self.Primary.Recoil + 3, self.Primary.NumShots, self.Primary.Cone + .05)
|
|
|
|
if self:GetFireMode() == "burst" then
|
|
self:SetBurstBulletNum(self:GetBurstBulletNum() + 1)
|
|
if self:GetBurstBulletNum() == 3 then
|
|
self:SetBurstTime(0)
|
|
self:SetBurstBulletNum(0)
|
|
else
|
|
self:SetBurstTime(CurTime() + 0.1)
|
|
end
|
|
end
|
|
|
|
-- Remove 1 bullet from our clip
|
|
self:TakePrimaryAmmo(1)
|
|
|
|
self:SetLastPrimaryAttack(CurTime())
|
|
|
|
if Owner:IsNPC() then return end
|
|
|
|
-- Punch the player's view
|
|
Owner:ViewPunch(Angle(util.SharedRandom("DarkRP_CSBase" .. self:EntIndex() .. "Mag" .. self:GetTotalUsedMagCount() .. "p" .. self:Clip1(), -1.2, -1.1) * self.Primary.Recoil, util.SharedRandom("DarkRP_CSBase" .. self:EntIndex() .. "Mag" .. self:GetTotalUsedMagCount() .. "y" .. self:Clip1(), -1.1, 1.1) * self.Primary.Recoil, 0))
|
|
end
|
|
|
|
function SWEP:CSShootBullet(dmg, recoil, numbul, cone)
|
|
local Owner = self:GetOwner()
|
|
|
|
if not IsValid(Owner) then return end
|
|
|
|
numbul = numbul or 1
|
|
cone = cone or 0.01
|
|
|
|
local bullet = {}
|
|
bullet.Num = numbul or 1
|
|
bullet.Src = Owner:GetShootPos() -- Source
|
|
bullet.Dir = (Owner:GetAimVector():Angle() + Owner:GetViewPunchAngles()):Forward() -- Dir of bullet
|
|
bullet.Spread = Vector(cone, cone, 0) -- Aim Cone
|
|
bullet.Tracer = 4 -- Show a tracer on every x bullets
|
|
bullet.Force = 5 -- Amount of force to give to phys objects
|
|
bullet.Damage = dmg
|
|
|
|
Owner:FireBullets(bullet)
|
|
self:SendWeaponAnim(ACT_VM_PRIMARYATTACK) -- View model animation
|
|
Owner:MuzzleFlash() -- Crappy muzzle light
|
|
Owner:SetAnimation(PLAYER_ATTACK1) -- 3rd Person Animation
|
|
|
|
if Owner:IsNPC() then return end
|
|
|
|
-- Part of workaround, different viewmodel position if shots have been fired
|
|
if CLIENT then self.hasShot = true end
|
|
end
|
|
|
|
local host_timescale = GetConVar("host_timescale")
|
|
local IRONSIGHT_TIME = 0.25
|
|
function SWEP:GetViewModelPosition(pos, ang)
|
|
if (not self.IronSightsPos) then return pos, ang end
|
|
|
|
pos = pos + ang:Forward() * -5
|
|
|
|
if (self.bIron == nil) then return pos, ang end
|
|
|
|
local bIron = self.bIron
|
|
local time = self.fCurrentTime + (SysTime() - self.fCurrentSysTime) * game.GetTimeScale() * host_timescale:GetFloat()
|
|
|
|
if bIron then
|
|
self.SwayScale = 0.3
|
|
self.BobScale = 0.1
|
|
else
|
|
self.SwayScale = 1.0
|
|
self.BobScale = 1.0
|
|
end
|
|
|
|
if GAMEMODE.Config.ironshoot then
|
|
ang:RotateAroundAxis(ang:Right(), -15)
|
|
end
|
|
|
|
local fIronTime = self.fIronTime
|
|
if (not bIron) and fIronTime < time - IRONSIGHT_TIME then
|
|
return pos, ang
|
|
end
|
|
|
|
local mul = 1.0
|
|
|
|
if fIronTime > time - IRONSIGHT_TIME then
|
|
mul = math.Clamp((time - fIronTime) / IRONSIGHT_TIME, 0, 1)
|
|
|
|
if not bIron then mul = 1 - mul end
|
|
end
|
|
|
|
local offset = self.IronSightsPos
|
|
|
|
if self.IronSightsAng then
|
|
ang = ang * 1
|
|
ang:RotateAroundAxis(ang:Right(), self.IronSightsAng.x * mul)
|
|
ang:RotateAroundAxis(ang:Up(), self.IronSightsAng.y * mul)
|
|
ang:RotateAroundAxis(ang:Forward(), self.IronSightsAng.z * mul)
|
|
end
|
|
|
|
if GAMEMODE.Config.ironshoot then
|
|
ang:RotateAroundAxis(ang:Right(), mul * 15)
|
|
else
|
|
ang:RotateAroundAxis(ang:Right(), mul)
|
|
end
|
|
|
|
pos = pos + offset.x * ang:Right() * mul
|
|
pos = pos + offset.y * ang:Forward() * mul
|
|
pos = pos + offset.z * ang:Up() * mul
|
|
|
|
if not self.hasShot then
|
|
if self.IronSightsAngAfterShootingAdjustment then
|
|
ang:RotateAroundAxis(ang:Right(), self.IronSightsAngAfterShootingAdjustment.x * mul)
|
|
ang:RotateAroundAxis(ang:Up(), self.IronSightsAngAfterShootingAdjustment.y * mul)
|
|
ang:RotateAroundAxis(ang:Forward(), self.IronSightsAngAfterShootingAdjustment.z * mul)
|
|
end
|
|
|
|
if self.IronSightsPosAfterShootingAdjustment then
|
|
offset = self.IronSightsPosAfterShootingAdjustment
|
|
local right = ang:Right()
|
|
local up = ang:Up()
|
|
local forward = ang:Forward()
|
|
|
|
pos = pos + offset.x * right * mul
|
|
pos = pos + offset.y * forward * mul
|
|
pos = pos + offset.z * up * mul
|
|
end
|
|
end
|
|
|
|
return pos, ang
|
|
end
|
|
|
|
function SWEP:SecondaryAttack()
|
|
if not self.IronSightsPos then return end
|
|
|
|
if self:GetReloading() then return end
|
|
|
|
self:SetIronsights(not self:GetIronsights())
|
|
|
|
self:SetNextSecondaryFire(CurTime() + 0.3)
|
|
end
|
|
|
|
--[[---------------------------------------------------------
|
|
Reload does nothing
|
|
---------------------------------------------------------]]
|
|
function SWEP:Reload()
|
|
if not self:DefaultReload(ACT_VM_RELOAD) then return end
|
|
self:SetReloading(true)
|
|
self:SetIronsights(false)
|
|
self:SetBurstTime(0)
|
|
self:SetBurstBulletNum(0)
|
|
self:GetOwner():SetAnimation(PLAYER_RELOAD)
|
|
self:SetReloadEndTime(CurTime() + 2)
|
|
self:SetTotalUsedMagCount(self:GetTotalUsedMagCount() + 1)
|
|
end
|
|
|
|
function SWEP:OnRestore()
|
|
self:SetNextSecondaryFire(0)
|
|
self:SetIronsights(false)
|
|
end
|
|
|
|
function SWEP:Equip(NewOwner)
|
|
if self.PrimaryClipLeft and self.SecondaryClipLeft and self.PrimaryAmmoLeft and self.SecondaryAmmoLeft then
|
|
NewOwner:SetAmmo(self.PrimaryAmmoLeft, self:GetPrimaryAmmoType())
|
|
NewOwner:SetAmmo(self.SecondaryAmmoLeft, self:GetSecondaryAmmoType())
|
|
|
|
self:SetClip1(self.PrimaryClipLeft)
|
|
self:SetClip2(self.SecondaryClipLeft)
|
|
end
|
|
end
|
|
|
|
function SWEP:OnDrop()
|
|
self.PrimaryClipLeft = self:Clip1()
|
|
self.SecondaryClipLeft = self:Clip2()
|
|
|
|
if not IsValid(self:GetLastOwner()) then return end
|
|
self.PrimaryAmmoLeft = self:GetLastOwner():GetAmmoCount(self:GetPrimaryAmmoType())
|
|
self.SecondaryAmmoLeft = self:GetLastOwner():GetAmmoCount(self:GetSecondaryAmmoType())
|
|
self:SetCollisionGroup(COLLISION_GROUP_INTERACTIVE_DEBRIS)
|
|
end
|
|
|
|
function SWEP:CalcViewModel()
|
|
if (not CLIENT) or (not IsFirstTimePredicted()) then return end
|
|
self.bIron = self:GetIronsights()
|
|
self.fIronTime = self:GetIronsightsTime()
|
|
self.fCurrentTime = CurTime()
|
|
self.fCurrentSysTime = SysTime()
|
|
end
|
|
|
|
-- Note that if you override Think in your SWEP, you should call
|
|
-- BaseClass.Think(self) so as not to break ironsights
|
|
function SWEP:Think()
|
|
self:CalcViewModel()
|
|
if self.Primary.ClipSize ~= -1 and not self:GetReloading() and not self:GetIronsights() and self:GetLastPrimaryAttack() + 1 < CurTime() and self:GetHoldType() == self.HoldType and GAMEMODE.Config.ironshoot then
|
|
self:SetHoldType(self.LoweredHoldType)
|
|
end
|
|
if self:GetReloadEndTime() ~= 0 and CurTime() >= self:GetReloadEndTime() then
|
|
self:SetReloadEndTime(0)
|
|
self:SetReloading(false)
|
|
if GAMEMODE.Config.ironshoot then
|
|
self:SetHoldType(self.LoweredHoldType)
|
|
end
|
|
if CLIENT then self.hasShot = false end
|
|
end
|
|
if self:GetBurstTime() ~= 0 and CurTime() >= self:GetBurstTime() then
|
|
self:PrimaryAttack()
|
|
end
|
|
end
|
|
|
|
function SWEP:DrawWeaponSelection(x, y, wide, tall, alpha)
|
|
if self.IconLetter and string.find(self.IconLetter, "^[0-9a-wA-Z]$") then
|
|
draw.DrawNonParsedSimpleText(self.IconLetter, "CSSelectIcons", x + wide / 2, y + tall * 0.2, Color(255, 210, 0, 255), TEXT_ALIGN_CENTER)
|
|
|
|
-- try to fool them into thinking they're playing a Tony Hawks game
|
|
draw.DrawNonParsedSimpleText(self.IconLetter, "CSSelectIcons", x + wide / 2 + math.Rand(-4, 4), y + tall * 0.2 + math.Rand(-14, 14), Color(255, 210, 0, math.Rand(10, 120)), TEXT_ALIGN_CENTER)
|
|
draw.DrawNonParsedSimpleText(self.IconLetter, "CSSelectIcons", x + wide / 2 + math.Rand(-4, 4), y + tall * 0.2 + math.Rand(-9, 9), Color(255, 210, 0, math.Rand(10, 120)), TEXT_ALIGN_CENTER)
|
|
else
|
|
-- Set us up the texture
|
|
surface.SetDrawColor(255, 255, 255, alpha)
|
|
surface.SetTexture(self.WepSelectIcon)
|
|
|
|
-- Lets get a sin wave to make it bounce
|
|
local fsin = 0
|
|
|
|
if self.BounceWeaponIcon then
|
|
fsin = math.sin(CurTime() * 10) * 5
|
|
end
|
|
|
|
-- Borders
|
|
y = y + 10
|
|
x = x + 10
|
|
wide = wide - 20
|
|
|
|
-- Draw that motherfucker
|
|
surface.DrawTexturedRect(x + fsin, y - fsin, wide - fsin * 2, (wide / 2) + fsin)
|
|
|
|
-- Draw weapon info box
|
|
self:PrintWeaponInfo(x + wide + 20, y + tall * 0.95, alpha)
|
|
end
|
|
end
|
|
|
|
if CLIENT then
|
|
function SWEP:ViewModelDrawn(vm)
|
|
if self.DarkRPViewModelBoneManipulations and not self:GetReloading() then
|
|
self:UpdateDarkRPBones(vm, self.DarkRPViewModelBoneManipulations)
|
|
else
|
|
self:ResetDarkRPBones(vm)
|
|
end
|
|
end
|
|
|
|
function SWEP:UpdateDarkRPBones(vm, manipulations)
|
|
if not IsValid(vm) or not vm:GetBoneCount() then return end
|
|
|
|
-- Fill in missing bone names. Things fuck up when we workaround the scale bug and bones are missing.
|
|
local bones = {}
|
|
for i = 0, vm:GetBoneCount() - 1 do
|
|
local bonename = vm:GetBoneName(i)
|
|
if manipulations[bonename] then
|
|
bones[bonename] = manipulations[bonename]
|
|
else
|
|
bones[bonename] = {
|
|
scale = Vector(1,1,1),
|
|
pos = Vector(0,0,0),
|
|
angle = Angle(0,0,0)
|
|
}
|
|
end
|
|
end
|
|
|
|
for k, v in pairs(bones) do
|
|
local bone = vm:LookupBone(k)
|
|
if not bone then continue end
|
|
|
|
-- Bone scaling seems to be buggy. Workaround.
|
|
local scale = Vector(v.scale.x, v.scale.y, v.scale.z)
|
|
local ms = Vector(1,1,1)
|
|
local cur = vm:GetBoneParent(bone)
|
|
while cur >= 0 do
|
|
local pscale = bones[vm:GetBoneName(cur)].scale
|
|
ms = ms * pscale
|
|
cur = vm:GetBoneParent(cur)
|
|
end
|
|
scale = scale * ms
|
|
|
|
if vm:GetManipulateBoneScale(bone) ~= scale then
|
|
vm:ManipulateBoneScale(bone, scale)
|
|
end
|
|
if vm:GetManipulateBonePosition(bone) ~= v.pos then
|
|
vm:ManipulateBonePosition(bone, v.pos)
|
|
end
|
|
if vm:GetManipulateBoneAngles(bone) ~= v.angle then
|
|
vm:ManipulateBoneAngles(bone, v.angle)
|
|
end
|
|
end
|
|
end
|
|
|
|
function SWEP:ResetDarkRPBones(vm)
|
|
if not IsValid(vm) or not vm:GetBoneCount() then return end
|
|
for i = 0, vm:GetBoneCount() - 1 do
|
|
vm:ManipulateBoneScale(i, Vector(1, 1, 1))
|
|
vm:ManipulateBoneAngles(i, Angle(0, 0, 0))
|
|
vm:ManipulateBonePosition(i, Vector(0, 0, 0))
|
|
end
|
|
end
|
|
end
|
|
|
|
hook.Add("SetupMove", "DarkRP_WeaponSpeed", function(ply, mv)
|
|
local wep = ply:GetActiveWeapon()
|
|
if not wep:IsValid() or not wep.DarkRPBased or not wep.GetIronsights or not wep:GetIronsights() then return end
|
|
|
|
mv:SetMaxClientSpeed(mv:GetMaxClientSpeed() / 3)
|
|
end)
|