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.
[code]
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
[/code]
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.[URL="http://wiki.garrysmod.com/page/util/QuickTrace"]QuickTrace[/URL](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
[code]
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 )
[/code]
Which sets the player as the physics attacker.
EDIT:
Oh, and the full error I get is
[code]
[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]
[/code]
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
[code]
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
[/code]
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.
[code]
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
[/code]
It works a [I]little[/I] 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!
[code]
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 [I]only[/I] 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 )
[/code]
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.
Sorry, you need to Log In to post a reply to this thread.