Idle Ambiance

Post your example zscripts/ACS scripts/etc here.

Idle Ambiance

Postby Hey Doomer » Sun Dec 05, 2021 10:58 am

I have seen ambient sound music tracks and it's possible to add ambiance to a sector in a map or attach to an object. Here is my attempt at a universal framework to add idle ambiance (that is, looping sounds for the most part) based on items or flats without using an AmbientSound Actor. (I'm not sure that matters, however.)

The concept is all the tech lamps make an electronic buzzing, certain areas have a distant rumbling sound, and there's a distant wind outside that bodes ill.

DECORATE:
Code: Select allExpand view
Actor IA_Sound
{
   +NOGRAVITY
   +FLOAT
   States
   {
   }
}


ZScript:
Code: Select allExpand view
class idleambiance_EventHandler : EventHandler
{
   struct item
   {
      string cname;
      string sname;
      int flags;
      double volume;
   }
   item ItemSounds[50];
   int iCounter;

   struct flat
   {
      string fname;
      int type;
      int height;
      string sname;
      int flags;
      double volume;
   }
   flat FlatSounds[50];
   int fCounter;
   
   void addItemSounds(string cname, string sname, int flags, double volume = 1.0)
   {
      ItemSounds[iCounter].cname = cname;
      ItemSounds[iCounter].sname = sname;
      ItemSounds[iCounter].flags = flags;
      ItemSounds[iCounter].volume = volume;
      iCounter++;
   }
   void addFlatSounds(string fname, int type, int height, string sname, int flags, double volume = 1.0)
   {
      FlatSounds[fCounter].fname = fname;
      FlatSounds[fCounter].type = type;
      FlatSounds[fCounter].height = height;
      FlatSounds[fCounter].sname = sname;
      FlatSounds[fCounter].flags = flags;
      FlatSounds[fCounter].volume = volume;
      fCounter++;
   }
   void LoadArrays()
   {
      iCounter = 0;
      fCounter = 0;
      int FLOOR = 0;
      int CEILING = 1;
      
      addItemSounds("Column", "buzz", CHANF_LISTENERZ | CHANF_LOOP | CHANF_NOSTOP | CHANF_OVERLAP);
      addItemSounds("TechLamp", "buzz", CHANF_LISTENERZ | CHANF_LOOP | CHANF_NOSTOP | CHANF_OVERLAP);
      addItemSounds("TechLamp2", "buzz", CHANF_LISTENERZ | CHANF_LOOP | CHANF_NOSTOP | CHANF_OVERLAP);

      addFlatSounds("F_SKY1", CEILING, 128, "outsidewind", CHANF_LOOP | CHANF_NOSTOP | CHANF_OVERLAP, 0.5);
      addFlatSounds("F_SKY2", CEILING, 128, "outsidewind", CHANF_LOOP | CHANF_NOSTOP | CHANF_OVERLAP, 0.5);
      addFlatSounds("F_SKY3", CEILING, 128, "outsidewind", CHANF_LOOP | CHANF_NOSTOP | CHANF_OVERLAP, 0.5);
      addFlatSounds("F_SKY4", CEILING, 128, "outsidewind", CHANF_LOOP | CHANF_NOSTOP | CHANF_OVERLAP, 0.5);
      addFlatSounds("FLOOR4_8", FLOOR, 64, "scifi-rumble", CHANF_LOOP | CHANF_NOSTOP | CHANF_OVERLAP);
      addFlatSounds("FLOOR5_1", FLOOR, 64, "scifi-rumble", CHANF_LOOP | CHANF_NOSTOP | CHANF_OVERLAP);
   }
   
   // returns area of sector (irregular polygon)
   float polygonArea(Sector sec)
   {
      Array<float> lineAreas;
      for (int i = 0; i < sec.Lines.Size(); i++)
      {
         int v1x = int(sec.Lines[i].V1.P.x);
         int v1y = int(sec.Lines[i].V1.P.y);

         int v2x = int(sec.Lines[i].V2.P.x);
         int v2y = int(sec.Lines[i].V2.P.y);

         float avgY = (v1y + v2y) * 0.5 * .015625;
         float widthX = (v2x - v1x) * .015625;
         lineAreas.Push(avgY * widthX);
      }
      float totArea;
      for (int i = 0; i < lineAreas.Size(); i++)
      {
         totArea += lineAreas[i];
      }
      return totArea;
   }
   
   // add idle ambient sounds
   override void WorldLoaded(WorldEvent e)
   {
      LoadArrays();
      
      int FLOOR = 0;
      int CEILING = 1;
      TexMan texture;

      for(int i = 0; i < Level.Sectors.Size(); i++)
      {
         textureid floorid = Level.Sectors[i].GetTexture(FLOOR);
         textureid ceilingid = Level.Sectors[i].GetTexture(CEILING);

         SecPlane ceilingplane = Level.Sectors[i].CeilingPlane;
         SecPlane floorplane = Level.Sectors[i].FloorPlane;

         int ceilingZ = int(ceilingplane.ZAtPoint((Level.Sectors[i].CenterSpot.x, Level.Sectors[i].CenterSpot.y)));
         int floorZ = int(floorplane.ZAtPoint((Level.Sectors[i].CenterSpot.x, Level.Sectors[i].CenterSpot.y)));
         int centerheight = ceilingZ - floorZ;

         for (int ii = 0; ii < fCounter; ii++)
         {
            string flat = FlatSounds[ii].type == CEILING ? texture.GetName(ceilingid) : texture.GetName(floorid);
            if (FlatSounds[ii].fname == flat && centerheight > FlatSounds[ii].height)
            {
               float pArea = polygonArea(Level.Sectors[i]);
               if (pArea > 64)
               {
                  console.printf("Adding %s to %s", FlatSounds[ii].sname, FlatSounds[ii].fname);
                  vector3 v3 = (Level.Sectors[i].CenterSpot.x, Level.Sectors[i].CenterSpot.y, floorZ);
                  Actor ptr = Actor.Spawn("IA_Sound", v3);
                  if (ptr)
                  {
                     console.printf(ptr.getSpecies());
                     ptr.A_StartSound(FlatSounds[ii].sname, CHAN_AUTO, FlatSounds[ii].flags, FlatSounds[ii].volume);
                  }
               }
               break;
            }
         }
      }
   }

   override void WorldThingSpawned(WorldEvent e)
   {
      if (e.thing)
      {
         for (int i = 0; i < iCounter; i++)
         {
            if (ItemSounds[i].cname == e.thing.GetSpecies())
            {
               console.printf("Adding %s to %s", ItemSounds[i].sname, ItemSounds[i].cname);
               Actor ptr = e.thing.Spawn("IA_Sound", e.thing.pos);
               if (ptr)
               {
                  console.printf(ptr.getSpecies());
                  ptr.A_StartSound(ItemSounds[i].sname, CHAN_AUTO, ItemSounds[i].flags, ItemSounds[i].volume);
               }
            }
            break;
         }
      }
   }
}


How this works is simple enough. Two structure arrays define what sound goes to what: ItemSounds that are added when an item spawns and FlatSounds that are added when WorldLoaded fires.

The item sound spawner is somewhat simpler. For now the only item sound is an electronic humming noise. When an item spawns, it loops through the ItemSounds array, matches the actor species, and spawns an IA_Sound actor that returns an Actor ptr. That ptr starts the sound in the array. This isn't attached to the originator, so it's only good for stationary objects such as lamps.

The flat sound spawner has a few more parameters to consider, since sectors often vary in size and shape depending on their purpose. The script loops through all the sectors and for each ceiling and floor flat also loops through the FlatSounds array. When it finds a match it also checks that height exceeds the defined height to add the sound. It also checks the sector area with an arbitrary size of at least 1 square map unit. This makes sure there aren't multiple overlapping sounds for tiny sectors created solely for lighting effects, for example. Other than that it works the same as item sounds.

The sounds aren't fancy. I've added short looping sound effects for outside wind and a "scifi" rumbling, but of course they can be anything that sets a tone or gets under your skin. My intention is to add a universal element of ambiance independent of map design.

I've set this up so all sounds loop, but that doesn't have to be the case; flags can be set differently in the LoadArrays function. An inventory pickup item, for example, may have an option of playing an "ambient" sound once using this method. I haven't tried that, and there are probably more effective ways of accomplishing that.
Hey Doomer
 
Joined: 25 Sep 2021

Re: Idle Ambiance

Postby BROS_ETT_311 » Tue Dec 07, 2021 12:18 am

At this rate, you could almost construct your own Zscriptified BD level enhancement system...not that I would suggest that (except I kind of am :wink: ).
User avatar
BROS_ETT_311
I've got somethin' and it goes thumpin' like this
 
Joined: 03 Nov 2017

Re: Idle Ambiance

Postby madsnark » Mon Jan 17, 2022 11:05 am

script doesn't seem to work with animated flats

also IA_Sound actors are spawned only for the first ItemSound in the array and not for the others
Last edited by madsnark on Mon Jan 17, 2022 12:45 pm, edited 3 times in total.
User avatar
madsnark
 
Joined: 12 Jun 2018

Re: Idle Ambiance

Postby Hey Doomer » Mon Jan 17, 2022 12:27 pm

madsnark wrote:script doesn't seem to work with animated flats


It should if the flat name is what's on the map definition. It doesn't check these for any changes in-game.
Hey Doomer
 
Joined: 25 Sep 2021

Re: Idle Ambiance

Postby McTed » Thu Apr 28, 2022 9:29 am

I managed to get animated textures to play sounds however like madsnark I can't techpillar and lamps on E1M1 to play more than the first sound called for items

Download
https://cdn.discordapp.com/attachments/ ... bience.pk3

Future edit: A break was in the wrong place
Last edited by McTed on Sat Apr 30, 2022 5:19 am, edited 1 time in total.
User avatar
McTed
 
Joined: 23 Apr 2022


Return to Script Library

Who is online

Users browsing this forum: No registered users and 0 guests