424 lines
11 KiB
Lua
424 lines
11 KiB
Lua
AddCSLuaFile('shared.lua')
|
|
AddCSLuaFile('cl_init.lua')
|
|
include('shared.lua')
|
|
|
|
local SwingSound = Sound('WeaponFrag.Throw')
|
|
local HitSound = Sound('Flesh.ImpactHard')
|
|
|
|
SWEP.HitDistance = 48
|
|
SWEP.KnockDistSqr = 65 * 65
|
|
SWEP.isDragging = false
|
|
SWEP.pickedEnt = nil
|
|
SWEP.pickedEntOffset = Vector(0,0,0)
|
|
|
|
-------------------------------------------------
|
|
-- MAIN
|
|
-------------------------------------------------
|
|
|
|
local function isCarOwner(ent, ply)
|
|
|
|
local owner = ent:CPPIGetOwner()
|
|
return IsValid(owner) and
|
|
(owner == ply or owner.Buddies and owner.Buddies[ply] and table.HasValue(owner.Buddies[ply], true)) and
|
|
not ply:InVehicle() -- to prevent mouse clicks lock/unlock the car
|
|
|
|
end
|
|
|
|
function SWEP:lookingAtLockable(ply, ent)
|
|
|
|
local eyepos = ply:EyePos()
|
|
if not IsValid(ent) then return false end
|
|
|
|
if ent:IsDoor() and not ent:IsBlocked() and eyepos:DistToSqr(ent:GetPos()) < self.KnockDistSqr then
|
|
return true
|
|
end
|
|
|
|
if ent.isFadingDoor and ent:GetPos():DistToSqr(eyepos) < self.KnockDistSqr then return true end
|
|
if ent.IsSimfphyscar then return true end
|
|
|
|
end
|
|
|
|
function SWEP:lockUnlockAnimation(ply, snd)
|
|
|
|
ply:EmitSound('doors/door_latch1.wav')
|
|
ply:DoAnimation(ACT_GMOD_GESTURE_ITEM_PLACE)
|
|
|
|
end
|
|
|
|
function SWEP:doKnock(ply, sound)
|
|
|
|
ply:EmitSound(sound, 100, math.random(90, 110))
|
|
ply:DoAnimation(ACT_HL2MP_GESTURE_RANGE_ATTACK_FIST)
|
|
|
|
end
|
|
|
|
SWEP.UseDel = CurTime()
|
|
function SWEP:DoTrace()
|
|
|
|
local trace = {}
|
|
trace.start = self.Owner:GetShootPos()
|
|
trace.endpos = trace.start + (self.Owner:GetAimVector() * 10000)
|
|
trace.filter = { self.Owner, self.Weapon }
|
|
local tr = util.TraceLine(trace)
|
|
|
|
return tr
|
|
|
|
end
|
|
|
|
function SWEP:DealDamage()
|
|
|
|
if not self.anim then return end
|
|
|
|
local owner = self:GetOwner()
|
|
owner:LagCompensation(true)
|
|
|
|
local aim = owner:EyeAngles():Forward()
|
|
local tr = util.TraceLine({
|
|
start = owner:GetShootPos(),
|
|
endpos = owner:GetShootPos() + aim * self.HitDistance,
|
|
filter = owner,
|
|
mask = MASK_SHOT_HULL
|
|
})
|
|
|
|
if not IsValid(tr.Entity) then
|
|
tr = util.TraceHull({
|
|
start = owner:GetShootPos(),
|
|
endpos = owner:GetShootPos() + aim * self.HitDistance,
|
|
filter = owner,
|
|
mins = Vector(-10, -10, -8),
|
|
maxs = Vector(10, 10, 8),
|
|
mask = MASK_SHOT_HULL
|
|
})
|
|
end
|
|
|
|
if tr.Hit then
|
|
owner:EmitSound(HitSound)
|
|
end
|
|
|
|
if (SERVER and IsValid(tr.Entity) and (tr.Entity:IsNPC() or tr.Entity:IsPlayer() or tr.Entity:Health() > 0)) then
|
|
local dmginfo = DamageInfo()
|
|
|
|
local attacker = owner
|
|
if not IsValid(attacker) then attacker = self end
|
|
dmginfo:SetAttacker(attacker)
|
|
|
|
dmginfo:SetInflictor(self)
|
|
local dmgAmount = math.random(8, 12)
|
|
if tr.Entity:IsPlayer() then dmgAmount = math.Clamp(tr.Entity:Health() - (attacker:HasBuff('Meth') and 0 or 20), 0, dmgAmount) end
|
|
dmginfo:SetDamage(dmgAmount)
|
|
dmginfo:SetDamageType(DMG_CLUB)
|
|
|
|
if (self.anim == 'fists_left') then
|
|
dmginfo:SetDamageForce(owner:GetRight() * 4912 + owner:GetForward() * 9998)
|
|
elseif (self.anim == 'fists_right') then
|
|
dmginfo:SetDamageForce(owner:GetRight() * -4912 + owner:GetForward() * 9989)
|
|
end
|
|
|
|
tr.Entity:TakeDamageInfo(dmginfo)
|
|
|
|
-- if tr.Entity:GetClass() == 'func_breakable_surf' then
|
|
-- tr.Entity:Fire('Shatter', Vector(0,0,0))
|
|
-- owner:TakeDamage(math.random(10, 15), Entity(0), tr.Entity)
|
|
-- end
|
|
end
|
|
|
|
if SERVER and IsValid(tr.Entity) then
|
|
local phys = tr.Entity:GetPhysicsObject()
|
|
if (IsValid(phys)) then
|
|
phys:ApplyForceOffset(owner:GetAimVector() * 80 * phys:GetMass(), tr.HitPos)
|
|
end
|
|
|
|
local ent = tr.Entity
|
|
local carOwner = ent:CPPIGetOwner()
|
|
if IsValid(carOwner) and ent.IsSimfphyscar and carOwner ~= owner
|
|
and not (carOwner.Buddies and carOwner.Buddies[owner] and table.HasValue(carOwner.Buddies[owner], true))
|
|
and CurTime() - (owner.karmaLast.cardamage or -180) > 180 then
|
|
owner:AddKarma(-1, L.cardamage)
|
|
print('[KARMA] ' .. tostring(owner) .. ' -1 karma for car fists damage')
|
|
owner.karmaLast.cardamage = CurTime()
|
|
end
|
|
end
|
|
|
|
owner:LagCompensation(false)
|
|
|
|
end
|
|
|
|
function SWEP:PrimaryAttack()
|
|
|
|
local owner = self:GetOwner()
|
|
if owner:IsGhost() or owner:IsHandcuffed() then return end
|
|
|
|
if self.isFists and self:GetHoldType() == 'fist' then
|
|
local can, why = hook.Run('dbg-hands.canPunch', owner, owner:GetEyeTrace())
|
|
if can == false then
|
|
if why then owner:Notify('warning', why) end
|
|
return
|
|
end
|
|
owner:SetAnimation(PLAYER_ATTACK1)
|
|
|
|
self.anim = self.punchRight and 'fists_right' or 'fists_left'
|
|
owner:EmitSound(SwingSound)
|
|
|
|
self:SetNextMeleeAttack(CurTime() + 0.2)
|
|
self:SetNextPrimaryFire(CurTime() + 0.9)
|
|
self:SetNextSecondaryFire(CurTime() + 0.9)
|
|
|
|
self.punchRight = not self.punchRight
|
|
else
|
|
local ent = owner:GetEyeTrace().Entity
|
|
|
|
if not self:lookingAtLockable(owner, ent) or owner:GetNetVar('Ghost') then return end
|
|
self:SetNextPrimaryFire(CurTime() + 0.3)
|
|
|
|
if ent:CanBeLockedBy(owner) and CurTime() > (ent.nextLock or 0) then
|
|
owner:DoAnimation(ACT_GMOD_GESTURE_ITEM_PLACE)
|
|
|
|
owner:DelayedAction('door_lock', 'Закрытие замка', {
|
|
time = 1.5,
|
|
check = function() return octolib.use.check(owner, ent) end,
|
|
succ = function()
|
|
ent:EmitSound('doors/door_latch1.wav')
|
|
ent:DoLock()
|
|
end,
|
|
})
|
|
elseif ent.IsSimfphyscar then
|
|
if isCarOwner(ent, owner) and CurTime() > (ent.nextLock or 0) then
|
|
ent:Lock()
|
|
end
|
|
else
|
|
self:doKnock(owner, 'physics/wood/wood_crate_impact_hard2.wav')
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
function SWEP:SecondaryAttack()
|
|
|
|
local owner = self.Owner
|
|
if owner:IsGhost() or owner:IsHandcuffed() then return end
|
|
|
|
local ent = owner:GetEyeTrace().Entity
|
|
if not self:lookingAtLockable(owner, ent) then return end
|
|
|
|
self:SetNextSecondaryFire(CurTime() + 0.3)
|
|
|
|
if ent:CanBeUnlockedBy(owner) then
|
|
ent:DoUnlock()
|
|
ent:EmitSound('doors/door_latch1.wav')
|
|
owner:DoAnimation(ACT_GMOD_GESTURE_ITEM_PLACE)
|
|
elseif ent.IsSimfphyscar then
|
|
if isCarOwner(ent, owner) then
|
|
ent:UnLock()
|
|
end
|
|
else
|
|
self:doKnock(owner, 'physics/wood/wood_crate_impact_hard3.wav')
|
|
end
|
|
|
|
end
|
|
|
|
function SWEP:Reload()
|
|
|
|
-- do nothing
|
|
|
|
end
|
|
|
|
function SWEP:PrimaryAttackPressed()
|
|
|
|
if self.isFists then return end
|
|
|
|
if self.isDragging then
|
|
self:StopDragging()
|
|
return
|
|
end
|
|
|
|
local owner = self:GetOwner()
|
|
local veh = owner:GetVehicle()
|
|
if IsValid(veh) and IsValid(veh:GetParent()) then return end
|
|
|
|
local tr = util.TraceLine({
|
|
start = owner:EyePos(),
|
|
endpos = owner:EyePos() + owner:GetAimVector() * 100,
|
|
filter = owner
|
|
})
|
|
|
|
local ent = tr.Entity
|
|
if not IsValid(ent) then
|
|
self:SetHoldType('pistol')
|
|
self.isPointing = true
|
|
return
|
|
end
|
|
|
|
local can, why = hook.Run('dbg-hands.canDrag', owner, ent, tr)
|
|
if can == false then
|
|
if why then owner:Notify('warning', why) end
|
|
return
|
|
end
|
|
|
|
local class = ent:GetClass()
|
|
local ph = class and not self.doNotDrag[class] and ent:GetPhysicsObjectNum(tr.PhysicsBone)
|
|
local should = not ent:IsPlayer() or (not ent:IsGhost() and ent:Crouching() and ent:KeyDown(IN_ATTACK))
|
|
if should and IsValid(ph) and not ent:IsDoor() then
|
|
self.pickedEnt = ent
|
|
if ent.OnHandsPickup then
|
|
ent:OnHandsPickup(owner, self)
|
|
end
|
|
self.pickedEntBone = tr.PhysicsBone
|
|
self.pickedEntOffset = ph:WorldToLocal(tr.HitPos)
|
|
self.isDragging = true
|
|
self.pickedEnt.lastCarrier = owner -- to register killer
|
|
self:SetHoldType(IsValid(veh) and 'pistol' or 'magic')
|
|
|
|
if class == 'prop_ragdoll' then
|
|
local name = owner:Name()
|
|
if owner:isCP() then name = name .. L.analyzer_cop end
|
|
|
|
ent.criminals = ent.criminals or {}
|
|
if not table.HasValue(ent.criminals, name) then table.insert(ent.criminals, name) end
|
|
end
|
|
else
|
|
self:SetHoldType('pistol')
|
|
self.isPointing = true
|
|
end
|
|
|
|
end
|
|
|
|
function SWEP:PrimaryAttackReleased()
|
|
|
|
if not self.isPointing then return end
|
|
|
|
self:SetHoldType('normal')
|
|
self.isPointing = nil
|
|
|
|
end
|
|
|
|
function SWEP:SecondaryAttackPressed()
|
|
|
|
self.isFists = true
|
|
self.isPointing = nil
|
|
|
|
self:SetHoldType('fist')
|
|
octolib.stopAnimations(self:GetOwner())
|
|
|
|
end
|
|
|
|
function SWEP:SecondaryAttackReleased()
|
|
|
|
self.isFists = nil
|
|
|
|
self:SetHoldType('normal')
|
|
self.anim = nil
|
|
|
|
end
|
|
|
|
function SWEP:StopDragging()
|
|
|
|
if self.isDragging then
|
|
self.isDragging = false
|
|
self.pickedEnt = nil
|
|
self:SetHoldType('normal')
|
|
|
|
netstream.Start(self:GetOwner(), 'dbg_hands.StopDragging')
|
|
end
|
|
|
|
end
|
|
|
|
function SWEP:Think()
|
|
|
|
local owner = self:GetOwner()
|
|
if owner:IsGhost() then return end
|
|
|
|
if self.isDragging then
|
|
local ent = self.pickedEnt
|
|
if not IsValid(ent) then
|
|
self:StopDragging()
|
|
return
|
|
end
|
|
|
|
local should = owner:KeyDown(IN_ATTACK) and not owner:KeyDown(IN_ATTACK2) and owner:GetGroundEntity() ~= ent and not ent.APG_Picked
|
|
if ent:IsPlayer() and not (ent:KeyDown(IN_ATTACK) and ent:Crouching() and not ent:IsGhost()) then should = false end
|
|
if ent.IsSimfphyscar and ent:EngineActive() and ent:GetThrottle() > 0.5 then should = false end
|
|
|
|
if not should then return self:StopDragging() end
|
|
|
|
local ph = ent:GetPhysicsObjectNum(self.pickedEntBone)
|
|
local ph2 = ent:GetPhysicsObject()
|
|
|
|
local pos = owner:EyePos() + owner:GetAimVector()*80
|
|
local pos2 = ph:LocalToWorld(self.pickedEntOffset)
|
|
if (pos - pos2):LengthSqr() > 10000 then
|
|
self:StopDragging()
|
|
return
|
|
end
|
|
|
|
local force = math.min(100, math.pow(ph2:GetMass(), 0.7) * 4)
|
|
if ent:GetPhysicsObjectCount() > 1 then force = force * 1.5 end
|
|
local vel = (pos - pos2) * force
|
|
if vel.z > 0 and ph:GetVelocity().z < 0 then vel.z = vel.z * 2.5 end
|
|
|
|
ph:ApplyForceOffset(vel, pos2)
|
|
ph:ApplyForceCenter(-ph:GetVelocity() * 0.3)
|
|
ph:AddAngleVelocity(-ph:GetAngleVelocity() * 0.3)
|
|
|
|
vel.z = vel.z * 0.4
|
|
owner:SetVelocity(-vel * 0.02)
|
|
elseif self.isFists then
|
|
local meleetime = self:GetNextMeleeAttack()
|
|
if meleetime > 0 and CurTime() > meleetime and owner:KeyDown(IN_ATTACK2) then
|
|
if not owner:InVehicle() then
|
|
self:DealDamage()
|
|
end
|
|
self:SetNextMeleeAttack(0)
|
|
end
|
|
end
|
|
|
|
self:NextThink(CurTime() + 0.03)
|
|
return true
|
|
|
|
end
|
|
|
|
-------------------------------------------------
|
|
-- JOB-SPECIFIC
|
|
-------------------------------------------------
|
|
|
|
util.AddNetworkString 'dbg-revive'
|
|
net.Receive('dbg-revive', function(len, ply)
|
|
|
|
local target = net.ReadEntity()
|
|
if not IsValid(target) or not target:IsPlayer() or not target:IsGhost()
|
|
or ply:IsGhost() or ply:Team() ~= TEAM_PRIEST or target:GetPos():DistToSqr(ply:GetPos()) > 6000 then
|
|
return
|
|
end
|
|
|
|
local ghostBuffs = target:GetDBVar('ghostBuffs', 0)
|
|
if ghostBuffs >= 10 then
|
|
ply:Notify('error', 'Священник может помочь грешникам, но всему есть предел!')
|
|
return
|
|
end
|
|
target:SetDBVar('ghostBuffs', ghostBuffs + 1)
|
|
|
|
target:SetNetVar('_SpawnTime', math.max((math.min(CurTime() + 5, target:GetNetVar('_SpawnTime', 0))), target:GetNetVar('_SpawnTime', 0) - 30))
|
|
target:EmitSound('dbg/revive.ogg', 70, 100, 0.8)
|
|
local effectdata = EffectData()
|
|
effectdata:SetOrigin(target:GetPos() + Vector(0,0,45))
|
|
effectdata:SetMagnitude(2.5)
|
|
effectdata:SetScale(2)
|
|
effectdata:SetRadius(3)
|
|
util.Effect('GlassImpact', effectdata, true, true)
|
|
|
|
end)
|
|
|
|
-------------------------------------------------
|
|
-- HOOKS
|
|
-------------------------------------------------
|
|
|
|
function SWEP:CanUseAnimation()
|
|
|
|
if self:GetHoldType() ~= 'normal' then
|
|
return false
|
|
end
|
|
|
|
end
|
|
|
|
hook.Add('AllowPlayerPickup', 'dbg_hands', function()
|
|
return false
|
|
end)
|