#include "g_local.h"

void angleToward(edict_t *self, vec3_t point, float speed);

// spawnflags
#define AC_SF_START_OFF			1
#define AC_SF_BERSERK			2
#define AC_SF_BERSERK_TOGGLE	4

// variables
#define AC_RANGE	2048
#define AC_TIMEOUT	2.0
#define AC_EXPLODE_DMG	150
#define AC_EXPLODE_RADIUS	384
#define AC_TURN_SPEED	6.0
#define AC_TURN_DELAY	1.0
// states
#define AC_S_IDLE	0
#define AC_S_ACTIVATING	1
#define AC_S_ACTIVE	2
#define AC_S_DEACTIVATING 3
// models
char* models[] = {	NULL, 
					"models/objects/acannon/chain/tris.md2",
					"models/objects/acannon/rocket/tris.md2",
					"models/objects/acannon/laser/tris.md2",
					"models/objects/acannon/laser/tris.md2" };
char* floorModels[] = {	NULL, 
					"",
					"models/objects/acannon/rocket2/tris.md2",
					"models/objects/acannon/laser2/tris.md2",
					"models/objects/acannon/laser2/tris.md2" };

// pitch extents
const int acPitchExtents[2][2] = {	{0,60}, // max, min
									{-60,0} 
								};

// frames filler/chain/rocket/laser
const int acIdleStart[] = { 0, 0, 0, 0, 0 };
const int acIdleEnd[] = { 0, 0, 0, 0, 0 };
const int acActStart[] = { 0, 1, 1, 1, 1 };
const int acActEnd[] = { 0, 9, 9, 9, 9 };
const int acActiveStart[] = { 0, 10, 10, 10, 10 };
const int acActiveEnd[] = { 0, 10, 10, 10, 10 };

typedef struct ac_anim_frame_s
{
	qboolean last;
	qboolean fire;
	int frame;
} ac_anim_frame_t;

typedef struct ac_anim_s
{
	int firstNonPause;
	ac_anim_frame_t frames[32];
} ac_anim_t;

ac_anim_t acFiringFrames[5] = 
{
	// dummy
	{
		0,
		{ true, false, -1 }
	},

	// chaingun
	{
		6,
		{
			{ false, false, 11 },
			{ false, false, 11 },
			{ false, false, 11 },
			{ false, false, 11 },
			{ false, false, 11 },
			{ false, false, 11 },
			// start of firing sequence
			{ false, true, 11 },
			{ false, false, 12 },
			{ false, true, 13 },
			{ false, false, 14 },
			{ false, true, 15 },
			{ false, false, 16 },
			{ false, true, 17 },
			{ false, false, 18 },
			{ false, true, 19 },
			{ false, false, 20 },
			{ false, true, 21 },
			{ true, false, 2 },
		}
	},

	// rockets
	{
		6,
		{
			{ false, false, 11 },
			{ false, false, 11 },
			{ false, false, 11 },
			{ false, false, 11 },
			{ false, false, 11 },
			{ false, false, 11 },
			// start of firing sequence
			{ false, true, 11 },
			{ false, false, 11 },
			{ false, false, 12 },
			{ false, false, 12 },
			{ false, false, 13 },
			{ false, false, 13 },
			{ false, false, 14 },
			{ false, false, 14 },
			{ false, false, 15 },
			{ false, false, 15 },
			{ false, false, 16 },
			{ true, false, 16 },
		}
	},

	// laser
	{
		6,
		{
			{ false, false, 11 },
			{ false, false, 11 },
			{ false, false, 11 },
			{ false, false, 11 },
			{ false, false, 11 },
			{ false, false, 11 },
			// start of firing sequence
			{ false, true, 11 },
			{ false, false, 11 },
			{ false, false, 11 },
			{ false, false, 11 },
			{ false, false, 11 },
			{ true, false, 11 },
		}
	},

	// slow laser
	{
		6,
		{
			{ false, false, 11 },
			{ false, false, 11 },
			{ false, false, 11 },
			{ false, false, 11 },
			{ false, false, 11 },
			{ false, false, 11 },
			// start of firing sequence
			{ false, true, 11 },
			{ false, false, 11 },
			{ false, false, 11 },
			{ false, false, 11 },
			{ false, false, 11 },
			{ false, false, 11 },
			{ false, false, 11 },
			{ false, false, 11 },
			{ false, false, 11 },
			{ false, false, 11 },
			{ false, false, 11 },
			{ true, false, 11 },
		}
	}
};

vec3_t fireOffset[5] = {	{0,0,0},
							{24,-4,0},
							{0,-4,0},
							{24,-5,0},
							{24,-5,0} };
const int acDeactStart[] = { 0, 23, 23, 23, 23 };
const int acDeactEnd[] = { 0, 31, 31, 31, 31 };
const qboolean turretIdle[] = { false, false, true, true }; // collapse when idle?

// turret animations
const int turretIdleStart = 0;
const int turretIdleEnd = 0;
const int turretActStart = 1;
const int turretActEnd = 9;
const int turretActiveStart = 10;
const int turretActiveEnd = 10;
const int turretDeactStart = 23;
const int turretDeactEnd = 31;

// bullet params
#define AC_BULLET_DMG	4.0
#define AC_BULLET_KICK	2.0
// rocket params
#define AC_ROCKET_DMG	100
#define AC_ROCKET_SPEED	650
#define AC_ROCKET_RADIUS_DMG	120
#define AC_ROCKET_DMG_RADIUS	120
// blaster params
#define AC_BLASTER_DMG		20
#define AC_BLASTER_SPEED	1000

void monster_autocannon_fire(edict_t *self)
{
	vec3_t forward, right, start;

	// fire straight ahead
	AngleVectors (self->s.angles, forward, right, NULL);
	if (self->onFloor)
		VectorNegate(right, right);
	VectorMA(self->s.origin, 24, forward, start);
	G_ProjectSource (self->s.origin, fireOffset[self->style], forward, right, start);

	if(EMPNukeCheck(self, start))
	{
		gi.sound (self, CHAN_AUTO, gi.soundindex("items/empnuke/emp_missfire.wav"), 1, ATTN_NORM, 0);
		return;
	}

	// what to fire?
	switch(self->style)
	{
	case 1:
	default:
		fire_bullet(self, start, forward, AC_BULLET_DMG, AC_BULLET_KICK, DEFAULT_BULLET_HSPREAD, DEFAULT_BULLET_VSPREAD, MOD_AUTOCANNON);
		gi.WriteByte (svc_muzzleflash);
		gi.WriteShort (self - g_edicts);
		gi.WriteByte (MZ_CHAINGUN2);
		gi.multicast (self->s.origin, MULTICAST_PVS);
		break;
	case 2:
		fire_rocket(self, start, forward, AC_ROCKET_DMG, AC_ROCKET_SPEED, AC_ROCKET_RADIUS_DMG, AC_ROCKET_DMG_RADIUS);
		gi.WriteByte (svc_muzzleflash);
		gi.WriteShort (self - g_edicts);
		gi.WriteByte (MZ_ROCKET);
		gi.multicast (self->s.origin, MULTICAST_PVS);
		break;
	case 3:
	case 4:
		fire_blaster (self, start, forward, AC_BLASTER_DMG, AC_BLASTER_SPEED, EF_HYPERBLASTER, true);
		gi.WriteByte (svc_muzzleflash);
		gi.WriteShort (self - g_edicts);
		gi.WriteByte (MZ_HYPERBLASTER);
		gi.multicast (self->s.origin, MULTICAST_PVS);
		break;
	}
}

qboolean angleBetween(float *ang, float *min, float *max)
{
	// directly between?
	if (*ang > *min && *ang < *max)
		return true;

	// make positive
	while(*min < 0)
		*min += 360.0;
	while(*ang < *min)
		*ang += 360.0;
	while(*max < *min)
		*max += 360.0;

	if (*ang > *min && *ang < *max)
		return true;
	else
		return false;
}

float mod180(float val)
{
	while(val > 180)
		val -= 360.0;
	while(val < -180)
		val += 360.0;
	return val;
}

qboolean canShoot(edict_t *self, edict_t *e)
{
	vec3_t delta;
	vec3_t dangles;

	VectorSubtract(e->s.origin, self->s.origin, delta);
	vectoangles(delta, dangles);
	dangles[PITCH] = mod180(dangles[PITCH]);
	
	if ((!self->onFloor && dangles[PITCH] < 0) ||
		(self->onFloor && dangles[PITCH] > 0)) // facing up or down
		return false;


	if (self->monsterinfo.linkcount > 0)
	{
		float ideal_yaw = self->monsterinfo.attack_state;
		float max_yaw = anglemod(ideal_yaw + self->monsterinfo.linkcount);
		float min_yaw = anglemod(ideal_yaw - self->monsterinfo.linkcount);
		
		if (!angleBetween(&dangles[YAW], &min_yaw, &max_yaw))
			return false;
	}
	
	return true;
}

qboolean autocannonInfront (edict_t *self, edict_t *other)
{
	vec3_t vec;
	vec3_t angle;
	float dot;
	float min = -30.0;
	float max = 30.0;
	
	// what's the yaw distance between the 2?
	VectorSubtract (other->s.origin, self->s.origin, vec);
	vectoangles(vec, angle);
	dot = angle[YAW] - self->s.angles[YAW];
	
	if (angleBetween(&dot, &min, &max))
		return true;
	return false;
}

void monster_autocannon_findenemy(edict_t *self)
{
	edict_t *e = NULL;

	// can we still use our enemy?
	if (self->enemy)
	{
		if (!canShoot(self, self->enemy))
		{
			self->oldenemy = NULL;
			self->enemy = NULL;
		}
		else if (!visible(self, self->enemy))
		{
			self->oldenemy = self->enemy;
			self->enemy = NULL;
		}
		else if (self->enemy->flags & FL_NOTARGET)
		{
			self->oldenemy = NULL;
			self->enemy = NULL;
		}
		else if (self->enemy->health <= 0)
		{
			self->oldenemy = NULL;
			self->enemy = NULL;
		}
	}

	while(self->enemy == NULL)
	{
		e = findradius(e, self->s.origin, AC_RANGE);
		if (e == NULL)
		{
			if (self->oldenemy == NULL)
				return;

			if (level.time > self->timeout)
			{
				self->oldenemy = NULL;
				return;
			}
			self->enemy = self->oldenemy;
			break;
		}


		if (self->spawnflags & AC_SF_BERSERK)
		{
			// attack clients and monsters
			if (!e->client && !(e->svflags & SVF_MONSTER))
				continue;
		}
		else
		{
			// only attack clients
			if (!e->client)
				continue;
		}

		// don't target dead stuff
		if (e->health <= 0)
			continue;

		// don't target notarget stuff
		if (e->flags & FL_NOTARGET)
			continue;

		// don't target other autocannons
		if (Q_stricmp(e->classname, "monster_autocannon") == 0)
			continue;
		
		// don't target self
		if (e == self)
			continue;
		
		// can it be seen?
		if (!visible(self, e))
			continue;

		if (!autocannonInfront(self, e))
			continue;

		if (canShoot(self, e))
			self->enemy = e;
	}
}

void monster_autocannon_turn(edict_t *self)
{
	vec3_t old_angles;
	VectorCopy(self->s.angles, old_angles);
	if (!self->enemy)
	{
		if (self->monsterinfo.linkcount > 0)
		{
			int ideal_yaw = self->monsterinfo.attack_state;
			int max_yaw = anglemod(ideal_yaw + self->monsterinfo.linkcount);
			int min_yaw = anglemod(ideal_yaw - self->monsterinfo.linkcount);

			while (max_yaw < min_yaw)
				max_yaw += 360.0;
		
			self->s.angles[YAW] += (self->monsterinfo.lefty ? -AC_TURN_SPEED : AC_TURN_SPEED);
			
			// back and forth
			if (self->s.angles[YAW] > max_yaw)
			{
				self->monsterinfo.lefty = 1;
				self->s.angles[YAW] = max_yaw;
			}
			else if (self->s.angles[YAW] < min_yaw)
			{
				self->monsterinfo.lefty = 0;
				self->s.angles[YAW] = min_yaw;
			}
		}
		else
		{
			self->s.angles[YAW] = anglemod(self->s.angles[YAW] + AC_TURN_SPEED);
		}

		// angle pitch towards 5 to 10...
		if (!self->onFloor)
		{
			if (self->s.angles[PITCH] > 10)
				self->s.angles[PITCH] -= 4;
			else if (self->s.angles[PITCH] < 5)
				self->s.angles[PITCH] += 4;
		}
		else
		{
			if (self->s.angles[PITCH] < -10)
				self->s.angles[PITCH] += 4;
			else if (self->s.angles[PITCH] > -5)
				self->s.angles[PITCH] -= 4;
		}
	}
	else
	{
		// look toward enemy mid point
		if (visible(self, self->enemy))
		{
			vec3_t offset, dest;
			VectorCopy(self->enemy->mins, offset);
			VectorAdd(offset, self->enemy->maxs, offset);
			VectorScale(offset, 0.65, offset);
			VectorAdd(self->enemy->s.origin, offset, dest);
			angleToward(self, dest, AC_TURN_SPEED);
			VectorCopy(dest, self->monsterinfo.last_sighting);
			self->timeout = level.time + AC_TIMEOUT;

			// restrict our range of movement if need be
			if (self->monsterinfo.linkcount > 0)
			{
				float amax = anglemod(self->monsterinfo.attack_state + self->monsterinfo.linkcount);
				float amin = anglemod(self->monsterinfo.attack_state - self->monsterinfo.linkcount);
				self->s.angles[YAW] = anglemod(self->s.angles[YAW]);
				if (!angleBetween(&self->s.angles[YAW], &amin, &amax))
				{
					// which is closer?
					if (self->s.angles[YAW] - amax < amin - self->s.angles[YAW])
						self->s.angles[YAW] = amin;
					else
						self->s.angles[YAW] = amax;
				}
			}

		}
		else // not visible now, so head toward last known spot
			angleToward(self, self->monsterinfo.last_sighting, AC_TURN_SPEED);
	}
	
	// get our angles between 180 and -180
	while(self->s.angles[PITCH] > 180)
		self->s.angles[PITCH] -= 360.0;
	while(self->s.angles[PITCH] < -180)
		self->s.angles[PITCH] += 360;

	// outside of the pitch extents?
	if (self->s.angles[PITCH] > acPitchExtents[self->onFloor][1])
		self->s.angles[PITCH] = acPitchExtents[self->onFloor][1];
	else if (self->s.angles[PITCH] < acPitchExtents[self->onFloor][0])
		self->s.angles[PITCH] = acPitchExtents[self->onFloor][0];
	
	// make sure the turret's angles match the gun's
	self->chain->s.angles[YAW] = self->s.angles[YAW];
	self->chain->s.angles[PITCH] = 0;

	// setup the sound
	if (VectorCompare(self->s.angles, old_angles))
		self->chain->s.sound = 0;
	else
		self->chain->s.sound = gi.soundindex("objects/acannon/ac_idle.wav");
}

void monster_autocannon_think(edict_t *self)
{
	ac_anim_frame_t frame;
	ac_anim_t anim;
	int lefty = 0;
	edict_t *old_enemy;

	self->nextthink = level.time + FRAMETIME;

	// get an enemy
	old_enemy = self->enemy;
	monster_autocannon_findenemy(self);
	if (self->enemy != NULL && old_enemy != self->enemy)
		gi.sound(self, CHAN_VOICE, gi.soundindex("objects/acannon/ac_act.wav"), 1, ATTN_NORM, 0);

	// turn whereever
	lefty = self->monsterinfo.lefty;
	if (level.time > self->delay)
	{
		monster_autocannon_turn(self);
		if (self->monsterinfo.lefty != lefty)
			self->delay = level.time + AC_TURN_DELAY;
	}

	anim = acFiringFrames[self->style];
	frame = anim.frames[self->seq];
		
	// ok, we don't have an enemy
	if (self->enemy == NULL)
	{
		if (self->seq == 0)
		{
			// get into idle animation
			self->s.frame++;
			if (self->s.frame > acActiveEnd[self->style] ||
				self->s.frame < acActiveStart[self->style])
				self->s.frame = acActiveStart[self->style];
			return; // done, we want to wait here
		}

		// set the frame
		self->s.frame = frame.frame;

		// fire
		if (frame.fire)
			monster_autocannon_fire(self);
	
		// if we're not done with the firing sequence, we need to finish it off
		if (frame.last) // end of the loop or firing frame?
			self->seq = 0;
		else
			self->seq++;

		return;
	}

	// we have an enemy but he's not infront, go to the beginning of the firing sequence
	if (!autocannonInfront(self, self->enemy))
	{
		self->s.frame = frame.frame;
		if (self->seq == anim.firstNonPause)
			return; // done, we want to wait here

		if (frame.last) // end of the loop or firing frame?
			self->seq = anim.firstNonPause;
		else
			self->seq++;

		return;
	}


	// we have an enemy, AND he's visible
	// let's kick his ass
	self->s.frame = frame.frame;
	if (frame.fire)
		monster_autocannon_fire(self);
	
	if (frame.last) // end of the loop?
		self->seq = anim.firstNonPause;
	else
		self->seq++;
}

void monster_autocannon_explode (edict_t *ent)
{
	vec3_t origin;

	T_RadiusDamage(ent, ent, AC_EXPLODE_DMG, ent->enemy, AC_EXPLODE_RADIUS, MOD_TRIPBOMB);

	VectorMA (ent->s.origin, -0.02, ent->velocity, origin);
	gi.WriteByte (svc_temp_entity);
	if (ent->waterlevel)
	{
		if (ent->groundentity)
			gi.WriteByte (TE_GRENADE_EXPLOSION_WATER);
		else
			gi.WriteByte (TE_ROCKET_EXPLOSION_WATER);
	}
	else
	{
		if (ent->groundentity)
			gi.WriteByte (TE_GRENADE_EXPLOSION);
		else
			gi.WriteByte (TE_ROCKET_EXPLOSION);
	}
	gi.WritePosition (origin);
	gi.multicast (ent->s.origin, MULTICAST_PHS);

	// set the pain skin
	ent->chain->chain->s.skinnum = 1; // pain
	ent->chain->chain->rideWith[0] = NULL;
	ent->chain->chain->rideWith[1] = NULL;
	G_FreeEdict(ent->chain);
	G_FreeEdict(ent);
}


void monster_autocannon_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
{
	// explode
	self->takedamage = DAMAGE_NO;
	self->think = monster_autocannon_explode;
	self->nextthink = level.time + FRAMETIME;
}

void monster_autocannon_pain (edict_t *self, edict_t *other, float kick, int damage)
{
	// keep the enemy
	if (other->client || other->svflags & SVF_MONSTER)
		self->enemy = other;
}

void monster_autocannon_activate(edict_t *self)
{
	self->active = AC_S_ACTIVATING;
	self->nextthink = level.time + FRAMETIME;

	// go thru the activation frames
	if (self->s.frame >= acActStart[self->style] &&
		self->s.frame < acActEnd[self->style])
	{
		if (self->s.frame == acActStart[self->style])
		{
			//gi.sound(self, CHAN_VOICE, gi.soundindex("objects/acannon/ac_out.wav"), 1, ATTN_NORM, 0);
		}
		// continue
		self->s.frame++;
		self->chain->s.frame++;
	}
	else if (self->s.frame == acActEnd[self->style])
	{
		self->s.frame = acActiveStart[self->style];
		self->chain->s.frame = turretActiveStart;
		self->think = monster_autocannon_think;
		self->active = AC_S_ACTIVE;
	}
	else
	{
		self->s.frame = acActStart[self->style];
		self->chain->s.frame = turretActStart;
	}
}

void monster_autocannon_deactivate(edict_t *self)
{
	self->active = AC_S_DEACTIVATING;
	self->nextthink = level.time + FRAMETIME;
	
	// go thru the deactivation frames
	if (self->s.angles[PITCH] != 0)
	{
		if (self->s.angles[PITCH] > 0)
		{
			self->s.angles[PITCH] -= 5;
			if (self->s.angles[PITCH] < 0)
				self->s.angles[PITCH] = 0;
		}
		else
		{
			self->s.angles[PITCH] += 5;
			if (self->s.angles[PITCH] > 0)
				self->s.angles[PITCH] = 0;
		}
	}
	else if (self->s.frame >= acDeactStart[self->style] &&
		self->s.frame < acDeactEnd[self->style])
	{
		self->chain->s.sound = 0;
		if (self->s.frame == acDeactStart[self->style])
		{
			//gi.sound(self, CHAN_VOICE, gi.soundindex("objects/acannon/ac_away.wav"), 1, ATTN_NORM, 0);
		}
		// continue
		self->s.frame++;
		self->chain->s.frame++;
	}
	else if (self->s.frame == acDeactEnd[self->style])
	{
		self->s.frame = acIdleStart[self->style];
		self->chain->s.frame = turretIdleStart;
		self->think = NULL;
		self->nextthink = 0;
		self->chain->s.sound = 0;
		self->active = AC_S_IDLE;
	}
	else
	{
		self->s.frame = acDeactStart[self->style];
		self->chain->s.frame = turretDeactStart;
	}
}

void monster_autocannon_act(edict_t *self)
{
	if (self->active == AC_S_IDLE)
	{
		if (acActStart[self->style] != -1)
			self->think = monster_autocannon_activate;
		else
		{
			self->s.frame = acActiveStart[self->style];
			self->chain->s.frame = turretActiveStart;
			self->think = monster_autocannon_think;
			self->active = AC_S_ACTIVE;
		}
		self->nextthink = level.time + FRAMETIME;
	}
	else if (self->active == AC_S_ACTIVE)
	{
		if (acDeactStart[self->style] != -1)
		{
			self->nextthink = level.time + FRAMETIME;
			self->think = monster_autocannon_deactivate;
		}
		else
		{
			if (turretIdle[self->style])
				self->chain->s.frame = turretIdleStart;
			else
				self->chain->s.frame = turretActiveStart;
			self->s.frame = acActiveStart[self->style];
			self->think = NULL;
			self->active = AC_S_IDLE;
			self->nextthink = 0;
		}
	}
}

void monster_autocannon_use(edict_t *self, edict_t *other, edict_t *activator)
{
	// on/off or berserk toggle?
	if (self->spawnflags & AC_SF_BERSERK_TOGGLE)
	{
		if (self->spawnflags & AC_SF_BERSERK)
			self->spawnflags &= ~AC_SF_BERSERK;
		else
			self->spawnflags |= AC_SF_BERSERK;
	}
	else
		monster_autocannon_act(self);
}

void monster_autocannon_usestub(edict_t *self)
{
	// stub
	monster_autocannon_act(self);
}

void SP_monster_autocannon(edict_t *self)
{
	edict_t *base, *turret;
	vec3_t offset;

	if (deathmatch->value)
	{
		G_FreeEdict(self);
		return;
	}
	
	if (self->style > 4 || self->style < 1)
		self->style = 1;

	// if we're on hard or nightmare, use fast lasers
	if (skill->value >= 2 && self->style == 4)
		self->style = 3;

	// precache some sounds and models
	gi.soundindex("objects/acannon/ac_idle.wav");
	gi.soundindex("objects/acannon/ac_act.wav");
	//gi.soundindex("objects/acannon/ac_out.wav");
	//gi.soundindex("objects/acannon/ac_away.wav");
	gi.modelindex("models/objects/rocket/tris.md2");
	gi.modelindex("models/objects/laser/tris.md2");

	// create the base
	base = G_Spawn();
	base->classname = "autocannon base";
	base->solid = SOLID_BBOX;
	VectorCopy(self->s.origin, base->s.origin);
	if (!self->onFloor)
		base->movetype = MOVETYPE_NONE;
	else
		base->movetype = MOVETYPE_RIDE; // make the base MOVETYPE_RIDE so that it can ride on trains

	if (!self->onFloor)
		base->s.modelindex = gi.modelindex("models/objects/acannon/base/tris.md2");
	else
		base->s.modelindex = gi.modelindex("models/objects/acannon/base2/tris.md2");
	gi.linkentity(base);

	// create the turret
	turret = G_Spawn();
	turret->classname = "autocannon turret";
	turret->solid = SOLID_BBOX;
	turret->movetype = MOVETYPE_NONE;
	turret->chain = base;
	VectorCopy(self->s.origin, turret->s.origin);
	if (!self->onFloor)
		turret->s.modelindex = gi.modelindex("models/objects/acannon/turret/tris.md2");
	else
		turret->s.modelindex = gi.modelindex("models/objects/acannon/turret2/tris.md2");
	if (turretIdle[self->style])
		turret->s.frame = turretIdleStart;
	else
		turret->s.frame = turretActiveStart;
	turret->s.angles[YAW] = self->s.angles[YAW];
	turret->s.angles[PITCH] = 0;
	gi.linkentity(turret);
	
	// fill in the details about ourself
	self->solid = SOLID_BBOX;
	self->movetype = MOVETYPE_NONE;
	if (!self->onFloor)
		VectorSet(offset, 0, 0, -20); // offset down a bit
	else
		VectorSet(offset, 0, 0, 20); // offset up a bit
	VectorAdd(self->s.origin, offset, self->s.origin);

	// set the bounding box
	if (!self->onFloor)
	{
		VectorSet(self->mins, -12, -12, -28);
		VectorSet(self->maxs, 12, 12, 16);
	}
	else
	{
		VectorSet(self->mins, -12, -12, -16);
		VectorSet(self->maxs, 12, 12, 28);
	}
	self->chain = turret;
	if (!self->onFloor)
		self->s.modelindex = gi.modelindex(models[self->style]);
	else
		self->s.modelindex = gi.modelindex(floorModels[self->style]);
	self->s.frame = acIdleStart[self->style];
	self->active = AC_S_IDLE;
	self->monsterinfo.lefty = 0;

	self->monsterinfo.attack_state = self->s.angles[YAW]; // used for centre of back-and-forth "search"
	self->seq = 0;
	if (st.lip)
		self->monsterinfo.linkcount = (st.lip > 0 ? st.lip : 0);
	//self->svflags = SVF_MONSTER;

	// default health
	if (!self->health)
		self->health = 100;

	// enable/disable? ... berserk/not
	if (self->targetname)
		self->use = monster_autocannon_use;
	
	if (self->spawnflags & AC_SF_BERSERK_TOGGLE || !(self->spawnflags & AC_SF_START_OFF))
	{
		self->think = monster_autocannon_usestub;
		self->nextthink = level.time + FRAMETIME;
	}

	self->takedamage = DAMAGE_AIM;
	self->die = monster_autocannon_die;
	self->pain = monster_autocannon_pain;

	// last but not least, setup the "rideWith" information
	base->rideWith[0] = turret;
	VectorSubtract(turret->s.origin, base->s.origin, base->rideWithOffset[0]);
	base->rideWith[1] = self;
	VectorSubtract(self->s.origin, base->s.origin, base->rideWithOffset[1]);

	gi.linkentity(self);
}

void SP_monster_autocannon_floor(edict_t *self)
{
	if (self->style == 1)
	{
		gi.error("monster_autocannon_floor does not permit bullet style");
		G_FreeEdict(self);
		return;
	}

	if (self->style < 1 || self->style > 4)
		self->style = 2;
	self->onFloor = 1; // signify floor mounted
	// call the other one
	SP_monster_autocannon(self);
}
