#include "templates.h"
#include "doomtype.h"
#include "doomstat.h"
#include "p_local.h"
#include "actor.h"
#include "m_bbox.h"
#include "m_random.h"
#include "s_sound.h"

static FRandom pr_quake ("Quake");

class DEarthquake : public DThinker
{
	DECLARE_CLASS (DEarthquake, DThinker)
	HAS_OBJECT_POINTERS
public:
	DEarthquake (AActor *center, int intensity, int duration, int damrad, int tremrad);

	void Serialize (FArchive &arc);
	void Tick ();

	AActor *m_Spot;
	fixed_t m_TremorRadius, m_DamageRadius;
	int m_Intensity;
	int m_Countdown;
	int m_QuakeSFX;
private:
	DEarthquake () {}
};

IMPLEMENT_POINTY_CLASS (DEarthquake)
 DECLARE_POINTER (m_Spot)
END_POINTERS

void DEarthquake::Serialize (FArchive &arc)
{
	Super::Serialize (arc);
	arc << m_Spot << m_Intensity << m_Countdown
		<< m_TremorRadius << m_DamageRadius;
	m_QuakeSFX = S_FindSound ("world/quake");
}

void DEarthquake::Tick ()
{
	int i;

	if (m_Spot == NULL)
	{
		Destroy ();
		return;
	}

	if (!S_GetSoundPlayingInfo (m_Spot, m_QuakeSFX))
		S_SoundID (m_Spot, CHAN_BODY, m_QuakeSFX, 1, ATTN_NORM);

	for (i = 0; i < MAXPLAYERS; i++)
	{
		if (playeringame[i] && !(players[i].cheats & CF_NOCLIP))
		{
			AActor *victim = players[i].mo;
			fixed_t dist;

			dist = P_AproxDistance (victim->x - m_Spot->x, victim->y - m_Spot->y);
			// Tested in tile units (64 pixels)
			if (dist < m_TremorRadius)
			{
				players[i].xviewshift = m_Intensity;
			}
			// Check if in damage radius
			if (dist < m_DamageRadius && victim->z <= victim->floorz)
			{
				if (pr_quake() < 50)
				{
					P_DamageMobj (victim, NULL, NULL, pr_quake.HitDice (1), MOD_UNKNOWN);
				}
				// Thrust player around
				angle_t an = victim->angle + ANGLE_1*pr_quake();
				P_ThrustMobj (victim, an, m_Intensity << (FRACBITS-1));
			}
		}
	}
	if (--m_Countdown == 0)
	{
		Destroy ();
	}
}

DEarthquake::DEarthquake (AActor *center, int intensity, int duration,
						  int damrad, int tremrad)
{
	m_QuakeSFX = S_FindSound ("world/quake");
	m_Spot = center;
	m_DamageRadius = damrad << (FRACBITS+6);
	m_TremorRadius = tremrad << (FRACBITS+6);
	m_Intensity = intensity;
	m_Countdown = duration;
}

bool P_StartQuake (int tid, int intensity, int duration, int damrad, int tremrad)
{
	AActor *center;
	FActorIterator iterator (tid);
	bool res = false;

	intensity = clamp (intensity, 1, 9);

	while ( (center = iterator.Next ()) )
	{
		res = true;
		new DEarthquake (center, intensity, duration, damrad, tremrad);
	}
	
	return res;
}
