Esempio n. 1
0
void CCommandAI::ExecuteAttack(Command& c)
{
	assert(owner->unitDef->canAttack);

	if (inCommand) {
		if (targetDied || (c.params.size() == 1 && UpdateTargetLostTimer(int(c.params[0])) == 0)) {
			FinishCommand();
			return;
		}
		if (!(c.options & ALT_KEY) && SkipParalyzeTarget(orderTarget)) {
			FinishCommand();
			return;
		}
	} else {
		if (c.params.size() == 1) {
			CUnit* targetUnit = unitHandler->GetUnit(c.params[0]);

			if (targetUnit == NULL) { FinishCommand(); return; }
			if (targetUnit == owner) { FinishCommand(); return; }
			if (targetUnit->GetTransporter() != NULL && !modInfo.targetableTransportedUnits) {
				FinishCommand(); return;
			}

			SetOrderTarget(targetUnit);
			owner->AttackUnit(targetUnit, (c.options & INTERNAL_ORDER) == 0, c.GetID() == CMD_MANUALFIRE);
			inCommand = true;
		} else {
			owner->AttackGround(c.GetPos(0), (c.options & INTERNAL_ORDER) == 0, c.GetID() == CMD_MANUALFIRE);
			inCommand = true;
		}
	}
}
Esempio n. 2
0
void CAirCAI::ExecuteAttack(Command& c)
{
	assert(owner->unitDef->canAttack);
	targetAge++;

	if (tempOrder && owner->moveState == MOVESTATE_MANEUVER) {
		// limit how far away we fly
		if (orderTarget && LinePointDist(commandPos1, commandPos2, orderTarget->pos) > 1500) {
			owner->AttackUnit(NULL, false, false);
			FinishCommand();
			return;
		}
	}

	if (inCommand) {
		if (targetDied || (c.params.size() == 1 && UpdateTargetLostTimer(int(c.params[0])) == 0)) {
			FinishCommand();
			return;
		}
		if (orderTarget != NULL) {
			if (orderTarget->unitDef->canfly && orderTarget->IsCrashing()) {
				owner->AttackUnit(NULL, false, false);
				FinishCommand();
				return;
			}
			if (!(c.options & ALT_KEY) && SkipParalyzeTarget(orderTarget)) {
				owner->AttackUnit(NULL, false, false);
				FinishCommand();
				return;
			}
		}
	} else {
		targetAge = 0;

		if (c.params.size() == 1) {
			CUnit* targetUnit = unitHandler->GetUnit(c.params[0]);

			if (targetUnit == NULL) { FinishCommand(); return; }
			if (targetUnit == owner) { FinishCommand(); return; }
			if (targetUnit->GetTransporter() != NULL && !modInfo.targetableTransportedUnits) {
				FinishCommand(); return;
			}

			SetGoal(targetUnit->pos, owner->pos, cancelDistance);
			SetOrderTarget(targetUnit);
			owner->AttackUnit(targetUnit, (c.options & INTERNAL_ORDER) == 0, false);

			inCommand = true;
		} else {
			SetGoal(c.GetPos(0), owner->pos, cancelDistance);
			owner->AttackGround(c.GetPos(0), (c.options & INTERNAL_ORDER) == 0, false);

			inCommand = true;
		}
	}
}
int LuaUnsyncedRead::GetUnitViewPosition(lua_State* L)
{
	CUnit* unit = ParseUnit(L, __FUNCTION__, 1);
	if (unit == NULL) {
		return 0;
	}
	const bool midPos = (lua_isboolean(L, 2) && lua_toboolean(L, 2));

	float3 pos = midPos ? (float3)unit->midPos : (float3)unit->pos;
	CTransportUnit *trans=unit->GetTransporter();
	if (trans == NULL) {
		pos += (unit->speed * gu->timeOffset);
	} else {
		pos += (trans->speed * gu->timeOffset);
	}

	lua_pushnumber(L, pos.x);
	lua_pushnumber(L, pos.y);
	lua_pushnumber(L, pos.z);
	return 3;
}
Esempio n. 4
0
void CCommandAI::ExecuteAttack(Command& c)
{
	assert(owner->unitDef->canAttack);

	if (inCommand) {
		if (targetDied || (c.params.size() == 1 && UpdateTargetLostTimer(int(c.params[0])) == 0)) {
			FinishCommand();
			return;
		}
		if ((c.params.size() == 3) && (owner->commandShotCount > 0) && (commandQue.size() > 1)) {
			FinishCommand();
			return;
		}
		if (!(c.options & ALT_KEY) && SkipParalyzeTarget(orderTarget)) {
			FinishCommand();
			return;
		}
	} else {
		owner->commandShotCount = -1;

		if (c.params.size() == 1) {
			CUnit* targetUnit = uh->GetUnit(c.params[0]);

			if (targetUnit == NULL) { FinishCommand(); return; }
			if (targetUnit == owner) { FinishCommand(); return; }
			if (targetUnit->GetTransporter() != NULL) { FinishCommand(); return; }

			SetOrderTarget(targetUnit);
			owner->AttackUnit(targetUnit, c.GetID() == CMD_MANUALFIRE);

			inCommand = true;
		} else {
			float3 pos(c.params[0], c.params[1], c.params[2]);
			owner->AttackGround(pos, c.GetID() == CMD_MANUALFIRE);
			inCommand = true;
		}
	}
}
Esempio n. 5
0
void CGameHelper::GenerateWeaponTargets(const CWeapon* weapon, const CUnit* lastTargetUnit, std::multimap<float, CUnit*>& targets)
{
	const CUnit* attacker = weapon->owner;
	const float radius    = weapon->range;
	const float3& pos     = attacker->pos;
	const float heightMod = weapon->heightMod;
	const float aHeight   = weapon->weaponPos.y;

	const WeaponDef* weaponDef = weapon->weaponDef;

	// how much damage the weapon deals over 1 second
	const float secDamage = weaponDef->damages.GetDefaultDamage() * weapon->salvoSize / weapon->reloadTime * GAME_SPEED;
	const bool paralyzer  = (weaponDef->damages.paralyzeDamageTime != 0);

	const std::vector<int>& quads = quadField->GetQuads(pos, radius + (aHeight - std::max(0.f, readmap->initMinHeight)) * heightMod);

	const int tempNum = targetTempNum++;

	typedef std::vector<int>::const_iterator VectorIt;
	typedef std::list<CUnit*>::const_iterator ListIt;

	for (VectorIt qi = quads.begin(); qi != quads.end(); ++qi) {
		for (int t = 0; t < teamHandler->ActiveAllyTeams(); ++t) {
			if (teamHandler->Ally(attacker->allyteam, t)) {
				continue;
			}

			const std::list<CUnit*>& allyTeamUnits = quadField->GetQuad(*qi).teamUnits[t];

			for (ListIt ui = allyTeamUnits.begin(); ui != allyTeamUnits.end(); ++ui) {
				CUnit* targetUnit = *ui;
				float targetPriority = 1.0f;

				if (!(targetUnit->category & weapon->onlyTargetCategory)) {
					continue;
				}
				if (targetUnit->GetTransporter() != NULL) {
					if (!modInfo.targetableTransportedUnits)
						continue;
					// the transportee might be "hidden" below terrain, in which case we can't target it
					if (targetUnit->pos.y < ground->GetHeightReal(targetUnit->pos.x, targetUnit->pos.z))
						continue;
				}
				if (tempTargetUnits[targetUnit->id] == tempNum) {
					continue;
				}

				tempTargetUnits[targetUnit->id] = tempNum;

				if (targetUnit->isUnderWater && !weaponDef->waterweapon) {
					continue;
				}
				if (targetUnit->isDead) {
					continue;
				}

				float3 targPos;
				const unsigned short targetLOSState = targetUnit->losStatus[attacker->allyteam];

				if (targetLOSState & LOS_INLOS) {
					targPos = targetUnit->aimPos;
				} else if (targetLOSState & LOS_INRADAR) {
					targPos = targetUnit->aimPos + (targetUnit->posErrorVector * radarhandler->radarErrorSize[attacker->allyteam]);
					targetPriority *= 10.0f;
				} else {
					continue;
				}

				const float modRange = radius + (aHeight - targPos.y) * heightMod;

				if ((pos - targPos).SqLength2D() > modRange * modRange) {
					continue;
				}

				const float dist2D = (pos - targPos).Length2D();
				const float rangeMul = (dist2D * weaponDef->proximityPriority + modRange * 0.4f + 100.0f);
				const float damageMul = weaponDef->damages[targetUnit->armorType] * targetUnit->curArmorMultiple;

				targetPriority *= rangeMul;

				if (targetLOSState & LOS_INLOS) {
					targetPriority *= (secDamage + targetUnit->health);

					if (targetUnit == lastTargetUnit) {
						targetPriority *= weapon->avoidTarget ? 10.0f : 0.4f;
					}

					if (paralyzer && targetUnit->paralyzeDamage > (modInfo.paralyzeOnMaxHealth? targetUnit->maxHealth: targetUnit->health)) {
						targetPriority *= 4.0f;
					}

					if (weapon->hasTargetWeight) {
						targetPriority *= weapon->TargetWeight(targetUnit);
					}
				} else {
					targetPriority *= (secDamage + 10000.0f);
				}

				if (targetLOSState & LOS_PREVLOS) {
					targetPriority /= (damageMul * targetUnit->power * (0.7f + gs->randFloat() * 0.6f));

					if (targetUnit->category & weapon->badTargetCategory) {
						targetPriority *= 100.0f;
					}
					if (targetUnit->IsCrashing()) {
						targetPriority *= 1000.0f;
					}
				}

				if (luaRules != NULL) {
					if (!luaRules->AllowWeaponTarget(attacker->id, targetUnit->id, weapon->weaponNum, weaponDef->id, &targetPriority)) {
						continue;
					}
				}

				targets.insert(std::pair<float, CUnit*>(targetPriority, targetUnit));
			}
		}
	}

#ifdef TRACE_SYNC
	{
		tracefile << "[GenerateWeaponTargets] attackerID, attackRadius: " << attacker->id << ", " << radius << " ";

		for (std::multimap<float, CUnit*>::const_iterator ti = targets.begin(); ti != targets.end(); ++ti)
			tracefile << "\tpriority: " << (ti->first) <<  ", targetID: " << (ti->second)->id <<  " ";

		tracefile << "\n";
	}
#endif
}
Esempio n. 6
0
void CMobileCAI::ExecuteAttack(Command &c)
{
	assert(owner->unitDef->canAttack);

	// limit how far away we fly based on our movestate
	if (tempOrder && orderTarget) {
		const float3& closestPos = ClosestPointOnLine(commandPos1, commandPos2, owner->pos);
		const float curTargetDist = LinePointDist(closestPos, commandPos2, orderTarget->pos);
		const float maxTargetDist = (500 * owner->moveState + owner->maxRange);

		if (owner->moveState < MOVESTATE_ROAM && curTargetDist > maxTargetDist) {
			StopMove();
			FinishCommand();
			return;
		}
	}

	// check if we are in direct command of attacker
	if (!inCommand) {
		if (c.params.size() == 1) {
			CUnit* targetUnit = unitHandler->GetUnit(c.params[0]);

			// check if we have valid target parameter and that we aren't attacking ourselves
			if (targetUnit == NULL) { StopMove(); FinishCommand(); return; }
			if (targetUnit == owner) { StopMove(); FinishCommand(); return; }
			if (targetUnit->GetTransporter() != NULL && !modInfo.targetableTransportedUnits) {
				StopMove(); FinishCommand(); return;
			}

			const float3 tgtErrPos = targetUnit->pos + owner->posErrorVector * 128;
			const float3 tgtPosDir = (tgtErrPos - owner->pos).Normalize();

			SetGoal(tgtErrPos - tgtPosDir * targetUnit->radius, owner->pos);
			SetOrderTarget(targetUnit);
			owner->AttackUnit(targetUnit, (c.options & INTERNAL_ORDER) == 0, c.GetID() == CMD_MANUALFIRE);

			inCommand = true;
		}
		else if (c.params.size() >= 3) {
			// user gave force-fire attack command
			SetGoal(c.GetPos(0), owner->pos);

			inCommand = true;
		}
	}

	// if our target is dead or we lost it then stop attacking
	// NOTE: unit should actually just continue to target area!
	if (targetDied || (c.params.size() == 1 && UpdateTargetLostTimer(int(c.params[0])) == 0)) {
		// cancel keeppointingto
		StopMove();
		FinishCommand();
		return;
	}


	// user clicked on enemy unit (note that we handle aircrafts slightly differently)
	if (orderTarget != NULL) {
		bool tryTargetRotate  = false;
		bool tryTargetHeading = false;

		float edgeFactor = 0.0f; // percent offset to target center
		const float3 targetMidPosVec = owner->midPos - orderTarget->midPos;

		const float targetGoalDist = (orderTarget->pos + owner->posErrorVector * 128.0f).SqDistance2D(goalPos);
		const float targetPosDist = Square(10.0f + orderTarget->pos.distance2D(owner->pos) * 0.2f);
		const float minPointingDist = std::min(1.0f * owner->losRadius * loshandler->losDiv, owner->maxRange * 0.9f);

		// FIXME? targetMidPosMaxDist is 3D, but compared with a 2D value
		const float targetMidPosDist2D = targetMidPosVec.Length2D();
		//const float targetMidPosMaxDist = owner->maxRange - (orderTarget->speed.SqLength() / owner->unitDef->maxAcc);

		if (!owner->weapons.empty()) {
			if (!(c.options & ALT_KEY) && SkipParalyzeTarget(orderTarget)) {
				StopMove();
				FinishCommand();
				return;
			}
		}

		for (unsigned int wNum = 0; wNum < owner->weapons.size(); wNum++) {
			CWeapon* w = owner->weapons[wNum];

			if (c.GetID() == CMD_MANUALFIRE) {
				assert(owner->unitDef->canManualFire);

				if (!w->weaponDef->manualfire) {
					continue;
				}
			}

			tryTargetRotate  = w->TryTargetRotate(orderTarget, (c.options & INTERNAL_ORDER) == 0);
			tryTargetHeading = w->TryTargetHeading(GetHeadingFromVector(-targetMidPosVec.x, -targetMidPosVec.z), orderTarget->pos, orderTarget != NULL, orderTarget);

			if (tryTargetRotate || tryTargetHeading)
				break;

			edgeFactor = math::fabs(w->targetBorder);
		}


		// if w->AttackUnit() returned true then we are already
		// in range with our biggest (?) weapon, so stop moving
		// also make sure that we're not locked in close-in/in-range state loop
		// due to rotates invoked by in-range or out-of-range states
		if (tryTargetRotate) {
			const bool canChaseTarget = (!tempOrder || owner->moveState != MOVESTATE_HOLDPOS);
			const bool targetBehind = (targetMidPosVec.dot(orderTarget->speed) < 0.0f);

			if (canChaseTarget && tryTargetHeading && targetBehind) {
				SetGoal(owner->pos + (orderTarget->speed * 80), owner->pos, SQUARE_SIZE, orderTarget->speed.Length() * 1.1f);
			} else {
				StopMove();

				if (gs->frameNum > lastCloseInTry + MAX_CLOSE_IN_RETRY_TICKS) {
					owner->moveType->KeepPointingTo(orderTarget->midPos, minPointingDist, true);
				}
			}

			owner->AttackUnit(orderTarget, (c.options & INTERNAL_ORDER) == 0, c.GetID() == CMD_MANUALFIRE);
		}

		// if we're on hold pos in a temporary order, then none of the close-in
		// code below should run, and the attack command is cancelled.
		else if (tempOrder && owner->moveState == MOVESTATE_HOLDPOS) {
			StopMove();
			FinishCommand();
			return;
		}

		// if ((our movetype has type HoverAirMoveType and length of 2D vector from us to target
		// less than 90% of our maximum range) OR squared length of 2D vector from us to target
		// less than 1024) then we are close enough
		else if (targetMidPosDist2D < (owner->maxRange * 0.9f)) {
			if (dynamic_cast<CHoverAirMoveType*>(owner->moveType) != NULL || (targetMidPosVec.SqLength2D() < 1024)) {
				StopMove();
				owner->moveType->KeepPointingTo(orderTarget->midPos, minPointingDist, true);
			}

			// if (((first weapon range minus first weapon length greater than distance to target)
			// and length of 2D vector from us to target less than 90% of our maximum range)
			// then we are close enough, but need to move sideways to get a shot.
			//assumption is flawed: The unit may be aiming or otherwise unable to shoot
			else if (owner->unitDef->strafeToAttack && targetMidPosDist2D < (owner->maxRange * 0.9f)) {
				moveDir ^= (owner->moveType->progressState == AMoveType::Failed);

				const float sin = moveDir ? 3.0/5 : -3.0/5;
				const float cos = 4.0 / 5;

				float3 goalDiff;
				goalDiff.x = targetMidPosVec.dot(float3(cos, 0, -sin));
				goalDiff.z = targetMidPosVec.dot(float3(sin, 0,  cos));
				goalDiff *= (targetMidPosDist2D < (owner->maxRange * 0.3f)) ? 1/cos : cos;
				goalDiff += orderTarget->pos;
				SetGoal(goalDiff, owner->pos);
			}
		}

		// if 2D distance of (target position plus attacker error vector times 128)
		// to goal position greater than
		// (10 plus 20% of 2D distance between attacker and target) then we need to close
		// in on target more
		else if (targetGoalDist > targetPosDist) {
			// if the target isn't in LOS, go to its approximate position
			// otherwise try to go precisely to the target
			// this should fix issues with low range weapons (mainly melee)
			const float3 errPos = ((orderTarget->losStatus[owner->allyteam] & LOS_INLOS)? ZeroVector: owner->posErrorVector * 128.0f);
			const float3 tgtPos = orderTarget->pos + errPos;

			const float3 norm = (tgtPos - owner->pos).Normalize();
			const float3 goal = tgtPos - norm * (orderTarget->radius * edgeFactor * 0.8f);

			SetGoal(goal, owner->pos);

			if (lastCloseInTry < gs->frameNum + MAX_CLOSE_IN_RETRY_TICKS)
				lastCloseInTry = gs->frameNum;
		}
	}

	// user wants to attack the ground; cycle through our
	// weapons until we find one that can accomodate him
	else if (c.params.size() >= 3) {
		const float3 attackPos = c.GetPos(0);
		const float3 attackVec = attackPos - owner->pos;

		bool foundWeapon = false;

		for (unsigned int wNum = 0; wNum < owner->weapons.size(); wNum++) {
			CWeapon* w = owner->weapons[wNum];

			if (foundWeapon)
				break;

			// XXX HACK - special weapon overrides any checks
			if (c.GetID() == CMD_MANUALFIRE) {
				assert(owner->unitDef->canManualFire);

				if (!w->weaponDef->manualfire)
					continue;
				if (attackVec.SqLength() >= (w->range * w->range))
					continue;

				StopMove();
				owner->AttackGround(attackPos, (c.options & INTERNAL_ORDER) == 0, c.GetID() == CMD_MANUALFIRE);
				owner->moveType->KeepPointingTo(attackPos, owner->maxRange * 0.9f, true);

				foundWeapon = true;
			} else {
				// NOTE:
				//   we call TryTargetHeading which is less restrictive than TryTarget
				//   (eg. the former succeeds even if the unit has not already aligned
				//   itself with <attackVec>)
				if (w->TryTargetHeading(GetHeadingFromVector(attackVec.x, attackVec.z), attackPos, (c.options & INTERNAL_ORDER) == 0, NULL)) {
					if (w->TryTargetRotate(attackPos, (c.options & INTERNAL_ORDER) == 0)) {
						StopMove();
						owner->AttackGround(attackPos, (c.options & INTERNAL_ORDER) == 0, c.GetID() == CMD_MANUALFIRE);

						foundWeapon = true;
					}

					// for gunships, this pitches the nose down such that
					// TryTargetRotate (which also checks range for itself)
					// has a bigger chance of succeeding
					//
					// hence it must be called as soon as we get in range
					// and may not depend on what TryTargetRotate returns
					// (otherwise we might never get a firing solution)
					owner->moveType->KeepPointingTo(attackPos, owner->maxRange * 0.9f, true);
				}
			}
		}

		#if 0
		// no weapons --> no need to stop at an arbitrary distance?
		else if (diff.SqLength2D() < 1024) {
			StopMove();
			owner->moveType->KeepPointingTo(attackPos, owner->maxRange * 0.9f, true);
		}
		#endif

		// if we are unarmed and more than 10 elmos distant
		// from target position, then keeping moving closer
		if (owner->weapons.empty() && attackPos.SqDistance2D(goalPos) > 100) {
			SetGoal(attackPos, owner->pos);
		}
	}
Esempio n. 7
0
void CProjectileHandler::Draw(bool drawReflection,bool drawRefraction)
{
	glDisable(GL_BLEND);
	glEnable(GL_TEXTURE_2D);
	glDepthMask(1);

	CVertexArray* va=GetVertexArray();

	int numFlyingPieces = 0;
	int drawnPieces = 0;

	/* Putting in, say, viewport culling will deserve refactoring. */

	unitDrawer->SetupForUnitDrawing();
	unitDrawer->SetupFor3DO();
	GML_RECMUTEX_LOCK(proj); // Draw

	Projectile_List::iterator psi;
	distlist.clear();

	// Projectiles (3do's get rendered, s3o qued)
	for (psi = ps.begin(); psi != ps.end(); ++psi) {
		CProjectile* pro = *psi;

		pro->UpdateDrawPos();

		if (camera->InView(pro->pos, pro->drawRadius) && (gu->spectatingFullView || loshandler->InLos(pro, gu->myAllyTeam) ||
			(pro->owner() && teamHandler->Ally(pro->owner()->allyteam, gu->myAllyTeam)))) {

			CUnit* owner = pro->owner();
			CUnit* trans = owner? (CUnit*) owner->GetTransporter(): 0;
			bool stunned = owner? owner->stunned: false;

			if (owner && trans && stunned && dynamic_cast<CShieldPartProjectile*>(pro)) {
				// if the unit that fired this projectile is inside a non-firebase
				// transport (so stunned) and the projectile forms part of a shield
				// (ie., the unit has a CPlasmaRepulser weapon but cannot fire it),
				// prevent the projectile (shield segment) from being drawn at the
				// unit's pre-pickup position (since CPlasmaRepulser::Update() is
				// responsible for updating CShieldPartProjectile::centerPos)
				continue;
			}


			if (drawReflection) {
				if (pro->pos.y < -pro->drawRadius)
					continue;

				float dif = pro->pos.y - camera->pos.y;
				float3 zeroPos = camera->pos * (pro->pos.y / dif) + pro->pos * (-camera->pos.y / dif);

				if (ground->GetApproximateHeight(zeroPos.x, zeroPos.z) > 3 + 0.5f * pro->drawRadius)
					continue;
			}
			if (drawRefraction && pro->pos.y > pro->drawRadius)
				continue;

			if (pro->s3domodel) {
				if (pro->s3domodel->textureType) {
					unitDrawer->QueS3ODraw(pro, pro->s3domodel->textureType);
				} else {
					pro->DrawUnitPart();
				}
			}

			struct projdist tmp;
			tmp.proj = pro;
			tmp.dist = pro->pos.dot(camera->forward);
			distlist.push_back(tmp);
		}
	}

	// 3DO flying pieces
	va->Initialize();
	va->EnlargeArrays(flying3doPieces->size()*4,0,VA_SIZE_TN);
	numFlyingPieces += flying3doPieces->size();
	for(list<FlyingPiece*>::iterator pi=flying3doPieces->begin();pi!=flying3doPieces->end();++pi){
		CMatrix44f m;
		m.Rotate((*pi)->rot,(*pi)->rotAxis);
		float3 interPos=(*pi)->pos+(*pi)->speed*gu->timeOffset;
		C3DOTextureHandler::UnitTexture* tex=(*pi)->prim->texture;
		const std::vector<S3DOVertex>& vertices    = (*pi)->object->vertices;
		const std::vector<int>&        verticesIdx = (*pi)->prim->vertices;

		const S3DOVertex* v=&vertices[verticesIdx[0]];
		float3 tp=m.Mul(v->pos);
		float3 tn=m.Mul(v->normal);
		tp+=interPos;
		va->AddVertexQTN(tp,tex->xstart,tex->ystart,tn);

		v=&vertices[verticesIdx[1]];
		tp=m.Mul(v->pos);
		tn=m.Mul(v->normal);
		tp+=interPos;
		va->AddVertexQTN(tp,tex->xend,tex->ystart,tn);

		v=&vertices[verticesIdx[2]];
		tp=m.Mul(v->pos);
		tn=m.Mul(v->normal);
		tp+=interPos;
		va->AddVertexQTN(tp,tex->xend,tex->yend,tn);

		v=&vertices[verticesIdx[3]];
		tp=m.Mul(v->pos);
		tn=m.Mul(v->normal);
		tp+=interPos;
		va->AddVertexQTN(tp,tex->xstart,tex->yend,tn);
	}
	drawnPieces+=va->drawIndex()/32;

	unitDrawer->CleanUp3DO();

	// draw qued S3O projectiles
	unitDrawer->DrawQuedS3O();

	// S3O flying pieces
	va->DrawArrayTN(GL_QUADS);
	for (int textureType = 1; textureType < flyings3oPieces.size(); textureType++){
		/* TODO Skip this if there's no FlyingPieces. */

		texturehandlerS3O->SetS3oTexture(textureType);

		for (int team = 0; team < flyings3oPieces[textureType].size(); team++){
			FlyingPiece_List * fpl = flyings3oPieces[textureType][team];

			unitDrawer->SetTeamColour(team);

			va->Initialize();
			va->EnlargeArrays(fpl->size()*4,0,VA_SIZE_TN);

			numFlyingPieces += fpl->size();

			for(list<FlyingPiece*>::iterator pi=fpl->begin();pi!=fpl->end();++pi){
				CMatrix44f m;
				m.Rotate((*pi)->rot,(*pi)->rotAxis);
				float3 interPos=(*pi)->pos+(*pi)->speed*gu->timeOffset;

				SS3OVertex * verts = (*pi)->verts;

				float3 tp, tn;

				for (int i = 0; i < 4; i++){
					tp=m.Mul(verts[i].pos);
					tn=m.Mul(verts[i].normal);
					tp+=interPos;
					va->AddVertexQTN(tp,verts[i].textureX,verts[i].textureY,tn);
				}
			}
			drawnPieces+=va->drawIndex()/32;
			va->DrawArrayTN(GL_QUADS);
		}
	}
	unitDrawer->CleanUpUnitDrawing();

	// Alpha transculent particles
	sort(distlist.begin(), distlist.end(), CompareProjDist);

	glEnable(GL_BLEND);
	glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
	glEnable(GL_TEXTURE_2D);
	textureAtlas->BindTexture();
	glColor4f(1,1,1,0.2f);
	glAlphaFunc(GL_GREATER,0.0f);
	glEnable(GL_ALPHA_TEST);
	glDepthMask(0);
//	glFogfv(GL_FOG_COLOR,mapInfo->atmosphere.fogColor);
	glDisable(GL_FOG);

	currentParticles=0;
	CProjectile::inArray=false;
	CProjectile::va=GetVertexArray();
	CProjectile::va->Initialize();
	for(int a=0;a<distlist.size();a++){
		distlist.at(a).proj->Draw();
	}
	if(CProjectile::inArray)
		CProjectile::DrawArray();

	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	//glDisable(GL_TEXTURE_2D);
	glDisable(GL_ALPHA_TEST);
	glColor4f(1,1,1,1.0f);
	glDepthMask(1);

	currentParticles=(int)(ps.size()*0.8f+currentParticles*0.2f);
	currentParticles+=(int)(0.2f*drawnPieces+0.3f*numFlyingPieces);
	particleSaturation=(float)currentParticles/(float)maxParticles;
//	glFogfv(GL_FOG_COLOR,mapInfo->atmosphere.fogColor);
}