Build Mode!

Hello. I’m trying to make a sort of build mode at the moment, where the player can select a prop from a menu, and it will appear as semi transparent where they are aiming. They can then press ‘e’ and rotate it, then ‘e’ again to finalize it. A bit like easy weld in sandbox.
Here’s what I’ve got so far.



BuildTable = {}

function BuildPosition(  )

	if BuildMode == 1 then
	
		--print( "Build Mode 1" )
		buildblock = table.GetFirstValue( BuildTable )
		builder = buildblock:GetPhysicsAttacker()
		local trace = builder:GetEyeTrace();
		trace.filter = table.GetFirstValue( BuildTable );
		
		buildblock:SetCollisionGroup( 20 )
		
		buildblock:SetPos( trace.HitPos )
		buildblock:SetAngles( trace.HitNormal:Angle() )
		buildblock:SetRenderMode(RENDERMODE_TRANSALPHA)
		buildblock:SetColor( Color( 255, 255, 255, 200 ) )
	
	elseif BuildMode == 2 then
	
		local blockangle = Angle( buildblock:GetAngles().y, 0, -builder:EyeAngles().y * 4 )

		--print( "Build Mode 2" )
		buildblock:SetPos( buildblock:GetPos( ) )
		buildblock:SetAngles( blockangle )
		buildblock:SetColor( Color( 255, 255, 255, 255 ) )
		
	end
	


end
hook.Add( "Think", "BuildModeHook", BuildPosition )

function GM:KeyPress( ply, key )
 
 	if  BuildMode == 1 and key == IN_USE then
	
		BuildMode = 2
		buildblock:SetMoveType( 0 )
		buildblock:SetCollisionGroup( 0 )
	
	elseif  BuildMode == 2 and key == IN_USE then
	
		constraint.Weld( buildblock, game.GetWorld() )
		table.RemoveByValue( BuildTable, buildblock )
		BuildMode = 3
	   
    end
 
--This stuff is irrelevant

	if ( key == IN_DUCK ) then
		
		local playerFallSpeed = ply:GetVelocity():Length()
		
		BounceProtect = true
		
		timer.Create( "FallDamageTimer", 150/playerFallSpeed, 1, function() BounceProtect = false end )
		
	else
	
		timer.Stop( "FallDamageTimer" )
		
	end

end


When the prop is spawned, it is added to buildtable. I pretty much made this up as I went along, so I have no idea if it’s the best way of doing things.

Anyway, I’m hitting a couple of problems at the moment. Firstly is that the GetEyeTrace() includes the prop I’ve spawned, so it moves towards me and then hovers inside my face :v: I tried to get around this by filtering buildblock from the table, but it doesn’t seem to have done anything. Ideally, I’d want to filter out the block itself, but no other entities.

Another slightly more troubling issue is that after a few seconds in buildmode = 1, I suddenly start getting spammed by ‘attempt to call method GetEyeTrace (a nil value)’ errors. I haven’t figured out whats causing this yet. Any ideas?

You’re using GetEyeTrace wrong…
When you call player.GetEyeTrace it returns a completed trace table, you can’t add a filter to it afterwords because the trace is already complete. You need to do a trace like this:
local trace = util.QuickTrace(ply:EyePos, ply:EyeAngles(), table.GetFirstValue(BuildTable))
because the filter needs to be applied before the trace.

And I’m pretty sure you’re getting the error because “builder” is not a player, or a player is not being returned by GetPhysicsAttacker.

I’m just struggling to figure out why ‘builder’ wouldn’t be a player- when I spawn the prop, I use



function spawn_MPlate1x1( ply )

	if ply:CanAfford( 4 ) then
	
	ply:AddMoney( -4 )

	local trace = { }  
	trace.start = ply:EyePos()  
	trace.endpos = trace.start + ply:GetAimVector() * 85  
	trace.filter = ply  
	 
	local tr = util.TraceLine( trace )  
	  
	local Mplate1x1 = ents.Create("prop_physics")  
	Mplate1x1:SetModel("models/props_phx/construct/metal_plate1.mdl")   
	Mplate1x1:SetPos(tr.HitPos)
	Mplate1x1:SetAngles( ply:GetAngles() )
	Mplate1x1:SetPhysicsAttacker(ply)
	BuildMode = 1
	table.insert( BuildTable, Mplate1x1 )
	Mplate1x1:Spawn() 
	
	end
	
end
concommand.Add( "spawn_1x1met", spawn_MPlate1x1 )


Which sets the player as the physics attacker.

EDIT:

Oh, and the full error I get is




[ERROR] gamemodes/ninjas!/gamemode/init.lua:613: Tried to use a NULL entity!
  1. EyePos - [C]:-1
   2. v - gamemodes/ninjas!/gamemode/init.lua:613
    3. unknown - lua/includes/modules/hook.lua:84

TEST [lua/includes/modules/hon.lua][lua/includes/modules/hook.lua]
TEST [lua/includes/modules/json.lua][lua/includes/modules/hook.lua]



I’m just assuming something is overriding the PhysicsAttacker entity, so try just setting a variable on the entity table:
Mplate1x1.Player = ply

and then accessing it with buildblock.Player

If you need more help just PM me and you can add me on Steam.
Also try printing the entity and checking if it is valid.

Cheers.

Using util.trace still doesn’t seem to work (the block sticks in my face). Is there no way at all to filter entity:GetEyeTrace()?

Okay, so now I have



function spawn_MPlate1x1( ply )

	if ply:CanAfford( 4 ) then
	
	ply:AddMoney( -4 )

	local trace = { }  
	trace.start = ply:EyePos()  
	trace.endpos = trace.start + ply:GetAimVector() * 85  
	trace.filter = ply  
	 
	local tr = util.TraceLine( trace )  
	  
	local Mplate1x1 = ents.Create("prop_physics")  
	Mplate1x1:SetModel("models/props_phx/construct/metal_plate1.mdl")   
	Mplate1x1:SetPos(tr.HitPos)
	Mplate1x1:SetAngles( ply:GetAngles() )
	Mplate1x1:SetPhysicsAttacker( ply )
	BuildMode = 1
	table.insert( BuildTable, Mplate1x1 )
	Mplate1x1:SetHealth( math.huge )
	Mplate1x1:Spawn() 
	Mplate1x1.builder = ply
	builderID = ply
	
	end
	
end
concommand.Add( "spawn_1x1met", spawn_MPlate1x1 )

BuildTable = {}

function BuildPosition(  )

	if BuildMode == 1 then
	
		--print( "Build Mode 1" )
		buildblock = table.GetFirstValue( BuildTable )
		builder = buildblock.builder
		--print( builder:Nick() )
		
		local trace = { }  
		trace.start = builder:EyePos()  
		trace.endpos = trace.start + builder:GetAimVector() * 200 
		trace.filter = {	builder,
							buildblock 
						}
						
		local tr = util.TraceLine( trace )  

		
		buildblock:SetCollisionGroup( 20 )
		
		buildblock:SetPos( tr.HitPos )
		buildblock:SetAngles( tr.HitNormal:Angle() )
		buildblock:SetRenderMode(RENDERMODE_TRANSALPHA)
		buildblock:SetColor( Color( 255, 255, 255, 200 ) )
	
	elseif BuildMode == 2 then
	
		local blockangle = Angle( buildblock:GetAngles().p, -builder:EyeAngles().y * 4, 0 )

		--print( "Build Mode 2" )
		buildblock:SetPos( buildblock:GetPos( ) )
		buildblock:SetAngles( blockangle )
		buildblock:SetColor( Color( 255, 255, 255, 255 ) )
		
	end
	


end
hook.Add( "Think", "BuildModeHook", BuildPosition )

function GM:KeyPress( ply, key )
 
 	if  BuildMode == 1 and key == IN_USE then
	
		BuildMode = 2
		buildblock:SetMoveType( 0 )
		buildblock:SetCollisionGroup( 0 )
		buildblock:EmitSound( table.Random( PropPlacedTable ), 100, 100 )
	
	elseif  BuildMode == 2 and key == IN_USE then
	
		buildblock:EmitSound( table.Random( PropPlacedTable ), 100, 100 )
		constraint.Weld( buildblock, game.GetWorld() )
		buildblock:SetHealth( buildblock:GetPhysicsObject():GetMass() )
		print( "block health is", buildblock:Health() )
		table.RemoveByValue( BuildTable, buildblock )
		BuildMode = 3
	   
    end

    etc,etc


I just tested it out with another player and it doesn’t work. This is because buildblock is set to = the first value in BuildTable. So basically, two people can’t be building at the same time. I expected this issue to crop up, but I don’t really know how I can avoid it. Maybe I could attach the build mode variable to the player, rather than have it as a global?

Hmm. I’ve changed it to this now.



function spawn_MPlate1x1( ply )

	if ply:CanAfford( 4 ) then
	
	ply:AddMoney( -0 )

	local trace = { }  
	trace.start = ply:EyePos()  
	trace.endpos = trace.start + ply:GetAimVector() * 85  
	trace.filter = ply  
	 
	local tr = util.TraceLine( trace )  
	  
	local Mplate1x1 = ents.Create("prop_physics")  
	Mplate1x1:SetModel("models/props_phx/construct/metal_plate1.mdl")   
	Mplate1x1:SetPos(tr.HitPos)
	Mplate1x1:SetAngles( ply:GetAngles() )
	Mplate1x1:SetPhysicsAttacker( ply )
	ply.BuildMode = 1
	--table.insert( BuildTable, Mplate1x1 )
	Mplate1x1:SetHealth( math.huge )
	Mplate1x1:Spawn() 
	buildblock = Mplate1x1
	Mplate1x1.builder = ply
	
	end
	
end
concommand.Add( "spawn_1x1met", spawn_MPlate1x1 )

--BuildTable = {}

function BuildPosition(  )

	if BuildMode == 1 then
	
		--print( "Build Mode 1" )		
		builder = buildblock.builder
		--print( builder:Nick() )		
		local trace = { }  
		trace.start = builder:EyePos()  
		trace.endpos = trace.start + builder:GetAimVector() * 200 
		trace.filter = {	builder,
							buildblock 
						}
						
		local tr = util.TraceLine( trace )  

		
		buildblock:SetCollisionGroup( 20 )
		
		buildblock:SetPos( tr.HitPos )
		buildblock:SetAngles( tr.HitNormal:Angle() )
		buildblock:SetRenderMode(RENDERMODE_TRANSALPHA)
		buildblock:SetColor( Color( 255, 255, 255, 200 ) )
	
	elseif BuildMode == 2 then
	
		local blockangle = Angle( buildblock:GetAngles().p, -builder:EyeAngles().y * 4, 0 )

		--print( "Build Mode 2" )
		buildblock:SetPos( buildblock:GetPos( ) )
		buildblock:SetAngles( blockangle )
		buildblock:SetColor( Color( 255, 255, 255, 255 ) )
		
	end

end
hook.Add( "Think", "BuildModeHook", BuildPosition )

function GM:KeyPress( ply, key )
 
 	if  BuildMode == 1 and key == IN_USE then
	
		BuildMode = 2
		buildblock:SetMoveType( 0 )
		ply:EmitSound( table.Random( PropPlacedTable ), 100, 100 )
	
	elseif  BuildMode == 2 and key == IN_USE then
	
		ply:EmitSound( table.Random( PropPlacedTable ), 100, 100 )
		constraint.Weld( buildblock, game.GetWorld() )
		buildblock:SetHealth( buildblock:GetPhysicsObject():GetMass() )
		print( "block health is", buildblock:Health() )
		buildblock:SetCollisionGroup( 0 )
		buildblock:SetMoveType( 0 )
		--table.RemoveByValue( BuildTable, buildblock )
		BuildMode = 3

etc


It works a little better now. Doesn’t spit out errors when two people try to build something. Unfortunately, however, when two people enter build mode, only the most recently spawned prop follow the spawner’s aim. Any ideas?

Oh god. It’s getting worse!



function spawn_MPlate2x2( ply )

	if ply:CanAfford( 4 ) then
	
	ply:AddMoney( -4 )

	local trace = { }  
	trace.start = ply:EyePos()  
	trace.endpos = trace.start + ply:GetAimVector() * 85  
	trace.filter = ply  
	 
	local tr = util.TraceLine( trace )  
	  
	local Mplate2x2 = ents.Create("prop_physics")  
	Mplate2x2:SetModel("models/props_phx/construct/metal_plate2x2.mdl")   
	Mplate2x2:SetPos(tr.HitPos)
	Mplate2x2:SetAngles( ply:GetAngles() )
	Mplate2x2:SetPhysicsAttacker( ply )
	ply.BuildMode = 1 --sets the player's buildmode
	table.insert( BuildTable, Mplate2x2 ) --adds it to the build table
	Mplate2x2:SetHealth( math.huge )
	Mplate2x2:Spawn() 
	Mplate2x2.builder = ply
	
		table.Add( ActiveBuildTable, BuildTable ) --everything in build table to 'active build table'
		
		buildblock = table.GetFirstValue( BuildTable ) --then the first entity in build table is defined as what we are building with
		
		builder = buildblock.builder --then the builder is defined as the player (attached to the prop earlier)
		
		table.Empty( BuildTable ) --then the build table is emptied, ready for someone else to start building
	
	end
	
end
concommand.Add( "spawn_2x2met", spawn_MPlate2x2 )

BuildTable = {}
ActiveBuildTable = {}

function IsValid( builder )

    if builder and builder:IsValid( ) then
	
        print( "builder is", builder:IsValid() ) --This doesn't seem to work, or something. Says the builder is valid,  before it's been defined
		builderExistsBool = false
		
    end
	
end

function BuildPosition(  ) --I need the stuff in this function to be called *only* when 'builder' has been defined.
	
	if builder.BuildMode == 1 then 
				
		local trace = { }  
		trace.start = builder:EyePos()  
		trace.endpos = trace.start + builder:GetAimVector() * 200 
		trace.filter = {	builder,
							buildblock 
						}
						
		local tr = util.TraceLine( trace )  

		
		buildblock:SetCollisionGroup( 20 )
		
		buildblock:SetPos( tr.HitPos )
		buildblock:SetAngles( tr.HitNormal:Angle() )
		buildblock:SetRenderMode(RENDERMODE_TRANSALPHA)
		buildblock:SetColor( Color( 255, 255, 255, 150 ) )
	
	elseif builder.BuildMode == 2 then
	
		local blockangle = Angle( buildblock:GetAngles().p, -builder:EyeAngles().y * 4, 0 )

		--print( "Build Mode 2" )
		buildblock:SetPos( buildblock:GetPos( ) )
		buildblock:SetAngles( blockangle )
		buildblock:SetColor( Color( 255, 255, 255, 240 ) )
	
			
	end

end
hook.Add( "Think", "BuildModeHook", BuildPosition )


I tried to make buildmode a variable which is attached to the builder. But the builder isn’t defined befor they start building, so I tried adding a check to see if the builder is valid. That doesn’t seem to work. The console is spitting out errors about builder being a nil value, while telling me that ‘builder’ is valid at the same time. I’m tying myself in knots.