Location Settings: Difference between revisions

From The DarkMod Wiki
Jump to navigationJump to search
 
(233 intermediate revisions by 11 users not shown)
Line 1: Line 1:
''written by demagogue''
== Introduction ==


This describes a method of doing ambient sounds in which a trigger turns on an omni ambient when you enter a zone, then turns it off when you leave, possibly starting a new ambient for the new zone.  It uses a script, a custom soundshader, and some in-map entities. 
This article describes how to setup '''locations''' (also called "location zones" or "zones") in your level, and then use these to:


It's useful for situations when you want one ambient sound to cover a very large or complex area that might be unweildy with multiple overlapping speakers, or any other situation where using speakers causes an issue.  On the other hand, in a lot of situations it's much easier to just use speakers, so it's not for every use.  (For the method using speakers, see this tutorial: [[Adding_ambient_Sounds_to_your_Map#Localized_Ambient_Sounds_in_one_Area_.28with_Speakers.29]]).
* fade to different ambient '''light levels''' for each zone
 
* fade between different ambient '''sounds''' in each zone
* Pros: A robust way to keep sound from leaking into areas you don't want it, or losing sound where you do.  For a very big area, or inside buildings or winding paths, a few triggers will fully cover the whole area with one seamless ambient, whereas it might take many speakers that have to overlap and aren't as seamless.  Also, you can change the ambient for a whole area by changing the property of one button (as opposed to many speakers).
* run '''scripts''' automatically when the player enters or exits a zone


* Cons: More involved setup than a speaker. An ambient fading in at the start is possible but easier with speakers. You need to make sure the player can't enter/leave the area without going through a trigger to turn on/off the ambient (a speaker doesn't care).  And at the beginning you should test it some to make sure everything is working (speakers are more drop and go). 


Mechnically, it works like a radio, where touching the trigger changes the station.  For that reason, the method is also a good template for any type of machine with various "states" that each have their own sound, e.g., pressing different buttons turns on their own unique sound, stopping the previous ones, including a button to turn all sounds off.  But I'll only deal with using it for zoning ambient sounds here. 
----


== Set up ==


'''The Blueroom'''
You need to use four types of entities in your map:


:Say there are 3 ambients you want to use in your map with the zone approach (it's easy to change the number once you get the principles, and you can even set up for more than you might actually need.  You don't have to use every channel, but it's set up in case you want to add some more ambients later). 
* a special global entity (e.g. add only one, the position does not matter): '''atdm:location_settings'''
* a special global light entity named '''ambient_world'''
* multiple '''info_location''' and '''info_locationseparation''' entities


:In your map, make a blueroom (i.e., a room off to the side the player will never enter) and create a set of 4 "command" buttons in it, the "radio stations", one for each sound you want to use, plus an extra one reserved for silence and turning all ambient sounds offCreate a button by right-click in the map window, create model, darkmod->mechanical->numberwheel_button.lwo->ok, then change the classname to "atdm:mover_button", then copy it as many times as you need.  
<small>
:Note:  Two alternative ways to get ambient sounds in your map are using speakers ([[Adding ambient Sounds to your Map]]), and using triggers ([[Ambient Sounds - Zone (using triggers)]]). Speakers are good if you only want your ambient to cover a definite radius and have a very simple setup.  Triggers are basically obsoleted by the method in this tutorial.  The only time you might still want to use a trigger-system is if you want a ambient sound to begin at a place where for some reason you can't create a portal to mark the zone boundary, but you can still have a trigger brushThere are some issues with a trigger system, too (f.i. their CPU and memory usage and more importantly they are not failsafe like locations are), that make the method in this tutorial superior.
</small>


:Also set up an ambient speaker in the blueroom (right-click, create-entity, Darkmod->Sound->speaker_ambient_music).
== The atdm:location_settings entity ==


::: (Two asides:  1. A blueroom is normally textured with common/caulk. You might temporarily put the buttons on a brush, light the blueroom, and have some other textures, all of which you can get rid of later, so you can put the StartingPoint inside and easily see and test the buttons later, which I recommend you do if you have any trouble.  2. The buttons would be your "machine" if you just wanted to use this method for machine sound-management.)
Create a '''atdm:location_settings''' entity in your blueroom (i.e., a room off to the side the player will never enter) or somewhere else in your map:


:* '''The Ambient Speaker'''
'''{{RMB}} -> Create entity -> Darkmod -> Info -> atdm:location_settings'''


::Name the ambient speaker you created "ambient_player" (If you want a different name, you'll need to change the name in the script below to correspond.) 
=== Default values ===


::The ambient speaker, at least for the purposes of this tutorial, works differently from a normal speaker.  Instead of each speaker having its own individual soundshader to play using the s_shader property, our one ambient speaker is going to contain multiple properties covering all the sounds we want to play with the zone method.  Again, it's like one radio with multiple stations. 
The location_settings entity takes a few ''default'' spawnargs related to the ambient light settings:


::Now you're going to add consecutive properties in the following form for as many ambients as you want to use with this approach, with values that I will explain below. 
* [[Location Settings#"ambient_light_fade_time"|"ambient_light_fade_time"]]
:::'''snd_station1'''
* [[Location Settings#"ambient_light_fade_delay"|"ambient_light_fade_delay"]]
:::'''snd_station2'''
:::'''snd_station3'''
:::etc.


::For each snd_station* property, you need to enter the value as the soundshader name of the sound you want to play.  Since you cannot add separate play instructions like a normal speaker (like looping, global, omni, etc), this information needs to be in the soundshader itself.  I have created a soundshader with instructions typical of ambient sounds (that covers all of the Darkmod ambients_ambience sounds) which you can create and drop into the Darkmod/sound folder (custom_ambient_trig.sndshd, I'll make a new wiki page for it for now [[Custom ambient trig]] so it doesn't get lost).  For its naming convention, it basically uses the normal ambient name (listed in sound/tdm_ambient_ambience.sndshd), and adds the suffix "_trig" so there isn't a conflict.
For their meaning and values, please refer to their sections.


::You can listen to the Darkmod ambients in the folder "Darkmod\sound\ambient\ambience" to choose the one you like (if your music player can play .ogg's), then the snd_station* value is going to (should) be the filename plus the "_trig" suffix after it.  But the name is literally defined in the custom_ambient_trig.sndshd file I linked to above, so use that name (e.g., in case there's a typo). 
=== update_period ===


::So some typical properties/values in your speaker_ambient_music entity might be:
The spawnarg '''update_period''' specifies the time in seconds between updates. A good value is 0.2, e.g. 5 times per second. That avoids to run the script too often, and still allows seamless transitions.


<code>
=== Sound Shaders ===
snd_station1            alien01_loop_trig
snd_station2            haunted_revenants01a_trig
snd_station3            mansion_piano02a_trig
etc...
(Strange, some of those underscores seem to be disappearing in the code tag.)
</code>


http://i41.tinypic.com/be7ck2.jpg
In the entity's spawnargs, put the names of all the ambient sounds you want to play in your map. (This pre-loads the sounds into the speaker so there isn't a pause when they start playing.)  Put it in one of these property/value forms (without the quote marks):


::Do this for each snd_station* that you want, and that's all you have to do with the ambient speaker itself.  You're done with it.  Everything else will be handled by the soundshader and the script.  (I hope to add a feature in the future to handle things like looping and volume in the ambient_player, however.)  By the way, notice that you do NOT need to create a snd_station0 for the silence channel.  That's handled all by the script itself.
'''"snd_streets" "city_night01_loop_z"'''


::You will probably typically want to use a shader name from custom_ambient_trig.sndshd for the value of your snd_stationX property. But technically you can use other names, e.g., from any soundshader in the sound folder, or even make your own. Just be very sure they will work like you want. I'll discuss more about a Sound Shader at the end of this tutorial to help you if you want to do something like that. 
  '''"snd_mansion" "sound/ambient/ambience/mansion_tense01a.ogg"'''


The left hand spawnarg should have a "snd_" prefix, and any name, but most useful is probably the name of the area it is for.  You'll use this name again when you place the location markers or if you use the override system. 


:* '''Command Buttons'''
The right hand value points to the actual sound file that will play.  You can enter either the soundshader name, or the address where the sound file is (but see footnote).  You can see the soundshader names of ambient-ready sounds in the sub-folder "Darkmod/sound/tdm_ambient_ambience_zoned.sndshd".  You can find the address of the sound files in "Darkmod/sound/ambient/ambience" (remember to start the address with "sound" in the spawnarg).  You can also listen to the sounds at that location.  They are are in .ogg format. But it's easier to just create a speaker (left-click>speaker) and it will give you a sound directory where can play sound files and find the names. No searching through sound folders outside of DR (thanks to Badcog for that tip).


::Name each command button (except for one exception below) whatever you want (e,g., the name of the zone it's covering, like: factory_zone, streets_zone, forest, whatever), just remember what it is so you can "target" it with a trigger entity later.   
:Note that it is very easy to add custom ambients with this system.  Just create a folder in your .pk4 (a .zip file renamed to .pk4) and name the folder "sound", with another folder inside it named after your FM or an abbreviation.  Put your custom sound inside that second folder.  (E.g., I have a custom ambient named Frozen.ogg, and my FM's name is Patently Dangerous.  So in my .pk4 was "sound/patent/frozen.ogg".  Note that ambients must be in either .ogg or .wav format. Now in your speaker_zone_ambient, just put the sound's address in the spawnarg (but see footnote).  In my case, I have "snd_warehouse" "sound/patent/frozen.ogg".   


::The one exception is that the first button, which is "station 0", should be reserved for turning all sounds off, that is, radio silence.  This is important because it is the initial state of the setup when the game starts, even if you set up a trigger to turn on a station the moment after it starts.  (So even though you don't have a "station0" listed in the ambient_player, you still have the button for it to activate silence in the script).  Name the button something that lets you know it is for having no ambient sound, such as "silence" or "nosound".   
It is worth noting that one special "sound" is already loaded by default.   


::Add a "state_change_callback" property to each command button, with the value being "station_0", "station_1", "station_2", etc., sequentially for each button.  This name needs to be *exactly* like that (including the underscore; and don't forget that the first button is station_0 and is reserved for "no sound").  
'''"snd_silence" "silence"'''  


http://i42.tinypic.com/do19b8.jpg
You can see it if you check "Show Inherited Properties". This is the sound you use to turn "off" the ambient sounds playing, so there is silence in the zone.


'''The Gameworld'''
=== On Shader Names ===


:You trigger ambients in the gameworld with a trigger_multipleTo make one, first create a brush the size of the entrance to the zone you want to do (e.g., door-sized, or cave-entrance sized, or street-wide-to-the-sky-sized). It should be quite thick so that they player cannot run through it without it registring a hit.
One footnote on the sound shader names. The ''shader tdm_ambient_ambience_zoned.sndshd'' has a special command ("leadin") for its sounds that begins the ambient with a short pauseThis is to pre-empt a possible *pop* of sound for loud ambients which can occur when the player's system slows down (and only occasionally, at that), so the player never hears the pop.  (It's a quirk of how Doom3 does fade-ins; when you start a new sound, you have to rapidly fade it out first, like .001 seconds, then slowly fade back in. But if the system slows and the ambient starts loud, it might stretch that .001 to something audible.)
  Edit:Baddcog- These seem to now have been appended with a _z if you look at sound names in speaker dialog.


::Footnote: Roughly speaking, a trigger checks in pulses, and if the player is inside during a pulse there's a hit (at least, I think that's how it works)To get an idea of how quickly the checks pulse, when you have everything set up you can stand inside of one and watch how fast a message like "Station 2 is already on." repeats itself. Try to make it thick enough that a player could not get lucky and run through without that pulse registering him inside it.  I'm still experimenting myself with the ideal thickness, 8 occassionally misses if I try, so maybe something like 16 or 24 or 32 just to be safe?  (I will update this with a recommended number as I test more.) Just be sure to test your setup a lot, running in and out, to make sure the trigger never fails to start the ambient.  
If you alternatively use the address or the shader name from "Darkmod/sound/tdm_ambient_ambience.sndshd" (which lack the "leadin" property) it will otherwise work fine, but you won't be protected from that occasional little pop (at least until we find another fix). For ambients that start quietly or areas that won't slow the system down, it will probably never even be a problem and the player will never hear it, so using the address is fine, or using the tdm_ambient_ambience shader name if you don't want the tiny pause for whatever reasonBut if you want to control it, use the tdm_ambient_ambience_zoned name, and for a custom sound, to pre-empt the pop you'll need to use a custom shader with the "leadin" line. Use the "Darkmod/sound/tdm_ambient_ambience_zoned.sndshd" as a template to do that, and name the custom shader file something like YourFMsName.sndshd, and put it in the "sound" folder in your .pk4.


:Note you can also make multiple trigger_multiple's to cover the entrance how you want, e.g., if it's L-shaped or something.  It doesn't have to be just one wall.  Whatever works.  It also doesn't have to be a perfect fit (like a visPortal), just that the player cannot possibly enter the zone without touching a trigger, but it doesn't hurt making it a perfect fit either. 
=== Sound Properties ===


:Texture it with common/trigmulti, then with the brush highlighted in Dark Radiant, right click on it, create-entity, Darkmod/Triggers/Trigger_multiple, ok.  If you have others the same size (e.g., door sized) you can copy-paste it for them.   
Regarding properties, the speaker (the '''atdm:location_settings''' entity is a speaker in disguise :) has by default the properties "omni", "global", and "looping" already turned on, so that all sounds played are heard everywhere, from no direction, and will loop.  If you change these properties (e.g., for one ambient) the change will apply to all ambients on the speaker.  So you probably don't want to turn these properties off.   


:In the map, set up a trigger_multiple entity at every entrance to an area you want covered by a single omni ambient sound, completely covering the entrance so the player has to pass through it to get in the area, each targeting the command button of the station you want to play in that zone (e.g., property/value "target"/factory_zone, or whatever you named the command button you want to use)(Do not add a "wait" property).    When you exit the area, have another trigger_multiple at the entrance of the new area target the command button for a new station.   
Another spawnarg worth noting is "s_volume".  Since the location_settings also acts as a speaker, this will work to change volume (0=full volume, -10=half volume, -60=silence), again globally on all the ambients playing on it.  Unless you have a very good reason for doing so however (e.g., you want all your ambients much louder or quieter) you probably don't want to use the s_volume spawnargIf you want to change the volume of a specific ambient, you can do so on the '''info_location''' entity with the "volume" spawnarg as described below (note some of the issues involved with volume changes there as well), not the location_settings entity (or directly in the sound's soundshader).   


:Here's an image of that for reference: [http://i40.tinypic.com/22zvr7.jpg]  (One note:  After some testing, I think that this trigger is not thick enough, as I described above, but maybe another 8 units thicker or so would do.) 
Your entity will look like this:


:Be sure you cover every possible entry even those you aren't sure the player can cross, and match every entry-trigger with a mirroring exit-trigger for the adjacent area, and watch for possible busts like teleports (where you can just make sure the teleport process includes activating the command button for the ambient it's teleporting into).
[[Image:Speaker4.jpg]]


:If you want no sound for an area (i.e., just turn the previous ambient off), have a trigger_multiple turn on the first command button, "station_0", which I recommended you name something like "silence".
=== Sound Override ===


:There's one special case. If you want the player to start in an area with an ambient playing, place a trigger right over the area where the player starts, targeting the command button for the sound you want to use (except for the first button, "silence", which would be redundant since the game starts already on "station_0" no sound).
From version 2.04, an "override" property for sounds has been added to the location settings entity. The purpose is to allow a mapper to easily override the location ambient to play their own sound, such as an ambient when a discovery is made, to build tension, action music when the player is seen, or for reading a book, etc., and then return to the location sounds when it's done.  


== The Script ==
The default value for "override" is "0", meaning override is turned off, so the location ambients play as normal. Although it looks boolean, the data type is a string, so it takes any characters. If at any time in-game you dynamically change "override" to any non-zero value (I'll explain how below), it will override the location ambients to allow you to play your own sound as long as it is non-zero. To turn location ambient sounds back on, you change "override" back to "0".


Once you have all that set up, you need to create a script in something like notepad or wordpad. Remember to name it the name of your map "mapname.script" (and that you don't accidentally name it mapname.script.txt), and put it into the same folder as your map.  
If you change "override" to one of the pre-cached sound names (that is, a name with the "snd_" prefix mentioned above, such as "snd_alert"), then the location system will fade out the location ambient and play the ambient sound you registered under that name as an override ambient. If you open the console, it will report that the override ambient is playing. The override ambient will continue to play until override is changed back to zero or to another override ambient. This means if you just want to play a one-shot ambient, you need to use a script with a wait function that waits the duration of the ambient then turns override off to restore the location ambient, otherwise it will loop. Using the function sys.waitFor( $[speaker_name]); after running the speaker is supposed to automatically wait the duration, but this needs to be tested.


The following only creates three stations, plus the nosound "off" station, but it should be enough so that you know how to create more by changing the code accordingly. You can just copy it right into your script and it should work.  
If you change the override property to another value (not a sound name) such as "1", it will simply turn the system off, allowing the mapper to play a speaker in the silence if they prefer.  


<code>
There are two basic ways to change the "override" property dynamically, calling a script and triggering a target_setKeyVal entity in-game.
// ZONED AMBIENT SYSTEM (demagogue)
 
  //
1. Script. The script function to change a property on an entity is $tdm_location_settings_1.setKey( "override", "[snd_name]"); to turn on an override sound and $[tls_1].setKey( "override", "0"); to turn the location ambients back on. Note because it's a string data type, you need to put zero in quote marks/inverted commas. Also be sure to use the right name for the location settings entity. It may not have a "_1" suffix.
// Description:  This script allows a method in which a trigger turns on an omni ambient when you enter a zone, then turns it off when you leave, possibly starting a new ambient for the new zone.  
 
// The set up is described at http://modetwo.net/darkmod/wiki/index.php?title=Ambient_Sounds%2C_a_zone_approach.   
2. Target_setKeyVal. At the bottom of the entity list is target_setKeyVal (note it's not with other target entities). Create one of these entities and have it target the location settings entity (e.g., enter a property/key "target" "tdm_location_settings_1"). Now add the property/key "keyval" "override;snd_name" to turn on the override sound. Note you separate the propery and key with a semicolon and no space. Now when you trigger that entity with a trigger brush, button, or anything else, it will change the propery and your override ambient will fade in.  You can have another target_setkeyval entity with "keyval" "override;0" to turn override off and turn the location based ambients back on.
 
 
float station_state = 0; 
The tdm_location_settings entity has also got new properties for fading behavior. Thus, the mapper can make the override ambient fade in as normal, or have it blast in with no fade-in if they want. These properties are the same 'fading' type spawnargs as are on the info_location settings described below, and work in the same way, except they are on the location settings entity and only apply to override ambients. (Volume is not included however. You'd just use the speaker property "s_volume" directly, but you'd need to restore it after the override is over if you don't want the change applying to all sounds.) You can change them dynamically in the same way as override. If you have multiple overrides, some with fading and some without, and want to revert to the default fading without looking it up, you can enter "-1" as the value of a property and it uses the default value.
// the number corresponds to the "current radio station playing"
 
   
 
  void main()
You are finished with the ambient sound part now.
  {
 
}
== The Ambient Light ==
    
 
  void station_0(entity button, boolean bOpen, boolean bLocked, boolean bInterrupted) //Reserved for turning ambients off
You need to create an ambient light in your level. This will cover the entire level, and provide a minimum default light for when there is no other light covering a surface:
{
 
  if(bOpen)   
# '''{{RMB}} -> Create light'''
  {
# Move the light's origin to the center of your map, and drag its size so that it covers your entire map, and then some more. Remember to increase the size of that light when you build more area into your map!
    button.Close();
# Open the light inspector (default shortcut {{key|L}}) and set the light texture to '''lights/ambientlightnfo'''
    if (station_state != 0) // If any station other than "off" (0) is playing
# Set the color of the light to a reasonable default, f.i. ".08 .08 .08" This value will be used when a zone has no other ambient light settings
    {
# Open the entity inspector (default shortcut {{key|N}}) and set the name of the light to '''ambient_world'''
station_state = 0;
 
        $ambient_player.stopSound( SND_CHANNEL_BODY, false );  // stops any currently playing sound
The last step is important!
sys.print("The radio is now turned off.");  
 
    }
You are done now with the global ambient light.
    else  
 
    {
== The Location Entities ==
sys.print("The radio is already off.");
 
    }
Now you need to set up the location system so the game knows where the zones are, and their boundaries where changes will happen.  A location is basically any area that's closed in by brushes and marked portals and contains an info_location entity. Unlike a vis-portaled area (which is one area between [[vis_portals]], a.k.a. a "leaf"), a zone can cover more than one portaled area ("leaves").
  }
 
}
As an aside, locations are also used for setting the EAX properties in the area (e.g., the echo-y-ness of a big hall or cave, or the dullness of a small carpeted room). So you have a good reason to have them even in addition to handling ambient sounds and lights.   
 
void station_1(entity button, boolean bOpen, boolean bLocked, boolean bInterrupted)
=== info_locationseparator ===
{
 
  if(bOpen) 
As I suggested above, you have to mark all the portals leading in and out of a zone by hand. You do this with an entity called a '''info_locationseparator'''. Create one so it touches or pierces the portal you want as a boundary, and it will mark that portal as a location boundary. (Note, you only make one separator per portal. In the photo example below, the one separator is piercing the portal.)  
  {
 
    button.Close();
  '''{{RMB}} -> Create entity -> Darkmod -> Info -> info_locationseparator'''
    if (station_state != 1)  // If any station other than #1 is playing
 
    {
If you don't touch a portal with this entity, then it will not register in the game as the boundary to a new zone, and the zone will continue into the area on the other side of the portal.   This can be a good thing because, say you have a mansion with 30 portals inside. You can cover the whole area inside it with one info_location entity, by just marking the 2 or 3 portals leading into the mansion area through the doors and open windows, and everything inside that marked area will be counted as one location (just don't miss marking an exit, or have an un-portaled gap to the outside between brushes, or the location will leak outside.  It has to be hermetically sealed)
station_state = 1;
 
        $ambient_player.startSound( "snd_station1", SND_CHANNEL_BODY, false ); // starts station 1 looping
To re-iterate, a portal must include an info_locationseparator to mark a location boundary. (It may have been the case that in Doom, a portal merely touched by a door could mark such a boundary, as indicated by early discussions and entityDef comments. This is not true for TDM.)
sys.print("Radio station 1 is now playing.");
 
    }
Here are special considerations when placing an info_locationseparator:
  else 
 
    {
* Avoid any visportal that is subdivided by worldspawn. If you really need to separate zones there, break the visportal up into separate brushes. (Details about this problem and how it affects warnings are below.)
sys.print("Radio station 1 is already playing.");
* It is really the ''axis-aligned bounding box'' of the visportal that is interrogated for touching an info_locationseparator. This can lead to problems in the vicinity of a large, non-axis-aligned portal: some other location separator can be assigned to this visportal, even though you can clearly see in DR that they don't contact.
    }
 
  }
Starting in TDM '''2.10''', there are new console warnings about info_locationSeparator problems:
}
 
   
* a separator touching no visportal
  void station_2(entity button, boolean bOpen, boolean bLocked, boolean bInterrupted)
* a separator touching more than one visportal
  {
* a visportal touching more than one separator
  if(bOpen)  
 
  {
You may see a variation of the second case, where the visportal id is duplicated, e.g.:
    button.Close();
 
    if (station_state != 2)   
  WARNING:Separator locationsep_on_double_portal covers both portal 203 at 4 -380 -112 and portal 203 at 4 -380 -112
    {
 
      station_state = 2;
This occurs when a separator is applied to a visportal brush that is internally turned into multiple visportals with the same rectangle (or perimeter) and same origin. It happens when opaque geometry splits the visportal face into connected components (two for example), and these components separate different pairs of visleafs/areas. Note that you cannot reliably detect such situations with console command "r_showPortals 1".
      $ambient_player.startSound( "snd_station2", SND_CHANNEL_BODY, false ); // starts station 2 looping
 
      sys.print("Radio station 2 is now playing.");
=== info_location ===
    }
 
    else 
Now, inside the zone you want to have, create an '''info_location''' entity:
    {
 
sys.print("Radio station 2 is already playing.");
'''{{RMB}} -> Create entity -> Darkmod -> Info -> info_location'''
      }
 
  }
Anywhere in the space of the zone is fine.  Name it the name of the zone you want (to make it easy to find when you push {{key|J}}).
  }
 
==== Debugging Info_Location Overlaps ====
 
If there is more than one info_location in a given zone, there is an overlap. In that case, the game will just pick one and use that for the whole zone; the other will be ignored. This is not ideal.
  void station_3(entity button, boolean bOpen, boolean bLocked, boolean bInterrupted)  
 
{
At game startup, any such overlap is reported to the console as a warning. The underlying cause may be obvious, but not always. To help with this diagnosis, starting with TDM '''2.10''', a pointfile is generated that runs between the two info_location objects involved in the overlap, via a path within the overlap zone. The filename will be of form:
  if(bOpen)   
 
  {
pointfile_<info_location_name1>_overlaps_<info_location_name2>.lin
    button.Close();
 
    if (station_state != 3)
As with other types of pointfiles, this appears in your FM’s /map/ folder, and should be renamed temporarily to “<FM>.lin” when you want to load it for examination it in DR.
    {
 
station_state = 3;
== Settings per location/zone ==
        $ambient_player.startSound( "snd_station3", SND_CHANNEL_BODY, false );  // starts station 3 looping
 
sys.print("Radio station 3 is now playing.");
On each info_location, you can set multiple spawnargs that adjust the light or sound for that location, as well
    }
as run scripts. We cover them next:
    else 
 
    {
=== Ambient sound ===
sys.print("Radio station 3 is already playing.");
 
      }
Now create a spawnarg property of "ambient" with the value being the speaker-name of the sound you used in the location_settings entity above (NOT the soundshader name or file name). For example,
  }
 
}
  '''"ambient"    "snd_streets"'''
</code>
 
This will send a command to the location_settings (in its speaker capacity) to play the ambient it has registered under "snd_streets" that you entered. When you enter a new zone, in turn, the new info_location sends a command to the speaker to turn off that ambient and start a new one. 
 
If you wanted to have no ambient playing in the zone (and turn off any ambient started from another zone), you would use "snd_silence" as the value to command the speaker to turn off. Also, if you have an info_location with no "ambient" value at all, it will also turn off the ambient (in the present version of this system).  That means if you make a new location but want the same ambient playing in it as the location next to it, you still need to add the ambient name for the already-playing ambient for it to continue playing into the new zone; otherwise it will turn off. 
 
==== Examples ====
 
Here are two info_locations on two sides of a portal, with one info_locationseparator touching the portal (actually piercing it), and with each info_location containing the name of the ambient that will play in its respective zone.
 
[[Image:Snd_streets.jpg]]
 
[[Image:Snd_silence.jpg‎]]
 
That's basically it. The ambients will now turn on when you enter a zone and turn off when you enter a new zone, turning on the new ambient in that new zone (unless it's the same ambient, then it just keeps playing, useful e.g., if you want 2 different EAX but 1 ambient playing). If you open up the console, you should see a message saying that a new ambient is now playing in your current location, naming the ambient and location.
 
=== Sound Fading ===
 
Finally, there are a few other spawnargs on the info_locations that you usually don't have to worry about (with the possible exception of "volume"), but you can use them if you like, so I will mention them.  They concern the properties of the ambient fading.  Check the "Show Inhereted Properties" to see their default values.  Entering a new value will over-write the default value, but only for that one object.  
 
[[Image:Info_loc.jpg‎]]
 
==== fiduration ====
 
"Fade in duration". This is the length of time in seconds it takes for the in-coming ambient (the one for this zone) to completely fade-in.  The default value is 4 seconds.  You can, e.g., make the fade last much longer with a larger value. 
 
If you don't want your ambient to fade in at all, but start immediately at full volume, change this value to ".001" (NOT zero).
 
==== foduration ====
 
"Fade out duration". This is the length of time in seconds it takes for the out-going ambient (the one from the zone you're leaving) to completely fade-out.  Again, if you want it to cut right off without fading, use a value of ".001"
 
==== fidelay ====
 
"Fade in Delay". You can delay the beginning of the in-coming ambient's fade-in after you enter the new area. You might want to do this, for example, if you want the out-going ambient to completely fade out before you begin fading in the new ambient.  So you would set fidelay to the same time as the foduration.  By default it is set to .001 so that there is no delay and the fade in starts immediately.  If the out-going fade-out also starts immediately (which happens by default), then they blend together in a nice transition fade. 
 
==== fodelay ====
 
"Fade out Delay".  Similarly you can delay the beginning of the out-going ambient's fade out. You might want to do this if you wanted the in-coming ambient to completely fade in before you started the fade out of the old ambient.  Then you would set it to fiduration.  Like fidelay, it is set to .001 so there is no delay and the fade out starts immediately.  
 
==== fovolume ====
 
"Fade out volume".  This sets the volume to which the out-going ambient fades to. -60 turns it off, which is the default (0 means no decrease in volume at all).  If you go any higher than -60, than it will leave the old ambient still playing at a lower volume under the new ambient (at least until you enter a 3rd new zone).  Not sure why you'd ever want to do that, so you probably don't want to change it.  
 
==== volume ====
 
This allows the mapper to override the volume of the ambient sound once it has fully faded in, and uses the same decibel scale as the '''s_volume''' speaker parameter.  Note, though, that it uses the spanwarg "volume" instead.  I found a decibel-loudness calculator here: http://www.sengpielaudio.com/calculator-levelchange.htm that lets you compute the decibel number for relative loudness.  According to it, "0" is the native volume of the sound (the default), "-60" is silence, "-4" is 3/4 (.75) of the volume, "-10" is half (.5) the volume, "-17" is 0.3 of the volume, and it goes quickly down from there.  "10" is double, but be warned there may be problems with positive numbers (e.g., volume clipping) and you should keep it not far above 0 if at all. See [[Setting Up Speakers#volume/ s_volume|"Volume / S_Volume"]] & [[Volume Issues]] for more detailed discussion of the volume parameter. 
 
'''Do not override ambient volumes unless strictly necessary'''. Maps with wildly different ambient volume settings result in a poor experience for players, and limit the ability of the Dark Mod team to respond to volume-related issues by changing default volumes in sound shader declarations.
 
 
These last two ('''foduration_2foip''' and '''fiduration_2foip''') are pretty obscure situations, so you probably don't need to ever mess with them unless you want to completely get rid of ambient fades (then you can set them to .001 like the other 'fade duration' properties).
 
==== foduration_2foip ====
 
"Fade-out duration during a '2 Fade-outs in progress' situation".  This provides a shorter fade-out duration (in seconds) for when the player quickly runs into a 3rd location, such that the 1st and 2nd ambients from areas the player just left are still fading out (hence the 2 fade-outs). But the quickend-fade-out only applies to the 1st ambient fading out. (i.e., not of the ambient you just left, but the one you left just before that -- unless you're backtracking, then it just fades the prev-prev ambient back in).  Fading out that prev-prev ambient more quickly in turn quickens the time the new fade-in can start (because you can't have 3 sounds on at the same time on the zone speaker, only 2).  So it in effect reduces the gap of quiet that happens before the newest ambient fully fades-in by enabling it to start its fade-in faster.  The default is 1 second.   
 
==== fiduration_2foip ====
 
"Fade-in duration during a '2 Fade-outs in progress' situation". Like its counterpart, this sets a shorter duration of a fade-in of a new ambient (in seconds) when you run into a 3rd location, while the ambients from the 1st and 2nd locations are fading out.  Again, because so many ambients are fading out, it can leave a gap of silence for a bit until the new 3rd ambient fades in.  So setting a shorter duration than usual for the newest ambient to fully fade in helps shorten the quiet space.  The default is 2 seconds (as opposed to a normal default fade-in duration of 4 seconds)
 
 
==== Example of Location with No Fading Transition ====
 
As mentioned above, if you wanted a set-up that turned off all fades so that the out-going ambient shut directly off and the in-coming ambient turns directly on, you'd change all the duration properties to .001, like the following image.  (Note that this modifies the transition only for entering this one location.  All the other info_locations maintain their default values unless you also change them by hand.)   
 
[[image: Fade_duration.jpg‎]]
 
=== Ambient light settings ===
 
These spawnargs can be set on '''atdm:info_location''' entities and apply to the current zone then:
 
==== "ambient_light" ====
 
This spawnarg specifies the ambient light color for this zone. Setting it f.i. to "0.10 0.02 0.02" would fade the ambient light to a slightly reddish color. This can be useful for a lava cave. Other examples are slightly blue light for moonlit outsides, greenish cast for caves or underwater areas etc.
 
If you do not set this on an info_location entity, the default value of the global "ambient_world" entity will be used.
 
'''Notes:'''
 
:*You need to set the color values as fractions between 0 and 1, e.g. '''"0.08 0.08 0.02"''' and '''NOT''' as integers like "8 8 2", or it will not work.
 
:*If the color change between two zones is too big, the fading and change can become obvious to the player and spoil the subtlety. This can happen for instance if you zoom from a bluish-lit outside directly to a reddish-glowing cave inside. To help the transition, consider adding an intermediate zone with a light color that is either between the two, or a more neutral grey. Making it not possible to look from one zone to the other also helps; this way the player can't see that f.i. the outside ground suddenly also glows reddish when looking back from the cave entrance to the outside.
 
:*There's a bug with setting ambient_light to 0 0 0 (to be fixed in 2.05), so use 0.004 0.004 0.004 if you want to remove ambient light from a location.
 
==== "ambient_light_fade_time" ====
 
This specifies the time in seconds it will take to fade from the current ambient light color to the one specified for this location. A slower fade means more gradual light changes, so they don't become too obvious to the player. Use at least 3, better 5 or 7 seconds.
 
If set to -1, the default time specified at the '''atdm:location_settings''' entity will be used.
 
==== "ambient_light_fade_delay" ====
 
This specifies the time in seconds the fading will be delayed when the player changes location.
 
Useful to prevent fades toggling back and forth when the player stands in a doorway and goes a nudge forward/backwards. A good value is at least 1 second.
 
If set to -1, the default time specified at the '''atdm:location_settings''' entity will be used.
 
==== "ambient_light_dynamic" ====
 
'''This is a factor''', all lights in the current area will be summed together and scaled by this value.
 
If set to a value other than "0 0 0", the lights in the current zone will be all summed together and then
added to the current base ambient light. This happens with the frequency of '''update_period'''.
 
The basic effect is that a roaring fire also slightly makes the walls flicker, and extinguishing all lights in a room would decrease the ambient light slightly.
 
The factor should be set up so that for large and dark rooms (e.g. caves) the dynamic part is small, while for small, bright rooms (white walls) the dynamic part is bigger. Examples are: "0.05 0.05 0.05" and "0.1 0.1 0.1".
 
Note that setting the dynamic factor to high can result in the player being busted by lights that are turned on in the room even when the player is in the shadow.
 
See also the article about [[Dynamic ambient light]].
 
==== "ambient_light_dynamic_cap" ====
 
Used to cap the dynamic ambient light part. If set to 0, will be ignored, so to have a very very small
cap, set to "0.01", e.g. if you want only the red part to be dynamic, set it f.i. "0.15 0.01 0.01".
 
See also the article about [[Dynamic ambient light]].
 
==== "ambient_light_falloff" ====
 
Possible values are:


In a future edition I hope to explain all the parts of this script in more detail here.
* -1 on info_location: use the value from the atdm:location_settings entity
* 0 - no dynamic falloff
* 0.5 - small, based on square root of distance
* 1 - medium, linear dynamic falloff (should be used as it looks best)
* 2 - high, square of distance based falloff


For now I should make the probably obvious point that if you want to add more stations, you can use the three stations I set up here as a template, to add more "void" entries in the script, e.g., changing things like "station_3", "station_state != 3", "station_state = 3" "snd_station3", etc, and replacing all the 3's with, say 4's if it's for the 4th ambient you want covered.  You can even just copy and paste an entire "void" entry and just change the numbers.
See also the article about [[Dynamic ambient light]].


A note on the "sys.print" comments.  When you have the script up and running, you can go into the console in-game and follow their operation through these comments when you touch a trigger.  You won't see the message only if the script isn't running (then you know it's probably a problem either with the trigger misspelling its command button's name in its "target" property, or the command button's state_change_callback property misspelling its value and not starting the script at all.)  I find them very useful, especially at the beginning, so I know exactly when an ambient is starting.  They can also be useful if they tell you "Station X is now playing", but you don't hear it.  Then the problem is probably that you mispelled the soundshader so it doesn't have a sound to play, or you used a soundshader that doesn't have a property like "global", so it's playing but you can't hear it. 
==== "ambient_light_dist_scale" ====


Once you have everything working how you like, however, then these messages may get in your way, especially when they flood the console with "Station X is already playing" when you are standing in a trigger. You can add "//" in front of the sys.print line (as you can see in other examples) so the script ignores them.  Note that for the sys.print function in the "else" section, you will have to get the script to ignore the entire "else" clause, not just the sys.print line.  To do that you can add "/*" in front of the word "else", and "*/" after the "}" following the sys.print line.  Alternatively you could just delete the entire "else" clause, as well as the standalone sys.print lines, but the nice thing about the "//" is that you can restore the lines if a problem comes up later you want to debug.
A factor to scale the distance before applying a dynamic light falloff. Only used then '''ambient_light_dynamic_falloff''' is not 0. Good values are around 1.0.


This is how the script would look with those lines commented out:
See also the article about [[Dynamic ambient light]].
<code>
void station_3(entity button, boolean bOpen, boolean bLocked, boolean bInterrupted)
  {
  if(bOpen) 
  {
  button.Close();
  if (station_state != 3) 
    {
station_state = 3;
        $ambient_player.startSound( "snd_station3", SND_CHANNEL_BODY, false );  // starts station 3 looping
// sys.print("Radio station 3 is now playing.");
    }
  /* else 
    {
sys.print("Radio station 3 is already playing.");
      } */
  }
}
</code>


On reflection, I am debating getting rid of the "else" clause altogether and just leaving the "Radio Station X is now playing" comments, since they are the most useful for debugging and tracking the script.  But I'll leave it there for now since it sometimes comes in handy, and an author can decide to comment it out later.
=== Script calls ===


== Tips and Issues ==
The next four spawnargs all specify script functions to call when the player enters or exits a zone. The call passes the zone as a function parameter. Note that the very first zone is also "entered" by the player when the map starts. That means "call_once_on_entry" for the start zone should be called at start-time.


'''General Tips'''
==== "call_once_on_exit" ====


:It probably goes without saying, but be sure to test your set up by running back and forth across two zones over and over, to make sure the ambients turn on and off when they're supposed to and it doesn't break or the player gets through without it triggering.
Called only once when the zone is left by the player.


:If there is a problem, you can put the StartingPoint in the blueroom to push command buttons and make sure the sounds turn on, and replace each other as they should, and that the first button stops all sound. 
==== "call_once_on_entry" ====


:Some typical problems might be, if the buttons work but the triggers don't, make sure the trigger spells the name of the button correctly in its "target" property.  Also make sure you spell the buttons' "state_change_callback" value correctly.
Called only once when the zone is entered by the player.


:The lines sys.print("Radio station X is now/already playing."); are there so you can go into the console and see exactly when and how the triggers are working.  If you don't see that line in the console when you touch a trigger, it means the script isn't even being called on, so check things like the trigger's target spells the command button's name correctly, and the command button spells the state_change_callback value properly.  Once you have it set up and working, you can add "//" in front of the sys.print lines to turn them off. 
==== "call_on_exit" ====


'''Soundshaders and Modifying or Custom Sounds '''
Always called when the player leaves that zone.


:As I mentioned above, I've made an triggered-ambient soundshader for general use with this method you can download from the link above.  But you may want to change some of its instructions or add custom ambients, so I'll describe a little about soundshaders here.  You can browse some soundshaders in the Darkmod/sound folder to get an idea of how they look. 
==== "call_on_entry" ====


:A typical entry looks like this:
Always called when the player enters that zone.


<code>
==== Example scripts ====
alien01_loop_trig
{
description "Made by Muze"
global
omnidirectional
looping
sound/ambient/ambience/alien01_loop.ogg
}
</code>


:For reference, the functions in the soundshader are:
Here is an example script that spawns an object where the '''info_location''' entity is for the zone the player just left. You can call this script by setting:


::* The first line is the name of the shader that you will use as your snd_station* values, as you can tell by looking at the example property/value I gave above.  
  '''"call_once_on_exit" "spawn_pear"'''
::* "global" means it will be heard everywhere in the map. 
::* "omnidirectional" means it will be heard from all directions (i.e., not from the direction of the speaker itself). 
::* "looping" means when the ambient is finished, it will start over (not always appropriate for every ambient). 
::* And the final line is the address of the actual sound file played.  As you can see, you can listen to all the Darkmod ambients in the folder "sound/ambient/ambience" to help you choose one (if you have a soundplayer that can play .ogg's). 


:If you want to modify some of these properties for your own purposes (e.g., deleting the looping, or adjusting the volume), or add a custom ambient sound, you can easily make a new soundshader by making a new text file with the file extension .sndshd and add an entry using the above example as a template, with your fan mission's name (or a simplified version) in the title, such as patent_ambient.sndshd (what I use; make sure you don't accidentally save it something like YourMapName_ambient.sndshd.txt).  Be sure if you modify an existing ambient that you use a different name, e.g., if I modified alien01_loop_trig, I might name it alien01_loop_patent. 
on your info_location entity.


:DON'T just modify the soundshader I made, or any tdm_* soundshader, and re-save it because you need to be in the habit of having all your custom sound-set ups in your own soundshader unique to your own fan mission. There are lots of tdm_* soundshaders that you don't want to alter, but you might use their sounds in your own way by making new entries in your own soundshader with the same sounds.
<pre>
void spawn_pear( entity old_zone )
  {
  // Get the name of the location that the player just left:
  string location_name = old_zone.getName();
  // and display it on the console for debugging
  sys.print ("Spawning pear after leaving " + location_name + "\n");
  // spawn the pear entity
  entity pear = sys.spawn("atdm:moveable_food_pear");


:A typical modification would be the volume. To change the volume, add a new line "volume" with a number (positive or negative), such as "volume 10" or "volume -10".  You can test the different volumes by changing your custom soundshader, then starting in the blueroom pushing command buttons to listen to it in-game. 
  // and now get the position of the old location entity:
  vector origin = old_zone.getOrigin();
  // finally move the pear to the point of the info_location
  pear.setOrigin( origin );
  // so once the player exits this location, ONE pear will spawn and fall down
  }
</pre>


:If you wanted to use a custom ambient, and note it has to be in either .ogg or .wav format (see the wiki entry on sound formats for more info), just make a custom soundshader with an entry for it using the template above, with its own unique name, and the address pointing to where your custom soundfile will be when packaged for release (I need to learn about this myself, and I'll add that information sometime; but the information on that is hopefully somewhere on the wiki or forums.)
==== Example script to turn entities on/off when player enters a location ====
The basic problem with normal TDM entity work is the difficulty of turning something deliberately and reliably on or off. You never turn something on or off, but instead you send toggles to the entities. Let's say you have a omni sound (a sound which is heard everywhere) which you need to shut off when the player goes to a certain location. Trigger_multiples trigger targets when the player stands on their volume, which may result in a completely wrong situation depending how long the player stands on the trigger.


:Once you modify or have a custom sound in your own soundshader, dropped into the Darkmod/sound folder, make sure you use its name for the value in the ambient_player entity to use it (or any other speaker with the property s_shader, for that matter).
Location entity scripting can be of great help here. This kind of scripting can generally solve any kind of problem where the mapper wants something to be switched specifically on and off when the player moves to a specific location. Here is an example, which was used to switch off a global thunder sound when the player moves to a location where the rumbling cannot be heard.
(Note to self:  I should also explain how to package the soundshader and custom sounds with the PK4 file when you get your fan mission ready for release).


'''Ambient Fades and Blends'''
Set up location setting as described above. Put a location separator on the visportal you want to be the point which switches your target on/off. Give the info_location-entity spawnargs
*call_on_entry yourscript
:If you want the ambient to fade in, as with a speaker, but still use this method, instead of shaping the trigger_multiples like 2 walls on each side of the entrance, you can shape them like a box (three walls around the entrance, with an inside layer and an outside layer, so 6 brushes altogether) and place a normal speaker inside ("waitfortrigger"/0, "global"/0, "looping"/1, "omni"/1, "s_mindistance" & "s_maxdistance" set appropriately, remember there is a button in Dark Radiant to see the speaker radius). 
*call_on_exit yourscript
:Here's an image of that for reference: [http://i39.tinypic.com/6ypmhi.jpg]
:It just uses 2 walls (four brushes), with the third being a brick wall. 


:The speaker will fade the ambient in, the trigger will turn on the ambient, and as the speaker fades out the ambient continues.  One issue, the ambient sound will duplicate in the overlap between speaker and trigger-on. Depending on the ambient that may sound strange. You may adjust the speaker radius and trigger locations to minimize that, though (better than I did for my screenshot above). 
Then go and create in your /maps folder a file yourmapname.script. Put in the following data there:


:In testing this, I found it good to have the ambient trigger be the outside layer and a "nosound" trigger (i.e., station0) the inside, the speaker radius does the fade in the nosound area in the middle. Then if you wanted the area on the other side of that door to have its own ambient, you'd create another 3 walls on the other side of that doorway with the ambient trigger an outside layer and a "nosound" trigger an inside layer, and its own speaker fading in its middle (basically just mirroring the screenshot I took for the other side of the doorway). Then the two speakers could even overlap in the middle so you get a nice blending gradient between the two ambients.  
void yourscript(entity zone)
{
sys.println("script yourscript running..");
sys.trigger( $entity_name );
}
  void main()
  {
  }


:(Also when testing this, I had some problems creating three walls as one trigger_multiple and had better luck creating three separate trigger_multiples, each as a single wall-brush. I don't know if that's a real problem or not, just something to remember).
*yourscript is the name of your script you call. Its parameter 'zone' has the location entity that called it, either:
** the location you're entering, when yourscript() is called via a call_on_entry; or
** the location you're leaving, when yourscript() is called via call_on_exit.
*sys.println is a debug feature which gives the text in console when the script is triggered. You can leave it for testing and remove it before releasing your map.
*sys.trigger ($entity_name) replace the entity_name with the entity which you want to be triggered.


:Note to self, there is a script function called "fadeSound" which may fade the new ambient in automatically in the script, which would be incredibly convienent. I will test this (and you the author can test it as well), to hopefully figure out how it works. For reference, the short discription id gave for it is:
Now your system works like this:  
*When the player is outside the location everything is normal. Ie sound off, light off, etc.
*When the player enters the location, entity_name gets triggered once. Ie sound on, light on, etc.
*When the player leaves the location, entity_name gets triggered again. Ie sound off, light off, etc.


::Fades the sound on this entity to a new level over a period of time. 
== See also ==
::Use SND_CHANNEL_ANY for all currently playing sounds.
::scriptEvent void fadeSound( float channel, float newLevel, float fadeTime );


* [[Adding ambient Sounds to your Map]]
* [[Dynamic ambient light]].
* [[Ambient Sounds - Zone (using triggers)]]
* [[Sound File Formats]]
* Debugging a location overlap:
**Forum posts about pre-2.10 approaches [https://forums.thedarkmod.com/index.php?/topic/9082-newbie-darkradiant-questions/&do=findComment&comment=451983 here] and [https://forums.thedarkmod.com/index.php?/topic/9082-newbie-darkradiant-questions/&do=findComment&comment=452084 here].
**Features introduced in TDM 2.10 [https://bugs.thedarkmod.com/view.php?id=5354#c13877 responding to bug report 0005354] and part of new location diagnostics announced [https://forums.thedarkmod.com/index.php?/topic/20905-210-dmap-locations-diagnostics/ here].


{{sound}}
{{tutorial-sound}}
{{tutorial-sound}}
{{editing}}
{{tutorial-scripting}}
{{tutorial-scripting}}
[[Category:Ambient Light]]

Latest revision as of 00:08, 7 October 2023

Introduction

This article describes how to setup locations (also called "location zones" or "zones") in your level, and then use these to:

  • fade to different ambient light levels for each zone
  • fade between different ambient sounds in each zone
  • run scripts automatically when the player enters or exits a zone




You need to use four types of entities in your map:

  • a special global entity (e.g. add only one, the position does not matter): atdm:location_settings
  • a special global light entity named ambient_world
  • multiple info_location and info_locationseparation entities

Note: Two alternative ways to get ambient sounds in your map are using speakers (Adding ambient Sounds to your Map), and using triggers (Ambient Sounds - Zone (using triggers)). Speakers are good if you only want your ambient to cover a definite radius and have a very simple setup. Triggers are basically obsoleted by the method in this tutorial. The only time you might still want to use a trigger-system is if you want a ambient sound to begin at a place where for some reason you can't create a portal to mark the zone boundary, but you can still have a trigger brush. There are some issues with a trigger system, too (f.i. their CPU and memory usage and more importantly they are not failsafe like locations are), that make the method in this tutorial superior.

The atdm:location_settings entity

Create a atdm:location_settings entity in your blueroom (i.e., a room off to the side the player will never enter) or somewhere else in your map:

Click the right mouse button -> Create entity -> Darkmod -> Info -> atdm:location_settings

Default values

The location_settings entity takes a few default spawnargs related to the ambient light settings:

For their meaning and values, please refer to their sections.

update_period

The spawnarg update_period specifies the time in seconds between updates. A good value is 0.2, e.g. 5 times per second. That avoids to run the script too often, and still allows seamless transitions.

Sound Shaders

In the entity's spawnargs, put the names of all the ambient sounds you want to play in your map. (This pre-loads the sounds into the speaker so there isn't a pause when they start playing.) Put it in one of these property/value forms (without the quote marks):

"snd_streets" "city_night01_loop_z"
"snd_mansion" "sound/ambient/ambience/mansion_tense01a.ogg"

The left hand spawnarg should have a "snd_" prefix, and any name, but most useful is probably the name of the area it is for. You'll use this name again when you place the location markers or if you use the override system.

The right hand value points to the actual sound file that will play. You can enter either the soundshader name, or the address where the sound file is (but see footnote). You can see the soundshader names of ambient-ready sounds in the sub-folder "Darkmod/sound/tdm_ambient_ambience_zoned.sndshd". You can find the address of the sound files in "Darkmod/sound/ambient/ambience" (remember to start the address with "sound" in the spawnarg). You can also listen to the sounds at that location. They are are in .ogg format. But it's easier to just create a speaker (left-click>speaker) and it will give you a sound directory where can play sound files and find the names. No searching through sound folders outside of DR (thanks to Badcog for that tip).

Note that it is very easy to add custom ambients with this system. Just create a folder in your .pk4 (a .zip file renamed to .pk4) and name the folder "sound", with another folder inside it named after your FM or an abbreviation. Put your custom sound inside that second folder. (E.g., I have a custom ambient named Frozen.ogg, and my FM's name is Patently Dangerous. So in my .pk4 was "sound/patent/frozen.ogg". Note that ambients must be in either .ogg or .wav format. Now in your speaker_zone_ambient, just put the sound's address in the spawnarg (but see footnote). In my case, I have "snd_warehouse" "sound/patent/frozen.ogg".

It is worth noting that one special "sound" is already loaded by default.

"snd_silence" "silence"  

You can see it if you check "Show Inherited Properties". This is the sound you use to turn "off" the ambient sounds playing, so there is silence in the zone.

On Shader Names

One footnote on the sound shader names. The shader tdm_ambient_ambience_zoned.sndshd has a special command ("leadin") for its sounds that begins the ambient with a short pause. This is to pre-empt a possible *pop* of sound for loud ambients which can occur when the player's system slows down (and only occasionally, at that), so the player never hears the pop. (It's a quirk of how Doom3 does fade-ins; when you start a new sound, you have to rapidly fade it out first, like .001 seconds, then slowly fade back in. But if the system slows and the ambient starts loud, it might stretch that .001 to something audible.)

Edit:Baddcog- These seem to now have been appended with a _z if you look at sound names in speaker dialog.

If you alternatively use the address or the shader name from "Darkmod/sound/tdm_ambient_ambience.sndshd" (which lack the "leadin" property) it will otherwise work fine, but you won't be protected from that occasional little pop (at least until we find another fix). For ambients that start quietly or areas that won't slow the system down, it will probably never even be a problem and the player will never hear it, so using the address is fine, or using the tdm_ambient_ambience shader name if you don't want the tiny pause for whatever reason. But if you want to control it, use the tdm_ambient_ambience_zoned name, and for a custom sound, to pre-empt the pop you'll need to use a custom shader with the "leadin" line. Use the "Darkmod/sound/tdm_ambient_ambience_zoned.sndshd" as a template to do that, and name the custom shader file something like YourFMsName.sndshd, and put it in the "sound" folder in your .pk4.

Sound Properties

Regarding properties, the speaker (the atdm:location_settings entity is a speaker in disguise :) has by default the properties "omni", "global", and "looping" already turned on, so that all sounds played are heard everywhere, from no direction, and will loop. If you change these properties (e.g., for one ambient) the change will apply to all ambients on the speaker. So you probably don't want to turn these properties off.

Another spawnarg worth noting is "s_volume". Since the location_settings also acts as a speaker, this will work to change volume (0=full volume, -10=half volume, -60=silence), again globally on all the ambients playing on it. Unless you have a very good reason for doing so however (e.g., you want all your ambients much louder or quieter) you probably don't want to use the s_volume spawnarg. If you want to change the volume of a specific ambient, you can do so on the info_location entity with the "volume" spawnarg as described below (note some of the issues involved with volume changes there as well), not the location_settings entity (or directly in the sound's soundshader).

Your entity will look like this:

Speaker4.jpg

Sound Override

From version 2.04, an "override" property for sounds has been added to the location settings entity. The purpose is to allow a mapper to easily override the location ambient to play their own sound, such as an ambient when a discovery is made, to build tension, action music when the player is seen, or for reading a book, etc., and then return to the location sounds when it's done.

The default value for "override" is "0", meaning override is turned off, so the location ambients play as normal. Although it looks boolean, the data type is a string, so it takes any characters. If at any time in-game you dynamically change "override" to any non-zero value (I'll explain how below), it will override the location ambients to allow you to play your own sound as long as it is non-zero. To turn location ambient sounds back on, you change "override" back to "0".

If you change "override" to one of the pre-cached sound names (that is, a name with the "snd_" prefix mentioned above, such as "snd_alert"), then the location system will fade out the location ambient and play the ambient sound you registered under that name as an override ambient. If you open the console, it will report that the override ambient is playing. The override ambient will continue to play until override is changed back to zero or to another override ambient. This means if you just want to play a one-shot ambient, you need to use a script with a wait function that waits the duration of the ambient then turns override off to restore the location ambient, otherwise it will loop. Using the function sys.waitFor( $[speaker_name]); after running the speaker is supposed to automatically wait the duration, but this needs to be tested.

If you change the override property to another value (not a sound name) such as "1", it will simply turn the system off, allowing the mapper to play a speaker in the silence if they prefer.

There are two basic ways to change the "override" property dynamically, calling a script and triggering a target_setKeyVal entity in-game.

1. Script. The script function to change a property on an entity is $tdm_location_settings_1.setKey( "override", "[snd_name]"); to turn on an override sound and $[tls_1].setKey( "override", "0"); to turn the location ambients back on. Note because it's a string data type, you need to put zero in quote marks/inverted commas. Also be sure to use the right name for the location settings entity. It may not have a "_1" suffix.

2. Target_setKeyVal. At the bottom of the entity list is target_setKeyVal (note it's not with other target entities). Create one of these entities and have it target the location settings entity (e.g., enter a property/key "target" "tdm_location_settings_1"). Now add the property/key "keyval" "override;snd_name" to turn on the override sound. Note you separate the propery and key with a semicolon and no space. Now when you trigger that entity with a trigger brush, button, or anything else, it will change the propery and your override ambient will fade in. You can have another target_setkeyval entity with "keyval" "override;0" to turn override off and turn the location based ambients back on.

The tdm_location_settings entity has also got new properties for fading behavior. Thus, the mapper can make the override ambient fade in as normal, or have it blast in with no fade-in if they want. These properties are the same 'fading' type spawnargs as are on the info_location settings described below, and work in the same way, except they are on the location settings entity and only apply to override ambients. (Volume is not included however. You'd just use the speaker property "s_volume" directly, but you'd need to restore it after the override is over if you don't want the change applying to all sounds.) You can change them dynamically in the same way as override. If you have multiple overrides, some with fading and some without, and want to revert to the default fading without looking it up, you can enter "-1" as the value of a property and it uses the default value.


You are finished with the ambient sound part now.

The Ambient Light

You need to create an ambient light in your level. This will cover the entire level, and provide a minimum default light for when there is no other light covering a surface:

  1. Click the right mouse button -> Create light
  2. Move the light's origin to the center of your map, and drag its size so that it covers your entire map, and then some more. Remember to increase the size of that light when you build more area into your map!
  3. Open the light inspector (default shortcut L) and set the light texture to lights/ambientlightnfo
  4. Set the color of the light to a reasonable default, f.i. ".08 .08 .08" This value will be used when a zone has no other ambient light settings
  5. Open the entity inspector (default shortcut N) and set the name of the light to ambient_world

The last step is important!

You are done now with the global ambient light.

The Location Entities

Now you need to set up the location system so the game knows where the zones are, and their boundaries where changes will happen. A location is basically any area that's closed in by brushes and marked portals and contains an info_location entity. Unlike a vis-portaled area (which is one area between vis_portals, a.k.a. a "leaf"), a zone can cover more than one portaled area ("leaves").

As an aside, locations are also used for setting the EAX properties in the area (e.g., the echo-y-ness of a big hall or cave, or the dullness of a small carpeted room). So you have a good reason to have them even in addition to handling ambient sounds and lights.

info_locationseparator

As I suggested above, you have to mark all the portals leading in and out of a zone by hand. You do this with an entity called a info_locationseparator. Create one so it touches or pierces the portal you want as a boundary, and it will mark that portal as a location boundary. (Note, you only make one separator per portal. In the photo example below, the one separator is piercing the portal.)

Click the right mouse button -> Create entity -> Darkmod -> Info -> info_locationseparator

If you don't touch a portal with this entity, then it will not register in the game as the boundary to a new zone, and the zone will continue into the area on the other side of the portal. This can be a good thing because, say you have a mansion with 30 portals inside. You can cover the whole area inside it with one info_location entity, by just marking the 2 or 3 portals leading into the mansion area through the doors and open windows, and everything inside that marked area will be counted as one location (just don't miss marking an exit, or have an un-portaled gap to the outside between brushes, or the location will leak outside. It has to be hermetically sealed).

To re-iterate, a portal must include an info_locationseparator to mark a location boundary. (It may have been the case that in Doom, a portal merely touched by a door could mark such a boundary, as indicated by early discussions and entityDef comments. This is not true for TDM.)

Here are special considerations when placing an info_locationseparator:

  • Avoid any visportal that is subdivided by worldspawn. If you really need to separate zones there, break the visportal up into separate brushes. (Details about this problem and how it affects warnings are below.)
  • It is really the axis-aligned bounding box of the visportal that is interrogated for touching an info_locationseparator. This can lead to problems in the vicinity of a large, non-axis-aligned portal: some other location separator can be assigned to this visportal, even though you can clearly see in DR that they don't contact.

Starting in TDM 2.10, there are new console warnings about info_locationSeparator problems:

  • a separator touching no visportal
  • a separator touching more than one visportal
  • a visportal touching more than one separator

You may see a variation of the second case, where the visportal id is duplicated, e.g.:

WARNING:Separator locationsep_on_double_portal covers both portal 203 at 4 -380 -112 and portal 203 at 4 -380 -112

This occurs when a separator is applied to a visportal brush that is internally turned into multiple visportals with the same rectangle (or perimeter) and same origin. It happens when opaque geometry splits the visportal face into connected components (two for example), and these components separate different pairs of visleafs/areas. Note that you cannot reliably detect such situations with console command "r_showPortals 1".

info_location

Now, inside the zone you want to have, create an info_location entity:

Click the right mouse button -> Create entity -> Darkmod -> Info -> info_location

Anywhere in the space of the zone is fine. Name it the name of the zone you want (to make it easy to find when you push J).

Debugging Info_Location Overlaps

If there is more than one info_location in a given zone, there is an overlap. In that case, the game will just pick one and use that for the whole zone; the other will be ignored. This is not ideal.

At game startup, any such overlap is reported to the console as a warning. The underlying cause may be obvious, but not always. To help with this diagnosis, starting with TDM 2.10, a pointfile is generated that runs between the two info_location objects involved in the overlap, via a path within the overlap zone. The filename will be of form:

pointfile_<info_location_name1>_overlaps_<info_location_name2>.lin

As with other types of pointfiles, this appears in your FM’s /map/ folder, and should be renamed temporarily to “<FM>.lin” when you want to load it for examination it in DR.

Settings per location/zone

On each info_location, you can set multiple spawnargs that adjust the light or sound for that location, as well as run scripts. We cover them next:

Ambient sound

Now create a spawnarg property of "ambient" with the value being the speaker-name of the sound you used in the location_settings entity above (NOT the soundshader name or file name). For example,

"ambient"    "snd_streets"

This will send a command to the location_settings (in its speaker capacity) to play the ambient it has registered under "snd_streets" that you entered. When you enter a new zone, in turn, the new info_location sends a command to the speaker to turn off that ambient and start a new one.

If you wanted to have no ambient playing in the zone (and turn off any ambient started from another zone), you would use "snd_silence" as the value to command the speaker to turn off. Also, if you have an info_location with no "ambient" value at all, it will also turn off the ambient (in the present version of this system). That means if you make a new location but want the same ambient playing in it as the location next to it, you still need to add the ambient name for the already-playing ambient for it to continue playing into the new zone; otherwise it will turn off.

Examples

Here are two info_locations on two sides of a portal, with one info_locationseparator touching the portal (actually piercing it), and with each info_location containing the name of the ambient that will play in its respective zone.

Snd streets.jpg

Snd silence.jpg

That's basically it. The ambients will now turn on when you enter a zone and turn off when you enter a new zone, turning on the new ambient in that new zone (unless it's the same ambient, then it just keeps playing, useful e.g., if you want 2 different EAX but 1 ambient playing). If you open up the console, you should see a message saying that a new ambient is now playing in your current location, naming the ambient and location.

Sound Fading

Finally, there are a few other spawnargs on the info_locations that you usually don't have to worry about (with the possible exception of "volume"), but you can use them if you like, so I will mention them. They concern the properties of the ambient fading. Check the "Show Inhereted Properties" to see their default values. Entering a new value will over-write the default value, but only for that one object.

Info loc.jpg

fiduration

"Fade in duration". This is the length of time in seconds it takes for the in-coming ambient (the one for this zone) to completely fade-in. The default value is 4 seconds. You can, e.g., make the fade last much longer with a larger value.

If you don't want your ambient to fade in at all, but start immediately at full volume, change this value to ".001" (NOT zero).

foduration

"Fade out duration". This is the length of time in seconds it takes for the out-going ambient (the one from the zone you're leaving) to completely fade-out. Again, if you want it to cut right off without fading, use a value of ".001".

fidelay

"Fade in Delay". You can delay the beginning of the in-coming ambient's fade-in after you enter the new area. You might want to do this, for example, if you want the out-going ambient to completely fade out before you begin fading in the new ambient. So you would set fidelay to the same time as the foduration. By default it is set to .001 so that there is no delay and the fade in starts immediately. If the out-going fade-out also starts immediately (which happens by default), then they blend together in a nice transition fade.

fodelay

"Fade out Delay". Similarly you can delay the beginning of the out-going ambient's fade out. You might want to do this if you wanted the in-coming ambient to completely fade in before you started the fade out of the old ambient. Then you would set it to fiduration. Like fidelay, it is set to .001 so there is no delay and the fade out starts immediately.

fovolume

"Fade out volume". This sets the volume to which the out-going ambient fades to. -60 turns it off, which is the default (0 means no decrease in volume at all). If you go any higher than -60, than it will leave the old ambient still playing at a lower volume under the new ambient (at least until you enter a 3rd new zone). Not sure why you'd ever want to do that, so you probably don't want to change it.

volume

This allows the mapper to override the volume of the ambient sound once it has fully faded in, and uses the same decibel scale as the s_volume speaker parameter. Note, though, that it uses the spanwarg "volume" instead. I found a decibel-loudness calculator here: http://www.sengpielaudio.com/calculator-levelchange.htm that lets you compute the decibel number for relative loudness. According to it, "0" is the native volume of the sound (the default), "-60" is silence, "-4" is 3/4 (.75) of the volume, "-10" is half (.5) the volume, "-17" is 0.3 of the volume, and it goes quickly down from there. "10" is double, but be warned there may be problems with positive numbers (e.g., volume clipping) and you should keep it not far above 0 if at all. See "Volume / S_Volume" & Volume Issues for more detailed discussion of the volume parameter.

Do not override ambient volumes unless strictly necessary. Maps with wildly different ambient volume settings result in a poor experience for players, and limit the ability of the Dark Mod team to respond to volume-related issues by changing default volumes in sound shader declarations.


These last two (foduration_2foip and fiduration_2foip) are pretty obscure situations, so you probably don't need to ever mess with them unless you want to completely get rid of ambient fades (then you can set them to .001 like the other 'fade duration' properties).

foduration_2foip

"Fade-out duration during a '2 Fade-outs in progress' situation". This provides a shorter fade-out duration (in seconds) for when the player quickly runs into a 3rd location, such that the 1st and 2nd ambients from areas the player just left are still fading out (hence the 2 fade-outs). But the quickend-fade-out only applies to the 1st ambient fading out. (i.e., not of the ambient you just left, but the one you left just before that -- unless you're backtracking, then it just fades the prev-prev ambient back in). Fading out that prev-prev ambient more quickly in turn quickens the time the new fade-in can start (because you can't have 3 sounds on at the same time on the zone speaker, only 2). So it in effect reduces the gap of quiet that happens before the newest ambient fully fades-in by enabling it to start its fade-in faster. The default is 1 second.

fiduration_2foip

"Fade-in duration during a '2 Fade-outs in progress' situation". Like its counterpart, this sets a shorter duration of a fade-in of a new ambient (in seconds) when you run into a 3rd location, while the ambients from the 1st and 2nd locations are fading out. Again, because so many ambients are fading out, it can leave a gap of silence for a bit until the new 3rd ambient fades in. So setting a shorter duration than usual for the newest ambient to fully fade in helps shorten the quiet space. The default is 2 seconds (as opposed to a normal default fade-in duration of 4 seconds).


Example of Location with No Fading Transition

As mentioned above, if you wanted a set-up that turned off all fades so that the out-going ambient shut directly off and the in-coming ambient turns directly on, you'd change all the duration properties to .001, like the following image. (Note that this modifies the transition only for entering this one location. All the other info_locations maintain their default values unless you also change them by hand.)

Fade duration.jpg

Ambient light settings

These spawnargs can be set on atdm:info_location entities and apply to the current zone then:

"ambient_light"

This spawnarg specifies the ambient light color for this zone. Setting it f.i. to "0.10 0.02 0.02" would fade the ambient light to a slightly reddish color. This can be useful for a lava cave. Other examples are slightly blue light for moonlit outsides, greenish cast for caves or underwater areas etc.

If you do not set this on an info_location entity, the default value of the global "ambient_world" entity will be used.

Notes:

  • You need to set the color values as fractions between 0 and 1, e.g. "0.08 0.08 0.02" and NOT as integers like "8 8 2", or it will not work.
  • If the color change between two zones is too big, the fading and change can become obvious to the player and spoil the subtlety. This can happen for instance if you zoom from a bluish-lit outside directly to a reddish-glowing cave inside. To help the transition, consider adding an intermediate zone with a light color that is either between the two, or a more neutral grey. Making it not possible to look from one zone to the other also helps; this way the player can't see that f.i. the outside ground suddenly also glows reddish when looking back from the cave entrance to the outside.
  • There's a bug with setting ambient_light to 0 0 0 (to be fixed in 2.05), so use 0.004 0.004 0.004 if you want to remove ambient light from a location.

"ambient_light_fade_time"

This specifies the time in seconds it will take to fade from the current ambient light color to the one specified for this location. A slower fade means more gradual light changes, so they don't become too obvious to the player. Use at least 3, better 5 or 7 seconds.

If set to -1, the default time specified at the atdm:location_settings entity will be used.

"ambient_light_fade_delay"

This specifies the time in seconds the fading will be delayed when the player changes location.

Useful to prevent fades toggling back and forth when the player stands in a doorway and goes a nudge forward/backwards. A good value is at least 1 second.

If set to -1, the default time specified at the atdm:location_settings entity will be used.

"ambient_light_dynamic"

This is a factor, all lights in the current area will be summed together and scaled by this value.

If set to a value other than "0 0 0", the lights in the current zone will be all summed together and then added to the current base ambient light. This happens with the frequency of update_period.

The basic effect is that a roaring fire also slightly makes the walls flicker, and extinguishing all lights in a room would decrease the ambient light slightly.

The factor should be set up so that for large and dark rooms (e.g. caves) the dynamic part is small, while for small, bright rooms (white walls) the dynamic part is bigger. Examples are: "0.05 0.05 0.05" and "0.1 0.1 0.1".

Note that setting the dynamic factor to high can result in the player being busted by lights that are turned on in the room even when the player is in the shadow.

See also the article about Dynamic ambient light.

"ambient_light_dynamic_cap"

Used to cap the dynamic ambient light part. If set to 0, will be ignored, so to have a very very small cap, set to "0.01", e.g. if you want only the red part to be dynamic, set it f.i. "0.15 0.01 0.01".

See also the article about Dynamic ambient light.

"ambient_light_falloff"

Possible values are:

  • -1 on info_location: use the value from the atdm:location_settings entity
  • 0 - no dynamic falloff
  • 0.5 - small, based on square root of distance
  • 1 - medium, linear dynamic falloff (should be used as it looks best)
  • 2 - high, square of distance based falloff

See also the article about Dynamic ambient light.

"ambient_light_dist_scale"

A factor to scale the distance before applying a dynamic light falloff. Only used then ambient_light_dynamic_falloff is not 0. Good values are around 1.0.

See also the article about Dynamic ambient light.

Script calls

The next four spawnargs all specify script functions to call when the player enters or exits a zone. The call passes the zone as a function parameter. Note that the very first zone is also "entered" by the player when the map starts. That means "call_once_on_entry" for the start zone should be called at start-time.

"call_once_on_exit"

Called only once when the zone is left by the player.

"call_once_on_entry"

Called only once when the zone is entered by the player.

"call_on_exit"

Always called when the player leaves that zone.

"call_on_entry"

Always called when the player enters that zone.

Example scripts

Here is an example script that spawns an object where the info_location entity is for the zone the player just left. You can call this script by setting:

"call_once_on_exit" "spawn_pear"

on your info_location entity.

void spawn_pear( entity old_zone )
  {
  // Get the name of the location that the player just left:
  string location_name = old_zone.getName();
  // and display it on the console for debugging
  sys.print ("Spawning pear after leaving " + location_name + "\n");
  // spawn the pear entity
  entity pear = sys.spawn("atdm:moveable_food_pear");

  // and now get the position of the old location entity:
  vector origin = old_zone.getOrigin();
  // finally move the pear to the point of the info_location
  pear.setOrigin( origin );
  // so once the player exits this location, ONE pear will spawn and fall down
  }

Example script to turn entities on/off when player enters a location

The basic problem with normal TDM entity work is the difficulty of turning something deliberately and reliably on or off. You never turn something on or off, but instead you send toggles to the entities. Let's say you have a omni sound (a sound which is heard everywhere) which you need to shut off when the player goes to a certain location. Trigger_multiples trigger targets when the player stands on their volume, which may result in a completely wrong situation depending how long the player stands on the trigger.

Location entity scripting can be of great help here. This kind of scripting can generally solve any kind of problem where the mapper wants something to be switched specifically on and off when the player moves to a specific location. Here is an example, which was used to switch off a global thunder sound when the player moves to a location where the rumbling cannot be heard.

Set up location setting as described above. Put a location separator on the visportal you want to be the point which switches your target on/off. Give the info_location-entity spawnargs

  • call_on_entry yourscript
  • call_on_exit yourscript

Then go and create in your /maps folder a file yourmapname.script. Put in the following data there:

void yourscript(entity zone)
{
sys.println("script yourscript running..");
sys.trigger( $entity_name );
}
void main()
{
}
  • yourscript is the name of your script you call. Its parameter 'zone' has the location entity that called it, either:
    • the location you're entering, when yourscript() is called via a call_on_entry; or
    • the location you're leaving, when yourscript() is called via call_on_exit.
  • sys.println is a debug feature which gives the text in console when the script is triggered. You can leave it for testing and remove it before releasing your map.
  • sys.trigger ($entity_name) replace the entity_name with the entity which you want to be triggered.

Now your system works like this:

  • When the player is outside the location everything is normal. Ie sound off, light off, etc.
  • When the player enters the location, entity_name gets triggered once. Ie sound on, light on, etc.
  • When the player leaves the location, entity_name gets triggered again. Ie sound off, light off, etc.

See also