mirror of
https://github.com/lifestorm/wnsrc.git
synced 2025-12-17 13:53:45 +03:00
Upload
This commit is contained in:
90
gamemodes/terrortown/entities/effects/crimescene_dummy.lua
Normal file
90
gamemodes/terrortown/entities/effects/crimescene_dummy.lua
Normal file
@@ -0,0 +1,90 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
|
||||
local backup_mdl = Model("models/player/phoenix.mdl")
|
||||
|
||||
function EFFECT:Init(data)
|
||||
self.Corpse = data:GetEntity()
|
||||
|
||||
self:SetPos(data:GetOrigin())
|
||||
|
||||
local ang = data:GetAngles()
|
||||
-- pitch is done via aim_pitch, and roll shouldn't happen
|
||||
ang.r = 0
|
||||
ang.p = 0
|
||||
self:SetAngles(ang)
|
||||
|
||||
self:SetRenderBounds(Vector(-18, -18, 0), Vector(18, 18, 64))
|
||||
|
||||
self.Sequence = data:GetColor()
|
||||
self.Cycle = data:GetScale()
|
||||
self.Duration = data:GetRadius() or 0
|
||||
self.EndTime = CurTime() + self.Duration
|
||||
|
||||
self.FadeTime = 2
|
||||
|
||||
self.FadeIn = CurTime() + self.FadeTime
|
||||
self.FadeOut = self.EndTime - self.FadeTime
|
||||
|
||||
self.Alpha = 0
|
||||
|
||||
if IsValid(self.Corpse) then
|
||||
local mdl = self.Corpse:GetModel()
|
||||
mdl = util.IsValidModel(mdl) and mdl or backup_mdl
|
||||
|
||||
self.Dummy = ClientsideModel(mdl, RENDERGROUP_TRANSLUCENT)
|
||||
if not self.Dummy then return end
|
||||
self.Dummy:SetPos(data:GetOrigin())
|
||||
self.Dummy:SetAngles(ang)
|
||||
self.Dummy:AddEffects(EF_NODRAW)
|
||||
|
||||
self.Dummy:SetSequence(self.Sequence)
|
||||
self.Dummy:SetCycle(self.Cycle)
|
||||
|
||||
local pose = data:GetStart()
|
||||
self.Dummy:SetPoseParameter("aim_yaw", pose.x)
|
||||
self.Dummy:SetPoseParameter("aim_pitch", pose.y)
|
||||
self.Dummy:SetPoseParameter("move_yaw", pose.z)
|
||||
else
|
||||
self.Dummy = nil
|
||||
end
|
||||
end
|
||||
|
||||
function EFFECT:Think()
|
||||
if self.EndTime < CurTime() then
|
||||
SafeRemoveEntity(self.Dummy)
|
||||
return false
|
||||
end
|
||||
|
||||
if self.FadeIn > CurTime() then
|
||||
self.Alpha = 1 - ((self.FadeIn - CurTime()) / self.FadeTime)
|
||||
elseif self.FadeOut < CurTime() then
|
||||
self.Alpha = 1 - ((CurTime() - self.FadeOut) / self.FadeTime)
|
||||
end
|
||||
|
||||
return IsValid(self.Dummy)
|
||||
end
|
||||
|
||||
function EFFECT:Render()
|
||||
render.SuppressEngineLighting( true )
|
||||
render.SetColorModulation(0.4, 0.4, 1)
|
||||
render.SetBlend(0.8 * self.Alpha)
|
||||
|
||||
if self.Dummy then
|
||||
--self.Dummy:ClearPoseParameters()
|
||||
self.Dummy:DrawModel()
|
||||
end
|
||||
|
||||
render.SetBlend(1)
|
||||
render.SetColorModulation(1, 1, 1)
|
||||
render.SuppressEngineLighting(false)
|
||||
end
|
||||
|
||||
54
gamemodes/terrortown/entities/effects/crimescene_shot.lua
Normal file
54
gamemodes/terrortown/entities/effects/crimescene_shot.lua
Normal file
@@ -0,0 +1,54 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
|
||||
function EFFECT:Init(data)
|
||||
self.ShotStart = data:GetStart()
|
||||
self.ShotEnd = data:GetOrigin()
|
||||
|
||||
-- ws = worldspace
|
||||
self:SetRenderBoundsWS(self.ShotStart, self.ShotEnd)
|
||||
|
||||
self.HitBox = data:GetMagnitude()
|
||||
|
||||
self.Duration = data:GetScale() or 0
|
||||
self.EndTime = CurTime() + self.Duration
|
||||
|
||||
self.FadeTime = 3
|
||||
|
||||
self.FadeIn = CurTime() + self.FadeTime
|
||||
self.FadeOut = self.EndTime - self.FadeTime
|
||||
|
||||
self.Width = 0
|
||||
self.WidthMax = 5
|
||||
end
|
||||
|
||||
function EFFECT:Think()
|
||||
if self.EndTime < CurTime() then
|
||||
return false
|
||||
end
|
||||
|
||||
if self.FadeIn > CurTime() then
|
||||
self.Width = self.WidthMax * (1 - ((self.FadeIn - CurTime()) / self.FadeTime))
|
||||
elseif self.FadeOut < CurTime() then
|
||||
self.Width = self.WidthMax * (1 - ((CurTime() - self.FadeOut) / self.FadeTime))
|
||||
end
|
||||
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
local shot_mat = Material("cable/blue_elec")
|
||||
--local clr = Color(0, 0, 100, 255)
|
||||
function EFFECT:Render()
|
||||
render.SetMaterial(shot_mat)
|
||||
|
||||
render.DrawBeam(self.ShotStart, self.ShotEnd, self.Width, 0, 0, self.Color)
|
||||
end
|
||||
74
gamemodes/terrortown/entities/effects/pulse_sphere.lua
Normal file
74
gamemodes/terrortown/entities/effects/pulse_sphere.lua
Normal file
@@ -0,0 +1,74 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
|
||||
local model_orb = Model("models/Combine_Helicopter/helicopter_bomb01.mdl")
|
||||
|
||||
function EFFECT:Init(data)
|
||||
self:SetPos(data:GetOrigin())
|
||||
|
||||
self.Radius = data:GetRadius()
|
||||
|
||||
local rh = self.Radius
|
||||
self:SetRenderBounds(Vector(-rh, -rh, -rh), Vector(rh, rh, rh))
|
||||
|
||||
self.EndTime = CurTime() + data:GetScale()
|
||||
self.FadeTime = data:GetMagnitude()
|
||||
|
||||
self.FadeIn = CurTime() + self.FadeTime
|
||||
self.FadeOut = self.EndTime - self.FadeTime
|
||||
|
||||
self.Alpha = 0
|
||||
|
||||
self.Orb = ClientsideModel(model_orb, RENDERGROUP_TRANSLUCENT)
|
||||
self.Orb:SetPos(data:GetOrigin())
|
||||
self.Orb:AddEffects(EF_NODRAW)
|
||||
|
||||
local r = 28 / 2 -- hardcoded because stuff like :BoundingRadius won't work here
|
||||
|
||||
self.EndScale = self.Radius / r
|
||||
end
|
||||
|
||||
function EFFECT:Think()
|
||||
if self.EndTime < CurTime() then
|
||||
SafeRemoveEntity(self.Orb)
|
||||
return false
|
||||
end
|
||||
|
||||
if self.FadeIn > CurTime() then
|
||||
self.Alpha = 1 - ((self.FadeIn - CurTime()) / self.FadeTime)
|
||||
elseif self.FadeOut < CurTime() then
|
||||
self.Alpha = 1 - ((CurTime() - self.FadeOut) / self.FadeTime)
|
||||
end
|
||||
|
||||
self.Orb:SetModelScale(self.EndScale * self.Alpha, 0)
|
||||
|
||||
local ang = self.Orb:GetAngles()
|
||||
ang.y = ang.y + 500 * FrameTime()
|
||||
self.Orb:SetAngles(ang)
|
||||
|
||||
return IsValid(self.Orb)
|
||||
end
|
||||
|
||||
local mat_orb = Material("models/effects/splodearc_sheet")
|
||||
function EFFECT:Render()
|
||||
render.MaterialOverride(mat_orb)
|
||||
render.SuppressEngineLighting( true )
|
||||
render.SetColorModulation(0, 0, 1)
|
||||
render.SetBlend(0.8 * self.Alpha)
|
||||
|
||||
self.Orb:DrawModel()
|
||||
|
||||
render.SetBlend(1)
|
||||
render.SetColorModulation(1, 1, 1)
|
||||
render.SuppressEngineLighting(false)
|
||||
render.MaterialOverride()
|
||||
end
|
||||
|
||||
108
gamemodes/terrortown/entities/effects/teleport_beamdown.lua
Normal file
108
gamemodes/terrortown/entities/effects/teleport_beamdown.lua
Normal file
@@ -0,0 +1,108 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
|
||||
local loopsound = Sound("ambient/levels/labs/teleport_mechanism_windup1.wav")
|
||||
|
||||
local mat_rising = Material( "models/props_combine/stasisshield_sheet")
|
||||
local mat_sparkle = Material("models/effects/comball_tape")
|
||||
local top = 80
|
||||
local mid = 32
|
||||
local final_height = top
|
||||
|
||||
function EFFECT:Init(data)
|
||||
self.Owner = data:GetEntity()
|
||||
|
||||
self:SetPos(data:GetOrigin())
|
||||
self:SetAngles(data:GetAngles())
|
||||
|
||||
self.BasePos = self:GetPos()
|
||||
self.BeamDownPos = self.BasePos + Vector(0, 0, final_height)
|
||||
|
||||
self.DrawTop = true
|
||||
|
||||
self.BeamDownTime = CurTime() + data:GetMagnitude()
|
||||
self.EndTime = self.BeamDownTime + data:GetRadius()
|
||||
|
||||
self.BeamDown = false
|
||||
|
||||
if IsValid(self.Owner) then
|
||||
self.Dummy = ClientsideModel(self.Owner:GetModel(), RENDERGROUP_OPAQUE)
|
||||
self.Dummy:SetPos(self.BasePos)
|
||||
self.Dummy:SetAngles(data:GetAngles())
|
||||
self.Dummy:AddEffects(EF_NODRAW)
|
||||
|
||||
local s = self.Dummy:LookupSequence("idle_all")
|
||||
self.Dummy:SetSequence(s)
|
||||
else
|
||||
self.Dummy = nil
|
||||
end
|
||||
|
||||
sound.Play(loopsound, self:GetPos(), 50, 100)
|
||||
end
|
||||
|
||||
function EFFECT:Think()
|
||||
if self.EndTime < CurTime() then
|
||||
SafeRemoveEntity(self.Dummy)
|
||||
return
|
||||
end
|
||||
|
||||
if not (IsValid(self.Owner) and IsValid(self.Dummy)) then
|
||||
return false
|
||||
end
|
||||
|
||||
-- first draw same effect as beamup
|
||||
if self.BeamDownTime >= CurTime() then
|
||||
local pos = self:GetPos()
|
||||
if pos.z - self.BasePos.z < final_height then
|
||||
pos.z = pos.z + (90 * FrameTime())
|
||||
self:SetPos(pos)
|
||||
end
|
||||
else
|
||||
-- then move to beamdown effects
|
||||
local pos = self:GetPos()
|
||||
if pos.z > self.BeamDownPos.z - final_height then
|
||||
pos.z = pos.z - (90 * FrameTime())
|
||||
self:SetPos(pos)
|
||||
else
|
||||
self.DrawTop = false
|
||||
end
|
||||
|
||||
self.BeamDown = true
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
local vector_up = Vector(0,0,1)
|
||||
function EFFECT:Render()
|
||||
local norm = vector_up * -1
|
||||
local pos = self:GetPos()
|
||||
local dist = norm:Dot(pos)
|
||||
|
||||
render.MaterialOverride(mat_rising)
|
||||
|
||||
render.EnableClipping(true)
|
||||
render.PushCustomClipPlane(norm, dist)
|
||||
if not self.BeamDown then
|
||||
self.Dummy:DrawModel()
|
||||
else
|
||||
self.Owner:DrawModel()
|
||||
end
|
||||
render.PopCustomClipPlane()
|
||||
render.EnableClipping(false)
|
||||
|
||||
render.MaterialOverride()
|
||||
|
||||
if self.DrawTop then
|
||||
render.SetMaterial(mat_rising)
|
||||
render.DrawQuadEasy(pos, vector_up, 30, 30, COLOR_RED, 0)
|
||||
end
|
||||
end
|
||||
114
gamemodes/terrortown/entities/effects/teleport_beamup.lua
Normal file
114
gamemodes/terrortown/entities/effects/teleport_beamup.lua
Normal file
@@ -0,0 +1,114 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
|
||||
local mat_rising = Material( "models/props_combine/stasisshield_sheet" )
|
||||
--local mat_rising = Material( "models/shadertest/shader4" )
|
||||
--local mat_rising = Material( "models/props_lab/Tank_Glass001" )
|
||||
local mat_sparkle = Material("models/effects/comball_tape")
|
||||
--local mat_sparkle = Material( "models/props_combine/stasisshield_sheet" )
|
||||
local top = 80
|
||||
local mid = 32
|
||||
local final_height = top
|
||||
|
||||
local loopsound = Sound("ambient/levels/labs/teleport_mechanism_windup1.wav")
|
||||
|
||||
function EFFECT:Init(data)
|
||||
self.Owner = data:GetEntity()
|
||||
|
||||
self:SetPos(data:GetOrigin())
|
||||
self:SetAngles(data:GetAngles())
|
||||
|
||||
self.BasePos = self:GetPos()
|
||||
self.BeamDownPos = self.BasePos + Vector(0, 0, final_height)
|
||||
|
||||
self.BeamDownTime = CurTime() + data:GetMagnitude()
|
||||
self.EndTime = self.BeamDownTime + data:GetRadius()
|
||||
|
||||
self.DrawTop = true
|
||||
|
||||
self.BeamDown = false
|
||||
|
||||
if IsValid(self.Owner) then
|
||||
self.Dummy = ClientsideModel(self.Owner:GetModel(), RENDERGROUP_OPAQUE)
|
||||
self.Dummy:SetPos(self.BasePos)
|
||||
self.Dummy:SetAngles(self:GetAngles())
|
||||
self.Dummy:AddEffects(EF_NODRAW)
|
||||
|
||||
local s = self.Dummy:LookupSequence("idle_all")
|
||||
self.Dummy:SetSequence(s)
|
||||
else
|
||||
self.Dummy = nil
|
||||
end
|
||||
|
||||
|
||||
sound.Play(loopsound, self:GetPos(), 50, 100)
|
||||
end
|
||||
|
||||
|
||||
function EFFECT:Think()
|
||||
if self.EndTime < CurTime() then
|
||||
SafeRemoveEntity(self.Dummy)
|
||||
return
|
||||
end
|
||||
|
||||
if not (IsValid(self.Owner) and IsValid(self.Dummy)) then
|
||||
return false
|
||||
end
|
||||
|
||||
if self.BeamDownTime >= CurTime() then
|
||||
local pos = self:GetPos()
|
||||
if pos.z - self.BasePos.z < final_height then
|
||||
pos.z = pos.z + (90 * FrameTime())
|
||||
self:SetPos(pos)
|
||||
end
|
||||
else
|
||||
-- then move to beamdown effects
|
||||
local pos = self:GetPos()
|
||||
if pos.z > self.BeamDownPos.z - final_height then
|
||||
pos.z = pos.z - (90 * FrameTime())
|
||||
self:SetPos(pos)
|
||||
else
|
||||
self.DrawTop = false
|
||||
end
|
||||
|
||||
self.BeamDown = true
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
local vector_up = Vector(0,0,1)
|
||||
function EFFECT:Render()
|
||||
-- clipping positioning
|
||||
local norm = vector_up * -1
|
||||
local pos = self:GetPos()
|
||||
local dist = norm:Dot(pos)
|
||||
|
||||
-- do rendering
|
||||
render.MaterialOverride(mat_rising)
|
||||
|
||||
render.EnableClipping(true)
|
||||
render.PushCustomClipPlane(norm, dist)
|
||||
if not self.BeamDown then
|
||||
self.Owner:DrawModel()
|
||||
else
|
||||
self.Dummy:DrawModel()
|
||||
end
|
||||
render.PopCustomClipPlane()
|
||||
render.EnableClipping(false)
|
||||
|
||||
render.MaterialOverride()
|
||||
|
||||
if self.DrawTop then
|
||||
render.SetMaterial(mat_rising)
|
||||
render.DrawQuadEasy(pos, vector_up, 30, 30, COLOR_RED, 0)
|
||||
end
|
||||
end
|
||||
131
gamemodes/terrortown/entities/entities/base_ammo_ttt.lua
Normal file
131
gamemodes/terrortown/entities/entities/base_ammo_ttt.lua
Normal file
@@ -0,0 +1,131 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
-- Ammo override base
|
||||
|
||||
AddCSLuaFile()
|
||||
|
||||
ENT.Type = "anim"
|
||||
|
||||
-- Override these values
|
||||
ENT.AmmoType = "Pistol"
|
||||
ENT.AmmoAmount = 1
|
||||
ENT.AmmoMax = 10
|
||||
ENT.AmmoEntMax = 1
|
||||
ENT.Model = Model( "models/items/boxsrounds.mdl" )
|
||||
|
||||
|
||||
function ENT:RealInit() end -- bw compat
|
||||
|
||||
-- Some subclasses want to do stuff before/after initing (eg. setting color)
|
||||
-- Using self.BaseClass gave weird problems, so stuff has been moved into a fn
|
||||
-- Subclasses can easily call this whenever they want to
|
||||
function ENT:Initialize()
|
||||
self:SetModel( self.Model )
|
||||
|
||||
self:PhysicsInit( SOLID_VPHYSICS )
|
||||
self:SetMoveType( MOVETYPE_VPHYSICS )
|
||||
self:AddSolidFlags( FSOLID_TRIGGER )
|
||||
|
||||
self:SetCollisionGroup( COLLISION_GROUP_WEAPON )
|
||||
|
||||
self:UseTriggerBounds( true, 24 )
|
||||
|
||||
self.tickRemoval = false
|
||||
|
||||
-- this made the ammo get physics'd too early, meaning it would fall
|
||||
-- through physics surfaces it was lying on on the client, leading to
|
||||
-- inconsistencies
|
||||
-- local phys = self:GetPhysicsObject()
|
||||
-- if (phys:IsValid()) then
|
||||
-- phys:Wake()
|
||||
-- end
|
||||
self.AmmoEntMax = self.AmmoAmount
|
||||
end
|
||||
|
||||
-- Pseudo-clone of SDK's UTIL_ItemCanBeTouchedByPlayer
|
||||
-- aims to prevent picking stuff up through fences and stuff
|
||||
function ENT:PlayerCanPickup(ply)
|
||||
if ply == self:GetOwner() then return false end
|
||||
|
||||
local result = hook.Call("TTTCanPickupAmmo", nil, ply, self)
|
||||
|
||||
if result != nil then
|
||||
return result
|
||||
end
|
||||
|
||||
|
||||
local ent = self
|
||||
local phys = ent:GetPhysicsObject()
|
||||
local spos = phys:IsValid() and phys:GetPos() or ent:OBBCenter()
|
||||
local epos = ply:GetShootPos() -- equiv to EyePos in SDK
|
||||
|
||||
local tr = util.TraceLine({start=spos, endpos=epos, filter={ply, ent}, mask=MASK_SOLID})
|
||||
|
||||
-- can pickup if trace was not stopped
|
||||
return tr.Fraction == 1.0
|
||||
end
|
||||
|
||||
function ENT:CheckForWeapon(ply)
|
||||
if not self.CachedWeapons then
|
||||
-- create a cache of what weapon classes use this ammo
|
||||
local tbl = {}
|
||||
for k,v in pairs(weapons.GetList()) do
|
||||
if v and v.AmmoEnt == self:GetClass() then
|
||||
table.insert(tbl, WEPS.GetClass(v))
|
||||
end
|
||||
end
|
||||
|
||||
self.CachedWeapons = tbl
|
||||
end
|
||||
|
||||
-- Check if player has a weapon that we know needs us. This is called in
|
||||
-- Touch, which is called many a time, so we use the cache here to avoid
|
||||
-- looping through every weapon the player has to check their AmmoEnt.
|
||||
for _, w in ipairs(self.CachedWeapons) do
|
||||
if ply:HasWeapon(w) then return true end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function ENT:Touch(ent)
|
||||
if (SERVER and self.tickRemoval ~= true) and ent:IsValid() and ent:IsPlayer() and self:CheckForWeapon(ent) and self:PlayerCanPickup(ent) then
|
||||
local ammo = ent:GetAmmoCount(self.AmmoType)
|
||||
|
||||
-- need clipmax info and room for at least 1/4th
|
||||
if self.AmmoMax >= (ammo + math.ceil(self.AmmoAmount * 0.25)) then
|
||||
local given = self.AmmoAmount
|
||||
given = math.min(given, self.AmmoMax - ammo)
|
||||
ent:GiveAmmo(given, self.AmmoType)
|
||||
|
||||
local newEntAmount = self.AmmoAmount - given
|
||||
self.AmmoAmount = newEntAmount
|
||||
|
||||
if self.AmmoAmount <= 0 or math.ceil(self.AmmoEntMax * 0.25) > self.AmmoAmount then
|
||||
self.tickRemoval = true
|
||||
self:Remove()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Hack to force ammo to physwake
|
||||
if SERVER then
|
||||
function ENT:Think()
|
||||
if not self.first_think then
|
||||
self:PhysWake()
|
||||
self.first_think = true
|
||||
|
||||
-- Immediately unhook the Think, save cycles. The first_think thing is
|
||||
-- just there in case it still Thinks somehow in the future.
|
||||
self.Think = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
75
gamemodes/terrortown/entities/entities/info_manipulate.lua
Normal file
75
gamemodes/terrortown/entities/entities/info_manipulate.lua
Normal file
@@ -0,0 +1,75 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
-- Dummy entity to convert ZM info_manipulate traps to TTT ones
|
||||
|
||||
ENT.Type = "point"
|
||||
ENT.Base = "base_point"
|
||||
|
||||
function ENT:Think()
|
||||
if not self.Replaced then
|
||||
self:CreateReplacement()
|
||||
|
||||
self:Remove()
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:KeyValue(key, value)
|
||||
if key == "OnPressed" then
|
||||
-- store raw, will be feeding this into the replacement's StoreOutput()
|
||||
self.RawOutputs = self.RawOutputs or {}
|
||||
table.insert(self.RawOutputs, value)
|
||||
elseif key == "Cost" then
|
||||
self[key] = tonumber(value)
|
||||
elseif key == "Active" or key == "RemoveOnTrigger" then
|
||||
self[key] = tobool(value)
|
||||
elseif key == "Description" then
|
||||
self[key] = tostring(value)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function ENT:CreateReplacement()
|
||||
local tgt = ents.Create("ttt_traitor_button")
|
||||
if not IsValid(tgt) then return end
|
||||
|
||||
self.Replaced = true
|
||||
|
||||
tgt:SetPos(self:GetPos())
|
||||
|
||||
-- feed in our properties into replacement as keyvals
|
||||
|
||||
tgt:SetKeyValue("targetname", self:GetName())
|
||||
|
||||
if not self.Active then
|
||||
-- start locked
|
||||
tgt:SetKeyValue("spawnflags", tostring(2048))
|
||||
end
|
||||
|
||||
if self.Description and self.Description != "" then
|
||||
tgt:SetKeyValue("description", self.Description)
|
||||
end
|
||||
|
||||
if self.Cost then
|
||||
tgt:SetKeyValue("wait", tostring(self.Cost))
|
||||
end
|
||||
|
||||
if self.RemoveOnTrigger then
|
||||
tgt:SetKeyValue("RemoveOnPress", tostring(true))
|
||||
end
|
||||
|
||||
if self.RawOutputs then
|
||||
for k, v in pairs(self.RawOutputs) do
|
||||
tgt:SetKeyValue("OnPressed", tostring(v))
|
||||
end
|
||||
end
|
||||
|
||||
tgt:Spawn()
|
||||
end
|
||||
21
gamemodes/terrortown/entities/entities/item_ammo_357_ttt.lua
Normal file
21
gamemodes/terrortown/entities/entities/item_ammo_357_ttt.lua
Normal file
@@ -0,0 +1,21 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
-- Rifle ammo override
|
||||
|
||||
AddCSLuaFile()
|
||||
|
||||
ENT.Type = "anim"
|
||||
ENT.Base = "base_ammo_ttt"
|
||||
ENT.AmmoType = "357"
|
||||
ENT.AmmoAmount = 10
|
||||
ENT.AmmoMax = 20
|
||||
ENT.Model = Model("models/items/357ammo.mdl")
|
||||
ENT.AutoSpawnable = true
|
||||
@@ -0,0 +1,21 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
-- Pistol ammo override
|
||||
|
||||
AddCSLuaFile()
|
||||
|
||||
ENT.Type = "anim"
|
||||
ENT.Base = "base_ammo_ttt"
|
||||
ENT.AmmoType = "Pistol"
|
||||
ENT.AmmoAmount = 20
|
||||
ENT.AmmoMax = 60
|
||||
ENT.Model = Model("models/items/boxsrounds.mdl")
|
||||
ENT.AutoSpawnable = true
|
||||
@@ -0,0 +1,28 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
-- Deagle ammo override
|
||||
|
||||
AddCSLuaFile()
|
||||
|
||||
ENT.Type = "anim"
|
||||
ENT.Base = "base_ammo_ttt"
|
||||
ENT.AmmoType = "AlyxGun"
|
||||
ENT.AmmoAmount = 12
|
||||
ENT.AmmoMax = 36
|
||||
ENT.Model = Model("models/items/357ammo.mdl")
|
||||
ENT.AutoSpawnable = true
|
||||
|
||||
function ENT:Initialize()
|
||||
-- Differentiate from rifle ammo
|
||||
self:SetColor(Color(255, 100, 100, 255))
|
||||
|
||||
return self.BaseClass.Initialize(self)
|
||||
end
|
||||
@@ -0,0 +1,21 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
-- MAC10 ammo override
|
||||
|
||||
AddCSLuaFile()
|
||||
|
||||
ENT.Type = "anim"
|
||||
ENT.Base = "base_ammo_ttt"
|
||||
ENT.AmmoType = "SMG1"
|
||||
ENT.AmmoAmount = 30
|
||||
ENT.AmmoMax = 60
|
||||
ENT.Model = Model("models/items/boxmrounds.mdl")
|
||||
ENT.AutoSpawnable = true
|
||||
@@ -0,0 +1,21 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
-- Shottie ammo override
|
||||
|
||||
AddCSLuaFile()
|
||||
|
||||
ENT.Type = "anim"
|
||||
ENT.Base = "base_ammo_ttt"
|
||||
ENT.AmmoType = "Buckshot"
|
||||
ENT.AmmoAmount = 8
|
||||
ENT.AmmoMax = 24
|
||||
ENT.Model = "models/items/boxbuckshot.mdl"
|
||||
ENT.AutoSpawnable = true
|
||||
@@ -0,0 +1,73 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
-- common grenade projectile code
|
||||
|
||||
AddCSLuaFile()
|
||||
|
||||
ENT.Type = "anim"
|
||||
ENT.Model = Model("models/weapons/w_eq_flashbang_thrown.mdl")
|
||||
|
||||
|
||||
AccessorFunc( ENT, "thrower", "Thrower")
|
||||
|
||||
function ENT:SetupDataTables()
|
||||
self:NetworkVar("Float", 0, "ExplodeTime")
|
||||
end
|
||||
|
||||
function ENT:Initialize()
|
||||
self:SetModel(self.Model)
|
||||
|
||||
self:PhysicsInit(SOLID_VPHYSICS)
|
||||
self:SetMoveType(MOVETYPE_VPHYSICS)
|
||||
self:SetSolid(SOLID_BBOX)
|
||||
self:SetCollisionGroup(COLLISION_GROUP_PROJECTILE)
|
||||
|
||||
if SERVER then
|
||||
self:SetExplodeTime(0)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function ENT:SetDetonateTimer(length)
|
||||
self:SetDetonateExact( CurTime() + length )
|
||||
end
|
||||
|
||||
function ENT:SetDetonateExact(t)
|
||||
self:SetExplodeTime(t or CurTime())
|
||||
end
|
||||
|
||||
-- override to describe what happens when the nade explodes
|
||||
function ENT:Explode(tr)
|
||||
ErrorNoHalt("ERROR: BaseGrenadeProjectile explosion code not overridden!\n")
|
||||
end
|
||||
|
||||
function ENT:Think()
|
||||
local etime = self:GetExplodeTime() or 0
|
||||
if etime != 0 and etime < CurTime() then
|
||||
-- if thrower disconnects before grenade explodes, just don't explode
|
||||
if SERVER and (not IsValid(self:GetThrower())) then
|
||||
self:Remove()
|
||||
etime = 0
|
||||
return
|
||||
end
|
||||
|
||||
-- find the ground if it's near and pass it to the explosion
|
||||
local spos = self:GetPos()
|
||||
local tr = util.TraceLine({start=spos, endpos=spos + Vector(0,0,-32), mask=MASK_SHOT_HULL, filter=self.thrower})
|
||||
|
||||
local success, err = pcall(self.Explode, self, tr)
|
||||
if not success then
|
||||
-- prevent effect spam on Lua error
|
||||
self:Remove()
|
||||
ErrorNoHalt("ERROR CAUGHT: ttt_basegrenade_proj: " .. err .. "\n")
|
||||
end
|
||||
end
|
||||
end
|
||||
113
gamemodes/terrortown/entities/entities/ttt_beacon.lua
Normal file
113
gamemodes/terrortown/entities/entities/ttt_beacon.lua
Normal file
@@ -0,0 +1,113 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
-- DISABLED: see weapon_ttt_beacon
|
||||
|
||||
-- DISABLED
|
||||
--AddCSLuaFile()
|
||||
|
||||
if CLIENT then
|
||||
-- this entity can be DNA-sampled so we need some display info
|
||||
ENT.Icon = "vgui/ttt/icon_beacon"
|
||||
ENT.PrintName = "Beacon"
|
||||
end
|
||||
|
||||
ENT.Type = "anim"
|
||||
ENT.Model = Model("models/props_lab/reciever01b.mdl")
|
||||
ENT.CanHavePrints = true
|
||||
ENT.CanUseKey = true
|
||||
|
||||
function ENT:Initialize()
|
||||
self:SetModel(self.Model)
|
||||
|
||||
if SERVER then
|
||||
self:PhysicsInit(SOLID_VPHYSICS)
|
||||
end
|
||||
|
||||
self:SetMoveType(MOVETYPE_VPHYSICS)
|
||||
self:SetSolid(SOLID_VPHYSICS)
|
||||
self:SetCollisionGroup(COLLISION_GROUP_INTERACTIVE)
|
||||
|
||||
if SERVER then
|
||||
self:SetMaxHealth(100)
|
||||
end
|
||||
self:SetHealth(100)
|
||||
|
||||
if SERVER then
|
||||
self:SetUseType(SIMPLE_USE)
|
||||
self:NextThink(CurTime() + 1)
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:UseOverride(activator)
|
||||
if IsValid(activator) and self:GetOwner() == activator then
|
||||
local wep = activator:GetWeapon("weapon_ttt_beacon")
|
||||
|
||||
if IsValid(wep) then
|
||||
local pickup = wep:PickupBeacon()
|
||||
if not pickup then
|
||||
return
|
||||
end
|
||||
-- else pickup successful, continue with print transfer and removal
|
||||
else
|
||||
wep = activator:Give("weapon_ttt_beacon")
|
||||
end
|
||||
|
||||
self:Remove()
|
||||
end
|
||||
end
|
||||
|
||||
local zapsound = Sound("npc/assassin/ball_zap1.wav")
|
||||
function ENT:OnTakeDamage(dmginfo)
|
||||
self:TakePhysicsDamage(dmginfo)
|
||||
|
||||
self:SetHealth(self:Health() - dmginfo:GetDamage())
|
||||
if self:Health() < 0 then
|
||||
self:Remove()
|
||||
|
||||
local effect = EffectData()
|
||||
effect:SetOrigin(self:GetPos())
|
||||
util.Effect("cball_explode", effect)
|
||||
sound.Play(zapsound, self:GetPos())
|
||||
|
||||
if IsValid(self:GetOwner()) then
|
||||
TraitorMsg(self:GetOwner(), "ONE OF YOUR BEACONS HAS BEEN DESTROYED!")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--local beep = Sound("weapons/c4/c4_beep1.wav")
|
||||
function ENT:Think()
|
||||
if SERVER then
|
||||
--sound.Play(beep, self:GetPos(), 100, 80)
|
||||
else
|
||||
local dlight = DynamicLight(self:EntIndex())
|
||||
if dlight then
|
||||
dlight.Pos = self:GetPos()
|
||||
dlight.r = 0
|
||||
dlight.g = 0
|
||||
dlight.b = 255
|
||||
dlight.Brightness = 1
|
||||
dlight.Size = 128
|
||||
dlight.Decay = 500
|
||||
dlight.DieTime = CurTime() + 0.1
|
||||
end
|
||||
end
|
||||
|
||||
self:NextThink(CurTime() + 5)
|
||||
return true
|
||||
end
|
||||
|
||||
if SERVER then
|
||||
function ENT:UpdateTransmitState()
|
||||
return TRANSMIT_ALWAYS
|
||||
end
|
||||
end
|
||||
|
||||
494
gamemodes/terrortown/entities/entities/ttt_c4/cl_init.lua
Normal file
494
gamemodes/terrortown/entities/entities/ttt_c4/cl_init.lua
Normal file
@@ -0,0 +1,494 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
-- bomb menus
|
||||
|
||||
include("shared.lua")
|
||||
|
||||
local starttime = C4_MINIMUM_TIME
|
||||
|
||||
local beep = Sound("weapons/c4/c4_click.wav")
|
||||
|
||||
local T = LANG.GetTranslation
|
||||
local PT = LANG.GetParamTranslation
|
||||
|
||||
---- ARMING
|
||||
|
||||
-- Initial bomb arming
|
||||
function ShowC4Config(bomb)
|
||||
local dframe = vgui.Create("DFrame")
|
||||
local w, h = 350, 270
|
||||
dframe:SetSize(w, h)
|
||||
dframe:Center()
|
||||
dframe:SetTitle(T("c4_arm"))
|
||||
dframe:SetVisible(true)
|
||||
dframe:ShowCloseButton(true)
|
||||
dframe:SetMouseInputEnabled(true)
|
||||
|
||||
local m = 5
|
||||
|
||||
local bg = vgui.Create("DPanel", dframe)
|
||||
bg:SetPaintBackground(false)
|
||||
bg:SetPos(0,0)
|
||||
bg:StretchToParent(m,m*5,m,m)
|
||||
|
||||
-- Time
|
||||
local dformtime = vgui.Create("DForm", bg)
|
||||
dformtime:SetPos(m, m)
|
||||
dformtime:SetSize(w - m*4, h / 2)
|
||||
dformtime:SetName(T("c4_arm_timer"))
|
||||
|
||||
local dclock = vgui.Create("DLabel", dformtime)
|
||||
dclock:SetFont("TimeLeft")
|
||||
dclock:SetText(util.SimpleTime(starttime, "%02i:%02i"))
|
||||
dclock:SizeToContents()
|
||||
dclock:SetPos(m*2, m*2)
|
||||
|
||||
dformtime:AddItem(dclock)
|
||||
|
||||
local ch, cw = dclock:GetSize()
|
||||
|
||||
local dtime = vgui.Create("DNumSlider", dformtime)
|
||||
dtime:SetWide(w - m*4)
|
||||
dtime:SetText(T("c4_arm_seconds"))
|
||||
dtime:SetDark(false)
|
||||
dtime:SetMin(C4_MINIMUM_TIME)
|
||||
dtime:SetMax(C4_MAXIMUM_TIME)
|
||||
dtime:SetDecimals(0)
|
||||
dtime:SetValue(starttime)
|
||||
dtime.Label:SetWrap(true)
|
||||
|
||||
local dwires
|
||||
|
||||
dtime.OnValueChanged = function(self, val)
|
||||
if not (IsValid(dclock) and IsValid(dwires)) then return end
|
||||
dclock:SetText(util.SimpleTime(val, "%02i:%02i"))
|
||||
|
||||
dwires:Update(val)
|
||||
end
|
||||
|
||||
dformtime:AddItem(dtime)
|
||||
|
||||
dwires = vgui.Create("DLabel", dformtime)
|
||||
dwires:SetText("")
|
||||
dwires:SetWrap(true)
|
||||
dwires:SetTall(30)
|
||||
|
||||
local SafeWires = bomb.SafeWiresForTime
|
||||
dwires.Update = function(s, t)
|
||||
s:SetText(PT("c4_arm_attempts", {num = C4_WIRE_COUNT - SafeWires(t)}))
|
||||
|
||||
s:InvalidateLayout()
|
||||
end
|
||||
|
||||
dwires:Update(starttime)
|
||||
|
||||
dformtime:AddItem(dwires)
|
||||
|
||||
local dformmisc = vgui.Create("DForm", bg)
|
||||
dformmisc:SetAutoSize(false)
|
||||
dformmisc:SetPos(m, m + 140)
|
||||
dformmisc:SetSize(w - m*4, h / 2)
|
||||
dformmisc:SetPadding(20)
|
||||
dformmisc:SetName(T("c4_remove_title"))
|
||||
|
||||
-- Buttons
|
||||
local by = 200
|
||||
|
||||
local bw, bh = 110, 25
|
||||
|
||||
local dgrab = vgui.Create("DButton", dformmisc)
|
||||
dgrab:SetPos(m*6, m*5)
|
||||
dgrab:SetSize(bw, bh)
|
||||
dgrab:SetText(T("c4_remove_pickup"))
|
||||
dgrab:SetDisabled(false)
|
||||
dgrab.DoClick = function()
|
||||
if not LocalPlayer() or not LocalPlayer():Alive() then return end
|
||||
|
||||
RunConsoleCommand("ttt_c4_pickup", bomb:EntIndex())
|
||||
dframe:Close()
|
||||
end
|
||||
|
||||
--dformmisc:AddItem(dgrab)
|
||||
|
||||
local ddestroy = vgui.Create("DButton", dformmisc)
|
||||
ddestroy:SetPos(w - m*4 - bw - m*6, m*5)
|
||||
ddestroy:SetSize(bw, bh)
|
||||
ddestroy:SetText(T("c4_remove_destroy1"))
|
||||
ddestroy:SetDisabled(false)
|
||||
ddestroy.Confirmed = false
|
||||
ddestroy.DoClick = function(s)
|
||||
if not LocalPlayer() or not LocalPlayer():Alive() then return end
|
||||
|
||||
if not s.Confirmed then
|
||||
s:SetText(T("c4_remove_destroy2"))
|
||||
s.Confirmed = true
|
||||
else
|
||||
RunConsoleCommand("ttt_c4_destroy", bomb:EntIndex())
|
||||
dframe:Close()
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local dconf = vgui.Create("DButton", bg)
|
||||
dconf:SetPos(m*2, m + by)
|
||||
dconf:SetSize(bw, bh)
|
||||
dconf:SetText(T("c4_arm"))
|
||||
dconf.DoClick = function()
|
||||
if not LocalPlayer() or not LocalPlayer():Alive() then return end
|
||||
local t = dtime:GetValue()
|
||||
if t and tonumber(t) then
|
||||
|
||||
RunConsoleCommand("ttt_c4_config", bomb:EntIndex(), t)
|
||||
dframe:Close()
|
||||
end
|
||||
end
|
||||
|
||||
local dcancel = vgui.Create("DButton", bg)
|
||||
dcancel:SetPos( w - m*4 - bw, m + by)
|
||||
dcancel:SetSize(bw, bh)
|
||||
dcancel:SetText(T("cancel"))
|
||||
dcancel.DoClick = function() dframe:Close() end
|
||||
|
||||
|
||||
dframe:MakePopup()
|
||||
end
|
||||
|
||||
---- DISARM
|
||||
|
||||
local disarm_beep = Sound("buttons/blip2.wav")
|
||||
local wire_cut = Sound("ttt/wirecut.wav")
|
||||
|
||||
local c4_bomb_mat = Material("vgui/ttt/c4_bomb")
|
||||
local c4_cut_mat = Material("vgui/ttt/c4_cut")
|
||||
local c4_wire_mat = Material("vgui/ttt/c4_wire")
|
||||
local c4_wirecut_mat = Material("vgui/ttt/c4_wire_cut")
|
||||
|
||||
--- Disarm panels
|
||||
local on_wire_cut = nil
|
||||
|
||||
-- Wire
|
||||
local PANEL = {}
|
||||
|
||||
|
||||
local wire_colors = {
|
||||
Color(200, 0, 0, 255), -- red
|
||||
Color(255, 255, 0, 255), -- yellow
|
||||
Color( 90, 90, 250, 255), -- blue
|
||||
Color(255, 255, 255, 255), -- white/grey
|
||||
Color( 20, 200, 20, 255), -- green
|
||||
Color(255, 160, 50, 255) -- brown
|
||||
};
|
||||
|
||||
function PANEL:Init()
|
||||
self.BaseClass.Init(self)
|
||||
|
||||
self:NoClipping(true)
|
||||
self:SetMouseInputEnabled(true)
|
||||
self:MoveToFront()
|
||||
|
||||
self.IsCut = false
|
||||
end
|
||||
|
||||
local c4_cut_tex = surface.GetTextureID(c4_cut_mat:GetName())
|
||||
function PANEL:PaintOverHovered()
|
||||
|
||||
surface.SetTexture(c4_cut_tex)
|
||||
surface.SetDrawColor(255, 255, 255, 255)
|
||||
surface.DrawTexturedRect(175, -20, 32, 32)
|
||||
|
||||
draw.SimpleText(PT("c4_disarm_cut", {num = self.Index}), "DermaDefault", 85, -10, COLOR_WHITE, 0, 0)
|
||||
end
|
||||
|
||||
PANEL.OnMousePressed = DButton.OnMousePressed
|
||||
|
||||
PANEL.OnMouseReleased = DButton.OnMouseReleased
|
||||
|
||||
|
||||
function PANEL:OnCursorEntered()
|
||||
if not self.IsCut then
|
||||
self.PaintOver = self.PaintOverHovered
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:OnCursorExited()
|
||||
self.PaintOver = self.BaseClass.PaintOver
|
||||
end
|
||||
|
||||
function PANEL:DoClick()
|
||||
if self:GetParent():GetDisabled() then return end
|
||||
|
||||
self.IsCut = true
|
||||
|
||||
self.PaintOver = self.BaseClass.PaintOver
|
||||
|
||||
self.m_Image:SetMaterial(c4_wirecut_mat)
|
||||
|
||||
surface.PlaySound(wire_cut)
|
||||
|
||||
if on_wire_cut then
|
||||
on_wire_cut(self.Index)
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:GetWireColor(i)
|
||||
i = i or 1
|
||||
i = i % (#wire_colors + 1)
|
||||
|
||||
return wire_colors[i] or COLOR_WHITE
|
||||
end
|
||||
|
||||
function PANEL:SetWireIndex(i)
|
||||
self.m_Image:SetImageColor(self:GetWireColor(i))
|
||||
|
||||
self.Index = i
|
||||
end
|
||||
|
||||
vgui.Register("DisarmWire", PANEL, "DImageButton")
|
||||
|
||||
|
||||
-- Bomb
|
||||
local PANEL = {}
|
||||
|
||||
AccessorFunc(PANEL, "wirecount", "WireCount")
|
||||
|
||||
|
||||
function PANEL:Init()
|
||||
self.Bomb = vgui.Create("DImage", self)
|
||||
self.Bomb:SetSize(256, 256)
|
||||
self.Bomb:SetPos(0,0)
|
||||
self.Bomb:SetMaterial(c4_bomb_mat)
|
||||
|
||||
self:SetWireCount(C4_WIRE_COUNT)
|
||||
|
||||
self.Wires = {}
|
||||
|
||||
local wx, wy = -84, 70
|
||||
local wc = 1
|
||||
for i=1, self:GetWireCount() do
|
||||
local w = vgui.Create("DisarmWire", self)
|
||||
w:SetPos(wx, wy)
|
||||
w:SetImage(c4_wire_mat:GetName())
|
||||
w:SizeToContents()
|
||||
|
||||
w:SetWireIndex(i)
|
||||
|
||||
table.insert(self.Wires, w)
|
||||
|
||||
wy = wy + 27
|
||||
end
|
||||
|
||||
self:SetPaintBackground(false)
|
||||
end
|
||||
|
||||
vgui.Register( "DisarmPanel", PANEL, "DPanel" )
|
||||
|
||||
|
||||
surface.CreateFont("C4Timer", {
|
||||
font = "TabLarge",
|
||||
size = 30,
|
||||
weight = 750
|
||||
})
|
||||
|
||||
local disarm_success, disarm_fail
|
||||
|
||||
function ShowC4Disarm(bomb)
|
||||
local dframe = vgui.Create("DFrame")
|
||||
local w, h = 420, 340
|
||||
dframe:SetSize(w, h)
|
||||
dframe:Center()
|
||||
dframe:SetTitle(T("c4_disarm"))
|
||||
dframe:SetVisible(true)
|
||||
dframe:ShowCloseButton(true)
|
||||
dframe:SetMouseInputEnabled(true)
|
||||
|
||||
local m = 5
|
||||
local title_h = 20
|
||||
|
||||
local left_w, left_h = 270, 270
|
||||
local right_w, right_h = 135, left_h
|
||||
|
||||
local bw, bh = 100, 25
|
||||
|
||||
local dleft = vgui.Create("ColoredBox", dframe)
|
||||
dleft:SetColor(Color(50, 50, 50))
|
||||
dleft:SetSize(left_w, left_h)
|
||||
dleft:SetPos(m, m + title_h)
|
||||
|
||||
local dright = vgui.Create("ColoredBox", dframe)
|
||||
dright:SetColor(Color(50, 50, 50))
|
||||
dright:SetSize(right_w, right_h)
|
||||
dright:SetPos(left_w + m * 2, m + title_h)
|
||||
|
||||
local dtimer = vgui.Create("DLabel", dright)
|
||||
dtimer:SetText("99:99:99")
|
||||
dtimer:SetFont("C4Timer")
|
||||
dtimer:SetTextColor(Color(200, 0, 0, 255))
|
||||
dtimer:SetExpensiveShadow(1, COLOR_BLACK)
|
||||
dtimer:SizeToContents()
|
||||
dtimer:SetWide(120)
|
||||
dtimer:SetPos(10, m)
|
||||
|
||||
dtimer.Bomb = bomb
|
||||
dtimer.Stop = false
|
||||
|
||||
dtimer.Think = function(s)
|
||||
if not IsValid(bomb) then return end
|
||||
if s.Stop then return end
|
||||
|
||||
local t = bomb:GetExplodeTime()
|
||||
if t then
|
||||
local r = t - CurTime()
|
||||
if r > 0 then
|
||||
s:SetText(util.SimpleTime(r, "%02i:%02i:%02i"))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local dstatus = vgui.Create("DLabel", dright)
|
||||
dstatus:SetText(T("c4_status_armed"))
|
||||
dstatus:SetFont("HealthAmmo")
|
||||
dstatus:SetTextColor(Color(200, 0, 0, 255))
|
||||
dstatus:SetExpensiveShadow(1, COLOR_BLACK)
|
||||
dstatus:SizeToContents()
|
||||
dstatus:SetPos(m, m*2 + 30)
|
||||
dstatus:CenterHorizontal()
|
||||
|
||||
|
||||
local dgrab = vgui.Create("DButton", dright)
|
||||
dgrab:SetPos(m, right_h - m*2 - bh*2)
|
||||
dgrab:SetSize(bw, bh)
|
||||
dgrab:CenterHorizontal()
|
||||
dgrab:SetText(T("c4_remove_pickup"))
|
||||
dgrab:SetDisabled(true)
|
||||
dgrab.DoClick = function()
|
||||
if (not LocalPlayer():Alive()) then return end
|
||||
RunConsoleCommand("ttt_c4_pickup", bomb:EntIndex())
|
||||
dframe:Close()
|
||||
end
|
||||
|
||||
local ddestroy = vgui.Create("DButton", dright)
|
||||
ddestroy:SetPos(m, right_h - m - bh)
|
||||
ddestroy:SetSize(bw, bh)
|
||||
ddestroy:CenterHorizontal()
|
||||
ddestroy:SetText(T("c4_remove_destroy1"))
|
||||
ddestroy:SetDisabled(true)
|
||||
ddestroy.Confirmed = false
|
||||
ddestroy.DoClick = function(s)
|
||||
if not LocalPlayer():Alive() then return end
|
||||
|
||||
if not s.Confirmed then
|
||||
s:SetText(T("c4_remove_destroy2"))
|
||||
s.Confirmed = true
|
||||
else
|
||||
RunConsoleCommand("ttt_c4_destroy", bomb:EntIndex())
|
||||
dframe:Close()
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local desc_h = 45
|
||||
|
||||
local ddesc = vgui.Create("DLabel", dleft)
|
||||
ddesc:SetBright(true)
|
||||
ddesc:SetFont("DermaDefaultBold")
|
||||
ddesc:SetSize(256, desc_h)
|
||||
ddesc:SetWrap(true)
|
||||
if LocalPlayer():IsTraitor() then
|
||||
ddesc:SetText(T("c4_disarm_t"))
|
||||
elseif LocalPlayer() == bomb:GetOwner() then
|
||||
ddesc:SetText(T("c4_disarm_owned"))
|
||||
else
|
||||
ddesc:SetText(T("c4_disarm_other"))
|
||||
end
|
||||
ddesc:SetPos(m, m)
|
||||
|
||||
local bg = vgui.Create("ColoredBox", dleft)
|
||||
bg:StretchToParent(m,m + desc_h,m,m)
|
||||
bg:SetColor(Color(20,20,20, 255))
|
||||
|
||||
local dbomb = vgui.Create("DisarmPanel", bg)
|
||||
dbomb:SetSize(256, 256)
|
||||
dbomb:Center()
|
||||
|
||||
|
||||
local dcancel = vgui.Create("DButton", dframe)
|
||||
dcancel:SetPos( w - bw - m, h - bh - m)
|
||||
dcancel:SetSize(bw, bh)
|
||||
dcancel:CenterHorizontal()
|
||||
dcancel:SetText(T("close"))
|
||||
dcancel.DoClick = function()
|
||||
dframe:Close()
|
||||
end
|
||||
|
||||
dframe:MakePopup()
|
||||
|
||||
disarm_success = function()
|
||||
surface.PlaySound(disarm_beep)
|
||||
dtimer.Stop = true
|
||||
|
||||
dtimer:SetTextColor(COLOR_GREEN)
|
||||
|
||||
dstatus:SetTextColor(COLOR_GREEN)
|
||||
dstatus:SetText(T("c4_status_disarmed"))
|
||||
dstatus:SizeToContents()
|
||||
dstatus:CenterHorizontal()
|
||||
|
||||
ddestroy:SetDisabled(false)
|
||||
dgrab:SetDisabled(false)
|
||||
end
|
||||
|
||||
disarm_fail = function()
|
||||
dframe:Close()
|
||||
end
|
||||
|
||||
on_wire_cut = function(idx)
|
||||
if IsValid(dbomb) then
|
||||
dbomb:SetDisabled(true)
|
||||
-- disabled lowers alpha, looks weird here so work around
|
||||
-- that
|
||||
dbomb:SetAlpha(255)
|
||||
end
|
||||
|
||||
if IsValid(bomb) then
|
||||
RunConsoleCommand("ttt_c4_disarm", tostring(bomb:EntIndex()), tostring(idx))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
---- Communication
|
||||
|
||||
local function C4ConfigHook()
|
||||
local bomb = net.ReadEntity()
|
||||
|
||||
if IsValid(bomb) then
|
||||
if not bomb:GetArmed() then
|
||||
ShowC4Config(bomb)
|
||||
else
|
||||
ShowC4Disarm(bomb)
|
||||
end
|
||||
end
|
||||
end
|
||||
net.Receive("TTT_C4Config", C4ConfigHook)
|
||||
|
||||
local function C4DisarmResultHook()
|
||||
local bomb = net.ReadEntity()
|
||||
local correct = net.ReadBit() == 1
|
||||
|
||||
if IsValid(bomb) then
|
||||
if correct and disarm_success then
|
||||
disarm_success()
|
||||
elseif disarm_fail then
|
||||
disarm_fail()
|
||||
end
|
||||
end
|
||||
end
|
||||
net.Receive("TTT_C4DisarmResult", C4DisarmResultHook)
|
||||
634
gamemodes/terrortown/entities/entities/ttt_c4/shared.lua
Normal file
634
gamemodes/terrortown/entities/entities/ttt_c4/shared.lua
Normal file
@@ -0,0 +1,634 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
-- c4 explosive
|
||||
|
||||
local math = math
|
||||
|
||||
if SERVER then
|
||||
AddCSLuaFile("cl_init.lua")
|
||||
AddCSLuaFile("shared.lua")
|
||||
end
|
||||
|
||||
if CLIENT then
|
||||
-- this entity can be DNA-sampled so we need some display info
|
||||
ENT.Icon = "vgui/ttt/icon_c4"
|
||||
ENT.PrintName = "C4"
|
||||
|
||||
local GetPTranslation = LANG.GetParamTranslation
|
||||
local hint_params = {usekey = Key("+use", "USE")}
|
||||
|
||||
ENT.TargetIDHint = {
|
||||
name = "C4",
|
||||
hint = "c4_hint",
|
||||
fmt = function(ent, txt) return GetPTranslation(txt, hint_params) end
|
||||
};
|
||||
end
|
||||
|
||||
C4_WIRE_COUNT = 6
|
||||
C4_MINIMUM_TIME = 45
|
||||
C4_MAXIMUM_TIME = 600
|
||||
|
||||
ENT.Type = "anim"
|
||||
ENT.Model = Model("models/weapons/w_c4_planted.mdl")
|
||||
|
||||
ENT.CanHavePrints = true
|
||||
ENT.CanUseKey = true
|
||||
ENT.Avoidable = true
|
||||
|
||||
AccessorFunc( ENT, "thrower", "Thrower")
|
||||
|
||||
AccessorFunc( ENT, "radius", "Radius", FORCE_NUMBER )
|
||||
AccessorFunc( ENT, "dmg", "Dmg", FORCE_NUMBER )
|
||||
|
||||
AccessorFunc( ENT, "arm_time", "ArmTime", FORCE_NUMBER)
|
||||
AccessorFunc( ENT, "timer_length", "TimerLength", FORCE_NUMBER)
|
||||
|
||||
-- Generate accessors for DT vars. This way all consumer code can keep accessing
|
||||
-- the vars as they always did, the only difference is that behind the scenes
|
||||
-- they are set up as DT vars.
|
||||
AccessorFuncDT(ENT, "explode_time", "ExplodeTime")
|
||||
AccessorFuncDT(ENT, "armed", "Armed")
|
||||
|
||||
ENT.Beep = 0
|
||||
ENT.DetectiveNearRadius = 300
|
||||
ENT.SafeWires = nil
|
||||
|
||||
function ENT:SetupDataTables()
|
||||
self:DTVar("Int", 0, "explode_time")
|
||||
self:DTVar("Bool", 0, "armed")
|
||||
end
|
||||
|
||||
function ENT:Initialize()
|
||||
self:SetModel(self.Model)
|
||||
|
||||
if SERVER then
|
||||
self:PhysicsInit(SOLID_VPHYSICS)
|
||||
end
|
||||
self:SetMoveType(MOVETYPE_VPHYSICS)
|
||||
self:SetSolid(SOLID_BBOX)
|
||||
self:SetCollisionGroup(COLLISION_GROUP_WEAPON)
|
||||
|
||||
if SERVER then
|
||||
self:SetUseType(SIMPLE_USE)
|
||||
end
|
||||
|
||||
self.SafeWires = nil
|
||||
self.Beep = 0
|
||||
self.DisarmCausedExplosion = false
|
||||
|
||||
self:SetTimerLength(0)
|
||||
self:SetExplodeTime(0)
|
||||
self:SetArmed(false)
|
||||
if not self:GetThrower() then self:SetThrower(nil) end
|
||||
|
||||
if not self:GetRadius() then self:SetRadius(1000) end
|
||||
if not self:GetDmg() then self:SetDmg(200) end
|
||||
|
||||
end
|
||||
|
||||
function ENT:SetDetonateTimer(length)
|
||||
self:SetTimerLength(length)
|
||||
self:SetExplodeTime( CurTime() + length )
|
||||
end
|
||||
|
||||
|
||||
function ENT:UseOverride(activator)
|
||||
if IsValid(activator) and activator:IsPlayer() then
|
||||
-- Traitors not allowed to disarm other traitor's C4 until he is dead
|
||||
local owner = self:GetOwner()
|
||||
if self:GetArmed() and owner != activator and activator:GetTraitor() and (IsValid(owner) and owner:Alive() and owner:GetTraitor()) then
|
||||
LANG.Msg(activator, "c4_no_disarm")
|
||||
return
|
||||
end
|
||||
|
||||
self:ShowC4Config(activator)
|
||||
end
|
||||
end
|
||||
|
||||
function ENT.SafeWiresForTime(t)
|
||||
local m = t / 60
|
||||
|
||||
if m > 4 then return 1
|
||||
elseif m > 3 then return 2
|
||||
elseif m > 2 then return 3
|
||||
elseif m > 1 then return 4
|
||||
else return 5
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:WeldToGround(state)
|
||||
if self.IsOnWall then return end
|
||||
|
||||
if state then
|
||||
-- getgroundentity does not work for non-players
|
||||
-- so sweep ent downward to find what we're lying on
|
||||
local ignore = player.GetAll()
|
||||
table.insert(ignore, self)
|
||||
|
||||
local tr = util.TraceEntity({start=self:GetPos(), endpos=self:GetPos() - Vector(0,0,16), filter=ignore, mask=MASK_SOLID}, self)
|
||||
|
||||
-- Start by increasing weight/making uncarryable
|
||||
local phys = self:GetPhysicsObject()
|
||||
if IsValid(phys) then
|
||||
-- Could just use a pickup flag for this. However, then it's easier to
|
||||
-- push it around.
|
||||
self.OrigMass = phys:GetMass()
|
||||
phys:SetMass(150)
|
||||
end
|
||||
|
||||
if tr.Hit and (IsValid(tr.Entity) or tr.HitWorld) then
|
||||
-- "Attach" to a brush if possible
|
||||
if IsValid(phys) and tr.HitWorld then
|
||||
phys:EnableMotion(false)
|
||||
end
|
||||
|
||||
-- Else weld to objects we cannot pick up
|
||||
local entphys = tr.Entity:GetPhysicsObject()
|
||||
if IsValid(entphys) and entphys:GetMass() > CARRY_WEIGHT_LIMIT then
|
||||
constraint.Weld(self, tr.Entity, 0, 0, 0, true)
|
||||
end
|
||||
|
||||
-- Worst case, we are still uncarryable
|
||||
end
|
||||
else
|
||||
constraint.RemoveConstraints(self, "Weld")
|
||||
local phys = self:GetPhysicsObject()
|
||||
if IsValid(phys) then
|
||||
phys:EnableMotion(true)
|
||||
phys:SetMass(self.OrigMass or 10)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:SphereDamage(dmgowner, center, radius)
|
||||
-- It seems intuitive to use FindInSphere here, but that will find all ents
|
||||
-- in the radius, whereas there exist only ~16 players. Hence it is more
|
||||
-- efficient to cycle through all those players and do a Lua-side distance
|
||||
-- check.
|
||||
|
||||
local r = radius ^ 2 -- square so we can compare with dot product directly
|
||||
|
||||
|
||||
-- pre-declare to avoid realloc
|
||||
local d = 0.0
|
||||
local diff = nil
|
||||
local dmg = 0
|
||||
for _, ent in player.Iterator() do
|
||||
if IsValid(ent) and ent:Team() == TEAM_TERROR then
|
||||
|
||||
-- dot of the difference with itself is distance squared
|
||||
diff = center - ent:GetPos()
|
||||
d = diff:Dot(diff)
|
||||
|
||||
if d < r then
|
||||
-- deadly up to a certain range, then a quick falloff within 100 units
|
||||
d = math.max(0, math.sqrt(d) - 490)
|
||||
dmg = -0.01 * (d^2) + 125
|
||||
|
||||
local dmginfo = DamageInfo()
|
||||
dmginfo:SetDamage(dmg)
|
||||
dmginfo:SetAttacker(dmgowner)
|
||||
dmginfo:SetInflictor(self)
|
||||
dmginfo:SetDamageType(DMG_BLAST)
|
||||
dmginfo:SetDamageForce(center - ent:GetPos())
|
||||
dmginfo:SetDamagePosition(ent:GetPos())
|
||||
|
||||
ent:TakeDamageInfo(dmginfo)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local c4boom = Sound("c4.explode")
|
||||
function ENT:Explode(tr)
|
||||
hook.Call("TTTC4Explode", nil, self)
|
||||
if SERVER then
|
||||
self:SetNoDraw(true)
|
||||
self:SetSolid(SOLID_NONE)
|
||||
|
||||
-- pull out of the surface
|
||||
if tr.Fraction != 1.0 then
|
||||
self:SetPos(tr.HitPos + tr.HitNormal * 0.6)
|
||||
end
|
||||
|
||||
local pos = self:GetPos()
|
||||
if util.PointContents(pos) == CONTENTS_WATER or GetRoundState() != ROUND_ACTIVE then
|
||||
self:Remove()
|
||||
self:SetExplodeTime(0)
|
||||
return
|
||||
end
|
||||
|
||||
local dmgowner = self:GetThrower()
|
||||
dmgowner = IsValid(dmgowner) and dmgowner or self
|
||||
|
||||
local r_inner = 750
|
||||
local r_outer = self:GetRadius()
|
||||
|
||||
if self.DisarmCausedExplosion then
|
||||
r_inner = r_inner / 2.5
|
||||
r_outer = r_outer / 2.5
|
||||
end
|
||||
|
||||
-- damage through walls
|
||||
self:SphereDamage(dmgowner, pos, r_inner)
|
||||
|
||||
-- explosion damage
|
||||
util.BlastDamage(self, dmgowner, pos, r_outer, self:GetDmg())
|
||||
|
||||
local effect = EffectData()
|
||||
effect:SetStart(pos)
|
||||
effect:SetOrigin(pos)
|
||||
-- these don't have much effect with the default Explosion
|
||||
effect:SetScale(r_outer)
|
||||
effect:SetRadius(r_outer)
|
||||
effect:SetMagnitude(self:GetDmg())
|
||||
|
||||
if tr.Fraction != 1.0 then
|
||||
effect:SetNormal(tr.HitNormal)
|
||||
end
|
||||
|
||||
effect:SetOrigin(pos)
|
||||
util.Effect("Explosion", effect, true, true)
|
||||
util.Effect("HelicopterMegaBomb", effect, true, true)
|
||||
|
||||
timer.Simple(0.1, function() sound.Play(c4boom, pos, 100, 100) end)
|
||||
|
||||
-- extra push
|
||||
local phexp = ents.Create("env_physexplosion")
|
||||
phexp:SetPos(pos)
|
||||
phexp:SetKeyValue("magnitude", self:GetDmg())
|
||||
phexp:SetKeyValue("radius", r_outer)
|
||||
phexp:SetKeyValue("spawnflags", "19")
|
||||
phexp:Spawn()
|
||||
phexp:Fire("Explode", "", 0)
|
||||
|
||||
|
||||
-- few fire bits to ignite things
|
||||
timer.Simple(0.2, function() StartFires(pos, tr, 4, 5, true, dmgowner) end)
|
||||
|
||||
self:SetExplodeTime(0)
|
||||
|
||||
SCORE:HandleC4Explosion(dmgowner, self:GetArmTime(), CurTime())
|
||||
|
||||
self:Remove()
|
||||
else
|
||||
local spos = self:GetPos()
|
||||
local trs = util.TraceLine({start=spos + Vector(0,0,64), endpos=spos + Vector(0,0,-128), filter=self})
|
||||
util.Decal("Scorch", trs.HitPos + trs.HitNormal, trs.HitPos - trs.HitNormal)
|
||||
|
||||
self:SetExplodeTime(0)
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:IsDetectiveNear()
|
||||
local center = self:GetPos()
|
||||
local r = self.DetectiveNearRadius ^ 2
|
||||
local d = 0.0
|
||||
local diff = nil
|
||||
for _, ent in player.Iterator() do
|
||||
if IsValid(ent) and ent:IsActiveDetective() then
|
||||
-- dot of the difference with itself is distance squared
|
||||
diff = center - ent:GetPos()
|
||||
d = diff:Dot(diff)
|
||||
|
||||
if d < r then
|
||||
if ent:HasWeapon("weapon_ttt_defuser") then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
local beep = Sound("weapons/c4/c4_beep1.wav")
|
||||
local MAX_MOVE_RANGE = 1000000 -- sq of 1000
|
||||
function ENT:Think()
|
||||
if not self:GetArmed() then return end
|
||||
|
||||
if SERVER then
|
||||
local curpos = self:GetPos()
|
||||
if self.LastPos and self.LastPos:DistToSqr(curpos) > MAX_MOVE_RANGE then
|
||||
self:Disarm(nil)
|
||||
return
|
||||
end
|
||||
self.LastPos = curpos
|
||||
end
|
||||
|
||||
local etime = self:GetExplodeTime()
|
||||
if self:GetArmed() and etime != 0 and etime < CurTime() then
|
||||
-- find the ground if it's near and pass it to the explosion
|
||||
local spos = self:GetPos()
|
||||
local tr = util.TraceLine({start=spos, endpos=spos + Vector(0,0,-32), mask=MASK_SHOT_HULL, filter=self:GetThrower()})
|
||||
|
||||
local success, err = pcall(self.Explode, self, tr)
|
||||
if not success then
|
||||
-- prevent effect spam on Lua error
|
||||
self:Remove()
|
||||
|
||||
ErrorNoHalt("ERROR CAUGHT: ttt_c4: " .. err .. "\n")
|
||||
end
|
||||
elseif self:GetArmed() and CurTime() > self.Beep then
|
||||
local amp = 48
|
||||
|
||||
if self:IsDetectiveNear() then
|
||||
amp = 65
|
||||
|
||||
local dlight = CLIENT and DynamicLight(self:EntIndex())
|
||||
if dlight then
|
||||
dlight.Pos = self:GetPos()
|
||||
dlight.r = 255
|
||||
dlight.g = 0
|
||||
dlight.b = 0
|
||||
dlight.Brightness = 1
|
||||
dlight.Size = 128
|
||||
dlight.Decay = 500
|
||||
dlight.DieTime = CurTime() + 0.1
|
||||
end
|
||||
|
||||
elseif SERVER then
|
||||
-- volume lower for long fuse times, bottoms at 50 at +5mins
|
||||
amp = amp + math.max(0, 12 - (0.03 * self:GetTimerLength()))
|
||||
end
|
||||
|
||||
if SERVER then
|
||||
sound.Play(beep, self:GetPos(), amp, 100)
|
||||
end
|
||||
|
||||
local btime = (etime - CurTime()) / 30
|
||||
self.Beep = CurTime() + btime
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:Defusable()
|
||||
return self:GetArmed()
|
||||
end
|
||||
|
||||
-- Timer configuration handlign
|
||||
|
||||
if SERVER then
|
||||
-- Inform traitors about us
|
||||
function ENT:SendWarn(armed)
|
||||
net.Start("TTT_C4Warn")
|
||||
net.WriteUInt(self:EntIndex(), 16)
|
||||
net.WriteBit(armed)
|
||||
if armed then
|
||||
net.WriteVector(self:GetPos())
|
||||
net.WriteFloat(self:GetExplodeTime())
|
||||
end
|
||||
net.Send(GetTraitorFilter(true))
|
||||
end
|
||||
|
||||
function ENT:OnRemove()
|
||||
self:SendWarn(false)
|
||||
end
|
||||
|
||||
function ENT:Disarm(ply)
|
||||
local owner = self:GetOwner()
|
||||
|
||||
SCORE:HandleC4Disarm(ply, owner, true)
|
||||
|
||||
if ply != owner and IsValid(owner) then
|
||||
LANG.Msg(owner, "c4_disarm_warn")
|
||||
end
|
||||
|
||||
self:SetExplodeTime(0)
|
||||
self:SetArmed(false)
|
||||
self:WeldToGround(false)
|
||||
self:SendWarn(false)
|
||||
|
||||
self.DisarmCausedExplosion = false
|
||||
end
|
||||
|
||||
function ENT:FailedDisarm(ply)
|
||||
self.DisarmCausedExplosion = true
|
||||
|
||||
SCORE:HandleC4Disarm(ply, self:GetOwner(), false)
|
||||
|
||||
-- tiny moment of zen and realization before the bang
|
||||
self:SetExplodeTime(CurTime() + 0.1)
|
||||
end
|
||||
|
||||
function ENT:Arm(ply, time)
|
||||
|
||||
-- Initialize armed state
|
||||
self:SetDetonateTimer(time)
|
||||
self:SetArmTime(CurTime())
|
||||
|
||||
self:SetArmed(true)
|
||||
self:WeldToGround(true)
|
||||
self.DisarmCausedExplosion = false
|
||||
|
||||
-- ply may be a different player than he who dropped us.
|
||||
-- Arming player should be the damage owner = "thrower"
|
||||
self:SetThrower(ply)
|
||||
-- Owner determines who gets messages and can quick-disarm if traitor,
|
||||
-- make that the armer as well for now. Theoretically the dropping player
|
||||
-- should also be able to quick-disarm, but that's going to be rare.
|
||||
self:SetOwner(ply)
|
||||
|
||||
-- Wire stuff:
|
||||
|
||||
self.SafeWires = {}
|
||||
|
||||
-- list of possible wires to make safe
|
||||
local choices = {}
|
||||
for i=1, C4_WIRE_COUNT do
|
||||
table.insert(choices, i)
|
||||
end
|
||||
|
||||
-- random selection process, lot like traitor selection
|
||||
local safe_count = self.SafeWiresForTime(time)
|
||||
local safes = {}
|
||||
local picked = 0
|
||||
while picked < safe_count do
|
||||
local pick = math.random(1, #choices)
|
||||
local w = choices[pick]
|
||||
|
||||
if not self.SafeWires[w] then
|
||||
self.SafeWires[w] = true
|
||||
table.remove(choices, pick)
|
||||
|
||||
-- owner will end up having the last safe wire on his corpse
|
||||
ply.bomb_wire = w
|
||||
|
||||
picked = picked + 1
|
||||
end
|
||||
end
|
||||
|
||||
-- send indicator to traitors
|
||||
self:SendWarn(true)
|
||||
end
|
||||
|
||||
function ENT:ShowC4Config(ply)
|
||||
-- show menu to player to configure or disarm us
|
||||
net.Start("TTT_C4Config")
|
||||
net.WriteEntity(self)
|
||||
net.Send(ply)
|
||||
end
|
||||
|
||||
local function ReceiveC4Config(ply, cmd, args)
|
||||
if not (IsValid(ply) and ply:IsTerror() and #args == 2) then return end
|
||||
local idx = tonumber(args[1])
|
||||
local time = tonumber(args[2])
|
||||
|
||||
if not idx or not time then return end
|
||||
|
||||
local bomb = ents.GetByIndex(idx)
|
||||
if IsValid(bomb) and bomb:GetClass() == "ttt_c4" and (not bomb:GetArmed()) then
|
||||
|
||||
if bomb:GetPos():Distance(ply:GetPos()) > 256 then
|
||||
-- These cases should never arise in normal play, so no messages
|
||||
return
|
||||
elseif time < C4_MINIMUM_TIME or time > C4_MAXIMUM_TIME then
|
||||
return
|
||||
elseif IsValid(bomb:GetPhysicsObject()) and bomb:GetPhysicsObject():HasGameFlag(FVPHYSICS_PLAYER_HELD) then
|
||||
return
|
||||
else
|
||||
LANG.Msg(ply, "c4_armed")
|
||||
|
||||
bomb:Arm(ply, time)
|
||||
hook.Call("TTTC4Arm", nil, bomb, ply)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
concommand.Add("ttt_c4_config", ReceiveC4Config)
|
||||
|
||||
local function SendDisarmResult(ply, bomb, result)
|
||||
hook.Call("TTTC4Disarm", nil, bomb, result, ply)
|
||||
|
||||
net.Start("TTT_C4DisarmResult")
|
||||
net.WriteEntity(bomb)
|
||||
net.WriteBit(result) -- this way we can squeeze this bit into 16
|
||||
net.Send(ply)
|
||||
end
|
||||
|
||||
local function ReceiveC4Disarm(ply, cmd, args)
|
||||
if not (IsValid(ply) and ply:IsTerror() and #args == 2) then return end
|
||||
local idx = tonumber(args[1])
|
||||
local wire = tonumber(args[2])
|
||||
|
||||
if not idx or not wire then return end
|
||||
|
||||
local bomb = ents.GetByIndex(idx)
|
||||
if IsValid(bomb) and bomb:GetClass() == "ttt_c4" and not bomb.DisarmCausedExplosion and bomb:GetArmed() then
|
||||
if bomb:GetPos():Distance(ply:GetPos()) > 256 then
|
||||
return
|
||||
elseif bomb.SafeWires[wire] or ply:IsTraitor() or ply == bomb:GetOwner() then
|
||||
LANG.Msg(ply, "c4_disarmed")
|
||||
|
||||
bomb:Disarm(ply)
|
||||
|
||||
-- only case with success net message
|
||||
SendDisarmResult(ply, bomb, true)
|
||||
else
|
||||
SendDisarmResult(ply, bomb, false)
|
||||
|
||||
-- wrong wire = bomb goes boom
|
||||
bomb:FailedDisarm(ply)
|
||||
end
|
||||
end
|
||||
end
|
||||
concommand.Add("ttt_c4_disarm", ReceiveC4Disarm)
|
||||
|
||||
|
||||
local function ReceiveC4Pickup(ply, cmd, args)
|
||||
if not (IsValid(ply) and ply:IsTerror() and #args == 1) then return end
|
||||
local idx = tonumber(args[1])
|
||||
|
||||
if not idx then return end
|
||||
|
||||
local bomb = ents.GetByIndex(idx)
|
||||
if IsValid(bomb) and bomb:GetClass() == "ttt_c4" and (not bomb:GetArmed()) then
|
||||
if bomb:GetPos():Distance(ply:GetPos()) > 256 then
|
||||
return
|
||||
elseif not ply:CanCarryType(WEAPON_EQUIP1) then
|
||||
LANG.Msg(ply, "c4_no_room")
|
||||
else
|
||||
local prints = bomb.fingerprints or {}
|
||||
|
||||
hook.Call("TTTC4Pickup", nil, bomb, ply)
|
||||
|
||||
local wep = ply:Give("weapon_ttt_c4")
|
||||
if IsValid(wep) then
|
||||
wep.fingerprints = wep.fingerprints or {}
|
||||
table.Add(wep.fingerprints, prints)
|
||||
|
||||
bomb:Remove()
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
concommand.Add("ttt_c4_pickup", ReceiveC4Pickup)
|
||||
|
||||
|
||||
local function ReceiveC4Destroy(ply, cmd, args)
|
||||
if not (IsValid(ply) and ply:IsTerror() and #args == 1) then return end
|
||||
local idx = tonumber(args[1])
|
||||
|
||||
if not idx then return end
|
||||
|
||||
local bomb = ents.GetByIndex(idx)
|
||||
if IsValid(bomb) and bomb:GetClass() == "ttt_c4" and (not bomb:GetArmed()) then
|
||||
if bomb:GetPos():Distance(ply:GetPos()) > 256 then
|
||||
return
|
||||
else
|
||||
-- spark to show onlookers we destroyed this bomb
|
||||
util.EquipmentDestroyed(bomb:GetPos())
|
||||
hook.Call("TTTC4Destroyed", nil, bomb, ply)
|
||||
|
||||
bomb:Remove()
|
||||
end
|
||||
end
|
||||
end
|
||||
concommand.Add("ttt_c4_destroy", ReceiveC4Destroy)
|
||||
end
|
||||
|
||||
if CLIENT then
|
||||
surface.CreateFont("C4ModelTimer", {
|
||||
font = "Default",
|
||||
size = 13,
|
||||
weight = 0,
|
||||
antialias = false
|
||||
})
|
||||
|
||||
|
||||
function ENT:GetTimerPos()
|
||||
local att = self:GetAttachment(self:LookupAttachment("controlpanel0_ur"))
|
||||
if att then
|
||||
return att
|
||||
else
|
||||
local ang = self:GetAngles()
|
||||
ang:RotateAroundAxis(self:GetUp(), -90)
|
||||
local pos = (self:GetPos() + self:GetForward() * 4.5 +
|
||||
self:GetUp() * 9.0 + self:GetRight() * 7.8)
|
||||
return { Pos = pos, Ang = ang }
|
||||
end
|
||||
end
|
||||
|
||||
local strtime = util.SimpleTime
|
||||
local max = math.max
|
||||
function ENT:Draw()
|
||||
self:DrawModel()
|
||||
|
||||
if self:GetArmed() then
|
||||
local angpos_ur = self:GetTimerPos()
|
||||
if angpos_ur then
|
||||
cam.Start3D2D(angpos_ur.Pos, angpos_ur.Ang, 0.2)
|
||||
draw.DrawText(strtime(max(0, self:GetExplodeTime() - CurTime()), "%02i:%02i"), "C4ModelTimer", -1, 1, COLOR_RED, TEXT_ALIGN_RIGHT)
|
||||
cam.End3D2D()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
163
gamemodes/terrortown/entities/entities/ttt_carry_handler.lua
Normal file
163
gamemodes/terrortown/entities/entities/ttt_carry_handler.lua
Normal file
@@ -0,0 +1,163 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
|
||||
AddCSLuaFile()
|
||||
|
||||
ENT.Type = "anim"
|
||||
|
||||
ENT.Carried = nil
|
||||
ENT.CarriedMass = 0
|
||||
ENT.PrevThink = 0
|
||||
ENT.TargetPos = Vector(0,0,0)
|
||||
ENT.TargetAng = Angle(0,0,0)
|
||||
ENT.Owner = nil
|
||||
|
||||
function ENT:Initialize()
|
||||
if SERVER and IsValid(self.Carried) then
|
||||
|
||||
-- self:SetModel("models/weapons/w_bugbait.mdl")
|
||||
self:SetModel(self.Carried:GetModel())
|
||||
-- self:SetSkin(self.Carried:GetSkin())
|
||||
-- self:SetColor(se.Carried:GetColor())
|
||||
end
|
||||
self:PhysicsInit( SOLID_VPHYSICS )
|
||||
self:SetMoveType( MOVETYPE_VPHYSICS )
|
||||
self:SetSolid( SOLID_VPHYSICS )
|
||||
self:SetCollisionGroup(COLLISION_GROUP_NONE)
|
||||
-- self:SetSolid(SOLID_NONE)
|
||||
self:SetNoDraw(true)
|
||||
|
||||
-- self:SetHealth(9999)
|
||||
|
||||
|
||||
|
||||
-- local ply = self:GetOwner()
|
||||
-- self.Owner = ply
|
||||
-- if IsValid(ply) then
|
||||
-- self.TargetPos = ply:GetShootPos() + (ply:GetAimVector() * 70)
|
||||
-- self.TargetAng = ply:GetAimVector()
|
||||
-- end
|
||||
|
||||
|
||||
if SERVER and IsValid(self.Carried) then
|
||||
|
||||
local phys = self:GetPhysicsObject()
|
||||
local carphys = self.Carried:GetPhysicsObject()
|
||||
|
||||
if IsValid(phys) and IsValid(carphys) then
|
||||
phys:Wake()
|
||||
carphys:Wake()
|
||||
|
||||
phys:SetMass(9999)
|
||||
|
||||
phys:SetDamping(0, 1000)
|
||||
carphys:SetDamping(0, 1000)
|
||||
|
||||
-- if not carphys:IsPenetrating() then
|
||||
-- phys:SetPos(carphys:GetPos())
|
||||
-- phys:SetAngle(carphys:GetAngle())
|
||||
-- carphys:SetPos( phys:GetPos() )
|
||||
-- carphys:SetAngle( phys:GetAngle() )
|
||||
-- end
|
||||
|
||||
end
|
||||
|
||||
self.Carried:SetGravity(false)
|
||||
self.Carried:SetOwner(self:GetOwner())
|
||||
-- self.Carried:SetNoDraw(true)
|
||||
-- self.Carried:SetSolid(SOLID_NONE)
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:OnRemove()
|
||||
if IsValid(self.Carried) then
|
||||
self.Carried:SetGravity(true)
|
||||
self.Carried:SetOwner(nil)
|
||||
-- self.Carried:SetNoDraw(false)
|
||||
-- self.Carried:SetSolid(SOLID_VPHYSICS)
|
||||
self.Carried:SetMoveType(MOVETYPE_VPHYSICS)
|
||||
|
||||
local carphys = self.Carried:GetPhysicsObject()
|
||||
if IsValid(carphys) then
|
||||
carphys:SetDamping(0,0)
|
||||
end
|
||||
|
||||
self.Carried:PhysWake()
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--function ENT:Think()
|
||||
-- if CLIENT then return end
|
||||
--
|
||||
-- -- Check on all entities involved
|
||||
--
|
||||
-- local obj = self.Carried
|
||||
-- local ply = self:GetOwner()
|
||||
-- if not IsValid(obj) or not IsValid(ply) or not ply:Alive() then
|
||||
-- self:Remove()
|
||||
-- return
|
||||
-- end
|
||||
--
|
||||
--
|
||||
--
|
||||
-- -- Check some other requirements
|
||||
-- local spos = ply:GetShootPos()
|
||||
-- if ply:GetGroundEntity() == obj or obj:NearestPoint(spos):Distance(spos) > 150 then
|
||||
-- self:Remove()
|
||||
-- return
|
||||
-- end
|
||||
--
|
||||
--
|
||||
-- self.TargetPos = spos + (ply:GetAimVector() * 70)
|
||||
-- self.TargetAng = ply:GetAimVector()
|
||||
--
|
||||
-- local phys = self:GetPhysicsObject()
|
||||
-- local carryphys = obj:GetPhysicsObject()
|
||||
-- if IsValid(phys) and IsValid(carryphys) then
|
||||
-- if phys:IsPenetrating() then
|
||||
-- self:Remove()
|
||||
-- return
|
||||
---- self.TargetPos = phys:GetPos() + Vector(0,0,5)
|
||||
---- phys:SetPos(self.TargetPos)
|
||||
-- end
|
||||
--
|
||||
-- carryphys:SetPos(phys:GetPos())
|
||||
-- carryphys:SetAngle(phys:GetAngles())
|
||||
-- carryphys:SetVelocity(phys:GetVelocity())
|
||||
-- end
|
||||
--
|
||||
--end
|
||||
|
||||
|
||||
--function ENT:PhysicsSimulate(phys, delta)
|
||||
-- phys:Wake()
|
||||
--
|
||||
-- local p = {}
|
||||
-- p.pos = self.TargetPos
|
||||
-- p.angle = self.TargetAng
|
||||
-- p.secondstoarrive = 0.05
|
||||
-- p.maxangular = 100
|
||||
-- p.maxangulardamp = 10000
|
||||
-- p.maxspeed = 100
|
||||
-- p.maxspeeddamp = 1000
|
||||
-- p.dampfactor = 0.8
|
||||
-- p.teleportdistance = 0
|
||||
-- p.deltatime = delta
|
||||
--
|
||||
-- phys:ComputeShadowControl(p)
|
||||
--end
|
||||
|
||||
|
||||
function ENT:OnTakeDamage(dmg)
|
||||
-- do nothing
|
||||
end
|
||||
109
gamemodes/terrortown/entities/entities/ttt_confgrenade_proj.lua
Normal file
109
gamemodes/terrortown/entities/entities/ttt_confgrenade_proj.lua
Normal file
@@ -0,0 +1,109 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
|
||||
AddCSLuaFile()
|
||||
|
||||
ENT.Type = "anim"
|
||||
ENT.Base = "ttt_basegrenade_proj"
|
||||
ENT.Model = Model("models/weapons/w_eq_fraggrenade_thrown.mdl")
|
||||
|
||||
local ttt_allow_jump = CreateConVar("ttt_allow_discomb_jump", "0")
|
||||
|
||||
local function PushPullRadius(pos, pusher)
|
||||
local radius = 400
|
||||
local phys_force = 1500
|
||||
local push_force = 256
|
||||
|
||||
-- pull physics objects and push players
|
||||
for k, target in ipairs(ents.FindInSphere(pos, radius)) do
|
||||
if IsValid(target) then
|
||||
local tpos = target:LocalToWorld(target:OBBCenter())
|
||||
local dir = (tpos - pos):GetNormal()
|
||||
local phys = target:GetPhysicsObject()
|
||||
|
||||
if target:IsPlayer() and (not target:IsFrozen()) and ((not target.was_pushed) or target.was_pushed.t != CurTime()) then
|
||||
|
||||
-- always need an upwards push to prevent the ground's friction from
|
||||
-- stopping nearly all movement
|
||||
dir.z = math.abs(dir.z) + 1
|
||||
|
||||
local push = dir * push_force
|
||||
|
||||
-- try to prevent excessive upwards force
|
||||
local vel = target:GetVelocity() + push
|
||||
vel.z = math.min(vel.z, push_force)
|
||||
|
||||
-- mess with discomb jumps
|
||||
if pusher == target and (not ttt_allow_jump:GetBool()) then
|
||||
vel = VectorRand() * vel:Length()
|
||||
vel.z = math.abs(vel.z)
|
||||
end
|
||||
|
||||
target:SetVelocity(vel)
|
||||
|
||||
target.was_pushed = {att=pusher, t=CurTime(), wep="weapon_ttt_confgrenade"}
|
||||
|
||||
elseif IsValid(phys) then
|
||||
phys:ApplyForceCenter(dir * -1 * phys_force)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local phexp = ents.Create("env_physexplosion")
|
||||
if IsValid(phexp) then
|
||||
phexp:SetPos(pos)
|
||||
phexp:SetKeyValue("magnitude", 100) --max
|
||||
phexp:SetKeyValue("radius", radius)
|
||||
-- 1 = no dmg, 2 = push ply, 4 = push radial, 8 = los, 16 = viewpunch
|
||||
phexp:SetKeyValue("spawnflags", 1 + 2 + 16)
|
||||
phexp:Spawn()
|
||||
phexp:Fire("Explode", "", 0.2)
|
||||
end
|
||||
end
|
||||
|
||||
local zapsound = Sound("npc/assassin/ball_zap1.wav")
|
||||
function ENT:Explode(tr)
|
||||
if SERVER then
|
||||
self:SetNoDraw(true)
|
||||
self:SetSolid(SOLID_NONE)
|
||||
|
||||
-- pull out of the surface
|
||||
if tr.Fraction != 1.0 then
|
||||
self:SetPos(tr.HitPos + tr.HitNormal * 0.6)
|
||||
end
|
||||
|
||||
local pos = self:GetPos()
|
||||
|
||||
-- make sure we are removed, even if errors occur later
|
||||
self:Remove()
|
||||
|
||||
PushPullRadius(pos, self:GetThrower())
|
||||
|
||||
local effect = EffectData()
|
||||
effect:SetStart(pos)
|
||||
effect:SetOrigin(pos)
|
||||
|
||||
if tr.Fraction != 1.0 then
|
||||
effect:SetNormal(tr.HitNormal)
|
||||
end
|
||||
|
||||
util.Effect("Explosion", effect, true, true)
|
||||
util.Effect("cball_explode", effect, true, true)
|
||||
|
||||
sound.Play(zapsound, pos, 100, 100)
|
||||
else
|
||||
local spos = self:GetPos()
|
||||
local trs = util.TraceLine({start=spos + Vector(0,0,64), endpos=spos + Vector(0,0,-128), filter=self})
|
||||
util.Decal("SmallScorch", trs.HitPos + trs.HitNormal, trs.HitPos - trs.HitNormal)
|
||||
|
||||
self:SetDetonateExact(0)
|
||||
end
|
||||
end
|
||||
46
gamemodes/terrortown/entities/entities/ttt_credit_adjust.lua
Normal file
46
gamemodes/terrortown/entities/entities/ttt_credit_adjust.lua
Normal file
@@ -0,0 +1,46 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
|
||||
ENT.Type = "point"
|
||||
ENT.Base = "base_point"
|
||||
|
||||
ENT.Credits = 0
|
||||
|
||||
function ENT:KeyValue(key, value)
|
||||
if key == "OnSuccess" or key == "OnFail" then
|
||||
self:StoreOutput(key, value)
|
||||
elseif key == "credits" then
|
||||
self.Credits = tonumber(value) or 0
|
||||
|
||||
if not tonumber(value) then
|
||||
ErrorNoHalt(tostring(self) .. " has bad 'credits' setting.\n")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function ENT:AcceptInput(name, activator)
|
||||
if name == "TakeCredits" then
|
||||
|
||||
if IsValid(activator) and activator:IsPlayer() then
|
||||
|
||||
if activator:GetCredits() >= self.Credits then
|
||||
activator:SubtractCredits(self.Credits)
|
||||
|
||||
self:TriggerOutput("OnSuccess", activator)
|
||||
else
|
||||
self:TriggerOutput("OnFail", activator)
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
end
|
||||
215
gamemodes/terrortown/entities/entities/ttt_cse_proj.lua
Normal file
215
gamemodes/terrortown/entities/entities/ttt_cse_proj.lua
Normal file
@@ -0,0 +1,215 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
|
||||
AddCSLuaFile()
|
||||
|
||||
if CLIENT then
|
||||
local GetPTranslation = LANG.GetParamTranslation
|
||||
local hint_params = {usekey = Key("+use", "USE")}
|
||||
|
||||
ENT.TargetIDHint = {
|
||||
name = "vis_name",
|
||||
hint = "vis_hint",
|
||||
fmt = function(ent, txt) return GetPTranslation(txt, hint_params) end
|
||||
};
|
||||
|
||||
end
|
||||
|
||||
ENT.Type = "anim"
|
||||
ENT.Base = "ttt_basegrenade_proj"
|
||||
ENT.Model = Model("models/Items/battery.mdl")
|
||||
|
||||
ENT.RenderGroup = RENDERGROUP_BOTH
|
||||
|
||||
ENT.Range = 128
|
||||
ENT.MaxScenesPerPulse = 3
|
||||
ENT.SceneDuration = 10
|
||||
ENT.PulseDelay = 10
|
||||
|
||||
ENT.CanUseKey = true
|
||||
|
||||
function ENT:Initialize()
|
||||
self.BaseClass.Initialize(self)
|
||||
|
||||
self:SetSolid(SOLID_VPHYSICS)
|
||||
|
||||
if SERVER then
|
||||
self:SetMaxHealth(50)
|
||||
self:SetExplodeTime(CurTime() + 1)
|
||||
end
|
||||
|
||||
self:SetHealth(50)
|
||||
end
|
||||
|
||||
function ENT:GetNearbyCorpses()
|
||||
local pos = self:GetPos()
|
||||
|
||||
local near = ents.FindInSphere(pos, self.Range)
|
||||
if not near then return end
|
||||
|
||||
local near_corpses = {}
|
||||
|
||||
local ent = nil
|
||||
for i=1, #near do
|
||||
ent = near[i]
|
||||
if IsValid(ent) and ent.player_ragdoll and ent.scene then
|
||||
table.insert(near_corpses, {ent=ent, dist=pos:LengthSqr()})
|
||||
end
|
||||
end
|
||||
|
||||
return near_corpses
|
||||
end
|
||||
|
||||
local zapsound = Sound("npc/assassin/ball_zap1.wav")
|
||||
function ENT:OnTakeDamage(dmginfo)
|
||||
self:TakePhysicsDamage(dmginfo)
|
||||
|
||||
self:SetHealth(self:Health() - dmginfo:GetDamage())
|
||||
if self:Health() < 0 then
|
||||
self:Remove()
|
||||
|
||||
local effect = EffectData()
|
||||
effect:SetOrigin(self:GetPos())
|
||||
util.Effect("cball_explode", effect)
|
||||
sound.Play(zapsound, self:GetPos())
|
||||
end
|
||||
end
|
||||
|
||||
local dummy_keys = {"victim", "killer"}
|
||||
function ENT:ShowSceneForCorpse(corpse)
|
||||
local scene = corpse.scene
|
||||
local hit = scene.hit_trace
|
||||
local dur = self.SceneDuration
|
||||
|
||||
if hit then
|
||||
-- line showing bullet trajectory
|
||||
local e = EffectData()
|
||||
e:SetEntity(corpse)
|
||||
e:SetStart(hit.StartPos)
|
||||
e:SetOrigin(hit.HitPos)
|
||||
e:SetMagnitude(hit.HitBox)
|
||||
e:SetScale(dur)
|
||||
|
||||
util.Effect("crimescene_shot", e)
|
||||
end
|
||||
|
||||
if not scene then return end
|
||||
|
||||
for _, dummy_key in ipairs(dummy_keys) do
|
||||
local dummy = scene[dummy_key]
|
||||
|
||||
if dummy then
|
||||
-- Horrible sins committed here to get all the data we need over the
|
||||
-- wire, the pose parameters are going to be truncated etc. but
|
||||
-- everything sort of works out. If you know a better way to get this
|
||||
-- much data to an effect, let me know.
|
||||
local e = EffectData()
|
||||
e:SetEntity(corpse)
|
||||
e:SetOrigin(dummy.pos)
|
||||
e:SetAngles(dummy.ang)
|
||||
e:SetColor(dummy.sequence)
|
||||
e:SetScale(dummy.cycle)
|
||||
e:SetStart(Vector(dummy.aim_yaw, dummy.aim_pitch, dummy.move_yaw))
|
||||
e:SetRadius(dur)
|
||||
|
||||
util.Effect("crimescene_dummy", e)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local scanloop = Sound("weapons/gauss/chargeloop.wav")
|
||||
function ENT:StartScanSound()
|
||||
if not self.ScanSound then
|
||||
self.ScanSound = CreateSound(self, scanloop)
|
||||
end
|
||||
|
||||
if not self.ScanSound:IsPlaying() then
|
||||
self.ScanSound:PlayEx(0.5, 100)
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:StopScanSound(force)
|
||||
if self.ScanSound and self.ScanSound:IsPlaying() then
|
||||
self.ScanSound:FadeOut(0.5)
|
||||
end
|
||||
|
||||
if self.ScanSound and force then
|
||||
self.ScanSound:Stop()
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
if CLIENT then
|
||||
local glow = Material("sprites/blueglow2")
|
||||
function ENT:DrawTranslucent()
|
||||
render.SetMaterial(glow)
|
||||
|
||||
render.DrawSprite(self:LocalToWorld(self:OBBCenter()), 32, 32, COLOR_WHITE)
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:UseOverride(activator)
|
||||
if IsValid(activator) and activator:IsPlayer() then
|
||||
if activator:IsActiveDetective() and activator:CanCarryType(WEAPON_EQUIP) then
|
||||
self:StopScanSound(true)
|
||||
self:Remove()
|
||||
|
||||
activator:Give("weapon_ttt_cse")
|
||||
else
|
||||
self:EmitSound("HL2Player.UseDeny")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function ENT:OnRemove()
|
||||
self:StopScanSound(true)
|
||||
end
|
||||
|
||||
function ENT:Explode(tr)
|
||||
if SERVER then
|
||||
|
||||
-- prevent starting effects when round is about to restart
|
||||
if GetRoundState() == ROUND_POST then return end
|
||||
|
||||
self:SetCollisionGroup(COLLISION_GROUP_WEAPON)
|
||||
|
||||
local corpses = self:GetNearbyCorpses()
|
||||
if #corpses > self.MaxScenesPerPulse then
|
||||
table.SortByMember(corpses, "dist", true)
|
||||
end
|
||||
|
||||
local e = EffectData()
|
||||
e:SetOrigin(self:GetPos())
|
||||
e:SetRadius(128)
|
||||
e:SetMagnitude(0.5)
|
||||
e:SetScale(4)
|
||||
util.Effect("pulse_sphere", e)
|
||||
|
||||
-- show scenes for nearest corpses
|
||||
for i=1, self.MaxScenesPerPulse do
|
||||
local corpse = corpses[i]
|
||||
if corpse and IsValid(corpse.ent) then
|
||||
self:ShowSceneForCorpse(corpse.ent)
|
||||
end
|
||||
end
|
||||
|
||||
if #corpses > 0 then
|
||||
self:StartScanSound()
|
||||
else
|
||||
self:StopScanSound()
|
||||
end
|
||||
|
||||
-- "schedule" next show pulse
|
||||
self:SetDetonateTimer(self.PulseDelay)
|
||||
|
||||
end
|
||||
end
|
||||
54
gamemodes/terrortown/entities/entities/ttt_damageowner.lua
Normal file
54
gamemodes/terrortown/entities/entities/ttt_damageowner.lua
Normal file
@@ -0,0 +1,54 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
|
||||
ENT.Type = "point"
|
||||
ENT.Base = "base_point"
|
||||
|
||||
ENT.Damager = nil
|
||||
ENT.KillName = nil
|
||||
|
||||
function ENT:KeyValue(key, value)
|
||||
if key == "damager" then
|
||||
self.Damager = tostring(value)
|
||||
elseif key == "killname" then
|
||||
self.KillName = tostring(value)
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:AcceptInput(name, activator, caller, data)
|
||||
if name == "SetActivatorAsDamageOwner" then
|
||||
if not self.Damager then return end
|
||||
|
||||
if IsValid(activator) and activator:IsPlayer() then
|
||||
for _, ent in ipairs(ents.FindByName(self.Damager) or {}) do
|
||||
if IsValid(ent) and ent.SetDamageOwner then
|
||||
Dev(2, "Setting damageowner on", ent, ent:GetName())
|
||||
|
||||
ent:SetDamageOwner(activator)
|
||||
|
||||
ent.ScoreName = self.KillName
|
||||
end
|
||||
end
|
||||
end
|
||||
return true
|
||||
elseif name == "ClearDamageOwner" then
|
||||
if not self.Damager then return end
|
||||
|
||||
for _, ent in ipairs(ents.FindByName(self.Damager) or {}) do
|
||||
if IsValid(ent) and ent.SetDamageOwner then
|
||||
Dev(2, "Clearing damageowner on", ent, ent:GetName())
|
||||
ent:SetDamageOwner(nil)
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
89
gamemodes/terrortown/entities/entities/ttt_decoy.lua
Normal file
89
gamemodes/terrortown/entities/entities/ttt_decoy.lua
Normal file
@@ -0,0 +1,89 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
-- Decoy sending out a radar blip and redirecting DNA scans. Based on old beacon
|
||||
-- code.
|
||||
|
||||
AddCSLuaFile()
|
||||
|
||||
ENT.Type = "anim"
|
||||
ENT.Model = Model("models/props_lab/reciever01b.mdl")
|
||||
ENT.CanHavePrints = false
|
||||
ENT.CanUseKey = true
|
||||
|
||||
function ENT:Initialize()
|
||||
self:SetModel(self.Model)
|
||||
|
||||
if SERVER then
|
||||
self:PhysicsInit(SOLID_VPHYSICS)
|
||||
end
|
||||
|
||||
self:SetMoveType(MOVETYPE_VPHYSICS)
|
||||
self:SetSolid(SOLID_VPHYSICS)
|
||||
self:SetCollisionGroup(COLLISION_GROUP_INTERACTIVE)
|
||||
|
||||
if SERVER then
|
||||
self:SetMaxHealth(100)
|
||||
end
|
||||
self:SetHealth(100)
|
||||
|
||||
-- can pick this up if we own it
|
||||
if SERVER then
|
||||
self:SetUseType(SIMPLE_USE)
|
||||
|
||||
local weptbl = util.WeaponForClass("weapon_ttt_decoy")
|
||||
if weptbl and weptbl.Kind then
|
||||
self.WeaponKind = weptbl.Kind
|
||||
else
|
||||
self.WeaponKind = WEAPON_EQUIP2
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:UseOverride(activator)
|
||||
if IsValid(activator) and self:GetOwner() == activator then
|
||||
|
||||
if not activator:CanCarryType(self.WeaponKind or WEAPON_EQUIP2) then
|
||||
LANG.Msg(activator, "decoy_no_room")
|
||||
return
|
||||
end
|
||||
|
||||
activator:Give("weapon_ttt_decoy")
|
||||
|
||||
self:Remove()
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local zapsound = Sound("npc/assassin/ball_zap1.wav")
|
||||
function ENT:OnTakeDamage(dmginfo)
|
||||
self:TakePhysicsDamage(dmginfo)
|
||||
|
||||
self:SetHealth(self:Health() - dmginfo:GetDamage())
|
||||
if self:Health() < 0 then
|
||||
self:Remove()
|
||||
|
||||
local effect = EffectData()
|
||||
effect:SetOrigin(self:GetPos())
|
||||
util.Effect("cball_explode", effect)
|
||||
sound.Play(zapsound, self:GetPos())
|
||||
|
||||
if IsValid(self:GetOwner()) then
|
||||
LANG.Msg(self:GetOwner(), "decoy_broken")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:OnRemove()
|
||||
if IsValid(self:GetOwner()) then
|
||||
self:GetOwner().decoy = nil
|
||||
end
|
||||
end
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
-- burning nade projectile
|
||||
|
||||
AddCSLuaFile()
|
||||
|
||||
ENT.Type = "anim"
|
||||
ENT.Base = "ttt_basegrenade_proj"
|
||||
ENT.Model = Model("models/weapons/w_eq_flashbang_thrown.mdl")
|
||||
|
||||
|
||||
AccessorFunc( ENT, "radius", "Radius", FORCE_NUMBER )
|
||||
AccessorFunc( ENT, "dmg", "Dmg", FORCE_NUMBER )
|
||||
|
||||
function ENT:Initialize()
|
||||
if not self:GetRadius() then self:SetRadius(256) end
|
||||
if not self:GetDmg() then self:SetDmg(25) end
|
||||
|
||||
return self.BaseClass.Initialize(self)
|
||||
end
|
||||
|
||||
function ENT:Explode(tr)
|
||||
if SERVER then
|
||||
self:SetNoDraw(true)
|
||||
self:SetSolid(SOLID_NONE)
|
||||
|
||||
-- pull out of the surface
|
||||
if tr.Fraction != 1.0 then
|
||||
self:SetPos(tr.HitPos + tr.HitNormal * 0.6)
|
||||
end
|
||||
|
||||
local pos = self:GetPos()
|
||||
|
||||
if util.PointContents(pos) == CONTENTS_WATER then
|
||||
self:Remove()
|
||||
return
|
||||
end
|
||||
|
||||
local effect = EffectData()
|
||||
effect:SetStart(pos)
|
||||
effect:SetOrigin(pos)
|
||||
effect:SetScale(self:GetRadius() * 0.3)
|
||||
effect:SetRadius(self:GetRadius())
|
||||
effect:SetMagnitude(self.dmg)
|
||||
|
||||
if tr.Fraction != 1.0 then
|
||||
effect:SetNormal(tr.HitNormal)
|
||||
end
|
||||
|
||||
util.Effect("Explosion", effect, true, true)
|
||||
|
||||
util.BlastDamage(self, self:GetThrower(), pos, self:GetRadius(), self:GetDmg())
|
||||
|
||||
StartFires(pos, tr, 10, 20, false, self:GetThrower())
|
||||
|
||||
self:SetDetonateExact(0)
|
||||
|
||||
self:Remove()
|
||||
else
|
||||
local spos = self:GetPos()
|
||||
local trs = util.TraceLine({start=spos + Vector(0,0,64), endpos=spos + Vector(0,0,-128), filter=self})
|
||||
util.Decal("Scorch", trs.HitPos + trs.HitNormal, trs.HitPos - trs.HitNormal)
|
||||
|
||||
self:SetDetonateExact(0)
|
||||
end
|
||||
end
|
||||
|
||||
231
gamemodes/terrortown/entities/entities/ttt_flame.lua
Normal file
231
gamemodes/terrortown/entities/entities/ttt_flame.lua
Normal file
@@ -0,0 +1,231 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
-- fire handler that does owned damage
|
||||
|
||||
AddCSLuaFile()
|
||||
|
||||
ENT.Type = "anim"
|
||||
ENT.Model = Model("models/weapons/w_eq_flashbang_thrown.mdl")
|
||||
|
||||
AccessorFunc(ENT, "dmgparent", "DamageParent")
|
||||
|
||||
AccessorFunc(ENT, "die_explode", "ExplodeOnDeath")
|
||||
AccessorFunc(ENT, "dietime", "DieTime")
|
||||
|
||||
AccessorFuncDT(ENT, "burning", "Burning")
|
||||
|
||||
ENT.firechild = nil
|
||||
ENT.fireparams = {size=120, growth=1}
|
||||
|
||||
ENT.dietime = 0
|
||||
ENT.next_hurt = 0
|
||||
ENT.hurt_interval = 1
|
||||
|
||||
CreateConVar("ttt_fire_fallback", "0", FCVAR_ARCHIVE)
|
||||
|
||||
function ENT:SetupDataTables()
|
||||
self:DTVar("Bool", 0, "burning")
|
||||
end
|
||||
|
||||
|
||||
function ENT:Initialize()
|
||||
self:SetModel(self.Model)
|
||||
self:DrawShadow(false)
|
||||
self:SetNoDraw(true)
|
||||
|
||||
if CLIENT and GetConVar("ttt_fire_fallback"):GetBool() then
|
||||
self.Draw = self.BackupDraw
|
||||
self:SetNoDraw(false)
|
||||
end
|
||||
|
||||
self:PhysicsInit(SOLID_VPHYSICS)
|
||||
self:SetMoveType(MOVETYPE_VPHYSICS)
|
||||
self:SetSolid(SOLID_VPHYSICS)
|
||||
self:SetCollisionGroup(COLLISION_GROUP_DEBRIS)
|
||||
self:SetHealth(99999)
|
||||
|
||||
self.next_hurt = CurTime() + self.hurt_interval + math.Rand(0, 3)
|
||||
|
||||
self:SetBurning(false)
|
||||
|
||||
if self.dietime == 0 then self.dietime = CurTime() + 20 end
|
||||
end
|
||||
|
||||
function StartFires(pos, tr, num, lifetime, explode, dmgowner)
|
||||
for i=1, num do
|
||||
local ang = Angle(-math.Rand(0, 180), math.Rand(0, 360), math.Rand(0, 360))
|
||||
|
||||
local vstart = pos + tr.HitNormal * 64
|
||||
|
||||
local flame = ents.Create("ttt_flame")
|
||||
flame:SetPos(pos)
|
||||
if IsValid(dmgowner) and dmgowner:IsPlayer() then
|
||||
flame:SetDamageParent(dmgowner)
|
||||
flame:SetOwner(dmgowner)
|
||||
end
|
||||
flame:SetDieTime(CurTime() + lifetime + math.Rand(-2, 2))
|
||||
flame:SetExplodeOnDeath(explode)
|
||||
|
||||
flame:Spawn()
|
||||
flame:PhysWake()
|
||||
|
||||
local phys = flame:GetPhysicsObject()
|
||||
if IsValid(phys) then
|
||||
-- the balance between mass and force is subtle, be careful adjusting
|
||||
phys:SetMass(2)
|
||||
phys:ApplyForceCenter(ang:Forward() * 500)
|
||||
phys:AddAngleVelocity(Vector(ang.p, ang.r, ang.y))
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function SpawnFire(pos, size, attack, fuel, owner, parent)
|
||||
local fire = ents.Create("env_fire")
|
||||
if not IsValid(fire) then return end
|
||||
|
||||
fire:SetParent(parent)
|
||||
fire:SetOwner(owner)
|
||||
fire:SetPos(pos)
|
||||
--no glow + delete when out + start on + last forever
|
||||
fire:SetKeyValue("spawnflags", tostring(128 + 32 + 4 + 2 + 1))
|
||||
fire:SetKeyValue("firesize", (size * math.Rand(0.7, 1.1)))
|
||||
fire:SetKeyValue("fireattack", attack)
|
||||
fire:SetKeyValue("health", fuel)
|
||||
fire:SetKeyValue("damagescale", "-10") -- only neg. value prevents dmg
|
||||
|
||||
fire:Spawn()
|
||||
fire:Activate()
|
||||
|
||||
return fire
|
||||
end
|
||||
|
||||
-- greatly simplified version of SDK's game_shard/gamerules.cpp:RadiusDamage
|
||||
-- does no block checking, radius should be very small
|
||||
function RadiusDamage(dmginfo, pos, radius, inflictor)
|
||||
local tr = nil
|
||||
for k, vic in ipairs(ents.FindInSphere(pos, radius)) do
|
||||
if IsValid(vic) and inflictor:Visible(vic) then
|
||||
if vic:IsPlayer() and vic:Alive() and vic:Team() == TEAM_TERROR then
|
||||
vic:TakeDamageInfo(dmginfo)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:OnRemove()
|
||||
if IsValid(self.firechild) then
|
||||
self.firechild:Remove()
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:OnTakeDamage()
|
||||
end
|
||||
|
||||
function ENT:Explode()
|
||||
local pos = self:GetPos()
|
||||
|
||||
local effect = EffectData()
|
||||
effect:SetStart(pos)
|
||||
effect:SetOrigin(pos)
|
||||
effect:SetScale(256)
|
||||
effect:SetRadius(256)
|
||||
effect:SetMagnitude(50)
|
||||
|
||||
util.Effect("Explosion", effect, true, true)
|
||||
|
||||
local dmgowner = self:GetDamageParent()
|
||||
if not IsValid(dmgowner) then
|
||||
dmgowner = self
|
||||
end
|
||||
util.BlastDamage(self, dmgowner, pos, 300, 40)
|
||||
end
|
||||
|
||||
function ENT:Think()
|
||||
if CLIENT then return end
|
||||
|
||||
if self.dietime < CurTime() then
|
||||
if self:GetExplodeOnDeath() then
|
||||
local success, err = pcall(self.Explode, self)
|
||||
|
||||
if not success then
|
||||
ErrorNoHalt("ERROR CAUGHT: ttt_flame: " .. err .. "\n")
|
||||
end
|
||||
end
|
||||
|
||||
if IsValid(self.firechild) then
|
||||
self.firechild:Remove()
|
||||
end
|
||||
|
||||
self:Remove()
|
||||
return
|
||||
end
|
||||
|
||||
if IsValid(self.firechild) then
|
||||
if self.next_hurt < CurTime() then
|
||||
if self:WaterLevel() > 0 then
|
||||
self.dietime = 0
|
||||
return
|
||||
end
|
||||
|
||||
-- deal damage
|
||||
|
||||
local dmg = DamageInfo()
|
||||
dmg:SetDamageType(DMG_BURN)
|
||||
dmg:SetDamage(math.random(4,6))
|
||||
if IsValid(self:GetDamageParent()) then
|
||||
dmg:SetAttacker(self:GetDamageParent())
|
||||
else
|
||||
dmg:SetAttacker(self)
|
||||
end
|
||||
dmg:SetInflictor(self.firechild)
|
||||
|
||||
RadiusDamage(dmg, self:GetPos(), 132, self)
|
||||
|
||||
self.next_hurt = CurTime() + self.hurt_interval
|
||||
end
|
||||
return
|
||||
elseif self:GetVelocity() == Vector(0,0,0) then
|
||||
if self:WaterLevel() > 0 then
|
||||
self.dietime = 0
|
||||
return
|
||||
end
|
||||
|
||||
self.firechild = SpawnFire(self:GetPos(), self.fireparams.size, self.fireparams.growth, 999, self:GetDamageParent(), self)
|
||||
|
||||
self:SetBurning(true)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
if CLIENT then
|
||||
local fakefire = Material("cable/smoke")
|
||||
local side = Angle(-90, 0, 0)
|
||||
function ENT:BackupDraw()
|
||||
if not self:GetBurning() then return end
|
||||
|
||||
local vstart = self:GetPos()
|
||||
local vend = vstart + Vector(0, 0, 90)
|
||||
|
||||
side.r = side.r + 0.1
|
||||
|
||||
cam.Start3D2D(vstart, side, 1)
|
||||
draw.DrawText("FIRE! IT BURNS!", "Default", 0, 0, COLOR_RED, ALIGN_CENTER)
|
||||
cam.End3D2D()
|
||||
|
||||
render.SetMaterial(fakefire)
|
||||
render.DrawBeam(vstart, vend, 80, 0, 0, COLOR_RED)
|
||||
end
|
||||
|
||||
function ENT:Draw()
|
||||
end
|
||||
end
|
||||
72
gamemodes/terrortown/entities/entities/ttt_game_text.lua
Normal file
72
gamemodes/terrortown/entities/entities/ttt_game_text.lua
Normal file
@@ -0,0 +1,72 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
|
||||
ENT.Type = "point"
|
||||
ENT.Base = "base_point"
|
||||
|
||||
ENT.Message = ""
|
||||
ENT.Color = COLOR_WHITE
|
||||
|
||||
local RECEIVE_ACTIVATOR = 0
|
||||
local RECEIVE_ALL = 1
|
||||
local RECEIVE_DETECTIVE = 2
|
||||
local RECEIVE_TRAITOR = 3
|
||||
local RECEIVE_INNOCENT = 4
|
||||
|
||||
ENT.Receiver = RECEIVE_ACTIVATOR
|
||||
|
||||
function ENT:KeyValue(key, value)
|
||||
if key == "message" then
|
||||
self.Message = tostring(value) or "ERROR: bad value"
|
||||
elseif key == "color" then
|
||||
local mr, mg, mb = string.match(value, "(%d*) (%d*) (%d*)")
|
||||
|
||||
local c = Color(0,0,0)
|
||||
c.r = tonumber(mr) or 255
|
||||
c.g = tonumber(mg) or 255
|
||||
c.b = tonumber(mb) or 255
|
||||
|
||||
self.Color = c
|
||||
elseif key == "receive" then
|
||||
self.Receiver = tonumber(value)
|
||||
if not (self.Receiver and self.Receiver >= 0 and self.Receiver <= 4) then
|
||||
ErrorNoHalt("ERROR: ttt_game_text has invalid receiver value\n")
|
||||
self.Receiver = RECEIVE_ACTIVATOR
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:AcceptInput(name, activator)
|
||||
if name == "Display" then
|
||||
local recv = activator
|
||||
|
||||
local r = self.Receiver
|
||||
if r == RECEIVE_ALL then
|
||||
recv = nil
|
||||
elseif r == RECEIVE_DETECTIVE then
|
||||
recv = GetDetectiveFilter()
|
||||
elseif r == RECEIVE_TRAITOR then
|
||||
recv = GetTraitorFilter()
|
||||
elseif r == RECEIVE_INNOCENT then
|
||||
recv = GetInnocentFilter()
|
||||
elseif r == RECEIVE_ACTIVATOR then
|
||||
if not (IsValid(activator) and activator:IsPlayer()) then
|
||||
ErrorNoHalt("ttt_game_text tried to show message to invalid !activator\n")
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
CustomMsg(recv, self.Message, self.Color)
|
||||
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
151
gamemodes/terrortown/entities/entities/ttt_hat_deerstalker.lua
Normal file
151
gamemodes/terrortown/entities/entities/ttt_hat_deerstalker.lua
Normal file
@@ -0,0 +1,151 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
|
||||
AddCSLuaFile()
|
||||
|
||||
ENT.Type = "anim"
|
||||
ENT.Base = "base_anim"
|
||||
|
||||
ENT.Model = Model("models/ttt/deerstalker.mdl")
|
||||
ENT.CanHavePrints = false
|
||||
ENT.CanUseKey = true
|
||||
|
||||
AccessorFuncDT(ENT, "worn", "BeingWorn")
|
||||
|
||||
function ENT:SetupDataTables()
|
||||
self:DTVar("Bool", 0, "worn")
|
||||
end
|
||||
|
||||
function ENT:Initialize()
|
||||
self:SetBeingWorn(true)
|
||||
|
||||
self:SetModel(self.Model)
|
||||
|
||||
self:DrawShadow(false)
|
||||
|
||||
-- don't physicsinit the ent here, because physicsing and then setting
|
||||
-- movetype none is 1) a waste of memory, 2) broken
|
||||
|
||||
self:SetMoveType(MOVETYPE_NONE)
|
||||
self:SetSolid(SOLID_NONE)
|
||||
self:SetCollisionGroup(COLLISION_GROUP_DEBRIS)
|
||||
|
||||
if SERVER then
|
||||
self.Wearer = self:GetParent()
|
||||
self:AddEffects(bit.bor(EF_BONEMERGE, EF_BONEMERGE_FASTCULL, EF_PARENT_ANIMATES))
|
||||
end
|
||||
end
|
||||
|
||||
if SERVER then
|
||||
local ttt_hats_reclaim = CreateConVar("ttt_detective_hats_reclaim", "1")
|
||||
local ttt_hats_innocent = CreateConVar("ttt_detective_hats_reclaim_any", "0")
|
||||
|
||||
function ENT:OnRemove()
|
||||
self:SetBeingWorn(false)
|
||||
end
|
||||
|
||||
function ENT:Drop(dir)
|
||||
local ply = self:GetParent()
|
||||
|
||||
ply.hat = nil
|
||||
self:SetParent(nil)
|
||||
|
||||
self:SetBeingWorn(false)
|
||||
self:SetUseType(SIMPLE_USE)
|
||||
|
||||
-- only now physics this entity
|
||||
self:PhysicsInit(SOLID_VPHYSICS)
|
||||
self:SetSolid(SOLID_VPHYSICS)
|
||||
self:SetMoveType(MOVETYPE_VPHYSICS)
|
||||
|
||||
-- position at head
|
||||
if IsValid(ply) then
|
||||
local bone = ply:LookupBone("ValveBiped.Bip01_Head1")
|
||||
if bone then
|
||||
local pos, ang = ply:GetBonePosition(bone)
|
||||
self:SetPos(pos)
|
||||
self:SetAngles(ang)
|
||||
else
|
||||
local pos = ply:GetPos()
|
||||
pos.z = pos.z + 68
|
||||
|
||||
self:SetPos(pos)
|
||||
end
|
||||
end
|
||||
|
||||
-- physics push
|
||||
local phys = self:GetPhysicsObject()
|
||||
if IsValid(phys) then
|
||||
phys:SetMass(10)
|
||||
|
||||
if IsValid(ply) then
|
||||
phys:SetVelocityInstantaneous(ply:GetVelocity())
|
||||
end
|
||||
|
||||
if not dir then
|
||||
phys:ApplyForceCenter(Vector(0, 0, 1200))
|
||||
else
|
||||
phys:ApplyForceCenter(Vector(0, 0, 700) + dir * 500)
|
||||
end
|
||||
|
||||
phys:AddAngleVelocity(VectorRand() * 200)
|
||||
|
||||
phys:Wake()
|
||||
end
|
||||
end
|
||||
|
||||
local function CanEquipHat(ply)
|
||||
return not IsValid(ply.hat) and
|
||||
(ttt_hats_innocent:GetBool() or ply:GetRole() == ROLE_DETECTIVE)
|
||||
end
|
||||
|
||||
function ENT:UseOverride(ply)
|
||||
if not ttt_hats_reclaim:GetBool() then return end
|
||||
|
||||
if IsValid(ply) and not self:GetBeingWorn() then
|
||||
if GetRoundState() != ROUND_ACTIVE then
|
||||
SafeRemoveEntity(self)
|
||||
return
|
||||
elseif not CanEquipHat(ply) then
|
||||
return
|
||||
end
|
||||
|
||||
sound.Play("weapon.ImpactSoft", self:GetPos(), 75, 100, 1)
|
||||
|
||||
self:SetMoveType(MOVETYPE_NONE)
|
||||
self:SetSolid(SOLID_NONE)
|
||||
self:SetCollisionGroup(COLLISION_GROUP_DEBRIS)
|
||||
|
||||
self:SetParent(ply)
|
||||
self.Wearer = ply
|
||||
|
||||
ply.hat = self
|
||||
|
||||
self:SetBeingWorn(true)
|
||||
|
||||
LANG.Msg(ply, "hat_retrieve")
|
||||
end
|
||||
end
|
||||
|
||||
local function TestHat(ply, cmd, args)
|
||||
local hat = ents.Create("ttt_hat_deerstalker")
|
||||
|
||||
hat:SetPos(ply:GetPos() + Vector(0,0,70))
|
||||
hat:SetAngles(ply:GetAngles())
|
||||
|
||||
hat:SetParent(ply)
|
||||
|
||||
ply.hat = hat
|
||||
|
||||
hat:Spawn()
|
||||
end
|
||||
concommand.Add("ttt_debug_testhat", TestHat, nil, nil, FCVAR_CHEAT)
|
||||
end
|
||||
186
gamemodes/terrortown/entities/entities/ttt_health_station.lua
Normal file
186
gamemodes/terrortown/entities/entities/ttt_health_station.lua
Normal file
@@ -0,0 +1,186 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
---- Health dispenser
|
||||
|
||||
AddCSLuaFile()
|
||||
|
||||
if CLIENT then
|
||||
-- this entity can be DNA-sampled so we need some display info
|
||||
ENT.Icon = "vgui/ttt/icon_health"
|
||||
ENT.PrintName = "hstation_name"
|
||||
|
||||
local GetPTranslation = LANG.GetParamTranslation
|
||||
|
||||
ENT.TargetIDHint = {
|
||||
name = "hstation_name",
|
||||
hint = "hstation_hint",
|
||||
fmt = function(ent, txt)
|
||||
return GetPTranslation(txt,
|
||||
{ usekey = Key("+use", "USE"),
|
||||
num = ent:GetStoredHealth() or 0 } )
|
||||
end
|
||||
};
|
||||
|
||||
end
|
||||
|
||||
ENT.Type = "anim"
|
||||
ENT.Model = Model("models/props/cs_office/microwave.mdl")
|
||||
|
||||
--ENT.CanUseKey = true
|
||||
ENT.CanHavePrints = true
|
||||
ENT.MaxHeal = 25
|
||||
ENT.MaxStored = 200
|
||||
ENT.RechargeRate = 1
|
||||
ENT.RechargeFreq = 2 -- in seconds
|
||||
|
||||
ENT.NextHeal = 0
|
||||
ENT.HealRate = 1
|
||||
ENT.HealFreq = 0.2
|
||||
|
||||
AccessorFuncDT(ENT, "StoredHealth", "StoredHealth")
|
||||
|
||||
AccessorFunc(ENT, "Placer", "Placer")
|
||||
|
||||
function ENT:SetupDataTables()
|
||||
self:DTVar("Int", 0, "StoredHealth")
|
||||
end
|
||||
|
||||
function ENT:Initialize()
|
||||
self:SetModel(self.Model)
|
||||
|
||||
self:PhysicsInit(SOLID_VPHYSICS)
|
||||
self:SetMoveType(MOVETYPE_VPHYSICS)
|
||||
self:SetSolid(SOLID_BBOX)
|
||||
|
||||
local b = 32
|
||||
self:SetCollisionBounds(Vector(-b, -b, -b), Vector(b,b,b))
|
||||
|
||||
self:SetCollisionGroup(COLLISION_GROUP_WEAPON)
|
||||
if SERVER then
|
||||
self:SetMaxHealth(200)
|
||||
|
||||
local phys = self:GetPhysicsObject()
|
||||
if IsValid(phys) then
|
||||
phys:SetMass(200)
|
||||
end
|
||||
|
||||
self:SetUseType(CONTINUOUS_USE)
|
||||
end
|
||||
self:SetHealth(200)
|
||||
|
||||
self:SetColor(Color(180, 180, 250, 255))
|
||||
|
||||
self:SetStoredHealth(200)
|
||||
|
||||
self:SetPlacer(nil)
|
||||
|
||||
self.NextHeal = 0
|
||||
|
||||
self.fingerprints = {}
|
||||
end
|
||||
|
||||
|
||||
function ENT:AddToStorage(amount)
|
||||
self:SetStoredHealth(math.min(self.MaxStored, self:GetStoredHealth() + amount))
|
||||
end
|
||||
|
||||
function ENT:TakeFromStorage(amount)
|
||||
-- if we only have 5 healthpts in store, that is the amount we heal
|
||||
amount = math.min(amount, self:GetStoredHealth())
|
||||
self:SetStoredHealth(math.max(0, self:GetStoredHealth() - amount))
|
||||
return amount
|
||||
end
|
||||
|
||||
local healsound = Sound("items/medshot4.wav")
|
||||
local failsound = Sound("items/medshotno1.wav")
|
||||
|
||||
local last_sound_time = 0
|
||||
function ENT:GiveHealth(ply, max_heal)
|
||||
if self:GetStoredHealth() > 0 then
|
||||
max_heal = max_heal or self.MaxHeal
|
||||
local dmg = ply:GetMaxHealth() - ply:Health()
|
||||
if dmg > 0 then
|
||||
-- constant clamping, no risks
|
||||
local healed = self:TakeFromStorage(math.min(max_heal, dmg))
|
||||
local new = math.min(ply:GetMaxHealth(), ply:Health() + healed)
|
||||
|
||||
ply:SetHealth(new)
|
||||
hook.Run("TTTPlayerUsedHealthStation", ply, self, healed)
|
||||
|
||||
if last_sound_time + 2 < CurTime() then
|
||||
self:EmitSound(healsound)
|
||||
last_sound_time = CurTime()
|
||||
end
|
||||
|
||||
if not table.HasValue(self.fingerprints, ply) then
|
||||
table.insert(self.fingerprints, ply)
|
||||
end
|
||||
|
||||
return true
|
||||
else
|
||||
self:EmitSound(failsound)
|
||||
end
|
||||
else
|
||||
self:EmitSound(failsound)
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
function ENT:Use(ply)
|
||||
if IsValid(ply) and ply:IsPlayer() and ply:IsActive() then
|
||||
local t = CurTime()
|
||||
if t > self.NextHeal then
|
||||
local healed = self:GiveHealth(ply, self.HealRate)
|
||||
|
||||
self.NextHeal = t + (self.HealFreq * (healed and 1 or 2))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if SERVER then
|
||||
-- recharge
|
||||
local nextcharge = 0
|
||||
function ENT:Think()
|
||||
if nextcharge < CurTime() then
|
||||
self:AddToStorage(self.RechargeRate)
|
||||
|
||||
nextcharge = CurTime() + self.RechargeFreq
|
||||
end
|
||||
end
|
||||
|
||||
local ttt_damage_own_healthstation = CreateConVar("ttt_damage_own_healthstation", "0") -- 0 as detective cannot damage their own health station
|
||||
|
||||
-- traditional equipment destruction effects
|
||||
function ENT:OnTakeDamage(dmginfo)
|
||||
if dmginfo:GetAttacker() == self:GetPlacer() and not ttt_damage_own_healthstation:GetBool() then return end
|
||||
|
||||
self:TakePhysicsDamage(dmginfo)
|
||||
|
||||
self:SetHealth(self:Health() - dmginfo:GetDamage())
|
||||
|
||||
local att = dmginfo:GetAttacker()
|
||||
local placer = self:GetPlacer()
|
||||
if IsPlayer(att) then
|
||||
DamageLog(Format("DMG: \t %s [%s] damaged health station [%s] for %d dmg", att:Nick(), att:GetRoleString(), (IsPlayer(placer) and placer:Nick() or "<disconnected>"), dmginfo:GetDamage()))
|
||||
end
|
||||
|
||||
if self:Health() < 0 then
|
||||
self:Remove()
|
||||
|
||||
util.EquipmentDestroyed(self:GetPos())
|
||||
|
||||
if IsValid(self:GetPlacer()) then
|
||||
LANG.Msg(self:GetPlacer(), "hstation_broken")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
212
gamemodes/terrortown/entities/entities/ttt_knife_proj.lua
Normal file
212
gamemodes/terrortown/entities/entities/ttt_knife_proj.lua
Normal file
@@ -0,0 +1,212 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
-- thrown knife
|
||||
|
||||
AddCSLuaFile()
|
||||
|
||||
if CLIENT then
|
||||
ENT.PrintName = "knife_thrown"
|
||||
ENT.Icon = "vgui/ttt/icon_knife"
|
||||
end
|
||||
|
||||
|
||||
ENT.Type = "anim"
|
||||
ENT.Model = Model("models/weapons/w_knife_t.mdl")
|
||||
|
||||
-- When true, score code considers us a weapon
|
||||
ENT.Projectile = true
|
||||
|
||||
ENT.Stuck = false
|
||||
ENT.Weaponised = false
|
||||
ENT.CanHavePrints = false
|
||||
ENT.IsSilent = true
|
||||
ENT.CanPickup = false
|
||||
|
||||
ENT.WeaponID = AMMO_KNIFE
|
||||
|
||||
ENT.Damage = 50
|
||||
|
||||
function ENT:Initialize()
|
||||
self:SetModel(self.Model)
|
||||
|
||||
self:PhysicsInit(SOLID_VPHYSICS)
|
||||
|
||||
if SERVER then
|
||||
self:SetGravity(0.4)
|
||||
self:SetFriction(1.0)
|
||||
self:SetElasticity(0.45)
|
||||
|
||||
self.StartPos = self:GetPos()
|
||||
|
||||
self:NextThink(CurTime())
|
||||
end
|
||||
|
||||
self.Weaponised = false
|
||||
self.Stuck = false
|
||||
end
|
||||
|
||||
function ENT:HitPlayer(other, tr)
|
||||
|
||||
local range_dmg = math.max(self.Damage, self.StartPos:Distance(self:GetPos()) / 3)
|
||||
|
||||
if other:Health() < range_dmg + 10 then
|
||||
self:KillPlayer(other, tr)
|
||||
elseif SERVER then
|
||||
local dmg = DamageInfo()
|
||||
dmg:SetDamage(range_dmg)
|
||||
dmg:SetAttacker(self:GetOwner())
|
||||
dmg:SetInflictor(self)
|
||||
dmg:SetDamageForce(self:EyeAngles():Forward())
|
||||
dmg:SetDamagePosition(self:GetPos())
|
||||
dmg:SetDamageType(DMG_SLASH)
|
||||
|
||||
local ang = Angle(-28,0,0) + tr.Normal:Angle()
|
||||
ang:RotateAroundAxis(ang:Right(), -90)
|
||||
other:DispatchTraceAttack(dmg, self:GetPos() + ang:Forward() * 3, other:GetPos())
|
||||
|
||||
if not self.Weaponised then
|
||||
self:BecomeWeaponDelayed()
|
||||
end
|
||||
end
|
||||
|
||||
-- As a thrown knife, after we hit a target we can never hit one again.
|
||||
-- If we are picked up and re-thrown, a new knife_proj entity is created.
|
||||
-- To make sure we can never deal damage twice, make HitPlayer do nothing.
|
||||
self.HitPlayer = util.noop
|
||||
end
|
||||
|
||||
function ENT:KillPlayer(other, tr)
|
||||
local dmg = DamageInfo()
|
||||
dmg:SetDamage(2000)
|
||||
dmg:SetAttacker(self:GetOwner())
|
||||
dmg:SetInflictor(self)
|
||||
dmg:SetDamageForce(self:EyeAngles():Forward())
|
||||
dmg:SetDamagePosition(self:GetPos())
|
||||
dmg:SetDamageType(DMG_SLASH)
|
||||
|
||||
-- this bone is why we need the trace
|
||||
local bone = tr.PhysicsBone
|
||||
local pos = tr.HitPos
|
||||
local norm = tr.Normal
|
||||
local ang = Angle(-28,0,0) + norm:Angle()
|
||||
ang:RotateAroundAxis(ang:Right(), -90)
|
||||
pos = pos - (ang:Forward() * 8)
|
||||
|
||||
local knife = self
|
||||
local prints = self.fingerprints
|
||||
|
||||
other.effect_fn = function(rag)
|
||||
|
||||
if not IsValid(knife) or not IsValid(rag) then return end
|
||||
|
||||
knife:SetPos(pos)
|
||||
knife:SetCollisionGroup(COLLISION_GROUP_DEBRIS)
|
||||
knife:SetAngles(ang)
|
||||
|
||||
knife:SetMoveCollide(MOVECOLLIDE_DEFAULT)
|
||||
knife:SetMoveType(MOVETYPE_VPHYSICS)
|
||||
|
||||
knife.fingerprints = prints
|
||||
knife:SetNWBool("HasPrints", true)
|
||||
|
||||
--knife:SetSolid(SOLID_NONE)
|
||||
-- knife needs to be trace-able to get prints
|
||||
local phys = knife:GetPhysicsObject()
|
||||
if IsValid(phys) then
|
||||
phys:EnableCollisions(false)
|
||||
end
|
||||
|
||||
constraint.Weld(rag, knife, bone, 0, 0, true)
|
||||
|
||||
rag:CallOnRemove("ttt_knife_cleanup", function() SafeRemoveEntity(knife) end)
|
||||
end
|
||||
|
||||
|
||||
other:DispatchTraceAttack(dmg, self:GetPos() + ang:Forward() * 3, other:GetPos())
|
||||
|
||||
self.Stuck = true
|
||||
end
|
||||
|
||||
if SERVER then
|
||||
function ENT:Think()
|
||||
if self.Stuck then return end
|
||||
|
||||
local vel = self:GetVelocity()
|
||||
if vel == vector_origin then return end
|
||||
|
||||
local tr = util.TraceLine({start=self:GetPos(), endpos=self:GetPos() + vel:GetNormal() * 20, filter={self, self:GetOwner()}, mask=MASK_SHOT_HULL})
|
||||
|
||||
if tr.Hit and tr.HitNonWorld and IsValid(tr.Entity) then
|
||||
local other = tr.Entity
|
||||
if other:IsPlayer() then
|
||||
self:HitPlayer(other, tr)
|
||||
end
|
||||
end
|
||||
|
||||
self:NextThink(CurTime())
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
-- When this entity touches anything that is not a player, it should turn into a
|
||||
-- weapon ent again. If it touches a player it sticks in it.
|
||||
if SERVER then
|
||||
function ENT:BecomeWeapon()
|
||||
self.Weaponised = true
|
||||
|
||||
local wep = ents.Create("weapon_ttt_knife")
|
||||
wep:SetPos(self:GetPos())
|
||||
wep:SetAngles(self:GetAngles())
|
||||
wep.IsDropped = true
|
||||
|
||||
local prints = self.fingerprints or {}
|
||||
|
||||
self:Remove()
|
||||
|
||||
wep:Spawn()
|
||||
wep.fingerprints = wep.fingerprints or {}
|
||||
table.Add(wep.fingerprints, prints)
|
||||
|
||||
return wep
|
||||
end
|
||||
|
||||
function ENT:BecomeWeaponDelayed()
|
||||
-- delay the weapon-replacement a tick because Source gets very angry
|
||||
-- if you do fancy stuff in a physics callback
|
||||
local knife = self
|
||||
timer.Simple(0,
|
||||
function()
|
||||
if IsValid(knife) and not knife.Weaponised then
|
||||
knife:BecomeWeapon()
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
function ENT:PhysicsCollide(data, phys)
|
||||
if self.Stuck then return false end
|
||||
|
||||
local other = data.HitEntity
|
||||
if not IsValid(other) and not other:IsWorld() then return end
|
||||
|
||||
if other:IsPlayer() then
|
||||
local tr = util.TraceLine({start=self:GetPos(), endpos=other:LocalToWorld(other:OBBCenter()), filter={self, self:GetOwner()}, mask=MASK_SHOT_HULL})
|
||||
if tr.Hit and tr.Entity == other then
|
||||
self:HitPlayer(other, tr)
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
if not self.Weaponised then
|
||||
self:BecomeWeaponDelayed()
|
||||
end
|
||||
end
|
||||
end
|
||||
51
gamemodes/terrortown/entities/entities/ttt_logic_role.lua
Normal file
51
gamemodes/terrortown/entities/entities/ttt_logic_role.lua
Normal file
@@ -0,0 +1,51 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
|
||||
ENT.Type = "point"
|
||||
ENT.Base = "base_point"
|
||||
|
||||
local ROLE_ANY = 3
|
||||
|
||||
ENT.Role = ROLE_ANY
|
||||
|
||||
function ENT:KeyValue(key, value)
|
||||
if key == "OnPass" or key == "OnFail" then
|
||||
-- this is our output, so handle it as such
|
||||
self:StoreOutput(key, value)
|
||||
elseif key == "Role" then
|
||||
self.Role = tonumber(value)
|
||||
|
||||
if not self.Role then
|
||||
ErrorNoHalt("ttt_logic_role: bad value for Role key, not a number\n")
|
||||
self.Role = ROLE_ANY
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function ENT:AcceptInput(name, activator)
|
||||
if name == "TestActivator" then
|
||||
if IsValid(activator) and activator:IsPlayer() then
|
||||
local activator_role = (GetRoundState() == ROUND_PREP) and ROLE_INNOCENT or activator:GetRole()
|
||||
|
||||
if self.Role == ROLE_ANY or self.Role == activator_role then
|
||||
Dev(2, activator, "passed logic_role test of", self:GetName())
|
||||
self:TriggerOutput("OnPass", activator)
|
||||
else
|
||||
Dev(2, activator, "failed logic_role test of", self:GetName())
|
||||
self:TriggerOutput("OnFail", activator)
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
78
gamemodes/terrortown/entities/entities/ttt_map_settings.lua
Normal file
78
gamemodes/terrortown/entities/entities/ttt_map_settings.lua
Normal file
@@ -0,0 +1,78 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
|
||||
ENT.Type = "point"
|
||||
ENT.Base = "base_point"
|
||||
|
||||
function ENT:Initialize()
|
||||
timer.Simple(0, function() self:TriggerOutput("MapSettingsSpawned", self) end)
|
||||
end
|
||||
|
||||
function ENT:KeyValue(k, v)
|
||||
if k == "cbar_doors" then
|
||||
Dev(2, "ttt_map_settings: crowbar door unlocking = " .. v)
|
||||
local opens = (v == "1")
|
||||
GAMEMODE.crowbar_unlocks[OPEN_DOOR] = opens
|
||||
GAMEMODE.crowbar_unlocks[OPEN_ROT] = opens
|
||||
elseif k == "cbar_buttons" then
|
||||
Dev(2, "ttt_map_settings: crowbar button unlocking = " .. v)
|
||||
GAMEMODE.crowbar_unlocks[OPEN_BUT] = (v == "1")
|
||||
elseif k == "cbar_other" then
|
||||
Dev(2, "ttt_map_settings: crowbar movelinear unlocking = " .. v)
|
||||
GAMEMODE.crowbar_unlocks[OPEN_NOTOGGLE] = (v == "1")
|
||||
elseif k == "plymodel" and v != "" then -- can ignore if empty
|
||||
if util.IsValidModel(v) then
|
||||
util.PrecacheModel(v)
|
||||
GAMEMODE.force_plymodel = v
|
||||
|
||||
Dev(2, "ttt_map_settings: set player model to be " .. v)
|
||||
else
|
||||
Dev(2, "ttt_map_settings: FAILED to set player model due to invalid path: " .. v)
|
||||
end
|
||||
elseif k == "propspec_named" or k == "propspec_allow_named" then
|
||||
Dev(2, "ttt_map_settings: propspec possessing named props = " .. v)
|
||||
GAMEMODE.propspec_allow_named = (v == "1")
|
||||
elseif k == "MapSettingsSpawned" or k == "RoundEnd" or k == "RoundPreparation" or k == "RoundStart" then
|
||||
self:StoreOutput(k, v)
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:AcceptInput(name, activator, caller, data)
|
||||
if name == "SetPlayerModels" then
|
||||
local mdlname = tostring(data)
|
||||
|
||||
if not mdlname then
|
||||
ErrorNoHalt("ttt_map_settings: Invalid parameter to SetPlayerModels input!\n")
|
||||
return false
|
||||
elseif not util.IsValidModel(mdlname) then
|
||||
ErrorNoHalt("ttt_map_settings: Invalid model given: " .. mdlname .. "\n")
|
||||
return false
|
||||
end
|
||||
|
||||
GAMEMODE.force_plymodel = Model(mdlname)
|
||||
|
||||
Dev(2, "ttt_map_settings: input set player model to be " .. mdlname)
|
||||
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
-- Fire an output when the round changes
|
||||
function ENT:RoundStateTrigger(r, data)
|
||||
if r == ROUND_PREP then
|
||||
self:TriggerOutput("RoundPreparation", self)
|
||||
elseif r == ROUND_ACTIVE then
|
||||
self:TriggerOutput("RoundStart", self)
|
||||
elseif r == ROUND_POST then
|
||||
-- RoundEnd has the type of win condition as param
|
||||
self:TriggerOutput("RoundEnd", self, tostring(data))
|
||||
end
|
||||
end
|
||||
183
gamemodes/terrortown/entities/entities/ttt_physhammer.lua
Normal file
183
gamemodes/terrortown/entities/entities/ttt_physhammer.lua
Normal file
@@ -0,0 +1,183 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
|
||||
AddCSLuaFile()
|
||||
|
||||
ENT.Type = "anim"
|
||||
ENT.Model = Model("models/Items/combine_rifle_ammo01.mdl")
|
||||
|
||||
ENT.Stuck = false
|
||||
ENT.Weaponised = false
|
||||
|
||||
ENT.PunchMax = 6
|
||||
ENT.PunchRemaining = 6
|
||||
|
||||
|
||||
function ENT:Initialize()
|
||||
self:SetModel(self.Model)
|
||||
|
||||
self:SetSolid(SOLID_NONE)
|
||||
|
||||
if SERVER then
|
||||
self:SetGravity(0.4)
|
||||
self:SetFriction(1.0)
|
||||
self:SetElasticity(0.45)
|
||||
|
||||
self:NextThink(CurTime() + 1)
|
||||
end
|
||||
|
||||
self:SetColor(Color(55, 50, 250, 255))
|
||||
|
||||
self.Stuck = false
|
||||
self.PunchMax = 6
|
||||
self.PunchRemaining = self.PunchMax
|
||||
end
|
||||
|
||||
function ENT:StickTo(ent)
|
||||
if (not IsValid(ent)) or ent:IsPlayer() or ent:GetMoveType() != MOVETYPE_VPHYSICS then return false end
|
||||
|
||||
local phys = ent:GetPhysicsObject()
|
||||
if (not IsValid(phys)) or (not phys:IsMoveable()) then return false end
|
||||
|
||||
-- local norm = self:GetAngles():Up()
|
||||
|
||||
self:SetParent(ent)
|
||||
|
||||
ent:SetPhysicsAttacker(self:GetOwner())
|
||||
ent:SetNWBool("punched", true)
|
||||
self.PunchEntity = ent
|
||||
|
||||
self:StartEffects()
|
||||
|
||||
self.Stuck = true
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function ENT:OnRemove()
|
||||
if IsValid(self.BallSprite) then
|
||||
self.BallSprite:Remove()
|
||||
end
|
||||
|
||||
if IsValid(self.PunchEntity) then
|
||||
self.PunchEntity:SetPhysicsAttacker(self.PunchEntity)
|
||||
self.PunchEntity:SetNWBool("punched", false)
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:StartEffects()
|
||||
-- MAKE IT PRETTY
|
||||
|
||||
local sprite = ents.Create("env_sprite")
|
||||
if IsValid(sprite) then
|
||||
-- local angpos = self:GetAttachment(ball)
|
||||
-- sometimes attachments don't work (Lua-side) on dedicated servers,
|
||||
-- so have to fudge it
|
||||
local ang = self:GetAngles()
|
||||
local pos = self:GetPos() + self:GetAngles():Up() * 6
|
||||
sprite:SetPos(pos)
|
||||
sprite:SetAngles(ang)
|
||||
sprite:SetParent(self)
|
||||
|
||||
sprite:SetKeyValue("model", "sprites/combineball_glow_blue_1.vmt")
|
||||
sprite:SetKeyValue("spawnflags", "1")
|
||||
sprite:SetKeyValue("scale", "0.25")
|
||||
sprite:SetKeyValue("rendermode", "5")
|
||||
sprite:SetKeyValue("renderfx", "7")
|
||||
|
||||
sprite:Spawn()
|
||||
sprite:Activate()
|
||||
|
||||
self.BallSprite = sprite
|
||||
|
||||
end
|
||||
|
||||
local effect = EffectData()
|
||||
effect:SetStart(self:GetPos())
|
||||
effect:SetOrigin(self:GetPos())
|
||||
effect:SetNormal(self:GetAngles():Up())
|
||||
util.Effect("ManhackSparks", effect, true, true)
|
||||
|
||||
if SERVER then
|
||||
local ball = self:LookupAttachment("attach_ball")
|
||||
util.SpriteTrail(self, ball, Color(250, 250, 250), false, 30, 0, 1, 0.07, "trails/physbeam.vmt")
|
||||
end
|
||||
end
|
||||
|
||||
if SERVER then
|
||||
local diesound = Sound("weapons/physcannon/energy_disintegrate4.wav")
|
||||
local punchsound = Sound("weapons/ar2/ar2_altfire.wav")
|
||||
function ENT:Think()
|
||||
if not self.Stuck then return end
|
||||
|
||||
if self.PunchRemaining <= 0 then
|
||||
-- self:StopParticles()
|
||||
|
||||
local pos = self:GetPos()
|
||||
|
||||
util.BlastDamage(self, self:GetOwner(), pos, 300, 125)
|
||||
|
||||
sound.Play(diesound, pos, 100, 100)
|
||||
self:Remove()
|
||||
|
||||
|
||||
local effect = EffectData()
|
||||
effect:SetStart(pos)
|
||||
effect:SetOrigin(pos)
|
||||
util.Effect("Explosion", effect, true, true)
|
||||
else
|
||||
self.PunchRemaining = self.PunchRemaining - 1
|
||||
|
||||
if IsValid(self.PunchEntity) and IsValid(self.PunchEntity:GetPhysicsObject()) then
|
||||
local punchphys = self.PunchEntity:GetPhysicsObject()
|
||||
|
||||
|
||||
-- Make physexplosion
|
||||
local phexp = ents.Create("env_physexplosion")
|
||||
if IsValid(phexp) then
|
||||
phexp:SetPos(self:GetPos())
|
||||
phexp:SetKeyValue("magnitude", 100)
|
||||
phexp:SetKeyValue("radius", 128)
|
||||
phexp:SetKeyValue("spawnflags", 1 + 2)
|
||||
phexp:Spawn()
|
||||
phexp:Fire("Explode", "", 0)
|
||||
end
|
||||
|
||||
local norm = self:GetAngles():Up() * -1
|
||||
|
||||
-- Add speed to ourselves
|
||||
local base = 120
|
||||
local bonus = punchphys:GetMass() * 2
|
||||
|
||||
local vel = math.max(base * 2, base + bonus)
|
||||
|
||||
punchphys:AddVelocity(norm * vel)
|
||||
|
||||
util.BlastDamage(self, self:GetOwner(), self:GetPos(), 200, 50)
|
||||
|
||||
local effect = EffectData()
|
||||
effect:SetStart(self:GetPos())
|
||||
effect:SetOrigin(self:GetPos())
|
||||
effect:SetNormal(norm * -1)
|
||||
effect:SetRadius(16)
|
||||
effect:SetScale(1)
|
||||
util.Effect("ManhackSparks", effect, true, true)
|
||||
|
||||
sound.Play(punchsound, self:GetPos(), 80, 100)
|
||||
end
|
||||
end
|
||||
|
||||
local delay = math.max(0.1, self.PunchRemaining / self.PunchMax) * 3
|
||||
self:NextThink(CurTime() + delay)
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
302
gamemodes/terrortown/entities/entities/ttt_radio.lua
Normal file
302
gamemodes/terrortown/entities/entities/ttt_radio.lua
Normal file
@@ -0,0 +1,302 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
---- Radio equipment playing distraction sounds
|
||||
|
||||
AddCSLuaFile()
|
||||
|
||||
if CLIENT then
|
||||
-- this entity can be DNA-sampled so we need some display info
|
||||
ENT.Icon = "vgui/ttt/icon_radio"
|
||||
ENT.PrintName = "radio_name"
|
||||
end
|
||||
|
||||
ENT.Type = "anim"
|
||||
ENT.Model = Model("models/props/cs_office/radio.mdl")
|
||||
|
||||
ENT.CanUseKey = true
|
||||
ENT.CanHavePrints = false
|
||||
ENT.SoundLimit = 5
|
||||
ENT.SoundDelay = 0.5
|
||||
|
||||
function ENT:Initialize()
|
||||
self:SetModel(self.Model)
|
||||
|
||||
self:PhysicsInit(SOLID_VPHYSICS)
|
||||
self:SetMoveType(MOVETYPE_VPHYSICS)
|
||||
self:SetSolid(SOLID_VPHYSICS)
|
||||
self:SetCollisionGroup(COLLISION_GROUP_NONE)
|
||||
if SERVER then
|
||||
self:SetMaxHealth(40)
|
||||
end
|
||||
self:SetHealth(40)
|
||||
|
||||
if SERVER then
|
||||
self:SetUseType(SIMPLE_USE)
|
||||
end
|
||||
|
||||
-- Register with owner
|
||||
if CLIENT then
|
||||
if LocalPlayer() == self:GetOwner() then
|
||||
LocalPlayer().radio = self
|
||||
end
|
||||
end
|
||||
|
||||
self.SoundQueue = {}
|
||||
self.Playing = false
|
||||
self.fingerprints = {}
|
||||
end
|
||||
|
||||
function ENT:UseOverride(activator)
|
||||
if IsValid(activator) and activator:IsPlayer() and activator:IsActiveTraitor() then
|
||||
local prints = self.fingerprints or {}
|
||||
self:Remove()
|
||||
|
||||
local wep = activator:Give("weapon_ttt_radio")
|
||||
if IsValid(wep) then
|
||||
wep.fingerprints = wep.fingerprints or {}
|
||||
table.Add(wep.fingerprints, prints)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local zapsound = Sound("npc/assassin/ball_zap1.wav")
|
||||
function ENT:OnTakeDamage(dmginfo)
|
||||
self:TakePhysicsDamage(dmginfo)
|
||||
|
||||
self:SetHealth(self:Health() - dmginfo:GetDamage())
|
||||
if self:Health() < 0 then
|
||||
self:Remove()
|
||||
|
||||
local effect = EffectData()
|
||||
effect:SetOrigin(self:GetPos())
|
||||
util.Effect("cball_explode", effect)
|
||||
sound.Play(zapsound, self:GetPos())
|
||||
|
||||
if IsValid(self:GetOwner()) then
|
||||
LANG.Msg(self:GetOwner(), "radio_broken")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:OnRemove()
|
||||
if CLIENT then
|
||||
if LocalPlayer() == self:GetOwner() then
|
||||
LocalPlayer().radio = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:AddSound(snd)
|
||||
if #self.SoundQueue < self.SoundLimit then
|
||||
table.insert(self.SoundQueue, snd)
|
||||
end
|
||||
end
|
||||
|
||||
local simplesounds = {
|
||||
scream = {
|
||||
Sound("vo/npc/male01/pain07.wav"),
|
||||
Sound("vo/npc/male01/pain08.wav"),
|
||||
Sound("vo/npc/male01/pain09.wav"),
|
||||
Sound("vo/npc/male01/no02.wav")
|
||||
},
|
||||
explosion = {
|
||||
Sound("BaseExplosionEffect.Sound")
|
||||
}
|
||||
};
|
||||
|
||||
local serialsounds = {
|
||||
footsteps = {
|
||||
sound = {
|
||||
{Sound("player/footsteps/concrete1.wav"), Sound("player/footsteps/concrete2.wav")},
|
||||
{Sound("player/footsteps/concrete3.wav"), Sound("player/footsteps/concrete4.wav")}
|
||||
},
|
||||
times = {8, 16},
|
||||
delay = 0.35,
|
||||
ampl = 80
|
||||
},
|
||||
|
||||
burning = {
|
||||
sound = {
|
||||
Sound("General.BurningObject"),
|
||||
Sound("General.StopBurning")
|
||||
},
|
||||
times = {2, 2},
|
||||
delay = 4,
|
||||
},
|
||||
|
||||
|
||||
beeps = {
|
||||
sound = { Sound("weapons/c4/c4_beep1.wav") },
|
||||
delay = 0.75,
|
||||
times = {8, 12},
|
||||
ampl = 70
|
||||
}
|
||||
};
|
||||
|
||||
local gunsounds = {
|
||||
shotgun = {
|
||||
sound = Sound( "Weapon_XM1014.Single" ),
|
||||
delay = 0.8,
|
||||
times = {1, 3},
|
||||
burst = false
|
||||
},
|
||||
|
||||
pistol = {
|
||||
sound = Sound( "Weapon_FiveSeven.Single" ),
|
||||
delay = 0.4,
|
||||
times = {2, 4},
|
||||
burst = false
|
||||
},
|
||||
|
||||
mac10 = {
|
||||
sound = Sound( "Weapon_mac10.Single" ),
|
||||
delay = 0.065,
|
||||
times = {5, 10},
|
||||
burst = true
|
||||
},
|
||||
|
||||
deagle = {
|
||||
sound = Sound( "Weapon_Deagle.Single" ),
|
||||
delay = 0.6,
|
||||
times = {1, 3},
|
||||
burst = false
|
||||
},
|
||||
|
||||
m16 = {
|
||||
sound = Sound( "Weapon_M4A1.Single" ),
|
||||
delay = 0.2,
|
||||
times = {1, 5},
|
||||
burst = true
|
||||
},
|
||||
|
||||
rifle = {
|
||||
sound = Sound( "weapons/scout/scout_fire-1.wav" ),
|
||||
delay = 1.5,
|
||||
times = {1, 1},
|
||||
burst = false,
|
||||
ampl = 80
|
||||
},
|
||||
|
||||
huge = {
|
||||
sound = Sound( "Weapon_m249.Single" ),
|
||||
delay = 0.055,
|
||||
times = {6, 12},
|
||||
burst = true
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
function ENT:PlayDelayedSound(snd, ampl, last)
|
||||
-- maybe we can get destroyed while a timer is still up
|
||||
if IsValid(self) then
|
||||
if istable(snd) then
|
||||
snd = table.Random(snd)
|
||||
end
|
||||
|
||||
sound.Play(snd, self:GetPos(), ampl)
|
||||
self.Playing = not last
|
||||
|
||||
--print("Playing", snd, last)
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:PlaySound(snd)
|
||||
local pos = self:GetPos()
|
||||
local this = self
|
||||
if simplesounds[snd] then
|
||||
sound.Play(table.Random(simplesounds[snd]), pos)
|
||||
elseif gunsounds[snd] then
|
||||
local gunsound = gunsounds[snd]
|
||||
local times = math.random(gunsound.times[1], gunsound.times[2])
|
||||
local t = 0
|
||||
for i=1, times do
|
||||
timer.Simple(t,
|
||||
function()
|
||||
if IsValid(this) then
|
||||
this:PlayDelayedSound(gunsound.sound, gunsound.ampl or 90, (i == times))
|
||||
end
|
||||
end)
|
||||
if gunsound.burst then
|
||||
t = t + gunsound.delay
|
||||
else
|
||||
t = t + math.Rand(gunsound.delay, gunsound.delay * 2)
|
||||
end
|
||||
end
|
||||
elseif serialsounds[snd] then
|
||||
local serialsound = serialsounds[snd]
|
||||
local num = #serialsound.sound
|
||||
local times = math.random(serialsound.times[1], serialsound.times[2])
|
||||
local t = 0
|
||||
local idx = 1
|
||||
for i=1, times do
|
||||
local sound = serialsound.sound[idx]
|
||||
timer.Simple(t,
|
||||
function()
|
||||
if IsValid(this) then
|
||||
this:PlayDelayedSound(sound, serialsound.ampl or 75, (i == times))
|
||||
end
|
||||
end)
|
||||
|
||||
t = t + serialsound.delay
|
||||
idx = idx + 1
|
||||
if idx > num then idx = 1 end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local nextplay = 0
|
||||
function ENT:Think()
|
||||
if CurTime() > nextplay and #self.SoundQueue > 0 then
|
||||
if not self.Playing then
|
||||
local snd = table.remove(self.SoundQueue, 1)
|
||||
self:PlaySound(snd)
|
||||
end
|
||||
|
||||
-- always do this, makes timing work out a little better
|
||||
nextplay = CurTime() + self.SoundDelay
|
||||
end
|
||||
end
|
||||
|
||||
if SERVER then
|
||||
|
||||
local soundtypes = {
|
||||
"scream", "shotgun", "explosion",
|
||||
"pistol", "mac10", "deagle",
|
||||
"m16", "rifle", "huge",
|
||||
"burning", "beeps", "footsteps"
|
||||
};
|
||||
|
||||
|
||||
local function RadioCmd(ply, cmd, args)
|
||||
if not IsValid(ply) or not ply:IsActiveTraitor() then return end
|
||||
if not (#args == 2) then return end
|
||||
|
||||
local eidx = tonumber(args[1])
|
||||
local snd = tostring(args[2])
|
||||
if not eidx or not snd then return end
|
||||
|
||||
local radio = Entity(eidx)
|
||||
if not IsValid(radio) then return end
|
||||
if not (radio:GetOwner() == ply) then return end
|
||||
if not (radio:GetClass() == "ttt_radio") then return end
|
||||
|
||||
if not table.HasValue(soundtypes, snd) then
|
||||
print("Received radio sound not in table from", ply)
|
||||
return
|
||||
end
|
||||
|
||||
radio:AddSound(snd)
|
||||
end
|
||||
concommand.Add("ttt_radio_play", RadioCmd)
|
||||
end
|
||||
|
||||
|
||||
32
gamemodes/terrortown/entities/entities/ttt_random_ammo.lua
Normal file
32
gamemodes/terrortown/entities/entities/ttt_random_ammo.lua
Normal file
@@ -0,0 +1,32 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
---- Dummy ent that just spawns a random TTT ammo item and kills itself
|
||||
|
||||
ENT.Type = "point"
|
||||
ENT.Base = "base_point"
|
||||
|
||||
|
||||
function ENT:Initialize()
|
||||
local ammos = ents.TTT.GetSpawnableAmmo()
|
||||
|
||||
if ammos then
|
||||
local cls = ammos[math.random(#ammos)]
|
||||
local ent = ents.Create(cls)
|
||||
if IsValid(ent) then
|
||||
ent:SetPos(self:GetPos())
|
||||
ent:SetAngles(self:GetAngles())
|
||||
ent:Spawn()
|
||||
ent:PhysWake()
|
||||
end
|
||||
|
||||
self:Remove()
|
||||
end
|
||||
end
|
||||
52
gamemodes/terrortown/entities/entities/ttt_random_weapon.lua
Normal file
52
gamemodes/terrortown/entities/entities/ttt_random_weapon.lua
Normal file
@@ -0,0 +1,52 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
---- Dummy ent that just spawns a random TTT weapon and kills itself
|
||||
|
||||
ENT.Type = "point"
|
||||
ENT.Base = "base_point"
|
||||
|
||||
ENT.AutoAmmo = 0
|
||||
|
||||
function ENT:KeyValue(key, value)
|
||||
if key == "auto_ammo" then
|
||||
self.AutoAmmo = tonumber(value)
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:Initialize()
|
||||
local weps = ents.TTT.GetSpawnableSWEPs()
|
||||
if weps then
|
||||
local w = weps[math.random(#weps)]
|
||||
local ent = ents.Create(WEPS.GetClass(w))
|
||||
if IsValid(ent) then
|
||||
local pos = self:GetPos()
|
||||
ent:SetPos(pos)
|
||||
ent:SetAngles(self:GetAngles())
|
||||
ent:Spawn()
|
||||
ent:PhysWake()
|
||||
|
||||
if ent.AmmoEnt and self.AutoAmmo > 0 then
|
||||
for i=1, self.AutoAmmo do
|
||||
local ammo = ents.Create(ent.AmmoEnt)
|
||||
if IsValid(ammo) then
|
||||
pos.z = pos.z + 3 -- shift every box up a bit
|
||||
ammo:SetPos(pos)
|
||||
ammo:SetAngles(VectorRand():Angle())
|
||||
ammo:Spawn()
|
||||
ammo:PhysWake()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
self:Remove()
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,99 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
|
||||
AddCSLuaFile()
|
||||
|
||||
ENT.Type = "anim"
|
||||
ENT.Base = "ttt_basegrenade_proj"
|
||||
ENT.Model = Model("models/weapons/w_eq_smokegrenade_thrown.mdl")
|
||||
|
||||
|
||||
AccessorFunc( ENT, "radius", "Radius", FORCE_NUMBER )
|
||||
|
||||
function ENT:Initialize()
|
||||
if not self:GetRadius() then self:SetRadius(20) end
|
||||
|
||||
return self.BaseClass.Initialize(self)
|
||||
end
|
||||
|
||||
if CLIENT then
|
||||
|
||||
local smokeparticles = {
|
||||
Model("particle/particle_smokegrenade"),
|
||||
Model("particle/particle_noisesphere")
|
||||
};
|
||||
|
||||
function ENT:CreateSmoke(center)
|
||||
local em = ParticleEmitter(center)
|
||||
|
||||
local r = self:GetRadius()
|
||||
for i=1, 20 do
|
||||
local prpos = VectorRand() * r
|
||||
prpos.z = prpos.z + 32
|
||||
local p = em:Add(table.Random(smokeparticles), center + prpos)
|
||||
if p then
|
||||
local gray = math.random(75, 200)
|
||||
p:SetColor(gray, gray, gray)
|
||||
p:SetStartAlpha(255)
|
||||
p:SetEndAlpha(200)
|
||||
p:SetVelocity(VectorRand() * math.Rand(900, 1300))
|
||||
p:SetLifeTime(0)
|
||||
|
||||
p:SetDieTime(math.Rand(50, 70))
|
||||
|
||||
p:SetStartSize(math.random(140, 150))
|
||||
p:SetEndSize(math.random(1, 40))
|
||||
p:SetRoll(math.random(-180, 180))
|
||||
p:SetRollDelta(math.Rand(-0.1, 0.1))
|
||||
p:SetAirResistance(600)
|
||||
|
||||
p:SetCollide(true)
|
||||
p:SetBounce(0.4)
|
||||
|
||||
p:SetLighting(false)
|
||||
end
|
||||
end
|
||||
|
||||
em:Finish()
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:Explode(tr)
|
||||
if SERVER then
|
||||
self:SetNoDraw(true)
|
||||
self:SetSolid(SOLID_NONE)
|
||||
|
||||
-- pull out of the surface
|
||||
if tr.Fraction != 1.0 then
|
||||
self:SetPos(tr.HitPos + tr.HitNormal * 0.6)
|
||||
end
|
||||
|
||||
local pos = self:GetPos()
|
||||
|
||||
self:Remove()
|
||||
else
|
||||
local spos = self:GetPos()
|
||||
local trs = util.TraceLine({start=spos + Vector(0,0,64), endpos=spos + Vector(0,0,-128), filter=self})
|
||||
util.Decal("SmallScorch", trs.HitPos + trs.HitNormal, trs.HitPos - trs.HitNormal)
|
||||
|
||||
self:SetDetonateExact(0)
|
||||
|
||||
if tr.Fraction != 1.0 then
|
||||
spos = tr.HitPos + tr.HitNormal * 0.6
|
||||
end
|
||||
|
||||
-- Smoke particles can't get cleaned up when a round restarts, so prevent
|
||||
-- them from existing post-round.
|
||||
if GetRoundState() == ROUND_POST then return end
|
||||
|
||||
self:CreateSmoke(spos)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,13 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
|
||||
ENT.Type = "point"
|
||||
ENT.Base = "base_point"
|
||||
@@ -0,0 +1,138 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
AddCSLuaFile("shared.lua")
|
||||
include("shared.lua")
|
||||
|
||||
-- serverside only
|
||||
ENT.RemoveOnPress = false
|
||||
|
||||
ENT.Model = Model("models/weapons/w_bugbait.mdl")
|
||||
|
||||
function ENT:Initialize()
|
||||
self:SetModel(self.Model)
|
||||
|
||||
self:SetNoDraw(true)
|
||||
self:DrawShadow(false)
|
||||
self:SetSolid(SOLID_NONE)
|
||||
self:SetMoveType(MOVETYPE_NONE)
|
||||
|
||||
self:SetDelay(self.RawDelay or 1)
|
||||
|
||||
if self:GetDelay() < 0 then
|
||||
-- func_button can be made single use by setting delay to be negative, so
|
||||
-- mimic that here
|
||||
self.RemoveOnPress = true
|
||||
end
|
||||
|
||||
if self.RemoveOnPress then
|
||||
self:SetDelay(-1) -- tells client we're single use
|
||||
end
|
||||
|
||||
if self:GetUsableRange() < 1 then
|
||||
self:SetUsableRange(1024)
|
||||
end
|
||||
|
||||
self:SetNextUseTime(0)
|
||||
self:SetLocked(self:HasSpawnFlags(2048))
|
||||
|
||||
self:SetDescription(self.RawDescription or "?")
|
||||
|
||||
self.RawDelay = nil
|
||||
self.RawDescription = nil
|
||||
end
|
||||
|
||||
function ENT:KeyValue(key, value)
|
||||
if key == "OnPressed" then
|
||||
self:StoreOutput(key, value)
|
||||
elseif key == "wait" then -- as Delay Before Reset in func_button
|
||||
self.RawDelay = tonumber(value)
|
||||
elseif key == "description" then
|
||||
self.RawDescription = tostring(value)
|
||||
|
||||
if self.RawDescription and string.len(self.RawDescription) < 1 then
|
||||
self.RawDescription = nil
|
||||
end
|
||||
elseif key == "RemoveOnPress" then
|
||||
self[key] = tobool(value)
|
||||
else
|
||||
self:SetNetworkKeyValue(key, value)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function ENT:AcceptInput(name, activator)
|
||||
if name == "Toggle" then
|
||||
self:SetLocked(not self:GetLocked())
|
||||
return true
|
||||
elseif name == "Hide" or name == "Lock" then
|
||||
self:SetLocked(true)
|
||||
return true
|
||||
elseif name == "Unhide" or name == "Unlock" then
|
||||
self:SetLocked(false)
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
function GAMEMODE:TTTCanUseTraitorButton(ent, ply)
|
||||
-- Can be used to prevent players from using this button.
|
||||
-- Return a boolean and a message that can shows up if you can't use the button.
|
||||
-- Example: return false, "Not allowed".
|
||||
return true
|
||||
end
|
||||
|
||||
function ENT:TraitorUse(ply)
|
||||
if not (IsValid(ply) and ply:IsActiveTraitor()) then return false end
|
||||
if not self:IsUsable() then return false end
|
||||
|
||||
if self:GetPos():Distance(ply:GetPos()) > self:GetUsableRange() then return false end
|
||||
|
||||
local use, message = hook.Run("TTTCanUseTraitorButton", self, ply)
|
||||
if not use then
|
||||
if message then TraitorMsg(ply, message) end
|
||||
return false
|
||||
end
|
||||
|
||||
net.Start("TTT_ConfirmUseTButton") net.Send(ply)
|
||||
|
||||
-- send output to all entities linked to us
|
||||
self:TriggerOutput("OnPressed", ply)
|
||||
|
||||
if self.RemoveOnPress then
|
||||
self:SetLocked(true)
|
||||
self:Remove()
|
||||
else
|
||||
-- lock ourselves until we should be usable again
|
||||
self:SetNextUseTime(CurTime() + self:GetDelay())
|
||||
end
|
||||
|
||||
hook.Run("TTTTraitorButtonActivated", self, ply)
|
||||
return true
|
||||
end
|
||||
|
||||
-- Fix for traitor buttons having awkward init/render behavior, in the event that a map has been optimized with area portals.
|
||||
function ENT:UpdateTransmitState()
|
||||
return TRANSMIT_ALWAYS
|
||||
end
|
||||
|
||||
local function TraitorUseCmd(ply, cmd, args)
|
||||
if #args != 1 then return end
|
||||
|
||||
if IsValid(ply) and ply:IsActiveTraitor() then
|
||||
local idx = tonumber(args[1])
|
||||
if idx then
|
||||
local ent = Entity(idx)
|
||||
if IsValid(ent) and ent:GetClass() == "ttt_traitor_button" and ent.TraitorUse then
|
||||
ent:TraitorUse(ply)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
concommand.Add("ttt_use_tbutton", TraitorUseCmd)
|
||||
@@ -0,0 +1,26 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
--- Special button only for traitors and usable from range
|
||||
|
||||
ENT.Type = "anim"
|
||||
ENT.Base = "base_anim"
|
||||
|
||||
function ENT:SetupDataTables()
|
||||
self:NetworkVar("Float", 0, "Delay")
|
||||
self:NetworkVar("Float", 1, "NextUseTime")
|
||||
self:NetworkVar("Bool", 0, "Locked")
|
||||
self:NetworkVar("String", 0, "Description")
|
||||
self:NetworkVar("Int", 0, "UsableRange", {KeyName = "UsableRange"})
|
||||
end
|
||||
|
||||
function ENT:IsUsable()
|
||||
return (not self:GetLocked()) and self:GetNextUseTime() < CurTime()
|
||||
end
|
||||
53
gamemodes/terrortown/entities/entities/ttt_traitor_check.lua
Normal file
53
gamemodes/terrortown/entities/entities/ttt_traitor_check.lua
Normal file
@@ -0,0 +1,53 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
|
||||
ENT.Type = "brush"
|
||||
ENT.Base = "base_brush"
|
||||
|
||||
function ENT:KeyValue(key, value)
|
||||
if key == "TraitorsFound" then
|
||||
-- this is our output, so handle it as such
|
||||
self:StoreOutput(key, value)
|
||||
end
|
||||
end
|
||||
|
||||
local function VectorInside(vec, mins, maxs)
|
||||
return (vec.x > mins.x and vec.x < maxs.x
|
||||
and vec.y > mins.y and vec.y < maxs.y
|
||||
and vec.z > mins.z and vec.z < maxs.z)
|
||||
end
|
||||
|
||||
function ENT:CountTraitors()
|
||||
local mins = self:LocalToWorld(self:OBBMins())
|
||||
local maxs = self:LocalToWorld(self:OBBMaxs())
|
||||
|
||||
local trs = 0
|
||||
for _,ply in player.Iterator() do
|
||||
if IsValid(ply) and ply:IsActiveTraitor() and ply:Alive() then
|
||||
local pos = ply:GetPos()
|
||||
if VectorInside(pos, mins, maxs) then
|
||||
trs = trs + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return trs
|
||||
end
|
||||
|
||||
function ENT:AcceptInput(name, activator, caller)
|
||||
if name == "CheckForTraitor" then
|
||||
local traitors = self:CountTraitors()
|
||||
|
||||
self:TriggerOutput("TraitorsFound", activator, traitors)
|
||||
|
||||
return true
|
||||
end
|
||||
end
|
||||
125
gamemodes/terrortown/entities/entities/ttt_weapon_check.lua
Normal file
125
gamemodes/terrortown/entities/entities/ttt_weapon_check.lua
Normal file
@@ -0,0 +1,125 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
|
||||
ENT.Type = "brush"
|
||||
ENT.Base = "base_brush"
|
||||
|
||||
function ENT:KeyValue(key, value)
|
||||
if key == "WeaponsFound" then
|
||||
-- this is our output, so handle it as such
|
||||
self:StoreOutput(key, value)
|
||||
end
|
||||
end
|
||||
|
||||
local function VectorInside(vec, mins, maxs)
|
||||
return (vec.x > mins.x and vec.x < maxs.x
|
||||
and vec.y > mins.y and vec.y < maxs.y
|
||||
and vec.z > mins.z and vec.z < maxs.z)
|
||||
end
|
||||
|
||||
-- We use stuff from weaponry.lua here, like weapon types
|
||||
|
||||
local function HasWeaponType(ply, weptype)
|
||||
for _, wep in ipairs(ply:GetWeapons()) do
|
||||
if IsValid(wep) and WEPS.TypeForWeapon(wep:GetClass()) == weptype then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function HasPrimary(ply)
|
||||
return HasWeaponType(ply, WEAPON_HEAVY)
|
||||
end
|
||||
|
||||
local function HasSecondary(ply)
|
||||
return HasWeaponType(ply, WEAPON_PISTOL)
|
||||
end
|
||||
|
||||
local function HasEquipment(ply)
|
||||
return ply:HasEquipment()
|
||||
end
|
||||
|
||||
local function HasNade(ply)
|
||||
return HasWeaponType(ply, WEAPON_NADE)
|
||||
end
|
||||
|
||||
local function HasAny(ply)
|
||||
return HasPrimary(ply) or HasSecondary(ply) or HasEquipment(ply) or HasNade(ply)
|
||||
end
|
||||
|
||||
local function HasNamed(name)
|
||||
return function(ply)
|
||||
return ply:HasWeapon(name)
|
||||
end
|
||||
end
|
||||
|
||||
local checkers = {
|
||||
[1] = HasPrimary,
|
||||
[2] = HasSecondary,
|
||||
[3] = HasEquipment,
|
||||
[4] = HasNade,
|
||||
[5] = HasAny
|
||||
};
|
||||
|
||||
function ENT:GetWeaponChecker(check)
|
||||
if isstring(check) then
|
||||
return HasNamed(check)
|
||||
else
|
||||
return checkers[check]
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:TestWeapons(weptype)
|
||||
local mins = self:LocalToWorld(self:OBBMins())
|
||||
local maxs = self:LocalToWorld(self:OBBMaxs())
|
||||
|
||||
local check = self:GetWeaponChecker(weptype)
|
||||
|
||||
if check == nil then
|
||||
ErrorNoHalt("ttt_weapon_check: invalid parameter\n")
|
||||
return 0
|
||||
end
|
||||
|
||||
for _,ply in player.Iterator() do
|
||||
if IsValid(ply) and ply:IsTerror() then
|
||||
local pos = ply:GetPos()
|
||||
local center = ply:LocalToWorld(ply:OBBCenter())
|
||||
if VectorInside(pos, mins, maxs) or VectorInside(center, mins, maxs) then
|
||||
if check(ply) then
|
||||
return 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return 0
|
||||
end
|
||||
|
||||
function ENT:AcceptInput(name, activator, caller, data)
|
||||
if name == "CheckForType" or name == "CheckForClass" then
|
||||
local weptype = tonumber(data)
|
||||
local wepname = tostring(data)
|
||||
|
||||
if not weptype and not wepname then
|
||||
ErrorNoHalt("ttt_weapon_check: Invalid parameter to CheckForWeapons input!\n")
|
||||
return false
|
||||
end
|
||||
|
||||
local weapons = self:TestWeapons(weptype or wepname)
|
||||
|
||||
self:TriggerOutput("WeaponsFound", activator, weapons)
|
||||
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
23
gamemodes/terrortown/entities/entities/ttt_win.lua
Normal file
23
gamemodes/terrortown/entities/entities/ttt_win.lua
Normal file
@@ -0,0 +1,23 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
|
||||
ENT.Type = "point"
|
||||
ENT.Base = "base_point"
|
||||
|
||||
function ENT:AcceptInput(name, activator, caller)
|
||||
if name == "TraitorWin" then
|
||||
GAMEMODE:MapTriggeredEnd(WIN_TRAITOR)
|
||||
return true
|
||||
elseif name == "InnocentWin" then
|
||||
GAMEMODE:MapTriggeredEnd(WIN_INNOCENT)
|
||||
return true
|
||||
end
|
||||
end
|
||||
227
gamemodes/terrortown/entities/weapons/weapon_ttt_beacon.lua
Normal file
227
gamemodes/terrortown/entities/weapons/weapon_ttt_beacon.lua
Normal file
@@ -0,0 +1,227 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
|
||||
-- DISABLED: Beacons are meant to signal locations. In practice no one uses
|
||||
-- them. I'm leaving the weapon in in case I think of a way to make it useful,
|
||||
-- or if you want to experiment with it. Uncomment the two lines that say
|
||||
-- "DISABLED" above them (ie. the AddCSLuaFile and SWEP.CanBuy) and the weapon will
|
||||
-- appear in the detective's equipment menu. Do the same for the ttt_beacon
|
||||
-- entity to make it all work.
|
||||
|
||||
--DISABLED
|
||||
--AddCSLuaFile()
|
||||
|
||||
SWEP.HoldType = "normal"
|
||||
|
||||
if CLIENT then
|
||||
SWEP.PrintName = "Beacon"
|
||||
SWEP.Slot = 6
|
||||
|
||||
SWEP.ViewModelFlip = false
|
||||
SWEP.ViewModelFOV = 10
|
||||
|
||||
SWEP.EquipMenuData = {
|
||||
type="Weapon",
|
||||
model="models/props_lab/reciever01b.mdl",
|
||||
desc="Broadcasts a location to everyone.\n\nUse to warn or group innocents."
|
||||
};
|
||||
|
||||
SWEP.Icon = "vgui/ttt/icon_beacon"
|
||||
end
|
||||
|
||||
SWEP.Base = "weapon_tttbase"
|
||||
|
||||
SWEP.ViewModel = "models/weapons/v_crowbar.mdl"
|
||||
SWEP.WorldModel = "models/props_lab/reciever01b.mdl"
|
||||
|
||||
SWEP.Primary.ClipSize = 3
|
||||
SWEP.Primary.DefaultClip = 1
|
||||
SWEP.Primary.Automatic = true
|
||||
SWEP.Primary.Ammo = "slam"
|
||||
SWEP.Primary.Delay = 1.0
|
||||
|
||||
SWEP.Secondary.ClipSize = -1
|
||||
SWEP.Secondary.DefaultClip = -1
|
||||
SWEP.Secondary.Automatic = true
|
||||
SWEP.Secondary.Ammo = "none"
|
||||
SWEP.Secondary.Delay = 1.0
|
||||
|
||||
SWEP.Kind = WEAPON_EQUIP
|
||||
-- DISABLED
|
||||
--SWEP.CanBuy = {ROLE_DETECTIVE} -- only detectives can buy
|
||||
SWEP.LimitedStock = true -- only buyable once
|
||||
SWEP.WeaponID = AMMO_BEACON
|
||||
|
||||
SWEP.Spawnable = true
|
||||
SWEP.AllowDrop = false
|
||||
SWEP.NoSights = true
|
||||
|
||||
function SWEP:OnDrop()
|
||||
self:Remove()
|
||||
end
|
||||
|
||||
function SWEP:PrimaryAttack()
|
||||
self:SetNextPrimaryFire( CurTime() + self.Primary.Delay )
|
||||
|
||||
if self:CanPrimaryAttack() then
|
||||
self:BeaconDrop()
|
||||
end
|
||||
end
|
||||
function SWEP:SecondaryAttack()
|
||||
self:SetNextSecondaryFire( CurTime() + self.Secondary.Delay )
|
||||
|
||||
if self:CanPrimaryAttack() then
|
||||
self:BeaconStick()
|
||||
end
|
||||
end
|
||||
|
||||
local throwsound = Sound( "Weapon_SLAM.SatchelThrow" )
|
||||
|
||||
-- might be able to move this drop/stick stuff into something more general now
|
||||
-- that a number of weapons use it
|
||||
function SWEP:BeaconDrop()
|
||||
if SERVER then
|
||||
local ply = self:GetOwner()
|
||||
if not IsValid(ply) then return end
|
||||
|
||||
if self.Planted then return end
|
||||
|
||||
local vsrc = ply:GetShootPos()
|
||||
local vang = ply:GetAimVector()
|
||||
local vvel = ply:GetVelocity()
|
||||
|
||||
local vthrow = vvel + vang * 200
|
||||
|
||||
local beacon = ents.Create("ttt_beacon")
|
||||
if IsValid(beacon) then
|
||||
beacon:SetPos(vsrc + vang * 10)
|
||||
beacon:SetOwner(ply)
|
||||
beacon:Spawn()
|
||||
|
||||
beacon:PointAtEntity(ply)
|
||||
|
||||
local ang = beacon:GetAngles()
|
||||
ang:RotateAroundAxis(ang:Right(), 90)
|
||||
beacon:SetAngles(ang)
|
||||
|
||||
beacon:PhysWake()
|
||||
local phys = beacon:GetPhysicsObject()
|
||||
if IsValid(phys) then
|
||||
phys:SetVelocity(vthrow)
|
||||
end
|
||||
|
||||
self:PlacedBeacon()
|
||||
end
|
||||
end
|
||||
|
||||
self:EmitSound(throwsound)
|
||||
end
|
||||
|
||||
function SWEP:BeaconStick()
|
||||
if SERVER then
|
||||
local ply = self:GetOwner()
|
||||
if not IsValid(ply) then return end
|
||||
|
||||
if self.Planted then return end
|
||||
|
||||
local ignore = {ply, self}
|
||||
local spos = ply:GetShootPos()
|
||||
local epos = spos + ply:GetAimVector() * 80
|
||||
local tr = util.TraceLine({start=spos, endpos=epos, filter=ignore, mask=MASK_SOLID})
|
||||
|
||||
if tr.HitWorld then
|
||||
local beacon = ents.Create("ttt_beacon")
|
||||
if IsValid(beacon) then
|
||||
beacon:PointAtEntity(ply)
|
||||
|
||||
local tr_ent = util.TraceEntity({start=spos, endpos=epos, filter=ignore, mask=MASK_SOLID}, beacon)
|
||||
|
||||
if tr_ent.HitWorld then
|
||||
|
||||
local ang = tr_ent.HitNormal:Angle()
|
||||
--ang:RotateAroundAxis(ang:Right(), -90)
|
||||
--ang:RotateAroundAxis(ang:Up(), -180)
|
||||
--ang:RotateAroundAxis(ang:Forward(), 90)
|
||||
|
||||
beacon:SetPos(tr_ent.HitPos + ang:Forward() * 2.5)
|
||||
beacon:SetAngles(ang)
|
||||
beacon:SetOwner(ply)
|
||||
beacon:Spawn()
|
||||
|
||||
local phys = beacon:GetPhysicsObject()
|
||||
if IsValid(phys) then
|
||||
phys:EnableMotion(false)
|
||||
end
|
||||
|
||||
beacon.IsOnWall = true
|
||||
|
||||
self:PlacedBeacon()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:PlacedBeacon()
|
||||
self:TakePrimaryAmmo(1)
|
||||
|
||||
if not self:CanPrimaryAttack() then
|
||||
self:Remove()
|
||||
|
||||
self.Planted = true
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:PickupBeacon()
|
||||
if self:Clip1() >= self.Primary.ClipSize then
|
||||
return false
|
||||
else
|
||||
self:SetClip1(self:Clip1() + 1)
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
-- Ammo hackery after getting bought
|
||||
function SWEP:WasBought(buyer)
|
||||
self:SetClip1(self:Clip1() + 2)
|
||||
end
|
||||
|
||||
function SWEP:Reload()
|
||||
return false
|
||||
end
|
||||
|
||||
function SWEP:OnRemove()
|
||||
if CLIENT and IsValid(self:GetOwner()) and self:GetOwner() == LocalPlayer() and self:GetOwner():IsTerror() then
|
||||
RunConsoleCommand("lastinv")
|
||||
end
|
||||
end
|
||||
|
||||
if CLIENT then
|
||||
function SWEP:Initialize()
|
||||
self:AddHUDHelp("Click to place the beacon")
|
||||
|
||||
return self.BaseClass.Initialize(self)
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:Deploy()
|
||||
self:GetOwner():DrawViewModel(false)
|
||||
return true
|
||||
end
|
||||
|
||||
function SWEP:DrawWorldModel()
|
||||
if not IsValid(self:GetOwner()) then
|
||||
self:DrawModel()
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:DrawWorldModelTranslucent()
|
||||
end
|
||||
256
gamemodes/terrortown/entities/weapons/weapon_ttt_binoculars.lua
Normal file
256
gamemodes/terrortown/entities/weapons/weapon_ttt_binoculars.lua
Normal file
@@ -0,0 +1,256 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
AddCSLuaFile()
|
||||
|
||||
DEFINE_BASECLASS "weapon_tttbase"
|
||||
|
||||
SWEP.HoldType = "normal"
|
||||
|
||||
if CLIENT then
|
||||
SWEP.PrintName = "binoc_name"
|
||||
SWEP.Slot = 7
|
||||
|
||||
SWEP.ViewModelFOV = 10
|
||||
SWEP.ViewModelFlip = false
|
||||
SWEP.DrawCrosshair = false
|
||||
|
||||
SWEP.EquipMenuData = {
|
||||
type = "item_weapon",
|
||||
desc = "binoc_desc"
|
||||
};
|
||||
|
||||
SWEP.Icon = "vgui/ttt/icon_binoc"
|
||||
end
|
||||
|
||||
SWEP.Base = "weapon_tttbase"
|
||||
|
||||
SWEP.ViewModel = "models/weapons/v_crowbar.mdl"
|
||||
SWEP.WorldModel = "models/props/cs_office/paper_towels.mdl"
|
||||
|
||||
SWEP.Primary.ClipSize = -1
|
||||
SWEP.Primary.DefaultClip = -1
|
||||
SWEP.Primary.Automatic = false
|
||||
SWEP.Primary.Ammo = "none"
|
||||
SWEP.Primary.Delay = 1.0
|
||||
|
||||
SWEP.Secondary.ClipSize = -1
|
||||
SWEP.Secondary.DefaultClip = -1
|
||||
SWEP.Secondary.Automatic = true
|
||||
SWEP.Secondary.Ammo = "none"
|
||||
SWEP.Secondary.Delay = 0.2
|
||||
|
||||
SWEP.Kind = WEAPON_EQUIP2
|
||||
SWEP.CanBuy = {ROLE_DETECTIVE} -- only detectives can buy
|
||||
SWEP.WeaponID = AMMO_BINOCULARS
|
||||
|
||||
SWEP.AllowDrop = true
|
||||
|
||||
SWEP.ZoomLevels = {
|
||||
0,
|
||||
30,
|
||||
20,
|
||||
10
|
||||
};
|
||||
|
||||
SWEP.ProcessingDelay = 5
|
||||
|
||||
function SWEP:SetupDataTables()
|
||||
self:DTVar("Bool", 0, "processing")
|
||||
self:DTVar("Float", 0, "start_time")
|
||||
self:DTVar("Int", 1, "zoom")
|
||||
|
||||
return self.BaseClass.SetupDataTables(self)
|
||||
end
|
||||
|
||||
|
||||
function SWEP:PrimaryAttack()
|
||||
self:SetNextPrimaryFire( CurTime() + 0.1 )
|
||||
|
||||
if self:IsTargetingCorpse() and not self.dt.processing then
|
||||
self:SetNextPrimaryFire( CurTime() + self.Primary.Delay )
|
||||
|
||||
if SERVER then
|
||||
self.dt.processing = true
|
||||
self.dt.start_time = CurTime()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local click = Sound("weapons/sniper/sniper_zoomin.wav")
|
||||
function SWEP:SecondaryAttack()
|
||||
self:SetNextSecondaryFire( CurTime() + self.Secondary.Delay )
|
||||
|
||||
if CLIENT and IsFirstTimePredicted() then
|
||||
LocalPlayer():EmitSound(click)
|
||||
end
|
||||
|
||||
self:CycleZoom()
|
||||
|
||||
self.dt.processing = false
|
||||
self.dt.start_time = 0
|
||||
end
|
||||
|
||||
function SWEP:SetZoom(level)
|
||||
if SERVER then
|
||||
self.dt.zoom = level
|
||||
self:GetOwner():SetFOV(self.ZoomLevels[level], 0.3)
|
||||
|
||||
self:GetOwner():DrawViewModel(false)
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:CycleZoom()
|
||||
self.dt.zoom = self.dt.zoom + 1
|
||||
if not self.ZoomLevels[self.dt.zoom] then
|
||||
self.dt.zoom = 1
|
||||
end
|
||||
|
||||
self:SetZoom(self.dt.zoom)
|
||||
end
|
||||
|
||||
function SWEP:PreDrop()
|
||||
self:SetZoom(1)
|
||||
self.dt.processing = false
|
||||
return self.BaseClass.PreDrop(self)
|
||||
end
|
||||
|
||||
function SWEP:Holster()
|
||||
self:SetZoom(1)
|
||||
self.dt.processing = false
|
||||
return true
|
||||
end
|
||||
|
||||
function SWEP:Deploy()
|
||||
if SERVER and IsValid(self:GetOwner()) then
|
||||
self:GetOwner():DrawViewModel(false)
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
function SWEP:Reload()
|
||||
return false
|
||||
end
|
||||
|
||||
function SWEP:IsTargetingCorpse()
|
||||
local tr = self:GetOwner():GetEyeTrace(MASK_SHOT)
|
||||
local ent = tr.Entity
|
||||
|
||||
return (IsValid(ent) and ent:GetClass() == "prop_ragdoll" and
|
||||
CORPSE.GetPlayerNick(ent, false) != false)
|
||||
end
|
||||
|
||||
local confirm = Sound("npc/turret_floor/click1.wav")
|
||||
function SWEP:IdentifyCorpse()
|
||||
if SERVER then
|
||||
local tr = self:GetOwner():GetEyeTrace(MASK_SHOT)
|
||||
CORPSE.ShowSearch(self:GetOwner(), tr.Entity, false, true)
|
||||
elseif IsFirstTimePredicted() then
|
||||
LocalPlayer():EmitSound(confirm)
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:Think()
|
||||
BaseClass.Think(self)
|
||||
if self.dt.processing then
|
||||
if self:IsTargetingCorpse() then
|
||||
if CurTime() > (self.dt.start_time + self.ProcessingDelay) then
|
||||
self:IdentifyCorpse()
|
||||
|
||||
self.dt.processing = false
|
||||
self.dt.start_time = 0
|
||||
end
|
||||
else
|
||||
self.dt.processing = false
|
||||
self.dt.start_time = 0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function SWEP:OnRemove()
|
||||
if CLIENT and IsValid(self:GetOwner()) and self:GetOwner() == LocalPlayer() and self:GetOwner():Alive() then
|
||||
RunConsoleCommand("lastinv")
|
||||
end
|
||||
end
|
||||
|
||||
if CLIENT then
|
||||
function SWEP:Initialize()
|
||||
self:AddHUDHelp("binoc_help_pri", "binoc_help_sec", true)
|
||||
|
||||
return self.BaseClass.Initialize(self)
|
||||
end
|
||||
|
||||
local T = LANG.GetTranslation
|
||||
function SWEP:DrawHUD()
|
||||
self:DrawHelp()
|
||||
|
||||
local length = 40
|
||||
local gap = 10
|
||||
|
||||
local corpse = self:IsTargetingCorpse()
|
||||
|
||||
if corpse then
|
||||
surface.SetDrawColor(0, 255, 0, 255)
|
||||
gap = 4
|
||||
length = 40
|
||||
else
|
||||
surface.SetDrawColor(0, 255, 0, 200)
|
||||
end
|
||||
|
||||
local x = ScrW() / 2.0
|
||||
local y = ScrH() / 2.0
|
||||
|
||||
surface.DrawLine( x - length, y, x - gap, y )
|
||||
surface.DrawLine( x + length, y, x + gap, y )
|
||||
surface.DrawLine( x, y - length, x, y - gap )
|
||||
surface.DrawLine( x, y + length, x, y + gap )
|
||||
|
||||
|
||||
surface.SetFont("DefaultFixedDropShadow")
|
||||
surface.SetTextColor(0, 255, 0, 200)
|
||||
surface.SetTextPos( x + length, y - length )
|
||||
surface.DrawText(T("binoc_zoom_level") .. " " .. self.dt.zoom)
|
||||
|
||||
if corpse then
|
||||
surface.SetTextPos( x + length, y - length + 15)
|
||||
surface.DrawText(T("binoc_body"))
|
||||
end
|
||||
|
||||
if self.dt.processing then
|
||||
y = y + (y / 2)
|
||||
|
||||
local w, h = 200, 20
|
||||
|
||||
surface.SetDrawColor(0, 255, 0, 255)
|
||||
surface.DrawOutlinedRect(x - w/2, y - h, w, h)
|
||||
|
||||
surface.SetDrawColor(0, 255, 0, 180)
|
||||
local pct = math.Clamp((CurTime() - self.dt.start_time) / self.ProcessingDelay, 0, 1)
|
||||
surface.DrawRect(x - w/2, y - h, w * pct, h)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function SWEP:DrawWorldModel()
|
||||
if not IsValid(self:GetOwner()) then
|
||||
self:DrawModel()
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:AdjustMouseSensitivity()
|
||||
if self.dt.zoom > 0 then
|
||||
return 1 / self.dt.zoom
|
||||
end
|
||||
return -1
|
||||
end
|
||||
end
|
||||
|
||||
182
gamemodes/terrortown/entities/weapons/weapon_ttt_c4.lua
Normal file
182
gamemodes/terrortown/entities/weapons/weapon_ttt_c4.lua
Normal file
@@ -0,0 +1,182 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
|
||||
-- traitor equipment: c4 bomb
|
||||
|
||||
AddCSLuaFile()
|
||||
|
||||
SWEP.HoldType = "slam"
|
||||
|
||||
if CLIENT then
|
||||
SWEP.PrintName = "C4"
|
||||
SWEP.Slot = 6
|
||||
|
||||
SWEP.ViewModelFlip = false
|
||||
SWEP.ViewModelFOV = 54
|
||||
SWEP.DrawCrosshair = false
|
||||
|
||||
SWEP.EquipMenuData = {
|
||||
type = "item_weapon",
|
||||
name = "C4",
|
||||
desc = "c4_desc"
|
||||
};
|
||||
|
||||
SWEP.Icon = "vgui/ttt/icon_c4"
|
||||
SWEP.IconLetter = "I"
|
||||
end
|
||||
|
||||
SWEP.Base = "weapon_tttbase"
|
||||
|
||||
SWEP.Kind = WEAPON_EQUIP
|
||||
SWEP.CanBuy = {ROLE_TRAITOR} -- only traitors can buy
|
||||
SWEP.WeaponID = AMMO_C4
|
||||
|
||||
SWEP.UseHands = true
|
||||
SWEP.ViewModel = Model("models/weapons/cstrike/c_c4.mdl")
|
||||
SWEP.WorldModel = Model("models/weapons/w_c4.mdl")
|
||||
|
||||
SWEP.Primary.ClipSize = -1
|
||||
SWEP.Primary.DefaultClip = -1
|
||||
SWEP.Primary.Automatic = true
|
||||
SWEP.Primary.Ammo = "none"
|
||||
SWEP.Primary.Delay = 5.0
|
||||
|
||||
SWEP.Secondary.ClipSize = -1
|
||||
SWEP.Secondary.DefaultClip = -1
|
||||
SWEP.Secondary.Automatic = true
|
||||
SWEP.Secondary.Ammo = "none"
|
||||
SWEP.Secondary.Delay = 1.0
|
||||
|
||||
SWEP.NoSights = true
|
||||
|
||||
local throwsound = Sound( "Weapon_SLAM.SatchelThrow" )
|
||||
|
||||
function SWEP:PrimaryAttack()
|
||||
self:SetNextPrimaryFire( CurTime() + self.Primary.Delay )
|
||||
self:BombDrop()
|
||||
end
|
||||
|
||||
function SWEP:SecondaryAttack()
|
||||
self:SetNextSecondaryFire( CurTime() + self.Secondary.Delay )
|
||||
self:BombStick()
|
||||
end
|
||||
|
||||
-- mostly replicating HL2DM slam throw here
|
||||
function SWEP:BombDrop()
|
||||
if SERVER then
|
||||
|
||||
local ply = self:GetOwner()
|
||||
if not IsValid(ply) then return end
|
||||
|
||||
if self.Planted then return end
|
||||
|
||||
local vsrc = ply:GetShootPos()
|
||||
local vang = ply:GetAimVector()
|
||||
local vvel = ply:GetVelocity()
|
||||
|
||||
local vthrow = vvel + vang * 200
|
||||
|
||||
local bomb = ents.Create("ttt_c4")
|
||||
if IsValid(bomb) then
|
||||
bomb:SetPos(vsrc + vang * 10)
|
||||
bomb:SetOwner(ply)
|
||||
bomb:SetThrower(ply)
|
||||
bomb:Spawn()
|
||||
|
||||
bomb:PointAtEntity(ply)
|
||||
|
||||
local ang = bomb:GetAngles()
|
||||
ang:RotateAroundAxis(ang:Up(), 180)
|
||||
bomb:SetAngles(ang)
|
||||
|
||||
bomb.fingerprints = self.fingerprints
|
||||
|
||||
bomb:PhysWake()
|
||||
local phys = bomb:GetPhysicsObject()
|
||||
if IsValid(phys) then
|
||||
phys:SetVelocity(vthrow)
|
||||
end
|
||||
self:Remove()
|
||||
|
||||
self.Planted = true
|
||||
|
||||
end
|
||||
|
||||
ply:SetAnimation( PLAYER_ATTACK1 )
|
||||
end
|
||||
|
||||
self:EmitSound(throwsound)
|
||||
self:SendWeaponAnim(ACT_VM_SECONDARYATTACK)
|
||||
end
|
||||
|
||||
-- again replicating slam, now its attach fn
|
||||
function SWEP:BombStick()
|
||||
if SERVER then
|
||||
local ply = self:GetOwner()
|
||||
if not IsValid(ply) then return end
|
||||
|
||||
if self.Planted then return end
|
||||
|
||||
local ignore = {ply, self}
|
||||
local spos = ply:GetShootPos()
|
||||
local epos = spos + ply:GetAimVector() * 80
|
||||
local tr = util.TraceLine({start=spos, endpos=epos, filter=ignore, mask=MASK_SOLID})
|
||||
|
||||
if tr.HitWorld then
|
||||
local bomb = ents.Create("ttt_c4")
|
||||
if IsValid(bomb) then
|
||||
bomb:PointAtEntity(ply)
|
||||
|
||||
local tr_ent = util.TraceEntity({start=spos, endpos=epos, filter=ignore, mask=MASK_SOLID}, bomb)
|
||||
|
||||
if tr_ent.HitWorld then
|
||||
|
||||
local ang = tr_ent.HitNormal:Angle()
|
||||
ang:RotateAroundAxis(ang:Right(), -90)
|
||||
ang:RotateAroundAxis(ang:Up(), 180)
|
||||
|
||||
bomb:SetPos(tr_ent.HitPos)
|
||||
bomb:SetAngles(ang)
|
||||
bomb:SetOwner(ply)
|
||||
bomb:SetThrower(ply)
|
||||
bomb:Spawn()
|
||||
|
||||
bomb.fingerprints = self.fingerprints
|
||||
|
||||
local phys = bomb:GetPhysicsObject()
|
||||
if IsValid(phys) then
|
||||
phys:EnableMotion(false)
|
||||
end
|
||||
|
||||
bomb.IsOnWall = true
|
||||
|
||||
self:Remove()
|
||||
|
||||
self.Planted = true
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
ply:SetAnimation( PLAYER_ATTACK1 )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function SWEP:Reload()
|
||||
return false
|
||||
end
|
||||
|
||||
function SWEP:OnRemove()
|
||||
if CLIENT and IsValid(self:GetOwner()) and self:GetOwner() == LocalPlayer() and self:GetOwner():Alive() then
|
||||
RunConsoleCommand("lastinv")
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,46 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
|
||||
AddCSLuaFile()
|
||||
|
||||
SWEP.HoldType = "grenade"
|
||||
|
||||
if CLIENT then
|
||||
SWEP.PrintName = "confgrenade_name"
|
||||
SWEP.Slot = 3
|
||||
|
||||
SWEP.ViewModelFlip = false
|
||||
SWEP.ViewModelFOV = 54
|
||||
|
||||
SWEP.Icon = "vgui/ttt/icon_nades"
|
||||
SWEP.IconLetter = "h"
|
||||
end
|
||||
|
||||
SWEP.Base = "weapon_tttbasegrenade"
|
||||
|
||||
SWEP.WeaponID = AMMO_DISCOMB
|
||||
SWEP.Kind = WEAPON_NADE
|
||||
|
||||
SWEP.Spawnable = true
|
||||
SWEP.AutoSpawnable = true
|
||||
|
||||
SWEP.UseHands = true
|
||||
SWEP.ViewModel = "models/weapons/cstrike/c_eq_fraggrenade.mdl"
|
||||
SWEP.WorldModel = "models/weapons/w_eq_fraggrenade.mdl"
|
||||
|
||||
SWEP.Weight = 5
|
||||
|
||||
-- really the only difference between grenade weapons: the model and the thrown
|
||||
-- ent.
|
||||
|
||||
function SWEP:GetGrenadeName()
|
||||
return "ttt_confgrenade_proj"
|
||||
end
|
||||
143
gamemodes/terrortown/entities/weapons/weapon_ttt_cse.lua
Normal file
143
gamemodes/terrortown/entities/weapons/weapon_ttt_cse.lua
Normal file
@@ -0,0 +1,143 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
AddCSLuaFile()
|
||||
|
||||
SWEP.HoldType = "normal"
|
||||
|
||||
if CLIENT then
|
||||
SWEP.PrintName = "vis_name"
|
||||
SWEP.Slot = 6
|
||||
|
||||
SWEP.ViewModelFOV = 10
|
||||
SWEP.ViewModelFlip = false
|
||||
SWEP.DrawCrosshair = false
|
||||
|
||||
SWEP.EquipMenuData = {
|
||||
type = "item_weapon",
|
||||
desc = "vis_desc"
|
||||
};
|
||||
|
||||
SWEP.Icon = "vgui/ttt/icon_cse"
|
||||
end
|
||||
|
||||
SWEP.Base = "weapon_tttbase"
|
||||
|
||||
SWEP.ViewModel = Model("models/weapons/v_crowbar.mdl")
|
||||
SWEP.WorldModel = Model("models/Items/battery.mdl")
|
||||
|
||||
SWEP.Primary.ClipSize = -1
|
||||
SWEP.Primary.DefaultClip = -1
|
||||
SWEP.Primary.Automatic = false
|
||||
SWEP.Primary.Ammo = "none"
|
||||
SWEP.Primary.Delay = 1.0
|
||||
|
||||
SWEP.Secondary.ClipSize = -1
|
||||
SWEP.Secondary.DefaultClip = -1
|
||||
SWEP.Secondary.Automatic = true
|
||||
SWEP.Secondary.Ammo = "none"
|
||||
SWEP.Secondary.Delay = 0.2
|
||||
|
||||
SWEP.Kind = WEAPON_EQUIP
|
||||
SWEP.CanBuy = {ROLE_DETECTIVE} -- only detectives can buy
|
||||
SWEP.WeaponID = AMMO_CSE
|
||||
|
||||
SWEP.LimitedStock = true -- only buyable once
|
||||
SWEP.NoSights = true
|
||||
SWEP.AllowDrop = false
|
||||
|
||||
SWEP.DeathScanDelay = 15
|
||||
|
||||
function SWEP:PrimaryAttack()
|
||||
self:SetNextPrimaryFire( CurTime() + self.Primary.Delay )
|
||||
self:DropDevice()
|
||||
end
|
||||
|
||||
function SWEP:SecondaryAttack()
|
||||
self:SetNextSecondaryFire( CurTime() + self.Secondary.Delay )
|
||||
self:DropDevice()
|
||||
end
|
||||
|
||||
function SWEP:DrawWorldModel()
|
||||
end
|
||||
|
||||
function SWEP:OnDrop()
|
||||
self:Remove()
|
||||
end
|
||||
|
||||
function SWEP:PreDrop(isdeath)
|
||||
if isdeath then
|
||||
local cse = self:DropDevice()
|
||||
if IsValid(cse) then
|
||||
cse:SetDetonateTimer(self.DeathScanDelay or 10)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:Reload()
|
||||
return false
|
||||
end
|
||||
|
||||
function SWEP:OnRemove()
|
||||
if CLIENT and IsValid(self:GetOwner()) and self:GetOwner() == LocalPlayer() and self:GetOwner():Alive() then
|
||||
RunConsoleCommand("lastinv")
|
||||
end
|
||||
end
|
||||
|
||||
local throwsound = Sound( "Weapon_SLAM.SatchelThrow" )
|
||||
|
||||
function SWEP:DropDevice()
|
||||
local cse = nil
|
||||
|
||||
if SERVER then
|
||||
local ply = self:GetOwner()
|
||||
if not IsValid(ply) then return end
|
||||
|
||||
if self.Planted then return end
|
||||
|
||||
local vsrc = ply:GetShootPos()
|
||||
local vang = ply:GetAimVector()
|
||||
local vvel = ply:GetVelocity()
|
||||
|
||||
local vthrow = vvel + vang * 200
|
||||
|
||||
cse = ents.Create("ttt_cse_proj")
|
||||
if IsValid(cse) then
|
||||
cse:SetPos(vsrc + vang * 10)
|
||||
cse:SetOwner(ply)
|
||||
cse:SetThrower(ply)
|
||||
cse:Spawn()
|
||||
|
||||
cse:PhysWake()
|
||||
local phys = cse:GetPhysicsObject()
|
||||
if IsValid(phys) then
|
||||
phys:SetVelocity(vthrow)
|
||||
end
|
||||
|
||||
self:Remove()
|
||||
|
||||
self.Planted = true
|
||||
end
|
||||
end
|
||||
|
||||
self:EmitSound(throwsound)
|
||||
|
||||
return cse
|
||||
end
|
||||
|
||||
if CLIENT then
|
||||
function SWEP:Initialize()
|
||||
self:AddHUDHelp("vis_help_pri", nil, true)
|
||||
|
||||
return self.BaseClass.Initialize(self)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
198
gamemodes/terrortown/entities/weapons/weapon_ttt_decoy.lua
Normal file
198
gamemodes/terrortown/entities/weapons/weapon_ttt_decoy.lua
Normal file
@@ -0,0 +1,198 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
|
||||
AddCSLuaFile()
|
||||
|
||||
SWEP.HoldType = "normal"
|
||||
|
||||
if CLIENT then
|
||||
SWEP.PrintName = "decoy_name"
|
||||
SWEP.Slot = 7
|
||||
|
||||
SWEP.ViewModelFOV = 10
|
||||
SWEP.ViewModelFlip = false
|
||||
SWEP.DrawCrosshair = false
|
||||
|
||||
SWEP.EquipMenuData = {
|
||||
type = "item_weapon",
|
||||
desc = "decoy_desc"
|
||||
};
|
||||
|
||||
SWEP.Icon = "vgui/ttt/icon_beacon"
|
||||
end
|
||||
|
||||
SWEP.Base = "weapon_tttbase"
|
||||
|
||||
SWEP.ViewModel = "models/weapons/v_crowbar.mdl"
|
||||
SWEP.WorldModel = "models/props_lab/reciever01b.mdl"
|
||||
|
||||
SWEP.Primary.ClipSize = -1
|
||||
SWEP.Primary.DefaultClip = -1
|
||||
SWEP.Primary.Automatic = true
|
||||
SWEP.Primary.Ammo = "none"
|
||||
SWEP.Primary.Delay = 1.0
|
||||
|
||||
SWEP.Secondary.ClipSize = -1
|
||||
SWEP.Secondary.DefaultClip = -1
|
||||
SWEP.Secondary.Automatic = true
|
||||
SWEP.Secondary.Ammo = "none"
|
||||
SWEP.Secondary.Delay = 1.0
|
||||
|
||||
SWEP.Kind = WEAPON_EQUIP2
|
||||
SWEP.CanBuy = {ROLE_TRAITOR}
|
||||
SWEP.LimitedStock = true -- only buyable once
|
||||
SWEP.WeaponID = AMMO_DECOY
|
||||
|
||||
SWEP.AllowDrop = false
|
||||
SWEP.NoSights = true
|
||||
|
||||
function SWEP:OnDrop()
|
||||
self:Remove()
|
||||
end
|
||||
|
||||
function SWEP:PrimaryAttack()
|
||||
self:SetNextPrimaryFire( CurTime() + self.Primary.Delay )
|
||||
|
||||
self:DecoyStick()
|
||||
end
|
||||
function SWEP:SecondaryAttack()
|
||||
self:SetNextSecondaryFire( CurTime() + self.Secondary.Delay )
|
||||
|
||||
self:DecoyStick()
|
||||
end
|
||||
|
||||
local throwsound = Sound( "Weapon_SLAM.SatchelThrow" )
|
||||
|
||||
-- Drop is disabled to prevent traitors from placing the decoy in unreachable
|
||||
-- places.
|
||||
function SWEP:DecoyDrop()
|
||||
if SERVER then
|
||||
local ply = self:GetOwner()
|
||||
if not IsValid(ply) then return end
|
||||
|
||||
if self.Planted then return end
|
||||
|
||||
local vsrc = ply:GetShootPos()
|
||||
local vang = ply:GetAimVector()
|
||||
local vvel = ply:GetVelocity()
|
||||
|
||||
local vthrow = vvel + vang * 200
|
||||
|
||||
local decoy = ents.Create("ttt_decoy")
|
||||
if IsValid(decoy) then
|
||||
decoy:SetPos(vsrc + vang * 10)
|
||||
decoy:SetOwner(ply)
|
||||
decoy:Spawn()
|
||||
|
||||
decoy:PointAtEntity(ply)
|
||||
|
||||
local ang = decoy:GetAngles()
|
||||
ang:RotateAroundAxis(ang:Right(), 90)
|
||||
decoy:SetAngles(ang)
|
||||
|
||||
decoy:PhysWake()
|
||||
local phys = decoy:GetPhysicsObject()
|
||||
if IsValid(phys) then
|
||||
phys:SetVelocity(vthrow)
|
||||
end
|
||||
|
||||
self:PlacedDecoy(decoy)
|
||||
end
|
||||
end
|
||||
|
||||
self:EmitSound(throwsound)
|
||||
end
|
||||
|
||||
function SWEP:DecoyStick()
|
||||
if SERVER then
|
||||
local ply = self:GetOwner()
|
||||
if not IsValid(ply) then return end
|
||||
|
||||
if self.Planted then return end
|
||||
|
||||
local ignore = {ply, self}
|
||||
local spos = ply:GetShootPos()
|
||||
local epos = spos + ply:GetAimVector() * 80
|
||||
local tr = util.TraceLine({start=spos, endpos=epos, filter=ignore, mask=MASK_SOLID})
|
||||
|
||||
if tr.HitWorld then
|
||||
local decoy = ents.Create("ttt_decoy")
|
||||
if IsValid(decoy) then
|
||||
decoy:PointAtEntity(ply)
|
||||
|
||||
local tr_ent = util.TraceEntity({start=spos, endpos=epos, filter=ignore, mask=MASK_SOLID}, decoy)
|
||||
|
||||
if tr_ent.HitWorld then
|
||||
|
||||
local ang = tr_ent.HitNormal:Angle()
|
||||
|
||||
decoy:SetPos(tr_ent.HitPos + ang:Forward() * 2.5)
|
||||
decoy:SetAngles(ang)
|
||||
decoy:SetOwner(ply)
|
||||
decoy:Spawn()
|
||||
|
||||
local phys = decoy:GetPhysicsObject()
|
||||
if IsValid(phys) then
|
||||
phys:EnableMotion(false)
|
||||
end
|
||||
|
||||
decoy.IsOnWall = true
|
||||
|
||||
self:PlacedDecoy(decoy)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:PlacedDecoy(decoy)
|
||||
self:GetOwner().decoy = decoy
|
||||
|
||||
self:TakePrimaryAmmo(1)
|
||||
|
||||
if not self:CanPrimaryAttack() then
|
||||
self:Remove()
|
||||
|
||||
self.Planted = true
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:Reload()
|
||||
return false
|
||||
end
|
||||
|
||||
function SWEP:OnRemove()
|
||||
if CLIENT and IsValid(self:GetOwner()) and self:GetOwner() == LocalPlayer() and self:GetOwner():Alive() then
|
||||
RunConsoleCommand("lastinv")
|
||||
end
|
||||
end
|
||||
|
||||
if CLIENT then
|
||||
function SWEP:Initialize()
|
||||
self:AddHUDHelp("decoy_help_pri", nil, true)
|
||||
|
||||
return self.BaseClass.Initialize(self)
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:Deploy()
|
||||
self:GetOwner():DrawViewModel(false)
|
||||
return true
|
||||
end
|
||||
|
||||
function SWEP:DrawWorldModel()
|
||||
if not IsValid(self:GetOwner()) then
|
||||
self:DrawModel()
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:DrawWorldModelTranslucent()
|
||||
end
|
||||
108
gamemodes/terrortown/entities/weapons/weapon_ttt_defuser.lua
Normal file
108
gamemodes/terrortown/entities/weapons/weapon_ttt_defuser.lua
Normal file
@@ -0,0 +1,108 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
AddCSLuaFile()
|
||||
|
||||
SWEP.HoldType = "slam"
|
||||
|
||||
if CLIENT then
|
||||
SWEP.PrintName = "defuser_name"
|
||||
SWEP.Slot = 7
|
||||
|
||||
SWEP.DrawCrosshair = false
|
||||
SWEP.ViewModelFOV = 10
|
||||
|
||||
SWEP.EquipMenuData = {
|
||||
type = "item_weapon",
|
||||
desc = "defuser_desc"
|
||||
};
|
||||
|
||||
SWEP.Icon = "vgui/ttt/icon_defuser"
|
||||
end
|
||||
|
||||
SWEP.Base = "weapon_tttbase"
|
||||
|
||||
SWEP.ViewModel = "models/weapons/v_crowbar.mdl"
|
||||
SWEP.WorldModel = "models/weapons/w_defuser.mdl"
|
||||
|
||||
SWEP.Primary.ClipSize = -1
|
||||
SWEP.Primary.DefaultClip = -1
|
||||
SWEP.Primary.Automatic = true
|
||||
SWEP.Primary.Delay = 1
|
||||
SWEP.Primary.Ammo = "none"
|
||||
|
||||
SWEP.Secondary.ClipSize = -1
|
||||
SWEP.Secondary.DefaultClip = -1
|
||||
SWEP.Secondary.Automatic = true
|
||||
SWEP.Secondary.Ammo = "none"
|
||||
SWEP.Secondary.Delay = 2
|
||||
|
||||
SWEP.Kind = WEAPON_EQUIP2
|
||||
SWEP.CanBuy = {ROLE_DETECTIVE} -- only detectives can buy
|
||||
SWEP.WeaponID = AMMO_DEFUSER
|
||||
|
||||
--SWEP.AllowDrop = false
|
||||
|
||||
local defuse = Sound("c4.disarmfinish")
|
||||
function SWEP:PrimaryAttack()
|
||||
self:SetNextPrimaryFire( CurTime() + self.Primary.Delay )
|
||||
|
||||
local spos = self:GetOwner():GetShootPos()
|
||||
local sdest = spos + (self:GetOwner():GetAimVector() * 80)
|
||||
|
||||
local tr = util.TraceLine({start=spos, endpos=sdest, filter=self:GetOwner(), mask=MASK_SHOT})
|
||||
|
||||
if IsValid(tr.Entity) and tr.Entity.Defusable then
|
||||
local bomb = tr.Entity
|
||||
if bomb.Defusable==true or bomb:Defusable() then
|
||||
if SERVER and bomb.Disarm then
|
||||
bomb:Disarm(self:GetOwner())
|
||||
sound.Play(defuse, bomb:GetPos())
|
||||
end
|
||||
|
||||
self:SetNextPrimaryFire( CurTime() + (self.Primary.Delay * 2) )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:SecondaryAttack()
|
||||
self:SetNextPrimaryFire( CurTime() + self.Primary.Delay )
|
||||
self:SetNextSecondaryFire( CurTime() + 0.1 )
|
||||
end
|
||||
|
||||
|
||||
if CLIENT then
|
||||
function SWEP:Initialize()
|
||||
self:AddHUDHelp("defuser_help", nil, true)
|
||||
|
||||
return self.BaseClass.Initialize(self)
|
||||
end
|
||||
|
||||
function SWEP:DrawWorldModel()
|
||||
if not IsValid(self:GetOwner()) then
|
||||
self:DrawModel()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:Reload()
|
||||
return false
|
||||
end
|
||||
|
||||
function SWEP:Deploy()
|
||||
if SERVER and IsValid(self:GetOwner()) then
|
||||
self:GetOwner():DrawViewModel(false)
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
function SWEP:OnDrop()
|
||||
end
|
||||
216
gamemodes/terrortown/entities/weapons/weapon_ttt_flaregun.lua
Normal file
216
gamemodes/terrortown/entities/weapons/weapon_ttt_flaregun.lua
Normal file
@@ -0,0 +1,216 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
|
||||
AddCSLuaFile()
|
||||
|
||||
SWEP.HoldType = "pistol"
|
||||
|
||||
if CLIENT then
|
||||
SWEP.PrintName = "flare_name"
|
||||
SWEP.Slot = 6
|
||||
|
||||
SWEP.ViewModelFOV = 54
|
||||
SWEP.ViewModelFlip = false
|
||||
|
||||
SWEP.EquipMenuData = {
|
||||
type = "item_weapon",
|
||||
desc = "flare_desc"
|
||||
};
|
||||
|
||||
SWEP.Icon = "vgui/ttt/icon_flare"
|
||||
end
|
||||
|
||||
SWEP.Base = "weapon_tttbase"
|
||||
|
||||
-- if I run out of ammo types, this weapon is one I could move to a custom ammo
|
||||
-- handling strategy, because you never need to pick up ammo for it
|
||||
SWEP.Primary.Ammo = "AR2AltFire"
|
||||
SWEP.Primary.Recoil = 4
|
||||
SWEP.Primary.Damage = 7
|
||||
SWEP.Primary.Delay = 1.0
|
||||
SWEP.Primary.Cone = 0.01
|
||||
SWEP.Primary.ClipSize = 4
|
||||
SWEP.Primary.Automatic = false
|
||||
SWEP.Primary.DefaultClip = 4
|
||||
SWEP.Primary.ClipMax = 4
|
||||
SWEP.Primary.Sound = Sound( "Weapon_USP.SilencedShot" )
|
||||
|
||||
SWEP.Kind = WEAPON_EQUIP
|
||||
SWEP.CanBuy = {ROLE_TRAITOR} -- only traitors can buy
|
||||
SWEP.LimitedStock = true -- only buyable once
|
||||
SWEP.WeaponID = AMMO_FLARE
|
||||
|
||||
SWEP.Tracer = "AR2Tracer"
|
||||
|
||||
SWEP.UseHands = true
|
||||
SWEP.ViewModel = Model("models/weapons/c_357.mdl")
|
||||
SWEP.WorldModel = Model("models/weapons/w_357.mdl")
|
||||
|
||||
local function RunIgniteTimer(ent, timer_name)
|
||||
if IsValid(ent) and ent:IsOnFire() then
|
||||
if ent:WaterLevel() > 0 then
|
||||
ent:Extinguish()
|
||||
elseif CurTime() > ent.burn_destroy then
|
||||
ent:SetNotSolid(true)
|
||||
ent:Remove()
|
||||
else
|
||||
-- keep on burning
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
timer.Remove(timer_name) -- stop running timer
|
||||
end
|
||||
|
||||
local SendScorches
|
||||
|
||||
if CLIENT then
|
||||
local function ReceiveScorches()
|
||||
local ent = net.ReadEntity()
|
||||
local num = net.ReadUInt(8)
|
||||
for i=1, num do
|
||||
util.PaintDown(net.ReadVector(), "FadingScorch", ent)
|
||||
end
|
||||
|
||||
if IsValid(ent) then
|
||||
util.PaintDown(ent:LocalToWorld(ent:OBBCenter()), "Scorch", ent)
|
||||
end
|
||||
end
|
||||
net.Receive("TTT_FlareScorch", ReceiveScorches)
|
||||
else
|
||||
-- it's sad that decals are so unreliable when drawn serverside, failing to
|
||||
-- draw more often than they work, that I have to do this
|
||||
SendScorches = function(ent, tbl)
|
||||
net.Start("TTT_FlareScorch")
|
||||
net.WriteEntity(ent)
|
||||
net.WriteUInt(#tbl, 8)
|
||||
for _, p in ipairs(tbl) do
|
||||
net.WriteVector(p)
|
||||
end
|
||||
net.Broadcast()
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
local function ScorchUnderRagdoll(ent)
|
||||
if SERVER then
|
||||
local postbl = {}
|
||||
-- small scorches under limbs
|
||||
for i=0, ent:GetPhysicsObjectCount()-1 do
|
||||
local subphys = ent:GetPhysicsObjectNum(i)
|
||||
if IsValid(subphys) then
|
||||
local pos = subphys:GetPos()
|
||||
util.PaintDown(pos, "FadingScorch", ent)
|
||||
|
||||
table.insert(postbl, pos)
|
||||
end
|
||||
end
|
||||
|
||||
SendScorches(ent, postbl)
|
||||
end
|
||||
|
||||
-- big scorch at center
|
||||
local mid = ent:LocalToWorld(ent:OBBCenter())
|
||||
mid.z = mid.z + 25
|
||||
util.PaintDown(mid, "Scorch", ent)
|
||||
end
|
||||
|
||||
|
||||
function IgniteTarget(att, path, dmginfo)
|
||||
local ent = path.Entity
|
||||
if not IsValid(ent) then return end
|
||||
|
||||
if CLIENT and IsFirstTimePredicted() then
|
||||
if ent:GetClass() == "prop_ragdoll" then
|
||||
ScorchUnderRagdoll(ent)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
if SERVER then
|
||||
|
||||
local dur = ent:IsPlayer() and 5 or 10
|
||||
|
||||
-- disallow if prep or post round
|
||||
if ent:IsPlayer() and (not GAMEMODE:AllowPVP()) then return end
|
||||
|
||||
ent:Ignite(dur, 100)
|
||||
|
||||
ent.ignite_info = {att=dmginfo:GetAttacker(), infl=dmginfo:GetInflictor()}
|
||||
|
||||
if ent:IsPlayer() then
|
||||
timer.Simple(dur + 0.1, function()
|
||||
if IsValid(ent) then
|
||||
ent.ignite_info = nil
|
||||
end
|
||||
end)
|
||||
|
||||
elseif ent:GetClass() == "prop_ragdoll" then
|
||||
ScorchUnderRagdoll(ent)
|
||||
|
||||
local burn_time = 6
|
||||
local tname = Format("ragburn_%d_%d", ent:EntIndex(), math.ceil(CurTime()))
|
||||
|
||||
ent.burn_destroy = CurTime() + burn_time
|
||||
|
||||
timer.Create(tname,
|
||||
0.1,
|
||||
math.ceil(1 + burn_time / 0.1), -- upper limit, failsafe
|
||||
function()
|
||||
RunIgniteTimer(ent, tname)
|
||||
end)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:ShootFlare()
|
||||
local cone = self.Primary.Cone
|
||||
local bullet = {}
|
||||
bullet.Num = 1
|
||||
bullet.Src = self:GetOwner():GetShootPos()
|
||||
bullet.Dir = self:GetOwner():GetAimVector()
|
||||
bullet.Spread = Vector( cone, cone, 0 )
|
||||
bullet.Tracer = 1
|
||||
bullet.Force = 2
|
||||
bullet.Damage = self.Primary.Damage
|
||||
bullet.TracerName = self.Tracer
|
||||
bullet.Callback = IgniteTarget
|
||||
|
||||
self:GetOwner():FireBullets( bullet )
|
||||
end
|
||||
|
||||
function SWEP:PrimaryAttack()
|
||||
self:SetNextPrimaryFire( CurTime() + self.Primary.Delay )
|
||||
|
||||
if not self:CanPrimaryAttack() then return end
|
||||
|
||||
self:EmitSound( self.Primary.Sound )
|
||||
|
||||
self:SendWeaponAnim( ACT_VM_PRIMARYATTACK )
|
||||
|
||||
self:ShootFlare()
|
||||
|
||||
self:TakePrimaryAmmo( 1 )
|
||||
|
||||
if IsValid(self:GetOwner()) then
|
||||
self:GetOwner():SetAnimation( PLAYER_ATTACK1 )
|
||||
|
||||
self:GetOwner():ViewPunch( Angle( math.Rand(-0.2,-0.1) * self.Primary.Recoil, math.Rand(-0.1,0.1) *self.Primary.Recoil, 0 ) )
|
||||
end
|
||||
|
||||
if ( (game.SinglePlayer() && SERVER) || CLIENT ) then
|
||||
self:SetNWFloat( "LastShootTime", CurTime() )
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:SecondaryAttack()
|
||||
end
|
||||
51
gamemodes/terrortown/entities/weapons/weapon_ttt_glock.lua
Normal file
51
gamemodes/terrortown/entities/weapons/weapon_ttt_glock.lua
Normal file
@@ -0,0 +1,51 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
AddCSLuaFile()
|
||||
|
||||
SWEP.HoldType = "pistol"
|
||||
|
||||
if CLIENT then
|
||||
SWEP.PrintName = "Glock"
|
||||
SWEP.Slot = 1
|
||||
|
||||
SWEP.ViewModelFlip = false
|
||||
SWEP.ViewModelFOV = 54
|
||||
|
||||
SWEP.Icon = "vgui/ttt/icon_glock"
|
||||
SWEP.IconLetter = "c"
|
||||
end
|
||||
|
||||
SWEP.Base = "weapon_tttbase"
|
||||
|
||||
SWEP.Primary.Recoil = 0.9
|
||||
SWEP.Primary.Damage = 12
|
||||
SWEP.Primary.Delay = 0.10
|
||||
SWEP.Primary.Cone = 0.028
|
||||
SWEP.Primary.ClipSize = 20
|
||||
SWEP.Primary.Automatic = true
|
||||
SWEP.Primary.DefaultClip = 20
|
||||
SWEP.Primary.ClipMax = 60
|
||||
SWEP.Primary.Ammo = "Pistol"
|
||||
SWEP.Primary.Sound = Sound( "Weapon_Glock.Single" )
|
||||
|
||||
SWEP.AutoSpawnable = true
|
||||
|
||||
SWEP.AmmoEnt = "item_ammo_pistol_ttt"
|
||||
SWEP.Kind = WEAPON_PISTOL
|
||||
SWEP.WeaponID = AMMO_GLOCK
|
||||
|
||||
SWEP.HeadshotMultiplier = 1.75
|
||||
|
||||
SWEP.UseHands = true
|
||||
SWEP.ViewModel = "models/weapons/cstrike/c_pist_glock18.mdl"
|
||||
SWEP.WorldModel = "models/weapons/w_pist_glock18.mdl"
|
||||
|
||||
SWEP.IronSightsPos = Vector( -5.79, -3.9982, 2.8289 )
|
||||
@@ -0,0 +1,137 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
AddCSLuaFile()
|
||||
|
||||
SWEP.HoldType = "normal"
|
||||
|
||||
if CLIENT then
|
||||
SWEP.PrintName = "hstation_name"
|
||||
SWEP.Slot = 6
|
||||
|
||||
SWEP.ViewModelFOV = 10
|
||||
SWEP.DrawCrosshair = false
|
||||
|
||||
SWEP.EquipMenuData = {
|
||||
type = "item_weapon",
|
||||
desc = "hstation_desc"
|
||||
};
|
||||
|
||||
SWEP.Icon = "vgui/ttt/icon_health"
|
||||
end
|
||||
|
||||
SWEP.Base = "weapon_tttbase"
|
||||
|
||||
SWEP.ViewModel = "models/weapons/v_crowbar.mdl"
|
||||
SWEP.WorldModel = "models/props/cs_office/microwave.mdl"
|
||||
|
||||
SWEP.Primary.ClipSize = -1
|
||||
SWEP.Primary.DefaultClip = -1
|
||||
SWEP.Primary.Automatic = true
|
||||
SWEP.Primary.Ammo = "none"
|
||||
SWEP.Primary.Delay = 1.0
|
||||
|
||||
SWEP.Secondary.ClipSize = -1
|
||||
SWEP.Secondary.DefaultClip = -1
|
||||
SWEP.Secondary.Automatic = true
|
||||
SWEP.Secondary.Ammo = "none"
|
||||
SWEP.Secondary.Delay = 1.0
|
||||
|
||||
-- This is special equipment
|
||||
SWEP.Kind = WEAPON_EQUIP
|
||||
SWEP.CanBuy = {ROLE_DETECTIVE} -- only detectives can buy
|
||||
SWEP.LimitedStock = true -- only buyable once
|
||||
SWEP.WeaponID = AMMO_HEALTHSTATION
|
||||
|
||||
SWEP.AllowDrop = false
|
||||
SWEP.NoSights = true
|
||||
|
||||
function SWEP:OnDrop()
|
||||
self:Remove()
|
||||
end
|
||||
|
||||
function SWEP:PrimaryAttack()
|
||||
self:SetNextPrimaryFire( CurTime() + self.Primary.Delay )
|
||||
self:HealthDrop()
|
||||
end
|
||||
function SWEP:SecondaryAttack()
|
||||
self:SetNextSecondaryFire( CurTime() + self.Secondary.Delay )
|
||||
self:HealthDrop()
|
||||
end
|
||||
|
||||
local throwsound = Sound( "Weapon_SLAM.SatchelThrow" )
|
||||
|
||||
-- ye olde droppe code
|
||||
function SWEP:HealthDrop()
|
||||
if SERVER then
|
||||
local ply = self:GetOwner()
|
||||
if not IsValid(ply) then return end
|
||||
|
||||
if self.Planted then return end
|
||||
|
||||
local vsrc = ply:GetShootPos()
|
||||
local vang = ply:GetAimVector()
|
||||
local vvel = ply:GetVelocity()
|
||||
|
||||
local vthrow = vvel + vang * 200
|
||||
|
||||
local health = ents.Create("ttt_health_station")
|
||||
if IsValid(health) then
|
||||
health:SetPos(vsrc + vang * 10)
|
||||
health:Spawn()
|
||||
|
||||
health:SetPlacer(ply)
|
||||
|
||||
health:PhysWake()
|
||||
local phys = health:GetPhysicsObject()
|
||||
if IsValid(phys) then
|
||||
phys:SetVelocity(vthrow)
|
||||
end
|
||||
self:Remove()
|
||||
|
||||
self.Planted = true
|
||||
end
|
||||
end
|
||||
|
||||
self:EmitSound(throwsound)
|
||||
end
|
||||
|
||||
|
||||
function SWEP:Reload()
|
||||
return false
|
||||
end
|
||||
|
||||
function SWEP:OnRemove()
|
||||
if CLIENT and IsValid(self:GetOwner()) and self:GetOwner() == LocalPlayer() and self:GetOwner():Alive() then
|
||||
RunConsoleCommand("lastinv")
|
||||
end
|
||||
end
|
||||
|
||||
if CLIENT then
|
||||
function SWEP:Initialize()
|
||||
self:AddHUDHelp("hstation_help", nil, true)
|
||||
|
||||
return self.BaseClass.Initialize(self)
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:Deploy()
|
||||
if SERVER and IsValid(self:GetOwner()) then
|
||||
self:GetOwner():DrawViewModel(false)
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function SWEP:DrawWorldModel()
|
||||
end
|
||||
|
||||
function SWEP:DrawWorldModelTranslucent()
|
||||
end
|
||||
|
||||
312
gamemodes/terrortown/entities/weapons/weapon_ttt_knife.lua
Normal file
312
gamemodes/terrortown/entities/weapons/weapon_ttt_knife.lua
Normal file
@@ -0,0 +1,312 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
AddCSLuaFile()
|
||||
|
||||
SWEP.HoldType = "knife"
|
||||
|
||||
if CLIENT then
|
||||
SWEP.PrintName = "knife_name"
|
||||
SWEP.Slot = 6
|
||||
|
||||
SWEP.ViewModelFlip = false
|
||||
SWEP.ViewModelFOV = 54
|
||||
SWEP.DrawCrosshair = false
|
||||
|
||||
SWEP.EquipMenuData = {
|
||||
type = "item_weapon",
|
||||
desc = "knife_desc"
|
||||
};
|
||||
|
||||
SWEP.Icon = "vgui/ttt/icon_knife"
|
||||
SWEP.IconLetter = "j"
|
||||
end
|
||||
|
||||
SWEP.Base = "weapon_tttbase"
|
||||
|
||||
SWEP.UseHands = true
|
||||
SWEP.ViewModel = "models/weapons/cstrike/c_knife_t.mdl"
|
||||
SWEP.WorldModel = "models/weapons/w_knife_t.mdl"
|
||||
|
||||
SWEP.Primary.Damage = 50
|
||||
SWEP.Primary.ClipSize = -1
|
||||
SWEP.Primary.DefaultClip = -1
|
||||
SWEP.Primary.Automatic = true
|
||||
SWEP.Primary.Delay = 1.1
|
||||
SWEP.Primary.Ammo = "none"
|
||||
|
||||
SWEP.Secondary.ClipSize = -1
|
||||
SWEP.Secondary.DefaultClip = -1
|
||||
SWEP.Secondary.Automatic = true
|
||||
SWEP.Secondary.Ammo = "none"
|
||||
SWEP.Secondary.Delay = 1.4
|
||||
|
||||
SWEP.Kind = WEAPON_EQUIP
|
||||
SWEP.CanBuy = {ROLE_TRAITOR} -- only traitors can buy
|
||||
SWEP.LimitedStock = true -- only buyable once
|
||||
SWEP.WeaponID = AMMO_KNIFE
|
||||
|
||||
SWEP.IsSilent = true
|
||||
|
||||
-- Pull out faster than standard guns
|
||||
SWEP.DeploySpeed = 2
|
||||
|
||||
function SWEP:PrimaryAttack()
|
||||
self:SetNextPrimaryFire( CurTime() + self.Primary.Delay )
|
||||
self:SetNextSecondaryFire( CurTime() + self.Secondary.Delay )
|
||||
|
||||
if not IsValid(self:GetOwner()) then return end
|
||||
|
||||
self:GetOwner():LagCompensation(true)
|
||||
|
||||
local spos = self:GetOwner():GetShootPos()
|
||||
local sdest = spos + (self:GetOwner():GetAimVector() * 70)
|
||||
|
||||
local kmins = Vector(1,1,1) * -10
|
||||
local kmaxs = Vector(1,1,1) * 10
|
||||
|
||||
local tr = util.TraceHull({start=spos, endpos=sdest, filter=self:GetOwner(), mask=MASK_SHOT_HULL, mins=kmins, maxs=kmaxs})
|
||||
|
||||
-- Hull might hit environment stuff that line does not hit
|
||||
if not IsValid(tr.Entity) then
|
||||
tr = util.TraceLine({start=spos, endpos=sdest, filter=self:GetOwner(), mask=MASK_SHOT_HULL})
|
||||
end
|
||||
|
||||
local hitEnt = tr.Entity
|
||||
|
||||
-- effects
|
||||
if IsValid(hitEnt) then
|
||||
self:SendWeaponAnim( ACT_VM_HITCENTER )
|
||||
|
||||
local edata = EffectData()
|
||||
edata:SetStart(spos)
|
||||
edata:SetOrigin(tr.HitPos)
|
||||
edata:SetNormal(tr.Normal)
|
||||
edata:SetEntity(hitEnt)
|
||||
|
||||
if hitEnt:IsPlayer() or hitEnt:GetClass() == "prop_ragdoll" then
|
||||
util.Effect("BloodImpact", edata)
|
||||
end
|
||||
else
|
||||
self:SendWeaponAnim( ACT_VM_MISSCENTER )
|
||||
end
|
||||
|
||||
if SERVER then
|
||||
self:GetOwner():SetAnimation( PLAYER_ATTACK1 )
|
||||
end
|
||||
|
||||
|
||||
if SERVER and tr.Hit and tr.HitNonWorld and IsValid(hitEnt) then
|
||||
if hitEnt:IsPlayer() then
|
||||
-- knife damage is never karma'd, so don't need to take that into
|
||||
-- account we do want to avoid rounding error strangeness caused by
|
||||
-- other damage scaling, causing a death when we don't expect one, so
|
||||
-- when the target's health is close to kill-point we just kill
|
||||
if hitEnt:Health() < (self.Primary.Damage + 10) then
|
||||
self:StabKill(tr, spos, sdest)
|
||||
else
|
||||
local dmg = DamageInfo()
|
||||
dmg:SetDamage(self.Primary.Damage)
|
||||
dmg:SetAttacker(self:GetOwner())
|
||||
dmg:SetInflictor(self)
|
||||
dmg:SetDamageForce(self:GetOwner():GetAimVector() * 5)
|
||||
dmg:SetDamagePosition(self:GetOwner():GetPos())
|
||||
dmg:SetDamageType(DMG_SLASH)
|
||||
|
||||
hitEnt:DispatchTraceAttack(dmg, spos + (self:GetOwner():GetAimVector() * 3), sdest)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
self:GetOwner():LagCompensation(false)
|
||||
end
|
||||
|
||||
function SWEP:StabKill(tr, spos, sdest)
|
||||
local target = tr.Entity
|
||||
|
||||
local dmg = DamageInfo()
|
||||
dmg:SetDamage(2000)
|
||||
dmg:SetAttacker(self:GetOwner())
|
||||
dmg:SetInflictor(self)
|
||||
dmg:SetDamageForce(self:GetOwner():GetAimVector())
|
||||
dmg:SetDamagePosition(self:GetOwner():GetPos())
|
||||
dmg:SetDamageType(DMG_SLASH)
|
||||
|
||||
-- now that we use a hull trace, our hitpos is guaranteed to be
|
||||
-- terrible, so try to make something of it with a separate trace and
|
||||
-- hope our effect_fn trace has more luck
|
||||
|
||||
-- first a straight up line trace to see if we aimed nicely
|
||||
local retr = util.TraceLine({start=spos, endpos=sdest, filter=self:GetOwner(), mask=MASK_SHOT_HULL})
|
||||
|
||||
-- if that fails, just trace to worldcenter so we have SOMETHING
|
||||
if retr.Entity != target then
|
||||
local center = target:LocalToWorld(target:OBBCenter())
|
||||
retr = util.TraceLine({start=spos, endpos=center, filter=self:GetOwner(), mask=MASK_SHOT_HULL})
|
||||
end
|
||||
|
||||
|
||||
-- create knife effect creation fn
|
||||
local bone = retr.PhysicsBone
|
||||
local pos = retr.HitPos
|
||||
local norm = tr.Normal
|
||||
local ang = Angle(-28,0,0) + norm:Angle()
|
||||
ang:RotateAroundAxis(ang:Right(), -90)
|
||||
pos = pos - (ang:Forward() * 7)
|
||||
|
||||
local prints = self.fingerprints
|
||||
local ignore = self:GetOwner()
|
||||
|
||||
target.effect_fn = function(rag)
|
||||
-- we might find a better location
|
||||
local rtr = util.TraceLine({start=pos, endpos=pos + norm * 40, filter=ignore, mask=MASK_SHOT_HULL})
|
||||
|
||||
if IsValid(rtr.Entity) and rtr.Entity == rag then
|
||||
bone = rtr.PhysicsBone
|
||||
pos = rtr.HitPos
|
||||
ang = Angle(-28,0,0) + rtr.Normal:Angle()
|
||||
ang:RotateAroundAxis(ang:Right(), -90)
|
||||
pos = pos - (ang:Forward() * 10)
|
||||
|
||||
end
|
||||
|
||||
local knife = ents.Create("prop_physics")
|
||||
knife:SetModel("models/weapons/w_knife_t.mdl")
|
||||
knife:SetPos(pos)
|
||||
knife:SetCollisionGroup(COLLISION_GROUP_DEBRIS)
|
||||
knife:SetAngles(ang)
|
||||
knife.CanPickup = false
|
||||
|
||||
knife:Spawn()
|
||||
|
||||
local phys = knife:GetPhysicsObject()
|
||||
if IsValid(phys) then
|
||||
phys:EnableCollisions(false)
|
||||
end
|
||||
|
||||
constraint.Weld(rag, knife, bone, 0, 0, true)
|
||||
|
||||
-- need to close over knife in order to keep a valid ref to it
|
||||
rag:CallOnRemove("ttt_knife_cleanup", function() SafeRemoveEntity(knife) end)
|
||||
end
|
||||
|
||||
|
||||
-- seems the spos and sdest are purely for effects/forces?
|
||||
target:DispatchTraceAttack(dmg, spos + (self:GetOwner():GetAimVector() * 3), sdest)
|
||||
|
||||
-- target appears to die right there, so we could theoretically get to
|
||||
-- the ragdoll in here...
|
||||
|
||||
self:Remove()
|
||||
end
|
||||
|
||||
function SWEP:SecondaryAttack()
|
||||
self:SetNextPrimaryFire( CurTime() + self.Primary.Delay )
|
||||
self:SetNextSecondaryFire( CurTime() + self.Secondary.Delay )
|
||||
|
||||
|
||||
self:SendWeaponAnim( ACT_VM_MISSCENTER )
|
||||
|
||||
if SERVER then
|
||||
local ply = self:GetOwner()
|
||||
if not IsValid(ply) then return end
|
||||
|
||||
ply:SetAnimation( PLAYER_ATTACK1 )
|
||||
|
||||
local ang = ply:EyeAngles()
|
||||
|
||||
if ang.p < 90 then
|
||||
ang.p = -10 + ang.p * ((90 + 10) / 90)
|
||||
else
|
||||
ang.p = 360 - ang.p
|
||||
ang.p = -10 + ang.p * -((90 + 10) / 90)
|
||||
end
|
||||
|
||||
local vel = math.Clamp((90 - ang.p) * 5.5, 550, 800)
|
||||
|
||||
local vfw = ang:Forward()
|
||||
local vrt = ang:Right()
|
||||
|
||||
local src = ply:GetPos() + (ply:Crouching() and ply:GetViewOffsetDucked() or ply:GetViewOffset())
|
||||
|
||||
src = src + (vfw * 1) + (vrt * 3)
|
||||
|
||||
local thr = vfw * vel + ply:GetVelocity()
|
||||
|
||||
local knife_ang = Angle(-28,0,0) + ang
|
||||
knife_ang:RotateAroundAxis(knife_ang:Right(), -90)
|
||||
|
||||
local knife = ents.Create("ttt_knife_proj")
|
||||
if not IsValid(knife) then return end
|
||||
knife:SetPos(src)
|
||||
knife:SetAngles(knife_ang)
|
||||
|
||||
knife:Spawn()
|
||||
|
||||
knife.Damage = self.Primary.Damage
|
||||
|
||||
knife:SetOwner(ply)
|
||||
|
||||
local phys = knife:GetPhysicsObject()
|
||||
if IsValid(phys) then
|
||||
phys:SetVelocity(thr)
|
||||
phys:AddAngleVelocity(Vector(0, 1500, 0))
|
||||
phys:Wake()
|
||||
end
|
||||
|
||||
self:Remove()
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:Equip()
|
||||
self:SetNextPrimaryFire( CurTime() + (self.Primary.Delay * 1.5) )
|
||||
self:SetNextSecondaryFire( CurTime() + (self.Secondary.Delay * 1.5) )
|
||||
end
|
||||
|
||||
function SWEP:PreDrop()
|
||||
-- for consistency, dropped knife should not have DNA/prints
|
||||
self.fingerprints = {}
|
||||
end
|
||||
|
||||
function SWEP:OnRemove()
|
||||
if CLIENT and IsValid(self:GetOwner()) and self:GetOwner() == LocalPlayer() and self:GetOwner():Alive() then
|
||||
RunConsoleCommand("lastinv")
|
||||
end
|
||||
end
|
||||
|
||||
if CLIENT then
|
||||
local T = LANG.GetTranslation
|
||||
function SWEP:DrawHUD()
|
||||
local tr = self:GetOwner():GetEyeTrace(MASK_SHOT)
|
||||
|
||||
if tr.HitNonWorld and IsValid(tr.Entity) and tr.Entity:IsPlayer()
|
||||
and tr.Entity:Health() < (self.Primary.Damage + 10) then
|
||||
|
||||
local x = ScrW() / 2.0
|
||||
local y = ScrH() / 2.0
|
||||
|
||||
surface.SetDrawColor(255, 0, 0, 255)
|
||||
|
||||
local outer = 20
|
||||
local inner = 10
|
||||
surface.DrawLine(x - outer, y - outer, x - inner, y - inner)
|
||||
surface.DrawLine(x + outer, y + outer, x + inner, y + inner)
|
||||
|
||||
surface.DrawLine(x - outer, y + outer, x - inner, y + inner)
|
||||
surface.DrawLine(x + outer, y - outer, x + inner, y - inner)
|
||||
|
||||
draw.SimpleText(T("knife_instant"), "TabLarge", x, y - 30, COLOR_RED, TEXT_ALIGN_CENTER, TEXT_ALIGN_BOTTOM)
|
||||
end
|
||||
|
||||
return self.BaseClass.DrawHUD(self)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
96
gamemodes/terrortown/entities/weapons/weapon_ttt_m16.lua
Normal file
96
gamemodes/terrortown/entities/weapons/weapon_ttt_m16.lua
Normal file
@@ -0,0 +1,96 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
AddCSLuaFile()
|
||||
|
||||
SWEP.HoldType = "ar2"
|
||||
|
||||
if CLIENT then
|
||||
SWEP.PrintName = "M16"
|
||||
SWEP.Slot = 2
|
||||
|
||||
SWEP.ViewModelFlip = false
|
||||
SWEP.ViewModelFOV = 64
|
||||
|
||||
SWEP.Icon = "vgui/ttt/icon_m16"
|
||||
SWEP.IconLetter = "w"
|
||||
end
|
||||
|
||||
SWEP.Base = "weapon_tttbase"
|
||||
|
||||
SWEP.Kind = WEAPON_HEAVY
|
||||
SWEP.WeaponID = AMMO_M16
|
||||
|
||||
SWEP.Primary.Delay = 0.19
|
||||
SWEP.Primary.Recoil = 1.6
|
||||
SWEP.Primary.Automatic = true
|
||||
SWEP.Primary.Ammo = "Pistol"
|
||||
SWEP.Primary.Damage = 23
|
||||
SWEP.Primary.Cone = 0.018
|
||||
SWEP.Primary.ClipSize = 20
|
||||
SWEP.Primary.ClipMax = 60
|
||||
SWEP.Primary.DefaultClip = 20
|
||||
SWEP.Primary.Sound = Sound( "Weapon_M4A1.Single" )
|
||||
|
||||
SWEP.AutoSpawnable = true
|
||||
SWEP.Spawnable = true
|
||||
SWEP.AmmoEnt = "item_ammo_pistol_ttt"
|
||||
|
||||
SWEP.UseHands = true
|
||||
SWEP.ViewModel = "models/weapons/cstrike/c_rif_m4a1.mdl"
|
||||
SWEP.WorldModel = "models/weapons/w_rif_m4a1.mdl"
|
||||
|
||||
SWEP.IronSightsPos = Vector(-7.58, -9.2, 0.55)
|
||||
SWEP.IronSightsAng = Vector(2.599, -1.3, -3.6)
|
||||
|
||||
function SWEP:SetZoom(state)
|
||||
if not (IsValid(self:GetOwner()) and self:GetOwner():IsPlayer()) then return end
|
||||
if state then
|
||||
self:GetOwner():SetFOV(35, 0.5)
|
||||
else
|
||||
self:GetOwner():SetFOV(0, 0.2)
|
||||
end
|
||||
end
|
||||
|
||||
-- Add some zoom to ironsights for this gun
|
||||
function SWEP:SecondaryAttack()
|
||||
if not self.IronSightsPos then return end
|
||||
if self:GetNextSecondaryFire() > CurTime() then return end
|
||||
|
||||
local bIronsights = not self:GetIronsights()
|
||||
|
||||
self:SetIronsights( bIronsights )
|
||||
|
||||
self:SetZoom( bIronsights )
|
||||
|
||||
self:SetNextSecondaryFire( CurTime() + 0.3 )
|
||||
end
|
||||
|
||||
function SWEP:PreDrop()
|
||||
self:SetZoom(false)
|
||||
self:SetIronsights(false)
|
||||
return self.BaseClass.PreDrop(self)
|
||||
end
|
||||
|
||||
function SWEP:Reload()
|
||||
if (self:Clip1() == self.Primary.ClipSize or
|
||||
self:GetOwner():GetAmmoCount(self.Primary.Ammo) <= 0) then
|
||||
return
|
||||
end
|
||||
self:DefaultReload(ACT_VM_RELOAD)
|
||||
self:SetIronsights(false)
|
||||
self:SetZoom(false)
|
||||
end
|
||||
|
||||
function SWEP:Holster()
|
||||
self:SetIronsights(false)
|
||||
self:SetZoom(false)
|
||||
return true
|
||||
end
|
||||
387
gamemodes/terrortown/entities/weapons/weapon_ttt_phammer.lua
Normal file
387
gamemodes/terrortown/entities/weapons/weapon_ttt_phammer.lua
Normal file
@@ -0,0 +1,387 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
AddCSLuaFile()
|
||||
|
||||
DEFINE_BASECLASS "weapon_tttbase"
|
||||
|
||||
SWEP.HoldType = "ar2"
|
||||
|
||||
if CLIENT then
|
||||
SWEP.PrintName = "polter_name"
|
||||
SWEP.Slot = 7
|
||||
|
||||
SWEP.ViewModelFlip = false
|
||||
SWEP.ViewModelFOV = 54
|
||||
|
||||
SWEP.EquipMenuData = {
|
||||
type = "item_weapon",
|
||||
desc = "polter_desc"
|
||||
};
|
||||
|
||||
SWEP.Icon = "vgui/ttt/icon_polter"
|
||||
end
|
||||
|
||||
SWEP.Base = "weapon_tttbase"
|
||||
|
||||
SWEP.Primary.Recoil = 0.1
|
||||
SWEP.Primary.Delay = 12.0
|
||||
SWEP.Primary.Cone = 0.02
|
||||
SWEP.Primary.ClipSize = 6
|
||||
SWEP.Primary.DefaultClip = 6
|
||||
SWEP.Primary.ClipMax = 6
|
||||
SWEP.Primary.Ammo = "Gravity"
|
||||
SWEP.Primary.Automatic = false
|
||||
SWEP.Primary.Sound = Sound( "weapons/airboat/airboat_gun_energy1.wav" )
|
||||
|
||||
SWEP.Secondary.Automatic = false
|
||||
|
||||
SWEP.Kind = WEAPON_EQUIP2
|
||||
SWEP.CanBuy = {ROLE_TRAITOR} -- only traitors can buy
|
||||
SWEP.WeaponID = AMMO_POLTER
|
||||
|
||||
SWEP.UseHands = true
|
||||
SWEP.ViewModel = "models/weapons/c_irifle.mdl"
|
||||
SWEP.WorldModel = "models/weapons/w_IRifle.mdl"
|
||||
|
||||
SWEP.NoSights = true
|
||||
|
||||
SWEP.IsCharging = false
|
||||
SWEP.NextCharge = 0
|
||||
SWEP.MaxRange = 800
|
||||
|
||||
AccessorFuncDT(SWEP, "charge", "Charge")
|
||||
|
||||
local math = math
|
||||
|
||||
-- Returns if an entity is a valid physhammer punching target. Does not take
|
||||
-- distance into account.
|
||||
local function ValidTarget(ent)
|
||||
return IsValid(ent) and ent:GetMoveType() == MOVETYPE_VPHYSICS and ent:GetPhysicsObject() and (not ent:IsWeapon()) and (not ent:GetNWBool("punched", false)) and (not ent:IsPlayer())
|
||||
-- NOTE: cannot check for motion disabled on client
|
||||
end
|
||||
|
||||
function SWEP:SetupDataTables()
|
||||
self:DTVar("Float", 0, "charge")
|
||||
end
|
||||
|
||||
local ghostmdl = Model("models/Items/combine_rifle_ammo01.mdl")
|
||||
function SWEP:Initialize()
|
||||
if CLIENT then
|
||||
-- create ghosted indicator
|
||||
local ghost = ents.CreateClientProp(ghostmdl)
|
||||
if IsValid(ghost) then
|
||||
ghost:SetPos(self:GetPos())
|
||||
ghost:Spawn()
|
||||
|
||||
-- PhysPropClientside whines here about not being able to parse the
|
||||
-- physmodel. This is not important as we won't use that anyway, and it
|
||||
-- happens in sandbox as well for the ghosted ents used there.
|
||||
|
||||
ghost:SetSolid(SOLID_NONE)
|
||||
ghost:SetMoveType(MOVETYPE_NONE)
|
||||
ghost:SetNotSolid(true)
|
||||
ghost:SetRenderMode(RENDERMODE_TRANSCOLOR)
|
||||
ghost:AddEffects(EF_NOSHADOW)
|
||||
ghost:SetNoDraw(true)
|
||||
|
||||
self.Ghost = ghost
|
||||
end
|
||||
end
|
||||
|
||||
self.IsCharging = false
|
||||
self:SetCharge(0)
|
||||
|
||||
return self.BaseClass.Initialize(self)
|
||||
end
|
||||
|
||||
function SWEP:PreDrop()
|
||||
self.IsCharging = false
|
||||
self:SetCharge(0)
|
||||
|
||||
-- OnDrop does not happen on client
|
||||
self:CallOnClient("HideGhost", "")
|
||||
end
|
||||
|
||||
function SWEP:HideGhost()
|
||||
if IsValid(self.Ghost) then
|
||||
self.Ghost:SetNoDraw(true)
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:PrimaryAttack()
|
||||
self:SetNextPrimaryFire(CurTime() + 0.1)
|
||||
if not self:CanPrimaryAttack() then return end
|
||||
if IsValid(self.hammer) then return end
|
||||
if SERVER then
|
||||
if self.IsCharging then return end
|
||||
|
||||
local ply = self:GetOwner()
|
||||
if not IsValid(ply) then return end
|
||||
|
||||
local tr = util.TraceLine({start=ply:GetShootPos(), endpos=ply:GetShootPos() + ply:GetAimVector() * self.MaxRange, filter={ply, self}, mask=MASK_SOLID})
|
||||
|
||||
if tr.HitNonWorld and ValidTarget(tr.Entity) and tr.Entity:GetPhysicsObject():IsMoveable() then
|
||||
|
||||
self:CreateHammer(tr.Entity, tr.HitPos)
|
||||
|
||||
self:EmitSound(self.Primary.Sound)
|
||||
|
||||
self:TakePrimaryAmmo(1)
|
||||
|
||||
self:SetNextPrimaryFire(CurTime() + self.Primary.Delay)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:SecondaryAttack()
|
||||
if self.IsCharging then return end
|
||||
|
||||
self:SetNextSecondaryFire( CurTime() + 0.1 )
|
||||
|
||||
if not (self:CanPrimaryAttack() and (self:GetNextPrimaryFire() - CurTime()) <= 0) then return end
|
||||
if IsValid(self.hammer) then return end
|
||||
if SERVER then
|
||||
local ply = self:GetOwner()
|
||||
if not IsValid(ply) then return end
|
||||
|
||||
local range = 30000
|
||||
|
||||
local tr = util.TraceLine({start=ply:GetShootPos(), endpos=ply:GetShootPos() + ply:GetAimVector() * range, filter={ply, self}, mask=MASK_SOLID})
|
||||
|
||||
if tr.HitNonWorld and ValidTarget(tr.Entity) and tr.Entity:GetPhysicsObject():IsMoveable() then
|
||||
|
||||
if self.IsCharging and self:GetCharge() >= 1 then
|
||||
return
|
||||
elseif tr.Fraction * range > self.MaxRange then
|
||||
self.IsCharging = true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:CreateHammer(tgt, pos)
|
||||
local hammer = ents.Create("ttt_physhammer")
|
||||
if IsValid(hammer) then
|
||||
local ang = self:GetOwner():GetAimVector():Angle()
|
||||
ang:RotateAroundAxis(ang:Right(), 90)
|
||||
|
||||
hammer:SetPos(pos)
|
||||
hammer:SetAngles(ang)
|
||||
|
||||
hammer:Spawn()
|
||||
|
||||
hammer:SetOwner(self:GetOwner())
|
||||
|
||||
local stuck = hammer:StickTo(tgt)
|
||||
|
||||
if not stuck then hammer:Remove() end
|
||||
self.hammer = hammer
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:OnRemove()
|
||||
if CLIENT and IsValid(self.Ghost) then
|
||||
self.Ghost:Remove()
|
||||
end
|
||||
|
||||
self.IsCharging = false
|
||||
self:SetCharge(0)
|
||||
end
|
||||
|
||||
function SWEP:Holster()
|
||||
if CLIENT and IsValid(self.Ghost) then
|
||||
self.Ghost:SetNoDraw(true)
|
||||
end
|
||||
|
||||
self.IsCharging = false
|
||||
self:SetCharge(0)
|
||||
|
||||
return self.BaseClass.Holster(self)
|
||||
end
|
||||
|
||||
|
||||
if SERVER then
|
||||
|
||||
local CHARGE_AMOUNT = 0.015
|
||||
local CHARGE_DELAY = 0.025
|
||||
|
||||
function SWEP:Think()
|
||||
BaseClass.Think(self)
|
||||
if not IsValid(self:GetOwner()) then return end
|
||||
|
||||
if self.IsCharging and self:GetOwner():KeyDown(IN_ATTACK2) then
|
||||
local tr = self:GetOwner():GetEyeTrace(MASK_SOLID)
|
||||
if tr.HitNonWorld and ValidTarget(tr.Entity) then
|
||||
|
||||
if self:GetCharge() >= 1 then
|
||||
self:CreateHammer(tr.Entity, tr.HitPos)
|
||||
|
||||
self:EmitSound(self.Primary.Sound)
|
||||
|
||||
self:TakePrimaryAmmo(1)
|
||||
|
||||
self:SetNextPrimaryFire(CurTime() + self.Primary.Delay)
|
||||
|
||||
self.IsCharging = false
|
||||
self:SetCharge(0)
|
||||
return true
|
||||
elseif self.NextCharge < CurTime() then
|
||||
local d = tr.Entity:GetPos():Distance(self:GetOwner():GetPos())
|
||||
local f = math.max(1, math.floor(d / self.MaxRange))
|
||||
|
||||
self:SetCharge(math.min(1, self:GetCharge() + (CHARGE_AMOUNT / f)))
|
||||
|
||||
self.NextCharge = CurTime() + CHARGE_DELAY
|
||||
end
|
||||
else
|
||||
self.IsCharging = false
|
||||
self:SetCharge(0)
|
||||
end
|
||||
|
||||
elseif self:GetCharge() > 0 then
|
||||
-- owner let go of rmouse
|
||||
self:SetCharge(0)
|
||||
self.IsCharging = false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function around( val )
|
||||
return math.Round( val * (10 ^ 3) ) / (10 ^ 3);
|
||||
end
|
||||
|
||||
if CLIENT then
|
||||
local surface = surface
|
||||
|
||||
function SWEP:UpdateGhost(pos, c, a)
|
||||
if IsValid(self.Ghost) then
|
||||
if self.Ghost:GetPos() != pos then
|
||||
self.Ghost:SetPos(pos)
|
||||
local ang = LocalPlayer():GetAimVector():Angle()
|
||||
ang:RotateAroundAxis(ang:Right(), 90)
|
||||
|
||||
self.Ghost:SetAngles(ang)
|
||||
|
||||
self.Ghost:SetColor(Color(c.r, c.g, c.b, a))
|
||||
|
||||
self.Ghost:SetNoDraw(false)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local linex = 0
|
||||
local liney = 0
|
||||
local laser = Material("trails/laser")
|
||||
function SWEP:ViewModelDrawn()
|
||||
local client = LocalPlayer()
|
||||
local vm = client:GetViewModel()
|
||||
if not IsValid(vm) then return end
|
||||
|
||||
local plytr = client:GetEyeTrace(MASK_SHOT)
|
||||
|
||||
local muzzle_angpos = vm:GetAttachment(1)
|
||||
local spos = muzzle_angpos.Pos + muzzle_angpos.Ang:Forward() * 10
|
||||
local epos = client:GetShootPos() + client:GetAimVector() * self.MaxRange
|
||||
|
||||
-- Painting beam
|
||||
local tr = util.TraceLine({start=spos, endpos=epos, filter=client, mask=MASK_ALL})
|
||||
|
||||
local c = COLOR_RED
|
||||
local a = 150
|
||||
local d = (plytr.StartPos - plytr.HitPos):Length()
|
||||
if plytr.HitNonWorld then
|
||||
if ValidTarget(plytr.Entity) then
|
||||
if d < self.MaxRange then
|
||||
c = COLOR_GREEN
|
||||
a = 255
|
||||
else
|
||||
c = COLOR_YELLOW
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
self:UpdateGhost(plytr.HitPos, c, a)
|
||||
|
||||
render.SetMaterial(laser)
|
||||
render.DrawBeam(spos, tr.HitPos, 5, 0, 0, c)
|
||||
|
||||
-- Charge indicator
|
||||
local vm_ang = muzzle_angpos.Ang
|
||||
local cpos = muzzle_angpos.Pos + (vm_ang:Up() * -8) + (vm_ang:Forward() * -5.5) + (vm_ang:Right() * 0)
|
||||
local cang = vm:GetAngles()
|
||||
cang:RotateAroundAxis(cang:Forward(), 90)
|
||||
cang:RotateAroundAxis(cang:Right(), 90)
|
||||
cang:RotateAroundAxis(cang:Up(), 90)
|
||||
|
||||
cam.Start3D2D(cpos, cang, 0.05)
|
||||
|
||||
surface.SetDrawColor(255, 55, 55, 50)
|
||||
surface.DrawOutlinedRect(0, 0, 50, 15)
|
||||
|
||||
local sz = 48
|
||||
local next = self:GetNextPrimaryFire()
|
||||
local ready = (next - CurTime()) <= 0
|
||||
local frac = 1.0
|
||||
if not ready then
|
||||
frac = 1 - ((next - CurTime()) / self.Primary.Delay)
|
||||
sz = sz * math.max(0, frac)
|
||||
end
|
||||
|
||||
surface.SetDrawColor(255, 10, 10, 170)
|
||||
surface.DrawRect(1, 1, sz, 13)
|
||||
|
||||
surface.SetTextColor(255,255,255,15)
|
||||
surface.SetFont("Default")
|
||||
surface.SetTextPos(2,0)
|
||||
surface.DrawText(string.format("%.3f", around(frac)))
|
||||
|
||||
surface.SetDrawColor(0,0,0, 80)
|
||||
surface.DrawRect(linex, 1, 3, 13)
|
||||
|
||||
surface.DrawLine(1, liney, 48, liney)
|
||||
|
||||
linex = linex + 3 > 48 and 0 or linex + 1
|
||||
liney = liney > 13 and 0 or liney + 1
|
||||
|
||||
cam.End3D2D()
|
||||
|
||||
end
|
||||
|
||||
local draw = draw
|
||||
function SWEP:DrawHUD()
|
||||
local x = ScrW() / 2.0
|
||||
local y = ScrH() / 2.0
|
||||
|
||||
|
||||
local charge = self.dt.charge
|
||||
|
||||
if charge > 0 then
|
||||
y = y + (y / 3)
|
||||
|
||||
local w, h = 100, 20
|
||||
|
||||
surface.DrawOutlinedRect(x - w/2, y - h, w, h)
|
||||
|
||||
if LocalPlayer():IsTraitor() then
|
||||
surface.SetDrawColor(255, 0, 0, 155)
|
||||
else
|
||||
surface.SetDrawColor(0, 255, 0, 155)
|
||||
end
|
||||
|
||||
surface.DrawRect(x - w/2, y - h, w * charge, h)
|
||||
|
||||
surface.SetFont("TabLarge")
|
||||
surface.SetTextColor(255, 255, 255, 180)
|
||||
surface.SetTextPos( (x - w / 2) + 3, y - h - 15)
|
||||
surface.DrawText("CHARGE")
|
||||
end
|
||||
end
|
||||
end
|
||||
271
gamemodes/terrortown/entities/weapons/weapon_ttt_push.lua
Normal file
271
gamemodes/terrortown/entities/weapons/weapon_ttt_push.lua
Normal file
@@ -0,0 +1,271 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
AddCSLuaFile()
|
||||
|
||||
DEFINE_BASECLASS "weapon_tttbase"
|
||||
|
||||
SWEP.HoldType = "physgun"
|
||||
|
||||
if CLIENT then
|
||||
SWEP.PrintName = "newton_name"
|
||||
SWEP.Slot = 7
|
||||
|
||||
SWEP.ViewModelFlip = false
|
||||
SWEP.ViewModelFOV = 54
|
||||
|
||||
SWEP.EquipMenuData = {
|
||||
type = "item_weapon",
|
||||
desc = "newton_desc"
|
||||
};
|
||||
|
||||
SWEP.Icon = "vgui/ttt/icon_launch"
|
||||
end
|
||||
|
||||
SWEP.Base = "weapon_tttbase"
|
||||
|
||||
SWEP.Primary.Ammo = "none"
|
||||
SWEP.Primary.ClipSize = -1
|
||||
SWEP.Primary.DefaultClip = -1
|
||||
SWEP.Primary.Automatic = true
|
||||
SWEP.Primary.Delay = 3
|
||||
SWEP.Primary.Cone = 0.005
|
||||
SWEP.Primary.Sound = Sound( "weapons/ar2/fire1.wav" )
|
||||
SWEP.Primary.SoundLevel = 54
|
||||
|
||||
SWEP.Secondary.ClipSize = -1
|
||||
SWEP.Secondary.DefaultClip = -1
|
||||
SWEP.Secondary.Automatic = false
|
||||
SWEP.Secondary.Ammo = "none"
|
||||
SWEP.Secondary.Delay = 0.5
|
||||
|
||||
SWEP.NoSights = true
|
||||
|
||||
SWEP.Kind = WEAPON_EQUIP2
|
||||
SWEP.CanBuy = {ROLE_TRAITOR}
|
||||
SWEP.WeaponID = AMMO_PUSH
|
||||
|
||||
SWEP.UseHands = true
|
||||
SWEP.ViewModel = "models/weapons/c_superphyscannon.mdl"
|
||||
SWEP.WorldModel = "models/weapons/w_physics.mdl"
|
||||
|
||||
AccessorFuncDT(SWEP, "charge", "Charge")
|
||||
|
||||
SWEP.IsCharging = false
|
||||
SWEP.NextCharge = 0
|
||||
|
||||
local CHARGE_AMOUNT = 0.02
|
||||
local CHARGE_DELAY = 0.025
|
||||
|
||||
local math = math
|
||||
|
||||
function SWEP:Initialize()
|
||||
if SERVER then
|
||||
self:SetSkin(1)
|
||||
end
|
||||
return self.BaseClass.Initialize(self)
|
||||
end
|
||||
|
||||
function SWEP:SetupDataTables()
|
||||
self:DTVar("Float", 0, "charge")
|
||||
end
|
||||
|
||||
function SWEP:PrimaryAttack()
|
||||
if self.IsCharging then return end
|
||||
|
||||
self:SetNextPrimaryFire( CurTime() + self.Primary.Delay )
|
||||
self:SetNextSecondaryFire( CurTime() + self.Primary.Delay )
|
||||
|
||||
self:FirePulse(600, 300)
|
||||
end
|
||||
|
||||
function SWEP:SecondaryAttack()
|
||||
if self.IsCharging then return end
|
||||
|
||||
self:SetNextPrimaryFire( CurTime() + self.Primary.Delay )
|
||||
self:SetNextSecondaryFire( CurTime() + self.Primary.Delay )
|
||||
|
||||
self.IsCharging = true
|
||||
end
|
||||
|
||||
function SWEP:FirePulse(force_fwd, force_up)
|
||||
if not IsValid(self:GetOwner()) then return end
|
||||
|
||||
self:GetOwner():SetAnimation( PLAYER_ATTACK1 )
|
||||
|
||||
sound.Play(self.Primary.Sound, self:GetPos(), self.Primary.SoundLevel)
|
||||
|
||||
self:SendWeaponAnim(ACT_VM_IDLE)
|
||||
|
||||
local cone = self.Primary.Cone or 0.1
|
||||
local num = 6
|
||||
|
||||
local bullet = {}
|
||||
bullet.Num = num
|
||||
bullet.Src = self:GetOwner():GetShootPos()
|
||||
bullet.Dir = self:GetOwner():GetAimVector()
|
||||
bullet.Spread = Vector( cone, cone, 0 )
|
||||
bullet.Tracer = 1
|
||||
bullet.Force = force_fwd / 10
|
||||
bullet.Damage = 1
|
||||
bullet.TracerName = "AirboatGunHeavyTracer"
|
||||
|
||||
local owner = self:GetOwner()
|
||||
local fwd = force_fwd / num
|
||||
local up = force_up / num
|
||||
bullet.Callback = function(att, tr, dmginfo)
|
||||
local ply = tr.Entity
|
||||
if SERVER and IsValid(ply) and ply:IsPlayer() and (not ply:IsFrozen()) then
|
||||
local pushvel = tr.Normal * fwd
|
||||
|
||||
pushvel.z = math.max(pushvel.z, up)
|
||||
|
||||
ply:SetGroundEntity(nil)
|
||||
ply:SetLocalVelocity(ply:GetVelocity() + pushvel)
|
||||
|
||||
ply.was_pushed = {att=owner, t=CurTime(), wep=self:GetClass()}
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
self:GetOwner():FireBullets( bullet )
|
||||
|
||||
end
|
||||
|
||||
local CHARGE_FORCE_FWD_MIN = 300
|
||||
local CHARGE_FORCE_FWD_MAX = 700
|
||||
local CHARGE_FORCE_UP_MIN = 100
|
||||
local CHARGE_FORCE_UP_MAX = 350
|
||||
function SWEP:ChargedAttack()
|
||||
local charge = math.Clamp(self:GetCharge(), 0, 1)
|
||||
|
||||
self.IsCharging = false
|
||||
self:SetCharge(0)
|
||||
|
||||
if charge <= 0 then return end
|
||||
|
||||
local max = CHARGE_FORCE_FWD_MAX
|
||||
local diff = max - CHARGE_FORCE_FWD_MIN
|
||||
|
||||
local force_fwd = ((charge * diff) - diff) + max
|
||||
|
||||
max = CHARGE_FORCE_UP_MAX
|
||||
diff = max - CHARGE_FORCE_UP_MIN
|
||||
|
||||
local force_up = ((charge * diff) - diff) + max
|
||||
|
||||
self:SetNextPrimaryFire( CurTime() + self.Primary.Delay )
|
||||
self:SetNextSecondaryFire( CurTime() + self.Primary.Delay )
|
||||
|
||||
self:FirePulse(force_fwd, force_up)
|
||||
end
|
||||
|
||||
function SWEP:PreDrop(death_drop)
|
||||
-- allow dropping for now, see if it helps against heisenbug on owner death
|
||||
-- if death_drop then
|
||||
self.IsCharging = false
|
||||
self:SetCharge(0)
|
||||
-- elseif self.IsCharging then
|
||||
-- self:ChargedAttack()
|
||||
-- end
|
||||
end
|
||||
|
||||
function SWEP:OnRemove()
|
||||
self.IsCharging = false
|
||||
self:SetCharge(0)
|
||||
end
|
||||
|
||||
function SWEP:Deploy()
|
||||
self.IsCharging = false
|
||||
self:SetCharge(0)
|
||||
return true
|
||||
end
|
||||
|
||||
function SWEP:Holster()
|
||||
return not self.IsCharging
|
||||
end
|
||||
|
||||
function SWEP:Think()
|
||||
BaseClass.Think(self)
|
||||
if self.IsCharging and IsValid(self:GetOwner()) and self:GetOwner():IsTerror() then
|
||||
-- on client this is prediction
|
||||
if not self:GetOwner():KeyDown(IN_ATTACK2) then
|
||||
self:ChargedAttack()
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
if SERVER and self:GetCharge() < 1 and self.NextCharge < CurTime() then
|
||||
self:SetCharge(math.min(1, self:GetCharge() + CHARGE_AMOUNT))
|
||||
|
||||
self.NextCharge = CurTime() + CHARGE_DELAY
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if CLIENT then
|
||||
local surface = surface
|
||||
function SWEP:DrawHUD()
|
||||
local x = ScrW() / 2.0
|
||||
local y = ScrH() / 2.0
|
||||
|
||||
local nxt = self:GetNextPrimaryFire()
|
||||
local charge = self.dt.charge
|
||||
|
||||
if LocalPlayer():IsTraitor() then
|
||||
surface.SetDrawColor(255, 0, 0, 255)
|
||||
else
|
||||
surface.SetDrawColor(0, 255, 0, 255)
|
||||
end
|
||||
|
||||
if nxt < CurTime() or CurTime() % 0.5 < 0.2 or charge > 0 then
|
||||
local length = 10
|
||||
local gap = 5
|
||||
|
||||
surface.DrawLine( x - length, y, x - gap, y )
|
||||
surface.DrawLine( x + length, y, x + gap, y )
|
||||
surface.DrawLine( x, y - length, x, y - gap )
|
||||
surface.DrawLine( x, y + length, x, y + gap )
|
||||
end
|
||||
|
||||
if nxt > CurTime() and charge == 0 then
|
||||
local w = 40
|
||||
|
||||
w = (w * ( math.max(0, nxt - CurTime()) / self.Primary.Delay )) / 2
|
||||
|
||||
local bx = x + 30
|
||||
surface.DrawLine(bx, y - w, bx, y + w)
|
||||
|
||||
bx = x - 30
|
||||
surface.DrawLine(bx, y - w, bx, y + w)
|
||||
end
|
||||
|
||||
if charge > 0 then
|
||||
y = y + (y / 3)
|
||||
|
||||
local w, h = 100, 20
|
||||
|
||||
surface.DrawOutlinedRect(x - w/2, y - h, w, h)
|
||||
|
||||
if LocalPlayer():IsTraitor() then
|
||||
surface.SetDrawColor(255, 0, 0, 155)
|
||||
else
|
||||
surface.SetDrawColor(0, 255, 0, 155)
|
||||
end
|
||||
|
||||
surface.DrawRect(x - w/2, y - h, w * charge, h)
|
||||
|
||||
surface.SetFont("TabLarge")
|
||||
surface.SetTextColor(255, 255, 255, 180)
|
||||
surface.SetTextPos( (x - w / 2) + 3, y - h - 15)
|
||||
surface.DrawText("FORCE")
|
||||
end
|
||||
end
|
||||
end
|
||||
185
gamemodes/terrortown/entities/weapons/weapon_ttt_radio.lua
Normal file
185
gamemodes/terrortown/entities/weapons/weapon_ttt_radio.lua
Normal file
@@ -0,0 +1,185 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
-- traitor equipment: radio
|
||||
|
||||
AddCSLuaFile()
|
||||
|
||||
SWEP.HoldType = "normal"
|
||||
|
||||
if CLIENT then
|
||||
SWEP.PrintName = "radio_name"
|
||||
SWEP.Slot = 7
|
||||
|
||||
SWEP.ViewModelFlip = false
|
||||
SWEP.ViewModelFOV = 10
|
||||
SWEP.DrawCrosshair = false
|
||||
|
||||
SWEP.EquipMenuData = {
|
||||
type = "item_weapon",
|
||||
desc = "radio_desc"
|
||||
};
|
||||
|
||||
SWEP.Icon = "vgui/ttt/icon_radio"
|
||||
end
|
||||
|
||||
SWEP.Base = "weapon_tttbase"
|
||||
|
||||
SWEP.ViewModel = "models/weapons/v_crowbar.mdl"
|
||||
SWEP.WorldModel = "models/props/cs_office/radio.mdl"
|
||||
|
||||
SWEP.Primary.ClipSize = -1
|
||||
SWEP.Primary.DefaultClip = -1
|
||||
SWEP.Primary.Automatic = true
|
||||
SWEP.Primary.Ammo = "none"
|
||||
SWEP.Primary.Delay = 1.0
|
||||
|
||||
SWEP.Secondary.ClipSize = -1
|
||||
SWEP.Secondary.DefaultClip = -1
|
||||
SWEP.Secondary.Automatic = true
|
||||
SWEP.Secondary.Ammo = "none"
|
||||
SWEP.Secondary.Delay = 1.0
|
||||
|
||||
SWEP.Kind = WEAPON_EQUIP2
|
||||
SWEP.CanBuy = {ROLE_TRAITOR} -- only traitors can buy
|
||||
SWEP.LimitedStock = true -- only buyable once
|
||||
SWEP.WeaponID = AMMO_RADIO
|
||||
|
||||
SWEP.AllowDrop = false
|
||||
SWEP.NoSights = true
|
||||
|
||||
function SWEP:OnDrop()
|
||||
self:Remove()
|
||||
end
|
||||
|
||||
function SWEP:PrimaryAttack()
|
||||
self:SetNextPrimaryFire( CurTime() + self.Primary.Delay )
|
||||
self:RadioDrop()
|
||||
end
|
||||
function SWEP:SecondaryAttack()
|
||||
self:SetNextSecondaryFire( CurTime() + self.Secondary.Delay )
|
||||
self:RadioStick()
|
||||
end
|
||||
|
||||
local throwsound = Sound( "Weapon_SLAM.SatchelThrow" )
|
||||
|
||||
-- c4 plant but different
|
||||
function SWEP:RadioDrop()
|
||||
if SERVER then
|
||||
local ply = self:GetOwner()
|
||||
if not IsValid(ply) then return end
|
||||
|
||||
if self.Planted then return end
|
||||
|
||||
local vsrc = ply:GetShootPos()
|
||||
local vang = ply:GetAimVector()
|
||||
local vvel = ply:GetVelocity()
|
||||
|
||||
local vthrow = vvel + vang * 200
|
||||
|
||||
local radio = ents.Create("ttt_radio")
|
||||
if IsValid(radio) then
|
||||
radio:SetPos(vsrc + vang * 10)
|
||||
radio:SetOwner(ply)
|
||||
radio:Spawn()
|
||||
|
||||
radio:PhysWake()
|
||||
local phys = radio:GetPhysicsObject()
|
||||
if IsValid(phys) then
|
||||
phys:SetVelocity(vthrow)
|
||||
end
|
||||
self:Remove()
|
||||
|
||||
self.Planted = true
|
||||
end
|
||||
end
|
||||
|
||||
self:EmitSound(throwsound)
|
||||
end
|
||||
|
||||
-- hey look, more C4 code
|
||||
function SWEP:RadioStick()
|
||||
if SERVER then
|
||||
local ply = self:GetOwner()
|
||||
if not IsValid(ply) then return end
|
||||
|
||||
if self.Planted then return end
|
||||
|
||||
local ignore = {ply, self}
|
||||
local spos = ply:GetShootPos()
|
||||
local epos = spos + ply:GetAimVector() * 80
|
||||
local tr = util.TraceLine({start=spos, endpos=epos, filter=ignore, mask=MASK_SOLID})
|
||||
|
||||
if tr.HitWorld then
|
||||
local radio = ents.Create("ttt_radio")
|
||||
if IsValid(radio) then
|
||||
radio:PointAtEntity(ply)
|
||||
|
||||
local tr_ent = util.TraceEntity({start=spos, endpos=epos, filter=ignore, mask=MASK_SOLID}, radio)
|
||||
|
||||
if tr_ent.HitWorld then
|
||||
|
||||
local ang = tr_ent.HitNormal:Angle()
|
||||
ang:RotateAroundAxis(ang:Up(), -180)
|
||||
|
||||
radio:SetPos(tr_ent.HitPos + ang:Forward() * -2.5)
|
||||
radio:SetAngles(ang)
|
||||
radio:SetOwner(ply)
|
||||
radio:Spawn()
|
||||
|
||||
local phys = radio:GetPhysicsObject()
|
||||
if IsValid(phys) then
|
||||
phys:EnableMotion(false)
|
||||
end
|
||||
|
||||
radio.IsOnWall = true
|
||||
|
||||
self:Remove()
|
||||
|
||||
self.Planted = true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:Reload()
|
||||
return false
|
||||
end
|
||||
|
||||
function SWEP:OnRemove()
|
||||
if CLIENT and IsValid(self:GetOwner()) and self:GetOwner() == LocalPlayer() and self:GetOwner():Alive() then
|
||||
RunConsoleCommand("lastinv")
|
||||
end
|
||||
end
|
||||
|
||||
if CLIENT then
|
||||
function SWEP:Initialize()
|
||||
self:AddHUDHelp("radio_help_pri", nil, true)
|
||||
|
||||
return self.BaseClass.Initialize(self)
|
||||
end
|
||||
|
||||
end
|
||||
-- Invisible, same hacks as holstered weapon
|
||||
|
||||
|
||||
function SWEP:Deploy()
|
||||
if SERVER and IsValid(self:GetOwner()) then
|
||||
self:GetOwner():DrawViewModel(false)
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function SWEP:DrawWorldModel()
|
||||
end
|
||||
|
||||
function SWEP:DrawWorldModelTranslucent()
|
||||
end
|
||||
@@ -0,0 +1,72 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
AddCSLuaFile()
|
||||
|
||||
SWEP.HoldType = "pistol"
|
||||
|
||||
if CLIENT then
|
||||
SWEP.PrintName = "sipistol_name"
|
||||
SWEP.Slot = 6
|
||||
|
||||
SWEP.ViewModelFlip = false
|
||||
SWEP.ViewModelFOV = 54
|
||||
|
||||
SWEP.EquipMenuData = {
|
||||
type = "item_weapon",
|
||||
desc = "sipistol_desc"
|
||||
};
|
||||
|
||||
SWEP.Icon = "vgui/ttt/icon_silenced"
|
||||
SWEP.IconLetter = "a"
|
||||
end
|
||||
|
||||
SWEP.Base = "weapon_tttbase"
|
||||
|
||||
SWEP.Primary.Recoil = 1.35
|
||||
SWEP.Primary.Damage = 28
|
||||
SWEP.Primary.Delay = 0.38
|
||||
SWEP.Primary.Cone = 0.02
|
||||
SWEP.Primary.ClipSize = 20
|
||||
SWEP.Primary.Automatic = true
|
||||
SWEP.Primary.DefaultClip = 20
|
||||
SWEP.Primary.ClipMax = 60
|
||||
SWEP.Primary.Ammo = "Pistol"
|
||||
SWEP.Primary.Sound = Sound( "Weapon_USP.SilencedShot" )
|
||||
SWEP.Primary.SoundLevel = 50
|
||||
|
||||
SWEP.Kind = WEAPON_EQUIP
|
||||
SWEP.CanBuy = {ROLE_TRAITOR} -- only traitors can buy
|
||||
SWEP.WeaponID = AMMO_SIPISTOL
|
||||
|
||||
SWEP.AmmoEnt = "item_ammo_pistol_ttt"
|
||||
SWEP.IsSilent = true
|
||||
|
||||
SWEP.UseHands = true
|
||||
SWEP.ViewModel = "models/weapons/cstrike/c_pist_usp.mdl"
|
||||
SWEP.WorldModel = "models/weapons/w_pist_usp_silencer.mdl"
|
||||
|
||||
SWEP.IronSightsPos = Vector( -5.91, -4, 2.84 )
|
||||
SWEP.IronSightsAng = Vector(-0.5, 0, 0)
|
||||
|
||||
SWEP.PrimaryAnim = ACT_VM_PRIMARYATTACK_SILENCED
|
||||
SWEP.ReloadAnim = ACT_VM_RELOAD_SILENCED
|
||||
|
||||
function SWEP:Deploy()
|
||||
self:SendWeaponAnim(ACT_VM_DRAW_SILENCED)
|
||||
return self.BaseClass.Deploy(self)
|
||||
end
|
||||
|
||||
-- We were bought as special equipment, and we have an extra to give
|
||||
function SWEP:WasBought(buyer)
|
||||
if IsValid(buyer) then -- probably already self:GetOwner()
|
||||
buyer:GiveAmmo( 20, "Pistol" )
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,43 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
AddCSLuaFile()
|
||||
|
||||
SWEP.HoldType = "grenade"
|
||||
|
||||
if CLIENT then
|
||||
SWEP.PrintName = "grenade_smoke"
|
||||
SWEP.Slot = 3
|
||||
|
||||
SWEP.ViewModelFlip = false
|
||||
SWEP.ViewModelFOV = 54
|
||||
|
||||
SWEP.Icon = "vgui/ttt/icon_nades"
|
||||
SWEP.IconLetter = "Q"
|
||||
end
|
||||
|
||||
SWEP.Base = "weapon_tttbasegrenade"
|
||||
|
||||
SWEP.WeaponID = AMMO_SMOKE
|
||||
SWEP.Kind = WEAPON_NADE
|
||||
|
||||
SWEP.UseHands = true
|
||||
SWEP.ViewModel = "models/weapons/cstrike/c_eq_smokegrenade.mdl"
|
||||
SWEP.WorldModel = "models/weapons/w_eq_smokegrenade.mdl"
|
||||
|
||||
SWEP.Weight = 5
|
||||
SWEP.AutoSpawnable = true
|
||||
SWEP.Spawnable = true
|
||||
-- really the only difference between grenade weapons: the model and the thrown
|
||||
-- ent.
|
||||
|
||||
function SWEP:GetGrenadeName()
|
||||
return "ttt_smokegrenade_proj"
|
||||
end
|
||||
125
gamemodes/terrortown/entities/weapons/weapon_ttt_stungun.lua
Normal file
125
gamemodes/terrortown/entities/weapons/weapon_ttt_stungun.lua
Normal file
@@ -0,0 +1,125 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
AddCSLuaFile()
|
||||
|
||||
SWEP.HoldType = "ar2"
|
||||
|
||||
if CLIENT then
|
||||
SWEP.PrintName = "stungun_name"
|
||||
SWEP.Slot = 6
|
||||
|
||||
SWEP.ViewModelFlip = false
|
||||
SWEP.ViewModelFOV = 54
|
||||
|
||||
SWEP.EquipMenuData = {
|
||||
type = "item_weapon",
|
||||
desc = "ump_desc"
|
||||
};
|
||||
|
||||
SWEP.Icon = "vgui/ttt/icon_ump"
|
||||
SWEP.IconLetter = "q"
|
||||
end
|
||||
|
||||
SWEP.Base = "weapon_tttbase"
|
||||
|
||||
SWEP.Kind = WEAPON_EQUIP
|
||||
SWEP.WeaponID = AMMO_STUN
|
||||
SWEP.CanBuy = {ROLE_DETECTIVE}
|
||||
SWEP.LimitedStock = false
|
||||
SWEP.AmmoEnt = "item_ammo_smg1_ttt"
|
||||
|
||||
SWEP.Primary.Damage = 9
|
||||
SWEP.Primary.Delay = 0.1
|
||||
SWEP.Primary.Cone = 0.02
|
||||
SWEP.Primary.ClipSize = 30
|
||||
SWEP.Primary.ClipMax = 60
|
||||
SWEP.Primary.DefaultClip = 30
|
||||
SWEP.Primary.Automatic = true
|
||||
SWEP.Primary.Ammo = "SMG1"
|
||||
SWEP.Primary.Recoil = 1.2
|
||||
SWEP.Primary.Sound = Sound( "Weapon_UMP45.Single" )
|
||||
|
||||
SWEP.UseHands = true
|
||||
SWEP.ViewModel = "models/weapons/cstrike/c_smg_ump45.mdl"
|
||||
SWEP.WorldModel = "models/weapons/w_smg_ump45.mdl"
|
||||
|
||||
SWEP.IronSightsPos = Vector(-8.735, -10, 4.039)
|
||||
SWEP.IronSightsAng = Vector(-1.201, -0.201, -2)
|
||||
|
||||
SWEP.HeadshotMultiplier = 4.5 -- brain fizz
|
||||
--SWEP.DeploySpeed = 3
|
||||
|
||||
function SWEP:ShootBullet( dmg, recoil, numbul, cone )
|
||||
local sights = self:GetIronsights()
|
||||
|
||||
numbul = numbul or 1
|
||||
cone = cone or 0.01
|
||||
|
||||
-- 10% accuracy bonus when sighting
|
||||
cone = sights and (cone * 0.9) or cone
|
||||
|
||||
local bullet = {}
|
||||
bullet.Num = numbul
|
||||
bullet.Src = self:GetOwner():GetShootPos()
|
||||
bullet.Dir = self:GetOwner():GetAimVector()
|
||||
bullet.Spread = Vector( cone, cone, 0 )
|
||||
bullet.Tracer = 4
|
||||
bullet.Force = 5
|
||||
bullet.Damage = dmg
|
||||
|
||||
bullet.Callback = function(att, tr, dmginfo)
|
||||
if SERVER or (CLIENT and IsFirstTimePredicted()) then
|
||||
local ent = tr.Entity
|
||||
if (not tr.HitWorld) and IsValid(ent) then
|
||||
local edata = EffectData()
|
||||
|
||||
edata:SetEntity(ent)
|
||||
edata:SetMagnitude(3)
|
||||
edata:SetScale(2)
|
||||
|
||||
util.Effect("TeslaHitBoxes", edata)
|
||||
|
||||
if SERVER and ent:IsPlayer() then
|
||||
local eyeang = ent:EyeAngles()
|
||||
|
||||
local j = 10
|
||||
eyeang.pitch = math.Clamp(eyeang.pitch + math.Rand(-j, j), -90, 90)
|
||||
eyeang.yaw = math.Clamp(eyeang.yaw + math.Rand(-j, j), -90, 90)
|
||||
ent:SetEyeAngles(eyeang)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
self:GetOwner():FireBullets( bullet )
|
||||
self:SendWeaponAnim(self.PrimaryAnim)
|
||||
|
||||
-- Owner can die after firebullets, giving an error at muzzleflash
|
||||
if not IsValid(self:GetOwner()) or not self:GetOwner():Alive() then return end
|
||||
|
||||
self:GetOwner():MuzzleFlash()
|
||||
self:GetOwner():SetAnimation( PLAYER_ATTACK1 )
|
||||
|
||||
if self:GetOwner():IsNPC() then return end
|
||||
|
||||
if ((game.SinglePlayer() and SERVER) or
|
||||
((not game.SinglePlayer()) and CLIENT and IsFirstTimePredicted() )) then
|
||||
|
||||
-- reduce recoil if ironsighting
|
||||
recoil = sights and (recoil * 0.75) or recoil
|
||||
|
||||
local eyeang = self:GetOwner():EyeAngles()
|
||||
eyeang.pitch = eyeang.pitch - recoil
|
||||
self:GetOwner():SetEyeAngles( eyeang )
|
||||
|
||||
end
|
||||
end
|
||||
350
gamemodes/terrortown/entities/weapons/weapon_ttt_teleport.lua
Normal file
350
gamemodes/terrortown/entities/weapons/weapon_ttt_teleport.lua
Normal file
@@ -0,0 +1,350 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
-- traitor equipment: teleporter
|
||||
|
||||
AddCSLuaFile()
|
||||
|
||||
SWEP.HoldType = "normal"
|
||||
|
||||
if CLIENT then
|
||||
SWEP.PrintName = "tele_name"
|
||||
SWEP.Slot = 7
|
||||
|
||||
SWEP.ViewModelFlip = false
|
||||
SWEP.ViewModelFOV = 10
|
||||
SWEP.DrawCrosshair = false
|
||||
SWEP.CSMuzzleFlashes = false
|
||||
|
||||
SWEP.EquipMenuData = {
|
||||
type = "item_weapon",
|
||||
desc = "tele_desc"
|
||||
};
|
||||
|
||||
SWEP.Icon = "vgui/ttt/icon_tport"
|
||||
end
|
||||
|
||||
SWEP.Base = "weapon_tttbase"
|
||||
|
||||
SWEP.ViewModel = "models/weapons/v_crowbar.mdl"
|
||||
SWEP.WorldModel = "models/weapons/w_slam.mdl"
|
||||
|
||||
SWEP.Primary.ClipSize = 16
|
||||
SWEP.Primary.DefaultClip = 16
|
||||
SWEP.Primary.ClipMax = 16
|
||||
SWEP.Primary.Automatic = false
|
||||
SWEP.Primary.Ammo = "GaussEnergy"
|
||||
SWEP.Primary.Delay = 0.5
|
||||
|
||||
SWEP.Secondary.Automatic = false
|
||||
SWEP.Secondary.Ammo = "none"
|
||||
SWEP.Secondary.Delay = 1.0
|
||||
|
||||
SWEP.Kind = WEAPON_EQUIP2
|
||||
SWEP.CanBuy = {ROLE_TRAITOR, ROLE_DETECTIVE}
|
||||
SWEP.WeaponID = AMMO_TELEPORT
|
||||
|
||||
SWEP.AllowDrop = true
|
||||
SWEP.NoSights = true
|
||||
|
||||
local delay_beamup = 1
|
||||
local delay_beamdown = 1
|
||||
|
||||
local ttt_telefrags = CreateConVar("ttt_teleport_telefrags", "1")
|
||||
|
||||
function SWEP:SetTeleportMark(pos, ang)
|
||||
self.teleport = {pos = pos, ang = ang}
|
||||
end
|
||||
|
||||
function SWEP:GetTeleportMark() return self.teleport end
|
||||
|
||||
function SWEP:PrimaryAttack()
|
||||
self:SetNextPrimaryFire( CurTime() + self.Primary.Delay )
|
||||
|
||||
if self:Clip1() <= 0 then
|
||||
self:DryFire(self.SetNextSecondaryFire)
|
||||
return
|
||||
end
|
||||
|
||||
-- Disallow initiating teleports during post, as it will occur across the
|
||||
-- restart and allow the user an advantage during prep
|
||||
if GetRoundState() == ROUND_POST then return end
|
||||
|
||||
if SERVER then
|
||||
self:TeleportRecall()
|
||||
else
|
||||
surface.PlaySound("buttons/combine_button7.wav")
|
||||
end
|
||||
end
|
||||
function SWEP:SecondaryAttack()
|
||||
self:SetNextSecondaryFire( CurTime() + self.Secondary.Delay )
|
||||
|
||||
if SERVER then
|
||||
self:TeleportStore()
|
||||
else
|
||||
surface.PlaySound("ui/buttonrollover.wav")
|
||||
end
|
||||
end
|
||||
|
||||
local zap = Sound("ambient/levels/labs/electric_explosion4.wav")
|
||||
local unzap = Sound("ambient/levels/labs/electric_explosion2.wav")
|
||||
|
||||
local function Telefrag(victim, attacker, weapon)
|
||||
if not IsValid(victim) then return end
|
||||
|
||||
local dmginfo = DamageInfo()
|
||||
dmginfo:SetDamage(5000)
|
||||
dmginfo:SetDamageType(DMG_SONIC)
|
||||
dmginfo:SetAttacker(attacker)
|
||||
dmginfo:SetInflictor(weapon)
|
||||
dmginfo:SetDamageForce(Vector(0,0,10))
|
||||
dmginfo:SetDamagePosition(attacker:GetPos())
|
||||
|
||||
victim:TakeDamageInfo(dmginfo)
|
||||
end
|
||||
|
||||
|
||||
local function ShouldCollide(ent)
|
||||
local g = ent:GetCollisionGroup()
|
||||
return (g != COLLISION_GROUP_WEAPON and
|
||||
g != COLLISION_GROUP_DEBRIS and
|
||||
g != COLLISION_GROUP_DEBRIS_TRIGGER and
|
||||
g != COLLISION_GROUP_INTERACTIVE_DEBRIS)
|
||||
end
|
||||
|
||||
-- Teleport a player to a {pos, ang}
|
||||
local function TeleportPlayer(ply, teleport)
|
||||
local oldpos = ply:GetPos()
|
||||
local pos = teleport.pos
|
||||
local ang = teleport.ang
|
||||
|
||||
-- print decal on destination
|
||||
util.PaintDown(pos + Vector(0,0,25), "GlassBreak", ply)
|
||||
|
||||
-- perform teleport
|
||||
ply:SetPos(pos)
|
||||
ply:SetEyeAngles(ang) -- ineffective due to freeze...
|
||||
|
||||
timer.Simple(delay_beamdown, function ()
|
||||
if IsValid(ply) then
|
||||
ply:Freeze(false)
|
||||
end
|
||||
end)
|
||||
|
||||
sound.Play(zap, oldpos, 65, 100)
|
||||
sound.Play(unzap, pos, 55, 100)
|
||||
|
||||
-- print decal on source now that we're gone, because else it will refuse
|
||||
-- to draw for some reason
|
||||
util.PaintDown(oldpos + Vector(0,0,25), "GlassBreak", ply)
|
||||
end
|
||||
|
||||
-- Checks teleport destination. Returns bool and table, if bool is true then
|
||||
-- location is blocked by world or prop. If table is non-nil it contains a list
|
||||
-- of blocking players.
|
||||
local function CanTeleportToPos(ply, pos)
|
||||
-- first check if we can teleport here at all, because any solid object or
|
||||
-- brush will make us stuck and therefore kills/blocks us instead, so the
|
||||
-- trace checks for anything solid to players that isn't a player
|
||||
local tr = nil
|
||||
local tres = {start=pos, endpos=pos, mask=MASK_PLAYERSOLID, filter=player.GetAll()}
|
||||
local collide = false
|
||||
|
||||
-- This thing is unnecessary if we can supply a collision group to trace
|
||||
-- functions, like we can in source and sanity suggests we should be able
|
||||
-- to do so, but I have not found a way to do so yet. Until then, re-trace
|
||||
-- while extending our filter whenever we hit something we don't want to
|
||||
-- hit (like weapons or ragdolls).
|
||||
repeat
|
||||
tr = util.TraceEntity(tres, ply)
|
||||
|
||||
if tr.HitWorld then
|
||||
collide = true
|
||||
elseif IsValid(tr.Entity) then
|
||||
if ShouldCollide(tr.Entity) then
|
||||
collide = true
|
||||
else
|
||||
table.insert(tres.filter, tr.Entity)
|
||||
end
|
||||
end
|
||||
until (not tr.Hit) or collide
|
||||
|
||||
if collide then
|
||||
--Telefrag(ply, ply)
|
||||
return true, nil
|
||||
else
|
||||
|
||||
-- find all players in the place where we will be and telefrag them
|
||||
local blockers = ents.FindInBox(pos + Vector(-16, -16, 0),
|
||||
pos + Vector(16, 16, 64))
|
||||
|
||||
local blocking_plys = {}
|
||||
|
||||
for _, block in ipairs(blockers) do
|
||||
if IsValid(block) then
|
||||
if block:IsPlayer() and block != ply then
|
||||
if block:IsTerror() and block:Alive() then
|
||||
table.insert(blocking_plys, block)
|
||||
-- telefrag blocker
|
||||
--Telefrag(block, ply)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return false, blocking_plys
|
||||
end
|
||||
|
||||
return false, nil
|
||||
end
|
||||
|
||||
local function DoTeleport(ply, teleport, weapon)
|
||||
if IsValid(ply) and ply:IsTerror() and teleport then
|
||||
local fail = false
|
||||
|
||||
local block_world, block_plys = CanTeleportToPos(ply, teleport.pos)
|
||||
|
||||
if block_world then
|
||||
-- if blocked by prop/world, always fail
|
||||
fail = true
|
||||
elseif block_plys and #block_plys > 0 then
|
||||
-- if blocked by player, maybe telefrag
|
||||
if ttt_telefrags:GetBool() then
|
||||
for _, p in ipairs(block_plys) do
|
||||
Telefrag(p, ply, weapon)
|
||||
end
|
||||
else
|
||||
fail = true
|
||||
end
|
||||
end
|
||||
|
||||
if not fail then
|
||||
TeleportPlayer(ply, teleport)
|
||||
else
|
||||
ply:Freeze(false)
|
||||
LANG.Msg(ply, "tele_failed")
|
||||
end
|
||||
elseif IsValid(ply) then
|
||||
-- should never happen, but at least unfreeze
|
||||
ply:Freeze(false)
|
||||
LANG.Msg(ply, "tele_failed")
|
||||
end
|
||||
end
|
||||
|
||||
local function StartTeleport(ply, teleport, weapon)
|
||||
if (not IsValid(ply)) or (not ply:IsTerror()) or (not teleport) then
|
||||
return end
|
||||
|
||||
teleport.ang = ply:EyeAngles()
|
||||
|
||||
timer.Simple(delay_beamup, function() DoTeleport(ply, teleport, weapon) end)
|
||||
|
||||
local ang = ply:GetAngles()
|
||||
|
||||
local edata_up = EffectData()
|
||||
edata_up:SetOrigin(ply:GetPos())
|
||||
ang = Angle(0, ang.y, ang.r) -- deep copy
|
||||
edata_up:SetAngles(ang)
|
||||
edata_up:SetEntity(ply)
|
||||
edata_up:SetMagnitude(delay_beamup)
|
||||
edata_up:SetRadius(delay_beamdown)
|
||||
|
||||
util.Effect("teleport_beamup", edata_up)
|
||||
|
||||
local edata_dn = EffectData()
|
||||
edata_dn:SetOrigin(teleport.pos)
|
||||
ang = Angle(0, ang.y, ang.r) -- deep copy
|
||||
edata_dn:SetAngles(ang)
|
||||
edata_dn:SetEntity(ply)
|
||||
edata_dn:SetMagnitude(delay_beamup)
|
||||
edata_dn:SetRadius(delay_beamdown)
|
||||
|
||||
util.Effect("teleport_beamdown", edata_dn)
|
||||
end
|
||||
|
||||
function SWEP:TeleportRecall()
|
||||
local ply = self:GetOwner()
|
||||
if IsValid(ply) and ply:IsTerror() then
|
||||
local mark = self:GetTeleportMark()
|
||||
if mark then
|
||||
|
||||
local g = ply:GetGroundEntity()
|
||||
if g != game.GetWorld() and not IsValid(g) then
|
||||
LANG.Msg(ply, "tele_no_ground")
|
||||
return
|
||||
end
|
||||
|
||||
if ply:Crouching() then
|
||||
LANG.Msg(ply, "tele_no_crouch")
|
||||
return
|
||||
end
|
||||
|
||||
ply:Freeze(true)
|
||||
|
||||
self:TakePrimaryAmmo(1)
|
||||
|
||||
timer.Simple(0.2, function() StartTeleport(ply, mark, self) end)
|
||||
else
|
||||
LANG.Msg(ply, "tele_no_mark")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function CanStoreTeleportPos(ply, pos)
|
||||
local g = ply:GetGroundEntity()
|
||||
if g != game.GetWorld() or (IsValid(g) and g:GetMoveType() != MOVETYPE_NONE) then
|
||||
return false, "tele_no_mark_ground"
|
||||
elseif ply:Crouching() then
|
||||
return false, "tele_no_mark_crouch"
|
||||
end
|
||||
|
||||
return true, nil
|
||||
end
|
||||
|
||||
function SWEP:TeleportStore()
|
||||
local ply = self:GetOwner()
|
||||
if IsValid(ply) and ply:IsTerror() then
|
||||
|
||||
local allow, msg = CanStoreTeleportPos(ply, self:GetPos())
|
||||
if not allow then
|
||||
LANG.Msg(ply, msg)
|
||||
return
|
||||
end
|
||||
|
||||
self:SetTeleportMark(ply:GetPos(), ply:EyeAngles())
|
||||
|
||||
LANG.Msg(ply, "tele_marked")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
function SWEP:Reload()
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
if CLIENT then
|
||||
function SWEP:Initialize()
|
||||
self:AddHUDHelp("tele_help_pri", "tele_help_sec", true)
|
||||
|
||||
return self.BaseClass.Initialize(self)
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:Deploy()
|
||||
if SERVER and IsValid(self:GetOwner()) then
|
||||
self:GetOwner():DrawViewModel(false)
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function SWEP:ShootEffects() end
|
||||
83
gamemodes/terrortown/entities/weapons/weapon_ttt_unarmed.lua
Normal file
83
gamemodes/terrortown/entities/weapons/weapon_ttt_unarmed.lua
Normal file
@@ -0,0 +1,83 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
AddCSLuaFile()
|
||||
|
||||
SWEP.HoldType = "normal"
|
||||
|
||||
if CLIENT then
|
||||
SWEP.PrintName = "unarmed_name"
|
||||
SWEP.Slot = 5
|
||||
|
||||
SWEP.ViewModelFOV = 10
|
||||
end
|
||||
|
||||
SWEP.Base = "weapon_tttbase"
|
||||
|
||||
SWEP.ViewModel = "models/weapons/v_crowbar.mdl"
|
||||
SWEP.WorldModel = "models/weapons/w_crowbar.mdl"
|
||||
|
||||
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.Kind = WEAPON_UNARMED
|
||||
SWEP.InLoadoutFor = {ROLE_INNOCENT, ROLE_TRAITOR, ROLE_DETECTIVE}
|
||||
|
||||
SWEP.AllowDelete = false
|
||||
SWEP.AllowDrop = false
|
||||
SWEP.NoSights = true
|
||||
|
||||
function SWEP:GetClass()
|
||||
return "weapon_ttt_unarmed"
|
||||
end
|
||||
|
||||
function SWEP:OnDrop()
|
||||
self:Remove()
|
||||
end
|
||||
|
||||
function SWEP:ShouldDropOnDie()
|
||||
return false
|
||||
end
|
||||
|
||||
function SWEP:PrimaryAttack()
|
||||
end
|
||||
|
||||
function SWEP:SecondaryAttack()
|
||||
end
|
||||
|
||||
function SWEP:Reload()
|
||||
end
|
||||
|
||||
function SWEP:Deploy()
|
||||
if SERVER and IsValid(self:GetOwner()) then
|
||||
self:GetOwner():DrawViewModel(false)
|
||||
end
|
||||
|
||||
self:DrawShadow(false)
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function SWEP:Holster()
|
||||
return true
|
||||
end
|
||||
|
||||
function SWEP:DrawWorldModel()
|
||||
end
|
||||
|
||||
function SWEP:DrawWorldModelTranslucent()
|
||||
end
|
||||
807
gamemodes/terrortown/entities/weapons/weapon_ttt_wtester.lua
Normal file
807
gamemodes/terrortown/entities/weapons/weapon_ttt_wtester.lua
Normal file
@@ -0,0 +1,807 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
-- DNA Scanner
|
||||
|
||||
AddCSLuaFile()
|
||||
|
||||
DEFINE_BASECLASS "weapon_tttbase"
|
||||
|
||||
SWEP.HoldType = "normal"
|
||||
|
||||
if CLIENT then
|
||||
SWEP.PrintName = "dna_name"
|
||||
SWEP.Slot = 8
|
||||
|
||||
SWEP.ViewModelFOV = 10
|
||||
SWEP.DrawCrosshair = false
|
||||
|
||||
SWEP.EquipMenuData = {
|
||||
type = "item_weapon",
|
||||
desc = "dna_desc"
|
||||
};
|
||||
|
||||
SWEP.Icon = "vgui/ttt/icon_wtester"
|
||||
end
|
||||
|
||||
SWEP.Base = "weapon_tttbase"
|
||||
|
||||
SWEP.ViewModel = "models/weapons/v_crowbar.mdl"
|
||||
SWEP.WorldModel = "models/props_lab/huladoll.mdl"
|
||||
|
||||
SWEP.Primary.ClipSize = -1
|
||||
SWEP.Primary.DefaultClip = -1
|
||||
SWEP.Primary.Automatic = false
|
||||
SWEP.Primary.Delay = 1
|
||||
SWEP.Primary.Ammo = "none"
|
||||
|
||||
SWEP.Secondary.ClipSize = -1
|
||||
SWEP.Secondary.DefaultClip = -1
|
||||
SWEP.Secondary.Automatic = false
|
||||
SWEP.Secondary.Ammo = "none"
|
||||
SWEP.Secondary.Delay = 2
|
||||
|
||||
SWEP.Kind = WEAPON_ROLE
|
||||
SWEP.CanBuy = nil -- no longer a buyable thing
|
||||
SWEP.WeaponID = AMMO_WTESTER
|
||||
|
||||
SWEP.InLoadoutFor = {ROLE_DETECTIVE}
|
||||
|
||||
--SWEP.AllowDrop = false
|
||||
SWEP.AutoSpawnable = false
|
||||
SWEP.NoSights = true
|
||||
|
||||
SWEP.Range = 175
|
||||
|
||||
SWEP.ItemSamples = {}
|
||||
|
||||
SWEP.NowRepeating = nil
|
||||
|
||||
local MAX_ITEM = 30
|
||||
SWEP.MaxItemSamples = MAX_ITEM
|
||||
|
||||
local CHARGE_DELAY = 0.1
|
||||
local CHARGE_RATE = 3
|
||||
local MAX_CHARGE = 1250
|
||||
|
||||
local SAMPLE_PLAYER = 1
|
||||
local SAMPLE_ITEM = 2
|
||||
|
||||
AccessorFuncDT(SWEP, "charge", "Charge")
|
||||
AccessorFuncDT(SWEP, "last_scanned", "LastScanned")
|
||||
|
||||
if CLIENT then
|
||||
CreateClientConVar("ttt_dna_scan_repeat", 1, true, true)
|
||||
else
|
||||
function SWEP:GetRepeating()
|
||||
local ply = self:GetOwner()
|
||||
return IsValid(ply) and ply:GetInfoNum("ttt_dna_scan_repeat", 1) == 1
|
||||
end
|
||||
end
|
||||
|
||||
SWEP.NextCharge = 0
|
||||
|
||||
function SWEP:SetupDataTables()
|
||||
self:DTVar("Int", 0, "charge")
|
||||
self:DTVar("Int", 1, "last_scanned")
|
||||
|
||||
return self.BaseClass.SetupDataTables(self)
|
||||
end
|
||||
|
||||
function SWEP:Initialize()
|
||||
self:SetCharge(MAX_CHARGE)
|
||||
self:SetLastScanned(-1)
|
||||
|
||||
if CLIENT then
|
||||
self:AddHUDHelp("dna_help_primary", "dna_help_secondary", true)
|
||||
end
|
||||
|
||||
return self.BaseClass.Initialize(self)
|
||||
end
|
||||
|
||||
local beep_miss = Sound("player/suit_denydevice.wav")
|
||||
function SWEP:PrimaryAttack()
|
||||
self:SetNextPrimaryFire( CurTime() + self.Primary.Delay )
|
||||
|
||||
-- will be tracing against players
|
||||
self:GetOwner():LagCompensation(true)
|
||||
|
||||
local spos = self:GetOwner():GetShootPos()
|
||||
local sdest = spos + (self:GetOwner():GetAimVector() * self.Range)
|
||||
|
||||
local tr = util.TraceLine({start=spos, endpos=sdest, filter=self:GetOwner(), mask=MASK_SHOT})
|
||||
local ent = tr.Entity
|
||||
if IsValid(ent) and (not ent:IsPlayer()) then
|
||||
if SERVER then
|
||||
if ent:GetClass() == "prop_ragdoll" and ent.killer_sample then
|
||||
if CORPSE.GetFound(ent, false) then
|
||||
self:GatherRagdollSample(ent)
|
||||
else
|
||||
self:Report("dna_identify")
|
||||
end
|
||||
elseif ent.fingerprints and #ent.fingerprints > 0 then
|
||||
self:GatherObjectSample(ent)
|
||||
else
|
||||
self:Report("dna_notfound")
|
||||
end
|
||||
end
|
||||
else
|
||||
if CLIENT then
|
||||
self:GetOwner():EmitSound(beep_miss)
|
||||
end
|
||||
end
|
||||
|
||||
self:GetOwner():LagCompensation(false)
|
||||
end
|
||||
|
||||
function SWEP:GatherRagdollSample(ent)
|
||||
local sample = ent.killer_sample or {t=0, killer=nil}
|
||||
local ply = sample.killer
|
||||
if (not IsValid(ply)) and sample.killer_sid64 then
|
||||
ply = player.GetBySteamID64(sample.killer_sid64)
|
||||
end
|
||||
|
||||
|
||||
if IsValid(ply) then
|
||||
if sample.t < CurTime() then
|
||||
self:Report("dna_decayed")
|
||||
return
|
||||
end
|
||||
|
||||
local added = self:AddPlayerSample(ent, ply)
|
||||
|
||||
if not added then
|
||||
self:Report("dna_limit")
|
||||
else
|
||||
self:Report("dna_killer")
|
||||
|
||||
if self:GetRepeating() and self:GetCharge() == MAX_CHARGE then
|
||||
self:PerformScan(#self.ItemSamples)
|
||||
end
|
||||
end
|
||||
elseif ply != nil then
|
||||
-- not valid but not nil -> disconnected?
|
||||
self:Report("dna_no_killer")
|
||||
else
|
||||
self:Report("dna_notfound")
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:GatherObjectSample(ent)
|
||||
if ent:GetClass() == "ttt_c4" and ent:GetArmed() then
|
||||
self:Report("dna_armed")
|
||||
else
|
||||
local collected, old, own = self:AddItemSample(ent)
|
||||
|
||||
if collected == -1 then
|
||||
self:Report("dna_limit")
|
||||
else
|
||||
self:Report("dna_object", {num = collected})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:Report(msg, params)
|
||||
LANG.Msg(self:GetOwner(), msg, params)
|
||||
end
|
||||
|
||||
function SWEP:AddPlayerSample(corpse, killer)
|
||||
if #self.ItemSamples < self.MaxItemSamples then
|
||||
local prnt = {source=corpse, ply=killer, type=SAMPLE_PLAYER, cls=killer:GetClass()}
|
||||
if not table.HasTable(self.ItemSamples, prnt) then
|
||||
table.insert(self.ItemSamples, prnt)
|
||||
|
||||
DamageLog("SAMPLE:\t " .. self:GetOwner():Nick() .. " retrieved DNA of " .. (IsValid(killer) and killer:Nick() or "<disconnected>") .. " from corpse of " .. (IsValid(corpse) and CORPSE.GetPlayerNick(corpse) or "<invalid>"))
|
||||
|
||||
hook.Call("TTTFoundDNA", GAMEMODE, self:GetOwner(), killer, corpse)
|
||||
end
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function SWEP:AddItemSample(ent)
|
||||
if #self.ItemSamples < self.MaxItemSamples then
|
||||
table.Shuffle(ent.fingerprints)
|
||||
|
||||
local new = 0
|
||||
local old = 0
|
||||
local own = 0
|
||||
for _, p in pairs(ent.fingerprints) do
|
||||
local prnt = {source=ent, ply=p, type=SAMPLE_ITEM, cls=ent:GetClass()}
|
||||
|
||||
if p == self:GetOwner() then
|
||||
own = own + 1
|
||||
elseif table.HasTable(self.ItemSamples, prnt) then
|
||||
old = old + 1
|
||||
else
|
||||
table.insert(self.ItemSamples, prnt)
|
||||
|
||||
DamageLog("SAMPLE:\t " .. self:GetOwner():Nick() .. " retrieved DNA of " .. (IsValid(p) and p:Nick() or "<disconnected>") .. " from " .. ent:GetClass())
|
||||
|
||||
new = new + 1
|
||||
hook.Call("TTTFoundDNA", GAMEMODE, self:GetOwner(), p, ent)
|
||||
end
|
||||
end
|
||||
return new, old, own
|
||||
end
|
||||
return -1
|
||||
end
|
||||
|
||||
function SWEP:RemoveItemSample(idx)
|
||||
if self.ItemSamples[idx] then
|
||||
if self:GetLastScanned() == idx then
|
||||
self:ClearScanState()
|
||||
end
|
||||
|
||||
table.remove(self.ItemSamples, idx)
|
||||
|
||||
self:SendPrints(false)
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:SecondaryAttack()
|
||||
self:SetNextSecondaryFire( CurTime() + 0.05 )
|
||||
|
||||
if CLIENT then return end
|
||||
|
||||
self:SendPrints(true)
|
||||
end
|
||||
|
||||
if SERVER then
|
||||
-- Sending this all in one umsg limits the max number of samples. 17 player
|
||||
-- samples and 20 item samples (with 20 matches) has been verified as
|
||||
-- working in the old DNA sampler.
|
||||
function SWEP:SendPrints(should_open)
|
||||
net.Start("TTT_ShowPrints", self:GetOwner())
|
||||
net.WriteBit(should_open)
|
||||
net.WriteUInt(#self.ItemSamples, 8)
|
||||
|
||||
for k, v in ipairs(self.ItemSamples) do
|
||||
net.WriteString(v.cls)
|
||||
end
|
||||
|
||||
net.Send(self:GetOwner())
|
||||
end
|
||||
|
||||
function SWEP:SendScan(pos)
|
||||
local clear = (pos == nil) or (not IsValid(self:GetOwner()))
|
||||
net.Start("TTT_ScanResult", self:GetOwner())
|
||||
net.WriteBit(clear)
|
||||
if not clear then
|
||||
net.WriteVector(pos)
|
||||
end
|
||||
net.Send(self:GetOwner())
|
||||
end
|
||||
|
||||
function SWEP:ClearScanState()
|
||||
self:SetLastScanned(-1)
|
||||
self.NowRepeating = nil
|
||||
self:SendScan(nil)
|
||||
end
|
||||
|
||||
local function GetScanTarget(sample)
|
||||
if not sample then return end
|
||||
|
||||
local target = sample.ply
|
||||
if not IsValid(target) then return end
|
||||
|
||||
-- decoys always take priority, even after death
|
||||
if IsValid(target.decoy) then
|
||||
target = target.decoy
|
||||
elseif not target:IsTerror() then
|
||||
-- fall back to ragdoll, as long as it's not destroyed
|
||||
target = target.server_ragdoll
|
||||
if not IsValid(target) then return end
|
||||
end
|
||||
|
||||
return target
|
||||
end
|
||||
|
||||
function SWEP:PerformScan(idx, repeated)
|
||||
if self:GetCharge() < MAX_CHARGE then return end
|
||||
|
||||
local sample = self.ItemSamples[idx]
|
||||
if (not sample) or (not IsValid(self:GetOwner())) then
|
||||
if repeated then self:ClearScanState() end
|
||||
return
|
||||
end
|
||||
|
||||
local target = GetScanTarget(sample)
|
||||
if not IsValid(target) then
|
||||
self:Report("dna_gone")
|
||||
self:SetCharge(self:GetCharge() - 50)
|
||||
|
||||
if repeated then self:ClearScanState() end
|
||||
return
|
||||
end
|
||||
|
||||
local pos = target:LocalToWorld(target:OBBCenter())
|
||||
|
||||
self:SendScan(pos)
|
||||
|
||||
self:SetLastScanned(idx)
|
||||
self.NowRepeating = self:GetRepeating()
|
||||
|
||||
local dist = math.ceil(self:GetOwner():GetPos():Distance(pos))
|
||||
|
||||
self:SetCharge(math.max(0, self:GetCharge() - math.max(50, dist / 2)))
|
||||
end
|
||||
|
||||
function SWEP:Think()
|
||||
if self:GetCharge() < MAX_CHARGE then
|
||||
if self.NextCharge < CurTime() then
|
||||
self:SetCharge(math.min(MAX_CHARGE, self:GetCharge() + CHARGE_RATE))
|
||||
|
||||
self.NextCharge = CurTime() + CHARGE_DELAY
|
||||
end
|
||||
elseif self.NowRepeating and IsValid(self:GetOwner()) then
|
||||
-- owner changed his mind since running last scan?
|
||||
if self:GetRepeating() then
|
||||
self:PerformScan(self:GetLastScanned(), true)
|
||||
else
|
||||
self.NowRepeating = self:GetRepeating()
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
-- Helper to get at a player's scanner, if he has one
|
||||
local function GetTester(ply)
|
||||
if IsValid(ply) then
|
||||
local tester = ply:GetActiveWeapon()
|
||||
if IsValid(tester) and tester:GetClass() == "weapon_ttt_wtester" then
|
||||
return tester
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
if CLIENT then
|
||||
|
||||
local T = LANG.GetTranslation
|
||||
local PT = LANG.GetParamTranslation
|
||||
local TT = LANG.TryTranslation
|
||||
|
||||
function SWEP:DrawHUD()
|
||||
self:DrawHelp()
|
||||
|
||||
local spos = self:GetOwner():GetShootPos()
|
||||
local sdest = spos + (self:GetOwner():GetAimVector() * self.Range)
|
||||
|
||||
local tr = util.TraceLine({start=spos, endpos=sdest, filter=self:GetOwner(), mask=MASK_SHOT})
|
||||
|
||||
local length = 20
|
||||
local gap = 6
|
||||
|
||||
local can_sample = false
|
||||
|
||||
local ent = tr.Entity
|
||||
if IsValid(ent) then
|
||||
-- weapon or dropped equipment
|
||||
if ((ent:IsWeapon() or ent.CanHavePrints) or
|
||||
-- knife in corpse, or a ragdoll
|
||||
ent:GetNWBool("HasPrints", false) or
|
||||
(ent:GetClass() == "prop_ragdoll" and
|
||||
CORPSE.GetPlayerNick(ent, false) and
|
||||
CORPSE.GetFound(ent, false))) then
|
||||
|
||||
surface.SetDrawColor(0, 255, 0, 255)
|
||||
gap = 0
|
||||
|
||||
can_sample = true
|
||||
else
|
||||
surface.SetDrawColor(255, 0, 0, 200)
|
||||
gap = 0
|
||||
end
|
||||
else
|
||||
surface.SetDrawColor(255, 255, 255, 200)
|
||||
end
|
||||
|
||||
local x = ScrW() / 2.0
|
||||
local y = ScrH() / 2.0
|
||||
|
||||
surface.DrawLine( x - length, y, x - gap, y )
|
||||
surface.DrawLine( x + length, y, x + gap, y )
|
||||
surface.DrawLine( x, y - length, x, y - gap )
|
||||
surface.DrawLine( x, y + length, x, y + gap )
|
||||
|
||||
if ent and can_sample then
|
||||
surface.SetFont("DefaultFixedDropShadow")
|
||||
surface.SetTextColor(0, 255, 0, 255)
|
||||
surface.SetTextPos( x + length*2, y - length*2 )
|
||||
surface.DrawText(T("dna_hud_type") .. ": " .. (ent:GetClass() == "prop_ragdoll" and T("dna_hud_body") or T("dna_hud_item")))
|
||||
surface.SetTextPos( x + length*2, y - length*2 + 15)
|
||||
surface.DrawText("ID: #" .. ent:EntIndex())
|
||||
end
|
||||
end
|
||||
|
||||
local basedir = "vgui/ttt/icon_"
|
||||
local function GetDisplayData(cls)
|
||||
local wep = util.WeaponForClass(cls)
|
||||
|
||||
local img = basedir .. "nades"
|
||||
local name = "something"
|
||||
|
||||
if cls == "player" then
|
||||
img = basedir .. "corpse"
|
||||
name = "corpse"
|
||||
elseif wep then
|
||||
img = wep.Icon or img
|
||||
name = wep.PrintName or name
|
||||
end
|
||||
|
||||
return img, name
|
||||
end
|
||||
|
||||
local last_panel_selected = 1
|
||||
local function ShowPrintsPopup(item_prints, tester)
|
||||
local m = 10
|
||||
local bw, bh = 100, 25
|
||||
|
||||
local dpanel = vgui.Create("DFrame")
|
||||
local w, h = 400, 250
|
||||
dpanel:SetSize(w, h)
|
||||
|
||||
dpanel:AlignRight(5)
|
||||
dpanel:AlignBottom(5)
|
||||
|
||||
dpanel:SetTitle(T("dna_menu_title"))
|
||||
dpanel:SetVisible(true)
|
||||
dpanel:ShowCloseButton(true)
|
||||
dpanel:SetMouseInputEnabled(true)
|
||||
|
||||
local wrap = vgui.Create("DPanel", dpanel)
|
||||
wrap:StretchToParent(m/2, m + 15, m/2, m + bh)
|
||||
wrap:SetPaintBackground(false)
|
||||
|
||||
-- item sample listing
|
||||
local ilist = vgui.Create("DPanelSelect", wrap)
|
||||
ilist:StretchToParent(0,0,0,0)
|
||||
ilist:EnableHorizontal(true)
|
||||
ilist:SetSpacing(1)
|
||||
ilist:SetPadding(1)
|
||||
|
||||
ilist.OnActivePanelChanged = function(s, old, new)
|
||||
last_panel_selected = new and new.key or 1
|
||||
end
|
||||
|
||||
ilist.OnScan = function(s, scanned_pnl)
|
||||
for k, pnl in pairs(s:GetItems()) do
|
||||
pnl:SetIconColor(COLOR_LGRAY)
|
||||
end
|
||||
|
||||
scanned_pnl:SetIconColor(COLOR_WHITE)
|
||||
end
|
||||
|
||||
if ilist.VBar then
|
||||
ilist.VBar:Remove()
|
||||
ilist.VBar = nil
|
||||
end
|
||||
|
||||
local iscroll = vgui.Create("DHorizontalScroller", ilist)
|
||||
iscroll:SetPos(3,1)
|
||||
iscroll:SetSize(363, 66)
|
||||
iscroll:SetOverlap(1)
|
||||
|
||||
iscroll.LoadFrom = function(s, tbl, layout)
|
||||
ilist:Clear(true)
|
||||
ilist.SelectedPanel = nil
|
||||
|
||||
-- Scroller has no Clear()
|
||||
for k, pnl in pairs(s.Panels) do
|
||||
if IsValid(pnl) then
|
||||
pnl:Remove()
|
||||
end
|
||||
end
|
||||
|
||||
s.Panels = {}
|
||||
|
||||
local last_scan = tester and tester:GetLastScanned() or -1
|
||||
|
||||
for k, v in ipairs(tbl) do
|
||||
local ic = vgui.Create("SimpleIcon", ilist)
|
||||
|
||||
ic:SetIconSize(64)
|
||||
|
||||
local img, name = GetDisplayData(v)
|
||||
|
||||
ic:SetIcon(img)
|
||||
|
||||
local tip = PT("dna_menu_sample", {source = TT(name) or "???"})
|
||||
|
||||
ic:SetTooltip(tip)
|
||||
|
||||
ic.key = k
|
||||
ic.val = v
|
||||
|
||||
if layout then
|
||||
ic:PerformLayout()
|
||||
end
|
||||
|
||||
ilist:AddPanel(ic)
|
||||
s:AddPanel(ic)
|
||||
|
||||
if k == last_panel_selected then
|
||||
ilist:SelectPanel(ic)
|
||||
end
|
||||
|
||||
if last_scan > 0 then
|
||||
ic:SetIconColor(last_scan == k and COLOR_WHITE or COLOR_LGRAY)
|
||||
end
|
||||
end
|
||||
|
||||
iscroll:InvalidateLayout()
|
||||
end
|
||||
|
||||
iscroll:LoadFrom(item_prints)
|
||||
|
||||
local delwrap = vgui.Create("DPanel", wrap)
|
||||
delwrap:SetPos(m, 70)
|
||||
delwrap:SetSize(370, bh)
|
||||
delwrap:SetPaintBackground(false)
|
||||
|
||||
local delitem = vgui.Create("DButton", delwrap)
|
||||
delitem:SetPos(0,0)
|
||||
delitem:SetSize(bw, bh)
|
||||
delitem:SetText(T("dna_menu_remove"))
|
||||
delitem.DoClick = function()
|
||||
if IsValid(ilist) and IsValid(ilist.SelectedPanel) then
|
||||
local idx = ilist.SelectedPanel.key
|
||||
RunConsoleCommand("ttt_wtester_remove", idx)
|
||||
end
|
||||
end
|
||||
|
||||
delitem.Think = function(s)
|
||||
if IsValid(ilist) and IsValid(ilist.SelectedPanel) then
|
||||
s:SetEnabled(true)
|
||||
else
|
||||
s:SetEnabled(false)
|
||||
end
|
||||
end
|
||||
|
||||
local delhlp = vgui.Create("DLabel", delwrap)
|
||||
delhlp:SetPos(bw + m, 0)
|
||||
delhlp:SetText(T("dna_menu_help1"))
|
||||
delhlp:SizeToContents()
|
||||
|
||||
|
||||
-- hammer out layouts
|
||||
wrap:PerformLayout()
|
||||
-- scroller needs to sort itself out so it displays all icons it should
|
||||
iscroll:PerformLayout()
|
||||
|
||||
local mwrap = vgui.Create("DPanel", wrap)
|
||||
mwrap:SetPaintBackground(false)
|
||||
mwrap:SetPos(m,100)
|
||||
mwrap:SetSize(370, 90)
|
||||
|
||||
|
||||
local bar = vgui.Create("TTTProgressBar", mwrap)
|
||||
bar:SetSize(370, 35)
|
||||
bar:SetPos(0, 0)
|
||||
bar:CenterHorizontal()
|
||||
bar:SetMin(0)
|
||||
bar:SetMax(MAX_CHARGE)
|
||||
bar:SetValue(tester and math.min(MAX_CHARGE, tester:GetCharge()))
|
||||
bar:SetColor(COLOR_GREEN)
|
||||
bar:LabelAsPercentage()
|
||||
|
||||
local state = vgui.Create("DLabel", bar)
|
||||
state:SetSize(0, 35)
|
||||
state:SetPos(10, 6)
|
||||
state:SetFont("Trebuchet22")
|
||||
state:SetText(T("dna_menu_ready"))
|
||||
state:SetTextColor(COLOR_WHITE)
|
||||
state:SizeToContents()
|
||||
|
||||
local scan = vgui.Create("DButton", mwrap)
|
||||
scan:SetText(T("dna_menu_scan"))
|
||||
scan:SetSize(bw, bh)
|
||||
scan:SetPos(0, 40)
|
||||
scan:SetEnabled(false)
|
||||
|
||||
scan.DoClick = function(s)
|
||||
if IsValid(ilist) then
|
||||
local i = ilist.SelectedPanel
|
||||
if IsValid(i) then
|
||||
RunConsoleCommand("ttt_wtester_scan", i.key)
|
||||
|
||||
ilist:OnScan(i)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local dcheck = vgui.Create("DCheckBoxLabel", mwrap)
|
||||
dcheck:SetPos(0, 70)
|
||||
dcheck:SetText(T("dna_menu_repeat"))
|
||||
dcheck:SetIndent(7)
|
||||
dcheck:SizeToContents()
|
||||
dcheck:SetConVar("ttt_dna_scan_repeat")
|
||||
--dcheck:SetValue(tester and tester:GetRepeating())
|
||||
|
||||
|
||||
local scanhlp = vgui.Create("DLabel", mwrap)
|
||||
scanhlp:SetPos(bw + m, 40)
|
||||
scanhlp:SetText(T("dna_menu_help2"))
|
||||
scanhlp:SizeToContents()
|
||||
|
||||
-- CLOSE
|
||||
local dbut = vgui.Create("DButton", dpanel)
|
||||
dbut:SetSize(bw, bh)
|
||||
dbut:SetPos(m, h - bh - m/1.5)
|
||||
dbut:CenterHorizontal()
|
||||
dbut:SetText(T("close"))
|
||||
dbut.DoClick = function() dpanel:Close() end
|
||||
|
||||
dpanel:MakePopup()
|
||||
dpanel:SetKeyboardInputEnabled(false)
|
||||
|
||||
-- Expose updating fns
|
||||
dpanel.UpdatePrints = function(s, its)
|
||||
if IsValid(iscroll) then
|
||||
iscroll:LoadFrom(its)
|
||||
end
|
||||
end
|
||||
|
||||
dpanel.Think = function(s)
|
||||
if IsValid(bar) and IsValid(scan) and tester then
|
||||
local charge = tester:GetCharge()
|
||||
bar:SetValue(math.min(MAX_CHARGE, charge))
|
||||
if charge < MAX_CHARGE then
|
||||
bar:SetColor(COLOR_RED)
|
||||
|
||||
state:SetText(T("dna_menu_charge"))
|
||||
state:SizeToContents()
|
||||
|
||||
scan:SetEnabled(false)
|
||||
else
|
||||
bar:SetColor(COLOR_GREEN)
|
||||
|
||||
if IsValid(ilist) and IsValid(ilist.SelectedPanel) then
|
||||
scan:SetEnabled(true)
|
||||
|
||||
state:SetText(T("dna_menu_ready"))
|
||||
state:SizeToContents()
|
||||
else
|
||||
state:SetText(T("dna_menu_select"))
|
||||
state:SizeToContents()
|
||||
scan:SetEnabled(false)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return dpanel
|
||||
end
|
||||
|
||||
local printspanel = nil
|
||||
local function RecvPrints()
|
||||
local should_open = net.ReadBit() == 1
|
||||
|
||||
local num = net.ReadUInt(8)
|
||||
local item_prints = {}
|
||||
for i=1, num do
|
||||
local ent = net.ReadString()
|
||||
table.insert(item_prints, ent)
|
||||
end
|
||||
|
||||
if should_open then
|
||||
if IsValid(printspanel) then
|
||||
printspanel:Remove()
|
||||
end
|
||||
|
||||
local tester = GetTester(LocalPlayer())
|
||||
|
||||
printspanel = ShowPrintsPopup(item_prints, tester)
|
||||
else
|
||||
if IsValid(printspanel) then
|
||||
printspanel:UpdatePrints(item_prints)
|
||||
end
|
||||
end
|
||||
end
|
||||
net.Receive("TTT_ShowPrints", RecvPrints)
|
||||
|
||||
local beep_success = Sound("buttons/blip2.wav")
|
||||
local function RecvScan()
|
||||
local clear = net.ReadBit() == 1
|
||||
if clear then
|
||||
RADAR.samples = {}
|
||||
RADAR.samples_count = 0
|
||||
return
|
||||
end
|
||||
|
||||
local target_pos = net.ReadVector()
|
||||
if not target_pos then return end
|
||||
|
||||
RADAR.samples = {
|
||||
{pos = target_pos}
|
||||
};
|
||||
|
||||
RADAR.samples_count = 1
|
||||
|
||||
surface.PlaySound(beep_success)
|
||||
end
|
||||
net.Receive("TTT_ScanResult", RecvScan)
|
||||
|
||||
function SWEP:ClosePrintsPanel()
|
||||
if IsValid(printspanel) then
|
||||
printspanel:Close()
|
||||
end
|
||||
end
|
||||
|
||||
else -- SERVER
|
||||
|
||||
local function ScanPrint(ply, cmd, args)
|
||||
if #args != 1 then return end
|
||||
|
||||
local tester = GetTester(ply)
|
||||
if IsValid(tester) then
|
||||
local i = tonumber(args[1])
|
||||
|
||||
if i then
|
||||
tester:PerformScan(i)
|
||||
end
|
||||
end
|
||||
end
|
||||
concommand.Add("ttt_wtester_scan", ScanPrint)
|
||||
|
||||
local function RemoveSample(ply, cmd, args)
|
||||
if #args != 1 then return end
|
||||
|
||||
local idx = tonumber(args[1])
|
||||
if not idx then return end
|
||||
|
||||
local tester = GetTester(ply)
|
||||
if IsValid(tester) then
|
||||
tester:RemoveItemSample(idx)
|
||||
end
|
||||
end
|
||||
concommand.Add("ttt_wtester_remove", RemoveSample)
|
||||
|
||||
end
|
||||
|
||||
function SWEP:OnRemove()
|
||||
if CLIENT then
|
||||
self:ClosePrintsPanel()
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:OnDrop()
|
||||
end
|
||||
|
||||
function SWEP:PreDrop()
|
||||
if IsValid(self:GetOwner()) then
|
||||
self:GetOwner().scanner_weapon = nil
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:Reload()
|
||||
return false
|
||||
end
|
||||
|
||||
function SWEP:Deploy()
|
||||
if SERVER and IsValid(self:GetOwner()) then
|
||||
self:GetOwner():DrawViewModel(false)
|
||||
self:GetOwner().scanner_weapon = self
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
if CLIENT then
|
||||
function SWEP:DrawWorldModel()
|
||||
if not IsValid(self:GetOwner()) then
|
||||
self:DrawModel()
|
||||
end
|
||||
end
|
||||
end
|
||||
594
gamemodes/terrortown/entities/weapons/weapon_tttbase.lua
Normal file
594
gamemodes/terrortown/entities/weapons/weapon_tttbase.lua
Normal file
@@ -0,0 +1,594 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
-- Custom weapon base, used to derive from CS one, still very similar
|
||||
|
||||
AddCSLuaFile()
|
||||
|
||||
---- TTT SPECIAL EQUIPMENT FIELDS
|
||||
|
||||
-- This must be set to one of the WEAPON_ types in TTT weapons for weapon
|
||||
-- carrying limits to work properly. See /gamemode/shared.lua for all possible
|
||||
-- weapon categories.
|
||||
SWEP.Kind = WEAPON_NONE
|
||||
|
||||
-- If CanBuy is a table that contains ROLE_TRAITOR and/or ROLE_DETECTIVE, those
|
||||
-- players are allowed to purchase it and it will appear in their Equipment Menu
|
||||
-- for that purpose. If CanBuy is nil this weapon cannot be bought.
|
||||
-- Example: SWEP.CanBuy = { ROLE_TRAITOR }
|
||||
-- (just setting to nil here to document its existence, don't make this buyable)
|
||||
SWEP.CanBuy = nil
|
||||
|
||||
if CLIENT then
|
||||
-- If this is a buyable weapon (ie. CanBuy is not nil) EquipMenuData must be
|
||||
-- a table containing some information to show in the Equipment Menu. See
|
||||
-- default equipment weapons for real-world examples.
|
||||
SWEP.EquipMenuData = nil
|
||||
|
||||
-- Example data:
|
||||
-- SWEP.EquipMenuData = {
|
||||
--
|
||||
---- Type tells players if it's a weapon or item
|
||||
-- type = "Weapon",
|
||||
--
|
||||
---- Desc is the description in the menu. Needs manual linebreaks (via \n).
|
||||
-- desc = "Text."
|
||||
-- };
|
||||
|
||||
-- This sets the icon shown for the weapon in the DNA sampler, search window,
|
||||
-- equipment menu (if buyable), etc.
|
||||
SWEP.Icon = "vgui/ttt/icon_nades" -- most generic icon I guess
|
||||
|
||||
-- You can make your own weapon icon using the template in:
|
||||
-- /garrysmod/gamemodes/terrortown/template/
|
||||
|
||||
-- Open one of TTT's icons with VTFEdit to see what kind of settings to use
|
||||
-- when exporting to VTF. Once you have a VTF and VMT, you can
|
||||
-- resource.AddFile("materials/vgui/...") them here. GIVE YOUR ICON A UNIQUE
|
||||
-- FILENAME, or it WILL be overwritten by other servers! Gmod does not check
|
||||
-- if the files are different, it only looks at the name. I recommend you
|
||||
-- create your own directory so that this does not happen,
|
||||
-- eg. /materials/vgui/ttt/mycoolserver/mygun.vmt
|
||||
end
|
||||
|
||||
---- MISC TTT-SPECIFIC BEHAVIOUR CONFIGURATION
|
||||
|
||||
-- ALL weapons in TTT must have weapon_tttbase as their SWEP.Base. It provides
|
||||
-- some functions that TTT expects, and you will get errors without them.
|
||||
-- Of course this is weapon_tttbase itself, so I comment this out here.
|
||||
-- SWEP.Base = "weapon_tttbase"
|
||||
|
||||
-- If true AND SWEP.Kind is not WEAPON_EQUIP, then this gun can be spawned as
|
||||
-- random weapon by a ttt_random_weapon entity.
|
||||
SWEP.AutoSpawnable = false
|
||||
|
||||
-- Set to true if weapon can be manually dropped by players (with Q)
|
||||
SWEP.AllowDrop = true
|
||||
|
||||
-- Set to true if weapon kills silently (no death scream)
|
||||
SWEP.IsSilent = false
|
||||
|
||||
-- If this weapon should be given to players upon spawning, set a table of the
|
||||
-- roles this should happen for here
|
||||
-- SWEP.InLoadoutFor = { ROLE_TRAITOR, ROLE_DETECTIVE, ROLE_INNOCENT }
|
||||
|
||||
-- DO NOT set SWEP.WeaponID. Only the standard TTT weapons can have it. Custom
|
||||
-- SWEPs do not need it for anything.
|
||||
-- SWEP.WeaponID = nil
|
||||
|
||||
---- YE OLDE SWEP STUFF
|
||||
|
||||
if CLIENT then
|
||||
SWEP.DrawCrosshair = false
|
||||
SWEP.ViewModelFOV = 82
|
||||
SWEP.ViewModelFlip = true
|
||||
SWEP.CSMuzzleFlashes = true
|
||||
end
|
||||
|
||||
SWEP.Base = "weapon_base"
|
||||
|
||||
SWEP.Category = "TTT"
|
||||
SWEP.Spawnable = false
|
||||
|
||||
SWEP.IsGrenade = false
|
||||
|
||||
SWEP.Weight = 5
|
||||
SWEP.AutoSwitchTo = false
|
||||
SWEP.AutoSwitchFrom = false
|
||||
|
||||
SWEP.Primary.Sound = Sound( "Weapon_Pistol.Empty" )
|
||||
SWEP.Primary.Recoil = 1.5
|
||||
SWEP.Primary.Damage = 1
|
||||
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.Primary.ClipMax = -1
|
||||
|
||||
SWEP.Secondary.ClipSize = 1
|
||||
SWEP.Secondary.DefaultClip = 1
|
||||
SWEP.Secondary.Automatic = false
|
||||
SWEP.Secondary.Ammo = "none"
|
||||
SWEP.Secondary.ClipMax = -1
|
||||
|
||||
SWEP.HeadshotMultiplier = 2.7
|
||||
|
||||
SWEP.StoredAmmo = 0
|
||||
SWEP.IsDropped = false
|
||||
|
||||
SWEP.DeploySpeed = 1.4
|
||||
|
||||
SWEP.PrimaryAnim = ACT_VM_PRIMARYATTACK
|
||||
SWEP.ReloadAnim = ACT_VM_RELOAD
|
||||
|
||||
SWEP.fingerprints = {}
|
||||
|
||||
local sparkle = CLIENT and CreateConVar("ttt_crazy_sparks", "0", FCVAR_ARCHIVE)
|
||||
|
||||
-- crosshair
|
||||
if CLIENT then
|
||||
local sights_opacity = CreateConVar("ttt_ironsights_crosshair_opacity", "0.8", FCVAR_ARCHIVE)
|
||||
local crosshair_brightness = CreateConVar("ttt_crosshair_brightness", "1.0", FCVAR_ARCHIVE)
|
||||
local crosshair_size = CreateConVar("ttt_crosshair_size", "1.0", FCVAR_ARCHIVE)
|
||||
local disable_crosshair = CreateConVar("ttt_disable_crosshair", "0", FCVAR_ARCHIVE)
|
||||
|
||||
function SWEP:DrawHUD()
|
||||
if self.HUDHelp then
|
||||
self:DrawHelp()
|
||||
end
|
||||
|
||||
local client = LocalPlayer()
|
||||
if disable_crosshair:GetBool() or (not IsValid(client)) then return end
|
||||
|
||||
local sights = (not self.NoSights) and self:GetIronsights()
|
||||
|
||||
local x = math.floor(ScrW() / 2.0)
|
||||
local y = math.floor(ScrH() / 2.0)
|
||||
local scale = math.max(0.2, 10 * self:GetPrimaryCone())
|
||||
|
||||
local LastShootTime = self:LastShootTime()
|
||||
scale = scale * (2 - math.Clamp( (CurTime() - LastShootTime) * 5, 0.0, 1.0 ))
|
||||
|
||||
local alpha = sights and sights_opacity:GetFloat() or 1
|
||||
local bright = crosshair_brightness:GetFloat() or 1
|
||||
|
||||
-- somehow it seems this can be called before my player metatable
|
||||
-- additions have loaded
|
||||
if client.IsTraitor and client:IsTraitor() then
|
||||
surface.SetDrawColor(255 * bright,
|
||||
50 * bright,
|
||||
50 * bright,
|
||||
255 * alpha)
|
||||
else
|
||||
surface.SetDrawColor(0,
|
||||
255 * bright,
|
||||
0,
|
||||
255 * alpha)
|
||||
end
|
||||
|
||||
local gap = math.floor(20 * scale * (sights and 0.8 or 1))
|
||||
local length = math.floor(gap + (25 * crosshair_size:GetFloat()) * scale)
|
||||
surface.DrawLine( x - length, y, x - gap, y )
|
||||
surface.DrawLine( x + length, y, x + gap, y )
|
||||
surface.DrawLine( x, y - length, x, y - gap )
|
||||
surface.DrawLine( x, y + length, x, y + gap )
|
||||
end
|
||||
|
||||
local GetPTranslation = LANG.GetParamTranslation
|
||||
|
||||
-- Many non-gun weapons benefit from some help
|
||||
local help_spec = {text = "", font = "TabLarge", xalign = TEXT_ALIGN_CENTER}
|
||||
function SWEP:DrawHelp()
|
||||
local data = self.HUDHelp
|
||||
|
||||
local translate = data.translatable
|
||||
local primary = data.primary
|
||||
local secondary = data.secondary
|
||||
|
||||
if translate then
|
||||
primary = primary and GetPTranslation(primary, data.translate_params)
|
||||
secondary = secondary and GetPTranslation(secondary, data.translate_params)
|
||||
end
|
||||
|
||||
help_spec.pos = {ScrW() / 2.0, ScrH() - 40}
|
||||
help_spec.text = secondary or primary
|
||||
draw.TextShadow(help_spec, 2)
|
||||
|
||||
-- if no secondary exists, primary is drawn at the bottom and no top line
|
||||
-- is drawn
|
||||
if secondary then
|
||||
help_spec.pos[2] = ScrH() - 60
|
||||
help_spec.text = primary
|
||||
draw.TextShadow(help_spec, 2)
|
||||
end
|
||||
end
|
||||
|
||||
-- mousebuttons are enough for most weapons
|
||||
local default_key_params = {
|
||||
primaryfire = Key("+attack", "LEFT MOUSE"),
|
||||
secondaryfire = Key("+attack2", "RIGHT MOUSE"),
|
||||
usekey = Key("+use", "USE")
|
||||
};
|
||||
|
||||
function SWEP:AddHUDHelp(primary_text, secondary_text, translate, extra_params)
|
||||
extra_params = extra_params or {}
|
||||
|
||||
self.HUDHelp = {
|
||||
primary = primary_text,
|
||||
secondary = secondary_text,
|
||||
translatable = translate,
|
||||
translate_params = table.Merge(extra_params, default_key_params)
|
||||
};
|
||||
end
|
||||
end
|
||||
|
||||
-- Shooting functions largely copied from weapon_cs_base
|
||||
function SWEP:PrimaryAttack(worldsnd)
|
||||
|
||||
self:SetNextSecondaryFire( CurTime() + self.Primary.Delay )
|
||||
self:SetNextPrimaryFire( CurTime() + self.Primary.Delay )
|
||||
|
||||
if not self:CanPrimaryAttack() then return end
|
||||
|
||||
if not worldsnd then
|
||||
self:EmitSound( self.Primary.Sound, self.Primary.SoundLevel )
|
||||
elseif SERVER then
|
||||
sound.Play(self.Primary.Sound, self:GetPos(), self.Primary.SoundLevel)
|
||||
end
|
||||
|
||||
self:ShootBullet( self.Primary.Damage, self.Primary.Recoil, self.Primary.NumShots, self:GetPrimaryCone() )
|
||||
|
||||
self:TakePrimaryAmmo( 1 )
|
||||
|
||||
local owner = self:GetOwner()
|
||||
if not IsValid(owner) or owner:IsNPC() or (not owner.ViewPunch) then return end
|
||||
|
||||
owner:ViewPunch( Angle( util.SharedRandom(self:GetClass(),-0.2,-0.1,0) * self.Primary.Recoil, util.SharedRandom(self:GetClass(),-0.1,0.1,1) * self.Primary.Recoil, 0 ) )
|
||||
end
|
||||
|
||||
function SWEP:DryFire(setnext)
|
||||
if CLIENT and LocalPlayer() == self:GetOwner() then
|
||||
self:EmitSound( "Weapon_Pistol.Empty" )
|
||||
end
|
||||
|
||||
setnext(self, CurTime() + 0.2)
|
||||
|
||||
self:Reload()
|
||||
end
|
||||
|
||||
function SWEP:CanPrimaryAttack()
|
||||
if not IsValid(self:GetOwner()) then return end
|
||||
|
||||
if self:Clip1() <= 0 then
|
||||
self:DryFire(self.SetNextPrimaryFire)
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function SWEP:CanSecondaryAttack()
|
||||
if not IsValid(self:GetOwner()) then return end
|
||||
|
||||
if self:Clip2() <= 0 then
|
||||
self:DryFire(self.SetNextSecondaryFire)
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
local function Sparklies(attacker, tr, dmginfo)
|
||||
if tr.HitWorld and tr.MatType == MAT_METAL then
|
||||
local eff = EffectData()
|
||||
eff:SetOrigin(tr.HitPos)
|
||||
eff:SetNormal(tr.HitNormal)
|
||||
util.Effect("cball_bounce", eff)
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:ShootBullet( dmg, recoil, numbul, cone )
|
||||
|
||||
self:SendWeaponAnim(self.PrimaryAnim)
|
||||
|
||||
self:GetOwner():MuzzleFlash()
|
||||
self:GetOwner():SetAnimation( PLAYER_ATTACK1 )
|
||||
|
||||
local sights = self:GetIronsights()
|
||||
|
||||
numbul = numbul or 1
|
||||
cone = cone or 0.01
|
||||
|
||||
local bullet = {}
|
||||
bullet.Num = numbul
|
||||
bullet.Src = self:GetOwner():GetShootPos()
|
||||
bullet.Dir = self:GetOwner():GetAimVector()
|
||||
bullet.Spread = Vector( cone, cone, 0 )
|
||||
bullet.Tracer = 4
|
||||
bullet.TracerName = self.Tracer or "Tracer"
|
||||
bullet.Force = 10
|
||||
bullet.Damage = dmg
|
||||
if CLIENT and sparkle:GetBool() then
|
||||
bullet.Callback = Sparklies
|
||||
end
|
||||
|
||||
self:GetOwner():FireBullets( bullet )
|
||||
|
||||
-- Owner can die after firebullets
|
||||
if (not IsValid(self:GetOwner())) or self:GetOwner():IsNPC() or (not self:GetOwner():Alive()) then return end
|
||||
|
||||
if ((game.SinglePlayer() and SERVER) or
|
||||
((not game.SinglePlayer()) and CLIENT and IsFirstTimePredicted())) then
|
||||
|
||||
-- reduce recoil if ironsighting
|
||||
recoil = sights and (recoil * 0.6) or recoil
|
||||
|
||||
local eyeang = self:GetOwner():EyeAngles()
|
||||
eyeang.pitch = eyeang.pitch - recoil
|
||||
self:GetOwner():SetEyeAngles( eyeang )
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:GetPrimaryCone()
|
||||
local cone = self.Primary.Cone or 0.2
|
||||
-- 15% accuracy bonus when sighting
|
||||
return self:GetIronsights() and (cone * 0.85) or cone
|
||||
end
|
||||
|
||||
function SWEP:GetHeadshotMultiplier(victim, dmginfo)
|
||||
return self.HeadshotMultiplier
|
||||
end
|
||||
|
||||
function SWEP:IsEquipment()
|
||||
return WEPS.IsEquipment(self)
|
||||
end
|
||||
|
||||
function SWEP:DrawWeaponSelection() end
|
||||
|
||||
function SWEP:SecondaryAttack()
|
||||
if self.NoSights or (not self.IronSightsPos) then return end
|
||||
|
||||
self:SetIronsights(not self:GetIronsights())
|
||||
|
||||
self:SetNextSecondaryFire(CurTime() + 0.3)
|
||||
end
|
||||
|
||||
function SWEP:Deploy()
|
||||
self:SetIronsights(false)
|
||||
return true
|
||||
end
|
||||
|
||||
function SWEP:Reload()
|
||||
if ( self:Clip1() == self.Primary.ClipSize or self:GetOwner():GetAmmoCount( self.Primary.Ammo ) <= 0 ) then return end
|
||||
self:DefaultReload(self.ReloadAnim)
|
||||
self:SetIronsights( false )
|
||||
end
|
||||
|
||||
|
||||
function SWEP:OnRestore()
|
||||
self.NextSecondaryAttack = 0
|
||||
self:SetIronsights( false )
|
||||
end
|
||||
|
||||
function SWEP:Ammo1()
|
||||
return IsValid(self:GetOwner()) and self:GetOwner():GetAmmoCount(self.Primary.Ammo) or false
|
||||
end
|
||||
|
||||
-- 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" 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
|
||||
|
||||
local SF_WEAPON_START_CONSTRAINED = 1
|
||||
|
||||
-- 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 not table.HasValue(self.fingerprints, newowner) then
|
||||
table.insert(self.fingerprints, newowner)
|
||||
end
|
||||
|
||||
if self:HasSpawnFlags(SF_WEAPON_START_CONSTRAINED) then
|
||||
-- If this weapon started constrained, unset that spawnflag, or the
|
||||
-- weapon will be re-constrained and float
|
||||
local flags = self:GetSpawnFlags()
|
||||
local newflags = bit.band(flags, bit.bnot(SF_WEAPON_START_CONSTRAINED))
|
||||
self:SetKeyValue("spawnflags", newflags)
|
||||
end
|
||||
end
|
||||
|
||||
if SERVER and IsValid(newowner) and self.StoredAmmo > 0 and self.Primary.Ammo != "none" 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
|
||||
end
|
||||
|
||||
-- We were bought as special equipment, some weapons will want to do something
|
||||
-- extra for their buyer
|
||||
function SWEP:WasBought(buyer)
|
||||
end
|
||||
|
||||
function SWEP:SetIronsights(b)
|
||||
if (b ~= self:GetIronsights()) then
|
||||
self:SetIronsightsPredicted(b)
|
||||
self:SetIronsightsTime(CurTime())
|
||||
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( time ) end
|
||||
function SWEP:GetIronsightsPredicted() return false end
|
||||
function SWEP:SetIronsightsPredicted( bool ) end
|
||||
|
||||
-- Set up ironsights dt bool. Weapons using their own DT vars will have to make
|
||||
-- sure they call this.
|
||||
function SWEP:SetupDataTables()
|
||||
self:NetworkVar("Bool", 3, "IronsightsPredicted")
|
||||
self:NetworkVar("Float", 3, "IronsightsTime")
|
||||
end
|
||||
|
||||
function SWEP:Initialize()
|
||||
if CLIENT and self:Clip1() == -1 then
|
||||
self:SetClip1(self.Primary.DefaultClip)
|
||||
elseif SERVER then
|
||||
self.fingerprints = {}
|
||||
|
||||
self:SetIronsights(false)
|
||||
end
|
||||
|
||||
self:SetDeploySpeed(self.DeploySpeed)
|
||||
|
||||
-- compat for gmod update
|
||||
if self.SetHoldType then
|
||||
self:SetHoldType(self.HoldType or "pistol")
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:CalcViewModel()
|
||||
if (not CLIENT) or (not IsFirstTimePredicted() and not game.SinglePlayer()) 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()
|
||||
end
|
||||
|
||||
function SWEP:DyingShot()
|
||||
local fired = false
|
||||
if self:GetIronsights() then
|
||||
self:SetIronsights(false)
|
||||
|
||||
if self:GetNextPrimaryFire() > CurTime() then
|
||||
return fired
|
||||
end
|
||||
|
||||
-- Owner should still be alive here
|
||||
if IsValid(self:GetOwner()) then
|
||||
local punch = self.Primary.Recoil or 5
|
||||
|
||||
-- Punch view to disorient aim before firing dying shot
|
||||
local eyeang = self:GetOwner():EyeAngles()
|
||||
eyeang.pitch = eyeang.pitch - math.Rand(-punch, punch)
|
||||
eyeang.yaw = eyeang.yaw - math.Rand(-punch, punch)
|
||||
self:GetOwner():SetEyeAngles( eyeang )
|
||||
|
||||
MsgN(self:GetOwner():Nick() .. " fired his DYING SHOT")
|
||||
|
||||
self:GetOwner().dying_wep = self
|
||||
|
||||
self:PrimaryAttack(true)
|
||||
|
||||
fired = true
|
||||
end
|
||||
end
|
||||
|
||||
return fired
|
||||
end
|
||||
|
||||
local ttt_lowered = CreateConVar("ttt_ironsights_lowered", "1", FCVAR_ARCHIVE)
|
||||
local host_timescale = GetConVar("host_timescale")
|
||||
|
||||
local LOWER_POS = Vector(0, 0, -2)
|
||||
|
||||
local IRONSIGHT_TIME = 0.25
|
||||
function SWEP:GetViewModelPosition( pos, ang )
|
||||
if (not self.IronSightsPos) or (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
|
||||
|
||||
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 + (ttt_lowered:GetBool() and LOWER_POS or vector_origin)
|
||||
|
||||
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
|
||||
|
||||
pos = pos + offset.x * ang:Right() * mul
|
||||
pos = pos + offset.y * ang:Forward() * mul
|
||||
pos = pos + offset.z * ang:Up() * mul
|
||||
|
||||
return pos, ang
|
||||
end
|
||||
272
gamemodes/terrortown/entities/weapons/weapon_tttbasegrenade.lua
Normal file
272
gamemodes/terrortown/entities/weapons/weapon_tttbasegrenade.lua
Normal file
@@ -0,0 +1,272 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
-- common code for all types of grenade
|
||||
|
||||
AddCSLuaFile()
|
||||
|
||||
DEFINE_BASECLASS "weapon_tttbase"
|
||||
|
||||
SWEP.HoldReady = "grenade"
|
||||
SWEP.HoldNormal = "slam"
|
||||
|
||||
if CLIENT then
|
||||
SWEP.PrintName = "Incendiary grenade"
|
||||
SWEP.Instructions = "Burn."
|
||||
SWEP.Slot = 3
|
||||
|
||||
SWEP.ViewModelFlip = true
|
||||
SWEP.DrawCrosshair = false
|
||||
|
||||
SWEP.Icon = "vgui/ttt/icon_nades"
|
||||
end
|
||||
|
||||
SWEP.Base = "weapon_tttbase"
|
||||
|
||||
SWEP.ViewModel = "models/weapons/v_eq_flashbang.mdl"
|
||||
SWEP.WorldModel = "models/weapons/w_eq_flashbang.mdl"
|
||||
|
||||
SWEP.Weight = 5
|
||||
SWEP.AutoSwitchFrom = true
|
||||
SWEP.NoSights = true
|
||||
|
||||
SWEP.Primary.ClipSize = -1
|
||||
SWEP.Primary.DefaultClip = -1
|
||||
SWEP.Primary.Automatic = false
|
||||
SWEP.Primary.Delay = 1.0
|
||||
SWEP.Primary.Ammo = "none"
|
||||
|
||||
SWEP.Secondary.ClipSize = -1
|
||||
SWEP.Secondary.DefaultClip = -1
|
||||
SWEP.Secondary.Automatic = false
|
||||
SWEP.Secondary.Ammo = "none"
|
||||
|
||||
SWEP.Kind = WEAPON_NADE
|
||||
SWEP.IsGrenade = true
|
||||
|
||||
SWEP.was_thrown = false
|
||||
SWEP.detonate_timer = 5
|
||||
SWEP.DeploySpeed = 1.5
|
||||
|
||||
AccessorFunc(SWEP, "det_time", "DetTime")
|
||||
|
||||
CreateConVar("ttt_no_nade_throw_during_prep", "1")
|
||||
|
||||
function SWEP:SetupDataTables()
|
||||
self:NetworkVar("Bool", 0, "Pin")
|
||||
self:NetworkVar("Int", 0, "ThrowTime")
|
||||
end
|
||||
|
||||
function SWEP:PrimaryAttack()
|
||||
self:SetNextPrimaryFire(CurTime() + self.Primary.Delay)
|
||||
|
||||
if GetRoundState() == ROUND_PREP and GetConVar("ttt_no_nade_throw_during_prep"):GetBool() then
|
||||
return
|
||||
end
|
||||
|
||||
self:PullPin()
|
||||
end
|
||||
|
||||
function SWEP:SecondaryAttack()
|
||||
end
|
||||
|
||||
function SWEP:PullPin()
|
||||
if self:GetPin() then return end
|
||||
|
||||
local ply = self:GetOwner()
|
||||
if not IsValid(ply) then return end
|
||||
|
||||
self:SendWeaponAnim(ACT_VM_PULLPIN)
|
||||
|
||||
if self.SetHoldType then
|
||||
self:SetHoldType(self.HoldReady)
|
||||
end
|
||||
|
||||
self:SetPin(true)
|
||||
|
||||
self:SetDetTime(CurTime() + self.detonate_timer)
|
||||
end
|
||||
|
||||
|
||||
function SWEP:Think()
|
||||
BaseClass.Think(self)
|
||||
local ply = self:GetOwner()
|
||||
if not IsValid(ply) then return end
|
||||
|
||||
-- pin pulled and attack loose = throw
|
||||
if self:GetPin() then
|
||||
-- we will throw now
|
||||
if not ply:KeyDown(IN_ATTACK) then
|
||||
self:StartThrow()
|
||||
|
||||
self:SetPin(false)
|
||||
self:SendWeaponAnim(ACT_VM_THROW)
|
||||
|
||||
if SERVER then
|
||||
self:GetOwner():SetAnimation( PLAYER_ATTACK1 )
|
||||
end
|
||||
else
|
||||
-- still cooking it, see if our time is up
|
||||
if SERVER and self:GetDetTime() < CurTime() then
|
||||
self:BlowInFace()
|
||||
end
|
||||
end
|
||||
elseif self:GetThrowTime() > 0 and self:GetThrowTime() < CurTime() then
|
||||
self:Throw()
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function SWEP:BlowInFace()
|
||||
local ply = self:GetOwner()
|
||||
if not IsValid(ply) then return end
|
||||
|
||||
if self.was_thrown then return end
|
||||
|
||||
self.was_thrown = true
|
||||
|
||||
-- drop the grenade so it can immediately explode
|
||||
|
||||
local ang = ply:GetAngles()
|
||||
local src = ply:GetPos() + (ply:Crouching() and ply:GetViewOffsetDucked() or ply:GetViewOffset())
|
||||
src = src + (ang:Right() * 10)
|
||||
|
||||
self:CreateGrenade(src, Angle(0,0,0), Vector(0,0,1), Vector(0,0,1), ply)
|
||||
|
||||
self:SetThrowTime(0)
|
||||
self:Remove()
|
||||
end
|
||||
|
||||
function SWEP:StartThrow()
|
||||
self:SetThrowTime(CurTime() + 0.1)
|
||||
end
|
||||
|
||||
function SWEP:Throw()
|
||||
if CLIENT then
|
||||
self:SetThrowTime(0)
|
||||
elseif SERVER then
|
||||
local ply = self:GetOwner()
|
||||
if not IsValid(ply) then return end
|
||||
|
||||
if self.was_thrown then return end
|
||||
|
||||
self.was_thrown = true
|
||||
|
||||
local ang = ply:EyeAngles()
|
||||
local src = ply:GetPos() + (ply:Crouching() and ply:GetViewOffsetDucked() or ply:GetViewOffset())+ (ang:Forward() * 8) + (ang:Right() * 10)
|
||||
local target = ply:GetEyeTraceNoCursor().HitPos
|
||||
local tang = (target-src):Angle() -- A target angle to actually throw the grenade to the crosshair instead of fowards
|
||||
-- Makes the grenade go upgwards
|
||||
if tang.p < 90 then
|
||||
tang.p = -10 + tang.p * ((90 + 10) / 90)
|
||||
else
|
||||
tang.p = 360 - tang.p
|
||||
tang.p = -10 + tang.p * -((90 + 10) / 90)
|
||||
end
|
||||
tang.p=math.Clamp(tang.p,-90,90) -- Makes the grenade not go backwards :/
|
||||
local vel = math.min(800, (90 - tang.p) * 6)
|
||||
local thr = tang:Forward() * vel + ply:GetVelocity()
|
||||
self:CreateGrenade(src, Angle(0,0,0), thr, Vector(600, math.random(-1200, 1200), 0), ply)
|
||||
|
||||
self:SetThrowTime(0)
|
||||
self:Remove()
|
||||
end
|
||||
end
|
||||
|
||||
-- subclasses must override with their own grenade ent
|
||||
function SWEP:GetGrenadeName()
|
||||
ErrorNoHalt("SWEP BASEGRENADE ERROR: GetGrenadeName not overridden! This is probably wrong!\n")
|
||||
return "ttt_firegrenade_proj"
|
||||
end
|
||||
|
||||
|
||||
function SWEP:CreateGrenade(src, ang, vel, angimp, ply)
|
||||
local gren = ents.Create(self:GetGrenadeName())
|
||||
if not IsValid(gren) then return end
|
||||
|
||||
gren:SetPos(src)
|
||||
gren:SetAngles(ang)
|
||||
|
||||
-- gren:SetVelocity(vel)
|
||||
gren:SetOwner(ply)
|
||||
gren:SetThrower(ply)
|
||||
|
||||
gren:SetGravity(0.4)
|
||||
gren:SetFriction(0.2)
|
||||
gren:SetElasticity(0.45)
|
||||
|
||||
gren:Spawn()
|
||||
|
||||
gren:PhysWake()
|
||||
|
||||
local phys = gren:GetPhysicsObject()
|
||||
if IsValid(phys) then
|
||||
phys:SetVelocity(vel)
|
||||
phys:AddAngleVelocity(angimp)
|
||||
end
|
||||
|
||||
-- This has to happen AFTER Spawn() calls gren's Initialize()
|
||||
gren:SetDetonateExact(self:GetDetTime())
|
||||
|
||||
return gren
|
||||
end
|
||||
|
||||
function SWEP:PreDrop()
|
||||
-- if owner dies or drops us while the pin has been pulled, create the armed
|
||||
-- grenade anyway
|
||||
if self:GetPin() then
|
||||
self:BlowInFace()
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:Deploy()
|
||||
|
||||
if self.SetHoldType then
|
||||
self:SetHoldType(self.HoldNormal)
|
||||
end
|
||||
|
||||
self:SetThrowTime(0)
|
||||
self:SetPin(false)
|
||||
return true
|
||||
end
|
||||
|
||||
function SWEP:Holster()
|
||||
if self:GetPin() then
|
||||
return false -- no switching after pulling pin
|
||||
end
|
||||
|
||||
self:SetThrowTime(0)
|
||||
self:SetPin(false)
|
||||
return true
|
||||
end
|
||||
|
||||
function SWEP:Reload()
|
||||
return false
|
||||
end
|
||||
|
||||
function SWEP:Initialize()
|
||||
if self.SetHoldType then
|
||||
self:SetHoldType(self.HoldNormal)
|
||||
end
|
||||
|
||||
self:SetDeploySpeed(self.DeploySpeed)
|
||||
|
||||
self:SetDetTime(0)
|
||||
self:SetThrowTime(0)
|
||||
self:SetPin(false)
|
||||
|
||||
self.was_thrown = false
|
||||
end
|
||||
|
||||
function SWEP:OnRemove()
|
||||
if CLIENT and IsValid(self:GetOwner()) and self:GetOwner() == LocalPlayer() and self:GetOwner():Alive() then
|
||||
RunConsoleCommand("use", "weapon_ttt_unarmed")
|
||||
end
|
||||
end
|
||||
609
gamemodes/terrortown/entities/weapons/weapon_zm_carry.lua
Normal file
609
gamemodes/terrortown/entities/weapons/weapon_zm_carry.lua
Normal file
@@ -0,0 +1,609 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
---- Carry weapon SWEP
|
||||
|
||||
AddCSLuaFile()
|
||||
|
||||
DEFINE_BASECLASS "weapon_tttbase"
|
||||
|
||||
SWEP.HoldType = "pistol"
|
||||
|
||||
if CLIENT then
|
||||
SWEP.PrintName = "magnet_name"
|
||||
SWEP.Slot = 4
|
||||
|
||||
SWEP.DrawCrosshair = false
|
||||
SWEP.ViewModelFlip = false
|
||||
end
|
||||
|
||||
SWEP.Base = "weapon_tttbase"
|
||||
|
||||
SWEP.AutoSpawnable = false
|
||||
|
||||
SWEP.ViewModel = Model("models/weapons/v_stunbaton.mdl")
|
||||
SWEP.WorldModel = Model("models/weapons/w_stunbaton.mdl")
|
||||
|
||||
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.InLoadoutFor = {ROLE_INNOCENT, ROLE_TRAITOR, ROLE_DETECTIVE}
|
||||
|
||||
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 prop_force = CreateConVar("ttt_prop_carrying_force", "60000")
|
||||
local no_throw = CreateConVar("ttt_no_prop_throwing", "0")
|
||||
local pin_rag = CreateConVar("ttt_ragdoll_pinning", "1")
|
||||
local pin_rag_inno = CreateConVar("ttt_ragdoll_pinning_innocents", "0")
|
||||
|
||||
-- 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", "0")
|
||||
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
|
||||
|
||||
local PIN_RAG_RANGE = 90
|
||||
|
||||
local player = player
|
||||
local IsValid = IsValid
|
||||
local CurTime = CurTime
|
||||
|
||||
|
||||
local function SetSubPhysMotionEnabled(ent, enable)
|
||||
if not IsValid(ent) then return end
|
||||
|
||||
for i=0, ent:GetPhysicsObjectCount()-1 do
|
||||
local subphys = ent:GetPhysicsObjectNum(i)
|
||||
if IsValid(subphys) then
|
||||
subphys:EnableMotion(enable)
|
||||
if enable then
|
||||
subphys:Wake()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function KillVelocity(ent)
|
||||
ent:SetVelocity(vector_origin)
|
||||
|
||||
-- The only truly effective way to prevent all kinds of velocity and
|
||||
-- inertia is motion disabling the entire ragdoll for a tick
|
||||
-- for non-ragdolls this will do the same for their single physobj
|
||||
SetSubPhysMotionEnabled(ent, false)
|
||||
|
||||
timer.Simple(0, function() SetSubPhysMotionEnabled(ent, true) end)
|
||||
end
|
||||
|
||||
function SWEP:Reset(keep_velocity)
|
||||
if IsValid(self.CarryHack) then
|
||||
self.CarryHack:Remove()
|
||||
end
|
||||
|
||||
if IsValid(self.Constr) then
|
||||
self.Constr:Remove()
|
||||
end
|
||||
|
||||
if IsValid(self.EntHolding) then
|
||||
-- it is possible for weapons to be already equipped at this point
|
||||
-- changing the owner in such a case would cause problems
|
||||
if not self.EntHolding:IsWeapon() then
|
||||
if not IsValid(self.PrevOwner) then
|
||||
self.EntHolding:SetOwner(nil)
|
||||
else
|
||||
self.EntHolding:SetOwner(self.PrevOwner)
|
||||
end
|
||||
end
|
||||
|
||||
-- the below ought to be unified with self:Drop()
|
||||
local phys = self.EntHolding:GetPhysicsObject()
|
||||
if IsValid(phys) then
|
||||
phys:ClearGameFlag(FVPHYSICS_PLAYER_HELD)
|
||||
phys:AddGameFlag(FVPHYSICS_WAS_THROWN)
|
||||
phys:EnableCollisions(true)
|
||||
phys:EnableGravity(true)
|
||||
phys:EnableDrag(true)
|
||||
phys:EnableMotion(true)
|
||||
end
|
||||
|
||||
if (not keep_velocity) and (no_throw:GetBool() or self.EntHolding:GetClass() == "prop_ragdoll") then
|
||||
KillVelocity(self.EntHolding)
|
||||
end
|
||||
end
|
||||
|
||||
self.dt.carried_rag = nil
|
||||
|
||||
self.EntHolding = nil
|
||||
self.CarryHack = nil
|
||||
self.Constr = nil
|
||||
end
|
||||
SWEP.reset = SWEP.Reset
|
||||
|
||||
function SWEP:CheckValidity()
|
||||
|
||||
if (not IsValid(self.EntHolding)) or (not IsValid(self.CarryHack)) or (not IsValid(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
|
||||
self:Reset()
|
||||
end
|
||||
|
||||
return false
|
||||
else
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
local function PlayerStandsOn(ent)
|
||||
for _, ply in player.Iterator() do
|
||||
if ply:GetGroundEntity() == ent and ply:IsTerror() then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
if SERVER then
|
||||
|
||||
local ent_diff = vector_origin
|
||||
local ent_diff_time = CurTime()
|
||||
|
||||
local stand_time = 0
|
||||
function SWEP:Think()
|
||||
BaseClass.Think(self)
|
||||
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) > 40000 then
|
||||
self:Reset()
|
||||
return
|
||||
end
|
||||
|
||||
ent_diff_time = CurTime() + 1
|
||||
end
|
||||
|
||||
if CurTime() > stand_time then
|
||||
|
||||
if PlayerStandsOn(self.EntHolding) then
|
||||
self:Reset()
|
||||
return
|
||||
end
|
||||
|
||||
stand_time = CurTime() + 0.1
|
||||
end
|
||||
|
||||
self.CarryHack:SetPos(self:GetOwner():EyePos() + self:GetOwner():GetAimVector() * 70)
|
||||
|
||||
self.CarryHack:SetAngles(self:GetOwner():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 IsValid(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 * 2
|
||||
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()
|
||||
elseif IsValid(target) and target:GetClass() == "prop_ragdoll" then
|
||||
return 75
|
||||
else
|
||||
return 100
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:AllowPickup(target)
|
||||
local phys = target:GetPhysicsObject()
|
||||
local ply = self:GetOwner()
|
||||
|
||||
return (IsValid(phys) and IsValid(ply) and
|
||||
(not phys:HasGameFlag(FVPHYSICS_NO_PLAYER_PICKUP)) and
|
||||
phys:GetMass() < CARRY_WEIGHT_LIMIT and
|
||||
(not PlayerStandsOn(target)) and
|
||||
(target.CanPickup != false) and
|
||||
(target:GetClass() != "prop_ragdoll" or allow_rag:GetBool()) and
|
||||
((not target:IsWeapon()) or allow_wep:GetBool()))
|
||||
end
|
||||
|
||||
function SWEP:DoAttack(pickup)
|
||||
self:SetNextPrimaryFire( CurTime() + self.Primary.Delay )
|
||||
self:SetNextSecondaryFire( CurTime() + self.Secondary.Delay )
|
||||
|
||||
if IsValid(self.EntHolding) then
|
||||
self:SendWeaponAnim( ACT_VM_MISSCENTER )
|
||||
|
||||
if (not pickup) and self.EntHolding:GetClass() == "prop_ragdoll" then
|
||||
-- see if we can pin this ragdoll to a wall in front of us
|
||||
if not self:PinRagdoll() then
|
||||
-- else just drop it as usual
|
||||
self:Drop()
|
||||
end
|
||||
else
|
||||
self:Drop()
|
||||
end
|
||||
|
||||
self:SetNextSecondaryFire(CurTime() + 0.3)
|
||||
return
|
||||
end
|
||||
|
||||
local ply = self:GetOwner()
|
||||
|
||||
local trace = ply:GetEyeTrace(MASK_SHOT)
|
||||
if IsValid(trace.Entity) then
|
||||
local ent = trace.Entity
|
||||
local phys = trace.Entity:GetPhysicsObject()
|
||||
|
||||
if not IsValid(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
|
||||
if (ply:EyePos() - trace.HitPos):Length() < self:GetRange(ent) then
|
||||
|
||||
if self:AllowPickup(ent) then
|
||||
self:Pickup()
|
||||
self:SendWeaponAnim( ACT_VM_HITCENTER )
|
||||
|
||||
-- make the refire slower to avoid immediately dropping
|
||||
local delay = (ent:GetClass() == "prop_ragdoll") and 0.8 or 0.5
|
||||
|
||||
self:SetNextSecondaryFire(CurTime() + delay)
|
||||
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
|
||||
|
||||
phys = ent:GetPhysicsObjectNum(trace.PhysicsBone)
|
||||
|
||||
-- increase refire to make rags easier to drag
|
||||
--self:SetNextSecondaryFire(CurTime() + 0.04)
|
||||
end
|
||||
|
||||
if IsValid(phys) then
|
||||
self:MoveObject(phys, pdir, 6000, is_ragdoll)
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
if (ply:EyePos() - trace.HitPos):Length() < 100 then
|
||||
local phys = trace.Entity:GetPhysicsObject()
|
||||
if IsValid(phys) then
|
||||
if IsValid(phys) then
|
||||
local pdir = trace.Normal
|
||||
self:MoveObject(phys, pdir, 6000, (trace.Entity:GetClass() == "prop_ragdoll"))
|
||||
|
||||
self:SetNextPrimaryFire(CurTime() + 0.03)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Perform a pickup
|
||||
function SWEP:Pickup()
|
||||
if CLIENT or IsValid(self.EntHolding) then return end
|
||||
|
||||
local ply = self:GetOwner()
|
||||
local trace = ply:GetEyeTrace(MASK_SHOT)
|
||||
local ent = trace.Entity
|
||||
self.EntHolding = ent
|
||||
local entphys = ent:GetPhysicsObject()
|
||||
|
||||
|
||||
if IsValid(ent) and IsValid(entphys) then
|
||||
|
||||
self.CarryHack = ents.Create("prop_physics")
|
||||
if IsValid(self.CarryHack) then
|
||||
self.CarryHack:SetPos(self.EntHolding:GetPos())
|
||||
|
||||
self.CarryHack:SetModel("models/weapons/w_bugbait.mdl")
|
||||
|
||||
self.CarryHack:SetColor(Color(50, 250, 50, 240))
|
||||
self.CarryHack:SetNoDraw(true)
|
||||
self.CarryHack:DrawShadow(false)
|
||||
|
||||
self.CarryHack:SetHealth(999)
|
||||
self.CarryHack:SetOwner(ply)
|
||||
self.CarryHack:SetCollisionGroup(COLLISION_GROUP_DEBRIS)
|
||||
self.CarryHack:SetSolid(SOLID_NONE)
|
||||
|
||||
-- set the desired angles before adding the constraint
|
||||
self.CarryHack:SetAngles(self:GetOwner():GetAngles())
|
||||
|
||||
self.CarryHack:Spawn()
|
||||
|
||||
-- if we already are owner before pickup, we will not want to disown
|
||||
-- this entity when we drop it
|
||||
-- weapons should not have their owner changed in this way
|
||||
if not self.EntHolding:IsWeapon() then
|
||||
self.PrevOwner = self.EntHolding:GetOwner()
|
||||
|
||||
self.EntHolding:SetOwner(ply)
|
||||
end
|
||||
|
||||
local phys = self.CarryHack:GetPhysicsObject()
|
||||
if IsValid(phys) then
|
||||
phys:SetMass(200)
|
||||
phys:SetDamping(0, 1000)
|
||||
phys:EnableGravity(false)
|
||||
phys:EnableCollisions(false)
|
||||
phys:EnableMotion(false)
|
||||
phys:AddGameFlag(FVPHYSICS_PLAYER_HELD)
|
||||
end
|
||||
|
||||
entphys:AddGameFlag(FVPHYSICS_PLAYER_HELD)
|
||||
local bone = math.Clamp(trace.PhysicsBone, 0, 1)
|
||||
local max_force = prop_force:GetInt()
|
||||
|
||||
if ent:GetClass() == "prop_ragdoll" then
|
||||
self.dt.carried_rag = ent
|
||||
|
||||
bone = trace.PhysicsBone
|
||||
max_force = 0
|
||||
else
|
||||
self.dt.carried_rag = nil
|
||||
end
|
||||
|
||||
self.Constr = constraint.Weld(self.CarryHack, self.EntHolding, 0, bone, max_force, true)
|
||||
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local down = Vector(0, 0, -1)
|
||||
function SWEP:AllowEntityDrop()
|
||||
local ply = self:GetOwner()
|
||||
local ent = self.CarryHack
|
||||
if (not IsValid(ply)) or (not IsValid(ent)) then return false end
|
||||
|
||||
local ground = ply:GetGroundEntity()
|
||||
if ground and (ground:IsWorld() or IsValid(ground)) then return true end
|
||||
|
||||
local diff = (ent:GetPos() - ply:GetShootPos()):GetNormalized()
|
||||
|
||||
return down:Dot(diff) <= 0.75
|
||||
end
|
||||
|
||||
function SWEP:Drop()
|
||||
if not self:CheckValidity() then return end
|
||||
if not self:AllowEntityDrop() then return end
|
||||
|
||||
if SERVER then
|
||||
self.Constr:Remove()
|
||||
self.CarryHack:Remove()
|
||||
|
||||
local ent = self.EntHolding
|
||||
|
||||
local phys = ent:GetPhysicsObject()
|
||||
if IsValid(phys) then
|
||||
phys:EnableCollisions(true)
|
||||
phys:EnableGravity(true)
|
||||
phys:EnableDrag(true)
|
||||
phys:EnableMotion(true)
|
||||
phys:Wake()
|
||||
phys:ApplyForceCenter(self:GetOwner():GetAimVector() * 500)
|
||||
|
||||
phys:ClearGameFlag(FVPHYSICS_PLAYER_HELD)
|
||||
phys:AddGameFlag(FVPHYSICS_WAS_THROWN)
|
||||
end
|
||||
|
||||
-- Try to limit ragdoll slinging
|
||||
if no_throw:GetBool() or ent:GetClass() == "prop_ragdoll" then
|
||||
KillVelocity(ent)
|
||||
end
|
||||
|
||||
ent:SetPhysicsAttacker(self:GetOwner())
|
||||
|
||||
end
|
||||
|
||||
self:Reset()
|
||||
end
|
||||
|
||||
local CONSTRAINT_TYPE = "Rope"
|
||||
|
||||
local function RagdollPinnedTakeDamage(rag, dmginfo)
|
||||
local att = dmginfo:GetAttacker()
|
||||
if not IsValid(att) then return end
|
||||
|
||||
-- drop from pinned position upon dmg
|
||||
constraint.RemoveConstraints(rag, CONSTRAINT_TYPE)
|
||||
rag:PhysWake()
|
||||
|
||||
rag:SetHealth(0)
|
||||
rag.is_pinned = false
|
||||
end
|
||||
|
||||
function SWEP:PinRagdoll()
|
||||
if not pin_rag:GetBool() then return end
|
||||
if (not self:GetOwner():IsTraitor()) and (not pin_rag_inno:GetBool()) then return end
|
||||
|
||||
local rag = self.EntHolding
|
||||
local ply = self:GetOwner()
|
||||
|
||||
local tr = util.TraceLine({start = ply:EyePos(),
|
||||
endpos = ply:EyePos() + (ply:GetAimVector() * PIN_RAG_RANGE),
|
||||
filter = {ply, self, rag, self.CarryHack},
|
||||
mask = MASK_SOLID})
|
||||
|
||||
if tr.HitWorld and (not tr.HitSky) then
|
||||
|
||||
-- find bone we're holding the ragdoll by
|
||||
local bone = self.Constr.Bone2
|
||||
|
||||
-- only allow one rope per bone
|
||||
for _, c in pairs(constraint.FindConstraints(rag, CONSTRAINT_TYPE)) do
|
||||
if c.Bone1 == bone then
|
||||
c.Constraint:Remove()
|
||||
end
|
||||
end
|
||||
|
||||
local bonephys = rag:GetPhysicsObjectNum(bone)
|
||||
if not IsValid(bonephys) then return end
|
||||
|
||||
local bonepos = bonephys:GetPos()
|
||||
local attachpos = tr.HitPos
|
||||
local length = (bonepos - attachpos):Length() * 0.9
|
||||
|
||||
-- we need to convert using this particular physobj to get the right
|
||||
-- coordinates
|
||||
bonepos = bonephys:WorldToLocal(bonepos)
|
||||
|
||||
constraint.Rope(rag, tr.Entity, bone, 0, bonepos, attachpos,
|
||||
length, length * 0.1, 6000,
|
||||
1, "cable/rope", false)
|
||||
|
||||
rag.is_pinned = true
|
||||
rag.OnPinnedDamage = RagdollPinnedTakeDamage
|
||||
|
||||
-- lets EntityTakeDamage run for the ragdoll
|
||||
rag:SetHealth(999999)
|
||||
|
||||
self:Reset(true)
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:SetupDataTables()
|
||||
-- we've got these dt slots anyway, might as well use them instead of a
|
||||
-- globalvar, probably cheaper
|
||||
self:DTVar("Bool", 0, "can_rag_pin")
|
||||
self:DTVar("Bool", 1, "can_rag_pin_inno")
|
||||
|
||||
-- client actually has no idea what we're holding, and almost never needs to
|
||||
-- know
|
||||
self:DTVar("Entity", 0, "carried_rag")
|
||||
return self.BaseClass.SetupDataTables(self)
|
||||
end
|
||||
|
||||
if SERVER then
|
||||
function SWEP:Initialize()
|
||||
self.dt.can_rag_pin = pin_rag:GetBool()
|
||||
self.dt.can_rag_pin_inno = pin_rag_inno:GetBool()
|
||||
self.dt.carried_rag = nil
|
||||
|
||||
return self.BaseClass.Initialize(self)
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:OnRemove()
|
||||
self:Reset()
|
||||
end
|
||||
|
||||
function SWEP:Deploy()
|
||||
self:Reset()
|
||||
return true
|
||||
end
|
||||
|
||||
function SWEP:Holster()
|
||||
self:Reset()
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
function SWEP:ShouldDropOnDie()
|
||||
return false
|
||||
end
|
||||
|
||||
function SWEP:OnDrop()
|
||||
self:Remove()
|
||||
end
|
||||
|
||||
if CLIENT then
|
||||
local draw = draw
|
||||
local util = util
|
||||
|
||||
local PT = LANG.GetParamTranslation
|
||||
local key_params = {primaryfire = Key("+attack", "LEFT MOUSE")}
|
||||
function SWEP:DrawHUD()
|
||||
self.BaseClass.DrawHUD(self)
|
||||
|
||||
if self.dt.can_rag_pin and IsValid(self.dt.carried_rag) then
|
||||
local client = LocalPlayer()
|
||||
|
||||
if not client:IsSpec() and (self.dt.can_rag_pin_inno or client:IsTraitor()) then
|
||||
local tr = util.TraceLine({start = client:EyePos(),
|
||||
endpos = client:EyePos() + (client:GetAimVector() * PIN_RAG_RANGE),
|
||||
filter = {client, self, self.dt.carried_rag},
|
||||
mask = MASK_SOLID})
|
||||
|
||||
if tr.HitWorld and (not tr.HitSky) then
|
||||
draw.SimpleText(PT("magnet_help", key_params), "TabLarge", ScrW() / 2, ScrH() / 2 - 50, COLOR_RED, TEXT_ALIGN_CENTER)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
272
gamemodes/terrortown/entities/weapons/weapon_zm_improvised.lua
Normal file
272
gamemodes/terrortown/entities/weapons/weapon_zm_improvised.lua
Normal file
@@ -0,0 +1,272 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
AddCSLuaFile()
|
||||
|
||||
SWEP.HoldType = "melee"
|
||||
|
||||
if CLIENT then
|
||||
SWEP.PrintName = "crowbar_name"
|
||||
SWEP.Slot = 0
|
||||
|
||||
SWEP.DrawCrosshair = false
|
||||
SWEP.ViewModelFlip = false
|
||||
SWEP.ViewModelFOV = 54
|
||||
|
||||
SWEP.Icon = "vgui/ttt/icon_cbar"
|
||||
end
|
||||
|
||||
SWEP.Base = "weapon_tttbase"
|
||||
|
||||
SWEP.UseHands = true
|
||||
SWEP.ViewModel = "models/weapons/c_crowbar.mdl"
|
||||
SWEP.WorldModel = "models/weapons/w_crowbar.mdl"
|
||||
|
||||
SWEP.Primary.Damage = 20
|
||||
SWEP.Primary.ClipSize = -1
|
||||
SWEP.Primary.DefaultClip = -1
|
||||
SWEP.Primary.Automatic = true
|
||||
SWEP.Primary.Delay = 0.5
|
||||
SWEP.Primary.Ammo = "none"
|
||||
|
||||
SWEP.Secondary.ClipSize = -1
|
||||
SWEP.Secondary.DefaultClip = -1
|
||||
SWEP.Secondary.Automatic = true
|
||||
SWEP.Secondary.Ammo = "none"
|
||||
SWEP.Secondary.Delay = 5
|
||||
|
||||
SWEP.Kind = WEAPON_MELEE
|
||||
SWEP.WeaponID = AMMO_CROWBAR
|
||||
SWEP.InLoadoutFor = {ROLE_INNOCENT, ROLE_TRAITOR, ROLE_DETECTIVE}
|
||||
|
||||
SWEP.NoSights = true
|
||||
SWEP.IsSilent = true
|
||||
|
||||
SWEP.Weight = 5
|
||||
SWEP.AutoSpawnable = false
|
||||
|
||||
SWEP.AllowDelete = false -- never removed for weapon reduction
|
||||
SWEP.AllowDrop = false
|
||||
|
||||
local sound_single = Sound("Weapon_Crowbar.Single")
|
||||
local sound_open = Sound("DoorHandles.Unlocked3")
|
||||
|
||||
if SERVER then
|
||||
CreateConVar("ttt_crowbar_unlocks", "1", FCVAR_ARCHIVE)
|
||||
CreateConVar("ttt_crowbar_pushforce", "395", FCVAR_NOTIFY)
|
||||
end
|
||||
|
||||
-- only open things that have a name (and are therefore likely to be meant to
|
||||
-- open) and are the right class. Opening behaviour also differs per class, so
|
||||
-- return one of the OPEN_ values
|
||||
local function OpenableEnt(ent)
|
||||
local cls = ent:GetClass()
|
||||
if ent:GetName() == "" then
|
||||
return OPEN_NO
|
||||
elseif cls == "prop_door_rotating" then
|
||||
return OPEN_ROT
|
||||
elseif cls == "func_door" or cls == "func_door_rotating" then
|
||||
return OPEN_DOOR
|
||||
elseif cls == "func_button" then
|
||||
return OPEN_BUT
|
||||
elseif cls == "func_movelinear" then
|
||||
return OPEN_NOTOGGLE
|
||||
else
|
||||
return OPEN_NO
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function CrowbarCanUnlock(t)
|
||||
return not GAMEMODE.crowbar_unlocks or GAMEMODE.crowbar_unlocks[t]
|
||||
end
|
||||
|
||||
-- will open door AND return what it did
|
||||
function SWEP:OpenEnt(hitEnt)
|
||||
-- Get ready for some prototype-quality code, all ye who read this
|
||||
if SERVER and GetConVar("ttt_crowbar_unlocks"):GetBool() then
|
||||
local openable = OpenableEnt(hitEnt)
|
||||
|
||||
if openable == OPEN_DOOR or openable == OPEN_ROT then
|
||||
local unlock = CrowbarCanUnlock(openable)
|
||||
if unlock then
|
||||
hitEnt:Fire("Unlock", nil, 0)
|
||||
end
|
||||
|
||||
if unlock or hitEnt:HasSpawnFlags(256) then
|
||||
if openable == OPEN_ROT then
|
||||
hitEnt:Fire("OpenAwayFrom", self:GetOwner(), 0)
|
||||
end
|
||||
hitEnt:Fire("Toggle", nil, 0)
|
||||
else
|
||||
return OPEN_NO
|
||||
end
|
||||
elseif openable == OPEN_BUT then
|
||||
if CrowbarCanUnlock(openable) then
|
||||
hitEnt:Fire("Unlock", nil, 0)
|
||||
hitEnt:Fire("Press", nil, 0)
|
||||
else
|
||||
return OPEN_NO
|
||||
end
|
||||
elseif openable == OPEN_NOTOGGLE then
|
||||
if CrowbarCanUnlock(openable) then
|
||||
hitEnt:Fire("Open", nil, 0)
|
||||
else
|
||||
return OPEN_NO
|
||||
end
|
||||
end
|
||||
return openable
|
||||
else
|
||||
return OPEN_NO
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:PrimaryAttack()
|
||||
self:SetNextPrimaryFire( CurTime() + self.Primary.Delay )
|
||||
|
||||
if not IsValid(self:GetOwner()) then return end
|
||||
|
||||
if self:GetOwner().LagCompensation then -- for some reason not always true
|
||||
self:GetOwner():LagCompensation(true)
|
||||
end
|
||||
|
||||
local spos = self:GetOwner():GetShootPos()
|
||||
local sdest = spos + (self:GetOwner():GetAimVector() * 70)
|
||||
|
||||
local tr_main = util.TraceLine({start=spos, endpos=sdest, filter=self:GetOwner(), mask=MASK_SHOT_HULL})
|
||||
local hitEnt = tr_main.Entity
|
||||
|
||||
self:EmitSound(sound_single)
|
||||
|
||||
if IsValid(hitEnt) or tr_main.HitWorld then
|
||||
self:SendWeaponAnim( ACT_VM_HITCENTER )
|
||||
|
||||
if not (CLIENT and (not IsFirstTimePredicted())) then
|
||||
local edata = EffectData()
|
||||
edata:SetStart(spos)
|
||||
edata:SetOrigin(tr_main.HitPos)
|
||||
edata:SetNormal(tr_main.Normal)
|
||||
edata:SetSurfaceProp(tr_main.SurfaceProps)
|
||||
edata:SetHitBox(tr_main.HitBox)
|
||||
--edata:SetDamageType(DMG_CLUB)
|
||||
edata:SetEntity(hitEnt)
|
||||
|
||||
if hitEnt:IsPlayer() or hitEnt:GetClass() == "prop_ragdoll" then
|
||||
util.Effect("BloodImpact", edata)
|
||||
|
||||
-- does not work on players rah
|
||||
--util.Decal("Blood", tr_main.HitPos + tr_main.HitNormal, tr_main.HitPos - tr_main.HitNormal)
|
||||
|
||||
-- do a bullet just to make blood decals work sanely
|
||||
-- need to disable lagcomp because firebullets does its own
|
||||
self:GetOwner():LagCompensation(false)
|
||||
self:GetOwner():FireBullets({Num=1, Src=spos, Dir=self:GetOwner():GetAimVector(), Spread=Vector(0,0,0), Tracer=0, Force=1, Damage=0})
|
||||
else
|
||||
util.Effect("Impact", edata)
|
||||
end
|
||||
end
|
||||
else
|
||||
self:SendWeaponAnim( ACT_VM_MISSCENTER )
|
||||
end
|
||||
|
||||
|
||||
if CLIENT then
|
||||
-- used to be some shit here
|
||||
else -- SERVER
|
||||
|
||||
-- Do another trace that sees nodraw stuff like func_button
|
||||
local tr_all = nil
|
||||
tr_all = util.TraceLine({start=spos, endpos=sdest, filter=self:GetOwner()})
|
||||
|
||||
self:GetOwner():SetAnimation( PLAYER_ATTACK1 )
|
||||
|
||||
if hitEnt and hitEnt:IsValid() then
|
||||
if self:OpenEnt(hitEnt) == OPEN_NO and tr_all.Entity and tr_all.Entity:IsValid() then
|
||||
-- See if there's a nodraw thing we should open
|
||||
self:OpenEnt(tr_all.Entity)
|
||||
end
|
||||
|
||||
local dmg = DamageInfo()
|
||||
dmg:SetDamage(self.Primary.Damage)
|
||||
dmg:SetAttacker(self:GetOwner())
|
||||
dmg:SetInflictor(self)
|
||||
dmg:SetDamageForce(self:GetOwner():GetAimVector() * 1500)
|
||||
dmg:SetDamagePosition(self:GetOwner():GetPos())
|
||||
dmg:SetDamageType(DMG_CLUB)
|
||||
|
||||
hitEnt:DispatchTraceAttack(dmg, spos + (self:GetOwner():GetAimVector() * 3), sdest)
|
||||
|
||||
-- self:SendWeaponAnim( ACT_VM_HITCENTER )
|
||||
|
||||
-- self:GetOwner():TraceHullAttack(spos, sdest, Vector(-16,-16,-16), Vector(16,16,16), 30, DMG_CLUB, 11, true)
|
||||
-- self:GetOwner():FireBullets({Num=1, Src=spos, Dir=self:GetOwner():GetAimVector(), Spread=Vector(0,0,0), Tracer=0, Force=1, Damage=20})
|
||||
|
||||
else
|
||||
-- if tr_main.HitWorld then
|
||||
-- self:SendWeaponAnim( ACT_VM_HITCENTER )
|
||||
-- else
|
||||
-- self:SendWeaponAnim( ACT_VM_MISSCENTER )
|
||||
-- end
|
||||
|
||||
-- See if our nodraw trace got the goods
|
||||
if tr_all.Entity and tr_all.Entity:IsValid() then
|
||||
self:OpenEnt(tr_all.Entity)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if self:GetOwner().LagCompensation then
|
||||
self:GetOwner():LagCompensation(false)
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:SecondaryAttack()
|
||||
self:SetNextPrimaryFire( CurTime() + self.Primary.Delay )
|
||||
self:SetNextSecondaryFire( CurTime() + 0.1 )
|
||||
|
||||
if self:GetOwner().LagCompensation then
|
||||
self:GetOwner():LagCompensation(true)
|
||||
end
|
||||
|
||||
local tr = self:GetOwner():GetEyeTrace(MASK_SHOT)
|
||||
|
||||
if tr.Hit and IsValid(tr.Entity) and tr.Entity:IsPlayer() and (self:GetOwner():EyePos() - tr.HitPos):Length() < 100 then
|
||||
local ply = tr.Entity
|
||||
|
||||
if SERVER and (not ply:IsFrozen()) then
|
||||
local pushvel = tr.Normal * GetConVar("ttt_crowbar_pushforce"):GetFloat()
|
||||
|
||||
-- limit the upward force to prevent launching
|
||||
pushvel.z = math.Clamp(pushvel.z, 50, 100)
|
||||
|
||||
ply:SetVelocity(ply:GetVelocity() + pushvel)
|
||||
self:GetOwner():SetAnimation( PLAYER_ATTACK1 )
|
||||
|
||||
ply.was_pushed = {att=self:GetOwner(), t=CurTime(), wep=self:GetClass()} --, infl=self}
|
||||
end
|
||||
|
||||
self:EmitSound(sound_single)
|
||||
self:SendWeaponAnim( ACT_VM_HITCENTER )
|
||||
|
||||
self:SetNextSecondaryFire( CurTime() + self.Secondary.Delay )
|
||||
end
|
||||
|
||||
if self:GetOwner().LagCompensation then
|
||||
self:GetOwner():LagCompensation(false)
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:GetClass()
|
||||
return "weapon_zm_improvised"
|
||||
end
|
||||
|
||||
function SWEP:OnDrop()
|
||||
self:Remove()
|
||||
end
|
||||
63
gamemodes/terrortown/entities/weapons/weapon_zm_mac10.lua
Normal file
63
gamemodes/terrortown/entities/weapons/weapon_zm_mac10.lua
Normal file
@@ -0,0 +1,63 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
AddCSLuaFile()
|
||||
|
||||
SWEP.HoldType = "ar2"
|
||||
|
||||
if CLIENT then
|
||||
SWEP.PrintName = "MAC10"
|
||||
SWEP.Slot = 2
|
||||
|
||||
SWEP.ViewModelFlip = false
|
||||
SWEP.ViewModelFOV = 54
|
||||
|
||||
SWEP.Icon = "vgui/ttt/icon_mac"
|
||||
SWEP.IconLetter = "l"
|
||||
end
|
||||
|
||||
SWEP.Base = "weapon_tttbase"
|
||||
|
||||
SWEP.Kind = WEAPON_HEAVY
|
||||
SWEP.WeaponID = AMMO_MAC10
|
||||
|
||||
SWEP.Primary.Damage = 12
|
||||
SWEP.Primary.Delay = 0.065
|
||||
SWEP.Primary.Cone = 0.03
|
||||
SWEP.Primary.ClipSize = 30
|
||||
SWEP.Primary.ClipMax = 60
|
||||
SWEP.Primary.DefaultClip = 30
|
||||
SWEP.Primary.Automatic = true
|
||||
SWEP.Primary.Ammo = "SMG1"
|
||||
SWEP.Primary.Recoil = 1.15
|
||||
SWEP.Primary.Sound = Sound( "Weapon_mac10.Single" )
|
||||
|
||||
SWEP.AutoSpawnable = true
|
||||
SWEP.AmmoEnt = "item_ammo_smg1_ttt"
|
||||
|
||||
SWEP.UseHands = true
|
||||
SWEP.ViewModel = "models/weapons/cstrike/c_smg_mac10.mdl"
|
||||
SWEP.WorldModel = "models/weapons/w_smg_mac10.mdl"
|
||||
|
||||
SWEP.IronSightsPos = Vector(-8.921, -9.528, 2.9)
|
||||
SWEP.IronSightsAng = Vector(0.699, -5.301, -7)
|
||||
|
||||
SWEP.DeploySpeed = 3
|
||||
|
||||
function SWEP:GetHeadshotMultiplier(victim, dmginfo)
|
||||
local att = dmginfo:GetAttacker()
|
||||
if not IsValid(att) then return 2 end
|
||||
|
||||
local dist = victim:GetPos():Distance(att:GetPos())
|
||||
local d = math.max(0, dist - 150)
|
||||
|
||||
-- decay from 3.2 to 1.7
|
||||
return 1.7 + math.max(0, (1.5 - 0.002 * (d ^ 1.25)))
|
||||
end
|
||||
43
gamemodes/terrortown/entities/weapons/weapon_zm_molotov.lua
Normal file
43
gamemodes/terrortown/entities/weapons/weapon_zm_molotov.lua
Normal file
@@ -0,0 +1,43 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
AddCSLuaFile()
|
||||
|
||||
SWEP.HoldType = "grenade"
|
||||
|
||||
if CLIENT then
|
||||
SWEP.PrintName = "grenade_fire"
|
||||
SWEP.Slot = 3
|
||||
|
||||
SWEP.ViewModelFlip = false
|
||||
SWEP.ViewModelFOV = 54
|
||||
|
||||
SWEP.Icon = "vgui/ttt/icon_nades"
|
||||
SWEP.IconLetter = "P"
|
||||
end
|
||||
|
||||
SWEP.Base = "weapon_tttbasegrenade"
|
||||
|
||||
SWEP.Kind = WEAPON_NADE
|
||||
SWEP.WeaponID = AMMO_MOLOTOV
|
||||
|
||||
SWEP.UseHands = true
|
||||
SWEP.ViewModel = "models/weapons/cstrike/c_eq_flashbang.mdl"
|
||||
SWEP.WorldModel = "models/weapons/w_eq_flashbang.mdl"
|
||||
|
||||
SWEP.Weight = 5
|
||||
SWEP.AutoSpawnable = true
|
||||
SWEP.Spawnable = true
|
||||
-- really the only difference between grenade weapons: the model and the thrown
|
||||
-- ent.
|
||||
|
||||
function SWEP:GetGrenadeName()
|
||||
return "ttt_firegrenade_proj"
|
||||
end
|
||||
50
gamemodes/terrortown/entities/weapons/weapon_zm_pistol.lua
Normal file
50
gamemodes/terrortown/entities/weapons/weapon_zm_pistol.lua
Normal file
@@ -0,0 +1,50 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
AddCSLuaFile()
|
||||
|
||||
SWEP.HoldType = "pistol"
|
||||
|
||||
if CLIENT then
|
||||
SWEP.PrintName = "pistol_name"
|
||||
SWEP.Slot = 1
|
||||
|
||||
SWEP.ViewModelFlip = false
|
||||
SWEP.ViewModelFOV = 54
|
||||
|
||||
SWEP.Icon = "vgui/ttt/icon_pistol"
|
||||
SWEP.IconLetter = "u"
|
||||
end
|
||||
|
||||
SWEP.Base = "weapon_tttbase"
|
||||
|
||||
SWEP.Kind = WEAPON_PISTOL
|
||||
SWEP.WeaponID = AMMO_PISTOL
|
||||
|
||||
SWEP.Primary.Recoil = 1.5
|
||||
SWEP.Primary.Damage = 25
|
||||
SWEP.Primary.Delay = 0.38
|
||||
SWEP.Primary.Cone = 0.02
|
||||
SWEP.Primary.ClipSize = 20
|
||||
SWEP.Primary.Automatic = true
|
||||
SWEP.Primary.DefaultClip = 20
|
||||
SWEP.Primary.ClipMax = 60
|
||||
SWEP.Primary.Ammo = "Pistol"
|
||||
SWEP.Primary.Sound = Sound( "Weapon_FiveSeven.Single" )
|
||||
|
||||
SWEP.AutoSpawnable = true
|
||||
SWEP.AmmoEnt = "item_ammo_pistol_ttt"
|
||||
|
||||
SWEP.UseHands = true
|
||||
SWEP.ViewModel = "models/weapons/cstrike/c_pist_fiveseven.mdl"
|
||||
SWEP.WorldModel = "models/weapons/w_pist_fiveseven.mdl"
|
||||
|
||||
SWEP.IronSightsPos = Vector(-5.95, -4, 2.799)
|
||||
SWEP.IronSightsAng = Vector(0, 0, 0)
|
||||
52
gamemodes/terrortown/entities/weapons/weapon_zm_revolver.lua
Normal file
52
gamemodes/terrortown/entities/weapons/weapon_zm_revolver.lua
Normal file
@@ -0,0 +1,52 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
AddCSLuaFile()
|
||||
|
||||
SWEP.HoldType = "pistol"
|
||||
|
||||
if CLIENT then
|
||||
SWEP.PrintName = "Deagle"
|
||||
SWEP.Slot = 1
|
||||
|
||||
SWEP.ViewModelFlip = false
|
||||
SWEP.ViewModelFOV = 54
|
||||
|
||||
SWEP.Icon = "vgui/ttt/icon_deagle"
|
||||
end
|
||||
|
||||
SWEP.Base = "weapon_tttbase"
|
||||
|
||||
SWEP.Kind = WEAPON_PISTOL
|
||||
SWEP.WeaponID = AMMO_DEAGLE
|
||||
|
||||
SWEP.Primary.Ammo = "AlyxGun" -- hijack an ammo type we don't use otherwise
|
||||
SWEP.Primary.Recoil = 6
|
||||
SWEP.Primary.Damage = 37
|
||||
SWEP.Primary.Delay = 0.6
|
||||
SWEP.Primary.Cone = 0.02
|
||||
SWEP.Primary.ClipSize = 8
|
||||
SWEP.Primary.ClipMax = 36
|
||||
SWEP.Primary.DefaultClip = 8
|
||||
SWEP.Primary.Automatic = true
|
||||
SWEP.Primary.Sound = Sound( "Weapon_Deagle.Single" )
|
||||
|
||||
SWEP.HeadshotMultiplier = 4
|
||||
|
||||
SWEP.AutoSpawnable = true
|
||||
SWEP.Spawnable = true
|
||||
SWEP.AmmoEnt = "item_ammo_revolver_ttt"
|
||||
|
||||
SWEP.UseHands = true
|
||||
SWEP.ViewModel = "models/weapons/cstrike/c_pist_deagle.mdl"
|
||||
SWEP.WorldModel = "models/weapons/w_pist_deagle.mdl"
|
||||
|
||||
SWEP.IronSightsPos = Vector(-6.361, -3.701, 2.15)
|
||||
SWEP.IronSightsAng = Vector(0, 0, 0)
|
||||
164
gamemodes/terrortown/entities/weapons/weapon_zm_rifle.lua
Normal file
164
gamemodes/terrortown/entities/weapons/weapon_zm_rifle.lua
Normal file
@@ -0,0 +1,164 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
AddCSLuaFile()
|
||||
|
||||
SWEP.HoldType = "ar2"
|
||||
|
||||
if CLIENT then
|
||||
SWEP.PrintName = "rifle_name"
|
||||
SWEP.Slot = 2
|
||||
|
||||
SWEP.ViewModelFlip = false
|
||||
SWEP.ViewModelFOV = 54
|
||||
|
||||
SWEP.Icon = "vgui/ttt/icon_scout"
|
||||
SWEP.IconLetter = "n"
|
||||
end
|
||||
|
||||
SWEP.Base = "weapon_tttbase"
|
||||
|
||||
SWEP.Kind = WEAPON_HEAVY
|
||||
SWEP.WeaponID = AMMO_RIFLE
|
||||
|
||||
SWEP.Primary.Delay = 1.5
|
||||
SWEP.Primary.Recoil = 7
|
||||
SWEP.Primary.Automatic = true
|
||||
SWEP.Primary.Ammo = "357"
|
||||
SWEP.Primary.Damage = 50
|
||||
SWEP.Primary.Cone = 0.005
|
||||
SWEP.Primary.ClipSize = 10
|
||||
SWEP.Primary.ClipMax = 20 -- keep mirrored to ammo
|
||||
SWEP.Primary.DefaultClip = 10
|
||||
SWEP.Primary.Sound = Sound("Weapon_Scout.Single")
|
||||
|
||||
SWEP.Secondary.Sound = Sound("Default.Zoom")
|
||||
|
||||
SWEP.HeadshotMultiplier = 4
|
||||
|
||||
SWEP.AutoSpawnable = true
|
||||
SWEP.Spawnable = true
|
||||
SWEP.AmmoEnt = "item_ammo_357_ttt"
|
||||
|
||||
SWEP.UseHands = true
|
||||
SWEP.ViewModel = Model("models/weapons/cstrike/c_snip_scout.mdl")
|
||||
SWEP.WorldModel = Model("models/weapons/w_snip_scout.mdl")
|
||||
|
||||
SWEP.IronSightsPos = Vector( 5, -15, -2 )
|
||||
SWEP.IronSightsAng = Vector( 2.6, 1.37, 3.5 )
|
||||
|
||||
function SWEP:SetZoom(state)
|
||||
if IsValid(self:GetOwner()) and self:GetOwner():IsPlayer() then
|
||||
if state then
|
||||
self:GetOwner():SetFOV(20, 0.3)
|
||||
else
|
||||
self:GetOwner():SetFOV(0, 0.2)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:PrimaryAttack( worldsnd )
|
||||
self.BaseClass.PrimaryAttack( self, worldsnd )
|
||||
self:SetNextSecondaryFire( CurTime() + 0.1 )
|
||||
end
|
||||
|
||||
-- Add some zoom to ironsights for this gun
|
||||
function SWEP:SecondaryAttack()
|
||||
if not self.IronSightsPos then return end
|
||||
if self:GetNextSecondaryFire() > CurTime() then return end
|
||||
|
||||
local bIronsights = not self:GetIronsights()
|
||||
|
||||
self:SetIronsights( bIronsights )
|
||||
|
||||
self:SetZoom(bIronsights)
|
||||
if (CLIENT) then
|
||||
self:EmitSound(self.Secondary.Sound)
|
||||
end
|
||||
|
||||
self:SetNextSecondaryFire( CurTime() + 0.3)
|
||||
end
|
||||
|
||||
function SWEP:PreDrop()
|
||||
self:SetZoom(false)
|
||||
self:SetIronsights(false)
|
||||
return self.BaseClass.PreDrop(self)
|
||||
end
|
||||
|
||||
function SWEP:Reload()
|
||||
if ( self:Clip1() == self.Primary.ClipSize or self:GetOwner():GetAmmoCount( self.Primary.Ammo ) <= 0 ) then return end
|
||||
self:DefaultReload( ACT_VM_RELOAD )
|
||||
self:SetIronsights( false )
|
||||
self:SetZoom( false )
|
||||
end
|
||||
|
||||
|
||||
function SWEP:Holster()
|
||||
self:SetIronsights(false)
|
||||
self:SetZoom(false)
|
||||
return true
|
||||
end
|
||||
|
||||
if CLIENT then
|
||||
local scope = surface.GetTextureID("sprites/scope")
|
||||
function SWEP:DrawHUD()
|
||||
if self:GetIronsights() then
|
||||
surface.SetDrawColor( 0, 0, 0, 255 )
|
||||
|
||||
local scrW = ScrW()
|
||||
local scrH = ScrH()
|
||||
|
||||
local x = scrW / 2.0
|
||||
local y = scrH / 2.0
|
||||
local scope_size = scrH
|
||||
|
||||
-- crosshair
|
||||
local gap = 80
|
||||
local length = scope_size
|
||||
surface.DrawLine( x - length, y, x - gap, y )
|
||||
surface.DrawLine( x + length, y, x + gap, y )
|
||||
surface.DrawLine( x, y - length, x, y - gap )
|
||||
surface.DrawLine( x, y + length, x, y + gap )
|
||||
|
||||
gap = 0
|
||||
length = 50
|
||||
surface.DrawLine( x - length, y, x - gap, y )
|
||||
surface.DrawLine( x + length, y, x + gap, y )
|
||||
surface.DrawLine( x, y - length, x, y - gap )
|
||||
surface.DrawLine( x, y + length, x, y + gap )
|
||||
|
||||
|
||||
-- cover edges
|
||||
local sh = scope_size / 2
|
||||
local w = (x - sh) + 2
|
||||
surface.DrawRect(0, 0, w, scope_size)
|
||||
surface.DrawRect(x + sh - 2, 0, w, scope_size)
|
||||
|
||||
-- cover gaps on top and bottom of screen
|
||||
surface.DrawLine( 0, 0, scrW, 0 )
|
||||
surface.DrawLine( 0, scrH - 1, scrW, scrH - 1 )
|
||||
|
||||
surface.SetDrawColor(255, 0, 0, 255)
|
||||
surface.DrawLine(x, y, x + 1, y + 1)
|
||||
|
||||
-- scope
|
||||
surface.SetTexture(scope)
|
||||
surface.SetDrawColor(255, 255, 255, 255)
|
||||
|
||||
surface.DrawTexturedRectRotated(x, y, scope_size, scope_size, 0)
|
||||
else
|
||||
return self.BaseClass.DrawHUD(self)
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:AdjustMouseSensitivity()
|
||||
return (self:GetIronsights() and 0.2) or nil
|
||||
end
|
||||
end
|
||||
191
gamemodes/terrortown/entities/weapons/weapon_zm_shotgun.lua
Normal file
191
gamemodes/terrortown/entities/weapons/weapon_zm_shotgun.lua
Normal file
@@ -0,0 +1,191 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
AddCSLuaFile()
|
||||
|
||||
DEFINE_BASECLASS "weapon_tttbase"
|
||||
|
||||
SWEP.HoldType = "shotgun"
|
||||
|
||||
if CLIENT then
|
||||
SWEP.PrintName = "shotgun_name"
|
||||
SWEP.Slot = 2
|
||||
|
||||
SWEP.ViewModelFlip = false
|
||||
SWEP.ViewModelFOV = 54
|
||||
|
||||
SWEP.Icon = "vgui/ttt/icon_shotgun"
|
||||
SWEP.IconLetter = "B"
|
||||
end
|
||||
|
||||
SWEP.Base = "weapon_tttbase"
|
||||
|
||||
SWEP.Kind = WEAPON_HEAVY
|
||||
SWEP.WeaponID = AMMO_SHOTGUN
|
||||
|
||||
SWEP.Primary.Ammo = "Buckshot"
|
||||
SWEP.Primary.Damage = 11
|
||||
SWEP.Primary.Cone = 0.082
|
||||
SWEP.Primary.Delay = 0.8
|
||||
SWEP.Primary.ClipSize = 8
|
||||
SWEP.Primary.ClipMax = 24
|
||||
SWEP.Primary.DefaultClip = 8
|
||||
SWEP.Primary.Automatic = true
|
||||
SWEP.Primary.NumShots = 8
|
||||
SWEP.Primary.Sound = Sound( "Weapon_XM1014.Single" )
|
||||
SWEP.Primary.Recoil = 7
|
||||
|
||||
SWEP.AutoSpawnable = true
|
||||
SWEP.Spawnable = true
|
||||
SWEP.AmmoEnt = "item_box_buckshot_ttt"
|
||||
|
||||
SWEP.UseHands = true
|
||||
SWEP.ViewModel = "models/weapons/cstrike/c_shot_xm1014.mdl"
|
||||
SWEP.WorldModel = "models/weapons/w_shot_xm1014.mdl"
|
||||
|
||||
SWEP.IronSightsPos = Vector(-6.881, -9.214, 2.66)
|
||||
SWEP.IronSightsAng = Vector(-0.101, -0.7, -0.201)
|
||||
|
||||
function SWEP:SetupDataTables()
|
||||
self:NetworkVar("Bool", 0, "Reloading")
|
||||
self:NetworkVar("Float", 0, "ReloadTimer")
|
||||
|
||||
return BaseClass.SetupDataTables(self)
|
||||
end
|
||||
|
||||
function SWEP:Reload()
|
||||
|
||||
if self:GetReloading() then return end
|
||||
|
||||
if self:Clip1() < self.Primary.ClipSize and self:GetOwner():GetAmmoCount( self.Primary.Ammo ) > 0 then
|
||||
|
||||
if self:StartReload() then
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function SWEP:StartReload()
|
||||
if self:GetReloading() then
|
||||
return false
|
||||
end
|
||||
|
||||
self:SetIronsights( false )
|
||||
|
||||
self:SetNextPrimaryFire( CurTime() + self.Primary.Delay )
|
||||
|
||||
local ply = self:GetOwner()
|
||||
|
||||
if not ply or ply:GetAmmoCount(self.Primary.Ammo) <= 0 then
|
||||
return false
|
||||
end
|
||||
|
||||
local wep = self
|
||||
|
||||
if wep:Clip1() >= self.Primary.ClipSize then
|
||||
return false
|
||||
end
|
||||
|
||||
wep:SendWeaponAnim(ACT_SHOTGUN_RELOAD_START)
|
||||
|
||||
self:SetReloadTimer(CurTime() + wep:SequenceDuration())
|
||||
|
||||
self:SetReloading(true)
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function SWEP:PerformReload()
|
||||
local ply = self:GetOwner()
|
||||
|
||||
-- prevent normal shooting in between reloads
|
||||
self:SetNextPrimaryFire( CurTime() + self.Primary.Delay )
|
||||
|
||||
if not ply or ply:GetAmmoCount(self.Primary.Ammo) <= 0 then return end
|
||||
|
||||
if self:Clip1() >= self.Primary.ClipSize then return end
|
||||
|
||||
self:GetOwner():RemoveAmmo( 1, self.Primary.Ammo, false )
|
||||
self:SetClip1( self:Clip1() + 1 )
|
||||
|
||||
self:SendWeaponAnim(ACT_VM_RELOAD)
|
||||
|
||||
self:SetReloadTimer(CurTime() + self:SequenceDuration())
|
||||
end
|
||||
|
||||
function SWEP:FinishReload()
|
||||
self:SetReloading(false)
|
||||
self:SendWeaponAnim(ACT_SHOTGUN_RELOAD_FINISH)
|
||||
|
||||
self:SetReloadTimer(CurTime() + self:SequenceDuration())
|
||||
end
|
||||
|
||||
function SWEP:CanPrimaryAttack()
|
||||
if self:Clip1() <= 0 then
|
||||
self:EmitSound( "Weapon_Shotgun.Empty" )
|
||||
self:SetNextPrimaryFire( CurTime() + self.Primary.Delay )
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function SWEP:Think()
|
||||
BaseClass.Think(self)
|
||||
if self:GetReloading() then
|
||||
if self:GetOwner():KeyDown(IN_ATTACK) then
|
||||
self:FinishReload()
|
||||
return
|
||||
end
|
||||
|
||||
if self:GetReloadTimer() <= CurTime() then
|
||||
|
||||
if self:GetOwner():GetAmmoCount(self.Primary.Ammo) <= 0 then
|
||||
self:FinishReload()
|
||||
elseif self:Clip1() < self.Primary.ClipSize then
|
||||
self:PerformReload()
|
||||
else
|
||||
self:FinishReload()
|
||||
end
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:Deploy()
|
||||
self:SetReloading(false)
|
||||
self:SetReloadTimer(0)
|
||||
return BaseClass.Deploy(self)
|
||||
end
|
||||
|
||||
-- The shotgun's headshot damage multiplier is based on distance. The closer it
|
||||
-- is, the more damage it does. This reinforces the shotgun's role as short
|
||||
-- range weapon by reducing effectiveness at mid-range, where one could score
|
||||
-- lucky headshots relatively easily due to the spread.
|
||||
function SWEP:GetHeadshotMultiplier(victim, dmginfo)
|
||||
local att = dmginfo:GetAttacker()
|
||||
if not IsValid(att) then return 3 end
|
||||
|
||||
local dist = victim:GetPos():Distance(att:GetPos())
|
||||
local d = math.max(0, dist - 140)
|
||||
|
||||
-- Decay from 2 to 1 slowly as distance increases. Note that this used to be
|
||||
-- 3+, but at that time shotgun bullets were treated like in HL2 where half
|
||||
-- of them were hull traces that could not headshot.
|
||||
return 1 + math.max(0, (1.0 - 0.002 * (d ^ 1.25)))
|
||||
end
|
||||
|
||||
function SWEP:SecondaryAttack()
|
||||
if self.NoSights or (not self.IronSightsPos) or self:GetReloading() then return end
|
||||
|
||||
self:SetIronsights(not self:GetIronsights())
|
||||
|
||||
self:SetNextSecondaryFire(CurTime() + 0.3)
|
||||
end
|
||||
52
gamemodes/terrortown/entities/weapons/weapon_zm_sledge.lua
Normal file
52
gamemodes/terrortown/entities/weapons/weapon_zm_sledge.lua
Normal file
@@ -0,0 +1,52 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
AddCSLuaFile()
|
||||
|
||||
SWEP.HoldType = "crossbow"
|
||||
|
||||
if CLIENT then
|
||||
SWEP.PrintName = "H.U.G.E-249"
|
||||
SWEP.Slot = 2
|
||||
|
||||
SWEP.ViewModelFlip = false
|
||||
SWEP.ViewModelFOV = 54
|
||||
|
||||
SWEP.Icon = "vgui/ttt/icon_m249"
|
||||
SWEP.IconLetter = "z"
|
||||
end
|
||||
|
||||
SWEP.Base = "weapon_tttbase"
|
||||
|
||||
SWEP.Spawnable = true
|
||||
SWEP.AutoSpawnable = true
|
||||
|
||||
SWEP.Kind = WEAPON_HEAVY
|
||||
SWEP.WeaponID = AMMO_M249
|
||||
|
||||
SWEP.Primary.Damage = 7
|
||||
SWEP.Primary.Delay = 0.06
|
||||
SWEP.Primary.Cone = 0.09
|
||||
SWEP.Primary.ClipSize = 150
|
||||
SWEP.Primary.ClipMax = 150
|
||||
SWEP.Primary.DefaultClip = 150
|
||||
SWEP.Primary.Automatic = true
|
||||
SWEP.Primary.Ammo = "AirboatGun"
|
||||
SWEP.Primary.Recoil = 1.9
|
||||
SWEP.Primary.Sound = Sound("Weapon_m249.Single")
|
||||
|
||||
SWEP.UseHands = true
|
||||
SWEP.ViewModel = "models/weapons/cstrike/c_mach_m249para.mdl"
|
||||
SWEP.WorldModel = "models/weapons/w_mach_m249para.mdl"
|
||||
|
||||
SWEP.HeadshotMultiplier = 2.2
|
||||
|
||||
SWEP.IronSightsPos = Vector(-5.96, -5.119, 2.349)
|
||||
SWEP.IronSightsAng = Vector(0, 0, 0)
|
||||
Reference in New Issue
Block a user