Location Settings

From The DarkMod Wiki
Jump to navigationJump to search

written by demagogue

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 and a some in-map entities.

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.

  • Pros: Doesn't leak sound into areas you don't want it, or lose sound where you do. For a very big area, or inside, a few triggers will cover the whole area, whereas it might take many carefully placed speakers. You can change the ambient for a whole area by changing the property of one button (as opposed to many speakers).
  • 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).

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

Say there are 10 ambients you want to use in your map with the zone approach (it's easy to change the number once you get the principles, but it's good to 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).
In your map, in a blueroom (i.e., a room off to the side the player will never enter), create two sets of 10 buttons, a "command" set (the "radio stations"), and a "play" set (the "songs"), so 20 buttons in all. Create 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.
Also set up 10 speakers there (right-click, create-entity, Darkmod->Sound->speaker).
(Three asides: 1. You might put the buttons on a brush and light the blueroom so you can put the StartingPoint inside and test the buttons later, but all that's optional. 2. The command set would be your "machine" if you just wanted to use this method for machine sound-management. 3. I'm working on a simpler setup has only the command buttons that I hope to edit in later; it's possible. But this works for now to get the principles across.)
  • Speakers
Give each speaker a property/value of "s_waitfortrigger"/1, "s_global"/1, "s_looping"/1, "s_omni"/1, (you can read about these properties in greebo's tutorial "Adding ambient Sounds to your Map"), plus:
For the property "s_shader", enter the name of each ambient you want to use in consecutive speakers, either the name listed in "/sound/tdm_ambient_ambience.sndshd", which you can open with a text editor; for example, "mansion_tense01a"; OR the address\name, for example, "sound\ambient\ambience\mansion_tense01a.ogg", good if you want to quickly try a custom sound you put into a folder by hand. You can listen to them in the "Darkmod\sound\ambient\ambience" folder.
For the property "s_volume", enter a negative value to be subtracted from your sound, 0(max) to -60(silent). It's good to just test different values. -5 is a value I like to start with, and go from there.
I named the speakers "speaker_station1", "speaker_station2", etc, but that's not essential.
One special case: for the first speaker (what I named "speaker_station1"), use the s_shader "nosound" as the default way to turn off ambients and have areas with no ambient sound. You should also put "s_looping"/0 just for this one speaker.
  • Command Buttons
Name each command button whatever you want (factory_zone, streets_zone, forest, whatever), just remember what it is so you can "target" it with a trigger entity.
Add a "state_change_callback" property to each command button, with the value being "station_1", station_2", etc., sequentially for each button.
  • Play Buttons
Name each "play" button sequentially "station1", "station2", etc. Add the "target" property to each play button, with the value of the speaker with the ambient you want for the zone it covers (for me "speaker_station1", "speaker_station2", etc.).
Here's what the blueroom I've been testing with looks like: [1]
(the walls should be textured with common/caulk; I just like this for testing, and I used 27*2 buttons, not just 10; and ignore the "testing_target" property ... just crap I left in the prompt).

The Gameworld

You trigger ambients with a trigger_multiple. To make one, create a brush the size of the entrance to the zone you want to do, 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.
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. (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.
Here's an image of that for reference: [2]
Be sure you cover every possible entry, even those you aren't sure the player can cross, and watch for things like teleports.
If you want no sound for an area (i.e., just turn the previous ambient off), have a trigger_multiple turn on the command button for station1 playing the ambient "nosound".
Once you have all that set up, you need to create a script. 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.
The following only creates three stations, but it should be enough so that you know how to create more.

The Script

// ZONED AMBIENT SYSTEM (demagogue)
//
// 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.  

float station_state = 1;  
   // the number corresponds to the "current radio station playing" 
           
void main()
{
}

void station_1(entity button, boolean bOpen, boolean bLocked, boolean bInterrupted) 
{
if(bOpen)  // do the following if the button is open (pressed) 
    {
    button.Close(); // close the button (push it back out). 
    if (station_state == 1) // if the current station playing is #1
     { 
      sys.print("Radio station 1 is already playing.");  // station 1 is already on, so nothing happens
     }
    else if (station_state == 2)
     {
      $station1.activate($player1);  // push the button that turns station 1 music on     
      $station2.activate($player1);  // turn station 2 music off
      station_state = 1;
      sys.print("Radio station 1 is now playing.");
     }
    else if (station_state == 3)
     {
      $station1.activate($player1);  // turn station 1 music on
      $station3.activate($player1);  // turn station 3 music off
      station_state = 1;
      sys.print("Radio station 1 is now playing.");
    }
 }
}

void station_2(entity button, boolean bOpen, boolean bLocked, boolean bInterrupted) 
{
if(bOpen)  
{
   button.Close();
   if (station_state == 1)  
    {
     $station1.activate($player1);  // turn station 1 music off
     $station2.activate($player1);  // turn station 2 music on
     station_state = 2;
     sys.print("Radio station 2 is now playing.");
    }
   else if (station_state == 2) 
    {
     sys.print("Radio station 2 is already playing.");   // station 2 is already on, so nothing happens
    }
   else if (station_state == 3)
    {
     $station2.activate($player1);  // turn station 2 music on
     $station3.activate($player1);  // turn station 3 music off
     station_state = 2;
     sys.print("Radio station 2 is now playing.");
    }
 }
    
}


void station_3(entity button, boolean bOpen, boolean bLocked, boolean bInterrupted) 
{
if(bOpen)
{
   button.Close();
   if (station_state == 1)
    {
     $station1.activate($player1);  // turn station 1 music off
     $station3.activate($player1);  // turn station 3 music on
     station_state = 3;
     sys.print("Radio station 3 is now playing.");
    }
   else if (station_state == 2)
    {
     $station2.activate($player1);  // turn station 2 music off 
     $station3.activate($player1);  // turn station 3 music on
     station_state = 3;
     sys.print("Radio station 3 is now playing.");
    }
   else if (station_state == 3) 
    {
     sys.print("Radio station 3 is already playing.");   // station 3 is already on, so nothing happens
    }
  }
}

Tips and Issues

General Tips

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 doesn't break, or get the toggles backwards or whatever. You could also put the StartingPoint in the blueroom to push buttons and make sure that all works.

The lines sys.print("Radio station X is now/already playing."); are there so you can go into the console and see how the triggers are working; once you have it set up and working, you can add "//" in front of them to turn them off.

Ambient Fades and Blends

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 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). Here's an image of that for reference: [3] 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).

In testing this, I found it good to have the ambient trigger be the outside layer and a "nosound" trigger (i.e., station1) the inside, the speaker radius does the fade in the nosound area in the middle. Then if you wanted the outside area to have its own ambient, you'd create another 3 walls on the other side of that doorway with the ambient trigger outside and a "nosound" trigger outside, and its own speaker fading in its middle. Then the two speakers could even overlap so you get a nice blending gradient between the two ambients.

(Also when testing this, I had some problems creating three walls as one trigger_multiple and had better luck creating three separate trigger_multiples as each wall. I don't know if that's a real problem or not, just something to remember).

Getting rid of the Play Buttons and the Toggle-approach

I'm working on a method to get rid of the play buttons, so you only deal with command buttons. It's more tidy. But it is also a better approach because it turns the sounds absolutely "on" & "off" instead of toggling them. I haven't yet had a problem with getting the toggles backwards, as long as the triggers are cleanly shaped so the player can't pass through a gap or something (or too skinny they don't register, I imagine). But it's just better if even the possibility can be eliminated, so any time you touch a trigger everything always rights itself.

I'm still learning scripting myself, so don't have it working yet, but here's some of the code I've been working with, in case it helps other people figure this out.


// NOTE, this is BAD CODE; it doesn't work for me; just an example of what I'm thinking about doing.

void station_1(entity button, boolean bOpen, boolean bLocked, boolean bInterrupted) 
{
if(bOpen)  
{
   button.Close();
   if (station_state == 1)  // If station 1 is currently playing
    {
   sys.print("Radio station 1 is already playing.");
    }
   else if (station_state == 2) // If station 2 is currently playing, etc...
    {
     station_state = 1;
     sys.wait($speaker_station1.startSound( "snd_loop", SND_CHANNEL_BODY, false ));  // turn station 1 on, looping
     sys.wait($speaker_station2.startSound( "snd_stop", SND_CHANNEL_BODY, false ));  // turn station 2 off
        sys.print("Radio station 1 is now playing.");
    }
   else if (station_state == 3)
    {
   station_state = 1;
     sys.wait($speaker_station1.startSound( "snd_loop", SND_CHANNEL_BODY, false ));  // turn station 1 on, looping
     sys.wait($speaker_station3.startSound( "snd_stop", SND_CHANNEL_BODY, false ));  // turn station 3 off
        sys.print("Radio station 1 is now playing.");
    }
 }
}