You'll need copper.def or copper.fgd, which are included with the mod. See documentation for your editor of choice for instructions on loading one of these. As a result of restrictions of the two formats, the Worldcraft-format .fgd contains more field metadata, but less descriptive text, while the QuakeEd/Radiant-format .def is the opposite. For human-readable tutorial text, searching copper.def for an entity classname will generally tell you everything you need to know.
There is no exhaustive documentation of spawnflags and keyvalues to be found here - copper.def/fgd loaded in your editor accomplish that better where you actually need such information. I have tried instead to provide a practical guide to what new tools are available to mappers and what new things they make possible, which is the sort of information that can't be easily gleaned from scanning technical specs.
Ideally, perusing this document will give you ideas. Tools for smoothing the act of play and the act of construction are common. Thematic sugar for worldbuilding is not, nor are means of taking direct control of the experience.
Copper makes things better for mappers primarily by making things better for players, not the other way around. What this means is that by polishing and deepening the core gameplay, Copper amplifies the mapper's power to create fun, dangerous challenges using the game's core elements: enemies, resources, and 3D space. Therefore, before you continue reading, understand that the most important things you need to know about Copper as a level designer are here.
The hallmark of "gamey" or "arcadey" level design is a conversational style. While the best levels aren't lacking competent visuals, the world the player finds themself in doesn't fully pretend to be a real setting, a chapter in a story, or anything other than a handcrafted clockwork machine designed to make them pay for every inch of progress. The designer builds an escalating arc out of challenges arranged using game rules and elements, but more importantly the player knows it, letting their understanding of how to exploit those same elements guide them past the threats. It's a lot more like a puzzle than a world in this regard. As first person games move ever further towards tightly-authored roleplaying experiences, this type of honest level design is increasingly forgotten.
While players can get better at a shooter by aiming better and reacting faster, there's a low ceiling for improvement there, and playing games that test only those skills can be downright painful. It's much better when a player has room to improve through learning mechanics and the interactions between them. For example, to a low-level player, infighting is just a funny thing that happens sometimes, but to a skilled player it's a tool on their belt for saving resources and managing the scale of a fight.
When players can't counter a greater challenge with better play, the outcome of play is more dependent on conditions set by the designer, and thus if a player dies in a certain place, there's little they can do to ensure they don't die there again. In other words, the difficulty spike is insurmountable and the level is imbalanced and wrong, usually for not having "enough" health or ammo.
When that skill ceiling is higher, however, "enough" ceases to be a meaningful term. Doom E4M1 famously provides exactly 5 points of health on Ultraviolence - it's a damn hard level, but still beatable without relying on luck, and (critically) small enough to encourage the experimental repeat attempts necessary to rise to the occasion.
Feel free to think of your design in these terms. Copper provides more of that depth for players to understand so that mappers like you can squeeze more excitement out of them.
Don't let the above convince you that Copper doesn't facilitate adventure level design, or that it values only shooting gameplay above all else. Doomworld's excellent retrospective of 25 years of Doom level design highlights two major threads in Doom's custom mapping history: hard-as-nails combat puzzle maps and more thematic, atmospheric adventure maps. More to the point, it highlights how the two threads have been fused in recent years. While Copper, for simplicity's sake, may not provide some atmospheric level design tools like breakables, that's not to say that exploration and discovery aren't supported. Learning your way around an environment that doesn't lead you by the hand is an important player skill for games of Quake's era, and there's still a million ways to hide a secret.
This has ramifications for difficulty balance. The two schools of level attract two types of player. Some players will prefer to go full run-and-gun, but are terrible secret hunters, while others aren't professional monster killers but love to investigate every corner and clue, relying on the extra spoils they uncover to keep them afloat. Since secrets are by nature optional, it isn't normally fair to balance your level such that it's impossible without finding some or all secrets ... at least on the first three difficulty levels. For the proverbial player that wants a tough challenge even with most or all secrets found (or known in advance), Nightmare is just the skill for them. Balance accordingly.
Every monster can be configured as a spawner, rendering exterior teleboxes obsolete. Set the TRIGGERED spawnflag on a monster, and it will spawn when triggered instead of at map start. Set the INSTAWAKE flag to make the monster spawn angry at its activator. To make this a multi-use spawner, set count to the limit you want, or -1 for infinite spawns.
If the limit is positive, that many kills will be added to the total at map start, but if the spawner is unlimited, a new kill will be added to the total each time a monster is spawned.
A pre-spawn delay can be built into any monster spawner with the wait and rand keyvalues, if you'd like to raggedly stagger an ambush without a lot of extra trigger_relays or thought. After being triggered, a spawner will not produce a monster (or the teleport flash) for wait seconds, plus anywhere up to rand additional seconds. Note that this is different from delay, which causes a monster not to fire its targets upon death until after that many seconds have elapsed.
Set the NO_TFOG flag to supress the teleport particle flash and sound effect when a monster is spawned this way. There's no need to stress the particle limit if a bunch of monsters are being spawned out of sight of any players.
Triggered monsters with the NO_TELEFRAG flag set will wait to spawn after being triggered until doing so will not telefrag any other monster. Useful for trickling monsters into a room (emulating Doom-style teleport ambushes) without comedy gib collisions. They will still spawn and self-gib if the player is in the way, regardless of this spawnflag. If a monster is blocked in this fashion it will automatically try again on its own, so from a level scripting point of view it is safe to fire and forget such monsters.
A monster with a movedir set to a valid vector will be launched with that velocity upon awakening the first time (or upon spawning, if trigger-spawned), to save on excess trigger_monsterjumps. This vector always has to be bigger than you think it does. A trigger-spawned monster or a monster with a movedir will never do the drop-to-floor test before spawning, and will appear exactly where you put it. rmovedir (with an 'r') will specify velocity relative to the monster's facing angle instead of absolute to the world - the format is (forward up right).
Monsters created by a spawner inherit all the target keyvalues (including killtarget) from the spawner, but not its targetname. If you really need to spawn a monster with a targetname, use an old-fashioned telebox for this special case. They will also inherit all of the following keyvalues:
spawnflags, deathtype, message, delay, movedir, rmovedir, alpha, skin, effects, armortype, armorvalue, items, invincible_finished, invisible_finished, super_damage_finished, radsuit_finished, lip, state, gravity
Many more keyvalues are hackable than vanilla Quake, in that they will not be set at spawn time if they already have a keyvalue of that name specified in the map. This can be used for a great deal of new nefarious things, the most immediately useful of which are documented here as well as in copper.def/fgd. For example, the deathtype keyvalue can be universally overridden, so any monster, bottomless pit, or crushing mover can provide a unique obituary message, should you feel inspired to fill your level with King's Quest-style death puns.
Many keyvalues that default to something other than zero (and therefore cannot otherwise be set to zero without defaulting to something else) can be set to -1 to force a zero. This logic was applied inconsistently in vanilla Quake on a case by case basis, but was made as universal as possible in Copper. This does not apply to keyvalues where -1 would itself be a valid setting (such as lip on movers like func_doors), in which case alternate methods are often available to achieve the same effect (in the prior example, using the distance keyvalue instead).
Any skip-textured solid entity can be effective as "creatureclip" by setting the notrace keyvalue to 1. This makes gunshots, line of sight checks, and projectiles (including gibs but not heads) treat this entity as nonsolid, but all other entities (anything with a bounding box) will see it as solid. This is very useful for making monsters walk over grates or other fancy holey floors without preventing players from shooting up at them or monsters seeing the player and returning fire. Use fancy runemetal floors over exposed lava without fear that your monsters will refuse to navigate over them.
Care should be taken when creating tandem invisible creatureclip movers to encourage monsters to walk onto func_door- or func_plat-based lifts. Monsters tend to 'bounce' when riding these double-platforms, since the game can't decide which mover the monster is standing on from one frame to the next. Shrinking the skip-notrace mover by a few units alleviates this, but can cause its own problems with the two movers becoming desynchronized if something blocks one, causing it to reverse, but not the other. Use an entity that doesn't reverse when blocked if at all possible, like a func_train, and test heavily.
Cheats and Impulses
Noclip mode has been improved to match noclipping in other Quakes, and prevents all interaction with the level. The player is nonsolid to everything when noclipping, including items and triggers, as well as invisible to monsters, so you can fly around without accidentally touching and activating stuff.
The following impulses have been added as development aids. (Most are aliased as commands by Copper for easier reference.)
- 9: give all weapons and ammo, but not keys (idkfa)
- 99: give a silver key and a gold key (keys)
- 64: toggle double brightness of light style 0 and double luma of all fog colors
- 80: add a fake client (see Cooperative)
- 81: remove a fake client
- 100: forcibly trigger at a distance any door, button, etc under the crosshair (invoke)
- 101: print all keyvalues of whatever entity is under the crosshair (reveal)
- 102: fire all targets matching player's name (debug_trigger)
- 103: print total ammo dmg vs total monster health present at map start (debug_ammo)
- 104: print the current serverflags (debug_flags)
- 127/129: core dump
- 140: print current version of Copper (ver)
- 200: list origin and classname of all monsters remaining alive and all undepleted spawners
- 66/205: kill all monsters (genocide)
- 215: toggle undying mode (undying)
- 225: give biosuit (suit)
- 235: give pentagram of protection (pent)
- 245: give ring of shadows (ring/invis)
- 255: duh (quad)
The nomonsters cvar actually works now, but if your map has monsters that fire triggers on death which are necessary for completing the level, it will break for obvious reasons. Use the genocide cheat instead to avoid this.
Cooperative support in singleplayer levels is quite often an afterthought. Rarely played thanks to the poor IP performance of Netquake and by being left out in the cold by Quakeworld's competitive focus, and thus rarely considered by mappers building for singleplayer beyond placing four coop spawnpoints behind the player start and adding "Cooperative: Yes" to the readme. This is a shame, since reasoning about how differently a level can play when the player can be in more than one place at once can add a fun and rewarding extra dimension to the 'design puzzle' a mapper might discover within their map as it unfolds.
All entities support the additional Quoth spawnflags for COOPONLY (4096) and NOTINCOOP (8192). These only examine the status of the coop cvar, so they can still be tested with one player. This allows you to make small coop adjustments, like to ensure players don't get trapped outside sealed arenas, or to completely reconfigure a level's progression and combat.
Trenchbroom should display these flags automatically, but NetRadiant might have trouble with this.
Adjusting for Player Count
The new spawnflags provide a way to react to coop status at map load, but this can be ineffective for tuning difficulty since it treats all coop games the same, regardless of the actual number of players. If you're willing to take on the testing challenge of so many permutations of the same map, Copper provides a few mechanisms to make per-player-count adjustment possible.
While unused spawnflags above 8192 certainly exist, using them to filter entities by player count isn't possible, since like all spawnflags, these would evaluate at map load before any clients connect. Therefore, player count reactivity has to be evaluated during gameplay, and since clients can technically join or leave at any time this is really the only method that's feasible.
A new entity, target_playercount, can be used to selectively trigger entities based on the number of players in game at the moment it's triggered. This acts like a trigger_relay, but is the only entity that treats the four target keyvalues separately: the keyvalue which corresponds to the number of players connected is the one fired. For example, in a 3-player game, only target3 is fired, and the other targets are ignored. The LT_EQUAL spawnflag will change this behavior to fire all target keyvalues up to the current player count. In that case, in a 3-player game, the entity would fire target, target2, and target3, disregarding only target4.
Be cautious when using target_playercount to trigger-spawn monsters. Monsters which aren't activated by a target_playercount because the number of clients isn't right will still exist unspawned. This is a problem because they will have added themselves to the total killcount anyway, unless their 'count' is -1 (see Monster Spawning), making it impossible for players to get 100% kills and potentially leaving them to wander your level in frustration looking for monsters that will never appear.
Other entities can be configured to react to player count on their own. Any entity with its count keyvalue set to the magic value -4 will make it adopt the number of players as its count when first triggered. This provides a great shorthand method for making trigger-spawned monsters or respawning items repeat themselves once for each player, as a lazy but effective way of saying "here, have more of these" without diverging too much from the singleplayer design.
When a monster with count -4 is detected at map start when the coop cvar isn't set, it defaults to 1 immediately rather than waiting until triggered, so the kill count doesn't change during gameplay when it doesn't have to.
Impulses 80 and 81 will add and remove dummy extra players for manipulating the current client count, to solo test entity setups using the above features without having to gather a bunch of warm bodies. A nonsolid player model will appear at the level start for each one spawned, to serve as a visual placeholder.
The standard behavior for trigger_multiple is to disable itself for its wait period after evaluating a single touch. This is all that's needed with one player or for triggers that cause one-time world events like doors opening. It can be a problem in cooperative mode if, for example, you wish to create a healing pool with a trigger that fires a target_heal at intervals. If more than one player stands in the pool (ie touches the trigger) at the same time, the first client to evaluate in a hostframe will activate the heal and then the trigger will go to sleep, skipping the additional players each and every time. The ALL_CLIENTS spawnflag has been added to trigger_multiple and trigger_multiple_box to make them respond to every player standing in them per active frame instead of just the first client before going dormant.
Coop player starts can be marked as disabled with the TRIGGER_FIRST spawnflag, and no coop player will spawn there until it's been triggered. This allows the mapper to move the coop starts forward as the players make progress, so a respawning player doesn't have to trek all the way back from the start to get back to the action. The old set of coop starts can simply be nuked with a killtarget at the same time a new set in a more convenient spot is activated. As many coop spawn point groups as your map needs can be placed and activated in staggered fashion this way.
With the five second respawn delay added to cooperative mode, shortening the return trip at regular intervals thoughtfully avoids making players suffer a double penalty for dying.
Moving start points forward in waves works in most cases, but if you require more complexity, coop starts can also be disabled and reenabled with a target_lock.
All items support respawning natively without use of the SUB_Regen hack (although this still works, for compatibility with maps that used it). Simply set a wait keyvalue on an item to make it reappear after that many seconds. It will respawn indefinitely, but it can be limited to a certain number of respawns by setting count.
Items can also be inhibited from spawning until triggered. This is automatic, and only requires giving the item a targetname. Entities which cannot fire targets are safe to target items with, such as lights using items as spotlight destinations.
Any items that don't already spin in place (ie ammo, health) can be safely rotated using the angle key. They will always stay visually centered within their bounding boxes wherever you placed them. Nothing else is required to make this work.
Items that are rotated very diagonally were found to 'feel' smaller than normal due to the tilted corners sticking out of their bounding boxes, allowing players to get closer to the visual representation of the items than usual before touching them. Angled items therefore have their bounds puffed outwards slightly to include most of the axially larger item. This is done after item floor placement and has not been observed to cause items to be too sensitive to nearby brushwork.
All items have a SUSPENDED spawnflag to skip the droptofloor test when they spawn, forcing them to remain exactly where they're placed in the source map. This can be used to create items that hover in midair, but it's also a useful hackaround for items that insist on falling out of the world and vanishing because they don't like where you placed them. This also provides a handy solution to the irritating problem that a monster and an item cannot overlap without pushing the item out of the world.
Even if items don't fall out of the world when you test your map in your Quake engine port of choice, inconsistent droptofloor() results are not unheard of across ports. Consider it best practice to always mark as SUSPENDED any progression-critical items like keys, or any other item that fires a necessary targetname, even when placed on the ground.
A suspended item can be caused to fall by activating a target_drop that targets the item. A target_drop is on the 'invalid triggerer' list, so it can target an item without inducing delayed-spawn behavior.
Here is some helpful reference for how much damage you can expect a player to withstand when wearing each type of armor, assuming a player with 100 health grabs an armor, then takes enough continuous damage to lose all 100 HP without grabbing any health items:
- Green: Player dies with 57 armor left, for an effective hit point total of 143
- Yellow: Dies with 50 armor, EHP of 200
- Red: Dies with 0 armor, EHP of 300
A small ammo reward for those little hideaways that aren't really full secrets. Comes in two flavors.
- Regular: Contains 6-8 shells, 12-16 nails, and is beige
- Plus: Contains 12-16 shells, 24-32 nails, 2 rockets, 3 cells, and is red
Exact amounts of shells and nails are chosen at spawn time, so players can't regressively minmax by quicksaving before touching every backpack.
Specifying custom amounts of ammo is not supported, so that players always receive what they expect to when grabbing it (and thus can make informed choices about wasting ammo like they can with normal ammo boxes). Note that there is a spillover mechanic in Copper, where ammo that would be wasted is converted into armor points (from these backpacks only, making them special), so that the player's informed choice is not about wasting ammo so much as managing a tradeoff between more ammo and more HP.
This entity is a catch-all device for interacting with the player's inventory. By default, when triggered it will give a player that activates it whatever items and goods it's specified to (ranging from ammo, health, and armor to weapons and items and even arbitrary serverflags - see the in-editor documentation for exact keyvalues). Several spawnflags enable alternate functionality.
If the TAKE flag is set, it will attempt to subtract all specified amounts instead of adding them. If all items can be successfully taken, this entity will fire its targets; if not, it will print its message at the player if it has one. Use this to create an elder god that demands the player sacrifice their rocket launcher before proceeding, or anything else you can think of.
If OVERRIDE is set, the activator's inventory is overwritten rather than added to or subtracted from. Use this to drain the player of shells at map start, give yourself better starting gear to test continuous play in an episode with estimated starting inventory, or force a shotgun start instead.
The FLASH flag is there to make the screen blink as if the player had picked up an item when his inventory is altered by this entity. You might want this to be stealthy, or you might not.
If TEST is set, it behaves as TAKE above but doesn't actually remove the inventory. Maybe that elder god just wants to make sure you're prepared. Setting both TEST and TAKE acts as a not operator on the test: targets will be fired if the player's inventory fails the test, and any message will print if it passes. This makes it behave as a trigger_relay predicated on whether or not the player has a certain item or an amount of a certain resource.
One of many very useful applications of target_items: in TEST mode, it can be used to make any trigger ignore invisible players.
Important safety tip: think carefully about how you want your items test to work in Coop. Does every player have to pass the test? Or any? Will the setup break with multiple players if the trigger isn't set to ALL_CLIENTS?
There are a number of other triggerable point entities, some of which have effects which formerly required volume triggers:
- target_secret: Grant secret credit as part of events, or tying it directly to picking up an item without creating a trigger for it. (Don't forget that sometimes some items can't be picked up.)
- target_setskill: Change the skill level in a start map through buttons or other events without the strange triggering-a-trigger setup.
- target_multiprint: The centerprint message functionality of trigger_relay, but repeatable with multiple messages in one entity for convenience.
- target_kill: An entity that kills all of its targets, not just its killtarget, if you need to remove a lot of things and don't want to spend a lot of relays on it.
Monsterjump triggers are now highly configurable and can be made very selective about when to launch a monster and when not to. New spawnflags:
- NO_LARGE: Only monsters with 32x32 bounds will be launched (Knights, Grunts, etc).
- NO_SMALL: Only monsters with 64x64 bounds will be launched (Ogres, Shamblers, etc).
- ONLY_MELEES: Only monsters with no ranged attack (Fiends, Knights, etc) will be launched. Great for letting your melee monsters spill over ledges while the ranged attackers retain the high ground.
- ONLY_FRONT: A monster will only trigger a jump if its enemy is in front of the trigger, relative to the jump angle. No more monsters randomly flinging themselves away from you because they touched that trigger behind them. (The default cone is 120 degrees wide, but can be changed by setting distance the same way you set it on an angled trigger, ie, cosine of desired facing angle).
- ONLY_BELOW: A monster will only trigger a jump if its enemy has a lesser z coordinate.
- ADD_VEL: Add velocity to monster rather than setting it (acts like a monster-only wind tunnel trigger).
If that's not specific enough, the include and exclude keyvalues can be used to selectively add or remove a monster by classname from jump candidacy.
Hurt triggers can have their damage and frequency customized with dmg and delay, for infrequent pulses that hurt real bad or a slow steady drain.
In vanilla Quake, tiny damages would always round to armor when the armor strength was yellow or red, stripping it away too fast, and always round to health when green, defeating the purpose of armor. This was very apparent with a trigger_hurt set to do 1 damage many times a second. Copper fixes this by randomly applying the last fractional point of any damage to either armor or health to remain statistically balanced.
The vanilla bug that caused all but one player to be immune to a trigger_hurt if more than one player was touching it at a time has also been fixed.
Two spawnflags have been added that add a lot of gameplay potential: trigger_hurts can be set to ignore monsters (subjecting players to movement limitations the baddies can ignore) and to ignore players wearing a biosuit (allowing you to make the biosuit useful against any number of new environmental hazards you might create).
Push triggers can be set to act more like gusty wind or flowing water than an inescapable wind tunnel with the ADD_VEL spawnflag. This will add the trigger's push velocity to the player's velocity, rather than override it, allowing them to retain control and push against the current. Since this could cause the player to endlessly accelerate up to ludicrous speed, the impulse can be limited to a maximum velocity with the speed2 key. Note that ADD_VEL will not cancel out gravity like a normal trigger_push, unless it pushes vertically at least as strong as gravity (the default is 800), and can be counted on to behave differently depending on whether the player's feet are touching the ground and causing friction.
The default whooshing-wind sound can be overriden with a .wav of your choice by specifying noise.
ADD_VEL with a reasonable speed limit, and a rushing water noise, is ideal for water currents.
When this entity is triggered, it acts as if the activator just touched a teleport trigger, with this entity as its destination. Thus a trigger_multiple targeting one of these behaves the same as a trigger_teleport targeting a teleport destination (the trigger won't emit the teleport whisper sound, however). This is not what makes them useful; there may be times when you want to teleport the player or monster to a certain spot, as part of some gameplay contrivance or a scripted sequence, regardless of where they're coming from. If it's not convenient or feasible to blanket the entire area in a teleport trigger, using this as the destination and triggering it via any other means will solve your problem.
This allows neat new things like shootable buttons that teleport you to them, or anything else you can think of (that you can illustrate to the player sensibly, of course).
Teleport targets have the added benefit that they can be locked with a target_lock, allowing a trigger_multiple to serve as a teleporter with a switchable destination. Multiple teleport triggers could be stacked in one place and selectively locked instead, but the former method is a bit cleaner.
Heals its activator for healamount when triggered, up to an optional lifetime limit of count hit points. Use for healing pools, first aid dispensers, etc.
If it has any targets, it heals them instead. This applies to anything with health: monsters, buttons, secret doors, exploboxes, you name it. This will not reset 'dead' doors/buttons with a wait of -1, or bring anything else back from the dead.
This was used to create the reinforced meatwall in the UDOB Shub level - it was implemented as a shootable func_button being constantly healed by a target_heal at a rate that only quad damage weapons could overtake.
While it's a bit against the spirit of classic Quake and that of Copper, target_heal has a "smart" mode (enabled with the AUTO_AMOUNT spawnflag) that can react to the amount of health the player needs and decide on its lifetime limit on first use, as a means of dynamically wishing more hit points into your level for players who are getting their asses kicked versus for those who aren't in dire need of medkits. See the in-editor documentation for exact use.
This is an optional little helper for the player. When triggered, all it does is save the current game in auto.sav, notifying the activator of this with a print in the corner of the screen (just like quicksaving). Players have to load it manually, however, so in the event they haven't quicksaved they do have to remember to try 'load auto' - it's not perfect but it's better than nothing, and it's easy to implement. Just make sure you trigger it when it's unlikely the player could be a half second from death ...
An attempt to make the last autosave load on death led to uncovering a frustrating set of impossibilities. Within the walls of progs.dat, it just can't be done reliably without bad edge cases - namely, it's impossible to know if the auto.sav on disk is actually from the current playthrough or even the current map.
A bigger problem is that demo recording and playback is ruined because the 'load auto' command is recorded into the demo. For the recording player, this stops the recording (unless your engine is special) where the normal restart-on-death functionality wouldn't. For the player watching the demo, the autoload in the demo causes that person's auto.sav to suddenly load. Not good.
Movers and Solids
Many movers now support a variety of ways to set movement angle and direction. The distance keyvalue works for all movers, so the guesswork of figuring out the right lip for angled doors and buttons can be skipped.
Many also support setting their A and B positions directly using the pos1 and pos2 keyvalues. Movers can translate along any arbitrary non-planar vector, plats can be made that drop when stepped on rather than rise, and so on. Bearing in mind that these coordinates are local, and that the position where a mover is constructed is always [0 0 0], more effects can be achieved by setting both pos1 and pos2, causing the mover to be lit where it is built but move between two other positions. With careful calculation of offsets, movers that are problematic to light can be built and lit in the position with most ideal lighting (including in special purpose boxes outside the map), and shifted into position at spawn time.
If either pos1 or pos2 are set, the other will default to [0 0 0]. Use of pos1 and pos2 will override angle and distance. distance likewise always overrides 'lip'.
You may override noise through noise4 on func_ entities that otherwise require you to choose from the hard-coded menu of sound sets, enabling the use of any sound (including new wav files you create and include with your map) for any event that makes noise. This is exploited in UDOB, for example, to make secret doors emit that characteristic clockwork trundling sound even when they aren't implemented using an actual func_door_secret classname, or to disguise func_trains with complex path logic as simple doors or plats with non-simple movement patterns.
A number of func entities have an equivalent func_*_point entity, allowing you to specify an external .bsp file to use via the model keyvalue.
Moves back and forth along a line following a sine wave, similar to the entities of the same name in Arcane Dimensions and Quake3. Does not require or utilize a speed value; specify wait for the total period of the sine and either angle and distance or pos1 and pos2 to determine the track, and the correct speed will be determined per frame.
Triggering a func_bobbing will freeze it in place or reactivate it. Will deal its damage (if any) once and reverse motion if blocked, but the CRUSHER spawnflag will change this behavior to deal damage every frame without reversing.
Aside from all the general additions above, doors support a handful of specific new things:
- The message value on a Silver/Gold key door will override the default 'you need this key' message. This was used in UDOB3 to make the layered silver exit doors tell the player specifically how many Silver Keys they still need to deliver.
- If speed2 is set, the door will open speed fast but close speed2 fast.
- Doors will flip their +0/+a animated textures to the opposite state when they open and close, helping to disguise them as a buttons when used that way.
Trains are more dynamic now, and can be used for more than simple endless loops.
Upon arriving at a path_corner with a wait value of -1 (meaning stop forever), the train will revert to waiting until triggered, allowing you to start and stop trains in multiple locations, even at every pathcorner if you desire.
Trains fire all of a path_corner's targets upon arrival. (If a path_corner's only target is the next path_corner, nothing happens.) If you want a train to always cause doors in its path to open for it, for example, this is the way to go, since having the player ride through a trigger doesn't work if the player has fallen off. This is important for making events relative to the movement of a train responsive to potential starts and stops.
Upon arriving at a path_corner with a nonzero speed value set, it will inherit that speed and forget its own. Besides variable speeds along a path, this can also be used to make the same train move at different speeds on different skill levels by having it pass through a skill-specific path_corner. This could be automatic but unfortunately requires setting the PATH_SPEED spawnflag, due to path_corners in id1 maps that have stale speed values breaking progression if inherited automatically.
When leaving a path_corner, trains search the map for the first path_corner with a targetname matching the prior path_corner's target, followed by target2, target3, and target4, stopping in place if none is found. It will otherwise always accept the first path_corner it finds, even if more than one path_corner would be a match if it kept searching. This means a train's looping path can be branched or altered by means such as removing a path_corner via killtargeting, or selectively disabling it with a target_lock.
func_trains with a pausetime value will use that as their default wait period at any pathcorners that do not specify a wait. This defaults to 0.1 seconds to match vanilla, but can be set to -1 to make a train move continuously without pausing (except at path_corners that do specify a wait).
A flytrain is controlled in exactly the same way as a func_train, with the same keyvalues on itself and on path_corners doing the same jobs, with a key difference: it travels a smoothly curving path rather than moving linearly from corner to corner. When inheriting a speed from a path_corner, it will smoothly accelerate or decelerate on its way to the corner rather than immediately changing speeds on arrival. It will do its best to steer towards the next path_corner, but path_corners that are close together paired with too high a speed will cause it to overshoot. It may take a little trial and error of corner placement and speed selection to get the curve you want.
When waiting at a path_corner, it will drift gently in place, hugging the path_corner at a distance of no more than 16 units.
It also uses the center of the func as its origin, and not the minimum corner like a normal train, making it easier to visualize the path it will take in the editor.
A plat can have its activation zone overridden manually by targeting it at a trigger_once or trigger_multiple (which is then absorbed and ceases to function as a regular trigger). This is useful for plats with unusual (ie not axis-aligned rectangular) shapes, when a trigger generated from the bounds of the plat sticks out too far.
Copper doesn't support full breakables, but misc_explobox has been generalized into a func entity, allowing you to create exploding brushmodels of any size and appearance. Their health and damage are customizable, they can be set off by triggering, and they fire their targets when they blow.
Walls can be toggled on or off by triggering them. This can toggle either +0/+a texture state (like on button faces) or outright hide/reveal the entire brushmodel, toggling both visibility and solidity.
A func_void simulates an infinite abyss like a bottomless pit or the bottom of a space map. Comes with Quoth trigger_void levels of utility plus Honey's pit-death levels of polish, including a nice scream, an even better meaty splat, and integrated "punishment teleporter" alternate functionality.
Your func_void should be a solid brush covering the bottom of your death pit, ideally textured with sky or solid black. This brush will be nonsolid in-game, and serves to hide all the dead scrags and fiend heads that can collect at the bottom of a pit or skybox. It also hides any lightmapped surface you have to put down there to ensure your Scrags are lit.
It also generates a trigger of the same dimensions height units above it, which is necessary for initiating the fade-to-black and triggering the scream effect when the player starts falling in. This trigger height must be set high enough above the bottom for a decent fade time, but not so high that it's possible for the player to touch it without having gone irretrievably over the edge (meaning, ensure it's below any walkable space).
When a client touches this trigger, their fall begins. The func_void's targets will be fired when a player touches this trigger. When they touch the solid at the bottom, their view will have fully faded out, and their entity is removed. They don't receive the red flash and the splat noise until a few moments later (the interval can be specified with delay), which makes the pit feel deeper than it really is, so you don't have to drag the brushes down too far.
Living monsters that get flung into the pit will disappear on contact with the bottom, rather than spraying gibs and blood back up out of the pit. They still count as killed for all purposes, including firing their targets.
If you don't want to kill players outright for taking one wrong step, the func_void can also be targeted at an info_teleport_destination. Players will take dmg damage when they "hit bottom" and (if they survive it) will be teleported to this spot instead.
The color to fade to defaults to black, but can be overridden to anything by setting fadecolor. Matching the level's fog or the average color of the bottom of your skybox looks nice.
The two start map gate entities, which selectively do or do not appear depending on the player's rune selection at spawn, have received a small but powerful addition: they fire any targets they have at map start if they are not inhibited by the player having the wrong runes. This can be exploited to alter triggers and scripting in your start map, beyond just turning walls on and off. (target_items can be used to similar effect.)
The expansion of the target keyvalue to support target2/3/4 values has been included from Quoth, AD, progs_dump, and others. Any entity that uses a target - either the usual trigger firing or a special case - will check all four keyvalues, and they can be safely considered equivalent for all purposes.
The targetname keyvalue has not been similarly expanded. The combinatorial problem of searching for matching targets grows from 4 searches to 16 when this is done, raising the potential of game-crashing progs stack overflows or loop timeouts in huge maps. Four targets cuts down the trigger_relay spaghetti significantly, but four targetnames on top of that suffers diminished returns not worth the added code complexity and performance cost.
Nearly everything can be accomplished cleanly with four targets, and the rare cases that can't, can be with one or two extra trigger_relays.
All triggers have a TRIGGER_FIRST spawnflag, enabling them to spawn unusable and be activated at a later time by themselves being triggered. Reversible toggling can be accomplished with a target_lock.
A reminder: delayed trigger functionality with the delay keyvalue, despite being documented only as a feature of trigger_relay, actually took place in vanilla Quake within UseTargets(), so any entity that fires a target (buttons, doors, dying monsters, and so on) can do so with a delay without requiring an intervening relay.
Many triggers have accompanying trigger_*_box point variants, to save on brushmodels if you're near the limit (or just for keeping your maps tidy). Place the point entity in the center of where you want the trigger's volume, and set mangle to the full x, y, and z size you want the bounds of the trigger to be.
This is a very powerful entity that selectively enables and disables many other entities. A target_lock will set a LOCKED flag on all entities it targets when triggered. The exact effect is specific to the entity being locked, but a good rule of thumb is to consider a locked entity inert and disabled. (Compare to the Arcane Dimensions ESTATE system.)
A great deal of complex things can be accomplished this way:
- A trigger_relay can be locked to prevent it from firing or evaluating, enabling complex control over scripting and map logic.
- All triggers (_once, _multiple, _teleport, _changelevel, _push, _hurt, _monsterjump, and even _secret) can be locked, and will not activate when touched or triggered.
- Locked path_corners are skipped when searching for the next corner on a path, enabling switchable branches for trains and monster patrols.
- A func_button will depress itself (without firing) and stop responding to touches or triggers when locked. It will not return until unlocked again. A shootable button takes no damage (and does not bleed) when locked.
- A locked func_door won't respond to touches or triggers, and its trigger field is disabled. A locked door with a message will print it when touched. A door that is open when locked will immediately close (unless it is a toggle or wait -1 door). Shootable doors also do not take damage when locked.
- A locked func_plat does not respond to a player standing on it.
Locking and unlocking is never permanent, and any lockable entity can be unlocked and locked again any number of times. A target_lock can be set to toggle the locked status of its targets each time it's triggered, always lock them, or always unlock them.
Don't go mad with power. If there's a way to accomplish something without a target_lock, it's probably the right way to do it. A button behind bars makes a lot more sense to the player than a mysteriously dead button that just doesn't respond to being touched. Or, consider the existing form language of a wind tunnel blocked with an octagonal plug door before locking the trigger_push itself.
This entity can hold an integer variable, similar to a counter, and alters that variable when triggered. Defaults to cycling between 0 and 1, but setting count will make it cycle through all integers up to the given maximum before looping back to 0. Set state to the desired starting value.
If the RELAY spawnflag is set, things get interesting. This will make it act as a trigger_relay when fired rather than storing its own variable. It will compare its own (static) state value to that of the entity with a targetname matching this entity's include keyvalue, and only fire targets if these two values match. The included entity can be another target_state holding a value, but a number of other entities already use the state field to store their present condition:
- func_door: 0 when open, 1 when closed, 2 while opening, 3 while closing
- func_plat: 0 at top position, 1 at bottom position, 2 while going up, 3 while going down
- func_button: 0 when pushed, 1 when unpushed, 2 while depressing, 3 while returning
- trigger_counter: current count
- light: 0 when off, 1 when on
This enables you to allow or block any trigger event based on the state of a number of entities that are otherwise unreliable or impossible to synchronize. Timing something to match the movement of doors and plats can't be done rigidly when either can be unpredictably blocked by players or monsters and reverse their course, for instance. With a target_state, your map's scripting can react to these entities directly.
The elevator at the beginning of the UDOB Start map, for instance, has triggers at the top and bottom that fire target_states checking the position of the elevator, which trigger the elevator only if it isn't at that floor, creating an automatic return system that summons the elevator to the player's level when they approach.
Combining target_lock and target_state can yield extremely powerful programming possibilities, leading to map scripting that is highly dynamic and reactive to player actions while remaining difficult for a player to break.
Relays support a few new things in Copper. They can make noise when triggered, either by setting sounds to something from the same menu as triggers, or by specifying any wav file with the noise keyvalue. Any relay can serve as a sound emitter, even if it doesn't fire any targets of its own.
The RANDOM spawnflag will cause the relay to randomly select one entity that it targets to trigger each time it is used. The selection is made from all entities across all four target keyvalues, and is recalculated on each use so it's resilient to targets being removed or destroyed. (If the relay only has one target, this naturally has no effect.)
Relays support the count keyvalue to limit the number of times they can be used. They remove themselves permanently when their count is exhausted.
Similar to the Quake2 entity of the same name, a timer just fires its targets on its own once every wait seconds. Can be limited with count. Set rand to add a random extra delay to the wait interval. Can be triggered to enable and disable it.
Copper includes smoothly blending fog functionality similar to Honey & Arcane Dimensions. The fog control mechanics are designed around making the map appear to be divided into separate zones with different local fog. Thinking of your fog triggers as the boundaries between zones will help you conceptualize how to use them well.
To begin with, fog values (the fog_color and fog_density keyvalues) can be set on every player start, teleport destination, and intermission camera, so any way that a player might enter a fog zone without passing through a trigger can still set their fog to the desired local values. Unless you're just using one global fog for your whole map, set appropriate fog values on all of these entities. (Creative license can be taken with intermission cameras, since they're one-way.)
An important thing to remember: the fog keyvalue that goes on Worldspawn doesn't interact with this system. It is a feature of various engines (such as those in the Fitzquake family) and isn't interpreted by game code. The reason for this is that it's interpreted as four values (one for density and three for color) and set by the engine at load time. The numeric fields that are passed on to the game code (ie Copper), however, can only be one value (a float) or three (a vector). If you set fog in your world with a fog parameter on worldspawn, and never bother with fog_color and fog_density on any entities, you'll get the usual static global fog the way it's always worked. Color and density on a player start will be evaluated on the first frame of gameplay, and will override any fog set by the engine.
Fog blends are achieved by issuing a dozen or more 'stuffcmd' calls, 24 times per second. This is probably fine!
A bug in most Quake engine ports will reset the eye position smoothing that happens when climbing stairs or riding a plat on every frame that a 'stuffcmd' is sent, so fog transitions during upwards motion will cause noticeable stuttering. There is unfortunately nothing that can be done within Copper to fix this.
To fire a one-time smooth fog transition, have any entity trigger one of these. The activating client will see their fog blend from the values specified in the keyvalues for fog_color/fog_density to those in fog_color2/fog_density2, over speed seconds. Triggering this again will cause them to transition back again over the same length of time (unless speed2 is set, in which case the reverse blend will take that much time instead).
The GLOBAL spawnflag will cause this to happen for all clients, not just the activator. Care should be taken that the fog values being blended from are the values clients are already set to, or they'll see their fog snap to the start values before blending to the final values.
Putting a trigger that fires a target_fogblend at either end of a hallway or transitional space does a decent job of separating fog zones, but they have to transition fast enough that a player can't race back and forth between them and cause one transition to cut off the other. Doing this also results in players only getting a fog transition when they reach the end of the hall, meaning they see a new area with the old fog suddenly blend into new fog.
A trigger_fogblend solves this by acting as a smoothly transitioning fog portal. Any client touching it will have their fog set to a smooth interpolation between fog_color/fog_density and fog_color2/fog_density2 proportional to their position within the trigger. As they move from one end to the other, their fog is adjusted between the two extremes so that when they exit on either side, their fog has already fully transitioned to the appropriate values. This hides the transition in the middle of the hall where it won't stand out, and with careful adjustment you can make the fog blend almost invisibly.
The axis of motion on which the blend happens is defined by angle, pointing to whatever zone has color2 and density2. The distance is taken from the size of the trigger on that angle, so the full range of the trigger's volume is used for the transition. This distance can be overridden by setting distance if you don't think Copper is getting it right (which is possible for diagonal transitions).
Care should be taken that a player arriving at a fogblend already has their fog set to the values the fogblend will transition them away from on that side, or they'll see their fog snap to the fogblend trigger's values the moment they touch it.
While these aren't strictly gameplay-focused, and fall under the category of 'thematic sugar' mentioned above, they had already been implemented for other reasons in the less strictly gameplay-focused mod that Copper's codebase evolved from, so in Copper they remain. There wasn't much reason to cut them other than maintaining ideological purity, and while these pages are honest about Copper's development goals, it isn't that prescriptive.
Explosions and Gibs
Several entities are provided for activating point-based special effects at their origins when triggered.
- target_explosion: Causes the rocket explosion effect and sound, just like the 'barrel_explode' info_null hack. Make it hurty with dmg.
- target_telefog: Causes the teleport flash effect and random sound.
- target_meat_fireworks: Sprays gibs from its origin and plays a random gib sound. Override velocity and direction with speed and mangle.
All three will automatically repeat count times if set, pausing wait seconds between events. Use delay to delay activation a little after triggering, and rand to vary the events by up to this much randomly each time.
Quake uses lightstyles for both flickering/animated lights and lights that can be triggered on and off. In vanilla, this means the two effects cannot be used in tandem. target_lightstyle gives the mapper direct control over the pattern of any of the 64 lightstyles when triggered. Styles 1-31 are the preset lightstyles, and 32-64 are assigned by the compiler to triggered lights, and both can be overridden. Set style to the lightstyle you want to override, and set message to the brightness pattern you want to set it to. If style is not set, and this entity targets any triggerable lights, it sets the brightness pattern of those lights when triggered instead. (Note that targeting anything at a light makes it a triggerable light with its own style automatically.) This is compatible with any Quake map compiler, because light.exe only cares about the style number - the pattern is only applied in-game.
Setting style to -1 will override the style of all static, unstyled lights in the level (style 0).
Multiple additional stylestrings can be specified in order using the noise and noise1-noise4 keyvalues. The target_lightstyle will cycle to the next unused one each time it is triggered.
Quake's light patterns are sequences of characters where each letter represents a brightness from 0-200% lasting 0.1 seconds, allowing long animations to be packed into an editable string. a represents total darkness, m is normal brightness, and z is double brightness. For example, the painful disco strobe in the Fiend-filled corridor in E3M5, The Wind Tunnels, is "mamamamamama". Implementing a standard on/off toggle would use a message of "a" and a noise of "m" or "z".
Now expanded with configurable launch speed, damage, direction (with mangle and movedir), and interval (with wait and rand). Can be turned on and off with a trigger, or be made to emit a lavaball on demand with the BURST spawnflag.
The lavaballs can also, with the appropriately named EXPLODE spawnflag set on the emitter, be made to explode on contact like rockets. The dmg keyvalue still applies - your two best options are either 40 to match an Ogre grenade, or 120 to match a rocket or a Ch'thon ball, for consistent gamefeel with the rest of Quake.
Emits quakedots within its bounds. It can sprinkle them continuously, toggled on and off by triggering, or it can dump all its particles at once with the BURST spawnflag. Color, quantity, and velocity are all configurable as well; see the in-editor documentation for exact use. Useful for forcefields, the bases of waterfalls, or lending a little bit of magic to important map features. Has an accompanying misc_particlefield_box.
Specifying a velocity will cause a wave of particles to sweep through the bounds of the field instead (moving at the specified velocity), to reproduce the typical forcefield effect from other maps and mods. This can be combined with BURST to cause one wave only when triggered.
Shakes the screen when triggered. Jostles the view of all clients, but doesn't physically move their bounding boxes or apply velocities. Set its intensity with strength. 1 is a hearty rumble, 10 is spasmodic bordering on hilarious.
Follows an attack-sustain-release curve: it builds for delay seconds, holds at full strength for length seconds, then fades back toward zero for wait seconds. Total quake duration is therefore delay + length + wait. All three have defaults, but any of them can be forced to 0 by setting them to -1. (You might want a quake with no delay to be caused by a sudden blast, for example.)
For .mdl decorations like the obligatory candles and dead ranger bodies. Specify with model, orient with mangle (remember: pitch, yaw, then roll), and select a frame and skin if necessary. For a randomly rotated prop on the cheap, set ideal_yaw to 1.
If a single frame isn't enough, the pos1 keyvalue can be used to specify an animation range: the format is [firstframe startframe lastframe] (and don't forget frame numbers are 0-ordinal). A second animation range can be specified the same way with pos2, and the misc_model will swap between them when triggered.
If your models aren't animating the way you expect, double-check that your frame numbers aren't off by 1. Also verify with your model editor of choice if the animation you're looking for is actually a framegroup and not a sequence of individual frames, in which case specifying only one frame should be enough.
A generic ambient sound entity that supports custom sounds, similar to ambient_generic in other mods. Provides control of volume and attenuation range (within Quake engine limits). Otherwise self-explanatory. Is always on and cannot be toggled (which is a Quake engine limitation Copper cannot fix.)
A triggerable audio emitter, with configurable volume and attenuation just like its ambient cousin. Specify any wav file you want with the noise keyvalue.
This entity is overloaded to play up to five sounds. Specify additional wav files with noise1-4, and the entity will cycle through them in order upon successive triggerings. The SHUFFLE spawnflag will cause it to shuffle the list instead (never playing the same sound twice in a row.)
With a little custom sound design, a trigger_timer causing a target_sound to cycle through a few similar noises makes a robust replacement for continuous ambient sound that can be turned on and off.