Permanent Entities

How can i add permanent entitites to my map? :o

What gamemode? :c

Cinema :stuck_out_tongue:

Then you will have to spawn them manually in Lua as I believe Cinema isn’t Sandbox derived. :l

So i have to know the coords?

And entity class, and angles, and model, and everything.

Shit… Sounds pretty hard. :smiley:

Use getpos in console to get your position. (Go to the spot you want the entity to be placed at…)
Entity class is the folder name it’s in, so for example “money_printer”.
Entity model can be anything you want it to be.

I want to add a Playable Piano, how can i figure out it’s Entity class and model? :o

Look at the lua file of the playable piano and paste it here

lua\effects\musicnotes\init.lua:


function EFFECT:Init( data )

	local pos = data:GetOrigin()

	local grav = Vector(0, 0, math.random(50, 60))
	local offset = Vector(0,0,10)

	local emitter = ParticleEmitter( pos )

	local particle = emitter:Add( "sprites/music", pos + offset )
	particle:SetVelocity( ( Vector( 0, 0, 1 ) + ( VectorRand() * 0.1 ) ) * math.random( 15, 30 ) )
	particle:SetDieTime( math.random( 0.5, 0.8 ) )
	particle:SetStartAlpha( 255 )
	particle:SetEndAlpha( 0 )
	particle:SetStartSize( 3 )
	particle:SetEndSize( 1.5 )
	particle:SetRoll( math.random(0.5, 10) )
	particle:SetRollDelta( math.Rand(-0.2, 0.2) )
	particle:SetColor( 255, 255, 255 )
	particle:SetCollide( false )

	particle:SetGravity( grav )
	grav = grav + Vector(0, 0, math.random(-10, -5))
	offset = offset + Vector( math.random(1, 5), math.random(.5, 5), math.random(1.5, 6))

	emitter:Finish()

end

function EFFECT:Think( )
	return false
end

function EFFECT:Render( )
end

lua\entities\gmt_instrument_base\cl_init.lua:


include("shared.lua")

ENT.DEBUG = false

ENT.KeysDown = {}
ENT.KeysWasDown = {}

ENT.AllowAdvancedMode = false
ENT.AdvancedMode = false
ENT.ShiftMode = false

ENT.PageTurnSound = Sound( "GModTower/inventory/move_paper.wav" )
surface.CreateFont( "InstrumentKeyLabel", {
	size = 22, weight = 400, antialias = true, font = "Impact"
} )
surface.CreateFont( "InstrumentNotice", {
	size = 30, weight = 400, antialias = true, font = "Impact"
} )

// For drawing purposes
// Override by adding MatWidth/MatHeight to key data
ENT.DefaultMatWidth = 128
ENT.DefaultMatHeight = 128
// Override by adding TextX/TextY to key data
ENT.DefaultTextX = 5
ENT.DefaultTextY = 10
ENT.DefaultTextColor = Color( 150, 150, 150, 255 )
ENT.DefaultTextColorActive = Color( 80, 80, 80, 255 )
ENT.DefaultTextInfoColor = Color( 120, 120, 120, 150 )

ENT.MaterialDir	= ""
ENT.KeyMaterials = {}

ENT.MainHUD = {
	Material = nil,
	X = 0,
	Y = 0,
	TextureWidth = 128,
	TextureHeight = 128,
	Width = 128,
	Height = 128,
}

ENT.AdvMainHUD = {
	Material = nil,
	X = 0,
	Y = 0,
	TextureWidth = 128,
	TextureHeight = 128,
	Width = 128,
	Height = 128,
}

ENT.BrowserHUD = {
	URL = "http://www.gmtower.org/apps/instruments/piano.php",
	Show = true, // display the sheet music?
	X = 0,
	Y = 0,
	Width = 1024,
	Height = 768,
}

function ENT:Initialize()
	self:PrecacheMaterials()
end

function ENT:Think()

	if !IsValid( LocalPlayer().Instrument ) || LocalPlayer().Instrument != self then return end

	if self.DelayKey && self.DelayKey > CurTime() then return end

	// Update last pressed
	for keylast, keyData in pairs( self.KeysDown ) do
		self.KeysWasDown[ keylast ] = self.KeysDown[ keylast ]
	end

	// Get keys
	for key, keyData in pairs( self.Keys ) do

		// Update key status
		self.KeysDown[ key ] = input.IsKeyDown( key )

		// Check for note keys
		if self:IsKeyTriggered( key ) then
		
			if self.ShiftMode && keyData.Shift then
				self:OnRegisteredKeyPlayed( keyData.Shift.Sound )
			elseif !self.ShiftMode then
				self:OnRegisteredKeyPlayed( keyData.Sound )
			end
			
		end

	end

	// Get control keys
	for key, keyData in pairs( self.ControlKeys ) do

		// Update key status
		self.KeysDown[ key ] = input.IsKeyDown( key )

		// Check for control keys
		if self:IsKeyTriggered( key ) then
			keyData( self, true )
		end
		
		// was a control key released?
		if self:IsKeyReleased( key ) then
			keyData( self, false )
		end

	end

	// Send da keys to everyone
	//self:SendKeys()

end

function ENT:IsKeyTriggered( key )
	return self.KeysDown[ key ] && !self.KeysWasDown[ key ]
end

function ENT:IsKeyReleased( key )
	return self.KeysWasDown[ key ] && !self.KeysDown[ key ]
end

function ENT:OnRegisteredKeyPlayed( key )

	// Play on the client first
	local sound = self:GetSound( key )
	self:EmitSound( sound, 100 )

	// Network it
	net.Start( "InstrumentNetwork" )

		net.WriteEntity( self )
		net.WriteInt( INSTNET_PLAY, 3 )
		net.WriteString( key )

	net.SendToServer()

	// Add the notes (limit to max notes)
	/*if #self.KeysToSend < self.MaxKeys then

		if !table.HasValue( self.KeysToSend, key ) then // only different notes, please
			table.insert( self.KeysToSend, key )
		end

	end*/

end

// Network it up, yo
function ENT:SendKeys()

	if !self.KeysToSend then return end

	// Send the queue of notes to everyone

	// Play on the client first
	for _, key in ipairs( self.KeysToSend ) do

		local sound = self:GetSound( key )

		if sound then
			self:EmitSound( sound, 100 )
		end

	end

	// Clear queue
	self.KeysToSend = nil

end

function ENT:DrawKey( mainX, mainY, key, keyData, bShiftMode )

	if keyData.Material then
		if ( self.ShiftMode && bShiftMode && input.IsKeyDown( key ) ) ||
		   ( !self.ShiftMode && !bShiftMode && input.IsKeyDown( key ) ) then

			surface.SetTexture( self.KeyMaterialIDs[ keyData.Material ] )
			surface.DrawTexturedRect( mainX + keyData.X, mainY + keyData.Y, 
									  self.DefaultMatWidth, self.DefaultMatHeight )
		end
		
	end

	// Draw keys
	if keyData.Label then

		local offsetX = self.DefaultTextX
		local offsetY = self.DefaultTextY
		local color = self.DefaultTextColor

		if ( self.ShiftMode && bShiftMode && input.IsKeyDown( key ) ) ||
		   ( !self.ShiftMode && !bShiftMode && input.IsKeyDown( key ) ) then
		   
			color = self.DefaultTextColorActive
			if keyData.AColor then color = keyData.AColor end
		else
			if keyData.Color then color = keyData.Color end
		end

		// Override positions, if needed
		if keyData.TextX then offsetX = keyData.TextX end
		if keyData.TextY then offsetY = keyData.TextY end
		
		draw.DrawText( keyData.Label, "InstrumentKeyLabel", 
						mainX + keyData.X + offsetX,
						mainY + keyData.Y + offsetY,
						color, TEXT_ALIGN_CENTER )
	end
end

function ENT:DrawHUD()

	surface.SetDrawColor( 255, 255, 255, 255 )

	local mainX, mainY, mainWidth, mainHeight

	// Draw main
	if self.MainHUD.Material && !self.AdvancedMode then

		mainX, mainY, mainWidth, mainHeight = self.MainHUD.X, self.MainHUD.Y, self.MainHUD.Width, self.MainHUD.Height

		surface.SetTexture( self.MainHUD.MatID )
		surface.DrawTexturedRect( mainX, mainY, self.MainHUD.TextureWidth, self.MainHUD.TextureHeight )

	end

	// Advanced main
	if self.AdvMainHUD.Material && self.AdvancedMode then

		mainX, mainY, mainWidth, mainHeight = self.AdvMainHUD.X, self.AdvMainHUD.Y, self.AdvMainHUD.Width, self.AdvMainHUD.Height

		surface.SetTexture( self.AdvMainHUD.MatID )
		surface.DrawTexturedRect( mainX, mainY, self.AdvMainHUD.TextureWidth, self.AdvMainHUD.TextureHeight )

	end

	// Draw keys (over top of main)
	for key, keyData in pairs( self.Keys ) do
	
		self:DrawKey( mainX, mainY, key, keyData, false )
		
		if keyData.Shift then
			self:DrawKey( mainX, mainY, key, keyData.Shift, true )
		end
	end

	// Sheet music help
	if !ValidPanel( self.Browser ) && self.BrowserHUD.Show then

		draw.DrawText( "SPACE FOR SHEET MUSIC", "InstrumentKeyLabel", 
						mainX + ( mainWidth / 2 ), mainY + 60, 
						self.DefaultTextInfoColor, TEXT_ALIGN_CENTER )

	end

	// Advanced mode
	if self.AllowAdvancedMode && !self.AdvancedMode then

		draw.DrawText( "CONTROL FOR ADVANCED MODE", "InstrumentKeyLabel", 
						mainX + ( mainWidth / 2 ), mainY + mainHeight + 30, 
						self.DefaultTextInfoColor, TEXT_ALIGN_CENTER )
						
	elseif self.AllowAdvancedMode && self.AdvancedMode then
	
		draw.DrawText( "CONTROL FOR BASIC MODE", "InstrumentKeyLabel", 
						mainX + ( mainWidth / 2 ), mainY + mainHeight + 30, 
						self.DefaultTextInfoColor, TEXT_ALIGN_CENTER )
	end

end

// This is so I don't have to do GetTextureID in the table EACH TIME, ugh
function ENT:PrecacheMaterials()

	if !self.Keys then return end

	self.KeyMaterialIDs = {}

	for name, keyMaterial in pairs( self.KeyMaterials ) do
		if type( keyMaterial ) == "string" then // TODO: what the fuck, this table is randomly created
			self.KeyMaterialIDs[name] = surface.GetTextureID( keyMaterial )
		end
	end

	if self.MainHUD.Material then
		self.MainHUD.MatID = surface.GetTextureID( self.MainHUD.Material )
	end

	if self.AdvMainHUD.Material then
		self.AdvMainHUD.MatID = surface.GetTextureID( self.AdvMainHUD.Material )
	end

end

function ENT:OpenSheetMusic()

	if ValidPanel( self.Browser ) || !self.BrowserHUD.Show then return end

	self.Browser = vgui.Create( "HTML" )
	self.Browser:SetVisible( false )

	local width = self.BrowserHUD.Width

	if self.BrowserHUD.AdvWidth && self.AdvancedMode then
		width = self.BrowserHUD.AdvWidth
	end

	local url = self.BrowserHUD.URL
	
	if self.AdvancedMode then
		url = self.BrowserHUD.URL .. "?&adv=1"
	end
	
	local x = self.BrowserHUD.X - ( width / 2 )

	self.Browser:OpenURL( url )

	// This is delayed because otherwise it won't load at all
	// for some silly reason...
	timer.Simple( .1, function()

		if ValidPanel( self.Browser ) then
			self.Browser:SetVisible( true )
			self.Browser:SetPos( x, self.BrowserHUD.Y )
			self.Browser:SetSize( width, self.BrowserHUD.Height )
		end

	end )

end

function ENT:CloseSheetMusic()

	if !ValidPanel( self.Browser ) then return end

	self.Browser:Remove()
	self.Browser = nil

end

function ENT:ToggleSheetMusic()

	if ValidPanel( self.Browser ) then
		self:CloseSheetMusic()
	else
		self:OpenSheetMusic()
	end

end

function ENT:SheetMusicForward()

	if !ValidPanel( self.Browser ) then return end

	self.Browser:Exec( "pageForward()" )
	self:EmitSound( self.PageTurnSound, 100, math.random( 120, 150 ) )

end

function ENT:SheetMusicBack()

	if !ValidPanel( self.Browser ) then return end

	self.Browser:Exec( "pageBack()" )
	self:EmitSound( self.PageTurnSound, 100, math.random( 100, 120 ) )

end

function ENT:OnRemove()

	self:CloseSheetMusic()

end

function ENT:Shutdown()

	self:CloseSheetMusic()
	
	self.AdvancedMode = false
	self.ShiftMode = false

	if self.OldKeys then
		self.Keys = self.OldKeys
		self.OldKeys = nil
	end

end

function ENT:ToggleAdvancedMode()
	self.AdvancedMode = !self.AdvancedMode
	
	if ValidPanel( self.Browser ) then
		self:CloseSheetMusic()
		self:OpenSheetMusic()
	end
	
end

function ENT:ToggleShiftMode()
	self.ShiftMode = !self.ShiftMode
end

function ENT:ShiftMod() end // Called when they press shift
function ENT:CtrlMod() end // Called when they press cntrl

hook.Add( "HUDPaint", "InstrumentPaint", function()

	if IsValid( LocalPlayer().Instrument ) then

		// HUD
		local inst = LocalPlayer().Instrument
		inst:DrawHUD()

		// Notice bar
		local name = inst.PrintName or "INSTRUMENT"
		name = string.upper( name )

		surface.SetDrawColor( 0, 0, 0, 180 )
		surface.DrawRect( 0, ScrH() - 60, ScrW(), 60 )

		draw.SimpleText( "PRESS TAB TO LEAVE THE " .. name, "InstrumentNotice", ScrW() / 2, ScrH() - 35, Color(255, 255, 255), TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER, 1 )

	end

end )

// Override regular keys
hook.Add( "PlayerBindPress", "InstrumentHook", function( ply, bind, pressed )

	if IsValid( ply.Instrument ) then
		return true
	end

end )

net.Receive( "InstrumentNetwork", function( length, client )

	local ent = net.ReadEntity()
	local enum = net.ReadInt( 3 )

	// When the player uses it or leaves it
	if enum == INSTNET_USE then

		if IsValid( LocalPlayer().Instrument ) then
			LocalPlayer().Instrument:Shutdown()
		end

		ent.DelayKey = CurTime() + .1 // delay to the key a bit so they don't play on use key
		LocalPlayer().Instrument = ent

	// Play the notes for everyone else
	elseif enum == INSTNET_HEAR then

		// Instrument doesn't exist
		if !IsValid( ent ) then return end

		// Don't play for the owner, they've already heard it!
		if IsValid( LocalPlayer().Instrument ) && LocalPlayer().Instrument == ent then
			return
		end

		// Gather note
		local key = net.ReadString()
		local sound = ent:GetSound( key )
			
		if sound then
			ent:EmitSound( sound, 80 )
		end

		// Gather notes
		/*local keys = net.ReadTable()
	
		for i=1, #keys do

			local key = keys[1]
			local sound = ent:GetSound( key )
			
			if sound then
				ent:EmitSound( sound, 80 )

				local eff = EffectData()
				eff:SetOrigin( ent:GetPos() + Vector(0, 0, 60) )
				eff:SetEntity( ent )

				util.Effect( "musicnotes", eff, true, true )
			end
			
		end*/

	end

end )

lua\entities\gmt_instrument_base\init.lua:


AddCSLuaFile( "cl_init.lua" )
AddCSLuaFile( "shared.lua" )
include( "shared.lua" )

util.AddNetworkString( "InstrumentNetwork" )

function ENT:Initialize()

	self:SetModel( self.Model )
	self:PhysicsInit( SOLID_VPHYSICS )
	self:SetMoveType( MOVETYPE_VPHYSICS )
	self:SetSolid( SOLID_VPHYSICS )
	self:SetUseType( SIMPLE_USE )
	self:DrawShadow( true )

	local phys = self:GetPhysicsObject()
	if IsValid( phys ) then
		phys:Wake()
	end

	self:InitializeAfter()
	
end

function ENT:InitializeAfter()
end

local function HandleRollercoasterAnimation( vehicle, player )
	return player:SelectWeightedSequence( ACT_GMOD_SIT_ROLLERCOASTER )
end

function ENT:SetupChair( vecmdl, angmdl, vecvehicle, angvehicle )

	// Chair Model
	self.ChairMDL = ents.Create( "prop_physics_multiplayer" )
	self.ChairMDL:SetModel( self.ChairModel )
	self.ChairMDL:SetParent( self )
	self.ChairMDL:SetPos( self:GetPos() + vecmdl )
	self.ChairMDL:SetAngles( angmdl )
	self.ChairMDL:DrawShadow( false )

	self.ChairMDL:SetCollisionGroup( COLLISION_GROUP_DEBRIS_TRIGGER )
	
	self.ChairMDL:Spawn()
	self.ChairMDL:Activate()
	self.ChairMDL:SetOwner( self )
	
	local phys = self.ChairMDL:GetPhysicsObject()
	if IsValid(phys) then
		phys:EnableMotion(false)
		phys:Sleep()
	end
	
	self.ChairMDL:SetKeyValue( "minhealthdmg", "999999" )
	
	// Chair Vehicle
	self.Chair = ents.Create( "prop_vehicle_prisoner_pod" )
	self.Chair:SetModel( "models/nova/airboat_seat.mdl" )
	self.Chair:SetKeyValue( "vehiclescript","scripts/vehicles/prisoner_pod.txt" )
	self.Chair:SetPos( self.ChairMDL:GetPos() + vecvehicle )
	self.Chair:SetParent( self.ChairMDL )
	self.Chair:SetAngles( angvehicle )
	self.Chair:SetNotSolid( true )
	self.Chair:SetNoDraw( true )
	self.Chair:DrawShadow( false )
	self.Chair:SetCollisionGroup( COLLISION_GROUP_DEBRIS_TRIGGER )

	self.Chair.HandleAnimation = HandleRollercoasterAnimation
	self.Chair:SetOwner( self )

	self.Chair:Spawn()
	self.Chair:Activate()
	
	local phys = self.Chair:GetPhysicsObject()
	if IsValid(phys) then
		phys:EnableMotion(false)
		phys:Sleep()
	end
	
end

local function HookChair( ply, ent )

	local inst = ent:GetOwner()

	if IsValid( inst ) && inst.Base == "gmt_instrument_base" then

		if !IsValid( inst.Owner ) then
			inst:AddOwner( ply )
			return true
		else
			if inst.Owner == ply then
				return true
			end
		end

		return false

	end

	return true

end

// Quick fix for overriding the instrument chair seating
hook.Add( "CanPlayerEnterVehicle", "InstrumentChairHook", HookChair )
hook.Add( "PlayerUse", "InstrumentChairModelHook", HookChair )

function ENT:Use( ply )

	if IsValid( self.Owner ) then return end

	self:AddOwner( ply )

end

function ENT:AddOwner( ply )

	if IsValid( self.Owner ) then return end

	net.Start( "InstrumentNetwork" )
		net.WriteEntity( self )
		net.WriteInt( INSTNET_USE, 4 )
	net.Send( ply )

	ply.EntryPoint = ply:GetPos()
	ply.EntryAngles = ply:EyeAngles()

	self.Owner = ply

	ply:EnterVehicle( self.Chair )

	self.Owner:SetEyeAngles( Angle( 25, 90, 0 ) )

end

function ENT:RemoveOwner()

	if !IsValid( self.Owner ) then return end

	net.Start( "InstrumentNetwork" )
		net.WriteEntity( nil )
		net.WriteInt( INSTNET_USE, 3 )
	net.Send( self.Owner )
		
	self.Owner:ExitVehicle( self.Chair )

	self.Owner:SetPos( self.Owner.EntryPoint )
	self.Owner:SetEyeAngles( self.Owner.EntryAngles )

	self.Owner = nil

end

/*function ENT:NetworkKeys( keys )

	if !IsValid( self.Owner ) then return end // no reason to broadcast it

	net.Start( "InstrumentNetwork" )

		net.WriteEntity( self )
		net.WriteInt( INSTNET_HEAR, 3 )
		net.WriteTable( keys )

	net.Broadcast()

end*/

function ENT:NetworkKey( key )

	if !IsValid( self.Owner ) then return end // no reason to broadcast it

	net.Start( "InstrumentNetwork" )

		net.WriteEntity( self )
		net.WriteInt( INSTNET_HEAR, 3 )
		net.WriteString( key )

	net.Broadcast()

end

// Returns the approximate "fitted" number based on linear regression.
function math.Fit( val, valMin, valMax, outMin, outMax )
	return ( val - valMin ) * ( outMax - outMin ) / ( valMax - valMin ) + outMin
end	

net.Receive( "InstrumentNetwork", function( length, client )

	local ent = net.ReadEntity()
	if !IsValid( ent ) then return end

	local enum = net.ReadInt( 3 )

	// When the player plays notes
	if enum == INSTNET_PLAY then

		// Filter out non-instruments
		if ent.Base != "gmt_instrument_base" then return end

		// This instrument doesn't have an owner...
		if !IsValid( ent.Owner ) then return end

		// Check if the player is actually the owner of the instrument
		if client == ent.Owner then

			// Gather note
			local key = net.ReadString()
		
			// Send it!!
			ent:NetworkKey( key )

			// Offset the note effect
			local pos = string.sub( key, 2, 3 )
			pos = math.Fit( tonumber( pos ), 1, 36, -3.8, 4 )

			// Note effect
			local eff = EffectData()
				eff:SetOrigin( client:GetPos() + Vector( -15, pos * 10, -5 ) )
			util.Effect( "musicnotes", eff, true, true )

			// Gather notes
			/*local keys = net.ReadTable()
		
			// Send them!!
			ent:NetworkKeys( keys )*/

		end

	end

end )

concommand.Add( "instrument_leave", function( ply, cmd, args )

	if #args < 1 then return end // no ent id

	// Get the instrument
	local entid = args[1]
	local ent = ents.GetByIndex( entid )

	// Filter out non-instruments
	if !IsValid( ent ) || ent.Base != "gmt_instrument_base" then return end

	// This instrument doesn't have an owner...
	if !IsValid( ent.Owner ) then return end

	// Leave instrument
	if ply == ent.Owner then
		ent:RemoveOwner()
	end

end )

lua\entities\gmt_instrument_base\shared.lua:


ENT.Base		= "base_anim"
ENT.Type		= "anim"
ENT.PrintName	= "Instrument Base"

ENT.Model		= Model( "models/fishy/furniture/piano.mdl" )
ENT.ChairModel	= Model( "models/fishy/furniture/piano_seat.mdl" )
ENT.MaxKeys		= 4 // how many keys can be played at once

ENT.SoundDir	= "GModTower/lobby/piano/note_"
ENT.SoundExt 	= ".wav"

INSTNET_USE		= 1
INSTNET_HEAR	= 2
INSTNET_PLAY	= 3

//ENT.Keys = {}
ENT.ControlKeys = { 
	[KEY_TAB] =	function( inst, bPressed )
		if ( !bPressed ) then return end
		RunConsoleCommand( "instrument_leave", inst:EntIndex() )
	end,
				
	[KEY_SPACE] = function( inst, bPressed ) 
		if ( !bPressed ) then return end
		inst:ToggleSheetMusic()
	end,
	
	[KEY_LEFT] = function( inst, bPressed )
		if ( !bPressed ) then return end
		inst:SheetMusicBack()
	end,
	[KEY_RIGHT] = function( inst, bPressed )
		if ( !bPressed ) then return end
		inst:SheetMusicForward()
	end,
	
	[KEY_LCONTROL] = function( inst, bPressed )
		if ( !bPressed ) then return end
		inst:CtrlMod() 
	end,
	[KEY_RCONTROL] = function( inst, bPressed )
		if ( !bPressed ) then return end
		inst:CtrlMod() 
	end,
	
	[KEY_LSHIFT] = function( inst, bPressed )
		inst:ShiftMod()
	end,
}

function ENT:GetSound( snd )

	if ( snd == nil || snd == "" ) then
		return nil
	end
	
	return self.SoundDir .. snd .. self.SoundExt
end

if SERVER then
	function ENT:Intiailize()
		self:PrecacheSounds()
	end

	function ENT:PrecacheSounds()

		if !self.Keys then return end

		for _, keyData in pairs( self.Keys ) do
			util.PrecacheSound( self:GetSound( keyData.Sound ) )
		end

	end
end

hook.Add( "PhysgunPickup", "NoPickupInsturmentChair", function( ply, ent )

	local inst = ent:GetOwner()

	if IsValid( inst ) && inst.Base == "gmt_instrument_base" then
		return false
	end

end )

Continues…

[editline]6th June 2014[/editline]

lua\entities\gmt_instrument_piano\cl_init.lua


include("shared.lua")

ENT.AllowAdvancedMode = true

// For drawing purposes
// Override by adding MatWidth/MatHeight to key data
ENT.DefaultMatWidth = 32
ENT.DefaultMatHeight = 128
// Override by adding TextX/TextY to key data
ENT.DefaultTextX = 11
ENT.DefaultTextY = 100
ENT.DefaultTextColor = Color( 150, 150, 150, 150 )
ENT.DefaultTextColorActive = Color( 80, 80, 80, 150 )
ENT.DefaultTextInfoColor = Color( 46, 20, 6, 255 )

ENT.MaterialDir	= "gmod_tower/instruments/piano/piano_note_"

ENT.KeyMaterials = {
	["left"] = ENT.MaterialDir .. "left",
	["leftmid"] = ENT.MaterialDir .. "leftmid",
	["right"] = ENT.MaterialDir .. "right",
	["rightmid"] = ENT.MaterialDir .. "rightmid",
	["middle"] = ENT.MaterialDir .. "middle",
	["top"] = ENT.MaterialDir .. "top",
	["full"] = ENT.MaterialDir .. "full",
}

ENT.MainHUD = {
	Material = "gmod_tower/instruments/piano/piano",
	X = ( ScrW() / 2 ) - ( 313 / 2 ),
	Y = ScrH() - 316,
	TextureWidth = 512,
	TextureHeight = 256,
	Width = 313,
	Height = 195,
}

ENT.AdvMainHUD = {
	Material = "gmod_tower/instruments/piano/piano_large",
	X = ( ScrW() / 2 ) - ( 940 / 2 ),
	Y = ScrH() - 316,
	TextureWidth = 1024,
	TextureHeight = 256,
	Width = 940,
	Height = 195,
}

ENT.BrowserHUD = {
	URL = "http://www.gmtower.org/apps/instruments/piano.php?",
	Show = true, // display the sheet music?
	X = ( ScrW() / 2 ),
	Y = ENT.MainHUD.Y - 190,
	Width = 450,
	Height = 250,
	AdvWidth = 600,
}

function ENT:CtrlMod()

	self:ToggleAdvancedMode()

	if self.OldKeys then
		self.Keys = self.OldKeys
		self.OldKeys = nil
	else
		self.OldKeys = self.Keys
		self.Keys = self.AdvancedKeys
	end

end

function ENT:ShiftMod()
	self:ToggleShiftMode()
end

lua\entities\gmt_instrument_piano\init.lua


AddCSLuaFile( "cl_init.lua" )
AddCSLuaFile( "shared.lua" )
include( "shared.lua" )

function ENT:InitializeAfter()

	self:SetupChair( 
		Vector( 75, 0, 0 ), Angle( 0, 0, 0 ), // chair model
		Vector( 0, 10, 24 ), Angle( 0, 90, 0 ) // actual chair
	)

end

function ENT:SpawnFunction( ply, tr )

    if !tr.Hit then return end

    local SpawnPos = tr.HitPos + tr.HitNormal * 16
    local ent = ents.Create( self.ClassName )
    ent:SetPos( SpawnPos + Vector( 0, 0, 4 ) )
    ent:Spawn()
    ent:Activate()

    return ent

end

lua\entities\gmt_instrument_piano\shared.lua


ENT.Base			= "gmt_instrument_base"
ENT.Type			= "anim"
ENT.PrintName		= "Playable Piano"
ENT.Author			= "MacDGuy"
ENT.Contact			= "http://www.gmtower.org"
ENT.Purpose			= "A fully playable piano!"
ENT.Category		= "Fun + Games"
ENT.Spawnable		= true
ENT.AdminSpawnable 	= true

ENT.Model		= Model( "models/fishy/furniture/piano.mdl" )
ENT.SoundDir	= "GModTower/lobby/instruments/piano/"

local darker = Color( 100, 100, 100, 150 )
ENT.Keys = {
	[KEY_A] = { Sound = "a15", Material = "left", Label = "A", X = 19, Y = 86 },
	[KEY_S] = { Sound = "a16", Material = "middle", Label = "S", X = 44, Y = 86 },
	[KEY_D] = { Sound = "a17", Material = "right", Label = "D", X = 68, Y = 86 },
	[KEY_F] = { Sound = "a18", Material = "left", Label = "F", X = 94, Y = 86 },
	[KEY_G] = { Sound = "a19", Material = "leftmid", Label = "G", X = 119, Y = 86 },
	[KEY_H] = { Sound = "a20", Material = "rightmid", Label = "H", X = 144, Y = 86 },
	[KEY_J] = { Sound = "a21", Material = "right", Label = "J", X = 169, Y = 86 },
	[KEY_K] = { Sound = "a22", Material = "left", Label = "K", X = 194, Y = 86 },
	[KEY_L] = { Sound = "a23", Material = "middle", Label = "L", X = 219, Y = 86 },
	[KEY_SEMICOLON] = { Sound = "a24", Material = "right", Label = ":", X = 244, Y = 86 },
	[KEY_APOSTROPHE] = { Sound = "a25", Material = "full", Label = "'", X = 269, Y = 86 },

	[KEY_W] = { Sound = "b11", Material = "top",  Label = "W", X = 33, Y = 31, TextX = 7, TextY = 90, Color = darker },
	[KEY_E] = { Sound = "b12", Material = "top", Label = "E", X = 64, Y = 31, TextX = 7, TextY = 90, Color = darker },
	[KEY_T] = { Sound = "b13", Material = "top", Label = "T", X = 108, Y = 31, TextX = 7, TextY = 90, Color = darker },
 	[KEY_Y] = { Sound = "b14", Material = "top", Label = "Y", X = 136, Y = 31, TextX = 7, TextY = 90, Color = darker },
 	[KEY_U] = { Sound = "b15", Material = "top", Label = "U", X = 164, Y = 31, TextX = 7, TextY = 90, Color = darker },
	[KEY_O] = { Sound = "b16",	Material = "top", Label = "O", X = 208, Y = 31, TextX = 7, TextY = 90, Color = darker },
	[KEY_P] = { Sound = "b17", Material = "top", Label = "P", X = 239, Y = 31, TextX = 7, TextY = 90, Color = darker },
}

ENT.AdvancedKeys = {
	[KEY_1] =
	{
		Sound = "a1", Material = "left", Label = "1", X = 19, Y = 86,
		Shift = { Sound = "b1", Material = "top", Label = "!", X = 33, Y = 31, TextX = 7, TextY = 90, Color = darker },
	},
	[KEY_2] =
	{
		Sound = "a2", Material = "middle", Label = "2", X = 44, Y = 86,
		Shift = { Sound = "b2", Material = "top", Label = "@", X = 64, Y = 31, TextX = 7, TextY = 90, Color = darker },
	},
	[KEY_3] = { Sound = "a3", Material = "right", Label = "3", X = 69, Y = 86 },
	[KEY_4] =
	{
		Sound = "a4", Material = "left", Label = "4", X = 94, Y = 86,
		Shift = { Sound = "b3", Material = "top", Label = "$", X = 108, Y = 31, TextX = 7, TextY = 90, Color = darker },
	},
	[KEY_5] =
	{
		Sound = "a5", Material = "leftmid", Label = "5", X = 119, Y = 86,
		Shift = { Sound = "b4", Material = "top", Label = "%", X = 136, Y = 31, TextX = 7, TextY = 90, Color = darker },
	},
	[KEY_6] =
	{
		Sound = "a6", Material = "rightmid", Label = "6", X = 144, Y = 86,
		Shift = { Sound = "b5", Material = "top", Label = "^", X = 164, Y = 31, TextX = 7, TextY = 90, Color = darker },
	},
	[KEY_7] = { Sound = "a7", Material = "right", Label = "7", X = 169, Y = 86 },
	[KEY_8] =
	{
		Sound = "a8", Material = "left", Label = "8", X = 194, Y = 86,
		Shift = { Sound = "b6", Material = "top", Label = "*", X = 208, Y = 31, TextX = 7, TextY = 90, Color = darker },
	},
	[KEY_9] =
	{
		Sound = "a9", Material = "middle", Label = "9", X = 219, Y = 86,
		Shift = { Sound = "b7", Material = "top", Label = "(", X = 239, Y = 31, TextX = 7, TextY = 90, Color = darker },
	},
	[KEY_0] = { Sound = "a10", Material = "right", Label = "0", X = 244, Y = 86 },
	[KEY_Q] =
	{
		Sound = "a11", Material = "left", Label = "q", X = 269, Y = 86,
		Shift = { Sound = "b8", Material = "top", Label = "Q", X = 283, Y = 31, TextX = 7, TextY = 90, Color = darker },
	},
	[KEY_W] =
	{
		Sound = "a12", Material = "leftmid", Label = "w", X = 294, Y = 86,
		Shift = { Sound = "b9", Material = "top", Label = "W", X = 310, Y = 31, TextX = 7, TextY = 90, Color = darker },
	}, // 310
	[KEY_E] =
	{
		Sound = "a13", Material = "rightmid", Label = "e", X = 319, Y = 86,
		Shift = { Sound = "b10", Material = "top", Label = "E", X = 339, Y = 31, TextX = 7, TextY = 90, Color = darker },
	}, // 339
	[KEY_R] = { Sound = "a14", Material = "right", Label = "r", X = 344, Y = 86 },
	[KEY_T] =
	{
		Sound = "a15", Material = "left", Label = "t", X = 369, Y = 86,
		Shift = { Sound = "b11", Material = "top", Label = "T", X = 383, Y = 31, TextX = 7, TextY = 90, Color = darker },
	}, // 383
	[KEY_Y] =
	{
		Sound = "a16", Material = "middle", Label = "y", X = 394, Y = 86,
		Shift = { Sound = "b12", Material = "top", Label = "Y", X = 414, Y = 31, TextX = 7, TextY = 90, Color = darker },
	}, // 415
	[KEY_U] = { Sound = "a17", Material = "right", Label = "u", X = 419, Y = 86 },
	[KEY_I] =
	{
		Sound = "a18", Material = "left", Label = "i", X = 444, Y = 86,
		Shift = { Sound = "b13", Material = "top", Label = "I", X = 458, Y = 31, TextX = 7, TextY = 90, Color = darker },
	}, // 459
	[KEY_O] =
	{
		Sound = "a19", Material = "leftmid", Label = "o", X = 469, Y = 86,
		Shift = { Sound = "b14", Material = "top", Label = "O", X = 486, Y = 31, TextX = 7, TextY = 90, Color = darker },
	}, // 486
	[KEY_P] =
	{
		Sound = "a20", Material = "rightmid", Label = "p", X = 494, Y = 86,
		Shift = { Sound = "b15", Material = "top", Label = "P", X = 514, Y = 31, TextX = 7, TextY = 90, Color = darker },
	}, // 515
	[KEY_A] = { Sound = "a21", Material = "right", Label = "a", X = 519, Y = 86 },
	[KEY_S] =
	{
		Sound = "a22", Material = "left", Label = "s", X = 544, Y = 86,
		Shift = { Sound = "b16", Material = "top", Label = "S", X = 558, Y = 31, TextX = 7, TextY = 90, Color = darker },
	}, // 559
	[KEY_D] =
	{
		Sound = "a23", Material = "middle", Label = "d", X = 569, Y = 86,
		Shift = { Sound = "b17", Material = "top", Label = "D", X = 590, Y = 31, TextX = 7, TextY = 90, Color = darker },
	}, // 590
	[KEY_F] = { Sound = "a24", Material = "right", Label = "f", X = 594, Y = 86 },
	[KEY_G] =
	{
		Sound = "a25", Material = "left", Label = "g", X = 619, Y = 86,
		Shift = { Sound = "b18", Material = "top", Label = "G", X = 633, Y = 31, TextX = 7, TextY = 90, Color = darker },
	}, // 633
	[KEY_H] =
	{
		Sound = "a26", Material = "leftmid", Label = "h", X = 644, Y = 86,
		Shift = { Sound = "b19", Material = "top", Label = "H", X = 661, Y = 31, TextX = 7, TextY = 90, Color = darker },
	}, // 661
	[KEY_J] =
	{
		Sound = "a27", Material = "rightmid", Label = "j", X = 669, Y = 86,
		Shift = { Sound = "b20", Material = "top", Label = "J", X = 690, Y = 31, TextX = 7, TextY = 90, Color = darker },
	}, // 690
	[KEY_K] = { Sound = "a28", Material = "right", Label = "k", X = 694, Y = 86 },
	[KEY_L] =
	{
		Sound = "a29", Material = "left", Label = "l", X = 719, Y = 86,
		Shift = { Sound = "b21", Material = "top", Label = "L", X = 734, Y = 31, TextX = 7, TextY = 90, Color = darker },
	}, // 734
	[KEY_Z] =
	{
		Sound = "a30", Material = "middle", Label = "z", X = 744, Y = 86,
		Shift = { Sound = "b22", Material = "top", Label = "Z", X = 765, Y = 31, TextX = 7, TextY = 90, Color = darker },
	}, // 765
	[KEY_X] = { Sound = "a31", Material = "right", Label = "x", X = 769, Y = 86 },
	[KEY_C] =
	{
		Sound = "a32", Material = "left", Label = "c", X = 794, Y = 86,
		Shift = { Sound = "b23", Material = "top", Label = "C", X = 809, Y = 31, TextX = 7, TextY = 90, Color = darker },
	}, // 809
	[KEY_V] =
	{
		Sound = "a33", Material = "leftmid", Label = "v", X = 819, Y = 86,
		Shift = { Sound = "b24", Material = "top", Label = "V", X = 837, Y = 31, TextX = 7, TextY = 90, Color = darker },
	}, // 837
	[KEY_B] =
	{
		Sound = "a34", Material = "rightmid", Label = "b", X = 844, Y = 86,
		Shift = { Sound = "b25", Material = "top", Label = "B", X = 865, Y = 31, TextX = 7, TextY = 90, Color = darker },
	}, // 865
	[KEY_N] = { Sound = "a35", Material = "right", Label = "n", X = 869, Y = 86 },
	[KEY_M] = { Sound = "a36", Material = "full", Label = "m", X = 894, Y = 86 },
}

Only post file paths, not their contents.

But

D:

Anyway, what you are looking for is: “gmt_instrument_piano” for entity class. You can forget about SetModel part too, since it is set by the entity itself.

Create the entity, set pos and angles, :Spawn() it and you should be done. And you gotta do all of this from serverside file, using InitPostEntity hook.

Im really new to hooks etc. So can you tell more specifically