1. Introduction to HL CodingProgramming is powerful. It's one of the mightiest things there are about PCs. And just like that, it's powerful in Half-Life.
Want a new trigger_ or func_ entity for your map? Maybe a new weapon, NPC, or completely changing some mechanisms? HL coding is the answer to that.
I wrote this tutorial with absolute beginners in mind, so there may be explanations for things that are obvious or self-explanatory, or are the very basics.
If you are one of those, I highly, highly and highly recommend you to learn C++ programming first. It will be annoying and a bit hard at first (so will HL coding), but you'll get used to it after a few weeks or so. Still, I hope that what I write here will be enough to understand, and later it'll make sense.
With that said, let's begin:
1.1. Tools and source codeIf you're new to HL modding in general, you might be thinking to yourself: "Half-Life is so old that I'll have to use the old software to develop for it."
No. Community members are still making modern tools for it (J.A.C.K. and Sledge for mapping, HL Texture Tools for texturing, new export/import plugins for Blender and 3ds Max etc.), and some people take a step further and improve its SDK code.
Solokiller's fork of Half-Life SDK 2.4 is what we'll use in this tutorial. You can find it here. It's compatible with VS2017, and it has a couple of features, such as some parts written in C being replaced by C++.
Speaking of VS2017, our IDE will be Visual Studio 2017.
An IDE is essentially an all-in-one tool for developing your program. Think of it as a very upgraded version of a text editor.
The mistake I made years ago was that I downloaded HL SDK 2.3, whose project file couldn't be converted to newer Visual Studio versions than 2008. I thought it would work only with Visual Studio 6.0 (1998), so I installed that instead. I also didn't have any prior C++ knowledge, and that led to many issues later on, hence I recommended you to stop and learn the basics first.
Lastly, "SDK" and "source code" aren't the same things. An SDK is a set of programs and tools for developing something for its respected software. Source code is the original code of a software, and it generally comes with an SDK. But since the actual SDK's tools are outdated as hell, I'll refer to the source code as the SDK. Keep that in mind.
Either way, let's download the HL SDK 2.4 code.
Click Clone or download:
Click Download ZIP. However, if you want to use somebody else's repository, you can find them here.
Or do whatever you want. :)
Once it's done downloading, you'll want to extract the ZIP file's contents somewhere.
I usually put it under
Workfolders/Half-Life/SourceCodes/, but if you're lazy enough, you can just extract the whole thing to your desktop. Understand that choosing the latter will result in one giant mess in the end. So I tell you: be organised, and make sure everything's tidy!
Now, once that is done, let's install Visual Studio 2017.
Download the Community edition. In the past, there was the so-called Express version, and Community just replaced it.
And then open the downloaded .exe file.
Hopefully your Internet connection isn't as bad as mine. :P
For what we're going to do, just check Desktop development with C++.
You can also enable XP support under Individual components if you wish.
1.2. Opening the HL SDK codeIt's a good idea to make a copy of the halflife-master-updated folder and rename it to something else. You might end up editing the code so much that you don't know what the original was like, yet you need the original one to start a new project.
Open the copied folder:
As you can see, there are all sorts of folders in there. The one we're interested in is projects. Open projects, and open the vs2017 inside of it.
That is the main solution file. It will contain the projects which we'll work on: the main DLL (hldll -> hl.dll) and the client DLL (hl_cdll -> client.dll).
When everything loads, your window will look like this:
(maybe not 100% exactly, since my Output window is in the bottom-left corner, I placed it there myself)
Here are our projects:
We'll be working with hl_cdll and hldll.
For starters, let's make a func_wall say "Hello, world!". Yes, you've read it right. A func_wall, when it gets triggered, will say "Hello world!" in the console.
? whyEvery first-time programmer's task is to make a hello world program. After all, this is a relatively simple thing to do even in GoldSrc.
If you double-click hldll:
No need to touch References and External Dependencies. External Dependencies are needed for the code to work and compile properly, since it uses something from them.
We'll work with header (.h) and source files (.cpp). Source files contain code, simple as that. Header files, think of them as a way of communication between two source files. They can be used for more than that though.
So, let's go to
Source Files/dlls/bmodels.cpp. Its name stands for "brush models". func_wall is defined there.
Now, do not get scared of the huge amount of stuff that's in here.
? // and /* */
The code marked in green are comments. When code is being compiled, comments are ignored.
// this is a comment
/* this is a multi
line comment */
This is the class definition of
CFuncWall. It inherits variables and functions from
? classIt's essentially like a blueprint for each instance. For example, CFuncWall is for instances of
func_wall, i.e. for each object created with this class. It contains variables and functions, and one class can inherit these from another class.
public, you can see 3 functions:
Spawnis a function that's called when the entity spawns in the map.
Useis called when the entity is triggered by another entity. You might think that this is called when the object is used by the player. Nope, not in this case.
This is a function that links the entity
func_wallto its class,
CFuncWall. When you code your own entity, you'll have to write this. Notice how it's written outside of the class definition.
Here is the
func_wall. What it essentially does, is changing the frame variable.
This means if you have a toggleable texture, it'll toggle it:
So, let's edit this thing.
Add the following line after the
pev->frameline, or before the
ALERT( at_console, "\nHello world!" );
A function can have parameters a.k.a. arguments, in this case it has two: an alert type, and the message contents themselves.
ALERTbasically outputs messages like those. It's actually a different way of writing this:
g_engfuncs.pfnAlertMessageThis was done for convenience. These other "utilities" are defined in enginecallback.h.
The Use function should now look like this:
You can experiment with this. Put up multiple messages, or try an
ALERT_TYPEother than "at_console". There are 6 in total:
at_notice - same as below, but it prefixes the message with
at_console - prints into the console, shown if developer level is 1 or higher.
at_aiconsole - same as above, but only shown if developer level is 2.
at_warning - prefixes the message with
at_error - prefixes the message with
at_logged - prints to the server console only in multiplayer game
These are defined in eiface.h, which serves as an engine interface for the SDK.
1.3. Your first DLLNow that this part is over, we can compile the code.
? compileCompiling is a process of translating the code in one programming language, to the language that the PC can understand (machine language), and later linking all that into an executable file. In this case, we're compiling DLL files.
Go to Build, and press Build hldll.
If everything goes fine, it should write "1 succeeded" at least. First time's a charm. If it didn't, well, feel free to ask here. HL coders are still around, even if they're rarer than they were 10 years ago.
It'll also say where it saved the DLL file. In this case, it's
projects/vs2017/Debug/hldll/. Go to that folder:
You could copy this straight into your valve/dlls folder, but don't do that. Instead, we'll create a new mod. (this tutorial being for beginners, I decided to include this too)
Create a new folder in Half-Life, in the same directory where hl.exe and the rest are located:
The folder structure inside the mod is quite simple.
Here's an example. Some files will be automatically generated. All you have to do is copy the following from the valve directory: cl_dlls, dlls, liblist.gam.
liblist.gamdefines the libraries (DLLs) that the mod will use, its title and other info.
After you've copied that, let's open
liblist.gam. You can open it with Notepad or any other text editor.
// Valve Game Info fileThe
// These are key/value pairs. Certain mods will use different settings.
gamestring defines the window title, and the game title you see in your Steam library. Yes, you can do
"[insert own name here]: The Game"and your friends will start messaging you asking about what you're playing.
Change it so that it differs from Half-Life, or anything else you have in your Steam library.
And finally, copy
dllsfolder. It would be a good idea to actually make a map, where you trigger a func_wall with an entity.
1.4. TestingOpen up the mod, and launch the map. Also set
Success. Keep in mind that this will affect literally every single func_wall there exists.
1.5. Finding your way around the SDK
1.5.1. 1.It's useful to always look at definitions of variables and functions.
For example, we can peek at
ALERT's definition, and we'll see that it's just a macro of one of the engine functions:
#define ALERT (*g_engfuncs.pfnAlertMessage)And we can do the same thing for at_console, see where the others are defined:
typedef enumAs you can see, they're nothing special. A typedef is basically giving a name to an enumeration and calls it
at_console, // same as at_notice, but forces a ConPrintf, not a message box
at_aiconsole, // same as at_console, but only shown if developer level is 2!
at_logged // Server print to console ( only in multiplayer games ).
This gives you the ability to do stuff like this:
ALERT_TYPE something;It's almost the same as doing this:
something = at_notice; ALERT( something, "\nHello world!" );
int something;Except the point of
something = 0;
ALERT( something, "\nHello world!" );
ALERT_TYPEis to replace these numbers with a more memorable name.
1.5.2. 2.Explore the header and source files. You'll always find something interesting, whether it's funny comments, or useful keyvalues. In const.h, you can find constants for the effects keyvalue:
// entity effectsYou can do stuff with this, like adding an
#define EF_BRIGHTFIELD 1 // swirling cloud of particles
#define EF_MUZZLEFLASH 2 // single frame ELIGHT on entity attachment 0
#define EF_BRIGHTLIGHT 4 // DLIGHT centered at entity origin
#define EF_DIMLIGHT 8 // player flashlight
effectskeyvalue to a solid entity (turn off SmartEdit), and give it a value of
1, for example. You should see something familiar if you placed NPCs in the wrong places in your maps:
(from left to right: 1, 8, 64)
Of course, I'm talking about the far left one. Don't forget to add an origin brush to your func_wall entities or else these effects will spawn in the world origin, 0,0,0.
However, this wouldn't be a good idea for some specific entities, since they set this keyvalue for themselves and are probably expecting it to be of a certain value. So be careful.
1.5.3. 3.If you aren't sure how an entity does something, like triggering another entity, then just search for that entity class and look at its functions.
Go to Class View and let's find env_explosion's way of dynamically changing the explosion sprite, for example:
explode.cpp. (sounds funny TBH)
This is how it dynamically changes its sprite scale depending on the explosion magnitude. You can alter this behaviour if you wish.
1.6. What next?There are many sites which do have coding tutorials laying around.
ModDB - https://www.moddb.com/games/half-life/tutorials?fi...
TheWavelength - http://articles.thewavelength.net/index.php?site=s...
The Whole Half-Life - https://twhl.info/wiki/page/category:Goldsource_Tu...
Sourcemodding - https://www.sourcemodding.com/tutorials/goldsrc/pr...
In the near future, I'll write more of these, including:
- semi-auto and auto pistol (modifying the existing HL weapon)
- env_explosion with more customisation options
- trigger_timer, and later a parametric env_viewsway, and a custom FGD
Happy coding! :)