bool IsExtrinsicsPossible(const matf & P2, const TrackedPoint & point) {
  matf P1(3,4,0.0f); //TODO do not recreate each call
  P1(0,0) = P1(1,1) = P1(2,2) = 1.0f;
  matf p3d = Triangulate(P1, P2, point);

  //return p3d(2,0) >= 0;
  return IsInFront(P1, p3d) && IsInFront(P2, p3d); //TODO IsInFront(P1 ...) is trivial, since P1 is identity
}
void CCarrier::ReAttackGrenade ()
{
	CoopCheck ();

	if (IsInFront(Entity, *Entity->Enemy) && (FrameCalc + 13) > Level.Frame) // four grenades
	{
		CurrentMove = &CarrierMoveAttackGrenade;
		return;
	}

	CurrentMove = &CarrierMoveAttackPostGrenade;
}
void CCarrier::ReAttackMachinegun ()
{
	CoopCheck();

	if (IsInFront(Entity, *Entity->Enemy))
	{
		if (frand() <= 0.5)
		{
			if ((frand() < 0.7) || (MonsterSlots <= 2))
				CurrentMove = &CarrierMoveAttackMG;
			else
				CurrentMove = &CarrierMoveSpawn;
		}
		else
			CurrentMove = &CarrierMoveAttackPostMG;
	}
	else
		CurrentMove = &CarrierMoveAttackPostMG;
}
bool CCarrier::CheckAttack ()
{
	if ((Entity->Enemy->EntityFlags & EF_HURTABLE) && entity_cast<IHurtableEntity>(*Entity->Enemy)->Health > 0)
	{
	// see if any entities are in the way of the shot
		vec3f	spot1 = Entity->State.GetOrigin() + vec3f(0, 0, Entity->ViewHeight),
				spot2 = Entity->Enemy->State.GetOrigin() + vec3f(0, 0, Entity->Enemy->ViewHeight);

		CTrace tr (spot1, spot2, Entity, CONTENTS_SOLID|CONTENTS_MONSTER|CONTENTS_SLIME|CONTENTS_LAVA);

		// do we have a clear shot?
		if (tr.Entity != Entity->Enemy)
		{	
			// go ahead and spawn stuff if we're mad a a client
			if ((Entity->Enemy->EntityFlags & EF_PLAYER) && MonsterSlots > 2)
			{
				AttackState = AS_BLIND;
				return true;
			}
				
			// PGM - we want them to go ahead and shoot at info_notnulls if they can.
			if (Entity->Enemy->GetSolid() != SOLID_NOT || tr.Fraction < 1.0)		//PGM
				return false;
		}
	}
	
	EnemyInfront = IsInFront(Entity, *Entity->Enemy);
	bool EnemyInback = IsInBack(Entity, *Entity->Enemy);
	bool EnemyBelow = IsBelow (Entity, *Entity->Enemy);

	EnemyRange = Range (Entity, *Entity->Enemy);
	IdealYaw = (Entity->Enemy->State.GetOrigin() - Entity->State.GetOrigin()).ToYaw();

	// PMM - shoot out the back if appropriate
	if ((EnemyInback) || (!EnemyInfront && EnemyBelow))
	{
		// this is using wait because the attack is supposed to be independent
		if (Level.Frame >= RefireWait)
		{
			RefireWait = Level.Frame + CARRIER_ROCKET_TIME;
			Attack ();
			if (frand() < 0.6)
				AttackState = AS_SLIDING;
			else
				AttackState = AS_STRAIGHT;
			return true;
		}
	}

	// melee attack
	if (EnemyRange == RANGE_MELEE)
	{
		AttackState = AS_MISSILE;
		return true;
	}
	
	float chance = 0.0f;
	if (AIFlags & AI_STAND_GROUND)
		chance = 0.4f;
	else 
	{
		switch (EnemyRange)
		{
		case RANGE_MELEE:
		case RANGE_NEAR:
		case RANGE_MID:
			chance = 0.8f;
			break;
		case RANGE_FAR:
			chance = 0.5f;
			break;
		};
	}

	// PGM - go ahead and shoot every time if it's a info_notnull
	if ((Entity->Enemy->GetSolid() == SOLID_NOT) || (frand () < chance))
	{
		AttackState = AS_MISSILE;
		return true;
	}

	if (AIFlags & AI_FLY)
	{
		if (frand() < 0.6)
			AttackState = AS_SLIDING;
		else
			AttackState = AS_STRAIGHT;
	}

	return false;
}
void CCarrier::Attack ()
{	
	AIFlags &= ~AI_HOLD_FRAME;

	if (!Entity->Enemy)
		return;

	bool EnemyInback = IsInBack(Entity, *Entity->Enemy);
	EnemyInfront = IsInFront (Entity, *Entity->Enemy);
	bool EnemyBelow = IsBelow (Entity, *Entity->Enemy);

	if (BadArea)
	{
		if ((EnemyInback) || (EnemyBelow))
			CurrentMove = &CarrierMoveAttackRocket;
		else if ((frand() < 0.1) || (Level.Frame < AttackFinished))
			CurrentMove = &CarrierMovePreAttackMG;
		else
		{
			Entity->PlaySound (CHAN_WEAPON, Sounds[SOUND_RAIL]);
			CurrentMove = &CarrierMoveAttackRail;
		}
		return;
	}

	if (AttackState == AS_BLIND)
	{
		CurrentMove = &CarrierMoveSpawn;
		return;
	}

	if (!EnemyInback && !EnemyInfront && !EnemyBelow) // to side and not under
	{
		if ((frand() < 0.1) || (Level.Frame < AttackFinished)) 
			CurrentMove = &CarrierMovePreAttackMG;
		else
		{
			Entity->PlaySound (CHAN_WEAPON, Sounds[SOUND_RAIL]);
			CurrentMove = &CarrierMoveAttackRail;
		}
		return;
	}

	if (EnemyInfront)
	{
		float range = (Entity->Enemy->State.GetOrigin() - Entity->State.GetOrigin()).Length();
		if (range <= 125)
		{
			if ((frand() < 0.8) || (Level.Frame < AttackFinished))
				CurrentMove = &CarrierMovePreAttackMG;
			else
			{
				Entity->PlaySound (CHAN_WEAPON, Sounds[SOUND_RAIL]);
				CurrentMove = &CarrierMoveAttackRail;
			}
		}
		else if (range < 600)
		{
			float luck = frand();
			if (MonsterSlots > 2)
			{
				if (luck <= 0.20)
					CurrentMove = &CarrierMovePreAttackMG;
				else if (luck <= 0.40)
					CurrentMove = &CarrierMoveAttackPreGrenade;
				else if ((luck <= 0.7) && !(Level.Frame < AttackFinished))
				{
					Entity->PlaySound (CHAN_WEAPON, Sounds[SOUND_RAIL]);
					CurrentMove = &CarrierMoveAttackRail;
				}
				else
					CurrentMove = &CarrierMoveSpawn;
			}
			else
			{
				if (luck <= 0.30)
					CurrentMove = &CarrierMovePreAttackMG;
				else if (luck <= 0.65)
					CurrentMove = &CarrierMoveAttackPreGrenade;
				else if (Level.Frame >= AttackFinished)
				{
					Entity->PlaySound (CHAN_WEAPON, Sounds[SOUND_RAIL]);
					CurrentMove = &CarrierMoveAttackRail;
				}
				else
					CurrentMove = &CarrierMovePreAttackMG;
			}
		}
		else // won't use grenades at this range
		{
			float luck = frand();
			if (MonsterSlots > 2)
			{
				if (luck < 0.3)
					CurrentMove = &CarrierMovePreAttackMG;
				else if ((luck < 0.65) && !(Level.Frame < AttackFinished))
				{
					Entity->PlaySound (CHAN_WEAPON, Sounds[SOUND_RAIL]);
					RailFireSpot = Entity->Enemy->State.GetOrigin() + vec3f(0, 0, Entity->Enemy->ViewHeight);
					CurrentMove = &CarrierMoveAttackRail;
				}
				else
					CurrentMove = &CarrierMoveSpawn;
			}
			else
			{
				if ((luck < 0.45f) || (Level.Frame < AttackFinished))
					CurrentMove = &CarrierMovePreAttackMG;
				else
				{
					Entity->PlaySound (CHAN_WEAPON, Sounds[SOUND_RAIL]);
					CurrentMove = &CarrierMoveAttackRail;
				}
			}
		}
	}
	else if ((EnemyBelow) || (EnemyInback))
		CurrentMove = &CarrierMoveAttackRocket;
}