Monsters seeing thru liquids and/or glass

Discuss Artificial Intelligence and Bot programming.
Post Reply
frag.machine
Posts: 2126
Joined: Sat Nov 25, 2006 1:49 pm

Monsters seeing thru liquids and/or glass

Post by frag.machine »

What would be the best approach to the subject ? Assuming I'm targeting modern engines like Darkplaces and FTE.

I've been thinking about and the best idea that occurred me would be multiple calls to traceline:

Code: Select all

// pseudocode
float (entity monster, entity player) canSee =
local entity etmp;
traceline (monster.origin, player.origin, FALSE, monster);
if (trace_fraction < 1.0) { 
  if (trace_inwater) { // player probably is inside water
    traceline (trace_endpos, player.origin, FALSE, world);
    if (trace_fraction == 1) { return TRUE; }
  }
  else if (trace_ent != player) {
    if ((trace_ent.solid == SOLID_BSP) && (trace_ent.alpha < 1.0)) { // assume some sort of glass
      etmp = trace_ent;
      traceline (player.origin, monster.origin, FALSE, player);
      if (trace_ent == etmp) { return TRUE; }
    }
  }
}

return FALSE;
}
Any better idea or suggestions ? I think this is kinda hacky and too prone to fail. :?
I know FrikaC made a cgi-bin version of the quakec interpreter once and wrote part of his website in QuakeC :) (LordHavoc)
Spike
Posts: 2914
Joined: Fri Nov 05, 2004 3:12 am
Location: UK
Contact:

Re: Monsters seeing thru liquids and/or glass

Post by Spike »

Firstly: modern engines tend to have watervis enabled, so there's no reason a monster/player wouldn't be able to see through water.

Remember that the forent argument ignores the specified entity. Alternatively, the trace will not hit entities that the forent entity owns.
Thus the following code tries multiple traces until it makes progress and should work in ANY quake engine.

Code: Select all

float(entity monster, entity player) canSee =
{
	float success = FALSE;
	float tries = 10;
	monster.owner = world;
	trace_endpos = monster.origin;
	vector pushdir = normalize(player.origin - monster.origin);
	while(tries --> 0)
	{
		traceline(trace_endpos, player.origin, TRUE, monster);
		if (trace_fraction == 1 || trace_ent == player)
		{
			sucess = TRUE;
			break;
		}
		if (!trace_ent.alpha || trace_ent.alpha >= 1)
			break; //this entity is opaque.
		//we hit something that was transparent, have another go.
		trace_endpos += pushdir; //push forwards slightly so we avoid infinite hitting on boundaries.
		monster.owner = trace_ent;
	}
	monster.owner = world;
	return sucess;
};
In exotic situations where two ents somehow overlap, its possible that you'll end up hitting the same two ents every single time. This shouldn't be too frequent though. If it does happen, instead changing the monster's owner you can set them to nonsolid before retracing (and adding them to a list), and then afterwards walking your list to restore the solid flags, due to nomonsters=TRUE they'll thankfully only be SOLID_BSP.

Alternatively in fte, you can always do something like this:

Code: Select all

void() func_window =
{
existing_func_window_code();
if (self.alpha < 1)
self.dimension_solid &= ~1; //default is 255, so this clears the low bit, leaving 7 other bits. all other ents will have bit 1 set.
};
float(entity monster, entity player) canSee =
{
monster.dimension_hit = 1;  //only allow the monster and traces belonging to the monster to hit things that are still in dimension&1
traceline(monster.origin, player.origin, TRUE, monster);
monster.dimension_hit = 255; //revert to default
if (trace_fraction == 1 || trace_ent == player)
return TRUE; //trace_ent won't be set to the player, but hey, completeness and paranoia. you might have made them into a bsp object, or maybe you want the bounding boxes of other monsters to block their vision?
return FALSE;
};
Assuming there's no typos, this will allow the monster to see through windows (because they are not solid in dimension 1), and through other monsters (because nomonsters=true ignores non-bsp entities), but NOT other bsp objects like closed doors, and all with a single trace.
Unfortunately dp doesn't support dimension_* so this method will only work in FTE.

In DP, I believe that there's some MOVE_WORLDONLY value you can pass in the nomonsters arg, but this means they'll see through closed doors etc too that you DO want to block their vision, so this probably isn't useful to you.
Post Reply