Team Fortress 2

Team Fortress 2

123 ratings
MvM Map Creation
By Mince and 1 collaborators
A comprehensive guide to the various logic seen in MvM maps, as well as navigation mesh and population file creation.
   
Award
Favorite
Favorited
Unfavorite
Preface
I am by no means an expert on the topics that will be presented shortly; rather this guide is a culmination of my personal experience of making MvM maps, along with information I have acquired from other guides on Steam or the Internet, and the Potato's Custom MvM discord. This guide will go over how and when to use the various logic and brush entities that typically make up MvM maps (as well as less common ones), how to create a proper .nav file, and the creation of missions (.pop files). You need all three for a functional MvM Map.

With that said, there are a few things that this guide is not:
  • It is not a guide on how to use Hammer; you are assumed to have basic knowledge of it's usage.
  • It is not a guide on how to design your MvM map, it only covers the logic and files required for a functional map.
  • It will not directly go over the creation of popfiles, as there are plenty of guides that already do this perfectly well, rather, it will point you to those guides which you can learn a great deal from.

I should also mention the map creation process and what is expected of you to have already done before you start with this guide. Generally, the creation of a map goes a little like this:
  • Draw the layout of your map on paper or in an image editing software, list features you want to implement and or how you plan on implementing them.
  • Create the basic layout in Hammer, don't focus on detailing your map.
  • Create the logic of the map.
  • Create a navigation mesh.
  • Create or re-purpose a popfile to be used for testing your map layout.
  • Test your map until you've verified the layout has no gameplay issues. Modify the layout or logic as needed. Don't forget to update your navigation mesh if you change your map layout.
  • Detail your finalized layout.
  • Modify your navigation mesh if appropriate.
  • Create an actual non testing mission for your map, or get someone else to create one.

This guide assumes you already have your basic map layout created in Hammer, and will help you complete the rest of the steps to finish your map by walking you through the logic you need, explaining how to create navigation meshes, and pointing you to resources for mission creation.

Again this is not a design or detailing guide for your map or your missions, it is to help people who are new to MvM mapping wrap their head around it's technical aspects. The design aspects are up to you.
Mission Creation Resources
Before you start mapping for MvM I recommend you at least have a basic knowledge of the syntax of popfiles and their relationship with the map. It'll help you in understanding why some things are the way they are when we go over the logic for the map.

Guides
Hydrogen's Titanium Tank Popfile Guide[hydrogen-mvm.github.io] Link deprecated
Hydrogen's Guide Re-uploaded[drive.google.com]
https://steamhost.cn/steamcommunity_com/sharedfiles/filedetails/?id=1451627628
https://steamhost.cn/steamcommunity_com/sharedfiles/filedetails/?id=1911160067
References
Sigsegv's KeyValue Documentation[gist.github.com]
https://wiki.teamfortress.com/wiki/List_of_item_attributes
https://wiki.alliedmods.net/Team_Fortress_2_Item_Definition_Indexes
Basic Map Logic
Regardless of what type of MvM map you want to create, whether it be gatebot or normal, multi-stage or multi-area, you're going to need a few pieces of logic that are essential to your map functioning properly.

Before we start with the logic make sure you have your layout created in Hammer already. I'll be using a modifed mvm_example for demonstration.

Let's start off easy, create these entities and place them somewhere obvious (for example near your hatch area).
  • tf_logic_mann_vs_machine
  • tf_gamerules
  • logic_auto
In general the place you pick should be where you place the majority of your point entity logic, but this isn't really a necessity. The only reason I say to do this is to make it easier on you and especially on anyone else who decompiles your map to look at it's logic. I don't know how many times I've scoured through a map and had to temporarily disable visgroups because some logic in the map was hidden inside of a brush somewhere at the edge of the map. (I'm being hyperbolic, but you get the idea.)
Basic Map Logic - Spawnrooms
Creating the spawnrooms for both teams is exactly the same as creating them in other gamemodes, just with a bit of MvM specific logic added on. I'm going to skim through the basics for the general spawnroom logic because it's something you can find in any basic TF2 Hammer Editor tutorial.
(And honestly something you should already know how to do if you know how to use Hammer.)

For both teams, create a func_respawnroom brush entity which spans each area that you want to act as a spawnroom. Give each one the proper Team KeyValue, and an appropriate name like respawnroom_red or respawnroom_blue. Don't forget to give each spawnroom a func_respawnroomvisualizer that has Solidity set to Always Solid and Associated Respawn Room set to whatever you named the func_respawnroom.

For red team, create at least 6 info_player_teamspawn entities across all your spawn rooms (so you don't have any players spawning at the same spawn point). Set their Team KeyValue to Red, and optionally give them a name like teamspawn_red.

Next you should create an upgrade station in each of your red spawnrooms. To do this, create a prop_static with the World Model models/props_mvm/mvm_upgrade_center.mdl. Place it wherever in your spawnroom. Once it's placed, select it if you haven't already (don't go into it's properties), and then in one of the grid views, shift drag it a short distance to copy the prop_static. (Make sure you let go of the left mouse button before you let go of shift, otherwise it will just move the original.) Move that copy back over to the same place as the other prop_static, then press Alt+Enter to open the object properties and change the World Model to models/props_mvm/mvm_upgrade_tools.mdl. Finally you need a func_upgradestation brush entity in front of those props to open the upgrade station GUI when players enter it. If you have multiple spawnrooms, you can group all of these entities (Ctrl+g) and copy paste that group to the other spawnrooms.

Keep in mind if you don't have atleast one func_upgradestation in your map, there's a bug where you aren't able to move after spawning unless you change classes or switch weapons.

If you want one of those rotating upgrade signs to go with your upgrade station, it's a prop_dynamic with these KeyValues:
  • World Model - models/props_mvm/mvm_upgrade_sign.mdl
  • Default Animation - idle
  • Disable Bone Followers - Yes

For blue team, the bomb is an item_teamflag with these KeyValues:
  • Game Type - Attack/Defend
  • Team - Blue
  • Model - models/props_td/atom_bomb.mdl

You want to place this item_teamflag in one of your blue spawnrooms, preferably in a portion where red can't see it. Aside from that it doesn't really matter where you put it as long as it's in a blue func_respawnroom, since the bomb is just teleported to the first bot to spawn. Make sure to give it a name such as intel.

The last thing you need to do concerning spawnrooms is create info_player_teamspawns for blue. You can have any number of them in each of your spawns, just try to make sure they aren't visible by red. Remember to set their Team KeyValue to Blue. What you name these spawn points is very important, as they're the names mission makers will need to reference when they want a bot to spawn in a certain bot spawn on the map. You're going to need to create a few types of spawn points depending on your map. (If you named every single spawn point the same then you wouldn't be able to have bots spawn in different locations.)

You should follow the established convention for spawn point names to give mission makers the easiest time making missions for your map. Here are some suggestions:
  • The spawn points in your main bot spawn (the spawnroom you consider to be the "main" one that will be most used) should be named spawnbot. Mission makers will generally use this when they don't have any specific place in mind for a WaveSpawn to spawn.
  • You should prefix any other spawn points with spawnbot, and name them based on their bot spawn's location in the map. (So a bot spawn that's to the left when faced at by red team would have spawn points named spawnbot_left) (Other examples: spawnbot_left, spawnbot_right, spawnbot_upper, spawnbot_lower) Though, if there is only one bot spawn other than the main one and it's not upper or lower, some maps use spawnbot_side for that bot spawn rather than spawnbot_left or right.
  • If you want Spies, Snipers, or Sentrybusters to spawn in a specific place, you can name a spawn point spawnbot_mission_spy, spawnbot_mission_sniper, or spawnbot_mission_sentrybuster respectively. Even if you don't have any specific place you want them to spawn, it's nice to include these to help support missions made for other maps.
  • You can also have what are called invasion spawns, you include at least one in each bot spawn, so if a mission maker uses it, bots from that WaveSpawn will spawn from all of the bot spawns instead of only one (they choose a random spawn point to spawn at). As you can guess you name them like this: spawnbot_invasion.


Here are most of the spawn points for this map to give you an example of what I've been talking about:

Main bot spawn
  • spawnbot
  • spawnbot_invasion
  • spawnbot_mission_sniper
  • spawnbot_mission_spy
  • spawnbot_mission_sentrybuster
Left bot spawn
  • spawnbot_side
  • spawnbot_invasion

What you should name gate spawn points is a little undefined, but generally I go with this:
Follow the same conventions as above, just instead of prefixing everything with spawnbot, you do it with spawnbot and the name of the gate. (Ex: spawnbot_gate01, spawnbot_gate02_left)

I generally don't include the mission spawn points if it's a gatebot map, spawnbot_gate01_mission_sniper is too much of a mouthful for my taste, and it would get tedious including all of those spawn points for each gate. I just do something like spawnbot_alt, spawnbot_gate01_alt, etc. You can also of course have invasion spawn points specifically for the bot spawns of a certain gate. (Ex. spawnbot_gate01_invasion)

As you can see a lot of this is pretty straightforward, but if you choose to ignore convention then at the very least be consistent with your naming scheme, the point of all this is to make it as easy as possible for people to make missions on your map.
Basic Map Logic - The Hatch
The next thing to create is the hatch so our puny robots are able to deploy the bomb. Before anything though, create a game_round_win point entity and place it wherever you placed your other point entities and name it bots_win.

We'll need that in just a bit, but for now lets worry about our hatch. Create a prop_dynamic and give it the World Model of the hatch you want (search for hatch or rottenburg), make sure you select the regular version of the hatch, not the destroyed one. Change Disable Shadows to Yes and Collisions to Not Solid. Name it hatch_prop, and place it where you would like your hatch to be.

Make sure the hatch edges aren't floating above the ground in one of the side grid views.
It's ok if it clips into the ground, just make sure it isn't fully beneath the brush.

With the metal hatch, this is fairly easy since it's straight and level, but for the other ones you need to make sure you can't see under the rocks or planks or whatever. (Some parts will be lower or higher than others.)

Now we need to work on clipping the floor brush below to create the hole for the hatch. I find it extremely useful (especially with the non-metal hatches) to use a reference brush. Create a brush and place it over the hatch, the edges of this brush will be where you plan on clipping the floor brush. Make sure you can see it from the top and make sure it clips or is flush with the floor brush.
The way you use this reference brush is you need to resize it to be within the edges of the hatch. (For the Rottenburg hatch that's inbetween those stones at the edge of the hatch) You shouldn't make it too big where the brush goes past the edge of the hatch, and you shouldn't make it too small where it's not inside the edges.

If you have the wooden hatch just make sure the brush is bigger than the bomb hole but smaller than the edges of the model.

Now all we need to do is select the floor brush and use the clipping tool along where we specified with the reference brush. Once that's done you can delete the reference brush. Now, select the brush that's been made in the middle of the hatch and delete it. If you clipped the floor in the right places, then all you need to do is create the brushwork for the pit below (make a small room), if not then adjust the clipping as necessary before you do.

With your regular hatch in place, we can work on the rest of the logic for the area. The first thing you're going to need is the destroyed version of your hatch that will play the animation of the hatch destruction when the bomb is deployed. To add this, copy the hatch prop we already have, and put this copy in the same position as the original. Change it's Name to hatch_prop_destroy and change Start Disabled to Yes.

The way this will work is hatch_prop will be visible as our hatch (since it starts enabled) until the bomb is deployed, once that happens, hatch_prop will be disabled, hatch_prop_destroy will be enabled and it's animation will be played, and then everything is reset once the map is reset for the new round.

Because we specified that the hatch shouldn't have collision, you need to create a clip brush (preferably blockbullets2) above the hatch like this:
The reason for this is to make it smoother for players moving over the hatch, if model collisions were enabled it would be quite bumpy with all those edges on the hatch.

To prevent pesky Engineers from building on our hatch, you should create a func_nobuild brush entity that covers it.

With the prop placement out of the way we can start working on the logic. To start off, lets create a brush entity that tells robots where their objective is. You want to create a brush over the bomb hole that mimics it's shape. So for example if the bomb hole is square like in the Rottenburg hatch, you should make a block above it, if it's a circle like the Mannhattan hatch, you should create a cylinder. You should make the brush 32 HU tall, and it should be flush with the top of the clip brush you created earlier. Make this brush a func_capturezone and set it's Team KeyValue to Blue.

Create a logic_relay named bomb_deploy_relay. We're going to specify what should be done when the bomb is deployed in it's outputs in just a second, first we need to hook up the func_capturezone to this relay. In your func_capturezone, create an OnCapTeam2 output which Triggers bomb_deploy_relay, this will trigger the relay when the bots deploy the bomb.

Go back to your bomb_deploy_relay, add these outputs:
Replace explode with whatever animation name you need for your model, explode only applies to the Rottenburg hatch. To view the animations for a model, you can view it in the Model tab in it's Object Properties.

This isn't the last time we'll be editing bomb_deploy_relay, we still have some logic to add for the hatch, so keep that in mind. First and foremost, we need to alert players when the bomb is nearing the hatch. This is very easily accomplished through the func_flagdetectionzone brush entity. You need to set the Team KeyValue to Blue and Alarm to Yes. When a bomb carrier enters this volume, an alarm will be played and the announcer will play voicelines. You should cover the general vicinity around the hatch area, and any other places where you want alarms.

The next thing to add is the explosion sound and the explosion particle. The sound is an ambient_generic named hatch_destroy_sound, and Sound Name set to MVM.BombExplodes. The particle is an info_particle_system named hatch_destroy_particle and Particle System Name set to mvm_hatch_destroy.

You should place the info_particle_system in the center of and level with the bomb hole, and you should place the ambient_generic slightly above that particle system, by around 24 HU or so. (This is so the sound is heard a little better.)
Add these ouputs to bomb_deploy_relay to support the new logic we just created:
Finally, the last bit of logic we need to add is to make the explosion... well, an explosion! Currently when the hatch explodes no one gets hurt from it, so let's fix that.

First, create a reasonably sized brush over the center of the hatch, and make it a trigger_hurt.
Set these KeyValues for the trigger_hurt:
  • Name - hatch_hurt_inner
  • Start Disabled - Yes
  • Damage Type - BURN (silent damage, does not ignite)
  • Damage - 9001
  • Zero Damage Force - Yes

Next, create another larger trigger_hurt around the hatch, this will be the outer reach of the killzone, so make it as big as you would like that to be. Give this one the same KeyValues as the previous, except name it hatch_hurt_outer.

The way these two will work together goes like this: When the hatch explodes, both of them are enabled, but after a short period of time, the outer trigger_hurt is disabled, leaving only the inner one to kill players. (This kind of simulates the explosion, and the fire hazard left over above the pit.) So to apply that to our map, you want to add these outputs to bomb_deploy_relay:
Basic Map Logic - The Bomb Path
Please keep in mind that the navigation mesh is a very important part of making some of the brush entities I'll be discussing in this section work properly. If you want to test your map before finishing this guide, I highly recommend you read the nav sections first.

Right now we have no way of telling players or bots where the bomb path is. Let's start with players first. The active bomb path is indicated to players through bomb path holograms, you've no doubt seen them by now. To create one, you need 2 props:

prop_static
  • World Model - models/props_mvm/hologram_projector.mdl
  • Collisions - Not Solid
  • Disable Shadows - Yes

prop_dynamic
  • World Model - models/props_mvm/robot_hologram.mdl
  • Disable Shadows - Yes
  • FX Color (R G B) - 138 187 247
  • Collisions - Not Solid
  • Disable Bone Followers - Yes
  • Start Disabled - Yes

Place the projector on the ground, and the hologram in the same spot. (The bottom of the hologram model should be aligned with the ground). Now you need to copy and paste these throughout your map to indicate bomb paths, generally at the very least you should put one at every change of direction in the path unless it's very obvious where bots will be going. You need to name your holograms based on the path they represent. (E.x. holograms_left, holograms_right)

Bot Pathing AI
Before I discuss how to make bots actually follow our bomb paths, I'd first like to take the time to go over how bots choose to get to their objective.

Every bot has an objective they want to accomplish (otherwise you would be fighting a bunch of training dummies). Here is the list of behaviors regular bots (non mission bots) can exhibit in MvM:
  • If no robot already has the bomb, and I'm not a gatebot, go fetch the bomb.
  • If I have the bomb, go deliver it to the hatch.
  • If another robot has the bomb, but has less than 4 bodyguards, and I'm not a gatebot, go escort the bomb carrier.
  • If another robot has the bomb, has 4 bodyguards, and I'm not a gatebot, go look for red players to kill.
  • If I have BehaviorModifiers Push and Attributes IgnoreFlag (I'm a gatebot), go to the hatch, then go look for red players to kill. (This is how gatebots function without prerequisites, I'll discuss this later in the gatebot logic section.)

What all of these behaviors have in common is they all have bots moving towards an objective of theirs (typically the hatch). How a bot chooses to get to their objective is based on what's called path cost. Obviously there isn't only going to be a single way to get to their destination, there will likely be multiple drastically different paths that they can take. As you can probably guess, bots will take the path with the lowest path cost. How path cost is calculated is a little more complicated than how long the path is, however.

There are a few class specific scenarios that apply as well, but in general, here are a few things that affect path cost:
  • If the robot has to jump to continue throughout the path, it's path cost is increased.
  • Distance increases the path cost. (Obviously)

Along with these, there are 2 brush entities that artificially modify path cost which enable you to control the flow of bots throughout your map.
  • func_nav_avoid
  • func_nav_prefer

It's very important to remember that these *modify* the path cost of a possible path. They don't order bots to obey them no matter what. You can think of them as recommendations, and just like recommendations, they can have different levels of strength.

Prefers are a light recommendation to bots, it decreases the path cost of a path by a decent margin, but it's not so much where bots will always follow it, there may be some times where bots ignore your suggestion because there's a much shorter path available to their objective.

Avoids are a much stronger recommendation to bots, it increases the path cost of a path by a considerable amount, but again (though it is highly unlikely considering how strong it is), there may come a time when bots will ignore this and barrel right through an avoid because the alternate paths are so astronomically out of their way that the path cost for the avoid is actually smaller.

Incidentally, if you've ever wondered why MvM maps use avoids for the bomb path instead of prefers, all of this is the reason why.

If you find that bots aren't taking the path you want, whether with prefer or avoid, you can simply add another prefer or avoid in the same spot as the first, they'll both be counted towards the path cost. Also, since avoids are stronger than prefers, if you happen to put both an avoid and a prefer in the same spot, as you can guess, bots will be inclined to avoid that path. (Though I'm unsure how many prefers it would take to cancel out an avoid, you shouldn't be mixing them regardless.)

Map Applications
Let's apply what we just discussed to our map now. The way avoids are applied to create bomb paths is every path gets a func_nav_avoid placed in it, and then when we want a specific bomb path, we disable the func_nav_avoid at that bomb path.

Here I've created 3 func_nav_avoid brush entities named nav_avoid_left, nav_avoid_middle, and nav_avoid_right respectively. (Team set to Blue and Tags set to bomb_carrier)

And that's it! You don't need to cover the entire path in an avoid, this could've even been implemented using a very thin brush (as long as the nav areas are within it, more on that in the nav section). The only problem would be if a bot gets airblasted past that brush, they would continue down that path because they're past the avoid, which is why it's a good idea to make the avoid a decent size.

Before we do anything though, since we're going to be enabling and disabling holograms and func_nav_avoids and what not, we should create some relays to clear everything back to the way we currently have it. This will make our logic cleaner.

Create a logic_relay named bombpath_clear_arrows_relay, and another named bombpath_clear_avoids_relay. You want to give them outputs to disable all holograms and enable all bomb path avoids respectively.

Next, let's create a relay to pick a random bomb path. Create a logic_relay and name it bombpath_choose_relay, after that create a logic_case and name it bombpath_choose_case. Your bombpath_choose_relay outputs should look like this:
The delay in the last output is important. You want the bomb path to be cleared before the relay starts picking another bomb path.

Once that's done, you should create logic_relays for each of your bomb paths, which need to enable holograms and disable the nav_avoid for that bomb path. (Example names: bombpath_left_relay, bombpath_right_relay) Here's my bombpath_left_relay:

Now you just need to add an OnCase output to bombpath_choose_case for each of your bomb path relays. Here's what mine looks like:
Basic Map Logic - Popfile Relays
You're almost there! There's still one last thing we need to cover before you have a basic MvM map.
Specifically, how a popfile interacts with the map. There are 3 different output blocks that mission makers can use in a popfile which execute during different parts of a wave:
  • InitWaveOutput - This is executed as soon as the wave is initialized. (Before setup time)
  • StartWaveOutput - This is executed as soon as the wave starts. (Bots start spawning)
  • DoneOutput - This is executed as soon as the wave ends, but before the next wave's initialization.

As the map maker, you can implement a different number of relays to be used with these output blocks. Keep in mind that you aren't obligated to provide them however, you should only make them if your map needs logic to be changed when that output is fired.

You should follow the relay naming convention for these outputs:

StartWaveOutput - wave_start_relay
DoneOutput - wave_finished_relay


InitWaveOutput's naming convention is out of the scope of this tutorial, as it's only used in multi-area gatebot maps, which as far as I know there are only 2 in existence (Doppler, Winterbridge), and are quite a notch up in difficulty to create.

For a basic map though, we only need logic on wave start and on wave end, so we need a wave_start_relay and a wave_finished_relay.

Go ahead and create those relays. We only need to add some logic concerning the bomb paths, since we never specified anything that uses bombpath_choose_relay.

You need an OnMapSpawn output for your logic_auto, and an OnTrigger output for your wave_finished_relay. Both should trigger bombpath_choose_relay. Finally, add an OnTrigger output to wave_start_relay that triggers bombpath_clear_arrows_relay.

What all of that does is makes it so a random path is chosen on map spawn and when a wave is finished, and the holograms are disabled during the wave so that they don't get in red team's way.

That's it for a basic MvM map! If that's all you want you can skip to the nav sections and finish the tutorial, but there are still some things you will probably be interested in adding to your map in the next section.
Misc. Map Logic
This section contains general logic you can apply to your map that isn't necessary for it to function, but still might be something that you want in your map.

Forward Upgrade Station
A forward upgrade station is simply an upgrade station near the bot spawns (the front) that disables on wave start. It's a very nice quality of life addition which makes it so players don't have to suicide or walk all the way back to spawn just to get upgrades (poor heavy).

To start off, create a regular upgrade station just like you did for red's spawnroom. Place it wherever you think would best benefit players, it should be clearly recognizable, but it shouldn't be in the way of gameplay. You also want to make sure you put it in an enclosed space (like a wall), this is because we're going to add a door to the front to visually signify to players that they can't access the upgrade station while the wave is running. (Be sure to leave a bit of extra space in the wall for the door when you place the upgrade station.)

Go ahead and create a func_door brush entity and place it however you need to in front of the upgrade station. You should know what to modify concerning func_door's KeyValues, so I won't bother with that, I'll just go over the outputs you need to make it work with the rest of the map. Be sure to give your door a name like forward_upgrade_door. You should also give the func_upgradestation a name, like forward_upgrade_trigger.

  • We want our door to open when the map starts. Add an OnMapSpawn output to logic_auto which sends the Open input to the door.
  • We want to disable our upgrade station when a wave starts, and we want to re-enable it when a wave ends.
    • For wave start, add an OnTrigger output to wave_start_relay which sends the Close input to your door, and another OnTrigger that sends the Disable input to your func_upgradestation brush entity.
    • You want to do the opposite for wave end. Add an OnTrigger to wave_finished_relay which Opens the door, and another OnTrigger which Enables the func_upgradestation.

Tank Logic
The way tanks function is quite simple and is very similar to payloads. As the mapper, all you need to do is create a path for the tank to follow using interconnected path_tracks. The popfile specifies which path_track the tank should start at, and the game appropriately spawns a tank_boss entity which follows the path you layed out!

The simplest setup is one tank spawn and one path. You just create path_tracks from that tank spawn all the way to the hatch. The height of the path_track off the ground doesn't really matter, since the tank moves using the navigation mesh (and as such won't work without one), the path_tracks are more of a reference for the tank as to where it should be going. (Note that how tanks move is determined by a combination of the world geometry along the path, the navigation mesh, and the path_tracks themselves. Also keep in mind that tanks ignore all clip brushes.)

You can easily link path_tracks together by using the shift drag copy method. The entity name will be incremented if there's a number in it (Ex. path_track_1 becomes path_track_2). The original path_track's Next Stop Target will also automatically be modified.

The distance between the the center of the last path_track and the center of the bomb hole should be 176 HU. (So the tank deploy animation goes to the center of the bomb hole.)

Sniper & Engineer Hints
Sniper and Engineer hints are what tell bots with Objective Sniper or Objective Engineer in the popfile where they should perform those class specific objectives. Sniper hints tell those bots where to stand and look at, while Engineer hints tell them where to build.

Sniper Hints
A Sniper hint is a func_tfbot_hint brush entity with the Team KeyValue set to Blue. You should only use these for Sniper hints, despite the presence of the Hint KeyValue. The size of this brush should be the general size of a player (I go with 64x64x96). Bots who use this hint will look at the origin of the brush entity (the purple sphere). You can move it around in the grid view to specify where you want them to look.
Keep in mind the actual size of a player is a bit smaller than that brush's size, so be considerate of that when moving the origin. If you want, you can create an info_player_teamspawn for a reference.

Engineer Hints
Engineer hints are a bit more complicated, they involve a couple different entities and demand a bit of logic between them and the rest of the map.

There are a minimum of two point entities you need: bot_hint_engineer_nest, and bot_hint_sentrygun. If you don't have both of these Engineers will just idle in spawn. The bot_hint_engineer_nest entity has multiple functionalities, first, it acts as a teleport destination for teleporting Engineers, and second it's the point from which the giant blue Engineer sky beam originates. You can also add a bot_hint_teleporter_exit to a nest, but it's optional.

To start off adding Engineer nests in our map, first let's create a nest that we'll copy paste into spots of our map. Go ahead and create a bot_hint_engineer_nest, bot_hint_sentrygun, and bot_hint_teleporter_exit, and set their Start Disabled KeyValue to Yes.

Now copy paste them throughout your map, you should place them with a bomb path in mind, since we're selectively going to enable them based on what bomb path is active later. Once that's done you can delete your temporary nest.

You should modify the placement of each entity in each nest to be where you want them to be specific to that location (move around the teleporter, sentrygun, etc). It's very important to keep in mind that the sentrygun hint in Hammer isn't indicative of where it's going to be looking in game, it's actually going to face the opposite direction. A good way to remember this is, if you select the hint and look at it's origin, the red line is the direction the sentry will face, because it's the direction red team will be coming from.

Since bot_hint_engineer_nest can essentially act as a spawnpoint for Engineers, you should raise it above the ground like you would with an info_player_teamspawn. You can have the sentrygun and teleporter flush with the ground though.

Now that we have nests around the map, we need to name our entities to function with some logic we need to add. You should name all of the hints in a single nest the same thing. You should also name a nest based on what bomb path it belongs to. I use this naming scheme: nest_<path name>_<number>. (Ex. nest_middle_1, nest_right_4, nest_left_2)

To only allow certain nests based on the active path, add an OnTrigger output to each of your bombpath path relays (Ex. bombpath_left_relay) that Enables the nests in that path.

You can do something like this to make it easy on yourself:
https://developer.valvesoftware.com/wiki/Wildcard If you don't know what that asterisk means.

All that's left is to Disable all the nests when a new bomb path is chosen to essentially reset the nests just like we did with the holograms and avoids. Add this output to bombpath_choose_relay:

Please note that teleporting engineers have very specific rules as to what nests they will spawn at. If your engineers aren't teleporting in and refuse to spawn when you have made sure that everything is correct, this is likely why.
Sigsegv has a full explanation on their teleport logic here: https://gist.github.com/sigsegv-mvm/a1f103ae79bbb0a5c5ff7dcd4a378958
Misc. Map Logic (Cont'd)
Flanker Bots
You may want bots that will flank around the main paths to take red by surprise. This is accomplished by giving bots a unique tag in the popfile, like bot_flanker, and adding a func_nav_prefer brush entity to the paths that you want them to follow.

Go ahead and cover the paths that you want with func_nav_prefers. Set their Team to Blue and Tags to bot_flanker. You should also give them a name based on what path they follow, for example nav_prefer_flank_left, nav_prefer_flank_topright, etc. This will give mission makers the ability to specify which flank path they want their bots to follow. (By using Tag nav_prefer_flank_left, etc)

Death Pits
There are a few things you need for death pits, here's an overview of things we're going to create:
  • A particle effect
  • An explosion sound
  • An env_shake
  • An entity to detect the bomb and trigger a relay.
  • An entity to reset the bomb
  • An entity to kill players

To start off, create these point entities and place them somewhere down in your death pit:

env_shake
  • Name - deathpit_shake
  • Amplitude - 7
  • Frequency - 200

ambient_generic
  • Name - deathpit_sound
  • Sound Name - MVM.BombResetExplode

info_particle_system
  • Name - deathpit_particle
  • Particle System Name - hightower_explosion

Next create a logic_relay called deathpit_relay, place it somewhere nearby. We'll add outputs to it in a second. We want to detect the bomb in the hatch and trigger this relay. Create a func_flagdetectionzone brush entity at some depth down in the deathpit that takes up the entire hole. Add an OnStartTouchFlag output to it that Triggers deathpit_relay.

To reset the bomb that falls down here, add a func_respawnflag brush entity in the death pit.

To kill anything that drops down here, add a trigger_hurt brush entity in the deathpit, set the Damage Type to FALL and Damage to any obscene number you can think of, set Zero Damage Force to Yes.

If you want to kill invulnerable players as well, (godmode, etc) add a trigger_multiple brush entity in the deathpit as well, set Delay Before Reset to 0, and add this output:

All that's left is to hook up the sound and other entities to deathpit_relay.

In deathpit_relay, add these outputs:
That's it for a death pit!

Useful Map Logic
Func_nobuild
While you should do this for any gamemode, I feel like it still deserves mention in this guide. Just like you did with the hatch, you should place func_nobuild brush entities in parts of your map where you don't want Engineers to build. For each part of your map you need to consider whether a sentry buster can access it. If a sentrygun is on top of a building somewhere a sentry buster can't reach, that's going to cause a few gameplay issues.

Spawnroom Money Collection
As a result of Scout's money collection magnet whirlpool, money can sometimes get stuck in blue spawnrooms with red unable to retrieve it. A very easy fix to this is to add trigger_hurts in those spawnrooms which will collect the money if it enters it's volume. To make sure it doesn't affect gameplay and only collects money, you should set it's Damage to 0 and Zero Damage Force to Yes.
Gatebot Map Logic
This section will walk you through the logic you need to add to a basic MvM map to make it a gatebot map. Please keep in mind that the navigation mesh has a heavy hand in making gatebots work properly, if you want to test your map before finishing this guide, I highly recommend reading the nav sections first.

Before anything, you should create a team_control_point_master in your designated logic area. This point entity manages the control points you'll create and controls their display on the HUD. Set it's Restrict team from winning KeyValue to Both. We'll modify some other KeyValues in the later sections.

Next you should create a point_populator_interface named pop_interface, and a tf_point_nav_interface named nav_interface. These point entities interface with the popfile and navigation mesh respectively, we'll need them later. You also need to create a filter_tf_bot_has_tag point entity named filter_gatebot and set it's Tags to bot_gatebot.

To start off, create spawnrooms for your gates if you haven't already. After that create an outline for the gate capture zone using overlays or a non-solid func_brush. Typically the texture props/hazardstrip001a is used. If your texture stretches in your overlay, turn off texture lock (tl at the top toolbar). Make sure Start Disabled is set to Yes for your gate spawn points, we don't want bots to spawn at them until their gates are captured.
Gatebot Map Logic - Prerequisites
Now we need a way to tell gatebots to go to our gates. This is accomplished using the func_nav_prerequisite brush entity. The way it works is if a bot wants to move through it's volume, that bot needs to do something first. In the case of gatebots, it tells them to move to a gate location entity. Before we add the prerequisites, we need to create that gate entity so we can reference it.

A gate location is represented by a named path_track. This will be the entity that gatebots move to. Generally you should place it in the center of the outlined capture zone you just made, and make sure it isn't inside of the ground. Go ahead and create a path_track at your first (or only) gate, and place it appropriately. Give it a name like gate01_location, and youre done with the location entity!

Now we need to create the prerequisites. You need to make a brush in each of your non-gate bot spawns. Any texture will do, but most maps give prerequisites the cp_manor/wallpaper01 texture to distinguish it from other logic. You should make sure that bots need to move through the brush to get out of spawn, generally you can simply put it in front of the spawnroom visualizer like this:
This can often be the simpler approach when it comes to complex spawnroom layouts, but it comes at the disadvantage of needing to make sure the nav mesh is split correctly in that spot (more on that in the nav sections).

If you don't want to worry about that, or your spawnroom is just one big square anyways, you can just cover the entire spawnroom floor with the prerequisite brush, instead of just the exit. Also keep in mind that the height of the brush has no effect on whether or not it will work, so you could have it 1 HU high if you so desired, but I obviously wouldn't recommend it.

Once you have your brushes, make them into func_nav_prerequisites with these KeyValues:
  • Name - gate01_prereq
  • Filter Name - filter_gatebot
  • Task - Move to Entity
  • Task Entity - gate01_location

Now gatebots will go to our first gate! They will continue on with their original objective once they reach it however, which was to push to the hatch. They will also take the shortest route to the gate, which may not be the one that we want. We'll make them go a specific path in a bit, since all you need for that is a func_nav_prefer, first let's focus on getting them to stay at our gate.

To get them to stick to the gate location, we need another prerequisite covering the gate capture zone area. What this effectively does is, once they reach the gate location, the prerequisite at the gate will continuously keep telling them to go back to the gate location once they reach it!

Go ahead and create the brush just like before, and give it the same KeyValues, except give it the name gate01_prereq_door.
With a proper nav mesh your bots should now stick to the gate! However, keep in mind there is a very small chance if a gatebot is airblasted off at the perfect time, they won't go back to the gate. If you want to add prerequisites around the gate just in case because of this you can, but it's not a requirement.

To make them follow the path you want, all you need is a func_nav_prefer with the KeyValues:
  • Name - nav_prefer_gate1_flank
  • Tags - bot_gatebot
  • Team - Blue

Unless your map requires you to name your gatebot prefers something specific, you should use that name, this is to allow gatebots in robot_gatebot.pop to follow your path. (They have Tag nav_prefer_gate1_flank)

If you have multiple gates, you should repeat this section for them as well. Keep in mind that you'll need to add the prerequisites in the previous gate spawn rather than the non-gate spawn you were working in earlier, since the gate spawn is where bots are going to be spawning once the gate is captured.
(Prerequisite at a gate spawn telling gatebots to go to the next gate location.)

Make sure to name your logic appropriately to each gate, the prerequisite in the image above for example would be named gate02_prereq, and the corresponding prerequisite at gate02 would be named gate02_prereq_door.

Similarly, you're going to want the func_nav_prefer to lead from one gate to another on the path that you want. However, you shouldn't change the name of this for each gate, the only reason it's named nav_prefer_gate1_flank, again is only to allow gatebots in robot_gatebot.pop to function in your map.

Also keep in mind, for any of these entities that you create that aren't for the first gate, you should set Start Disabled to yes, you don't want gatebots to think they can capture a later gate when they haven't even captured the first.
Gatebot Map Logic - Control Points
Now that our gatebots go to our gates the way we want and stay there, the next thing to do is to add the capture logic. First we need to create a control point to capture. Create a team_control_point point entity and place it near your gate, it doesn't matter where since it isn't going to be visible.
Give it these KeyValues:
  • Name - gate01_controlpoint
  • Print Name - Gate A
  • Default Owner - Red
  • BLUE Previous Required Point 1 - gate01_controlpoint
In the Flags tab at the top of Object Properties, check Start with model hidden and Disable Sounds.

Let's add the capture zone, create a trigger_timer_door brush entity of appropriate size. Give it these KeyValues:
  • Name - gate01_capturezone
  • Filter Name - filter_gatebot
  • Door Name - gate01_door
  • Control Point - gate01_controlpoint
  • Can RED Cap? - No

Change Time to cap (sec) to your liking, a good number to start at is 10. You may have noticed I mentioned gate01_door, we need to create this for the trigger_timer_door to function, it will be used to visually represent the cap time left.

Create a func_door and place it somewhere, name it gate01_door and modify it's KeyValues to your liking if necessary (Keep in mind modifying Speed is not necessary, as the speed of the door is controlled by trigger_timer_door). It's very important to make sure you check Toggle in it's Flags tab, otherwise the door will not open and close properly.

Create a logic_relay named gate01_relay, we'll add outputs to this in the Gate Relays section.

Let's add a few basic outputs to gate01_capturezone:
gate01_relay will contain everything that we want to happen when the gate is captured.

The last thing to add is a gate alarm to be played when gatebots are close to the gate. Create these point entities:

logic_timer
  • Name - gate01_alarm_timer
  • Start Disabled - Yes
  • Refire Interval - 1.25

ambient_generic
  • Name - gate01_alarm_sound
  • Sound Name - mvm.cpoint_alarm
  • Max Audible Distance - 8000

You should change the Max Audible Distance for the sound to cover your entire map, however 8000 should be enough for most map sizes. Give the logic_timer an OnTimer output that sends the PlaySound input to gate01_alarm_sound. This timer will play the gate alarm sound every 1.25 seconds when it's enabled.

Now we need a brush entity to enable the timer when gatebots enter it. Create a trigger_multiple as large as you want near your gate, and give it these KeyValues:
  • Name - gate01_alarm_trigger
  • Filter Name - filter_gatebot
  • Delay Before Reset - 0
And give it these outputs:
And that's it for the alarm!

Just as in the Prerequisite section, if you have multiple gates, you should repeat this section for them as well. Make sure that the BLUE Previous Required Point reflects which gate it is in the map.

For example, if you're doing the logic for the second gate, and the second gate requires the first to be captured, you would set BLUE Previous Required Point 1 to gate01_controlpoint. If you're doing logic for say, a third gate, and it requires both previous gates to be captured, you would set BLUE Previous Required Point 1 to gate02_controlpoint and BLUE Previous Required Point 2 to gate01_controlpoint. You should also increment the Index KeyValue for each new gate.

Also keep in mind, for any of these entities that you create that aren't for the first gate, you should set Start Disabled to yes (except for the team_control_point, it should stay enabled), you don't want gatebots to think they can capture a later gate when they haven't even captured the first.

One last thing, you should make sure that the control points are layed out how you want them in your team_control_point_master. The Cap Layout KeyValue explains itself sufficiently in Hammer. You can also change the cap layout position on the screen using the appropriate KeyValues. The x and y values start at the top left of the screen with 0, 0.
Gatebot Map Logic - Misc.
There are few pieces of logic that we need to add before we work on the gate relays.

Right now gatebots that cap our gates can accidentally walk inside the gate spawnroom, so we need a spawnroom bot blocker while the gate is uncapped. This can be accomplished by creating a func_brush with the nodraw texture covering the door hole (basically in the same spot as the respawnroom visualizer). Give it these KeyValues:
  • Name - gate01_botblocker
  • Disable Shadows - Yes

Also, for every gate / area in our map, if there's another gate to capture after it, we're going to need entities to teleport bots from the previous spawnrooms to the new active gate spawnrooms. For example, say you have a map with 2 gates + the non-gate regular bot spawns. When gate01 is captured, we need to have entities to teleport any bots in the non-gate spawnrooms to gate01. When gate02 is captured, we need to have entities to teleport any bots in the gate01 spawnrooms to gate02. Gate02 doesn't have anything after it, so we don't need to do anything with it.

The reason for all of this is to prevent bots from lagging behind or getting stuck in the previous gate / area spawnrooms. To implement this, we're going to need a trigger_teleport brush entity in the spawnrooms we want bots to be teleported from, and an info_teleport_destination point entity in the spawnroom where we want them to be teleported to.

So using the previous example, the non-gate spawnrooms will have a trigger_teleport that links to an info_teleport_destination in gate01, gate01's spawnrooms will have a trigger_teleport that links to an info_teleport_destination in gate02. Gate02 won't have a trigger_teleport since there isn't anything after it to teleport to.

To implement this for your first gate, create a trigger_teleport brush entity that covers all of your regular non-gate spawn rooms and give it these KeyValues:
  • Name - gate01_teleport
  • Start Disabled - Yes
  • Filter Name - filter_blue
  • Remote Destination - gate01_teleport_destination
Make sure to check Clients in the Flags tab, otherwise it won't teleport anything.

Next create an info_teleport_destination in the main gate01 spawnroom with the name gate01_teleport_destination.

And that's it for the teleport logic! We'll enable the trigger_teleport in the next section with the gate relay. If you have any other gates, you should create these entities for them as well.

The other two things we need to create are the entities to stun bots on gate capture and give bots crits when the gate stun break ends. They both are implemented using trigger_add_tf_player_condition. Before we create those brush entities however, we need to create a few filters, since we only want to affect small bots, not giants or sentrybusters.

If you don't already have a filter for blue team, create a filter_activator_tfteam point entity named filter_blue with Team set to Blue. Next create these point entities:

filter_tf_bot_has_tag
  • Name - filter_giant_false
  • Filter mode - Disallow entities that match criteria
  • Tags - bot_giant

filter_tf_bot_has_tag
  • Name - filter_sentrybuster_false
  • Filter mode - Disallow entities that match criteria
  • Tags - bot_sentrybuster

filter_multi
  • Name - filter_smallbot
  • Filter 1 - filter_blue
  • Filter 2 - filter_giant_false
  • Filter 3 - filter_sentrybuster_false

Now, create these brush entities and have them cover the entire map:

trigger_add_tf_player_condition
  • Name - bot_condition_stun
  • Start Disabled - Yes
  • Filter Name - filter_smallbot
  • Condition - TF_COND_MVM_BOT_STUN_RADIOWAVE
  • Duration - 22

trigger_add_tf_player_condition
  • Name - bot_condition_critboost
  • Start Disabled - Yes
  • Filter Name - filter_smallbot
  • Condition - TF_COND_CRITBOOSTED
  • Duration - 10

The stun sound that will be played when the gate is captured is an ambient_generic with the KeyValues:
  • Name - gate01_stun_sound
  • Sound Name - mvm_robo_stun.wav
You should make a copy of this sound for each gate (with the appropriately changed name) and place them near that gate.
Gatebot Map Logic - Gate Relays
Gate relays are the relays that will be activated when your gates get captured, they typically disable the logic for the captured gate and enable any necessary logic for the next gate.

There are a lot of things we need to add to a gate relay, it can be overwhelming to look at it all at once, so I'll go through it in digestible pieces instead of showing you the entire thing.

Let's start modifying the gate relay for your first gate. We should start by pausing bot spawning. Add this to gate01_relay:

Next, we need to disable the logic related to the capture of this gate (prerequisites, the capturezone) since it's been captured. You can do that through these outputs:
You might be confused about the output to the door, since we already opened it didn't we? The thing with disabling the capturezone however, is right before it's fully disabled, it calls the OnEndTouchAll output, which in our trigger_timer_door was set to *close* the door. This is why reopening it after a short delay is necessary.

Next, we should enable our gate spawn points, since they're currently disabled. We should also disable the alarm trigger and play the gate stun sound. (We'll get to the stun and critboost triggers in a bit.)
Your new outputs should look similar to this (you'll probably have more gate spawns):
We also need to disable the gate bot blocker and enable the trigger_teleport to teleport bots inside the previous spawnrooms to this one.
Keep in mind we disable gate01_teleport shortly after since the teleporting is already done once it's enabled.

Now that we're done with the outputs for the logic pertaining to this gate, we can add any outputs we need for the other gates / areas.

The next thing to do is disable the spawn points of the previous gate / area, since we don't want bots to spawn there anymore.

We should also enable the capture logic for the next gate if it exists. (Enable the prerequisites, capturezone, and alarm trigger.)

To stun bots, add these outputs:

You also need to ForceReset your active bombs. If you have gate bombs that you want to be active after the gate is captured, you can enable those as well.

If there aren't any other gates after this one, you need to change the attributes of all gatebots to RevertGateBotsBehavior. You can do this through these outputs:
This makes them behave like regular bots again once this last gate is capped.

Finally, there are only a few things left that we need to add.
All this does is recompute some things for the nav mesh, unpause bot spawning, and enables crits for the bots.

And you're done with your first gate relay! Go ahead and repeat this section for any other gate relays in your map.
Gatebot Map Logic - wave_reset_relay
With so much logic being changed and modified throughout the map, you're going to need a way to reset it all when you move to a new wave, this is where wave_reset_relay comes in. This is a relay you'll trigger in wave_finished_relay which resets any map logic that was changed mid wave to the state it was in before the wave.

In general though, you should reset any logic that you modified in the gate relays to the state it was in originally. For example, if you Enabled an item_teamflag in gate01_relay, you should Disable it in wave_reset_relay. It's also important that you send the CancelPending input to your gate relays. This is to stop any of their pending I/O if the wave happens to end before they're done sending outputs.

Here's what my 1 gate example map's wave_reset_relay looks like:
If I had 2 gates I would disable most of gate02's entities as well, make sure it's owner is correct, etc etc.

Once you've created your wave_reset_relay, all that's left is to Trigger it in your wave_finished_relay.
Nav Creation
Once you're ready to start testing your map with bots, you're going to need a navigation mesh, which is what tells bots how to traverse your map. Contrary to popular belief, typing nav_generate in console and calling it a day will not net you a good navigation mesh. It may be functional (rarely), but as it is auto generated, there are bound to be mistakes made. Therefore instead of solely relying on the command to do all the work flawlessly, you're going to have to put in a little extra work for a quality navigation mesh.

Quick note before we start, when I say to enter a command in console, don't include the quotations when you type or paste it in. Also keep in mind that if at any point before the end of this nav tutorial you want to test your nav mesh, you need to type "nav_analyze" in console first. Otherwise bots will behave improperly, this is because nav_analyze, as it's name suggests, analyzes the nav mesh to give bots useful information for use in their planning and attention systems.

To start off, load your map and type "sv_cheats 1; nav_generate" in console, and wait for it to finish. Once the map has been reloaded and you're back in-game, type "nav_edit 1" in console. You should now see a bunch of boxes on the ground that nav_generate created for you. These are called nav areas and they're what bots will use to walk around your map.

As you can see nav_generate has done most of the grunt work for us. The nav mesh may look complete to some of you, especially since a lot of nav creation guides stop here, but there's still quite a bit of work on our part that we need to do for this nav mesh to not have any problems down the road.

Speaking of problems, the first one people tend to run into at this point is some areas not being generated in places where they should be. Like in this bot spawn for example:

This happens a lot on raised areas, which also tend to be bot spawn cliffs. And when people stop at nav_generate and wonder why their bots don't work, most of the time it's because of this very common and very simple to fix problem.

To fix this we can use another nav generation command: nav_generate_incremental. What this does is it generates nav areas outward from seed positions defined by nav_mark_walkable. (Whereas nav_generate creates a completely new nav mesh from scratch.)

So to demonstrate, first we need to mark the ground where we want the nav areas to be with the console command nav_mark_walkable. Once that's done you should see a purple wireframe pyramid like this one:

This is our seed position. Now we just type nav_generate_incremental in console, wait for it to do it's thing, and now we have nav areas specifically in this area!


Right now all the nav areas that have been generated are selected, to clear the selection you can type nav_select_invalid_areas in console.

If you have multiple areas that need nav areas, you can use nav_mark_walkable to define multiple seed positions, and then use nav_generate_incremental to create them all at once. If you ever mess up and use nav_mark_walkable in some place you didn't intend, you can type nav_clear_walkable_marks in console to clear all your marks.

If there are any other areas in your map that don't have nav areas where bots can walk, go ahead and fix them now.

Just as there may be nav areas that you need to add, there may also be some that you need to remove. You want to make sure that any areas of your map that won't be visited by bots don't have any nav areas. For example at the bottom of a cliff, or maybe in a secret room.

Here's the cliff example I just mentioned:

Obviously we don't want bots to think they can walk down here, so we need to remove these nav areas. The simple solution is to delete every nav area individually using nav_mark to select one or more, and nav_delete to delete them. You can also just use nav_delete, if you have nothing selected it'll delete the nav area under your crosshair. Either way that would take forever and I doubt either of us have that kind of time. Luckily there's a much easier solution.

First we need to nav_mark an area, and then we can use nav_flood_select. What this does is it selects all areas that are connected to the marked area, then selects all the areas that are connected to each of those, and so on until there are no more connections. Because of this we need to make sure the area we want to select is *isolated*, otherwise we might end up selecting the entire navigation mesh, like this:

If you're wondering what connections are, they're these light or dark blue lines you see linking nav areas, they allow bots to move between each area.


A light blue line means it's a bi-directional or 2-way connection, meaning bots can move between them freely as they wish. A dark blue line means it's a uni-directional or 1-way connection, bots can only move from the nav area defined first in the connection to the other, not the other way around. We'll cover this more in the next section, but for now just know that they're what link nav areas together.

Back to our unwanted nav areas, if nav_flood_select marks more than that portion of the nav mesh, you need to find what is linking it to the rest of the nav mesh. Here's mine:

So to fix this just delete the nav area(s) in the unwanted area connecting it to the rest of the map, then we can use nav_flood_select without trouble. (nav_mark, nav_flood_select, nav_delete)

No more connection after the bottom area has been deleted:

If there are any other unwanted nav areas in your map, use which ever method you find most convenient to delete them.
Fine-Tuning Your Nav
Now that you have a basic navigation mesh created, we can work on modifying it to make it function as best it can.

Keep in mind that to work through this section you're going to need the .vmf for the map you're on, so if the map isn't yours or you don't have the .vmf, you need to decompile it using something like BSPSource.

Some of the brush entities in our map require the navigation mesh to function properly. These include func_nav_prefers, avoids, prerequisites, and func_respawnrooms. The way these brush entities and the nav mesh interact goes like this: Only nav areas that are fully within the brush entity's volume will act as that brush entity. In other words bots pay attention to the nav areas, not the brush entities.

So for example, in Mannhattan, this red dev brush is a func_nav_avoid:

Only nav areas completely within that volume will act as nav_avoids, any that are partially contained won't. Func_respawnrooms act mostly the same, they only gives bots uber spawn protection if the nav area they're standing on is completely within the brush entity volume.

What all of this means is that you need to split your nav areas using nav_split to conform to your map side brush entities. If you have a bunch of large nav areas that are larger than those brushes, then bots aren't going to behave like you expect them to. Luckily fixing this is very easy, you just need to split the nav areas near your prefers, avoids, etc to be small enough for them to function properly.

You might ask the question: "Well why don't you just split the entire nav mesh into tiny pieces to not worry about this?" There are a few reasons, the first is it increases the .nav file size by a considerable margin, and the second is it reduces game performance on your map. This is because of the calculations bots need to do to move around. More nav areas means more calculations. Again you're just trying to get the nav areas in the brush entities mentioned earlier just small enough to function correctly, but you don't want too many as to cause performance issues. The only exception is func_nav_prerequisites at gates, you should split them a bit differently. We'll go over all of that in a bit.

For now, you should split (appropriately) the nav areas within all the prefers, avoids, and respawnrooms in your map.

The splitting doesn't have to be perfect, it just needs to get the job done. If you're super worried about getting it perfect, you can compile the map with a temporary reference brush above where the brush entity is in Hammer, and just remove it when you're done with splitting the nav.

Also if you ever split something you didn't want to, you can nav_mark one area, and then type nav_merge while looking at another to merge them together.

Once you're done with that the only brush entities left are the gate prerequisites. But before we do that, I think it's best to build on what I explained in the earlier Gatebot map logic section about how prerequisites keep gatebots glued to the point. I'm going to start from the beginning however, just in case you haven't read that section.

The way prerequisites work is if a bot wants to move through prerequisite nav areas, they need to do something first, in the case of gatebots, they need to go to a gate location entity (A path_track with a name). Once they reach the location, they wait a second or two, then continue with their original objective, which was to push to the hatch, and then they get snagged again by the prerequisite at the gate which tells them to go back to the gate location, rinse repeat.

A small but very important detail is this: Once a gatebot reaches the location, they ignore the ENTIRE nav area under their feet, atleast in terms of prerequisites. What this means is they won't go back to the gate location until they reach an entirely new prerequisite nav area which tells them to do so. Because of this bit of information you can split the nav areas at a gate a bit more intelligently than splitting them willy nilly until it works like most guides say to do. Though this isn't necessarily a wrong way of doing it, if you need to get a nav mesh into production quickly it's a very valid way to do things. But personally I value knowing how bots behave using the nav areas to create a more professional looking nav mesh.

So to split your nav areas keeping that information in mind, all we need to do is make sure that there's a relatively small nav area directly under the gate location entity, and that intial nav area has prerequisite nav areas around it.


The larger you make this initial area, the less the gatebots will try and clump up, so keep that in mind if they're too close together. Make sure to split the nav areas near the edge of the prerequisite brush if necessary so that they fit within.

Keep in mind you should only do this for the nav areas within the prerequisites at the gates, the prerequisites in the bot spawns that you use to originally make gatebots go to those gates should be split normally like the other brush entities.

That's it for splitting the nav mesh for brush entities. The final thing left to do for splitting the nav mesh in general is to make sure that there are no parts of your nav that hover above or go into the ground too much. Here are some examples:

It won't be the end of the world if the nav areas are off by a little bit, but the further it deviates the greater the chance that bots will have trouble navigating that area, plus it just looks plain terrible. We can fix areas like these by using nav_corner_place_on_ground, and nav_split if necessary. (If nav_corner_place_on_ground doesn't work initially, you need to split the area to be smaller, and then use that command.)


Now that we've split the nav mesh, we can start working on the connections using nav_connect and nav_disconnect. I'll start with showing you how to create connections. Let's create a 1-way connection first. You use these when you want a bot to go through some area, and not try to go back. Typically you're going to mostly use these for ledges that bots drop off which are high enough that they're unable to jump back up.

To create one, nav_mark the nav area you want to start with.

This is the one that bots will move from, the second one will be the one bots will move to. To complete the connection look at the second nav area and type nav_connect in console. Now the ledge has a 1-way connection to the ground below.


To create a 2-way connection, you start with the same thing, and then do it again in reverse to define an opposite connection. So let's use this small ledge as an example.

I define a 1-way connection just like before,

and then I define another 1-way connection in the opposite direction of the first. And they both combine into a 2-way connection.

This indicates to bots that they can either jump up the ledge or go down it.

If you want to delete a connection, nav_mark a nav area, and then point to another and type nav_disconnect in console. It doesn't matter which you mark first or what connection type exists between the two, if a connection exists it'll be removed.
Fine-Tuning Your Nav (Cont'd)
Now that you know how to create and delete connections, we can work on the connections for the map. The way I tend to go about it is I delete every connection in the map, and then manually create my own. I do this because personally it helps me differentiate between connections I've made and know are correct, and ones that nav_generate made, which I don't know are correct. This basically makes it impossible to not notice a bad connection created by nav_generate, since they've all been deleted. You don't have to do it this way, by all means if you can remember which ones are yours and which ones you've verified you can modify the connections however you would like, but I'm going to demonstrate it my way.

Keep in mind when I say delete all connections I mean delete ledge connections (like the ones you just saw), not ground connections (the majority of connections in the nav mesh which are on the ground).

Go ahead and delete any ledge connections you see in the map.

Now that all the ledge connections are deleted, we can get started on creating our own. Keep in mind that you shouldn't create ledge connections on every single ledge you see, only in places where you think bots need a path somewhere. When you do get started there are a few things you should be considerate of. When you're making connections for ledges, you want to make sure the connection doesn't go straight down through a brush. A little bit of clipping is inevitable, but if you have something like this:

Bots will try and move through the floor when they try and go down the ledge, because that's essentially what you've told them they could do. Don't be afraid to split or merge a few areas at the bottom of the ledge to create a proper connection. You want to be able to see both areas from a top view, if the bottom area is under the ledge, you should probably move the connection over a bit more.


Another thing to watch out for when doing ledge connections are walls or fences that border it, or really anything that bots need to jump over before going down.

You don't want to create a connection through the wall down below, that will just cause problems for the bots. What you need to do instead is create a connection to the top of the wall, and *then* a connection to the ground below. However, typically with small walls, nav areas usually aren't generated on top of them, so you're going to need to create your own nav areas manually.

To create a nav area, you use nav_begin_area with your crosshair pointed at a corner where you want the area to start, and nav_end_area at another opposite corner where you want it to end. Dont worry if it's not perfect, just make sure the nav area doesn't go *over* the edge. And then you can give your newly created area the proper connections.

If you have a wall that isn't at a 90 degree angle, you won't be able to create one nav area that spans the entire wall, since you aren't able to rotate nav areas or create them at angles. What you need to do is create nav areas as large as you can at points where you want bots to jump up the wall. You shouldn't cover the entire wall with them, especially since you need to give each one connections, just give your wall a reasonable amount. You might need to split the nav at the ground near them since the connections are going to be at an angle, and they tend to be picky about who they wanna connect with.

And that's it for editing the nav!

IMPORTANT : Now type nav_analyze in console to finalize your nav mesh for bot usage.
Conclusion
If you've read through the entire guide, congratulations! You're now armed with enough knowledge to continue and see your map through to it's own conclusion! Keep in mind that this guide, despite it's length, is not an exhaustive explanation of every single thing you can possibly do in MvM mapping, there's always something else cool or interesting you can add to your map!

What Happens Now?
Now? *chuckles* Let's go practice medi- Er Whoops, wrong script.

Now that you have a functional map, you should start testing your map in public or private servers with a mission, any old mission will do, even ones from other maps as long as you convert their outputs, spawn point names, etc to conform to your map logic. You're likely not going to get your layout right the first time, so be prepared to edit the layout and modify your navigation mesh as needed.

Once you do finally get your map to play well, you should start detailing your map to start looking how you envisioned it. Whether your map is an out of this world space theme or a more humble badlands theme, go wild with your imagination! Just be smart about your detailing. Here are some useful guides:

https://tf2maps.net/threads/nodraw-density-of-detailing.14713/
https://tf2maps.net/threads/guide-composition-and-you.23566/

After that you just need to modify your navigation mesh to conform to your detailing if needed and get a proper mission created for your map!

Good luck in your MvM adventures!
25 Comments
Archie 18 May @ 3:12am 
why does upgrades don't apply for me
Jonas 24 Sep, 2024 @ 9:37pm 
Just say what i have to create and what outputs okay, for a single hologram
Jonas 23 Sep, 2024 @ 9:20am 
My head is so confused you have to tell how
Mince  [author] 23 Sep, 2024 @ 8:47am 
So change the outputs to target the other paths if you only want one remaining. For the tank you need an OnBombDroppedOutput in the Tank spawner in the popfile. One of the popfile guides listed should give you some examples.
Jonas 23 Sep, 2024 @ 2:53am 
I have 2 more issues. #1 When you was talking about the hologram, you only talked about 3, and you never said how to disable a single path hologram when the wave starts #2 then the tank arrives at the hatch and does all of its deply animation, when it drops the bomb, nothing happens
Mince  [author] 22 Sep, 2024 @ 8:03am 
Make sure the nav is made properly (nav mesh within bot spawns) and make sure the popfile has no errors. Check the console, the game will complain if either of these are the case.
Jonas 22 Sep, 2024 @ 12:02am 
Why when i play my mvm map, why is there no waves, how can i fix this?
Spectλtor™ 28 Jan, 2024 @ 12:25pm 
Great guide! Thx very much:btd6thumbsup:
2020 30 May, 2023 @ 11:40am 
hi, please tell me why when the control point is captured by robots, the game automatically ends the game with a victory
Amethyst 5 Nov, 2022 @ 2:47pm 
What's the best practice for having bots walk closer to the center of pathways? My bots really like to cut corners and walk dangerously close to ledges, and I'd love to have them feel a bit more natural.