Toying with ZScript based level controls

Wed Sep 15, 2021 11:51 am

Sooo I've been playing around a little bit with an idea. Basically as an alternative to ACS for repeatable level elements, a thing that can be placed in a level to perform a common task. I'm sure other people have done it before, but this is how I'm trying it myself, and maybe it will be useful to someone. Or maybe it's a terrible idea and everyone will tell me how bad it is. 🤷

I made a class with some common functions to help deal with sectors, for one thing:

Code:
class SectorUtil play
{
    static void SectorIndicesForTag(int tag, Array<int> results)
    {
        results.Clear();
        int i;
      let itr = Level.CreateSectorTagIterator(tag);
      while ((i = itr.Next()) >= 0)
      {
         results.Push(i);
      }
    }
   
    static bool IsFloorMoving(int i)
    {
      Sector s = Level.Sectors[i];
        return s.PlaneMoving(0);
    }
   
    static bool IsCeilingMoving(int i)
    {
      Sector s = Level.Sectors[i];
        return s.PlaneMoving(1);
    }
   
    static bool AnySurfaceMoving(Array<int> sectorIndices, bool checkFloor, bool checkCeiling)
    {
      if (!checkFloor && !checkCeiling)
      {
         // ???
         return false;
      }
   
      bool anyMoving = false;
      
      for (int i = 0; i < sectorIndices.Size(); i++)
      {
         if (checkFloor)
         {
            if (IsFloorMoving(sectorIndices[i]))
            {
               anyMoving = true;
               break;
            }
         }
         
         if (checkCeiling)
         {
            if (IsCeilingMoving(sectorIndices[i]))
            {
               anyMoving = true;
               break;
            }
         }
      }
      
      return anyMoving;
    }
}


From that I can start having a bit of fun with scripts, for instance, a basic lift object that works by pointing a linedef at it using Thing_Activate:

Code:
class StandardLift : Actor
{
   // Arg 0: Tag
   // Arg 1: Move distance
   // Arg 2: Move speed
   // Arg 3: Start in up state
   // Arg 4: Also move ceiling
   
   //$Category "Util"

   int tag;
   int moveDist;
   int moveSpeed;
   bool upState;
   bool useCeiling;
   
   bool waiting;
   Array<int> affectedSectors;

   Default
   {
      +NOBLOCKMAP;
      +NOGRAVITY;
   }
   
   override void PostBeginPlay()
   {
      Super.PostBeginPlay();
      
      tag = args[0];
      
      // Doing our stuff to tag 0 would be... very, very bad
      if (tag == 0)
      {
         A_Log("Lift controller at " .. Pos.xy .. " in sector tag 0! That's bad!");
         return;
      }
      
      moveDist = args[1];      
      moveSpeed = args[2];
      upState = args[3];
      useCeiling = args[4];

      // Gather our sectors for inspection
      SectorUtil.SectorIndicesForTag(tag, affectedSectors);
   }
   
   override void Activate(Actor activator)
   {
      if (waiting || tag == 0)
         return;

      if (upState)
      {
         DoMove(-1);
         upState = false;
      }
      else
      {
         DoMove(1);
         upState = true;
      }
      
      waiting = true;
   }
   
   void DoMove(int dir)
   {
      if (dir == 1)
      {
         Floor_RaiseByValue(tag, moveSpeed, moveDist);
         
         if (useCeiling)
            Ceiling_RaiseByValue(tag, moveSpeed, moveDist);
      }
      else if (dir == -1)
      {
         Floor_LowerByValue(tag, moveSpeed, moveDist);
         
         if (useCeiling)
            Ceiling_LowerByValue(tag, moveSpeed, moveDist);
      }
   }
   
   override void Tick()
   {
      Super.Tick();
      
      // Make sure all of our sectors have stopped moving before we continue
      if (waiting)
      {
         if (!SectorUtil.AnySurfaceMoving(affectedSectors, true, useCeiling))
         {
            waiting = false;
         }
      }
   }
}


Of course, this has some problems - if you block one of the sectors to move, it will happily remain behind while the others carry on and your elevator kind of falls apart... Still, maybe there's something here.

Or I should just use ACS libraries and stuff. Oh well, it was still fun to work this stuff out.

Re: Toying with ZScript based level controls

Wed Sep 15, 2021 2:05 pm

To prevent one sector going out of sync with the other sectors of a lift
I usually use the action special 51: Sector_SetLink.

I don't know if it would help in your case.

Re: Toying with ZScript based level controls

Wed Sep 15, 2021 2:32 pm

Oh cool, I'll give that a try later...

Re: Toying with ZScript based level controls

Thu Sep 16, 2021 2:35 pm

Finally got around to messing with it. I really wanted to make it an automated thing where you just fed it some sectors and it would link everything to the first one on its own for super quick and easy creation of more advanced elevators, but it looks like the way Sector_SetLink is set up is not really feasible in that scenario. I'd have to write my own equivalent somewhere, probably on the source side, which is probably possible but, ehh... Just ended up doing it in the mapping side of things, which works just fine, and is probably more flexible anyway. I am definitely going to pursue making more modules like this, though, because uhh... honestly, I don't really like ACS all that much. Hard to put my finger on why, just feels kinda awkward, I guess. Having to compile things, etc... I guess there is no way to have the engine automagically compile a SCRIPTS lump at runtime, is there? I for one would welcome our new overlords in the glorious ZScript level logic revolution. 🤔