Ambushers AI Mod [Updated 12/24/21]

Post your example zscripts/ACS scripts/etc here.
Forum rules
The Projects forums are only for projects. If you are asking questions about a project, either find that project's thread, or start a thread in the General section instead.

Got a cool project idea but nothing else? Put it in the project ideas thread instead!

Projects for any Doom-based engine (especially 3DGE) are perfectly acceptable here too.

Please read the full rules for more details.
Hey Doomer
Posts: 283
Joined: Sat Sep 25, 2021 3:38 am

Ambushers AI Mod [Updated 12/24/21]

Post by Hey Doomer »

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 all

	// 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:
Last edited by Hey Doomer on Fri Dec 24, 2021 7:01 am, edited 6 times in total.
User avatar
Enjay
 
 
Posts: 26643
Joined: Tue Jul 15, 2003 4:58 pm
Location: Scotland

Re: Ambushers AI Mod

Post by Enjay »

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
openroadracer
Posts: 496
Joined: Mon Sep 23, 2019 1:03 pm
Preferred Pronouns: He/Him
Operating System Version (Optional): Windows 7 Professional 64-bit SP1
Graphics Processor: ATI/AMD with Vulkan/Metal Support
Location: Doomworld Forums

Re: Ambushers AI Mod

Post by openroadracer »

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.
thugsta
Posts: 150
Joined: Mon Jan 21, 2019 10:10 am

Re: Ambushers AI Mod

Post by thugsta »

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 👆
Hey Doomer
Posts: 283
Joined: Sat Sep 25, 2021 3:38 am

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

Post by Hey Doomer »

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.
Hey Doomer
Posts: 283
Joined: Sat Sep 25, 2021 3:38 am

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

Post by Hey Doomer »

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.
Hey Doomer
Posts: 283
Joined: Sat Sep 25, 2021 3:38 am

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

Post by Hey Doomer »

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

Code: Select all

// 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.
Hey Doomer
Posts: 283
Joined: Sat Sep 25, 2021 3:38 am

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

Post by Hey Doomer »

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.

Return to “Script Library”