You are on page 1of 7

Tutorial Information Difficulty: * Time required: Less than 15 minutes What does it do?

This code will print a welcome message everytime someone connec ts to the server. Introduction So here's my *second* GSC tutorial for you all. It's a very simple one -- a welc ome message. I'll show you how to make the script, and explain in detail how it works as well as give you some pointers for optimising and improving your code. Table of Contents Prerequisites Getting started Testing it out! Improving our code Final notes 1. Prerequisites Open up your Call of Duty/main folder. If you don't have one already, make a fol der called 'maps'. Navigate inside of maps, and then make another folder called 'mp'. Navigate inside mp, and then make a final folder called 'gametypes'. You s hould have a directory structure like this: Call of Duty/main/maps/mp/gametypes 1.1 is really neat in that you do not have to create a PK3 file EVERYTIME you wa nt to test your scripts. We can actually just place the .gsc files in the gamety pes folder, run CoD, update the scripts, restart the map and everything will be updated nice and easy. smile I HIGHLY suggest using a text editor such as Notepad++ or Programmer's Notepad f or ANY scripting! It will make your life much easier than just using notepad! No tepad++ and Programmer's Notepad. Once you open a script, I suggest you set the code formatting/language to 'C', as CoD scripting is based off of it. 2. Getting started After we've created our folders, the next thing we need to do is extract the 'dm .gsc' file from pak5.pk3. Navigate to your main folder and open pak5.pk3 with yo ur favorite archiver (I use Winrar, but Winzip or 7zip work as well). Open the m aps/mp/gametypes folder and extract 'dm.gsc' to the gametypes folder you made in your main folder. Close pak5.pk3. Now you should have dm.gsc in your main/maps/mp/gametypes folder. Let's open it up. As soon as you open it, you'll be greeted by this: GSC (Quake3) Render in: 0.004 /* Deathmatch Objective: Score points by eliminating other players Map ends: When one player reaches the score limit, or time limit is reach ed Respawning: No wait / Away from other players Level requirements -----------------Spawnpoints: classname mp_deathmatch_spawn All players spawn from these. The spawnpoint chosen is dependent on the current locations of enemies at the time of spawn. Players generally spawn away from enemies. Spectator Spawnpoints: classname mp_deathmatch_intermission Spectators spawn from these and intermission is viewed from these po sitions.

Atleast one is required, any more and they are randomly chosen betwe en. Level script requirements ------------------------Team Definitions: game["allies"] = "american"; game["axis"] = "german"; Because Deathmatch doesn't have teams with regard to gameplay or sco ring, this effectively sets the available weapons. If using minefields or exploders: maps\mp\_load::main(); ... No need to pay any attention to this -- although it is good to read through. Thi s comment will tell you everything you need to know about creating a map that wo rks properly with the DM gametype. We don't need to know this though -- so skip down a bit until you find the main() function. GSC (Quake3) Render in: 0.010 main() { level.callbackStartGameType = ::Callback_StartGameType; level.callbackPlayerConnect = ::Callback_PlayerConnect; level.callbackPlayerDisconnect = ::Callback_PlayerDisconnect; level.callbackPlayerDamage = ::Callback_PlayerDamage; level.callbackPlayerKilled = ::Callback_PlayerKilled; maps\mp\gametypes\_callbacksetup::SetupCallbacks(); allowed[0] = "dm"; maps\mp\gametypes\_gameobjects::main(allowed); if(getcvar("scr_dm_timelimit") == "") // Time limit per map setcvar("scr_dm_timelimit", "30"); else if(getcvarfloat("scr_dm_timelimit") > 1440) setcvar("scr_dm_timelimit", "1440"); level.timelimit = getcvarfloat("scr_dm_timelimit"); if(getcvar("scr_dm_scorelimit") == "") // Score limit per map setcvar("scr_dm_scorelimit", "50"); level.scorelimit = getcvarint("scr_dm_scorelimit"); if(getcvar("scr_forcerespawn") == "") setcvar("scr_forcerespawn", "0"); if(getcvar("g_allowvote") == "") setcvar("g_allowvote", "1"); level.allowvote = getcvarint("g_allowvote"); setcvar("scr_allow_vote", level.allowvote); if(!isdefined(game["state"])) game["state"] = "playing"; level.QuickMessageToAll = true; level.mapended = false; level.healthqueue = []; level.healthqueuecurrent = 0; spawnpointname = "mp_deathmatch_spawn"; spawnpoints = getentarray(spawnpointname, "classname"); // Force respawning

if(spawnpoints.size > 0) { for(i = 0; i < spawnpoints.size; i++) spawnpoints[i] placeSpawnpoint(); } else maps\mp\_utility::error("NO " + spawnpointname + " SPAWNPOINTS IN MAP"); setarchive(true); } This is the entry point for our gametype. CoD calls this function and expects it to exist. If you renamed main(), it would break the gametype. So don't do that. What we want to create is a welcome message that is displayed to the player when they join. Just below main() and Callback_StartGametype() is a function called Callback_PlayerConnect(). This is the function we want. GSC (Quake3) Render in: 0.005 Callback_PlayerConnect() { self.statusicon = "gfx/hud/hud@status_connecting.tga"; self waittill("begin"); self.statusicon = ""; iprintln(&"MPSCRIPT_CONNECTED", self); lpselfnum = self getEntityNumber(); logPrint("J;" + lpselfnum + ";" + self.name + "\n"); if(game["state"] == "intermission") { spawnIntermission(); return; } level endon("intermission"); ... Right below the line iprintln(&"MPSCRIPT_CONNECTED", self);, let's add a variabl e to our player. GSC (Quake3) Render in: 0.004 ... iprintln(&"MPSCRIPT_CONNECTED", self); self.wasWelcomed = false; ... We'll use this later, but for right now all this code says is self.wasWelcomed i s false. smile Now, let's scroll on down to the function spawnPlayer() (or you can do a CTRL-F and search for it smile). GSC (Quake3) Render in: 0.008 spawnPlayer() { self notify("spawned"); self notify("end_respawn"); resettimeout(); // // // // if(isdefined(self.shocked)) { self stopShellshock(); self.shocked = undefined;

//

} self.sessionteam = "none"; self.sessionstate = "playing"; self.spectatorclient = -1; self.archivetime = 0; spawnpointname = "mp_deathmatch_spawn"; spawnpoints = getentarray(spawnpointname, "classname"); spawnpoint = maps\mp\gametypes\_spawnlogic::getSpawnpoint_DM(spawnpoints); if(isdefined(spawnpoint)) self spawn(spawnpoint.origin, spawnpoint.angles); else maps\mp\_utility::error("NO " + spawnpointname + " SPAWNPOINTS IN MAP"); self.statusicon = ""; self.maxhealth = 100; self.health = self.maxhealth; if(!isdefined(self.pers["savedmodel"])) maps\mp\gametypes\_teams::model(); else maps\mp\_utility::loadModel(self.pers["savedmodel"]); maps\mp\gametypes\_teams::loadout(); self giveWeapon(self.pers["weapon"]); self giveMaxAmmo(self.pers["weapon"]); self setSpawnWeapon(self.pers["weapon"]);

self setClientCvar("cg_objectiveText", &"DM_KILL_OTHER_PLAYERS"); } Let's add a little bit to the end right before the final closing brace '}'. GSC (Quake3) Render in: 0.004 ... self setClientCvar("cg_objectiveText", &"DM_KILL_OTHER_PLAYERS"); if ( !self.wasWelcomed ) { self iPrintLnBold( "Welcome to our server!" ); self.wasWelcomed = true; } } Our code, translated into psuedocode, does this: if NOT self.wasWelcomed equals true then self print to center screen "Welcome to our server!" set self.wasWelcomed to TRUE end if In other words, if self.wasWelcomed is equal to false, then we know he has not b een welcomed, so we print a message to his screen, and then set our variable to true so we later know he WAS welcomed. Easy, right? 3. Testing it out! Save your work and let's open CoD. Once open, type this into your console: /devmap mp_harbor It will load harbor with cheats (which is helpful when testing code). If you did everything right, you should see this: shot0044.jpg If not, you'll see something like this:

broken.jpg All this means is that there's an error in your code. To see what the problem wa s, type /developer 1 in the console, and /devmap mp_harbor again. It will show t he same screen again, but this time, bring the console back down and you'll see this: broken2.jpg So now we know something's wrong on line 625. Let's take a look at that line and the surrounding area: GSC (Quake3) Render in: 0.004 ... self setClientCvar("cg_objectiveText", &"DM_KILL_OTHER_PLAYERS"); if ( !self.wasWelcomed ) { self iPrintLnBold( "Welcome to our server!" ); self.wasWelcomed = true } } The second to last curly brace '}' is the line it's saying, but if we look at th e line above it, we'll see that we are missing a semicolon. GSC (Quake3) Render in: 0.004 ... self iPrintLnBold( "Welcome to our server!" ); self.wasWelcomed = true // <-- missing semicolon here } } Add it, and test again and if you get another error, keep checking with /develop er 1 to see what your problem is. If you ever get one of these: broken3.jpg Your code is working fine! Don't freak out. This error means very little, and wi ll go away once you set /developer 0. Only, and I repeat ONLY use developer mode when you have a SCRIPT COMPILE ERROR. EVER. smile Seriously, if you try to fix every 'script runtime error' you'll go nuts. 4. Improving our code One of the most important things about coding (in any language) is optimisation. If you can do something in 5 lines of code instead of 20... DO IT. If instead o f copy and pasting code, you can make a function that does the same thing, DO IT -- this type of code is called recursive. You always will want to do this. It w ill make your code better, and you can more easily debug and find issues this wa y. So, taking a look at our code that we added (which isn't much, by the way), I se e a few ways we can improve this. For one, we don't need to have our code in the dm.gsc. I like to keep all of my actual code in separate files so it's easier t o debug than having to scroll through 1000's of lines of code. Let's make a file called _welcome.gsc and put it in the gametypes folder next to dm.gsc. In it, we'll create a function called 'main()', which is to be called when the p layer connects. _welcome.gsc GSC (Quake3) Render in: 0.004 main() { self waittill( "spawned_player" ); self iPrintLnBold( "Welcome to our server!" ); } Now let's remove our code from spawnPlayer() in dm.gsc: GSC (Quake3) Render in: 0.004 ...

self setClientCvar("cg_objectiveText", &"DM_KILL_OTHER_PLAYERS"); /* if ( !self.wasWelcomed ) { self iPrintLnBold( "Welcome to our server!" ); self.wasWelcomed = true } */ } And at the beginning of spawnPlayer(), add this: GSC (Quake3) Render in: 0.004 spawnPlayer() { self notify("spawned"); self notify("end_respawn"); // add self notify( "spawned_player" ); ... And instead of having this in Callback_PlayerConnect(): GSC (Quake3) Render in: 0.004 ... iprintln(&"MPSCRIPT_CONNECTED", self); self.wasWelcomed = false; ... Let's do this: GSC (Quake3) Render in: 0.004 ... iprintln(&"MPSCRIPT_CONNECTED", self); self thread maps\mp\gametypes\_welcome::main(); ... Basically, what this will do is start a 'thread' on a player. The thread is our main() function in _welcome, and it will run it for us. So now when we test, it looks exactly the same. But notice the code is slightly different. Let's take a look at how this works. _welcome.gsc GSC (Quake3) Render in: 0.004 main() { self waittill( "spawned_player" ); self iPrintLnBold( "Welcome to our server!" ); } This function is called when the player connects. We're using a more advanced fe ature of CoD on the third line -- notify/waittill. What you can do with this is notify an object of an event, in this case, the spawnPlayer() function notifies our player "spawned", and you can tell code to WAIT UNTIL that event happens, he nce "waittill spawned". Any code you put above the waittill will execute immediately, but the code after wards will only execute once the event happens. Basically, instead of having to check a variable every time the player spawns, we're only running this code ONCE . Not only that, but we have our code nice and neatly in a new file for easy deb ugging. This also allows us to add more extravagant things to our code, such as the abil ity to have the server owner set their own welcome messages, like this: GSC (Quake3) Render in: 0.005 main() { messages = [];

i = 0; while ( getCvar( "welcome_message" + i ) != "" ) { messages[ messages.size ] = getCvar( "welcome_message" + i ); i++; } self waittill( "spawned_player" ); for ( i = 0; i < messages.size; i++ ) { self iPrintLnBold( messages[ i ] ); wait 2; } } In the server config, you could then set this: set welcome_message0 "Hey there." set welcome_message1 "Welcome to our server." set welcome_message2 "We like bacon." And the game would do this: shot0045.jpg Pretty cool, eh? big_smile 5. Final notes I know this is a lot of information for such a simple tutorial, but once you hav e a grasp of it, it will be easy for you. No coding is ever simple, especially g ame coding. But have some faith -- with a little practice you'll get the hang of it. If you have any specific tutorials you want me to show, like how to integrate st uff with CoDaM, or how to make people into cows, etc. LET ME KNOW! I'll be glad to post some specific examples of how things work. wink

You might also like