This commit is contained in:
lifestorm
2024-08-04 22:55:00 +03:00
parent 8064ba84d8
commit 73479cff9e
7338 changed files with 1718883 additions and 14 deletions

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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/
--]]
-- 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

View File

@@ -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

View 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/
--]]
-- 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

View 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/
--]]
-- 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

View File

@@ -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

View 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

View 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)

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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/
--]]
-- 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View File

@@ -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

View File

@@ -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"

View File

@@ -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)

View File

@@ -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

View 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

View 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

View 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

View 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

View 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

View 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

View 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/
--]]
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

View 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

View 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

View 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

View 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

View 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 )

View File

@@ -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

View 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

View 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

View 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

View 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

View 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

View 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/
--]]
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

View 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_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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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)

View 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)

View 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

View 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

View 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)