Half-Life 2

Half-Life 2

Resevoir24
 This topic has been pinned, so it's probably important
JS21O3  [developer] 17 Feb @ 12:26pm
How the combat sequence was made.
A continuation of my reply to Gears' comment, considering the workshop's comment section has a 1000 character limit.

Originally posted by Gears:
Really enjoyed this, where could I grab the vmf file to look at how you made it?

Originally posted by JS21O3:
Hey Gears, I currently don't have any plans to publicly release the .vmf files of my levels. However, I am happy to answer any questions you might have on how some parts were built. The best way is to ask your questions is by making a new thread using the discussions tab, since the Workshop does not let me reply to specific comments directly.

Here are a few key elements that make the level work: The main combat sequence is handled using npc_enemyfinder and aiscripted_sequence entities, the documentation for which one can find on the Valve Developer Community wiki. These entities are used to make the Combine run out of their spawn and rush towards the player.
These aiscripted_sequence entities are synchronized to the music played by an ambient_generic entity, starting their schedule multiple times during #music/hl1_song10.mp3 (a.k.a Lambda Core/Diabolical Adrenaline Guitar). A logic_relay entity helps to keep this scripting and timing manageable.

There are effectively four "waves" of Combine soldiers, about 20 in total, with a maximum of six active at a time across two different squads and three spawners. The two Combine scanners which appear near the end of the combat sequence are also part of these squads.
Both squads have their own set of npc_enemyfinder entities which help to spread them around the level. These are set up in such a way to force the Combine soldiers to cross the central basin, with an aiscripted_sequence guiding nearby troops towards the lower platform (depicted prominently in the workshop thumbnail).
Also, whenever a Combine soldier is spawned in by an npc_template_maker I make it fire an "OnSpawnNPC !activator UpdateEnemyMemory !player" output so the soldiers are always made aware of the player.
These spawners are deactivated a few seconds before the music ends, at which point the logic_relay also triggers an autosave. The spawn limits prevent the combat from dragging on too long for slower players, but gives experienced players a nice fight. All soldiers have infinite grenades and ten of them are explicitly set not to drop their SMG.
There is only one shotgunner active at a time, with three spawning in total, whose tactical variant is set to keep pushing the player no matter what.

A simpler version of this setup is used for the first encounter with the Combine soldiers, which spawns four soldiers of the same squad in two waves using two npc_template_maker entities. Again, an aiscripted_sequence is used to make them charge into the building.
The Metrocops in the warehouse also use an aiscripted_sequence to charge the player in the control room, where a trigger_once is used to make them rush the player if the latter enters a cheap/boring camping spot. Do not pay attention to how the vending machines magically disappear as well.
This is done in addition to both the Manhacks and Combine camera being part of their squad, so the player has little chance to hide from them.

Turrets are tricked into firing "at" players behind cover using npc_bullseye and ai_relationship entities whose relationship is enabled/disabled by entering/exiting a trigger_multiple volume placed behind the cover. It works well enough, but a player can easily break the illusion by knocking over the cover using the Gravity Gun or a grenade.
There is a trigger volume at the end which uses an entity filter to force the turrets to self-destruct if you try to take them to the airboat/use them against the last Zombine.

I found that attempting to control zombies/Zombines with either these entities or a path_corner was unreliable and inconsistent, so I tried to use scripted_sequence and invisible (toolsnpcclip) func_brush walls instead. But the zombies will often ignore these as well, which can be really annoying when you suddenly spot one wandering off behind an invisible wall.
Worse yet is when you catch one of them A-posing mid-air as they attempt to use a jump node or end up stuck behind a floating crate. I had to use a combination hint groups, info_node_link_controller, and logic_navigation to limit which NPC's can use what nodes and when props can be pushed out of the way.
In the end, I found that baiting zombies with an npc_bullseye was the most consistent approach for this level specifically. Also, the Zombine knocking on the window at the first switch? That scripted_sequence keeps restarting itself constantly because it tends to just fail for no good reason.