[lua]
---- Carry weapon SWEP
if SERVER then
AddCSLuaFile(“shared.lua”)
end
SWEP.HoldType = “pistol”
if CLIENT then
SWEP.PrintName = “Magneto-stick”
SWEP.Instructions = “Pick things up with this.”
SWEP.Slot = 4
SWEP.SlotPos = 1
–SWEP.ViewModelFOV = 54
end
SWEP.Base = “weapon_tttbase”
SWEP.Spawnable = true
SWEP.AdminSpawnable = true
SWEP.AutoSpawnable = false
SWEP.ViewModel = “models/weapons/v_stunbaton.mdl”
SWEP.WorldModel = “models/weapons/w_stunbaton.mdl”
SWEP.Weight = 5
SWEP.AutoSwitchTo = false
SWEP.AutoSwitchFrom = false
SWEP.DrawCrosshair = false
SWEP.ViewModelFlip = false
SWEP.Primary.ClipSize = -1
SWEP.Primary.DefaultClip = -1
SWEP.Primary.Automatic = true
SWEP.Primary.Ammo = “none”
SWEP.Primary.Delay = 0.1
SWEP.Secondary.ClipSize = -1
SWEP.Secondary.DefaultClip = -1
SWEP.Secondary.Automatic = true
SWEP.Secondary.Ammo = “none”
SWEP.Secondary.Delay = 0.1
SWEP.Kind = WEAPON_CARRY
SWEP.AllowDelete = false
SWEP.AllowDrop = false
SWEP.NoSights = true
SWEP.EntHolding = nil
SWEP.CarryHack = nil
SWEP.Constr = nil
SWEP.PrevOwner = nil
local allow_rag = CreateConVar(“ttt_ragdoll_carrying”, “1”)
local rag_force = CreateConVar(“ttt_ragdoll_carrying_force”, “8000”)
– Allowing weapon pickups can allow players to cause a crash in the physics
– system (ie. not fixable). Tuning the range seems to make this more
– difficult. Not sure why. It’s that kind of crash.
local allow_wep = CreateConVar(“ttt_weapon_carrying”, “1”)
local wep_range = CreateConVar(“ttt_weapon_carrying_range”, “50”)
– not customizable via convars as some objects rely on not being carryable for
– gameplay purposes
CARRY_WEIGHT_LIMIT = 45
function SWEP:reset()
– debug
–print(“called reset with carryhack/constr/entholding:”, self.CarryHack, self.Constr, self.EntHolding)
if ValidEntity(self.CarryHack) then
self.CarryHack:Remove()
end
if ValidEntity(self.Constr) then
self.Constr:Remove()
end
if ValidEntity(self.EntHolding) then
if not IsValid(self.PrevOwner) then
–print(“resetting entholding owner to nil”)
self.EntHolding:SetOwner(nil)
else
–print(“resetting entholding owner to previous”)
self.EntHolding:SetOwner(self.PrevOwner)
end
local phys = self.EntHolding:GetPhysicsObject()
if ValidEntity(phys) then
--print("clearing physobj flags")
phys:ClearGameFlag(FVPHYSICS_PLAYER_HELD)
phys:EnableCollisions(true)
end
end
self.EntHolding = nil
self.CarryHack = nil
self.Constr = nil
end
function SWEP:CheckValidity()
if not ValidEntity(self.EntHolding) or not ValidEntity(self.CarryHack) or not ValidEntity(self.Constr) then
-- if one of them is not valid but another is non-nil...
if (self.EntHolding or self.CarryHack or self.Constr) then
--print("checkvalidity found badness, resetting...")
self:reset()
end
return false
else
return true
end
end
local ent_diff = Vector()
local ent_diff_time = CurTime()
function SWEP:Think()
if SERVER then
if not self:CheckValidity() then return end
-- If we are too far from our object, force a drop. To avoid doing this
-- vector math extremely often (esp. when everyone is carrying something)
-- even though the occurrence is very rare, limited to once per
-- second. This should be plenty to catch the rare glitcher.
if CurTime() > ent_diff_time then
ent_diff = self:GetPos() - self.EntHolding:GetPos()
if ent_diff:Dot(ent_diff) > 20000 then
self:reset()
return
end
ent_diff_time = CurTime() + 1
end
self.CarryHack:SetPos(self.Owner:EyePos() + self.Owner:GetAimVector() * 70)
self.CarryHack:SetAngles(self.Owner:GetAngles())
self.EntHolding:PhysWake()
end
end
function SWEP:PrimaryAttack()
self:DoAttack(false)
end
function SWEP:SecondaryAttack()
self:DoAttack(true)
end
function SWEP:MoveObject(phys, pdir, maxforce, is_ragdoll)
if not ValidEntity(phys) then return end
local speed = phys:GetVelocity():Length()
– remap speed from 0 -> 125 to force 1 -> 4000
local force = maxforce + (1 - maxforce) * (speed / 125)
if is_ragdoll then
force = force * 4
end
pdir = pdir * force
local mass = phys:GetMass()
– scale more for light objects
if mass < 50 then
pdir = pdir * (mass + 0.5) * (1 / 50)
end
phys:ApplyForceCenter(pdir)
end
function SWEP:GetRange(target)
if IsValid(target) and target:IsWeapon() and allow_wep:GetBool() then
return wep_range:GetFloat()
else
return 100
end
end
function SWEP:AllowPickup(target)
local phys = target:GetPhysicsObject()
local ply = self:GetOwner()
return (IsValid(phys) and IsValid(ply) and
phys:GetMass() < CARRY_WEIGHT_LIMIT and
ply:GetGroundEntity() != target and
(target:GetClass() != “prop_ragdoll” or allow_rag:GetBool()) and
((not target:IsWeapon()) or allow_wep:GetBool()))
end
function SWEP:DoAttack(pickup)
self.Weapon:SetNextPrimaryFire( CurTime() + self.Primary.Delay )
self.Weapon:SetNextSecondaryFire( CurTime() + self.Secondary.Delay )
if ValidEntity(self.EntHolding) then
–print(“attacking with held item; dropping”)
self.Weapon:SendWeaponAnim( ACT_VM_MISSCENTER )
self:Drop()
self.Weapon:SetNextSecondaryFire(CurTime() + 0.3)
return
end
local ply = self.Owner
local trace = ply:GetEyeTrace()
if ValidEntity(trace.Entity) then
local ent = trace.Entity
local phys = trace.Entity:GetPhysicsObject()
if not ValidEntity(phys) or not phys:IsMoveable() or phys:HasGameFlag(FVPHYSICS_PLAYER_HELD) then
return
end
-- if we let the client mess with physics, desync ensues
if CLIENT then return end
if pickup then
--print("attempting pickup")
if (ply:EyePos() - trace.HitPos):Length() < self:GetRange(ent) then
--print("target is in range")
if self:AllowPickup(ent) then
self:Pickup()
self.Weapon:SendWeaponAnim( ACT_VM_HITCENTER )
-- make the refire slower to avoid immediately dropping
self.Weapon:SetNextSecondaryFire(CurTime() + 0.4)
return
else
local is_ragdoll = trace.Entity:GetClass() == "prop_ragdoll"
-- pull heavy stuff
local ent = trace.Entity
local phys = ent:GetPhysicsObject()
local pdir = trace.Normal * -1
if is_ragdoll then
if ValidEntity(phys) then
self:MoveObject(phys, pdir, 3000, is_ragdoll)
end
phys = ent:GetPhysicsObjectNum(trace.PhysicsBone)
-- increase refire to make rags easier to drag
self.Weapon:SetNextSecondaryFire(CurTime() + 0.04)
end
if ValidEntity(phys) then
self:MoveObject(phys, pdir, 6000, is_ragdoll)
return
end
end
end
else
if (ply:EyePos() - trace.HitPos):Length() < 125 then
local phys = trace.Entity:GetPhysicsObject()
if ValidEntity(phys) then
if ValidEntity(phys) then
local pdir = trace.Normal
self:MoveObject(phys, pdir, 6000, (trace.Entity:GetClass() == "prop_ragdoll"))
self.Weapon:SetNextPrimaryFire(CurTime() + 0.03)
return
end
end
return
end
end
end
end
– Perform a pickup
function SWEP:Pickup()
if CLIENT or ValidEntity(self.EntHolding) then return end
local ply = self.Owner
local trace = ply:GetEyeTrace()
local ent = trace.Entity
self.EntHolding = ent
local entphys = ent:GetPhysicsObject()
–print(“picking up”, ent)
if ValidEntity(ent) and ValidEntity(entphys) then
--print("creating carry hack ent")
self.CarryHack = ents.Create("prop_physics")
if ValidEntity(self.CarryHack) then
self.CarryHack:SetPos(self.EntHolding:GetPos())
self.CarryHack:SetModel("models/weapons/w_bugbait.mdl")
self.CarryHack:SetHealth(999)
self.CarryHack:SetOwner(ply)
self.CarryHack:SetCollisionGroup(COLLISION_GROUP_NONE)
self.CarryHack:SetSolid(SOLID_NONE)
self.CarryHack:SetNoDraw(true)
self.CarryHack:Spawn()
-- if we already are owner before pickup, we will not want to disown
-- this entity when we drop it
self.PrevOwner = self.EntHolding:GetOwner()
--print("entholding had owner", self.PrevOwner)
self.EntHolding:SetOwner(ply)
local phys = self.CarryHack:GetPhysicsObject()
if ValidEntity(phys) then
phys:SetMass(200)
phys:SetDamping(0, 1000)
phys:AddGameFlag(FVPHYSICS_PLAYER_HELD)
end
entphys:AddGameFlag(FVPHYSICS_PLAYER_HELD)
local bone = math.Clamp(trace.PhysicsBone, 0, 1)
if ent:GetClass() == "prop_ragdoll" then
self.Constr = constraint.Weld(self.CarryHack, self.EntHolding, 0, trace.PhysicsBone, rag_force:GetInt(), true)
else
--print("creating constraint between", self.CarryHack, self.EntHolding)
self.Constr = constraint.Weld(self.CarryHack, self.EntHolding, 0, bone, 0, true)
end
--print("pickup complete, carry/constr/held", self.CarryHack, self.Constr, self.EntHolding)
--entphys:EnableCollisions(false)
--timer.Simple(0, entphys.EnableCollisions, entphys, true)
end
-- end
end
end
local down = Vector(0, 0, -1)
function SWEP:AllowEntityDrop()
local ply = self.Owner
local ent = self.CarryHack
if not ValidEntity(ply) or not ValidEntity(ent) then return false end
local ground = ply:GetGroundEntity()
if ground and (ground:IsWorld() or ValidEntity(ground)) then return true end
local diff = (ent:GetPos() - ply:GetShootPos()):Normalize()
if down:Dot(diff) > 0.75 then
return false
else
return true
end
end
– Drop it
function SWEP:Drop()
if not self:CheckValidity() then return end
if not self:AllowEntityDrop() then return end
if SERVER then
–print(“going to drop, carry/constr/holding”, self.CarryHack, self.Constr, self.EntHolding)
self.Constr:Remove()
self.CarryHack:Remove()
local phys = self.EntHolding:GetPhysicsObject()
if ValidEntity(phys) then
phys:EnableCollisions(true)
phys:EnableGravity(true)
phys:EnableDrag(true)
phys:EnableMotion(true)
phys:Wake()
phys:ApplyForceCenter(self.Owner:GetAimVector() * 500)
phys:ClearGameFlag(FVPHYSICS_PLAYER_HELD)
--phys:SetMass(self.OldMass)
end
-- Try to limit ragdoll slinging
if self.EntHolding:GetClass() == "prop_ragdoll" then
for i=0, self.EntHolding:GetPhysicsObjectCount()-1 do
local subphys = self.EntHolding:GetPhysicsObjectNum(i)
if IsValid(subphys) then
subphys:SetVelocity(vector_origin)
subphys:SetVelocityInstantaneous(vector_origin)
subphys:AddAngleVelocity(-1 * subphys:GetAngleVelocity())
end
end
self.EntHolding:SetVelocity(vector_origin)
end
-- this is already done in reset()
--self.EntHolding:SetOwner(nil)
end
self:reset()
end
function SWEP:OnRemove()
self:reset()
end
function SWEP:Deploy()
self:reset()
return self.BaseClass:Deploy()
end
function SWEP:Holster()
self:Drop()
return self.BaseClass:Holster()
end
function SWEP:ShouldDropOnDie()
return false
end
function SWEP:OnDrop()
self:Remove()
end
function SWEP:GetClass()
return “weapon_zm_carry”
end
[/lua]
Check that code out.