Tutorial: Non-coding workarounds for your mod Last edited 1 week ago2025-02-10 02:58:29 UTC

NOTE: This guide is a work in progress, which means that you might find it very short, lacking of information, poorly done or with terrible spelling. If you have any suggestions, ideas or you know any workarounds, please go to the comments section.
If you are not a programmer, or don't have one in your team, it might present a problem for the development of your mod. The next course of action would be to use other mods as a base (like Featureful or Opposing Force). However, in some cases, you may not be able to use custom code either. This is where this guide comes in: it contains a list of cool Half-Life features and stuff that you can implement in your mod!
Most of these went unused, or are workarounds rather than features themselves. It also contains some cool map tricks (such as the screen tint). These are meant to solve or tackle specific problems.

Caveats

The techniques discussed in this page isn't without gotchas. Here's some of the main problems of no-coding workarounds and dummied out features:

UI/Menu

A common feature in a lot of mods. Add an .mp3 file named gamestartup.mp3 to YourMod/media. This file should contain your selected main menu music. It will be played every time the game is launched. However, the music plays in a loop and does not play again when the player leaves the game or disconnects from a server.

Mapping/Level editing

Screen tint

This one was created by the user Mota. The original map presents a México-yellowish tint, but you can change this to other colors to fit your needs.
Normal Screen picture by MotaNormal Screen picture by Mota
Tinted Screen picture by MotaTinted Screen picture by Mota
Explanation and steps for achieving this effect is available in the vault entry page, linked below:
Loading embedded content: Vault Item #6681

Moving sprites

You would have seen sprites that moves through a level in various locations in Half-Life: Opposing Force. It is achieved through a custom entity (func_spritetrain) that are not available in vanilla Half-Life. However, you can use a trick to have a func_train display a sprite. This trick requires that you use some version of ZHLT compile tools (or its derivatives e.g. VHLT) that supports the use of the zhlt_usemodel keyvalue.

The following vault item has an example and the steps of achieving this trick:
Loading embedded content: Vault Item #6461
You can also use this trick to have a simple rotating sprite that doesn't use animation frames (thus saving on filesize), by using the angular velocity (avelocity) keyvalue. To have the sprite rotate in place, make the path_corner entities very close together (e.g. 1u apart) and have the func_train move very slowly e.g. 0.1u speed.

Model-based func_ entities

Using the same zhlt_usemodel keyvalue as mentioned in previous section, you can force a func_ entity, which is usually brush-based (just like func_train in previous section) to use a model. This is actually used in Condition Zero: Deleted Scenes with many of the mounted guns: they are func_tank entities that use a model, that are otherwise identical in function to the vanilla entity.

The tutorial page "Using models for health/HEV chargers in vanilla Half-Life" explores the use case for this workaround with the health and HEV chargers in particular. A vault item is linked in the page, but here's the same thing:
Loading embedded content: Vault Item #6897
Caveats
  • Crashing: A few func_ entities constantly runs a check if they are blocked, to deal damage to the blocking entity. If the entity is using a model instead of brushwork, this check will fail when an entity touches it and the game will crash. You need to make sure you check the "not solid" or "passable" flags for these entities.
  • Collision: If collision is still desired, place User posted image CLIP brushes around the entity. If the func_ entity moves, then turn those clip brushes into the same entity class as the model-func entity in question, sharing the same name. Some attributes might need to be identical, some might need to be adjusted.
  • Orientation: Some entities like func_door and func_button can't be rotated as the angles value is used to denote direction of movement. What you can do is define the model's root bone as a bone controller with angular range of 0-360. Then you can add a controller keyvalue with the range 0-255 which interpolates to 0-360 orientation around the Z axis.

Corpses of monsters without a _dead variant

You may notice that some monsters don't have a _dead variant. Checking the models used by these monsters (See Reference: Entities and their models page) with a model viewer (e.g. Half-Life Asset Manager aka HLAM) would reveal that they don't have any dead poses. But don't fret! You can use the env_sprite entity to display a model, and can display dead poses. Just turn off SmartEdit on J.A.C.K./Hammer and input the path to the monster's model as the model keyvalue. Then, with SmartEdit still turned off, add the following keyvalues:
Cons of this method
  • The corpse is not gibbable.
  • The env_sprite entity would not accept a custom body or skin keyvalue due to technical reasons. body and skin keyvalues were hijacked to enable env_sprite entities to become attached to another entity's model attachment points.
  • The dying animation will play (albeit sped up 8x) at the start of the level, so you need to place the entity out of sight of the players near the loading/transition place(s) to hide the jank.
Or, you could just edit the models and add dead sequences (being the last frames of the dying sequences) at the end of the QC file.

Higher-resolution / 3D skybox

The GoldSource skybox has a resolution of 256x256 per side, and unfortunately, the GoldSource game engine doesn't accept higher-resolution images for the skybox.

One solution is to place a ludicrously large model of an inverted box (i.e. the box textures face inward). The model face is textured with the sky image. It has to be really, really large (e.g. 50,000 units) so as to minimize parallax effect.
In order to have this really large model visible at all times, and not occluded by the engine:
Sven Co-op's engine (Svengine) seems to support higher resolution skybox, and a full-bright texture on models, alleviating some of the issues above.

Also, the Spirit of Half-Life mod has proper 3D skybox support, but it doesn't always work, and incurs fps penalty.
Expanding on the idea is 3D skyboxes. Instead of just an inverted box, the model can have detail such as terrain, buildings, etc. Unlike in Source, the details aren't miniatures but closer to the size it would've been if placed on the playable areas of the map. The models can even have animations, simulating a moving cloud layer in the sky. The mods Delta Particles and Half-Life: Enriched use 3D skyboxes.
Limits
  • Model textures are limited to 8-bit bitmaps vs 24-bit (full RGB) TGA images used in normal skyboxes. A solution is to cut the model into more sides each displaying a separate 8-bit indexed texture.
  • Model textures are limited to 512x512px (GoldSrc) or 1024x1024 (Svengine).
Skybox model showcase
Loading embedded content: Vault Item #6631
3D skybox example
Loading embedded content: Vault Item #4507

Difficulty/skill detection

Suppose we want to have different number of monsters spawn depending on difficulty (higher skill == more monsters):
Skill level Easy Medium Hard
"easy" monsters ✔️ ✔️ ✔️
"medium" monsters ✔️ ✔️
"hard" monsters ✔️
Legend
✔️ - keep
❌ - remove
Whereas Half-Life entities have a reserved global spawnflag value: 2048 - Not in deathmatch, which removes the entity if the map is run in deathmatch, unfortunately there is no flag to make entities spawn only at certain difficulty/skill level in the vanilla codebase.

One way to get around this is to use a monster with differing health based on skill level (set in the skill.cfg file), detect the monster's health with map logic, and use the result to do some other map logic; in this case removing certain monsters.

Suppose we'll be using the alien grunt, the only monster in vanilla Half-Life with differing health per skill level. The following snippet from skill.cfg sets their health:
// Alien Grunt
sk_agrunt_health1    "60"
sk_agrunt_health2    "90"
sk_agrunt_health3    "120"
In the starting map (i.e. the starting map when you start a new game), place 2 alien grunts in a room, separate from the rest of the map. Then, add two env_global entities that will get triggered if the skill is below a certain level.
monster_alien_grunt #1
  • TriggerCondition: Death
  • TriggerTarget: BelowMedium
monster_alien_grunt #2
  • TriggerCondition: Death
  • TriggerTarget: BelowHard
env_global #1
  • Name: BelowMedium
  • Global State to Set: SkillBelowMedium
  • Trigger Mode: On
env_global #2
  • Name: BelowHard
  • Global State to Set: SkillBelowHard
  • Trigger Mode: On
Next, set up two doors that will crush the monsters, each set to deal damage slightly below the medium and hard health levels, respectively.
func_door #1
  • Name: SkillDetect
  • Damage when blocked: 89
  • Wait before close: 1
func_door #2
  • Name: SkillDetect
  • Damage when blocked: 119
  • Wait before close: 1
trigger_auto
  • Target: SkillDetect
  • Flag: ☑️Remove on fire
Now, when the map starts, the doors will try to crush the monsters. In easy mode, both monsters will be crushed. In medium skill, the first monster lives. In hard skill, both survives. Their deaths sets the appropriate global state.

With the env_global's global states set, you can use trigger_auto in the rest of the campaign to killtarget (i.e. remove from the level) monsters that should only appear in medium or hard skills.
trigger_auto
  • Global state to read: SkillBelowMedium
  • KillTarget: (entities that should appear in medium skill and above)
  • Flag: ☑️Remove on fire
trigger_auto
  • Global state to read: SkillBelowHard
  • KillTarget: (entities that should only appear in hard skill)
  • Flag: ☑️Remove on fire
  • Having the setup as global states makes this setup unaffected by players manually changing the skill level partway through the campaign via console commands (which they shouldn't be doing anyway.)
  • The global states reset if a map has "New Level Unit" set to yes. If you need to set a new level unit, you need to add the setup again.

Advanced Half-Life FGD

This FGD implements plenty of dummied out features from Half-Life, such as Dynamic Lights, the inclusion of new unused entities like item_sodacan or grenade. Keep in mind, this FGD is meant for Hammer. Other programs, like J.A.C.K. editor, can't really handle some features, and cause bugs.
Loading embedded content: Vault Item #6618

Models

Different models for a single entity

You can make variations of pickups, monsters, etc. by adding a bodygroup to the model and adding the "body" property in a map editor:
PickupsPickups
MonstersMonsters
You can also add "limitless" body/skingroups to entities that already use them like HECU grunts or scientists. I wrote "limitless" because there is still a limit on every model. According to The303.org, the limits are: A problem with this workaround is that entities that already work with bodygroups and skingroups (like the scientists and security guards) can't have more groups added, since this will break the models.
An example can be found here, by UrbaNebula.
Loading embedded content: Vault Item #6409
monster_ entity warning
Many monster_ entities programmatically override body and skin keyvalues. For example, a monster_barney with non-standard body groups will break, and so would monster_scientist with non-standard bodygroups and skins, and monster_houndeye with their skin values being used to blink their eyes, among others. You can consult the Reference: Entities and their models page for whether custom body and skin groups are supported by any particular entity in the vanilla codebase.

Add/remove melee/ranged attacks or abilities of monsters

In GoldSource, the models themselves can contain a lot of metadata that defines generic as well as specific abilities and behaviours. The relevant ones for this section are the ACTs that can be assigned to sequences/animations. For example, barneys and scientists have walking and running ACTs, alien slaves have ranged and melee attack ACTs, etc.

By adding or removing the ACT tags on the model sequences, one can add or remove the monsters' defined abilities. As an example, the frankenstein monsters in They Hunger is a modified alien grunt with their range (hivehand) attacks removed.

This zombie model has added sequences and ACTs that makes zombie able to be snared by barnacles.
Using a mod base (e.g. Spirit of Half-Life or Featureful) that allows per-entity custom model gives you more flexibility in exploiting this method. For example, making some scientists hostile to players by placing monster_zombies with a scientist model, turns the scientist's healing animation into a melee attack that administers poison.

Misc

(LEGACY) Different launch screen

NOTE: This may no longer work, as the game got an update for the 25 anniversary. If you really want to use this feature, consider switching to steam_legacy version.
TODO: update information on usage for HL25 branch.
Half-Life uses a single background image both for the menu and when the game is loading. However, you can add different backgrounds for the loading screen and menu.
Main MenuMain Menu
Loading screenLoading screen
This can be achieved by modifying the file YourMod/resource/BackgroundLoadingLayout.txt, and adding your new background files. A recommended way to organize the backgrounds is putting the files in different folders, rather than putting everything in the background folder:
ImageImage

6 Comments

Commented 1 year ago2023-08-13 10:23:31 UTC Comment #105469
All of the features shown in Mapping are included in Advanced Half-Life FGD by default. Thus, there is no need to turn off the Smart Edit and add relevant keyvalues every time.
User posted image
User posted image
Even scientists with different pitches using the same head bodygroup can be created.
User posted image
Body and Skin keys are already included as long as they are supported on almost most monster_* entities.
User posted image
All func_* entities include the effects key by default.
User posted image
User posted image
Of course, this FGD did not become popular because it didn't get enough attention, and everyone is still making maps with the outdated Half-Life FGD.
Commented 1 year ago2023-08-13 21:38:29 UTC Comment #105470
User posted image
Commented 1 year ago2023-08-14 05:00:13 UTC Comment #105471
我曾经在ka_airtrain里使用func_conveyor的时候,也出现了光照效果,即使我并未有任何非常规操作。
When I once used the func_conveyor entity in ka_airtrain, it also compiled with a light effect in the map, even though I didn't do anything unconventional with it.
Commented 1 year ago2023-08-14 21:05:10 UTC Comment #105472
Interesting, could you please elaborate further?
Commented 1 year ago2023-08-15 10:14:12 UTC Comment #105476
很难说,因为这个实体很久以前就被我删除了。
It's hard to say because this entity was deleted by me a long time ago.
Commented 1 year ago2023-09-04 16:41:42 UTC Comment #105523
Here to clarify what "loading screen" means. It's the moment after the game entered fullscreen and before the main menu shows. It's actually not the time when the maps are loading (I tested) so you're actually only seeing it for a fraction of a second tops. No wonder nobody bothers making a custom one.

You must log in to post a comment. You can login or register a new account.