STEAM GROUP
Moffein's Maps Moff's Maps
STEAM GROUP
Moffein's Maps Moff's Maps
5
IN-GAME
36
ONLINE
Founded
October 14, 2015
Language
English
Location
United States 
ABOUT Moffein's Maps

Welcome!

I figured that it's about time I make a group for those who are interested in my maps. I'll post what I'm up to along with a few sneak peak screenshots here and there. If you have any questions about Hammer start a discussion here and let me know on my profile and I'll try to respond in a timely manner.

Map Releases
coop_uchi
coop_headquarters
coop_vault

Old Maps
coop_mission_railway
coop_mission_hotline
coop_mission_headquarters
coop_homeinvasion
POPULAR DISCUSSIONS
VIEW ALL (76)
RECENT ANNOUNCEMENTS
Spiritual successor to coop_mission_hotline
A final post - Headquarters/Vault/Uchi sourcefiles.
Nerd Stuff
If you're not into mapping, you can skip everything here.

Before I leave, I'd like to release the sourcefiles of my newer maps, so that others may learn how I did stuff like Classes and player reviving, and other stuff.
You can get them at: https://drive.google.com/file/d/1BgIOtHpiG8YYbNMHMOFQqZSEiPBXihxl/view?usp=sharing

The scripts/models/textures aren't included due to filesize bloat, but you can find out how to extract them from the map .bsp files with the included README.txt.

I'd REALLY like to see someone try and continue to make maps like this, so I'll be providing a brief overview of how these maps worked to help. I'll be going over coop_uchi as it is the newest one. Its systems are an updated version of coop_vault's systems. coop_headquarters uses a completely different system internally, and its pretty hacky and unmaintainable. If you plan to make a coop map like the ones here, I'd recommend starting over from scratch since there were a lot of bad design choices early-on that made things difficult to work with, along with bloat/feature creep from adding new features on the fly.

This all may seem overwhelming at first, but it should be manageable to do if you rebuild the systems in this order:
Player Management -> Downing/Reviving -> Heroes (Classes) -> Enemies -> Objectives

Globals
To start, there's a lot of stuff in scripts\vscripts\coop\crypto\crypto_globals.nut once you've extracted the map files.

The original idea was that it would be a central place where you can just change values easily, but it's bad in practice because it makes it confusing when trying to find out where certain values are stored. Most of the variables should be moved back to their respective script files for the sake of organization. In the code, '::' signifies a global variable. Try to minimize the amount of them that are present.

Objective System
You can find the Objectives script file in scripts\vscripts\coop\crypto\crypto_objectives.nut

There's nothing much to say here. A game_text is used to display objective text on a player's screen, along with messages in chat. All the objective text is stored in an array and accessed by calling their respective index. Since you need to know the number to call, it becomes confusing really fast. A better way to store the objective text would be to create a dictionary of key/value pairs, where you can look up something like "obj_drill" and get the corresponding text related to the drill objective.

For the actual objective logic, I basically just have an initial objective available to the player (in the case of coop_uchi, the drill they need to pick up), and interacting with it causes the next objective to become enabled. For the timed button interactables, I use some hacky system involving func_buttons and game_ui, along with a bunch of Hammer instances, but luckily a CS:GO update added prop_mapplaced_long_use_entity which does this cleanly with a single entity.

There's some additional variables and stuff used for keeping track of the progress on certain objectives, but those are all map-specific.

Player Management
You can find the Player script file in scripts\vscripts\coop\crypto\crypto_player.nut

Players spawn outside of the map, then are registered into the script via a trigger that teleports them into the playable area.

Most player management related stuff happens in the Think() function, which runs every 0.1s. There's a lot of moving parts here, and this is one of the areas that could definitely use some improvement. I'll try to sum it up with bullet points.
  • Players have 32768 (overflow number) + 100 HP, which can change based on the chosen 'Hero' (Classes are called 'Heroes' internally because 'class' is a reserved keyword)
  • Players have multiple states: Alive, Downed, LastStand, Dead
  • While alive, Player HP is constantly updated, and health regen starts when players haven't taken damage for a certain amount of time.
  • LastStand is simply used to add a small delay before players go down, due to the way the system is set up. It's nothing but a hacky middleman.
  • While downed, a timer ticks down until it's time to kill the player. The player's HP will be set to below 32768 while downed. If a player is being revived, their down timer pauses.

There's a bunch of timers used for different things, but the timers are handled poorly. They count the number of 'steps' (how many times Think() runs) instead of actual time, so it gets confusing very quickly. A better way of doing things would be to have them represent seconds, and to subtract -0.1s every time Think() is run. This 0.1s number should be stored in a separate variable, so that there isn't a bunch of magic 0.1s numbers everywhere. Additionally, I think creating a separate standardized class/struct to handle temporary buffs would make things a lot easier to work with, once you start adding 10 different buff types or something.

Heroes (Classes)
To avoid confusion, 'Classes' will be called 'Heroes' in this section.
This is included in the player management script in scripts\vscripts\coop\crypto\crypto_player.nut

When a player spawns, their weapons are stripped via player_weaponstrip, and then they are assigned a Hero with the SetHero function. Not sure if this is still an issue, but I remember that trying to give players their knives back after stripping their weapons would cause hundreds of them to spawn, so there's something in the code for cleaning them up.

To handle grenade cooldowns, there's something in the Think function to figure out when a player has thrown their Hero's nade type, and the cooldown begins ticking down once that's detected.

When changing Hero, the player's deployables are wiped. If a player is using class-locked equipment (LMGs, Heavy Assault Suit, Shields), they are stripped of their weapons with player_weaponstrip. After that, based on the selected Hero, the player's stats and model are changed by running a huge if/else statement. To start, a more readable way of doing this would be to convert it into a switch statement. But an even better way to do it would be to create some sort of standardized HeroDef class/struct, fill out a bunch of them with info for the various classes, and simply pass that in as an input to SetHero so that there isn't a need for a giant switch/if-else block.

Deployables
This is included in the player management script in scripts\vscripts\coop\crypto\crypto_player.nut as well as the scripts in scripts\vscripts\coop\crypto\heroes\

Frankly the deployable system is terrible. I'll only give a brief explanation on how the current system works, since it absolutely needs to be fully rewritten. Every player has an array of size 5 that stores how many deployables they have, with values set to null to indicate that they don't have a deployable active.

When a player throws a decoy, a big if-else statement is run to spawn their class's corresponding deployable via a point_template. When the deployable is spawned, it looks through the list of players and finds a player who has the 'abilityTriggered' (jank way of figuring out who needs a deployable) variable set to true, and then spawns at that player's location. Also all the deployables have an 'immune' variable to prevent them from being teamkilled by mine explosions.

I believe I use some sort of damage filter to make deployables only killable by certain types of damage, but they're a bit iffy at times and don't always fully work (ex. tripmine explosions ignore the explosive damage filter).

A better way of handling deployables would be to use a List or some sort of struct that doesn't have a fixed size limit. Additionally, instead of using a point_template, env_entity_maker should be used to spawn things since you can directly get the spawned entity via vscripts, instead of the hacky stuff that the current deployable system uses. On top of that, the deployables themselves all have a lot of copypasted code. It would be easier to maintain if they all inherited from some base deployable class, so that the spawning/removal code doesn't need to be copied a ton of times. And finally, instead of having a ton of if-else statements to handle what deployable should be spawned, there should just be a simple string/entity variable included in a player's HeroDef, and that alone should be used to choose what deployable to spawn.

Downing
This is included in the player management script in scripts\vscripts\coop\crypto\crypto_player.nut

If a player's HP is less than 32768+1, they are put in the LastStand state and go down 0.1s later (I think there was some sort of reason for this but I forgot why). Only 1 player can be downed per Think() to prevent crashes from spawning too many entities at once. If a player needs to go down but another one already went down that Think() cycle, they simply get postponed to go down at the next Think().

Once a player goes down with the DownPlayer() function, their old position is saved and they get teleported to a sealed off room at the bottom of the map.
The following entities are then spawned:
  • [Script] Downed player model
  • [Point_Template] Downed player button + game_ui
  • [Script] point_viewcontrol to handle the player's camera while downed

The model/button/game_ui can all be handled via a single prop_mapplaced_long_use_entity spawned via script now, which makes things easier. Do note that some entities dont behave right when spawned via script, so you should check to make sure that nothing weird is going on with the prop_mapplaced_long_use_entity since I haven't tested this in-game myself.

Enemy Management
You can find the Player script file in scripts\vscripts\coop\crypto\crypto_enemies.nut

Enemies are handled similarly to players, where they spawn outside of the map and are registered into the script via a trigger that teleports them. The Think() function handles enemy passive abilities rather than health regen. There's a ton of voiceline-related stuff in here but I'd recommend cutting everything except for the Player-Killed taunts and the deathsounds, since they take up a lot of entities.

I forgot the specifics of what I did to set up the enemies for Co-op strike, but basically I have them set to respawn the moment they're killed. When an assault wave is over, I disable their respawns and kill any of the bots that aren't inside the map, and that causes players to respawn once the remaining enemies are dead. Co-op Strike bots don't seem to work with dedicated servers, so if you want to make a map for server hosting, just use the regular CS:GO bots (and also they're easier to work with anyways, compared to the amount of hoops you need to jump through to get Co-op Strike bots working).

Cops throw decoy grenades that turn into smoke that only they can see through since these maps were made back when smoke made bots fully unable to see you, but now that vanilla bots are better at noticing players in the smoke, you could probably just give them regular smoke grenades.

The code for assigning enemy types is a giant ugly if-else statement. You should cut most of this stuff out and instead create preset EnemyDefs that can be easily applied with one short function.

The code for assigning enemies to a squad is an even bigger ugly if-else statement, and should be replaced by a more elegant weighted selection function or something like that.

Assaults and breaks are handled via an internal timer. Enemies respawn instantly, but only actually spawn into the map once their internal spawn timer ticks down. Destroying Cameras increases their spawn timer, but each wave reduces it. When bots are given their weapons and sent to the playable area, there is a random delay to prevent crashes from creating too many entities at once.

There's an internal variable used for keeping track of enemy ammo/armor drops, where you're guaranteed to get an ammopack every 8 kills, and an armor pack every 24 kills, starting from a 1/8 and 1/24 chance per kill respectively.

For the navmesh, I used 3kliksphilip's bot video as a reference https://www.youtube.com/watch?v=iVOIG_BnBzI
The key to making bots play well is to simplify the navmesh for them, by removing weird jump spots so that they don't try to climb on every single table and chair imaginable. You can also use nav blocker entities to temporarily block off areas that bots aren't supposed to access, like a vault door that hasn't opened yet. Creative use of nav blockers lets you make the bot tactics feel a lot more varied.

Barricades
You can find the Barricade script files in scripts\vscripts\coop\crypto\buttons\barricades\

Barricades contain 2 parts: a barricade frame, and the planks themselves. These are handled via Hammer instances. Every barricadable window/doorway has a barricade frame instance. However, there is only 1 instance for each corresponding plank set. To put it simply, a house can have 30 of one type of window, but there'll only be a single instance of planks for that window type. Each type of barricade frame will have 1 corresponding plank set, and the plank set is spawned via a point_template. This was done due to hammer limits. I believe that a breakable prop or something would be a better way to handle this, but that's beyond my level of knowledge.

Barricades are immune to enemy bullets, but will slowly break if a bot gets close to them, to make it look like the bot is smashing it down.

When a player interacts with the barricade frame, it will activate the point_template of its frame type's corresponding plank set, and the planks get spawned in and somehow manage to find the corresponding frame that requested them. I forgot how this is done, but I remember that I put all the frames into a table that can be searched, so that probably has something to do with it.

Stealth
I only included stealth in coop_headquarters because it's hard to set up. I had to manually create a detection radius with triggers for every single camera on the map. However, I think the TraceLine() vscript function could be used to make easily-duplicable cameras that can be placed anywhere, but I haven't tested that out. Aside from cameras, stealth is simply a matter of creating a separate objective set, and swapping from that to the Loud objective set once the alarm sounds.

Misc Thoughts
  • My player management system removes players the moment they die or if they disconnect. I'd say that you should only remove players upon disconnect, and the code for removing players needs to be cleaned up.
  • As I stated earlier, all timers should be converted to handle things in seconds instead of Think() cycles for the sake of readability. Use a variable called 'fixedDeltaTime' or something and set it to 0.1s (length of time between Think() cycles), and subtract this from timers every Think() cylce.
  • All the areas with giant ugly if-else blocks should be converted to use simple HeroDef and EnemyDef classes if possible.
  • Replace point_template spawning with env_entity_maker since it works better with vscripts.
  • Ideally you should rely on point_template as little as possible. The goal should be to have most spawning and stuff happen via vscripts itself, though there are some things that need to be spawned via point_template due to vscript weirdness.
  • Gamemode settings are handled in crypto_startarea.nut. There's an issue where bots don't spawn if you played Danger Zone (and maybe some other modes?) before playing Co-op Strike, but I haven't found out what's causing it. If you plan to make a co-op map, fixing this is important.
  • When using scripts, models need to be precached (just 1 line of code per model) before being spawned, or else errors occur. This only applies to models that are script-only and not found in the map.
  • Try to delete unused entities once they've finished serving their purpose, to prevent crashes.
  • VIDE doesn't auto-include vscripts and models that only show up in the script files. Be sure to remember to manually add these!

Closing Thoughts
There's probably a lot more I'm forgetting, so if you have any questions about mapping, how I did things, etc, leave a comment on my profile and feel free to add me, and I'll be able to explain further.

Just be warned that you'll need a good understanding of Hammer before you even begin to attempt any of this stuff.

I'm glad I spent the time making all of these Co-op maps over the years, and thank YOU for playing them!

12 Comments
rest in pici Aug 17, 2020 @ 1:15pm 
i love your maps bro! keep working on it!!
Moffein Jul 9, 2019 @ 3:35pm 
Shield is definitely coming. Drones probably not as of now.
Уретральный спец Jun 10, 2019 @ 6:36am 
mb add shield and drones in future maps?
Auditormadness9 Jan 12, 2019 @ 11:10am 
New guy here. Your maps are so awesome, I can't thank you enough. No jokes, no acting, like legit no other coop strike map author has done such an amazing and thought-out map format as you did (many settings in pre-match scene)
BoyToy [DTF] Oct 28, 2017 @ 2:52am 
dude what r all his maps? I wanna see but idk how

I rly liked ur agency 1 tho great job ur the best map creator ever :D
[TDR] SirTup™ Aug 4, 2017 @ 12:08pm 
need a new maaap :O <3 omg such in love with them bruuuh I re installed CSGO for that!
VIEW ALL (219)
GROUP MEMBERS
Administrators
5
IN-GAME
36
ONLINE
2 IN CHAT
Enter chat room
Founded
October 14, 2015
Language
English
Location
United States 
ASSOCIATED GAMES