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