Simple Gamemode
From GMod Wiki
Lua: Basic Gamemode with Basic Functions |
Description: | Simple Gamemode, intended to help people learn Lua and how to make a Gamemode. |
Original Author: | Darkcha0s |
Created: | 11th April, 2009 |
Contents |
The beginning
Foreword
If you're like me, you're probably a lonely straggler, who feels he will never be able to shoot for the Moon. (Get the reference to Lua? Lua = Moon) Anyways, I know I'm not funny, but I do hope I can help you learn a little bit today. I'll start this as the first part in my little tutorial series. I found for myself, the easiest thing for me, to learn Lua, was to start difficult, and once you master it, everything else becomes clear. You might not be this type of learner, but this tutorial will help you anyways, since it teaches basic concepts on how to create a Gamemode you can call your own.
Let me start at this point, by saying we aren't creating another TacoScript, Cakescript or anything close to those. With this base it would be fairly difficult to derive it all that way. Don't get your hopes too high, you are infact probably still a beginner, but we all once were.
Without further blabbering, let's begin.
Starting your Gamemode
Before we jump right in let's establish what every Gamemode needs to have.
- A clear goal. Right now, ours is creating a Gamemode which works and does basic things
- The proper folder hierarchy. It should go like this :
Program Files/steam/steamapps/%username%/garrysmod/garrysmod/gamemodes/Your Gamemode
One thing that I usually push towards a clean work environment. I choose to use Notepad++, but this is always up to you. Anyways, let's start by going in the Gamemodes folder and creating our gamemode folder. I named ours SimpleBuild. Within this folder create another folder entitled gamemode. This is where your actual gamemode will be inside. Here is what it should look like:
Assuming that's done let's open our Notepad++. I went ahead and opened three tabs, one for:
- init.lua
- cl_init.lua
- shared.lua
You're probably asking yourself what those are?
Well simply put, init.lua is responsible for the server side of things, cl_init.lua for the clientside of things and shared.lua for, you guessed it, both.
What appears to be complex and totally confusing, actually is quite easy to grasp. I'll come back to that later though.
Lame I know, now let's start with some code.
I'll handle it this way, first I start with what both need, then server, then client.
Now the ball's rolling
I'll repeat myself saying this file contains the code which is used both client and server side. For our gamemode this file will actually be rather short, however it will contain vital information needed.
GM.Name = "SimpleBuild" //What is your Gamemode's name? GM.Author = "Darkcha0s" // Who authored it? GM.Email = "[email protected]" //Your email? GM.Website = "thec0re.net" //Website, only if you feel it
So far so good, the green things behind the // are comments. These are not read by Garry's Mod and have no effect on your code. Assuming I commented that well, you should see how basic some Lua code really is.
Next is a crucial step for any simple gamemode. Deriving. While this deriving is not as complicated as the one you do in calculus, it's just as needed. Without deriving you're missing most basic functions which are found in your sandbox gamemode, kindly premade by Garry himself. For this we do:
DeriveGamemode( "sandbox" )
Again that was not at all hard.
Now we have the little how I call it, header, finished. From now on this file only contains repetitive code. What this code is? Oh, probably the most important part. The teams. You want to differentiate different players by class, this is capitalism not god damn socialism. This far not as complicated or as fucked up as any economy, much more it's easy and overviewable. I'll go ahead and give you the code here:
team.SetUp( 1, "Guest", Color( 125, 125, 125, 255 ) ) //Team guest team.SetUp( 2, "Admin", Color( 255, 255, 255, 255 ) ) //Team admin team.SetUp( 3, "Darkcha0s", Color( 148, 0, 211, 255 ) ) //It's not me! team.SetUp( 4, "Joining", Color( 0, 0 , 0, 255 ) ) //Joining
You can grasp the basics.
- team.SetUp means we are setting up a new team here.
- 1 or 2 or 3 or 4 in this case is the team's ID. This is for code purposes so you can easily assign this team to players.
- The text in quotes ("") is simply that team's identifier. Call them what you want.
- The color that this team has, this affects chat colors and scoreboard, depending on how it's setup.
- The color goes by this format. ( Red, Green, Blue, Alpha)
- Alpha is how 'see through' it is. 255 means you can't, 0 means you can.
- The color goes by this format. ( Red, Green, Blue, Alpha)
That would be it for shared.lua!
Init.lua
Welcome to the beast. This code in most gamemodes will probably be the longest and most complicated, and will provide the most headaches. But hey, not for us, we know how to do this!
First off, we are telling the client what files this gamemode has which need to be downloaded, a.k.a every file of your gamemode. Again this is quite simple code.
AddCSLuaFile( "cl_init.lua" ) //dl that AddCSLuaFile( "shared.lua" ) //dl that too AddCSLuaFile( "specialchars.lua" ) //and this
Now the client has the file, but it's just sitting in his /lua folder. Quite useless I'd say. For init.lua we don't need to include cl_init.lua, because the server side can't execute clientside code, making useless. What does include mean? It means include that lua file in this lua file so we can use it's functions in this file. I hope that's not too complicated, re-read it a couple of times to understand it perfectly. That being said, let's include.
include( 'shared.lua' ) //load that include( 'specialchars.lua' ) //This too.
Now we have all those functions from those files, (I'll get to specialchars.lua later) in this file right here. We can call on them et cetera.
Like with Shared.lua we are now done with what I call the header, the stuff that is just given code that doesn't have any direct functions for your gamemode.
First off, Garry's Sandbox gamemode is not perfect. There is a well known error in it, which will give you an error around line 40. I don't recall what it was, but we don't need to know that, because guess what- I have a workaround. Without much explanation, put this in the code now.
-- What happens when a player spawns function GM:PlayerSpawn( ply ) self.BaseClass:PlayerSpawn( ply ) ply:SetGravity ( 0.75 ) -- Although I suggest 1, becuase .75 is just too low. ply:SetMaxHealth( 100, true ) ply:SetWalkSpeed( 325 ) ply:SetRunSpeed ( 325 ) end
For the record, GM:PlayerSpawn( ply ) means this happens every time the player spawns. Be it the first spawn, the spawn after he dies, or his last spawn. The code inside it simply sets his gravity, max health, et cetera, you can read it out of there.
Assuming so far you aren't all too lost lets begin with the first complex piece of code so far. Our GM:PlayerInitialSpawn. What this does? Well simply put, it's code that's executed the first time he spawns on joining the server. It will only execute that one time, and not again, unless you call on it again.
Here it is:
function GM:PlayerInitialSpawn( ply ) // When spawning after joining the sever CheckSpecialCharacters( ply ) //Is he defined in the specialchars.lua file? If yes, then he gets those values, and it stops here. if ply:IsAdmin() then //Is the connecting player admin? sb_team2( ply ) //If he is then set his team to team 2. else // If he isn't admin then, joining( ply ) //Call the function joining (found near the bottom of this file) RunConsoleCommand( "sb_start" ) //Run the console command defined in cl_init.lua. end //Close the if end //close the function
The comments say it pretty well, but here it is again.
- So we open our function- Remember that, functions are the most useful thing in lua.
- So then it runs the function CheckSpecialCharacters( ply )- What that does I'll come to later, it's quite simple really.
- Then we start a conditional.
- if the ply( which is player. Everytime you see it, it means player. REMEMBER THAT. (Garry uses pl, that works too.))IsAdmin() then, it's almost like english, isn't it? If the player is an admin then, we execute the following.
sb_team2( ply ). This runs the function which is a bit lower in this file, I'll come back to it.
- Assuming he can't be anything else for the time being, we don't need elseif, we since we handled all possible scenarios, hence we use else. We call the function joining( ply ), which is another function lower in this file.
- So now we ran that code, now we run the consolecommand sb_start. This command was added to the pool of console commands in the cl_init.lua file. Using console commands for this is quite basic, most people say use usermessages, but there's not point in explaining it, seeing as people won't understand it right away.
- end. We end the conditional we had opened.
- end. We close the GM:InitialPlayerSpawn function. Simple really?
Now we have the top level function complete. Let's get into what happens behind the scene.
Firstly, the player needs a loadout, I hope you would agree. Unless you want him to run around with nothing, spawning random props, you will follow what I do next. We use the function GM:PlayerLoadout to give the player his load out.
function GM:PlayerLoadout( ply ) // What should the player recieve when joining a team? if ply:Team() == 1 then //If he is team 1, then give him the following items ply:Give( "weapon_physcannon" ) // A Gravity gun ply:Give( "weapon_physgun" ) // A Physics gun ply:Give( "gmod_tool" ) // and don't forget the tool gun! elseif ply:Team() == 2 then // So if he isn't team 1, he could be team 2? ply:Give( "weapon_physcannon" ) //Assuming he is, then give him Gravity gun ply:Give( "weapon_physgun" ) // Physics gun ply:Give( "weapon_ar2" ) // AR2 ply:Give( "gmod_tool" ) // and the gmod tool //I should mention at this point you can put in else, but there is no point. All possible scenarios are covered. Thus we end it end //right here. end // End the function
I'll do it step by step again.
- if the players team is team 1, then give him the following. ply:Give is used to give the player items, you should get the names from the comments behind it.
- Elseif, meaning if he isn't team 1 and is team 2, then give him the following.
- I could have used else, but I chose not to, simply to show this alternative. Assuming he was team 3, he wouldn't any conditions, meaning he'd have no load out. How I avoid this in my code you find out later. (Remember we have 4 teams).
Alright, that was quite easy to grasp, wasn't it? I hope it was. Now we are basically in the clear. Just 3 more functions.
The first one is that we have a function, as to what happens when the player is set to join team 1. This function name is what ever you wish. Easy isn't it?
function sb_team1( ply ) //This is what happens when we enter sb_team1 into the console. ply:UnSpectate() //Since he was set to spectate until he presses the 'hell yeah' button, we now unspecatate him ply:SetTeam( 1 ) //We set his team to one, a.k.a 'guest' ply:Spawn() //Spawn the player ply:PrintMessage( HUD_PRINTTALK, "[SimpleBuild]Welcome to the server, " .. ply:Nick() ) //Gives the message [SimpleBuild]Welcome to the server, (playername here)" in the talk area. end //End the function.
Here is what we did;
- Open the function
- Unspectate the player, if you look at the last function in a minute, you'll see why I unspectate at this point.
- We set his team to team 1. Remember, defined in the shared.lua
- We spawn the player. We don't want him floating around doing nothing.
- Now we print a server message. ply:PrintMessage will tell the player something, depending on where you define, as by me here in the talk area. The comment behind it tells what it says.
- And we end the function.
Now, team 1 is handled. Team 2 is now up, which is the admin team if you remember out InitialSpawn function.
Here is that code;
function sb_team2( ply ) // Function sb_team2. Called at the beginning // Why no unspectate? Look carefully at the GM:PlayerSpawn; We only call to spectate after we see if he's an admin. Assuming he's always ready to build, I chose to skip it. ply:SetTeam( 2 ) //Set his team to team 2. ply:Spawn() // Spawn him ply:PrintMessage( HUD_PRINTTALK, "[SimpleBuild]I recognize you as an admin, " .. ply:Nick() ) //Again, a message in the talk area. //This time saying "[SimpleBuild] I recognize you as an admin (playername here)" end //End this function
- We open the function again, remember this was called at the top of init.lua.
- This time we don't unspectate, since that code was called AFTER we check for this function.
- We set his team to the admin team.
- We spawn him again.
- We print the talkarea message again, as seen in the comment behind it.
Now we add a console command. This is very important otherwise the players can't play. PUT THIS CODE IN, IT'S CRUCIAL.
concommand.Add( "sb_team1", sb_team1 ) //Now, we make sure that when we enter sb_team1 into console that it calls the function. This is KEY. Otherwise players won't be able to play.
We add the console command sb_team1 which runs the function sb_team1. We call on this console command in cl_init.lua. Console commands are the easiest way for these files in to interact with each other.
Finally, the last function.
function joining( ply ) // The function that's called when the player is not admin or a special character, at the top. ply:Spectate( 5 ) //Set him to spectate in free-roam mode. He doesn't actually fly around, since he has a window open at this point. ply:SetTeam( 4 ) //Set his team to Joining end //End the function
This was called in the intialspawn. If it's a simple player, he first runs this function, which;
- Spectates him, using freeroam mode.
- Sets his team to team 4, which is the joining team.
Remember how in the sb_team1 function I unspectate him? I specatated him here, so I must undo it there.
We are done with init.lua! What a beast!
cl_init.lua
This is no different then the others, we start with the 'header'. This time we only include shared, since it doesn't call on any other functions.
include( 'shared.lua' ) //LOAD THAT
Simple enough.
Now to this part. What I chose to do, is if the player is not admin or a special character, he has to press a button, saying he is ready to build. Why I do this? For fun. This uses vgui, a concept only present on the client side, hence it's in the cl_init. It's one function, but quite a long one, so bare with me.
function set_team() //Function for the window when joining as neither special character nor Admin Ready = vgui.Create( "DFrame" ) //Define ready as a "DFrame" Ready:SetPos( ScrW() / 2, ScrH() / 2 ) //Set the position. Half the screen height and half the screen width. This will result in being bottom right of the middle of the screen. Ready:SetSize( 175, 75 ) //The size, in pixels of the Frame Ready:SetTitle( "Are you ready to build?" ) //The title; It's at the top. Ready:SetVisible( true ) // Should it be seen? Ready:SetDraggable( false ) // Can people drag it around? Ready:ShowCloseButton( false ) //Show the little X top right? I chose no, because I have no alternative, meaning people would roam around with no weapons Ready:MakePopup() //Make it popup. Of course.
Basically, we open a function, called set_team, then define Variable Ready as a DFrame, a simple frame. Assuming you can read (I do hope so), you can grasp code beneath it. We simply define some things for the frame, such as size, location, title, et cetera.
Notice it's not closed yet. Because it's not done! Now we add a button.
ready1 = vgui.Create( "DButton", Ready ) // Define ready1 as a "DButton" with its parent the Ready frame we just created above. ready1:SetPos( 20, 25 ) //Set position, relative to the frame (If you didn't parent it, it would be relative to the screen ready1:SetSize( 140, 40 ) // How big it should be, again in pixels ready1:SetText( "Hell yeah!" ) //What should the button say? ready1.DoClick = function() //ready1.doclick = function, we just defined it as a function RunConsoleCommand( "sb_team1" ) //When it clicks, which function does it run? sb_team1, which is defined in init.lua end // end the doclick function end // end the set_team function
So now ready1 is defined as a DButton. Notice behind DButton is says Ready? It's because it's parent is the ready frame, making everything defined relative to it. Again it's defined, but now notice one difference- DoClick. This basically says, when it's click run a function. When it's clicked, it runs the console command, sb_team1, which we defined in init.lua- Using RunConsoleCommand.
Remember how we ran at GM:IntialSpawn Set_Team? Well that's this function, hence we must add it as a console command.
concommand.Add( "sb_start", set_team ) //Now we add a console command for the function we just created. It can be run straight from the console. If you look under the first couple of lines under init.lua, we // said that it should run this command!
Then we end the button and the function.
That's it for cl_init!
The basic Gamemode runs
What's missing
As promised, you now have a fully working gamemode. But what would a gamemode be without you abusing your status 24/7? Exactly, really lame for you. So let's make a file which allows us to add our own characters! :-)
Specialchars.lua
If you recall GM:IntitialSpawn, you will remember I called on the function checkspecial characters. Here is where I define it. The code is straight forward, and well commented.
function CheckSpecialCharacters( ply ) //This function is called upon on GM:PlayerInitialSpawn //Here we add our characters which are special. //I left an example with what I have --Darkcha0s if ( ply:SteamID() == "STEAM_0:1:7880281" ) then //If steamid is that, then execute the following ply:PrintMessage( HUD_PRINTTALK, "Welcome back, " .. ply:Nick() .. "\nYou connected under the IP: " .. ply:IPAddress() ) // Gives the message //Welcome back Darkcha0s, you have connected under the IP: blah. In local multiplayer it will be loopback. ply:SetTeam( 3 ) //Set it to this team, look in shared.lua for this one. // He should recieve the following weapons ply:Give( "weapon_crowbar" ) ply:Give( "weapon_pistol" ) ply:Give( "weapon_smg1" ) ply:Give( "weapon_frag" ) ply:Give( "weapon_physcannon" ) ply:Give( "weapon_crossbow" ) ply:Give( "weapon_shotgun" ) ply:Give( "weapon_357" ) ply:Give( "weapon_rpg" ) ply:Give( "weapon_ar2" ) ply:Give( "gmod_tool" ) ply:Give( "gmod_camera" ) ply:Give( "weapon_physgun" ) end //end this if //For other characters, use elseif for example // elseif (ply:SteamID() == "STEAM 02984529" ) then //ply:SetTeam( 2 ) //etc. etc. end //end the function
And we're done! Your character is defined, and when you go on, you will recieve everything you want.
Overview
Here's all the code for you.
- Shared.lua
GM.Name = "SimpleBuild" //What is your Gamemode's name? GM.Author = "Darkcha0s" // Who authored it? GM.Email = "[email protected]" //Your email? GM.Website = "thec0re.net" //Website, only if you feel it DeriveGamemode( "sandbox" ) //Derive from Sandbox. If you want the Q menu's and Garry's scoreboard, keep this. team.SetUp( 1, "Guest", Color( 125, 125, 125, 255 ) ) //Team guest team.SetUp( 2, "Admin", Color( 255, 255, 255, 255 ) ) //Team admin team.SetUp( 3, "Darkcha0s", Color( 148, 0, 211, 255 ) ) //It's me! team.SetUp( 4, "Joining", Color( 0, 0 , 0, 255 ) ) //Joining
- Init.lua
AddCSLuaFile( "cl_init.lua" ) //dl that AddCSLuaFile( "shared.lua" ) //dl that too AddCSLuaFile( "specialchars.lua" ) //and this include( 'shared.lua' ) //load that include( 'specialchars.lua' ) //This too. function GM:PlayerSpawn( ply ) //What happens when the player spawns self.BaseClass:PlayerSpawn( ply ) // Lines 12 through 18 are all fixes to the sandbox glitch. Don't change // them unless you know what you're doing. ply:SetGravity( 0.75 ) ply:SetMaxHealth( 100, true ) ply:SetWalkSpeed( 325 ) ply:SetRunSpeed( 325 ) end function GM:PlayerInitialSpawn( ply ) // When spawning after joining the sever CheckSpecialCharacters( ply ) //Is he defined in the specialchars.lua file? If yes, then he gets those values, and it stops here. if ply:IsAdmin() then //Is the connecting player admin? sb_team2( ply ) //If he is then set his team to team 2. else // If he isn't admin then, joining( ply ) //Call the function joining (found near the bottom of this file) RunConsoleCommand( "sb_start" ) //Run the console command defined in cl_init.lua. end //Close the if end //close the function function GM:PlayerLoadout( ply ) // What should the player recieve when joining a team? if ply:Team() == 1 then //If he is team 1, then give him the following items ply:Give( "weapon_physcannon" ) // A Gravity gun ply:Give( "weapon_physgun" ) // A Physics gun ply:Give( "gmod_tool" ) // and don't forget the tool gun! elseif ply:Team() == 2 then // So if he isn't team 1, he could be team 2? ply:Give( "weapon_physcannon" ) //Assuming he is, then give him Gravity gun ply:Give( "weapon_physgun" ) // Physics gun ply:Give( "weapon_ar2" ) // AR2 ply:Give( "gmod_tool" ) // and the gmod tool //I should mention at this point you can put in else, but there is no point. All possible scenarios are covered. Thus we end it end //right here. end // End the function function sb_team1( ply ) //This is what happens when we enter sb_team1 into the console. ply:UnSpectate() //Since he was set to spectate until he presses the 'hell yeah' button, we now unspecatate him ply:SetTeam( 1 ) //We set his team to one, a.k.a 'guest' ply:Spawn() //Spawn the player ply:PrintMessage( HUD_PRINTTALK, "[SimpleBuild]Welcome to the server, " .. ply:Nick() ) //Gives the message [SimpleBuild]Welcome to the server, (playername here)" in the talk area. end //End the function. function sb_team2( ply ) // Function sb_team2. Called at the beginning // Why no unspectate? Look carefully at the GM:PlayerSpawn; We only call to spectate after we see if he's an admin. Assuming he's always ready to build, I chose to skip it. ply:SetTeam( 2 ) //Set his team to team 2. ply:Spawn() // Spawn him ply:PrintMessage( HUD_PRINTTALK, "[SimpleBuild]I recognize you as an admin, " .. ply:Nick() ) //Again, a message in the talk area. //This time saying "[SimpleBuild] I recognize you as an admin (playername here)" end //End this function concommand.Add( "sb_team1", sb_team1 ) //Now, we make sure that when we enter sb_team1 into console that it calls the function. This is KEY. Otherwise players won't be able to play. function joining( ply ) // The function that's called when the player is not admin or a special character, at the top. ply:Spectate( 5 ) //Set him to spectate in free-roam mode. He doesn't actually fly around, since he has a window open at this point. ply:SetTeam( 4 ) //Set his team to Joining end //End the function
- cl_init.lua
include( 'shared.lua' ) //LOAD THAT function set_team() //Function for the window when joining as neither special character nor Admin Ready = vgui.Create( "DFrame" ) //Define ready as a "DFrame" Ready:SetPos( ScrW() / 2, ScrH() / 2 ) //Set the position. Half the screen height and half the screen width. This will result in being bottom right of the middle of the screen. Ready:SetSize( 175, 75 ) //The size, in pixels of the Frame Ready:SetTitle( "Are you ready to build?" ) //The title; It's at the top. Ready:SetVisible( true ) // Should it be seen? Ready:SetDraggable( false ) // Can people drag it around? Ready:ShowCloseButton( false ) //Show the little X top right? I chose no, because I have no alternative, meaning people would roam around with no weapons Ready:MakePopup() //Make it popup. Of course. ready1 = vgui.Create( "DButton", Ready ) // Define ready1 as a "DButton" with its parent the Ready frame we just created above. ready1:SetPos( 20, 25 ) //Set position, relative to the frame (If you didn't parent it, it would be relative to the screen ready1:SetSize( 140, 40 ) // How big it should be, again in pixels ready1:SetText( "Hell yeah!" ) //What should the button say? ready1.DoClick = function() //ready1.doclick = function, we just defined it as a function RunConsoleCommand( "sb_team1" ) //When it clicks, which function does it run? sb_team1, which is defined in init.lua end // end the doclick function end // end the set_team function concommand.Add( "sb_start", set_team ) //Now we add a console command for the function we just created. It can be run straight from the console. If you look under the first couple of lines under init.lua, we // said that it should run this command!
- Specialchars.lua
function CheckSpecialCharacters( ply ) //This function is called upon on GM:PlayerInitialSpawn //Here we add our characters which are special. //I left an example with what I have --Darkcha0s if ( ply:SteamID() == "STEAM_0:1:7880281" ) then //If steamid is that, then execute the following ply:PrintMessage( HUD_PRINTTALK, "Welcome back, " .. ply:Nick() .. "\nYou connected under the IP: " .. ply:IPAddress() ) // Gives the message //Welcome back Darkcha0s, you have connected under the IP: blah. In local multiplayer it will be loopback. ply:SetTeam( 3 ) //Set it to this team, look in shared.lua for this one. // He should recieve the following weapons ply:Give( "weapon_crowbar" ) ply:Give( "weapon_pistol" ) ply:Give( "weapon_smg1" ) ply:Give( "weapon_frag" ) ply:Give( "weapon_physcannon" ) ply:Give( "weapon_crossbow" ) ply:Give( "weapon_shotgun" ) ply:Give( "weapon_357" ) ply:Give( "weapon_rpg" ) ply:Give( "weapon_ar2" ) ply:Give( "gmod_tool" ) ply:Give( "gmod_camera" ) ply:Give( "weapon_physgun" ) end //end this if //For other characters, use elseif for example // elseif (ply:SteamID() == "STEAM 02984529" ) then //ply:SetTeam( 2 ) //etc. etc. end //end the function
That's it, I hope I helped, I'll make more help soon.
Quick Start Gamemode .zip A .zip file containing everything from this tutorial, plus a ReadMe