// Buzzy fly ----------------------------------------------------------------

class LittleFly : Actor
{
	Default
	{
		+NOBLOCKMAP +NOGRAVITY
		+CANPASS
		
		Speed 6;
		Radius 5;
		Height 5;
		Mass 2;
		ActiveSound "FlyBuzz";
	}
	
	States
	{
	Spawn:
		TNT1 A 20 A_FlySearch;	// [RH] Invisible when not flying
		Loop;
	Buzz:
		AFLY ABCD 3 A_FlyBuzz;
		Loop;
	}
	
	//===========================================================================
	//
	// FindCorpse
	//
	// Finds a corpse to buzz around. We can't use a blockmap check because
	// corpses generally aren't linked into the blockmap.
	//
	//===========================================================================

	private Actor FindCorpse(sector sec, int recurselimit)
	{
		Actor fallback = null;
		sec.validcount = validcount;

		// Search the current sector
		for (Actor check = sec.thinglist; check != null; check = check.snext)
		{
			if (check == self)
				continue;
			if (!check.bCorpse)
				continue;
			if (!CheckSight(check))
				continue;
			fallback = check;
			if (random[Fly](0,1))	// 50% chance to try to pick a different corpse
				continue;
			return check;
		}
		if (--recurselimit <= 0 || (fallback != null && random[Fly](0,1)))
		{
			return fallback;
		}
		// Try neighboring sectors
		for (int i = 0; i < sec.lines.Size(); ++i)
		{
			line ln = sec.lines[i];
			sector sec2 = (ln.frontsector == sec) ? ln.backsector : ln.frontsector;
			if (sec2 != null && sec2.validcount != validcount)
			{
				Actor neighbor = FindCorpse(sec2, recurselimit);
				if (neighbor != null)
				{
					return neighbor;
				}
			}
		}
		return fallback;
	}

	void A_FlySearch()
	{
		// The version from the retail beta is not so great for general use:
		// 1. Pick one of the first fifty thinkers at random.
		// 2. Starting from that thinker, find one that is an actor, not itself,
		//    and within sight. Give up after 100 sequential thinkers.
		// It's effectively useless if there are more than 150 thinkers on a map.
		//
		// So search the sectors instead. We can't potentially find something all
		// the way on the other side of the map and we can't find invisible corpses,
		// but at least we aren't crippled on maps with lots of stuff going on.
		validcount++;
		Actor other = FindCorpse(CurSector, 5);
		if (other != null)
		{
			target = other;
			SetStateLabel("Buzz");
		}
	}

	void A_FlyBuzz()
	{
		Actor targ = target;

		if (targ == null || !targ.bCorpse || random[Fly]() < 5)
		{
			SetIdle();
			return;
		}

		Angle = AngleTo(targ);
		args[0]++;
		if (!TryMove(Pos.XY + AngleToVector(angle, 6), true))
		{
			SetIdle(true);
			return;
		}
		if (args[0] & 2)
		{
			Vel.X += (random[Fly]() - 128) / 512.;
			Vel.Y += (random[Fly]() - 128) / 512.;
		}
		int zrand = random[Fly]();
		if (targ.pos.Z + 5. < pos.z && zrand > 150)
		{
			zrand = -zrand;
		}
		Vel.Z = zrand / 512.;
		if (random[Fly]() < 40)
		{
			A_StartSound(ActiveSound, CHAN_VOICE, CHANF_DEFAULT, 0.5f, ATTN_STATIC);
		}
	}
}