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; } } }
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; }
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; } } }
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 }
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); } }
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); }