Ambushers AI Mod [Updated 12/24/21]

Post your example zscripts/ACS scripts/etc here.

Ambushers AI Mod [Updated 12/24/21]

Postby Hey Doomer » Wed Dec 22, 2021 8:01 am

It strikes me that Doom's AI isn't so much stupid as responding to static elements in somewhat random ways. (I look stupid too when I have nothing to do but feel compelled to do the same thing over and over or respond in only one way to any kind of stimuli.) Since a player's behavior is not random, intelligent decisions can be made based on player input. That's one reason for Flankers, and another for Ambushers, my new AI mod. This is a work in early stages, but the idea is that a monster finds and moves to a corner or otherwise just out of sight of the player and attacks the player once the player is seen.

Screenshots:
Spoiler:


If you look carefully at these you'll see a shotgun sergeant hiding in each screenshot. (I took this with chase cam on, since I couldn't see them otherwise.)

So far this is a demo that shows the behavior of seeking the ambush points only. But how it works is simple and similar to Flankers.

  • In the WorldLoaded event AM_Point is spawned at each orthogonal (where two lines meet in 90 degrees) point.
  • A thinker finds all monsters within a distance of the player, and if they are at the beginning of "See" they search for a nearby AM_Point that they can see but the player can't.
  • The monster makes the AM_Point (if found) a target.

ZScript:
Code: Select allExpand view
   // add sector orthogonal points
   override void WorldLoaded(WorldEvent e)
   {
      for(int i = 0; i < Level.Sectors.Size(); i++)
      {
         SecPlane floorplane = Level.Sectors[i].FloorPlane;
         int floorZ = int(floorplane.ZAtPoint((Level.Sectors[i].CenterSpot.x, Level.Sectors[i].CenterSpot.y)));

         vector3 v3;

         for (int ii = 0; ii < Level.Sectors[i].Lines.Size(); ii++)
         {
            let sideB = (Level.Sectors[i].Lines[ii].V1.P - Level.Sectors[i].Lines[ii].V2.P);
            int AB = 0;
            
            for (int j = 0; j < Level.Sectors[i].Lines.Size(); j++)
            {
               if (j != ii)
               {
                  if (Level.Sectors[i].Lines[ii].V1.P == Level.Sectors[i].Lines[j].V2.P)
                  {
                     let sideA = (Level.Sectors[i].Lines[j].V1.P - Level.Sectors[i].Lines[j].V2.P);
                     if (sideA dot sideB == 0)
                     {
                        v3 = (Level.Sectors[i].Lines[ii].V1.P.x, Level.Sectors[i].Lines[ii].V1.P.y, floorZ);
                        spawnPoint(v3);
                     }
                     AB++;
                  }
                  if (Level.Sectors[i].Lines[ii].V2.P == Level.Sectors[i].Lines[j].V1.P)
                  {
                     let sideC = (Level.Sectors[i].Lines[j].V1.P - Level.Sectors[i].Lines[j].V2.P);
                     if (sideC dot sideB == 0)
                     {
                        v3 = (Level.Sectors[i].Lines[ii].V2.P.x, Level.Sectors[i].Lines[ii].V2.P.y, floorZ);
                        spawnPoint(v3);
                     }
                     AB++;
                  }
               }
               if (AB == 2)
               {
                  break;
               }
            }
         }
      }
   }


That's it thus far. Work in progress, but that's the notion.

Update 12/24/21 - Close to a first release!
Spoiler:
You do not have the required permissions to view the files attached to this post.
Last edited by Hey Doomer on Fri Dec 24, 2021 7:01 am, edited 6 times in total.
User avatar
Hey Doomer
 
Joined: 25 Sep 2021
Operating System: Windows 11
OS Test Version: No (Using Stable Public Version)
Graphics Processor: ATI/AMD with Vulkan Support

Re: Ambushers AI Mod

Postby Enjay » Wed Dec 22, 2021 8:07 am

Ooh, this is interesting and, I suspect, will work better within the limits of Doom AI and typical map geometry than flankers does to achieve a very similar end result: the player being attacked from an angle or location that is unexpected because an enemy has displayed some apparent intelligence.
User avatar
Enjay
Everyone is a moon, and has a dark side which he never shows to anybody. Twain
 
 
 
Joined: 15 Jul 2003
Location: Scotland

Re: Ambushers AI Mod

Postby openroadracer » Wed Dec 22, 2021 8:12 am

If you keep at this, OP, sooner or later we're going to end up with truly scarily intelligent enemies in a game from 1993.
User avatar
openroadracer
 
Joined: 23 Sep 2019
Operating System: Windows Vista/7/2008 64-bit
OS Test Version: No (Using Stable Public Version)
Graphics Processor: ATI/AMD with Vulkan Support

Re: Ambushers AI Mod

Postby thugsta » Wed Dec 22, 2021 5:21 pm

I know this is a WIP Demo but i could not make it work with just the script, can you post a demo pk3 to test when you can. Loving your ideas, there is some fantastic work going on keep it up 👆
thugsta
 
Joined: 21 Jan 2019

Re: Ambushers AI Mod [Demo added 12/23/21]

Postby Hey Doomer » Thu Dec 23, 2021 3:50 am

Ah thanks to all for kind words. This is a great community! :D

I have cleaned this up a little, added ambushOn and ambushOff flagging criteria, and added a cool down period. Currently the monsters will move to an ambush point and start shooting when they spot the player. This is rough but illustrates the concept.

Currently it also shows all the ambush points. For the most part it tags 90-degree angles. This can be done a number of ways but is simple using the dot operator as explained here: https://zdoom.org/wiki/ZScript_special_words. Generally I am happy with this. The code always drops the AM_Point to the lowest Z, which works since a monster generally wants to hide out of sight behind or around something that is taller. It also tags completely transparent sectors used for lighting, as in the tech room in E1M1. I don't think these are targeted, although they add to the inefficiency of the iterator.

Interesting that despite Doom being the first "non-orthogonal" shooter of the 90's it still contains many right angles, as does our real-world architecture. And of course there are no curves anywhere, which is simpler. Time to put those right angles to use!

Now I just have to decide on behavior, but there's this: Ambushers and Flankers both use DECORATE args[] to store data on the fly (Ambushers sets the MOFLAG to 2 instead of 1) so I suppose it's possible to make a monster a Flanker once it spots a player from an ambush location. That is, while the separate mods don't communicate per se they do have the player flags in common. Flankers should pick up on the change in flag and immediately change behavior (and vice versa). I suppose the mods could be combined, but this works also and raises an option of any number of mods with nuanced behavior that might work together. Encapsulating the behavior is always better anyway, I think.

That kind of thing.
User avatar
Hey Doomer
 
Joined: 25 Sep 2021
Operating System: Windows 11
OS Test Version: No (Using Stable Public Version)
Graphics Processor: ATI/AMD with Vulkan Support

Re: Ambushers AI Mod [Demo added 12/23/21]

Postby Hey Doomer » Thu Dec 23, 2021 11:36 am

Removed the demo file... which appears to be broken. :(

I'll work on this later and upload. That's what I get for playing with it.

Update - found the problem and uploaded. This demo shows the behavior, at least. I've also added to the corner-finding algorithm using a more efficient method.
User avatar
Hey Doomer
 
Joined: 25 Sep 2021
Operating System: Windows 11
OS Test Version: No (Using Stable Public Version)
Graphics Processor: ATI/AMD with Vulkan Support

Re: Ambushers AI Mod [Updated 12/24/21]

Postby Hey Doomer » Fri Dec 24, 2021 6:59 am

Just posted an update. I worked on this after reading about concave and convex angles in irregular polygons.

Code: Select allExpand view
// add sector corner points
   override void WorldLoaded(WorldEvent e)
   {
      int BLOCKING = 1;                 // bit 0x0001
      int TWOSIDED = 2;                 // bit 0x0004
      int DONTPEGTOP = 4;               // bit 0x0008
      int DONTPEGBOTTOM = 8;            // bit 0x0010
      int INNER = 0;
      
      MOFLAG = 0;
      AMSTATUS = 1;

      for(int i = 0; i < Level.Sectors.Size(); i++)
      {
         SecPlane floorplane = Level.Sectors[i].FloorPlane;
         int floorZ = int(floorplane.ZAtPoint((Level.Sectors[i].CenterSpot.x, Level.Sectors[i].CenterSpot.y)));

         vector3 v3 = (Level.Sectors[i].CenterSpot.x, Level.Sectors[i].CenterSpot.y, floorZ);
         
         Actor center = Actor.Spawn("AM_Point", v3);

         for (int ii = 0; ii < Level.Sectors[i].Lines.Size(); ii++)
         {
            vector2 vA1 = Level.Sectors[i].Lines[ii].V1.P;
            vector2 vA2 = Level.Sectors[i].Lines[ii].V2.P;
            double vaii = VectorAngle(vA1.x - vA2.x, vA1.y - vA2.y);
            int AB = 0;
            
            int f = Level.Sectors[i].lines[ii].flags;
            int peg_top = f & (TWOSIDED + DONTPEGTOP);
            int peg_bottom = f & (TWOSIDED + DONTPEGBOTTOM);
            int blocking = f & (BLOCKING);

            Side sidedef = Level.Sectors[i].Lines[ii].Sidedef[INNER];
            textureid botid = sidedef.GetTexture(sidedef.Bottom); // depends on good design
            bool hide = !botid.isNull();

            if (blocking || hide)
            {
               for (int j = 0; j < Level.Sectors[i].Lines.Size(); j++)
               {
                  if (j != ii)
                  {
                     vector2 vB1 = Level.Sectors[i].Lines[j].V1.P;
                     vector2 vB2 = Level.Sectors[i].Lines[j].V2.P;
                     double vaj = VectorAngle(vB1.x - vB2.x, vB1.y - vB2.y);
                     double vabs = abs(abs(vaii) - abs(vaj));

                     if (vA1 == vB2 && sharpCorner(vabs, 30))
                     {
                        v3 = (vA1.x, vA1.y, floorZ);
                        spawnPoint(v3, center);
                        AB++;
                     }
                     if (vA2 == vB1 && sharpCorner(vabs, 30))
                     {
                        v3 = (vA2.x, vA2.y, floorZ);
                        spawnPoint(v3, center);
                        AB++;
                     }
                  }
                  if (AB == 2)
                  {
                     break;
                  }
               }
            }
         }
         center.A_Remove(AAPTR_DEFAULT, RMVF_EVERYTHING, "AM_Point");
         center = null;
      }
   }


Note a few things are happening here to optimize where the ambush points are placed: A point is spawned in the center of the sector as a visual reference point; only blocking or two-sided lines with an unpegged bottom texture (something to hide behind) are targeted; vector angles are considered in spawning. Previously I removed ambush points based on the proximity of others, which is first-come first-serve and suboptimal. This method eliminates an ambush point if it can be seen from the center, which is theoretically the most visible spot. I think that seems to work much better. Intuitively it makes more sense.

Again I'm not sure there's much more to be done from the lines in a single sector without somehow reconstructing the map to see how sectors interact, and that may not help. So far I'm encouraged by how this works. See what you think!

I also noticed a bug while testing this that affects my Flankers mod. Update on that soon.
User avatar
Hey Doomer
 
Joined: 25 Sep 2021
Operating System: Windows 11
OS Test Version: No (Using Stable Public Version)
Graphics Processor: ATI/AMD with Vulkan Support

Re: Ambushers AI Mod [Updated 12/24/21]

Postby Hey Doomer » Fri Dec 24, 2021 2:28 pm

Added menu options, tweaked debug mode, tested by itself and with Flankers (you're correct Enjay, it does seems to be more effected than Flankers), and posted in Gameplay Mods.
User avatar
Hey Doomer
 
Joined: 25 Sep 2021
Operating System: Windows 11
OS Test Version: No (Using Stable Public Version)
Graphics Processor: ATI/AMD with Vulkan Support


Return to Script Library

Who is online

Users browsing this forum: No registered users and 1 guest