void CInterceptHandler::AddInterceptTarget(CWeaponProjectile* target, const float3& destination) { int targTeam = -1; if (target->owner()) { targTeam=target->owner()->allyteam; } for (std::list<CWeapon*>::iterator wi = interceptors.begin(); wi != interceptors.end(); ++wi) { CWeapon* w = *wi; if (((targTeam == -1) || !teamHandler->Ally(w->owner->allyteam,targTeam)) && (target->weaponDef->targetable & w->weaponDef->interceptor) && (w->weaponPos.SqDistance2D(destination) < Square(w->weaponDef->coverageRange))) { w->incoming.push_back(target); w->AddDeathDependence(target, CObject::DEPENDENCE_INTERCEPT); } } }
void CInterceptHandler::Update(bool forced) { if (((gs->frameNum % UNIT_SLOWUPDATE_RATE) != 0) && !forced) return; std::list<CWeapon*>::iterator wit; std::map<int, CWeaponProjectile*>::const_iterator pit; for (wit = interceptors.begin(); wit != interceptors.end(); ++wit) { CWeapon* w = *wit; const WeaponDef* wDef = w->weaponDef; const CUnit* wOwner = w->owner; // const float3& wOwnerPos = wOwner->pos; const float3& wPos = w->weaponPos; assert(wDef->interceptor || wDef->isShield); for (pit = interceptables.begin(); pit != interceptables.end(); ++pit) { CWeaponProjectile* p = pit->second; const WeaponDef* pDef = p->weaponDef; if ((pDef->targetable & wDef->interceptor) == 0) continue; if (w->incomingProjectiles.find(p->id) != w->incomingProjectiles.end()) continue; const CUnit* pOwner = p->owner(); const int pAllyTeam = (pOwner != NULL)? pOwner->allyteam: -1; if (pAllyTeam != -1 && teamHandler->Ally(wOwner->allyteam, pAllyTeam)) continue; // there are four cases when an interceptor <w> should fire at a projectile <p>: // 1. p's target position inside w's interception circle (w's owner can move!) // 2. p's current position inside w's interception circle // 3. p's projected impact position inside w's interception circle // 4. p's trajectory intersects w's interception circle // // these checks all need to be evaluated periodically, not just // when a projectile is created and handed to AddInterceptTarget const float interceptDist = w->weaponPos.distance(p->pos); const float impactDist = ground->LineGroundCol(p->pos, p->pos + p->dir * interceptDist); const float3& pFlightPos = p->pos; const float3& pImpactPos = p->pos + p->dir * impactDist; const float3& pTargetPos = p->targetPos; if ((pTargetPos - wPos).SqLength2D() < Square(wDef->coverageRange)) { w->AddDeathDependence(p, CObject::DEPENDENCE_INTERCEPT); w->incomingProjectiles[p->id] = p; continue; // 1 } if (wDef->interceptor == 1) { // <w> is just a static interceptor and fires only at projectiles // TARGETED within its current interception area; any projectiles // CROSSING its interception area are fired at only if interceptor // is >= 2 continue; } if ((pFlightPos - wPos).SqLength2D() < Square(wDef->coverageRange)) { w->AddDeathDependence(p, CObject::DEPENDENCE_INTERCEPT); w->incomingProjectiles[p->id] = p; continue; // 2 } if ((pImpactPos - wPos).SqLength2D() < Square(wDef->coverageRange)) { const float3 pTargetDir = (pTargetPos - pFlightPos).SafeNormalize(); const float3 pImpactDir = (pImpactPos - pFlightPos).SafeNormalize(); // the projected impact position can briefly shift into the covered // area during transition from vertical to horizontal flight, so we // perform an extra test (NOTE: assumes non-parabolic trajectory) if (pTargetDir.dot(pImpactDir) >= 0.999f) { w->AddDeathDependence(p, CObject::DEPENDENCE_INTERCEPT); w->incomingProjectiles[p->id] = p; continue; // 3 } } const float3 pCurSeparationVec = wPos - pFlightPos; const float pMinSeparationDist = std::max(pCurSeparationVec.dot(p->dir), 0.0f); const float3 pMinSeparationPos = pFlightPos + (p->dir * pMinSeparationDist); const float3 pMinSeparationVec = wPos - pMinSeparationPos; if (pMinSeparationVec.SqLength() < Square(wDef->coverageRange)) { w->AddDeathDependence(p, CObject::DEPENDENCE_INTERCEPT); w->incomingProjectiles[p->id] = p; continue; // 4 } } } }