My First Mod
From Valve Developer Community
This tutorial will teach you how to create and build your first simple Source mod. In this mod we will change the speed at which Half-Life 2's rocket travels through the world. This tutorial uses Source SDK 2007 but most of it is applicable to Source SDK 2013.
Create the project
For the purpose of this tutorial we will call our first mod, "Mymod". When you make your own mod this can be any name you wish (i.e. "Quickmod", "TreasureTroll", "Old Man With Melons Mod", etc.)
Please note that in order to get the latest source code, you must refresh the SDK content.
To create a mod project:
- Open the Source SDK application from Steam's Tools menu.
- If it already hasn't been selected for you, select Source Engine 2007 for Engine Version.
- Open Create A Mod from the Utilities group.
- Select either modify Half-Life 2 Single Player or modify Half-Life 2 Multiplayer.
- Specify where you would like the mod's project to be placed (i.e.
- Enter the name of your mod. In this example, we'll use "Mymod".
- Vista may require extra steps depending on your security settings.
The Steam application will then copy all the relevant files to the destination directory that you specified. You now have a copy of Half-Life 2's source code at that location which you can modify.
Prepare the solution
- Carefully follow the instructions in Compiling under VS2008 / 2005 / 2010 / 2012, whichever is appropriate.
- Carefully follow the instructions in Launching from Visual Studio.
Alter the rocket's speed
If you are editing Half-life Single-player: in the Solution Explorer, open file
Server Episodic > Source Files > HL2 DLL > weapon_rpg.cpp.
Near the beginning of the file, find the line:
#define RPG_SPEED 1500
Change this line to read:
#define RPG_SPEED 400
By altering this number, we've told the rocket to travel at 400 units per second (or about 33 feet per second, or 10 meters per second). Previously the rocket traveled at 1500 units per second (125 ft/s, 38 m/s). The difference in speed will be very apparent when we compile and run the mod.
Compile the project
Now we are ready to build the solution. Choose Build->Build Solution to begin. When the requested compilation has completed there will be a
client.dll file in the
(i.e. C:\Mymod\Mymod\Bin). This is the custom server and client DLL for our mod and is one of the pieces that would be distributed to end-users.
Run the mod
Now before you launch it's important to ensure you have something to launch. By default, new mods will use AppId 215 (Ep1) or 218 (Orange Box). This loads the correct version of Source SDK Base, which is accessible to almost everyone with a Valve game and, in the case of 218/Orange Box, is kept up to date with the latest engine built. You can check or change your AppId in the game's GameInfo.txt file.
SDK Base provides only Half-Life 2's shared content, however. If you want to use content from a specific game, or HL2's maps, you will need to change the AppId to that of the game you want to load from. If you want to 'mount' more than one game you can do so, but you should avoid that method unless you really need it for reasons explained on its page.
In the directory that you created your mod, there will be a batch file called
run_mod.bat. Don't use the bat file to launch your mod, as it has not been updated for the SDK Base. The best way to launch your mod is from within Visual Studio by pressing F5, or restart Steam and launch it from your Games list (where it should hopefully have appeared - if not, check your AppId).
No maps are provided with your single player mod, so copy a map from the HL2 maps with GCFScape to your mod's /maps/ directory inside of SteamApps/Sourcemods. Load a map by typing "map " (including the space) into the console and select from the list (e.g. "
map d1_canals_01") then type "
impulse 101" to give yourself a load of weapons.
Now that there's content to load, start the game up. When the engine has loaded, you'll be taken to the title screen. If you are warned about missing content, such as
maplist.txt, ignore it.
steamapps\[USER ID]\half-life 2\hl2\maps
sv_cheats 1" in the console (~) for the "
impulse 101" command to work
Select and fire the RPG to view the changes you've made.
You may have some difficulties following these steps using Release configuration. Change to Debug if you run into problems. Before moving on, I must point out that Source modding is a VERY difficult way to learn C++. You will be better served trying out lots of tutorials designed for learning C++, and applying what you've learned to modding.
In the game code, right click the RPG_SPEED and select Find all references.... This will display the Find Symbol Results pane. Click the plus to expand the results. The first is obviously the definition, the part we were playing with before. The other two are locations where RPG_SPEED is actually being used. Double click the last one.
This brings us to a function called
void CMissile::IgniteThink(). When the rocket is first launched, it's not ignited. This code is used, or “called”, to ignite the rocket. The line of code we are at:
SetAbsVelocity( vecForward * RPG_SPEED );
sets the velocity (direction and speed) of the rocket. Use the Find all references..., Go to Definition, and Go to Declaration tools liberally. If ever you don't know what something is, where it came from, or what it is doing, these tools can help you wrap your mind around the code.
By default, the RPG_SPEED is set to 1500.
Using break points
In the gray margin to the left of this line of code, click the left mouse button. If this doesn't do anything or you are just confused, you can alternatively press F9 or use
Debug Menu > Toggle Breakpoint. This will put a red dot in the margin known as a break point. A break point is used to pause execution of a program. This may seem like a strange concept, so some explanation is in order.
It's often useful to see what's going on in a program at a specific moment. When the program tries to run this line of code, it will be stopped. EVERYTHING in the program will be stopped. 3D graphics, mouse input, even clicking the close button have been paused. This allows you to scrutinize the state/data of the program, frozen in a moment in time.
If the mod isn't already running, press F5 and fire it up. Load up your map and some weapons, and shoot the RPG.
Examining and modifying state
Right when the rocket tried to kick on, Visual Studio should have popped up and should say Paused in the title bar. The cursor will be on the line of code, a yellow arrow is in the margin. Hover the mouse over vecForward and you'll see a box pop showing you the values inside of vecForward, X Y and Z. These values are currently rather small (This is a normalized vector, but that's another tutorial). If you click the plus to expand the values, you can actually modify the values, changing the internal state of the program.
Go to Debug Menu > Windows > Autos. This will automatically show you any variables that are being used by the current code, and show them in red if they've changed their value.
Stepping through execution
This line of code we have “paused execution” at is an instruction to use the SetAbsVelocity function, and it also passes some data to the function: vecForward * RPG_SPEED.
Press F11. Alternatively, you can use Debug Menu > Step Into, or find the button in the toolbar with the same icon. Notice again the yellow arrow in the gray margin. This arrow indicates the next instruction the program will execute.
We are now inside the SetAbsVelocity function. Examine the value of vecAbsVelocity. You'll notice that it's much bigger than vecForward before, because it's been multiplied by RPG_SPEED. Now examine, m_vecAbsVelocity. This is the actual saved velocity of the rocket. Right now it's still rather small, because the rocket just got ignited and hasn't sped up at all. Press F10. F10 is similar to F11, except that it wont go inside of functions.
If we had pressed F10 before, the yellow arrow wouldn't have moved into the SetAbsVelocity function. Instead it would have moved to the line after
SetAbsVelocity(vecForward*RPG_SPEED). The call to SetAbsVelocity still occurs, but it happens very quickly without us watching. Press F10 a few times till you get to the line:
m_vecAbsVelocity = vecAbsVelocity;
If you examine the two variables again, you will see they haven't changed yet. Press F11 (or F10. Since this isn't a function to step into, it has the same effect) once more. Now examine m_vecAbsVelocity. You will see that the values from vecAbsVelocity have been assigned, or copied over. This is a very powerful debugging technique. It allows us to see how the programs state changes from one line to the next. If something isn't working how we expect, we can watch things as they happen and trace to where things are going wrong. You can also trace backwards. Start from where the unexpected result is observed, and examine the state there. If part of the state (like a variable) is not right, go back to where the wrong state might have become present (the last place the variable got assigned a value). Use the Go to Definition, or Find all References to help you hunt down the possible places. Repeat the process until you hone in on the source of the error.
More debugging techniques
There are many features in Visual Studio to help you debug more efficiently. You can make break points only break on a certain condition, see the call stack - the chain of functions that have been called to get where you are now, and many others. If you want to get great at mod programming, you have to get great at programming C++, debugging C++, and how to get make full use of your IDE (in this case VS).
You've built your first mod. Now what?
You’ve now set up, built, and debugged your first mod. Now experiment and tinker. Getting your hands dirty is the best way. If you have coding questions, there is a formal news group called HLCODERS. If you are serious about mod programming, track it down. There are also many communities centered around HL2 and modding, and most have a programming forum, tutorials, etc.
For a stepping stone to different aspects of mapping see What makes a good level?