// render
void PlayerOverlaySpecial::Render(unsigned int aId, float aTime, const Transform2 &aTransform)
{
	// get the player
	Player *player = Database::player.Get(aId);

	// get the player entity (HACK)
	unsigned int id = player->GetId();

	// get "special" ammo resource (HACK)
	Resource *specialresource = Database::resource.Get(id).Get(0xd940d530 /* "special" */);
	if (!specialresource)
		return;
	int new_special = xs_FloorToInt(specialresource->GetValue());

	// if the special has not changed...
	if (new_special == cur_special && !wasreset)
	{
		// call the existing draw list
		glCallList(special_handle);
		return;
	}

	// update special
	cur_special = new_special;

	// start a new draw list list
	glNewList(special_handle, GL_COMPILE_AND_EXECUTE);

	// draw the special ammo icon
	glColor4f(0.4f, 0.5f, 1.0f, 1.0f);
	glPushMatrix();
	glTranslatef(specialpos.x, specialpos.y, 0.0f);
	glScalef(4, 4, 1);
	glCallList(Database::drawlist.Get(0x8cdedbba /* "circle16" */));
	glPopMatrix();

	// draw remaining special ammo
	char special[16];
	sprintf(special, "x%d", cur_special);

	glEnable(GL_TEXTURE_2D);
	glBindTexture(GL_TEXTURE_2D, OGLCONSOLE_glFontHandle);

	glColor4f(0.4f, 0.5f, 1.0f, 1.0f);

	glBegin(GL_QUADS);
	float w = 8;
	float h = -8;
	float x = specialpos.x + 8;
	float y = specialpos.y - 0.5f * h;
	float z = 0;
	OGLCONSOLE_DrawString(special, x, y, w, h, z);

	glEnd();

	glDisable(GL_TEXTURE_2D);

	glEndList();
}
// Weapon Update
void Weapon::Update(float aStep)
{
	// get controller
	const Controller *controller = Database::controller.Get(mControlId);
	if (!controller)
		return;

	// advance fire timer
	mTimer += aStep;

	// get template data
	const WeaponTemplate &weapon = Database::weapontemplate.Get(mId);

	// get trigger value
	float fire = controller->mFire[mChannel];
	bool trigger;
	switch (weapon.mTrigger)
	{
	default:
	case WeaponTemplate::TRIGGER_HOLD: trigger = fire != 0; break;
	case WeaponTemplate::TRIGGER_PRESS: trigger = fire != 0 && mPrevFire == 0; break;
	case WeaponTemplate::TRIGGER_RELEASE: trigger = fire == 0 && mPrevFire != 0; break;
	}
	mPrevFire = fire;

	// if triggered...
	if (trigger)
	{
		// if not busy
		if (mBurst <= 0 && mTimer > -0.001f && (!weapon.mTrack || mTrack < weapon.mTrack))
		{
			// if firing on this phase...
			if (mPhase == 0)
			{
				Resource *resource = NULL;

				// if using ammo
				if (weapon.mCost)
				{
					// ammo resource (if any)
					resource = Database::resource.Get(mAmmo).Get(weapon.mType);
				}

				// if enough ammo...
				if (!resource || weapon.mCost <= resource->GetValue())
				{
					// deduct ammo
					if (resource)
						resource->Add(mId, -weapon.mCost);

					// start a new burst
					mTimer -= weapon.mBurstStart;
					mBurst = weapon.mBurstLength;
				}
				else
				{
					// start "empty" sound cue
					PlaySoundCue(mId, 0x18a7beee /* "empty" */);
				}

				// wrap around
				mPhase = weapon.mCycle - 1;
			}
			else
			{
				// advance phase
				--mPhase;

				// wait for next phase
				mTimer -= weapon.mDelay / weapon.mCycle;
			}
		}
	}

	// if ready to fire...
	while (mBurst > 0 && mTimer > -0.001f && (!weapon.mTrack || mTrack < weapon.mTrack))
	{
		// deduct a burst
		--mBurst;

		// get the entity
		Entity *entity = Database::entity.Get(mId);

		// start the "fire" sound cue
		PlaySoundCue(mId, 0x8eab16d9 /* "fire" */);

		// interpolated transform
		Transform2 basetransform(entity->GetInterpolatedTransform(mTimer / aStep));

		for (int salvo = 0; salvo < weapon.mSalvoShots; ++salvo)
		{
			// get local position
			Transform2 position(weapon.mOffset);

			// apply transform offset
			Transform2 transform(position * basetransform);

			if (weapon.mRecoil)
			{
				// apply recoil force
				for (unsigned int id = mId; id != 0; id = Database::backlink.Get(id))
				{
					if (Collidable *collidable = Database::collidable.Get(id))
					{
						collidable->GetBody()->ApplyImpulse(transform.Rotate(Vector2(0, -weapon.mRecoil)), transform.p);
						break;
					}
				}
			}

			if (weapon.mFlash)
			{
				// instantiate a flash
				unsigned int flashId = Database::Instantiate(weapon.mFlash, Database::owner.Get(mId), mId,
					transform.Angle(), transform.p, entity->GetVelocity(), entity->GetOmega());

				// set fractional turn
				if (Renderable *renderable = Database::renderable.Get(flashId))
					renderable->SetFraction(mTimer / aStep);

				// link it (HACK)
				LinkTemplate linktemplate;
				linktemplate.mOffset = weapon.mOffset;
				linktemplate.mSub = flashId;
				linktemplate.mSecondary = flashId;
				Link *link = new Link(linktemplate, mId);
				Database::Typed<Link *> &links = Database::link.Open(mId);
				links.Put(flashId, link);
				Database::link.Close(mId);
				link->Activate();
			}

			if (weapon.mOrdnance)
			{
				// TO DO: consolidate this with similar spawn patterns (Graze, Spawner)

				// apply position scatter
				position.a += Random::Value(0.0f, weapon.mScatter.a);
				position.p.x += Random::Value(0.0f, weapon.mScatter.p.x);
				position.p.y += Random::Value(0.0f, weapon.mScatter.p.y);

				// get world position
				position *= basetransform;

				// get local velocity
				Transform2 velocity(entity->GetOmega(), position.Unrotate(entity->GetVelocity()));

				// apply velocity inherit
				velocity.a *= weapon.mInherit.a;
				velocity.p.x *= weapon.mInherit.p.x;
				velocity.p.y *= weapon.mInherit.p.y;

				// apply velocity add
				velocity.a += weapon.mVelocity.a;
				velocity.p.x += weapon.mVelocity.p.x;
				velocity.p.y += weapon.mVelocity.p.y;

				// apply velocity variance
				velocity.a += Random::Value(0.0f, weapon.mVariance.a);
				velocity.p.x += Random::Value(0.0f, weapon.mVariance.p.x);
				velocity.p.y += Random::Value(0.0f, weapon.mVariance.p.y);

				// apply velocity aim
				velocity.p.x += controller->mAim.x * weapon.mAim.x;
				velocity.p.y += controller->mAim.y * weapon.mAim.y;

				// get world velocity
				velocity.p = position.Rotate(velocity.p);

				// instantiate a bullet
				unsigned int ordId = Database::Instantiate(weapon.mOrdnance, Database::owner.Get(mId), mId, position.a, position.p, velocity.p, velocity.a);
#ifdef DEBUG_WEAPON_CREATE_ORDNANCE
				DebugPrint("ordnance=\"%s\" owner=\"%s\"\n",
					Database::name.Get(ordId).c_str(),
					Database::name.Get(Database::owner.Get(ordId)).c_str());
#endif

				// set fractional turn
				if (Renderable *renderable = Database::renderable.Get(ordId))
					renderable->SetFraction(mTimer / aStep);

				// if tracking....
				if (weapon.mTrack)
				{
					// add a tracker
					Database::weapontracker.Put(ordId, WeaponTracker(mId));
				}
			}
		}

		// update weapon delay
		if (mBurst > 0)
			mTimer -= weapon.mBurstDelay;
		else
			mTimer -= (weapon.mDelay - weapon.mBurstStart - weapon.mBurstDelay * (weapon.mBurstLength - 1)) / weapon.mCycle;
	}

	if (mTimer > 0.0f)
	{
		// clamp fire delay
		mTimer = 0.0f;
	}
}
// render
void PlayerOverlayLevel::Render(unsigned int aId, float aTime, const Transform2 &aTransform)
{
	// get the player
	Player *player = Database::player.Get(aId);

	// get the attached entity identifier
	unsigned int id = player->mAttach;

	// get level resource
	Resource *levelresource = Database::resource.Get(id).Get(0x9b99e7dd /* "level" */);
	if (!levelresource)
		return;
	int new_level = xs_FloorToInt(levelresource->GetValue());
	float new_part = levelresource->GetValue() - new_level;

	// if the level has not changed...
	if (new_part == cur_part && new_level == cur_level && !wasreset)
	{
		// call the existing draw list
		glCallList(level_handle);
		return;
	}

	// update level
	cur_level = new_level;
	cur_part = new_part;

	// start a new draw list list
	glNewList(level_handle, GL_COMPILE_AND_EXECUTE);

	// draw level gauge
	glBegin(GL_QUADS);

	// background
	glColor4fv(levelcolor[cur_level]);
	glVertex2f(levelrect.x, levelrect.y);
	glVertex2f(levelrect.x + levelrect.w, levelrect.y);
	glVertex2f(levelrect.x + levelrect.w, levelrect.y + levelrect.h);
	glVertex2f(levelrect.x, levelrect.y + levelrect.h);

	// fill gauge
	glColor4fv(levelcolor[cur_level+1]);
	glVertex2f(levelrect.x, levelrect.y);
	glVertex2f(levelrect.x + levelrect.w * cur_part, levelrect.y);
	glVertex2f(levelrect.x + levelrect.w * cur_part, levelrect.y + levelrect.h);
	glVertex2f(levelrect.x, levelrect.y + levelrect.h);

	glEnd();

	// draw the level icon
	glColor4f(0.4f, 0.5f, 1.0f, 1.0f);
	glPushMatrix();
	glTranslatef(levelpos.x, levelpos.y, 0.0f);
	glScalef(4, 4, 1);
	glCallList(Database::drawlist.Get(0x8cdedbba /* "circle16" */));
	glPopMatrix();

	// draw level number
	char level[16];
	sprintf(level, "x%d", cur_level);

	glEnable(GL_TEXTURE_2D);
	glBindTexture(GL_TEXTURE_2D, OGLCONSOLE_glFontHandle);

	glColor4f(0.4f, 0.5f, 1.0f, 1.0f);

	glBegin(GL_QUADS);
	float w = 8;
	float h = -8;
	float x = levelpos.x + 8;
	float y = levelpos.y - 0.5f * h;
	float z = 0;
	OGLCONSOLE_DrawString(level, x, y, w, h, z);

	glEnd();

	glDisable(GL_TEXTURE_2D);

	glEndList();
}
// render
void PlayerOverlayAmmo::Render(unsigned int aId, float aTime, const Transform2 &aTransform)
{
	// get the player
	Player *player = Database::player.Get(aId);

	// get the attached entity identifier
	unsigned int id = player->mAttach;

	// draw player ammo (HACK)
	Resource *ammoresource = Database::resource.Get(id).Get(0x5b9b0daf /* "ammo" */);
	if (!ammoresource)
		return;

	// get ammo ratio
	float new_ammo = 0.0f;
	const ResourceTemplate &ammoresourcetemplate = Database::resourcetemplate.Get(id).Get(0x5b9b0daf /* "ammo" */);
	if (ammoresourcetemplate.mMaximum > 0)
	{
		new_ammo = ammoresource->GetValue() / ammoresourcetemplate.mMaximum;
	}
	
	// get level
	int new_level = 1;
	Resource *levelresource = Database::resource.Get(id).Get(0x9b99e7dd /* "level" */);
	if (levelresource)
	{
		new_level = xs_FloorToInt(levelresource->GetValue());
	}

	// if the lives count has not changed...
	if (new_ammo == cur_ammo && new_level == cur_level && glIsList(ammo_handle))
	{
		// call the existing draw list
		glCallList(ammo_handle);
		return;
	}

	// update ammo
	cur_ammo = new_ammo;
	cur_level = new_level;

	// start a new draw list list
	glNewList(ammo_handle, GL_COMPILE_AND_EXECUTE);

	glBegin(GL_QUADS);

	// background
	glColor4fv(ammocolor[cur_level]);
	glVertex2f(ammorect.x, ammorect.y);
	glVertex2f(ammorect.x + ammorect.w, ammorect.y);
	glVertex2f(ammorect.x + ammorect.w, ammorect.y + ammorect.h);
	glVertex2f(ammorect.x, ammorect.y + ammorect.h);

	// fill gauge
	glColor4fv(ammocolor[cur_level+1]);
	glVertex2f(ammorect.x, ammorect.y);
	glVertex2f(ammorect.x + ammorect.w * cur_ammo, ammorect.y);
	glVertex2f(ammorect.x + ammorect.w * cur_ammo, ammorect.y + ammorect.h);
	glVertex2f(ammorect.x, ammorect.y + ammorect.h);

	glEnd();

	glEndList();
}