/*
#include "actor.h"
#include "info.h"
#include "p_local.h"
#include "s_sound.h"
#include "a_action.h"
#include "m_random.h"
#include "a_hexenglobal.h"
#include "i_system.h"
#include "thingdef/thingdef.h"
#include "g_level.h"
*/

//============================================================================
//
//	Sorcerer stuff
//
// Sorcerer Variables
//		special1		Angle of ball 1 (all others relative to that)
//		StopBall		which ball to stop at in stop mode (MT_???)
//		args[0]			Defense time
//		args[1]			Number of full rotations since stopping mode
//		args[2]			Target orbit speed for acceleration/deceleration
//		args[3]			Movement mode (see SORC_ macros)
//		args[4]			Current ball orbit speed
//	Sorcerer Ball Variables
//		special1		Previous angle of ball (for woosh)
//		special2		Countdown of rapid fire (FX4)
//============================================================================

#define SORCBALL_INITIAL_SPEED 		7
#define SORCBALL_TERMINAL_SPEED		25
#define SORCBALL_SPEED_ROTATIONS 	5
#define SORC_DEFENSE_TIME			255
#define SORC_DEFENSE_HEIGHT			45
#define BOUNCE_TIME_UNIT			(35/2)
#define SORCFX4_RAPIDFIRE_TIME		(6*3)		// 3 seconds
#define SORCFX4_SPREAD_ANGLE		20

#define SORC_DECELERATE		0
#define SORC_ACCELERATE 	1
#define SORC_STOPPING		2
#define SORC_FIRESPELL		3
#define SORC_STOPPED		4
#define SORC_NORMAL			5
#define SORC_FIRING_SPELL	6

#define BALL1_ANGLEOFFSET	0
#define BALL2_ANGLEOFFSET	(ANGLE_MAX/3)
#define BALL3_ANGLEOFFSET	((ANGLE_MAX/3)*2)

void A_SlowBalls (AActor *actor);
void A_StopBalls (AActor *actor);
void A_AccelBalls (AActor *actor);
void A_DecelBalls (AActor *actor);
void A_SorcOffense2 (AActor *actor);
void A_DoBounceCheck (AActor *actor, const char *sound);

static FRandom pr_heresiarch ("Heresiarch");

// The Heresiarch him/itself ------------------------------------------------

class AHeresiarch : public AActor
{
	DECLARE_CLASS (AHeresiarch, AActor)
public:
	const PClass *StopBall;

	void Serialize (FArchive &arc);
	void Die (AActor *source, AActor *inflictor, int dmgflags);
};

IMPLEMENT_CLASS (AHeresiarch)

void AHeresiarch::Serialize (FArchive &arc)
{
	Super::Serialize (arc);
	arc << StopBall;
}

void AHeresiarch::Die (AActor *source, AActor *inflictor, int dmgflags)
{
	// The heresiarch just executes a script instead of a special upon death
	int script = special;
	special = 0;

	Super::Die (source, inflictor, dmgflags);

	if (script != 0)
	{
		P_StartScript (this, NULL, script, level.mapname, NULL, 0, 0);
	}
}

// Base class for the balls flying around the Heresiarch's head -------------

class ASorcBall : public AActor
{
	DECLARE_CLASS (ASorcBall, AActor)
public:
	virtual void DoFireSpell ();
	virtual void SorcUpdateBallAngle ();
	virtual void CastSorcererSpell ();
	angle_t AngleOffset;

	void Serialize (FArchive &arc)
	{
		Super::Serialize (arc);
		arc << AngleOffset;
	}

	bool SpecialBlastHandling (AActor *source, fixed_t strength)
	{ // don't blast sorcerer balls
		return false;
	}
};

IMPLEMENT_CLASS (ASorcBall)

// First ball (purple) - fires projectiles ----------------------------------

class ASorcBall1 : public ASorcBall
{
	DECLARE_CLASS (ASorcBall1, ASorcBall)
public:
	void BeginPlay ()
	{
		Super::BeginPlay ();
		AngleOffset = BALL1_ANGLEOFFSET;
	}
	virtual void DoFireSpell ();
	virtual void SorcUpdateBallAngle ();
	virtual void CastSorcererSpell ();
};

IMPLEMENT_CLASS (ASorcBall1)

// Second ball (blue) - generates the shield --------------------------------

class ASorcBall2 : public ASorcBall
{
	DECLARE_CLASS (ASorcBall2, ASorcBall)
public:
	void BeginPlay ()
	{
		Super::BeginPlay ();
		AngleOffset = BALL2_ANGLEOFFSET;
	}
	virtual void CastSorcererSpell ();
};

IMPLEMENT_CLASS (ASorcBall2)

// Third ball (green) - summons Bishops -------------------------------------

class ASorcBall3 : public ASorcBall
{
	DECLARE_CLASS (ASorcBall3, ASorcBall)
public:
	void BeginPlay ()
	{
		Super::BeginPlay ();
		AngleOffset = BALL3_ANGLEOFFSET;
	}
	virtual void CastSorcererSpell ();
};

IMPLEMENT_CLASS (ASorcBall3)

// Sorcerer spell 1 (The burning, bouncing head thing) ----------------------

/*
class ASorcFX1 : public AActor
{
	DECLARE_CLASS (ASorcFX1, AActor)
public:
	bool FloorBounceMissile (secplane_t &plane)
	{
		fixed_t orgvelz = velz;

		if (!Super::FloorBounceMissile (plane))
		{
			velz = -orgvelz;		// no energy absorbed
			return false;
		}
		return true;
	}
};
IMPLEMENT_CLASS (ASorcFX1)
*/

//============================================================================
//
// SorcBall::DoFireSpell
//
//============================================================================

void ASorcBall::DoFireSpell ()
{
	CastSorcererSpell ();

	// [BB] This is server-side.
	if ( NETWORK_InClientMode() )
	{
		return;
	}

	target->args[3] = SORC_STOPPED;

	// [BB] If we're the server, tell the clients to set the arguments of target.
	if ( NETWORK_GetState( ) == NETSTATE_SERVER )
		SERVERCOMMANDS_SetThingArguments( target );

}

//============================================================================
//
// SorcBall1::DoFireSpell
//
//============================================================================

void ASorcBall1::DoFireSpell ()
{
	// [BB] This is server-side (involves random numbers).
	if ( NETWORK_InClientMode() )
	{
		return;
	}

	if (pr_heresiarch() < 200)
	{
		S_Sound (target, CHAN_VOICE, "SorcererSpellCast", 1, ATTN_NONE, true);	// [BB] Inform the clients.
		special2 = SORCFX4_RAPIDFIRE_TIME;
		args[4] = 128;
		target->args[3] = SORC_FIRING_SPELL;

		// [BB] If we're the server, tell the clients to set special2 and arguments.
		if ( NETWORK_GetState( ) == NETSTATE_SERVER )
		{
			SERVERCOMMANDS_SetThingSpecial2( this );
			SERVERCOMMANDS_SetThingArguments( this );
			SERVERCOMMANDS_SetThingArguments( target );
		}
	}
	else
	{
		Super::DoFireSpell ();
	}
}

//============================================================================
//
// A_SorcSpinBalls
//
// Spawn spinning balls above head - actor is sorcerer
//============================================================================

DEFINE_ACTION_FUNCTION(AActor, A_SorcSpinBalls)
{
	AActor *mo;
	fixed_t z;

	self->SpawnState += 2;		// [RH] Don't spawn balls again
	A_SlowBalls(self);
	self->args[0] = 0;								// Currently no defense
	self->args[3] = SORC_NORMAL;
	self->args[4] = SORCBALL_INITIAL_SPEED;		// Initial orbit speed
	self->special1 = ANGLE_1;
	z = self->z - self->floorclip + self->height;
	
	// [BB] This is server-side.
	if ( NETWORK_InClientMode() )
	{
		return;
	}

	mo = Spawn("SorcBall1", self->x, self->y, z, NO_REPLACE);
	if (mo)
	{
		mo->target = self;
		mo->special2 = SORCFX4_RAPIDFIRE_TIME;

		// [BB] If we're the server, tell the clients to spawn the thing and set its target and special2.
		if ( NETWORK_GetState( ) == NETSTATE_SERVER )
		{
			SERVERCOMMANDS_SpawnThing( mo );
			SERVERCOMMANDS_SetThingTarget( mo );
			SERVERCOMMANDS_SetThingSpecial2( mo );
		}
	}
	mo = Spawn("SorcBall2", self->x, self->y, z, NO_REPLACE);
	if (mo) mo->target = self;

	// [BB] If we're the server, tell the clients to spawn the thing and set its target.
	if ( mo && (NETWORK_GetState( ) == NETSTATE_SERVER) )
	{
		SERVERCOMMANDS_SpawnThing( mo );
		SERVERCOMMANDS_SetThingTarget( mo );
	}

	mo = Spawn("SorcBall3", self->x, self->y, z, NO_REPLACE);
	if (mo) mo->target = self;

	// [BB] If we're the server, tell the clients to spawn the thing and set its target.
	if ( mo && (NETWORK_GetState( ) == NETSTATE_SERVER) )
	{
		SERVERCOMMANDS_SpawnThing( mo );
		SERVERCOMMANDS_SetThingTarget( mo );
	}
}


//============================================================================
//
// A_SorcBallOrbit
//
//============================================================================

DEFINE_ACTION_FUNCTION(AActor, A_SorcBallOrbit)
{
	// [BB] The client should do most of this stuff here. This way the balls
	// can smoothly orbit the Heresiarch without causing too much net traffic.

	// [RH] If no parent, then die instead of crashing
	if (self->target == NULL)
	{
		// [BB] Tell clients to set the thing's state.
		if ( NETWORK_GetState( ) == NETSTATE_SERVER )
			SERVERCOMMANDS_SetThingState( self, STATE_PAIN );

		self->SetState (self->FindState(NAME_Pain));
		return;
	}

	ASorcBall *actor;
	int x,y;
	angle_t angle, baseangle;
	int mode = self->target->args[3];
	AHeresiarch *parent = barrier_cast<AHeresiarch *>(self->target);
	int dist = parent->radius - (self->radius<<1);
	angle_t prevangle = self->special1;

	if (!self->IsKindOf (RUNTIME_CLASS(ASorcBall)))
	{
		I_Error ("Corrupted sorcerer:\nTried to use a %s", RUNTIME_TYPE(self)->TypeName.GetChars());
	}
	actor = static_cast<ASorcBall *> (self);

	if (actor->target->health <= 0)
	{
		// [BB] Tell clients to set the thing's state.
		if ( NETWORK_GetState( ) == NETSTATE_SERVER )
			SERVERCOMMANDS_SetThingState( actor, STATE_PAIN );

		actor->SetState (actor->FindState(NAME_Pain));
		return;
	}

	baseangle = (angle_t)parent->special1;
	angle = baseangle + actor->AngleOffset;
	actor->angle = angle;
	angle >>= ANGLETOFINESHIFT;

	switch (mode)
	{
	case SORC_NORMAL:			// Balls rotating normally
		actor->SorcUpdateBallAngle ();
		break;

	case SORC_DECELERATE:		// Balls decelerating
		A_DecelBalls(actor);
		actor->SorcUpdateBallAngle ();
		break;

	case SORC_ACCELERATE:		// Balls accelerating
		A_AccelBalls(actor);
		actor->SorcUpdateBallAngle ();
		break;

	case SORC_STOPPING:			// Balls stopping
		if ((parent->StopBall == RUNTIME_TYPE(actor)) &&
			 (parent->args[1] > SORCBALL_SPEED_ROTATIONS) &&
			 (absangle(angle - (parent->angle>>ANGLETOFINESHIFT)) < (30<<5)))
		{
			// [BB] This is server-side (since StopBall involved random numbers).
			if ( NETWORK_InClientMode() )
			{
				break;
			}

			// Can stop now
			actor->target->args[3] = SORC_FIRESPELL;
			actor->target->args[4] = 0;
			// Set angle so self angle == sorcerer angle
			parent->special1 = (int)(parent->angle - actor->AngleOffset);

			// [BB] If we're the server, tell the clients to set the arguments of actor->target and special1 of parent.
			if ( NETWORK_GetState( ) == NETSTATE_SERVER )
			{
				SERVERCOMMANDS_SetThingArguments( actor->target );
				SERVERCOMMANDS_SetThingSpecial1( parent );
			}
		}
		else
		{
			actor->SorcUpdateBallAngle ();
		}
		break;

	case SORC_FIRESPELL:			// Casting spell
		if (parent->StopBall == RUNTIME_TYPE(actor))
		{
			// [BB] This is server-side (since StopBall involved random numbers).
			if ( NETWORK_InClientMode() )
			{
				break;
			}

			// Put sorcerer into special throw spell anim
			if (parent->health > 0)
			{
				// [BB] Tell clients to change this thing's state.
				if ( NETWORK_GetState( ) == NETSTATE_SERVER )
					SERVERCOMMANDS_SetThingFrame( parent, parent->FindState("Attack1") );

				parent->SetState (parent->FindState("Attack1"));
			}

			actor->DoFireSpell ();
		}
		break;

	case SORC_FIRING_SPELL:
		if (parent->StopBall == RUNTIME_TYPE(actor))
		{
			// [BB] This is server-side (since StopBall involved random numbers).
			if ( NETWORK_InClientMode() )
			{
				break;
			}

			if (actor->special2-- <= 0)
			{
				// Done rapid firing 
				parent->args[3] = SORC_STOPPED;

				// [BB] If we're the server, tell the clients to set the arguments of parent.
				if ( NETWORK_GetState( ) == NETSTATE_SERVER )
					SERVERCOMMANDS_SetThingArguments( parent );

				// Back to orbit balls
				if (parent->health > 0)
				{
					// [BB] Tell clients to change this thing's state.
					if ( NETWORK_GetState( ) == NETSTATE_SERVER )
						SERVERCOMMANDS_SetThingFrame( parent, parent->FindState("Attack2") );

					parent->SetState (parent->FindState("Attack2"));
				}
			}
			else
			{
				// Do rapid fire spell
				A_SorcOffense2(actor);
			}
		}
		break;

	case SORC_STOPPED:			// Balls stopped
	default:
		break;
	}

	if ((angle < prevangle) && (parent->args[4]==SORCBALL_TERMINAL_SPEED))
	{
		parent->args[1]++;			// Bump rotation counter
		// Completed full rotation - make woosh sound
		S_Sound (actor, CHAN_BODY, "SorcererBallWoosh", 1, ATTN_NORM);
	}
	actor->special1 = angle;		// Set previous angle
	x = parent->x + FixedMul(dist, finecosine[angle]);
	y = parent->y + FixedMul(dist, finesine[angle]);
	actor->SetOrigin (x, y, parent->z - parent->floorclip + parent->height);
	actor->floorz = parent->floorz;
	actor->ceilingz = parent->ceilingz;
}

//============================================================================
//
// A_SpeedBalls
//
// Set balls to speed mode - self is sorcerer
//
//============================================================================

DEFINE_ACTION_FUNCTION(AActor, A_SpeedBalls)
{
	self->args[3] = SORC_ACCELERATE;				// speed mode
	self->args[2] = SORCBALL_TERMINAL_SPEED;		// target speed
}


//============================================================================
//
// A_SlowBalls
//
// Set balls to slow mode - actor is sorcerer
//
//============================================================================

void A_SlowBalls(AActor *actor)
{
	actor->args[3] = SORC_DECELERATE;				// slow mode
	actor->args[2] = SORCBALL_INITIAL_SPEED;		// target speed
}

//============================================================================
//
// A_StopBalls
//
// Instant stop when rotation gets to ball in special2
//		self is sorcerer
//
//============================================================================

void A_StopBalls(AActor *scary)
{
	AHeresiarch *actor = static_cast<AHeresiarch *> (scary);
	int chance = pr_heresiarch();
	actor->args[3] = SORC_STOPPING;				// stopping mode
	actor->args[1] = 0;							// Reset rotation counter

	if ((actor->args[0] <= 0) && (chance < 200))
	{
		actor->StopBall = RUNTIME_CLASS(ASorcBall2);	// Blue
	}
	else if((actor->health < (actor->SpawnHealth() >> 1)) &&
			(chance < 200))
	{
		actor->StopBall = RUNTIME_CLASS(ASorcBall3);	// Green
	}
	else
	{
		actor->StopBall = RUNTIME_CLASS(ASorcBall1);	// Yellow
	}
}

//============================================================================
//
// A_AccelBalls
//
// Increase ball orbit speed - actor is ball
//
//============================================================================

void A_AccelBalls(AActor *actor)
{
	AActor *sorc = actor->target;

	if (sorc->args[4] < sorc->args[2])
	{
		sorc->args[4]++;
	}
	else
	{
		sorc->args[3] = SORC_NORMAL;
		if (sorc->args[4] >= SORCBALL_TERMINAL_SPEED)
		{
			// Reached terminal velocity - stop balls
			A_StopBalls(sorc);
		}
	}
}

//============================================================================
//
// A_DecelBalls
//
// Decrease ball orbit speed - actor is ball
//
//============================================================================

void A_DecelBalls(AActor *actor)
{
	AActor *sorc = actor->target;

	if (sorc->args[4] > sorc->args[2])
	{
		sorc->args[4]--;
	}
	else
	{
		sorc->args[3] = SORC_NORMAL;
	}
}

//============================================================================
//
// ASorcBall1::SorcUpdateBallAngle
//
// Update angle if first ball
//============================================================================

void ASorcBall1::SorcUpdateBallAngle ()
{
	target->special1 += ANGLE_1*target->args[4];
}

//============================================================================
//
// ASorcBall::SorcUpdateBallAngle
//
//============================================================================

void ASorcBall::SorcUpdateBallAngle ()
{
}

//============================================================================
//
// ASorcBall::CastSorcererSpell
//
// Make noise and change the parent sorcerer's animation
//
//============================================================================

void ASorcBall::CastSorcererSpell ()
{
	S_Sound (target, CHAN_VOICE, "SorcererSpellCast", 1, ATTN_NONE, true);	// [BB] Inform the clients.

	// Put sorcerer into throw spell animation
	if (target->health > 0)
	{
		// [BB] Tell clients to change this thing's state.
		if ( NETWORK_GetState( ) == NETSTATE_SERVER )
			SERVERCOMMANDS_SetThingFrame( target, target->FindState("Attack2") );

		target->SetState (target->FindState("Attack2"));
	}
}

//============================================================================
//
// ASorcBall2::CastSorcererSpell
//
// Defensive
//
//============================================================================

void ASorcBall2::CastSorcererSpell ()
{
	Super::CastSorcererSpell ();

	// [BB] This is server-side.
	if ( NETWORK_InClientMode() )
	{
		return;
	}

	AActor *parent = target;
	AActor *mo;

	fixed_t z = parent->z - parent->floorclip + SORC_DEFENSE_HEIGHT*FRACUNIT;
	mo = Spawn("SorcFX2", x, y, z, ALLOW_REPLACE);
	parent->flags2 |= MF2_REFLECTIVE|MF2_INVULNERABLE;
	parent->args[0] = SORC_DEFENSE_TIME;
	if (mo) mo->target = parent;

	// [BB] If we're the server, tell the clients to set some things and spawn mo.
	if ( NETWORK_GetState( ) == NETSTATE_SERVER )
	{
		SERVERCOMMANDS_SetThingFlags( parent, FLAGSET_FLAGS2 );
		SERVERCOMMANDS_SetThingArguments( parent );
		if ( mo )
		{
			SERVERCOMMANDS_SpawnThing( mo );
			SERVERCOMMANDS_SetThingTarget( mo );
		}
	}
}

//============================================================================
//
// ASorcBall3::CastSorcererSpell
//
// Reinforcements
//
//============================================================================

void ASorcBall3::CastSorcererSpell ()
{
	Super::CastSorcererSpell ();

	// [BB] This is server-side.
	if ( NETWORK_InClientMode() )
	{
		return;
	}

	AActor *mo;
	angle_t ang1, ang2;
	AActor *parent = target;

	ang1 = angle - ANGLE_45;
	ang2 = angle + ANGLE_45;
	const PClass *cls = PClass::FindClass("SorcFX3");
	if (health < (SpawnHealth()/3))
	{	// Spawn 2 at a time
		mo = P_SpawnMissileAngle(parent, cls, ang1, 4*FRACUNIT);
		if (mo) mo->target = parent;

		// [BB] If we're the server, tell the clients to spawn the missile.
		if ( mo && (NETWORK_GetState( ) == NETSTATE_SERVER) )
			SERVERCOMMANDS_SpawnMissile( mo );

		mo = P_SpawnMissileAngle(parent, cls, ang2, 4*FRACUNIT);
		if (mo) mo->target = parent;

		// [BB] If we're the server, tell the clients to spawn the missile.
		if ( mo && (NETWORK_GetState( ) == NETSTATE_SERVER) )
			SERVERCOMMANDS_SpawnMissile( mo );
	}			
	else
	{
		if (pr_heresiarch() < 128)
			ang1 = ang2;
		mo = P_SpawnMissileAngle(parent, cls, ang1, 4*FRACUNIT);
		if (mo) mo->target = parent;

		// [BB] If we're the server, tell the clients to spawn the missile.
		if ( mo && (NETWORK_GetState( ) == NETSTATE_SERVER) )
			SERVERCOMMANDS_SpawnMissile( mo );
	}
}


/*
void A_SpawnReinforcements(AActor *actor)
{
	AActor *parent = actor->target;
	AActor *mo;
	angle_t ang;

	ang = ANGLE_1 * P_Random();
	mo = P_SpawnMissileAngle(actor, MT_SORCFX3, ang, 5*FRACUNIT);
	if (mo) mo->target = parent;
}
*/

//============================================================================
//
// SorcBall1::CastSorcererSpell
//
// Offensive
//
//============================================================================

void ASorcBall1::CastSorcererSpell ()
{
	Super::CastSorcererSpell ();

	// [BB] This is server-side.
	if ( NETWORK_InClientMode() )
	{
		return;
	}

	AActor *mo;
	angle_t ang1, ang2;
	AActor *parent = target;

	ang1 = angle + ANGLE_1*70;
	ang2 = angle - ANGLE_1*70;
	const PClass *cls = PClass::FindClass("SorcFX1");
	mo = P_SpawnMissileAngle (parent, cls, ang1, 0);
	if (mo)
	{
		mo->target = parent;
		mo->tracer = parent->target;
		mo->args[4] = BOUNCE_TIME_UNIT;
		mo->args[3] = 15;				// Bounce time in seconds

		// [BB] If we're the server, tell the clients to spawn the missile and set the arguments.
		if ( mo && (NETWORK_GetState( ) == NETSTATE_SERVER) )
		{
			SERVERCOMMANDS_SpawnMissile( mo );
			SERVERCOMMANDS_SetThingArguments( mo );
		}
	}
	mo = P_SpawnMissileAngle (parent, cls, ang2, 0);
	if (mo)
	{
		mo->target = parent;
		mo->tracer = parent->target;
		mo->args[4] = BOUNCE_TIME_UNIT;
		mo->args[3] = 15;				// Bounce time in seconds

		// [BB] If we're the server, tell the clients to spawn the missile and set the arguments.
		if ( mo && (NETWORK_GetState( ) == NETSTATE_SERVER) )
		{
			SERVERCOMMANDS_SpawnMissile( mo );
			SERVERCOMMANDS_SetThingArguments( mo );
		}
	}
}

//============================================================================
//
// A_SorcOffense2
//
// Actor is ball
//
//============================================================================

void A_SorcOffense2(AActor *actor)
{
	angle_t ang1;
	AActor *mo;
	int delta, index;
	AActor *parent = actor->target;
	AActor *dest = parent->target;
	int dist;

	// [RH] If no enemy, then don't try to shoot.
	if (dest == NULL)
	{
		return;
	}

	// [BB] This is server-side.
	if ( NETWORK_InClientMode() )
	{
		return;
	}

	index = actor->args[4] << 5;
	actor->args[4] = (actor->args[4] + 15) & 255;

	// [BB] If we're the server, tell the clients to set the arguments of actor.
	if ( NETWORK_GetState( ) == NETSTATE_SERVER )
		SERVERCOMMANDS_SetThingArguments( actor );

	delta = (finesine[index])*SORCFX4_SPREAD_ANGLE;
	delta = (delta>>FRACBITS)*ANGLE_1;
	ang1 = actor->angle + delta;
	mo = P_SpawnMissileAngle(parent, PClass::FindClass("SorcFX4"), ang1, 0);
	if (mo)
	{
		mo->special2 = 35*5/2;		// 5 seconds
		dist = P_AproxDistance(dest->x - mo->x, dest->y - mo->y);
		dist = dist/mo->Speed;
		if(dist < 1) dist = 1;
		mo->velz = (dest->z - mo->z) / dist;

		// [BB] If we're the server, tell the clients to spawn the missile and set its special2.
		if ( NETWORK_GetState( ) == NETSTATE_SERVER )
		{
			SERVERCOMMANDS_SpawnMissile( mo );
			SERVERCOMMANDS_SetThingSpecial2( mo );
		}
	}
}

//============================================================================
//
// A_SorcBossAttack
//
// Resume ball spinning
//
//============================================================================

DEFINE_ACTION_FUNCTION(AActor, A_SorcBossAttack)
{
	self->args[3] = SORC_ACCELERATE;
	self->args[2] = SORCBALL_INITIAL_SPEED;
}

//============================================================================
//
// A_SpawnFizzle
//
// spell cast magic fizzle
//
//============================================================================

DEFINE_ACTION_FUNCTION(AActor, A_SpawnFizzle)
{
	fixed_t x,y,z;
	fixed_t dist = 5*FRACUNIT;
	angle_t angle = self->angle >> ANGLETOFINESHIFT;
	fixed_t speed = self->Speed;
	angle_t rangle;
	AActor *mo;
	int ix;

	x = self->x + FixedMul(dist,finecosine[angle]);
	y = self->y + FixedMul(dist,finesine[angle]);
	z = self->z - self->floorclip + (self->height>>1);
	for (ix=0; ix<5; ix++)
	{
		mo = Spawn("SorcSpark1", x, y, z, ALLOW_REPLACE);
		if (mo)
		{
			rangle = angle + ((pr_heresiarch()%5) << 1);
			mo->velx = FixedMul(pr_heresiarch()%speed, finecosine[rangle]);
			mo->vely = FixedMul(pr_heresiarch()%speed, finesine[rangle]);
			mo->velz = FRACUNIT*2;
		}
	}
}


//============================================================================
//
// A_SorcFX1Seek
//
// Yellow spell - offense
//
//============================================================================

DEFINE_ACTION_FUNCTION(AActor, A_SorcFX1Seek)
{
	A_DoBounceCheck (self, "SorcererHeadScream");
	P_SeekerMissile (self,ANGLE_1*2,ANGLE_1*6);
}


//============================================================================
//
// A_SorcFX2Split
//
// Blue spell - defense
//
//============================================================================
//
// FX2 Variables
//		special1		current angle
//		special2
//		args[0]		0 = CW,  1 = CCW
//		args[1]		
//============================================================================

// Split ball in two
DEFINE_ACTION_FUNCTION(AActor, A_SorcFX2Split)
{
	AActor *mo;

	// [BB] This is server-side. The client only destroy the actor.
	if ( NETWORK_InClientMode() )
	{
		self->Destroy ();
		return;
	}

	mo = Spawn(self->GetClass(), self->x, self->y, self->z, NO_REPLACE);
	if (mo)
	{
		mo->target = self->target;
		mo->args[0] = 0;									// CW
		mo->special1 = self->angle;					// Set angle

		// [BB] If we're the server, tell the clients to spawn the thing.
		if ( NETWORK_GetState( ) == NETSTATE_SERVER )
		{
			SERVERCOMMANDS_SpawnThing( mo );
			SERVERCOMMANDS_SetThingTarget( mo );
			SERVERCOMMANDS_SetThingArguments( mo );
			SERVERCOMMANDS_SetThingSpecial1( mo );
			SERVERCOMMANDS_SetThingFrame( mo, mo->FindState("Orbit") );
		}

		mo->SetState (mo->FindState("Orbit"));
	}
	mo = Spawn(self->GetClass(), self->x, self->y, self->z, NO_REPLACE);
	if (mo)
	{
		mo->target = self->target;
		mo->args[0] = 1;									// CCW
		mo->special1 = self->angle;					// Set angle

		// [BB] If we're the server, tell the clients to spawn the thing.
		if ( NETWORK_GetState( ) == NETSTATE_SERVER )
		{
			SERVERCOMMANDS_SpawnThing( mo );
			SERVERCOMMANDS_SetThingTarget( mo );
			SERVERCOMMANDS_SetThingArguments( mo );
			SERVERCOMMANDS_SetThingSpecial1( mo );
			SERVERCOMMANDS_SetThingFrame( mo, mo->FindState("Orbit") );
		}

		mo->SetState (mo->FindState("Orbit"));
	}
	self->Destroy ();
}

//============================================================================
//
// A_SorcFX2Orbit
//
// Orbit FX2 about sorcerer
//
//============================================================================

DEFINE_ACTION_FUNCTION(AActor, A_SorcFX2Orbit)
{
	angle_t angle;
	fixed_t x,y,z;
	AActor *parent = self->target;

	// [RH] If no parent, then disappear
	if (parent == NULL)
	{
		self->Destroy();
		return;
	}

	fixed_t dist = parent->radius;

	// [BB] This is server-side.
	if ( NETWORK_InClientMode() == false )
	{
		if ((parent->health <= 0) ||		// Sorcerer is dead
			(!parent->args[0]))				// Time expired
		{
			// [BB] Tell clients to set the thing's state.
			if ( NETWORK_GetState( ) == NETSTATE_SERVER )
				SERVERCOMMANDS_SetThingState( self, STATE_DEATH );

			self->SetState (self->FindState(NAME_Death));
			parent->args[0] = 0;
			parent->flags2 &= ~MF2_REFLECTIVE;
			parent->flags2 &= ~MF2_INVULNERABLE;

			// [BB] If we're the server, tell the clients to update flags and arguments.
			if ( NETWORK_GetState( ) == NETSTATE_SERVER )
			{
				SERVERCOMMANDS_SetThingFlags( parent, FLAGSET_FLAGS2 );
				SERVERCOMMANDS_SetThingArguments( parent );
			}
		}

		if (self->args[0] && (parent->args[0]-- <= 0))		// Time expired
		{
			// [BB] Tell clients to set the thing's state.
			if ( NETWORK_GetState( ) == NETSTATE_SERVER )
				SERVERCOMMANDS_SetThingState( self, STATE_DEATH );

			self->SetState (self->FindState(NAME_Death));
			parent->args[0] = 0;
			parent->flags2 &= ~MF2_REFLECTIVE;

			// [BB] If we're the server, tell the clients to update flags and arguments.
			if ( NETWORK_GetState( ) == NETSTATE_SERVER )
			{
				SERVERCOMMANDS_SetThingFlags( parent, FLAGSET_FLAGS2 );
				SERVERCOMMANDS_SetThingArguments( parent );
			}
		}
	}

	// Move to new position based on angle
	if (self->args[0])		// Counter clock-wise
	{
		self->special1 += ANGLE_1*10;
		angle = ((angle_t)self->special1) >> ANGLETOFINESHIFT;
		x = parent->x + FixedMul(dist, finecosine[angle]);
		y = parent->y + FixedMul(dist, finesine[angle]);
		z = parent->z - parent->floorclip + SORC_DEFENSE_HEIGHT*FRACUNIT;
		z += FixedMul(15*FRACUNIT,finecosine[angle]);
		// Spawn trailer
		Spawn("SorcFX2T1", x, y, z, ALLOW_REPLACE);
	}
	else							// Clock wise
	{
		self->special1 -= ANGLE_1*10;
		angle = ((angle_t)self->special1) >> ANGLETOFINESHIFT;
		x = parent->x + FixedMul(dist, finecosine[angle]);
		y = parent->y + FixedMul(dist, finesine[angle]);
		z = parent->z - parent->floorclip + SORC_DEFENSE_HEIGHT*FRACUNIT;
		z += FixedMul(20*FRACUNIT,finesine[angle]);
		// Spawn trailer
		Spawn("SorcFX2T1", x, y, z, ALLOW_REPLACE);
	}

	self->SetOrigin (x, y, z);
	self->floorz = parent->floorz;
	self->ceilingz = parent->ceilingz;
}

//============================================================================
//
// A_SpawnBishop
//
// Green spell - spawn bishops
//
//============================================================================

DEFINE_ACTION_FUNCTION(AActor, A_SpawnBishop)
{
	// [BB] This is server-side. The client only destroy the actor.
	if ( NETWORK_InClientMode() )
	{
		self->Destroy ();
		return;
	}

	AActor *mo;
	mo = Spawn("Bishop", self->x, self->y, self->z, ALLOW_REPLACE);
	if (mo)
	{
		if (!P_TestMobjLocation(mo))
		{
			mo->ClearCounters();
			mo->Destroy ();
		}
		else if (self->target != NULL)
		{ // [RH] Make the new bishops inherit the Heriarch's target
			mo->CopyFriendliness (self->target, true);
			mo->master = self->target;

			// [BB] If we're the server, tell the clients to spawn the thing.
			if ( NETWORK_GetState( ) == NETSTATE_SERVER )
				SERVERCOMMANDS_SpawnThing( mo );
		}
	}
	self->Destroy ();
}

//============================================================================
//
// A_SorcererBishopEntry
//
//============================================================================

DEFINE_ACTION_FUNCTION(AActor, A_SorcererBishopEntry)
{
	Spawn("SorcFX3Explosion", self->x, self->y, self->z, ALLOW_REPLACE);
	S_Sound (self, CHAN_VOICE, self->SeeSound, 1, ATTN_NORM);
}

//============================================================================
//
// A_SorcFX4Check
//
// FX4 - rapid fire balls
//
//============================================================================

DEFINE_ACTION_FUNCTION(AActor, A_SorcFX4Check)
{
	// [BB] This is server-side.
	if ( NETWORK_InClientMode() )
	{
		return;
	}

	if (self->special2-- <= 0)
	{
		// [BB] Tell clients to set the thing's state.
		if ( NETWORK_GetState( ) == NETSTATE_SERVER )
			SERVERCOMMANDS_SetThingState( self, STATE_DEATH );

		self->SetState (self->FindState(NAME_Death));
	}
}

//============================================================================
//
// A_SorcBallPop
//
// Ball death - bounce away in a random direction
//
//============================================================================

DEFINE_ACTION_FUNCTION(AActor, A_SorcBallPop)
{
	S_Sound (self, CHAN_BODY, "SorcererBallPop", 1, ATTN_NONE);
	self->flags &= ~MF_NOGRAVITY;
	self->gravity = FRACUNIT/8;
	self->velx = ((pr_heresiarch()%10)-5) << FRACBITS;
	self->vely = ((pr_heresiarch()%10)-5) << FRACBITS;
	self->velz = (2+(pr_heresiarch()%3)) << FRACBITS;
	self->special2 = 4*FRACUNIT;		// Initial bounce factor
	self->args[4] = BOUNCE_TIME_UNIT;	// Bounce time unit
	self->args[3] = 5;					// Bounce time in seconds
}

//============================================================================
//
// A_DoBounceCheck
//
//============================================================================

void A_DoBounceCheck (AActor *self, const char *sound)
{
	if (self->args[4]-- <= 0)
	{
		if (self->args[3]-- <= 0)
		{
			self->SetState (self->FindState(NAME_Death));
			S_Sound (self, CHAN_BODY, sound, 1, ATTN_NONE);
		}
		else
		{
			self->args[4] = BOUNCE_TIME_UNIT;
		}
	}
}

//============================================================================
//
// A_BounceCheck
//
//============================================================================

DEFINE_ACTION_FUNCTION(AActor, A_BounceCheck)
{
	A_DoBounceCheck (self, "SorcererBigBallExplode");
}
