Help with grenade SWEP

I’ve designed a grenade intending Traitors to randomly produce one of several overpowered weapons.

However, my test SWEP is not functioning correctly, while it occasioonally successfully drops one of the listed weapons it does not leave the players inventory and it only drops one kind of weapon per map.

Any help would be wonderful.



if SERVER then
   AddCSLuaFile( "shared.lua" )
   
end

SWEP.HoldType = "grenade"


if CLIENT then
   SWEP.PrintName = "Mystery"
   SWEP.Slot = 3
   SWEP.SlotPos	= 0

   SWEP.Icon = "VGUI/ttt/icon_nades"
end

SWEP.Base				= "weapon_tttbasegrenade"


SWEP.Kind = WEAPON_NADE
SWEP.WeaponID = AMMO_DISCOMB

SWEP.Spawnable = true
SWEP.AdminSpawnable = true


SWEP.AutoSpawnable      = false

SWEP.UseHands			= true
SWEP.ViewModelFlip		= false
SWEP.ViewModelFOV		= 54
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()
	local chance = math.random(4)
	if chance == 1 then
		return "weapon_ttt_knife"
	elseif chance == 2 then
		return "dragon"
	elseif chance == 3 then
		return "heatgoogles"
	else
		return "ttt_manhacknade_proj"
	end
end



Try that


function SWEP:GetGrenadeName()
	local chance = math.random(1,4)
	if chance == 1 then
		return "weapon_ttt_knife"
	elseif chance == 2 then
		return "dragon"
	elseif chance == 3 then
		return "heatgoogles"
	else
		return "ttt_manhacknade_proj"
	end
end

or this:


function SWEP:GetGrenadeName()
	local chance = math.Rand(1,4)
	if chance == 1 then
		return "weapon_ttt_knife"
	elseif chance == 2 then
		return "dragon"
	elseif chance == 3 then
		return "heatgoogles"
	else
		return "ttt_manhacknade_proj"
	end
end

All that does is change the possible numbers from 0, 1, 2, or 3 to 1, 2, 3, or 4.

Or it does nothing I forget whether lua counts from zero.

Well if that is the case then you need to look at the grenades base. I see nothing wrong with what you have.

I should mention that the only thing I’ve gotten to work from it is the knife. Additionally here is the base grenade code, it is quite lengthy.

– common code for all types of grenade

[LUA]if SERVER then

AddCSLuaFile( “shared.lua” )

end

SWEP.HoldReady = “grenade”
SWEP.HoldNormal = “slam”

if CLIENT then

SWEP.PrintName = “Incendiary grenade”
SWEP.Instructions = “Burn.”
SWEP.Slot = 3
SWEP.SlotPos = 0

SWEP.Icon = “VGUI/ttt/icon_nades”
end

SWEP.Base = “weapon_tttbase”

SWEP.Kind = WEAPON_NADE

SWEP.ViewModel = “models/weapons/v_eq_flashbang.mdl”
SWEP.WorldModel = “models/weapons/w_eq_flashbang.mdl”
SWEP.Weight = 5

SWEP.ViewModelFlip = true
SWEP.AutoSwitchFrom = true

SWEP.DrawCrosshair = false

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.IsGrenade = true
SWEP.NoSights = true

SWEP.pin_pulled = false
SWEP.throw_time = 0

SWEP.was_thrown = false

SWEP.detonate_timer = 5

SWEP.DeploySpeed = 1.5

AccessorFuncDT( SWEP, “pin_pulled”, “Pin”)
AccessorFuncDT( SWEP, “throw_time”, “ThrowTime”)

AccessorFunc(SWEP, “det_time”, “DetTime”)

CreateConVar(“ttt_no_nade_throw_during_prep”, “0”)

function SWEP:SetupDataTables()
self:DTVar(“Bool”, 0, “pin_pulled”)
self:DTVar(“Int”, 0, “throw_time”)
end

function SWEP:PrimaryAttack()
self.Weapon: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.Owner
if not IsValid(ply) then return end

self.Weapon:SendWeaponAnim(ACT_VM_PULLPIN)

if self.SetWeaponHoldType then
self:SetWeaponHoldType(self.HoldReady)
end

self:SetPin(true)

self:SetDetTime(CurTime() + self.detonate_timer)
end

function SWEP:Think()
local ply = self.Owner
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.Weapon:SendWeaponAnim(ACT_VM_THROW)

     if SERVER then
        self.Owner: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.Owner
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.Owner
if not IsValid(ply) then return end

  if self.was_thrown then return end

  self.was_thrown = true

  local ang = ply:EyeAngles()

  -- don't even know what this bit is for, but SDK has it
  -- probably to make it throw upward a bit
  if ang.p &lt; 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.min(800, (90 - ang.p) * 6)

  local vfw = ang:Forward()
  local vrt = ang:Right()
  --      local vup = ang:Up()

  local src = ply:GetPos() + (ply:Crouching() and ply:GetViewOffsetDucked() or ply:GetViewOffset())
  src = src + (vfw * 8) + (vrt * 10)

  local thr = vfw * 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!
")
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.SetWeaponHoldType then
self:SetWeaponHoldType(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.SetWeaponHoldType then
self:SetWeaponHoldType(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.Owner) and self.Owner == LocalPlayer() and self.Owner:Alive() then
RunConsoleCommand(“use”, “weapon_ttt_unarmed”)
end
end
[/LUA]

Instead of it being a grenade which throws a knife and such, why dont you just make it a weapon? I mean why dont you just do the math random and just do if chance == 4 then self.Owner:Give(“weapon_ttt_manhackgrenade”) ? It would work better.

Ideally I’m trying to make a grenade that will wait a few seconds and then spawn an item with this grenade acting as a placeholder but that may work as a last resort.

[editline]24th January 2014[/editline]

Alright modified the primary attack of the teleporter slightly to give weapons with an effect so here is the code I modified


function SWEP:PrimaryAttack()
--test
   self.Weapon:SetNextSecondaryFire( CurTime() + self.Secondary.Delay )

   if SERVER then
      self:TeleportStore()
   else
      surface.PlaySound("ui/buttonrollover.wav")
   end
--test
   self.Weapon:SetNextPrimaryFire( CurTime() + self.Primary.Delay )

   if self.Weapon: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()
	self.remove()
	local chance = math.Rand(1,4)
	if chance == 1 then
		self.Owner:Give("weapon_ttt_knife")
	elseif chance == 2 then
		self.Owner:Give("dragon")
	elseif chance == 3 then
		self.Owner:Give("heatgoogles")
	else
		self.Owner:Give("weapon_ttt_manhackgrenade")
	end
   else
      surface.PlaySound("buttons/combine_button7.wav")
   end
end

And here is all of the code put together



-- traitor equipment: teleporter

if SERVER then
   AddCSLuaFile( "shared.lua" )
end

SWEP.HoldType = "normal"

if CLIENT then
   SWEP.PrintName = "Mystery"
   SWEP.Slot = 7

   SWEP.ViewModelFOV = 10

   SWEP.EquipMenuData = {
      type = "item_weapon",
      desc = "Teleport a weapon from the aether"
   };

   SWEP.Icon = "VGUI/ttt/icon_beacon"
end

SWEP.Base = "weapon_tttbase"

SWEP.ViewModel          = "models/weapons/v_crowbar.mdl"
SWEP.WorldModel         = "models/weapons/w_slam.mdl"

SWEP.DrawCrosshair      = false
SWEP.ViewModelFlip      = false
SWEP.Primary.ClipSize    = 1
SWEP.Primary.DefaultClip = 1
SWEP.Primary.ClipMax     = 1
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

SWEP.CSMuzzleFlashes = false

local delay_beamup = 1
local delay_beamdown = 1

local ttt_telefrags = CreateConVar("ttt_teleport_telefrags", "0")

function SWEP:SetTeleportMark(pos, ang)
   self.teleport = {pos = pos, ang = ang}
end

function SWEP:GetTeleportMark() return self.teleport end

function SWEP:PrimaryAttack()
--test
   self.Weapon:SetNextSecondaryFire( CurTime() + self.Secondary.Delay )

   if SERVER then
      self:TeleportStore()
   else
      surface.PlaySound("ui/buttonrollover.wav")
   end
--test
   self.Weapon:SetNextPrimaryFire( CurTime() + self.Primary.Delay )

   if self.Weapon: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()
	self.remove()
	local chance = math.Rand(1,4)
	if chance == 1 then
		self.Owner:Give("weapon_ttt_knife")
	elseif chance == 2 then
		self.Owner:Give("dragon")
	elseif chance == 3 then
		self.Owner:Give("heatgoogles")
	else
		self.Owner:Give("weapon_ttt_manhackgrenade")
	end
   else
      surface.PlaySound("buttons/combine_button7.wav")
   end
end
function SWEP:SecondaryAttack()
   self.Weapon: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)
   if not IsValid(victim) then return end

   local dmginfo = DamageInfo()
   dmginfo:SetDamage(5000)
   dmginfo:SetDamageType(DMG_SONIC)
   dmginfo:SetAttacker(attacker)
   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 pairs(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)
   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 pairs(block_plys) do
               Telefrag(p, ply)
            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)
   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) 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_up:SetOrigin(teleport.pos)
   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_beamdown", edata_dn)
end

function SWEP:TeleportRecall(ply)
   local ply = self.Owner
   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) 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.Owner
   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.Owner) then
      self.Owner:DrawViewModel(false)
   end

   return true
end

function SWEP:ShootEffects() end


It doesn’t work… the tp goes through but no items are given.