#include "global.h"

float abs(float x) { if (x > 0) return x; else return -x; }

display_list TankObject::tankModel[NUM_TANK_MODELS];
unsigned short int TankObject::tankArmorStrength[NUM_TANK_MODELS] =
{ 100, };
//timet TankObject::TankStateDuration[TS_NUM_STATES] = { 3.0, 0.0, 1.5, 0.0 };
timet TankObject::TankStateDuration[TS_NUM_STATES] = { 3.0, 0.0, 3.0, 0.0 };

vec3f TankObject::tankTurretOffset[NUM_TANK_MODELS] = {
	vec3f( 1, 2, 1.5 )*3,
	vec3f( 0, 0, 0   ),
};
display_list TankObject::tankTurret[NUM_TANK_MODELS];


inline vec3f ComputeCubicSplinePos(vec3f p0, vec3f v0, vec3f p3, vec3f v3, double t)
{
	vec3f p1 = p0 + v0 * 0.3;
	vec3f p2 = p3 - v3 * 0.3;

	vec3f A = p3 - p2*3 + p1*3 - p0;
	vec3f B = p2*3 - p1*6 + p0*3;
	vec3f C = p1*3 - p0*3;
	vec3f D = p0;

	vec3f pos = A * t*t*t + B * t*t + C * t + D;

	return pos;
}

bool MobileModel::updateInterp(double timenow, double dtime)
{
	double progress = (timenow-startTime)/interpTime;
	if (progress < 1.0 && hasTargetPos)
	{
		pos = ComputeCubicSplinePos(startPos,startVel,targetPos,targetVel,progress);
		//pos = targetPos * progress + startPos * (1.0-progress);
		//vel = targetVel * progress + startVel * (1.0-progress);
		//printf(" [%.1lf] ",progress);
	}
	else
	{
		if (hasTargetPos)
		{
			dtime = timenow-startTime-interpTime;
			hasTargetPos = false;
			pos = targetPos;
			vel = targetVel;
		}
		else
			progress = 0;
		update(dtime);
	}
	return hasTargetPos;
}
void MobileModel::setInterpParam(vec3f tgtPos, vec3f tgtVel, double timenow, double interpDuration)
{
	startPos = pos;
	startVel = vel;
	targetPos = tgtPos;
	targetVel = tgtVel;
	startTime = timenow;
	interpTime = interpDuration;
	hasTargetPos = true;
}


TankObject::TankObject()
{
	model.pos = vec3f(0,0,0);
	model.vel = vec3f(0,0,0);
	model.acc = vec3f(0,0,0);

	model.hasTargetPos = false;

	leftside =true;

	hoverStatus = HOVER_OFF;

	Acceleration = 5000;
	BrakeStrength = 1000;
	MaxVelocity = 200;

	FullHoverHeight = 1.0;
	HoverEngineStrength = 20;

	MaxDamageFreeBounceDepth = 1.0;

	model.dlist = &tankModel[0];

	weaponFireRate = 0.050;

	//myworld = NULL;
	
	hoverStartupTime = 2.0135;
	hoverShutdownTime = 2.01363;

	active = false;
	kills = deaths = 0;
}

#define KB_TURN_SPEED -100.0
#define TANK_DRAG 0.1
//#define BOUNCE_VEL 0.05
#define BOUNCE_VEL 0.2

void TankObject::HandleInput(float dtime, InputStatus_type input)
{
	if (state != TS_NORMAL)
	{
		float FrictionStrength = 0.1;
		model.vel += -model.vel * FrictionStrength * (1.0-GetHoverAmount()) * dtime;
		model.acc[X] = model.acc[Y] = model.acc[Z] = 0;
		return;
	}

	if (input.left)
		model.dir -= KB_TURN_SPEED * dtime;
	if (input.right)
		model.dir += KB_TURN_SPEED * dtime;
	model.dir -= input.turnAmount;

	if (model.dir >= 360)	model.dir -= 360;
	if (model.dir < 0)		model.dir += 360;

	if (input.thrust && hoverStatus!=HOVER_OFF)
	{
		model.acc[X] = cos(model.dir * M_PI / 180.0) * Acceleration * dtime;
		model.acc[Y] = sin(model.dir * M_PI / 180.0) * Acceleration * dtime;
	}
	else if (input.brake)
	{
		model.acc[X] = -model.vel[X]/model.vel.length() * BrakeStrength * dtime;
		model.acc[Y] = -model.vel[Y]/model.vel.length() * BrakeStrength * dtime;
		model.acc[X] += -model.vel[X] * BrakeStrength/100 * dtime;
		model.acc[Y] += -model.vel[Y] * BrakeStrength/100 * dtime;
	}
	else
	{
		model.acc[X] = model.acc[Y] = 0;
	}
	model.acc[Z] = 0;

	// Friction
	float FrictionStrength = 0.1;
	model.vel += -model.vel * FrictionStrength * (1.0-GetHoverAmount()) * dtime;

	if (input.fire)
	{
		/*
		if (myworld != &game.world)
			printf("Trying to fire!\n");
		*/
		if (myworld->currentTime-lastWeaponFire > weaponFireRate)
		{
			// Fire something!
			FireWeapon();
			//printf("\nFire!\n");
		}
		else
		{
			//printf("\r Can't fire: rate = %.3f   dtime = %.3f  lastfire = %.3f ",weaponFireRate,myworld->currentTime-lastWeaponFire,lastWeaponFire);
		}
	}

	/*
	vec3f sliding = myworld->map.GetNormal(model.pos) * myworld->settings.gravity;
	model.acc[X] += sliding[X];
	model.acc[Y] += sliding[Y];
	model.acc[Z] -= sliding[Z];
	*/

	// Limit velocity
	if (model.vel.length() > MaxVelocity)
		model.vel = model.vel / model.vel.length() * MaxVelocity;

}

double frand();

void TankObject::FireWeapon()
{
	int i;
	// Must find an empty slot in the world projectile array
	for (i=0; i < MAX_PROJECTILES; i++)
	{
		if (myworld->projectiles[i].active == PS_INACTIVE)
		{
			// Found one!  Now set it's attributes:

			/*
			float dir = model.dir + (frand()-0.5)*5;
			//float dvel = (frand()-0.5)*0.05;
			float dz = norm.dot(UP)*2;
			vec3f pvel( cos(dir*M_PI/180.0), sin(dir*M_PI/180.0), dz);
			*/
			
			/*
			float dir = model.dir + (frand()-0.5)*5;
			float dvel = (frand()-0.5)*0.05;
			vec3f pvel( cos(dir*M_PI/180.0), sin(dir*M_PI/180.0), dvel);
			vec3f ppos = model.pos;
			*/
			//vec3f dvel = norm*(frand()-0.5)*0.05 + raxis*(frand()-0.5)*0.05;
			//vec3f pvel = dir3d + dvel;
			
			/*
			float dir = -model.dir + (frand()-0.5)*5;
			float u = (1+(frand()-0.5)*0.05) * cos(dir*M_PI/180.0); 
			float v = (1+(frand()-0.5)*0.05) * sin(dir*M_PI/180.0);
			
			vec3f pvel = raxis * u + dir3d * v;
			*/

			vec3f mnrm = myworld->map.GetNormal(model.pos);
			float dir = model.dir + (frand()-0.5)*5;
			float dvel = (frand()-0.5)*0.05;
			vec3f pvel( cos(dir*M_PI/180.0), sin(dir*M_PI/180.0), dvel);

			//pvel.normalize();
			vec3f perpv = mnrm.cross(pvel);
			perpv.normalize();
			vec3f adjvel = perpv.cross(mnrm);
			//adjvel.normalize();
			pvel = adjvel;

			vec3f ppos = model.pos;

			if (leftside)
				ppos += perpv*12.0 + adjvel*10.0 + mnrm*10.0;
				//ppos += vec3f(12,10,10);
			else
				ppos += perpv*-12.0 + adjvel*10.0 + mnrm*10.0;
				//ppos += vec3f(-12,10,10);
			leftside = !leftside;

			myworld->projectiles[i] = ProjectileInfo(PT_MGBULLET, ppos, pvel*700.0, myworld);
			myworld->projectiles[i].active = PS_FLYING;
			myworld->projectiles[i].sourceObjectPtr = (void*)this;

			float dist = (model.pos-myworld->eyepoint.pos).square_norm() * 1.0/5000.0;

			dist = 55-dist;
			//printf("\r firing at %d vol  \n",(int)dist);
			if (myworld->settings.sound && dist > 0)
				PlaySound(HTMGunFireSound,FALSE,(int)dist);
			lastWeaponFire = myworld->currentTime;
			return;
		}
	}
	printf("* No free projectiles! *\n");

}

vec3f calcHoverAccel(const MobileModel& hoverer, float tgtHoverHeight, float enginePower, float gravity, MapObject& m)
{
	vec3f accel(0,0,0);
	float height = hoverer.pos[Z] - m.GetHeight(hoverer.pos[X],hoverer.pos[Y]);
	// Normalized height wrt target HH -- 0 @ HH, 1 at Z=0, (-) at Z > HH
	float normheight = (tgtHoverHeight-height)/tgtHoverHeight;
	float fuzzyrange = -10.0;
	float fuzzyval = normheight/fuzzyrange;
	if (normheight >= 0)
	{
		accel[Z] = normheight*normheight * enginePower;// + -hoverer.vel[Z];
		accel[Z] += - (1-normheight) * hoverer.vel[Z];
	}
	else if (normheight >= fuzzyrange)
	{
		accel[Z] = - fuzzyval * gravity;
		accel[Z] += - (1-fuzzyval) * hoverer.vel[Z];
	}
	else
		accel[Z] = -gravity;

	return accel;
}	

void calcSimpleHoverAccel(MobileModel& hoverer, float tgtHoverHeight, float gravity, MapObject& m)
{
	float mapZ = m.GetHeight(hoverer.pos);
	float height = hoverer.pos[Z] - mapZ;

	float zVelThresh = 10;

	if (height > tgtHoverHeight)
	{
		//hoverer.acc[Z] -= gravity;
		hoverer.vel[Z] = tgtHoverHeight-height;
	}
	else //if (abs(hoverer.vel[Z]) < zVelThresh)
	{
		hoverer.vel[Z] = tgtHoverHeight-height;
	}
}

void TankObject::update(double dtime)
{
	MapObject& m=myworld->map;
	float height = model.pos[Z] - m.GetHeight(model.pos);

	//--- Update motion information:
	// Drag
	model.acc += (-model.vel) * TANK_DRAG;
	// (Thrust taken care of in handle input)
	// Gravity
	//model.acc += calcHoverAccel(model,FullHoverHeight,HoverEngineStrength,myworld->settings.gravity,m);
	calcSimpleHoverAccel(model,FullHoverHeight,myworld->settings.gravity,m);

	//--- Check Collisions:
	// (1) Collision with screen edge
	// Check if the soon-to-be new position will bounce off the edges of the world
	vec3f bounceNormal = m.GetBoundsNorm(model.pos+model.vel*dtime);
	// If we got a non-zero normal back, then it hit something,
	// so compute the bounce trajectory
	if (bounceNormal.square_norm() != 0)
		model.bounce(bounceNormal);
	// (2) Collision with ground -- changes velocity insantaneously (ouch!)
	if (height <= 0)
	{
		if (height < -MaxDamageFreeBounceDepth)
			BounceDamage(MaxDamageFreeBounceDepth, model.vel[Z] );
		if (model.vel[Z] < 0)
			model.vel[Z] = -model.vel[Z] * BOUNCE_VEL;
		//model.pos[Z] = m.GetHeight(model.pos[X],model.pos[Y])+0.5;
		model.pos[Z] = m.GetHeight(model.pos[X],model.pos[Y]);
	}

	// Update the tank pos
	//if (model.hasTargetPos && myworld->settings.usingCubicSplineInterp)
	if (myworld->settings.usingCubicSplineInterp)
		model.updateInterp(myworld->currentTime,dtime);
	else
		model.update(dtime);
	// Attach to ground
	//model.pos[Z] = m.GetHeight(model.pos[X],model.pos[Y])+0.5;

	if (hoverStatus != HOVER_OFF)
	{
		float hfreq = model.vel.length() / MaxVelocity / 5 + 1;
		float dist  = (myworld->eyepoint.pos-model.pos).length()/500.0;
		if (dist < 0.5) dist *= 2.5;
		if (dist < 0.1) dist *= 5;
		if (dist > 1) dist = 1;
		//dist = dist*dist;
		/*
		if (hoverStatus == HOVER_STARTUP)
			ModSoundParms(HTEngineStartupSound,1.0-dist,hfreq);
		else if (hoverStatus == HOVER_GOING)
			ModSoundParms(HTEngineHoverSound,1.0-dist,hfreq);
		else if (hoverStatus == HOVER_SHUTDOWN)
			ModSoundParms(HTEngineShutdownSound,1.0-dist,hfreq);
		else
			printf("Unknown hover status!\n");
			*/
	}

	/*
	if (myworld != &game.world)
		printf("*");
	*/

	if (TankStateDuration[state] != 0)
	{
		const float engineStartTime = TankStateDuration[TS_CREATING] * 0.75;
		float progress = (myworld->currentTime-stateStartTime)/TankStateDuration[state];
		if (state==TS_CREATING && hoverStatus == HOVER_OFF)
		{
			if (myworld->currentTime > stateStartTime + engineStartTime)
				StartHover();
		}
		if (myworld->currentTime > stateStartTime + TankStateDuration[state])
		{
			if (state == TS_CREATING)
				state = TS_NORMAL;
			else if (state == TS_EXPLODING)
			{
				// Check to see if we're a server or client:
				//if (myworld != &game.world)
				if (myworld->ThisWorldIsTheServer)
					Spawn();
				else
					state = TS_WAIT_FOR_SERVER;
			}
		}
	}

	// Update the hovering status:
	CheckHoverStatus();
}

void TankObject::BounceDamage(float bouncedepth, float velmag)
{
	//printf(" * BANG * \n");
	/*
	if (myworld->settings.sound)
		PlaySound(HTTankInpactSoft);
	*/
	// Compute bounce damage and play appropriate sound here
}

void TankObject::StartHover()
{
	if (hoverStatus == HOVER_OFF)
	{
		//printf("Starting hover!\n");
		if (myworld->settings.sound)
			PlaySound(HTEngineStartupSound,0,20);
		hoverStatus = HOVER_STARTUP;
		hoverTimeRef = myworld->currentTime;

		/*
		ParticleEmitter& p = myworld->particleEngine.NewEmitter();
		p.InitEmitter( PARTICLE_EMITTER_DUST_KICKUP ); 
		p.attach( &model );
		p.grav = vec3f( 0.0, 0.0, 0.3 );
		p.wind = vec3f( 5.0, 0.0, 0.5 );
		p.k_drag = 0.3;
		p.particleEmissionRate = 50.0;
		*/
	}
}

void TankObject::StopHover()
{
	//printf("Stopping!\n");
	if (hoverStatus == HOVER_GOING)
	{
		if (myworld->settings.sound)
		{
			StopSound(HTEngineHoverSound);
			PlaySound(HTEngineShutdownSound,0,20);
		}
		hoverStatus = HOVER_SHUTDOWN;
		hoverTimeRef = myworld->currentTime;

		//myworld->particleEngine.unattach(&model);
	}
	else if (hoverStatus == HOVER_STARTUP)
	{
		if (myworld->settings.sound)
		{
			StopSound(HTEngineStartupSound);
			PlaySound(HTEngineShutdownSound,0,20);
		}
		hoverStatus = HOVER_SHUTDOWN;
		hoverTimeRef = myworld->currentTime;

		//myworld->particleEngine.unattach(&model);
	}
}
float TankObject::GetHoverAmount()
{
	switch (hoverStatus)
	{
		case HOVER_STARTUP:
			return (myworld->currentTime-hoverTimeRef)/hoverStartupTime;
		case HOVER_GOING:			return 1;
		case HOVER_SHUTDOWN:
			return 1.0-(myworld->currentTime-hoverTimeRef)/hoverShutdownTime;
		case HOVER_OFF:			return 0;
	}
	return 0;
}
void TankObject::CheckHoverStatus()
{
	if (hoverStatus == HOVER_STARTUP)
	{
		if (myworld->currentTime > hoverTimeRef + hoverStartupTime)
		{
			if (myworld->settings.sound)
				PlaySound(HTEngineHoverSound,TRUE,20);
			hoverStatus = HOVER_GOING;
			/*
			if (myworld == &game.world)
				printf("\nClient Hover Engaged\n");
			else
				printf("\nServer Hover Engaged\n");
				*/
		}
	}
	else if (hoverStatus == HOVER_SHUTDOWN)
	{
		if (myworld->currentTime > hoverTimeRef + hoverShutdownTime)
		{
			hoverStatus = HOVER_OFF;
			/*
			printf("\nHover off\n");
			*/
		}
	}
}

void DrawTankModel();
void TankObject::LoadModelData()
{}

void TankObject::ReleaseModelData()
{
	for (int i=0; i < NUM_TANK_MODELS; i++)
		tankModel[i].del();
}

GLint GenTankJuggernaut();
GLint GenTankAssault();
GLint GenTankSunder();
GLint GenTankSmurfCruncher();
GLint GenTankUAV();
void TankObject::RegisterModelData()
{
	if (!tankModel[0].is_valid())
	{
		tankModel[0].SetListID( GenTankUAV() );
	}
	if (!tankModel[1].is_valid())
	{
		tankModel[1].SetListID( GenTankSmurfCruncher() );
	}
	if (!tankModel[2].is_valid())
	{
		tankModel[2].SetListID( GenTankSunder() );
	}
	if (!tankModel[3].is_valid())
	{
		tankModel[3].SetListID( GenTankAssault() );
	}
	if (!tankModel[4].is_valid())
	{
		tankModel[4].SetListID( GenTankJuggernaut() );
	}

	/*
	model.dlist.new_list(GL_COMPILE);
	glPushMatrix();
	glRotatef(90,0.0,0.0,1.0);
	glRotatef(90,1.0,0.0,0.0);
	glScalef(20.0,20.0,20.0);
	DrawTankModel();
	glPopMatrix();
	*/
	/*
	glTranslatef(0,0,2.6);
	glutSolidCube(5.0);

	glPushMatrix();
	glScalef(1,1,10);
	glTranslatef(0,0,-0.5);
	glutSolidCube(1.0);
	glPopMatrix();

	glTranslatef(0,0,2.51);
	//glColorMaterial(GL_FRONT,GL_EMISSION);
	vec4f e(1.0,1.0,0.0,1.0);
	vec4f d(0.0,0.0,0.0,1.0);
	glMaterial(GL_FRONT,GL_EMISSION,e);
	glColor3f(1.0,1.0,0.0);
	glBegin(GL_POLYGON);
		glVertex3f(-2.5,-2.5,0.0);
		glVertex3f(2.5,0.0,0.0);
		glVertex3f(-2.5,2.5,0.0);
	glEnd();
	glMaterial(GL_FRONT,GL_EMISSION,d);
	*/
	//model.dlist.end_list();
}

void TankObject::Draw()
{
	/*
	vec3f landnorm = myworld->map.GetNormal(model.pos);
	vec3f yawaxis = landnorm.cross(model.vel);
	vec3f tiltaxis = model.vel;

	if (raxis.square_norm > 0)
	{
		glRotate();
	}
	*/
	//DrawModel();

	glPushMatrix();
	glTranslatef(model.pos[X],model.pos[Y],model.pos[Z]);//myworld->map.GetHeight(model.pos));

	switch (state)
	{
		case TS_CREATING:
		case TS_NORMAL:
		{
			float progress = (myworld->currentTime-stateStartTime)/TankStateDuration[TS_CREATING];
			if (progress > 0.85)
			{
				//if (model.pos[Z]-myworld->map.GetHeight(model.pos) < FullHoverHeight)
				{
					vec3f n = myworld->map.GetNormal(model.pos);
					raxis = n.cross(UP);
					float phi = acos(n.dot(UP)) * 180.0 / M_PI;
					glRotate(-phi,raxis);

					dir3d = raxis.cross(n);
					dir3d.normalize();
					raxis.normalize();
					mnrm = n;
				}

				glRotate(model.dir, UP);

				//glTranslatef(0,0,model.pos[Z]-myworld->map.GetHeight(model.pos));
				glColor4f(1.0,1.0,1.0,1.0);
				//model.dlist->call_list();
				tankModel[modelnum].call_list();
				/*
				if (tankTurret[0].is_valid())
				{
					glTranslate(tankTurretOffset[0]);
					tankTurret[0].call_list();
				}
				*/
			}

			if (progress < 1.0)
			{
				glEnable(GL_BLEND);
				GLfloat difmat[4] = { progress, progress, 0.0, 1.0-progress };
				glMaterialfv(GL_FRONT, GL_DIFFUSE, difmat);
				glColor4f( progress, progress, 0.0 , 1.0-progress );
				glutSolidSphere(25.0*progress,8,8);
				glDisable(GL_BLEND);
			}

			break;
		}
		case TS_EXPLODING:
		{
			/*
			float progress = (myworld->currentTime-stateStartTime)/TankStateDuration[state];
			glEnable(GL_BLEND);
			glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
			glColor4f( 1.0, 1.0-progress*progress, 1.0-progress*progress, progress );
			glutSolidSphere(10.0+30*progress,8,8);
			glDisable(GL_BLEND);
			*/
			break;
		}
		case TS_WAIT_FOR_SERVER:
		{
			// Do nothing -- waiting for server to respawn us!
			break;
		}
		default:
			printf("\nError: Unknown tank state: %d\n",state);
			break;
	}

	glPopMatrix();
	
	/*
	glRotatef(90,0.0,0.0,1.0);
	glRotatef(90,1.0,0.0,0.0);
	glScalef(20.0,20.0,20.0);
	DrawTankModel();
	*/

	/*
	GLfloat headlightcol[] = { 1.0, 1.0, 1.0, 1.0 };
	GLfloat headlightpos[] = { 1.0, 1.0, 1.25, 1.0 };
	GLfloat headlightdir[] = { 0.0, -1.0, 0.0, 1.0 };
	glLightfv(GL_LIGHT1,GL_DIFFUSE,headlightcol);
	glLightfv(GL_LIGHT1,GL_POSITION,headlightpos);
	glLightfv(GL_LIGHT1,GL_SPOT_DIRECTION,headlightdir);
	glLightf(GL_LIGHT1,GL_SPOT_CUTOFF,10.0);
	glEnable(GL_LIGHT1);
	*/
}

bool TankObject::ContainsPoint(vec3f p)
{
	float r2 = (model.pos - p).square_norm();
	if (r2 < 35*35 && r2 > 0)
		return true;
	return false;
}

//TankObject tank;

//class WorldInformation* ProjectileInfo::myworld = &game.world;

ProjectileInfo::ProjectileInfo(ProjectileType newType, vec3f pos, vec3f vel, class WorldInformation* world)
{
	myworld = world;
	type = newType;
	model.pos = pos;
	model.vel = vel;
	model.acc = vec3f(0,0,-myworld->settings.gravity/50);
	startTime = myworld->currentTime;
	lifespan = 0.500; // .5 = 1/2 sec
}

void ProjectileInfo::update(timet dtime)
{
	printf("\r ");
	if (myworld->currentTime-startTime > lifespan)
		active = PS_INACTIVE;
	else if (!myworld->map.IsWithinBounds(model.pos))
		active = PS_INACTIVE;
	else if (model.pos[Z] < myworld->map.GetHeight(model.pos))
		active = PS_INACTIVE;
	else
	{
		for (int i=0; i < MAX_PLAYERS; i++)
			if (&myworld->tank[i] != this->sourceObjectPtr && myworld->tank[i].active)
			//if (&myworld->tank[i] == this->sourceObjectPtr && myworld->tank[i].active)
			{
				if (myworld->tank[i].ContainsPoint(model.pos))
				{
					if (active == PS_FLYING)
					{
						float speed = model.vel.length();
						model.vel = vec3f((frand()-0.5)*speed,(frand()-0.5)*speed,(frand()-0.5)*speed);
						startTime = myworld->currentTime;
						lifespan = 0.020;

						// Only the server computes hitting
						if (myworld->tank[i].ReceiveHit(this))
						{
							((TankObject*)this->sourceObjectPtr)->kills++;
							game.netserver.NotifyOfKill(((TankObject*)this->sourceObjectPtr), &myworld->tank[i]);
							printf("SERVER> notified server of kill\n");
						}

						active = PS_EXPLODING;

						if (myworld->settings.sound)
							PlaySound(HTMGunTankHit,0,60);
					}
				}
			}
	}

	if (active != PS_INACTIVE)
		model.update(dtime);
}

void ProjectileInfo::Draw()
{
	float dtime = 0.030;
	if (type == PT_MGBULLET)
	{
		glBegin(GL_LINES);
			glVertex(model.pos);
			glVertex(model.pos+model.vel*dtime);
		glEnd();
	}
}


vec3f calcFollow(const MobileModel& follower, const MobileModel& target, const FollowerDynamics& dyn)
{
	float Vtarget;
	float A, ApproachVel;

	ApproachVel = max( dyn.ApproachVel , target.vel.length() * dyn.closeAccelScale );

	vec3f dp = dyn.followerOffset;
	vec3f dt = follower.pos-(target.pos+dyn.followerOffset);
	float normdist = (dt[X]*dt[X]+dt[Y]*dt[Y])/(dp[X]*dp[X]+dp[Y]*dp[Y]);
	vec3f pTarget = follower.pos-target.pos-dyn.followerOffset;
	if (normdist > 0)
		normdist = sqrt(normdist);
	if (normdist > 1)
		pTarget[Z] -= normdist * dyn.followerOffset[Z] / 10.0;

	//vec3f dpos = follower.pos-target.pos-dyn.followerOffset;
	vec3f dpos = pTarget;
	vec3f accel;

	// Treat each component (X,Y,Z) independently
	for (int i=0; i < dpos.size(); i++)
	{
		// First, check to see if we're within the "close handling" distance:
		if (abs(dpos[i]) < dyn.ApproachRadius)
		{
			// If we are, then first scale the target velocity gradually to
			// zero from the ApproachVel as the follower gets close to the target
			//Vtarget = -dpos[i]/dyn.ApproachRadius * dyn.ApproachVel;
			Vtarget = -dpos[i]/dyn.ApproachRadius * ApproachVel;
			// Also, scale the acceleration to counteract the current velocity.
			// This way, if we enter the radius with a huge velocity, it'll
			// quickly close on the target velocity.
			//A = abs(Vtarget - follower.vel[i]);
			//A = abs(Vtarget - follower.vel[i]) * (1.0 + 3 * myworld->tank.model.vel.length() / myworld->tank.MaxVelocity);
			A = abs(Vtarget - follower.vel[i]) * (1.0 + 3 * target.vel.length() / game.world.tank[0].MaxVelocity);
		}

		// Otherwise, we are approaching the object
		// and needed to determine which side
		// we're approaching from.
		else
		{
			A = dyn.ApproachAccel;
			//if (dpos[i] < 0)	Vtarget = dyn.ApproachVel;
			//else					Vtarget = -dyn.ApproachVel;
			if (dpos[i] < 0)	Vtarget = ApproachVel;
			else					Vtarget = -ApproachVel;
		}

		// Finally, assign the actual acceleration depending on
		// our current velocity vs the target velocity.
		if (follower.vel[i] > Vtarget)	accel[i] = -A;
		else										accel[i] =  A;
	}
	
	return accel;
}

/*
void GameVariablesReset()
{

	return;
}
*/

bool TankObject::ReceiveHit(ProjectileInfo *bullet)
{
	// Disallow local client to predict damage or hits
	//if (myworld == &game.world)
	//	return false;
	if (!myworld->ThisWorldIsTheServer)
		return false;

	switch (bullet->type)
	{
		case PT_MGBULLET:
			armor -= 5;
			break;
		default:
			printf("\nHit by unknown bullet type!\n");
			break;
	}
	if (armor < 0 && state != TS_EXPLODING)
	{
		/*
		state = TS_EXPLODING;
		stateStartTime = myworld->currentTime;
		*/
		SetState(TS_EXPLODING);
		return true;
	}
	return false;
}

void TankObject::Reset(WorldInformation *containerWorld)
{
	myworld = containerWorld;
	//model.pos = vec3f(100,100,0);
	//model.pos[Z] = myworld->map.GetHeight(model.pos);
	modelnum = 0;
	model.dlist = &tankModel[modelnum];
	//active = false;
	kills = deaths = 0;
	state = TS_WAIT_FOR_SERVER;
	stateStartTime = myworld->currentTime;
	hoverStatus = HOVER_OFF;
	lastWeaponFire = 0;
	armor = tankArmorStrength[modelnum];
}

void TankObject::Spawn()
{
	model.pos = myworld->map.GetStartingLocation();		// Random starting loc
	model.vel = vec3f(0,0,0);
	model.dir = frand() * 360.0;
	lastWeaponFire = 0;
	armor = tankArmorStrength[0];
	state = TS_CREATING;
	stateStartTime = myworld->currentTime;
	hoverStatus = HOVER_OFF;
	active = true;
}


/*
char* ParticleEngine::ParticleImageFileNames[NUM_PARTICLE_TYPES] = {
	"part_smoke.png",
};
*/

ParticleEngine::ParticleEngine()
{
	int i;
	for (i=0; i < MAX_GLOBAL_PARTICLES; i++)
		particle[i].energy = 0;
}

gs_image_type ParticleEngine::particleTexImage[NUM_PARTICLE_TYPES];
tex_object_2D ParticleEngine::particleTexOb[NUM_PARTICLE_TYPES];
char* ParticleEngine::ParticleImageFileNames[NUM_PARTICLE_TYPES] = {
	"../Data/part_generic.png",
};

bool ParticleEngine::LoadData()
{
	bool success = true;
	int i;
	for (i=0; i < NUM_PARTICLE_TYPES; i++)
	{
		printf("ParticleEngine> Reading texture %s: ",ParticleImageFileNames[i]);
		success &= read_png_grey( ParticleImageFileNames[i], particleTexImage[i] );
		if (!success)
			printf("<ERROR> Could not load file!\n");
		else
			printf("Loaded %dx%d image\n",
				particleTexImage[i].get_width(),
				particleTexImage[i].get_height());
	}
	return success;
}

void ParticleEngine::ReleaseModels()
{
	int i;
	for (i=0; i < NUM_PARTICLE_TYPES; i++)
		particleTexOb[i].del();
}

bool ParticleEngine::RegisterModels()
{
	int i;
	for (i=0; i < NUM_PARTICLE_TYPES; i++)
	{
		particleTexOb[i].bind();
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
			glTexImage2D(GL_TEXTURE_2D,				// 2D texture mode
				0,												// mipmap level
				GL_ALPHA,									// internal format
				particleTexImage[i].get_width(),		// image width
				particleTexImage[i].get_height(),	// image height
				0,												// image border (0/1)
				GL_ALPHA,									// image pixel format
				GL_UNSIGNED_BYTE,							// image pixel size/type
				particleTexImage[i].get_pointer());	// ptr to image data
		particleTexOb[i].unbind();
	}
	return true;
}
void ParticleEngine::Draw()
{
	//vec3f camdir = game.world.eyepoint.pos - game.world.tank[0].model.pos;
	//vec3f& camdir = game.world.eyeDirection;
	vec3f& camdir = myworld->eyeDirection;
	vec3f partu  = camdir.cross(UP);
	vec3f partv  = camdir.cross(partu);
	partu.normalize();
	partv.normalize();

	float size = 30;
	vec3f center;

	vec3f uoff;
	vec3f voff;

	int i,j;

	glEnable(GL_BLEND);
	//glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
	glBlendFunc(GL_SRC_ALPHA,GL_ONE);

	glDepthMask(GL_FALSE);

	GLfloat difmat[] = { 1,1,1,1 };
	glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, difmat);

	tex_object_2D::enable();

	Particle_type boundTex = particle[0].type;

	particleTexOb[boundTex].bind();

	int totalActiveParticles = 0;
	int totalActiveEmitters = 0;
	for (i=0; i < MAX_GLOBAL_PARTICLES; i++)
	{
		Particle& p = particle[i];
		if (p.energy > 0)
		{
			if (boundTex != p.type)
				particleTexOb[boundTex = p.type].bind();

			p.Draw(partu,partv);

			totalActiveParticles++;
		}
	}

	for (i=0; i < MAX_PARTICLE_EMITTERS; i++)
	{
		ParticleEmitter* ppe = &emitter[i];
		if (ppe)
		{
			ParticleEmitter& pe = *ppe;
			if (pe.active)
			{
				totalActiveEmitters++;
				if (pe.type == PARTICLE_EMITTER_FLARE_TRAIL)
				{
					if (boundTex != pe.sourceParticle.type)
						particleTexOb[boundTex = pe.sourceParticle.type].bind();
					pe.sourceParticle.Draw(partu,partv);
				}

				for (j=0; j < MAX_EMITTER_PARTICLES; j++)
				{
					Particle& p = pe.particle[j];
					if (p.energy > 0)
					{
						if (boundTex != p.type)
							particleTexOb[boundTex = p.type].bind();
						p.Draw(partu,partv);

						totalActiveParticles++;
					}
				}
			}
		}
	}

	//printf("\r Total active particles: %d  emitters: %d  ",totalActiveParticles,totalActiveEmitters);

	particleTexOb[boundTex].unbind();

	tex_object_2D::disable();

	glDepthMask(GL_TRUE);

	glDisable(GL_BLEND);
}

void ParticleEngine::update(timet dtime)
{
	int i;

	float k_drag = 0.9;
	vec3f grav( 0.0, 0.0, -0.1 );
	vec3f wind( 0.0, 0.0, 0.0  );

	for (i=0; i < MAX_GLOBAL_PARTICLES; i++)
		if (particle[i].energy > 0)
			particle[i].update(dtime,k_drag,grav,wind);

	//bool foundDustEmitter = false;
	ParticleEmitter* de=NULL;
	int deIdx;

	for (i=0; i < MAX_PARTICLE_EMITTERS; i++)
	{
		if (emitter[i].active)
		{
			emitter[i].update(dtime);
			if (emitter[i].type == PARTICLE_EMITTER_DUST_KICKUP)
			{
				de = &emitter[i];
				deIdx = i;
				//foundDustEmitter = true;
			}
		}
	}

	/*
	if (!inServer)
	{
		if (de)
			printf(" Found emitter (%d): LS: %.1f nEP: %d er: %.3f q: %.3f \r",deIdx,(float)de->lifetime,(int)de->numEmittedParticles,(float)de->particleEmissionRate,(float)de->particlesToEmit);
		else
			printf(" No dust emitter \r");
	}
	*/
}

bool ParticleEngine::launch(Particle p)
{
	int i;
	for (i=0; i < MAX_GLOBAL_PARTICLES; i++)
		if (particle[i].energy <= 0)
		{
			particle[i] = p;
			return true;
		}
	return false;
}

bool ParticleEngine::launch(ParticleEmitter& pe)
{
	int i;
	for (i=0; i < MAX_PARTICLE_EMITTERS; i++)
		if (!emitter[i].active)
		{
			emitter[i] = pe;
			emitter[i].active = true;
			return true;
		}
	return false;
}

/*
void Particle::Particle(Particle_type newType)
{
	type = newType;
	switch(type)
	{
		default:
		case PARTICLE_GENERIC:
			{
				col = vec4f( 
	}
}
*/

inline vec3f dirvec( float dir, float radius, float height = 0 )
{
	float z = height;
	radius = sqrt( radius*radius - height*height );
	float x = radius * cos( dir * M_PI / 180.0 );
	float y = radius * sin( dir * M_PI / 180.0 );
	return vec3f( x,y,z );
}

// Launches a new particle based on the emitter's type
void ParticleEmitter::launch(int particleIndex)
{
	Particle p;
	switch (type)
	{
		default:
		case PARTICLE_EMITTER_GENERIC:
		{
			p.pos = model.pos;

			p.energy = 100;
			p.dEnergy = 50;

			p.col = vec3f( frand(), frand(), frand() );
			p.vel = vec3f( frand(), frand(), frand() );
			p.type = PARTICLE_GENERIC;

			break;
		}
		case PARTICLE_EMITTER_FLARE_CLOUD:
		{
			p.pos = sourceParticle.pos;
			p.vel = sourceParticle.vel * 0.1 + vec3f( frand()-0.5, frand()-0.5, frand() )*10;
			p.col = vec3f(0.7+frand()*.3,0.7*frand(),0.0);
			p.alpha = 0.5;
			p.energy = 80;
			p.dEnergy = sourceParticle.dEnergy*5;
			p.size = sourceParticle.size;
			p.dSize = 20;
			p.type = PARTICLE_GENERIC;
			break;
		}
		case PARTICLE_EMITTER_FLARE_TRAIL:
		{
			p.pos = sourceParticle.pos;
			p.vel = sourceParticle.vel * 0.1 + vec3f( frand()-0.5, frand()-0.5, frand()-0.5 )*5;
			p.col = vec3f(0.8,0.5,0.3);
			p.alpha = 0.5;
			p.energy = 80;
			p.dEnergy = sourceParticle.dEnergy*5;
			p.size = sourceParticle.size * 0.3;
			p.dSize = 20;
			p.type = PARTICLE_GENERIC;
			break;
		}
		case PARTICLE_EMITTER_RAINBOW_VORTEX:
		{
			float angle = frand()*360.0;
			vec3f offset = dirvec( angle, sourceParticle.size, frand()*sourceParticle.size );

			//float normangle = abs(angle-master->dir)/180.0;
			p.vel = -offset * 0.3;
			p.pos = sourceParticle.pos + offset;
			p.col = vec3f( frand()*0.7+0.3, frand()*0.7+0.3, frand()*0.7+0.3 );
			p.energy = 1;
			p.dEnergy = -30;
			p.size = 2;
			p.type = PARTICLE_GENERIC;
			p.dSize = 5;
			p.alpha = sourceParticle.alpha;
			break;
		}
		case PARTICLE_EMITTER_DUST_KICKUP:
		{
			p = sourceParticle;
			float angle = frand()*360.0;
			vec3f offset = dirvec( angle, 8.0 );
			float normangle = abs(angle-master->dir)/180.0;
			p.vel = sourceParticle.vel*0.5 + UP*20 + offset * (1.0+normangle);
			p.pos += offset + dirvec( master->dir , 5 );
			p.dEnergy = 0.3 * p.energy *frand() + 0.7*p.energy;
			p.size = 3;
			p.dSize = 10;
			p.alpha = 1.0;
			break;
		}
		case PARTICLE_EMITTER_FIRE:
		{
			//*
			float angle = frand()*360.0;
			float distf = frand();
			float dist = distf*sourceParticle.size;
			//vec3f offset = dirvec( angle, dist, (sourceParticle.size-dist)/2 );
			p.pos = sourceParticle.pos;//+offset;
			//angle = frand()*360.0;
			vec3f offset = dirvec( angle, dist*5 );
			p.vel = sourceParticle.vel + offset;
			p.col = vec3f( 1.0, frand(), 0 );
			p.energy = 130;
			//p.dEnergy = 10;
			p.dEnergy = dist*3+25;
			p.size = sourceParticle.size*frand()*0.15;
			p.dSize = (frand()+5)*5;
			p.alpha = distf*0.5+0.5;
			p.type = PARTICLE_GENERIC;
			//*/
			/*
			p.energy = 0;
			ParticleEmitter& pe = game.world.particleEngine.NewEmitter();

				float randangle  = frand()*360.0;
				float randdist = frand()*sourceParticle.size;
				vec3f randradial = dirvec( randangle, randdist );
				pe.lifetime = 1;
				pe.grav = -randradial;
				pe.grav[Z] = 100;
				pe.k_drag = 0.3;
				pe.wind = vec3f( 0,0,0 );
				pe.particleEmissionRate = 20;
				pe.sourceParticle.pos = sourceParticle.pos;
				pe.sourceParticle.vel = randradial;
				pe.sourceParticle.size = sourceParticle.size;
				pe.sourceParticle.dSize = sourceParticle.dSize;
				pe.sourceParticle.energy = 100;
				pe.sourceParticle.dEnergy = 30;
				//p.sourceParticle.col = vec3f( frand()*0.5+0.5, frand()*0.5+0.5, frand() * 0.5 );
				pe.sourceParticle.col[1] = frand()*0.8+0.3;
				pe.sourceParticle.col[2] = max( frand()*0.5+0.5, pe.sourceParticle.col[1] );
				pe.sourceParticle.col[3] = 0;
			*/	
			break;
		}
	}
	launch(p,particleIndex);
}
void ParticleEmitter::launch(Particle& p, int particleIndex)
{
	for (int i=0; i < MAX_EMITTER_PARTICLES && particleIndex < 0; i++)
		if (particle[i].energy <= 0)
			particleIndex = i;

	particle[particleIndex] = p;
	numEmittedParticles++;

	return;
}

void ParticleEmitter::update(timet dtime)
{
	int i;

	if (master)
	{
		sourceParticle.pos = master->pos;
		sourceParticle.vel = master->vel;
	}
	// If we've lost our master for the vortex, let current particles die off,
	// then deactivate.
	else if (type == PARTICLE_EMITTER_RAINBOW_VORTEX)
	{
		sourceParticle.vel = vec3f(0,0,0);
		grav = sourceParticle.vel;
		wind = UP * 20;
		totalParticleCount = numEmittedParticles;
		lifetime = 0;
	}

	if (type == PARTICLE_EMITTER_FIRE)
		sourceParticle.update(dtime,0,vec3f(0,0,0),vec3f(0,0,0));
	else
		sourceParticle.update(dtime,0,grav,wind);


	if (type == PARTICLE_EMITTER_DUST_KICKUP)
	{
		if (master)
			particleEmissionRate = master->vel.square_norm() / 1000.0 + 5.0;
		else
		{
			lifetime = 0;
			particleEmissionRate = 0;
			totalParticleCount = numEmittedParticles;
		}
	}

	if (totalParticleCount > 0)
		particlesToEmit += min( particleEmissionRate * dtime , totalParticleCount-numEmittedParticles );
	else
		particlesToEmit += particleEmissionRate * dtime;

	bool foundActiveParticle = false;
	for (i=0; i < MAX_EMITTER_PARTICLES; i++)
	{
		if (particle[i].energy > 0)
		{
			if (type == PARTICLE_EMITTER_RAINBOW_VORTEX)
			{
				grav = sourceParticle.pos - particle[i].pos;
				grav[Z] += 10;
				if (particle[i].energy > 90)
					particle[i].dEnergy = abs(particle[i].dEnergy*2);
			}
			else if (type == PARTICLE_EMITTER_FIRE)
			{
				grav = sourceParticle.pos - particle[i].pos;
				float life = particle[i].energy/100.0;
				//particle[i].alpha = life;
				grav[Z] = 80;
				vec3f tgtCol(0.4, 0.4, 0.4);
				particle[i].col += (tgtCol-particle[i].col)*dtime*(1.0-life);
			}
			particle[i].update(dtime,k_drag,grav,wind);
			foundActiveParticle = true;
		}
		else if (particlesToEmit > 1.0)
		{
			launch(i);
			particlesToEmit--;
			foundActiveParticle = true;
		}
	}

	// Check to see if emitter dies because of running out of particles:
	if (totalParticleCount > 0 && numEmittedParticles >= totalParticleCount && !foundActiveParticle)
		active = false;

	// Check to see if emitter dies because of old age:
	if (lifetime != LIFETIME_ETERNAL_EMITTER)
	{
		lifetime -= dtime;
		if (lifetime <= 0)
		{
			if (type == PARTICLE_EMITTER_RAINBOW_VORTEX)
			{
				for (int i=0; i < MAX_EMITTER_PARTICLES; i++)
					if (particle[i].dEnergy < 0)
						particle[i].dEnergy = -particle[i].dEnergy;
				particleEmissionRate = 0;
				particlesToEmit = 0;
			}
			lifetime = LIFETIME_ETERNAL_EMITTER;
			totalParticleCount = numEmittedParticles;
		}
	}
}

void ParticleEmitter::InitEmitter( ParticleEmitter_type inType )
{
	type = inType;
	active = true;
	lifetime = DefaultEmitterLifetime[type];
	totalParticleCount = 0;
	numEmittedParticles = 0;
	particlesToEmit = 0;
	switch( type )
	{
		default:
		case PARTICLE_EMITTER_GENERIC:
			break;
		case PARTICLE_EMITTER_RAINBOW_VORTEX:
			sourceParticle.type = PARTICLE_GENERIC;
			sourceParticle.energy = 100;
			sourceParticle.dEnergy = 10.0;
			sourceParticle.alpha = 0.7;
			break;
		case PARTICLE_EMITTER_FLARE_TRAIL:
		case PARTICLE_EMITTER_FLARE_CLOUD:
			sourceParticle.type = PARTICLE_GENERIC;
			sourceParticle.col  = vec3f(1.0,1.0,0.0);
			sourceParticle.energy = 100;
			sourceParticle.dEnergy = 10.0;
			sourceParticle.alpha = 0.5;
			break;
		case PARTICLE_EMITTER_DUST_KICKUP:
			sourceParticle.col = vec3f( 0.4,0.4,0.4 );
			sourceParticle.type = PARTICLE_GENERIC;
			sourceParticle.energy = 50.0;
			sourceParticle.dEnergy = 0.0;		// For infinite lifetime
			break;
		case PARTICLE_EMITTER_FIRE:
			sourceParticle.col = vec3f( 1.0, 0.0, 0.0 );
			sourceParticle.type = PARTICLE_GENERIC;
			sourceParticle.energy = 100.0;
			sourceParticle.dEnergy =  5.0;		 // For infinite lifetime
			sourceParticle.alpha = 1.0;
			break;
	}
	for (int i=0; i < MAX_EMITTER_PARTICLES; i++)
		particle[i].energy = 0;
}

timet ParticleEmitter::DefaultEmitterLifetime[NUM_PARTICLE_EMITTER_TYPES] = {
	5.0,									// 5 sec for generic emitter
	LIFETIME_ETERNAL_EMITTER,		// dust kickup emitters are like diamonds
	5.0,									// 5 sec rainbow vortex
	10.0,									// 10 sec flare/smoke particles
	3.0,									// 3 sec flare clouds
	15.0,									// 25 sec fires
};

ParticleEmitter::ParticleEmitter(ParticleEmitter_type inType, vec3f pos, vec3f vel, float inLifetime)
{
	type = inType;
	model.pos = pos;
	model.vel = vel;
	lifetime = inLifetime;
	master = NULL;

	switch(type)
	{
		default:
		case PARTICLE_EMITTER_GENERIC:
			if (lifetime == LIFETIME_DEFAULT_EMITTER)
				lifetime = DefaultEmitterLifetime[type];
			model.acc = vec3f(0,0,0);
			break;
	}
}

ParticleEmitter* ParticleEngine::NewEmitter()
{
	int i;
	float minLifeTime = emitter[0].lifetime;
	int   minLifeTimeIdx = 0;

	for (i=0; i < MAX_PARTICLE_EMITTERS; i++)
		if (!emitter[i].active)
			return &emitter[i];
		else if (emitter[i].lifetime < minLifeTime)
		{
			minLifeTime = emitter[i].lifetime;
			minLifeTimeIdx = i;
		}
	return &emitter[minLifeTimeIdx];
}

void ParticleEmitter::attach( MobileModel* target )
{
	master = target;
}

void ParticleEngine::unattach( MobileModel* target )
{
	int i;
	for (i=0; i < MAX_PARTICLE_EMITTERS; i++)
		if (emitter[i].master == target)
		{
			emitter[i].master = NULL;
			/*
			// Only kill it if it has an unlimited life time
			if (emitter[i].lifetime == LIFETIME_ETERNAL_EMITTER)
				emitter[i].active = false;
			*/
		}
}

void TankObject::SetState(enum TankState_type newState)
{
	state = newState;
	if (state != TS_NORMAL)
		stateStartTime = myworld->currentTime;

	if (state == TS_EXPLODING)
	{
		printf("\nBoom!\n");
		deaths++;
	}

	//if (myworld != &game.netserver.m_world)
	if (!myworld->ThisWorldIsTheServer)
	{
		myworld->particleEngine.unattach(&model);

		switch(state)
		{
			case TS_CREATING:
			{
				modelnum = game.netclient.GetNextModelNum(this);
				ParticleEmitter* pp = myworld->particleEngine.NewEmitter();
				if (pp)
				{
					pp->InitEmitter(PARTICLE_EMITTER_RAINBOW_VORTEX);
					pp->attach(&model);
					pp->lifetime = TankStateDuration[state];
					pp->particleEmissionRate = 30.0;
					pp->sourceParticle.size = 120.0;
				}
				break;
			}
			case TS_EXPLODING:
			{
				//printf("\nBoom!\n");
				hoverStatus = HOVER_OFF;
				StopSound(HTEngineHoverSound);
				PlaySound(HTExplode);
				for (int i=0; i < 20; i++)
				{
					ParticleEmitter* pp = myworld->particleEngine.NewEmitter();
					if (pp)
					{
						ParticleEmitter& p = *pp;
						p.InitEmitter(PARTICLE_EMITTER_FLARE_TRAIL);
						//p.lifetime = TankStateDuration[state]*10;
						p.lifetime = 3;
						p.grav = vec3f( 0.0, 0.0, -80 );
						p.k_drag = 0.3;
						p.wind = vec3f( 0,0,0 );
						p.particleEmissionRate = 20;
						p.sourceParticle.pos = model.pos;
						vec3f randdir = vec3f( frand()-0.5, frand()-0.5, frand()+0.5 ) * 200;
						p.sourceParticle.vel = model.vel*1.5 + randdir;
						p.sourceParticle.size = 20;
						p.sourceParticle.dSize = -2;
						p.sourceParticle.energy = 100;
						p.sourceParticle.dEnergy = 20;
						//p.sourceParticle.col = vec3f( frand()*0.5+0.5, frand()*0.5+0.5, frand() * 0.5 );
						p.sourceParticle.col[1] = frand()*0.8+0.3;
						p.sourceParticle.col[2] = max( frand()*0.5+0.5, p.sourceParticle.col[1] );
						p.sourceParticle.col[3] = frand()*0.5;
					}
				}
				ParticleEmitter* pp = myworld->particleEngine.NewEmitter();
				if (pp)
				{
						ParticleEmitter& p = *pp;
					p.InitEmitter(PARTICLE_EMITTER_FLARE_CLOUD);
					//p.lifetime = TankStateDuration[state]*10;
					p.lifetime = 3;
					//p.attach(&model);
					p.grav = vec3f( 0, 0, 1 );
					p.k_drag = 0.5;
					p.wind = vec3f( 0,0,0 );
					p.particleEmissionRate = 10;
					p.sourceParticle.pos = model.pos;
					vec3f randdir = vec3f( frand()-0.5, frand()-0.5, frand()+0.5 ) * 100;
					//p.sourceParticle.vel = model.vel*2 + randdir;
					p.sourceParticle.vel = model.vel*0.7;
					p.sourceParticle.col = vec3f(1.0,0.7,0.0);
					p.sourceParticle.size = 10;
					p.sourceParticle.dSize = 5;
					p.sourceParticle.energy = 100;
					p.sourceParticle.dEnergy = 20;
				}

				for (i=0; i < myworld->map.treeInfo.size(); i++)
				{
					if (!myworld->map.treeInfo[i].onFireAndGonnaDieAHorribleFlamingDeath)
					{
						vec2f curpos( model.pos[X], model.pos[Y] );
						float dist = (curpos - myworld->map.treeInfo[i].pos).square_norm();
						const tree_death_dist = 10000;
						if (dist < tree_death_dist)
							myworld->StartTreeFire(i);
					}
				}

				break;
			}
			case TS_NORMAL:
			default:
			{
				ParticleEmitter* pp = myworld->particleEngine.NewEmitter();
				if (pp)
				{
					ParticleEmitter& p = *pp;
					p.InitEmitter( PARTICLE_EMITTER_DUST_KICKUP ); 
					p.attach( &model );
					p.grav = vec3f( 0.0, 0.0, 0.3 );
					p.wind = vec3f( 5.0, 0.0, 0.5 );
					p.k_drag = 0.3;
					p.particleEmissionRate = 50.0;
					p.particlesToEmit = 3;
				}
				break;
			}
		}
	}

}

void TankObject::SetTankModel(int in_modelnum)
{
	modelnum = in_modelnum;
	model.dlist = &tankModel[modelnum];
}
