void CProjectileHandler::CheckUnitCollisions( CProjectile* p, std::vector<CUnit*>& tempUnits, CUnit** endUnit, const float3& ppos0, const float3& ppos1) { CollisionQuery q; for (CUnit** ui = &tempUnits[0]; ui != endUnit; ++ui) { CUnit* unit = *ui; const CUnit* attacker = p->owner(); const bool raytraced = (unit->collisionVolume->GetTestType() == CollisionVolume::COLVOL_HITTEST_CONT); // if this unit fired this projectile, always ignore if (attacker == unit) { continue; } if (p->GetCollisionFlags() & Collision::NOFRIENDLIES) { if (attacker != NULL && (unit->allyteam == attacker->allyteam)) { continue; } } if (p->GetCollisionFlags() & Collision::NOENEMIES) { if (attacker != NULL && (unit->allyteam != attacker->allyteam)) { continue; } } if (p->GetCollisionFlags() & Collision::NONEUTRALS) { if (unit->IsNeutral()) { continue; } } if (CCollisionHandler::DetectHit(unit, ppos0, ppos1, &q)) { if (q.lmp != NULL) { unit->SetLastAttackedPiece(q.lmp, gs->frameNum); } // The current projectile <p> won't reach the raytraced surface impact // position until ::Update() is called (same frame). This is a problem // when dealing with fast low-AOE projectiles since they would do almost // no damage if detonated outside the collision volume. Therefore, smuggle // a bit with its position now (rather than rolling it back in ::Update() // and waiting for the next-frame CheckUnitCol(), which is problematic // for noExplode projectiles). // const float3& pimpp = (q.b0)? q.p0: q.p1; const float3 pimpp = (q.b0 && q.b1)? ( q.p0 + q.p1) * 0.5f: (q.b0 )? ( q.p0 + ppos1) * 0.5f: (ppos0 + q.p1) * 0.5f; p->pos = (raytraced)? pimpp: ppos0; p->Collision(unit); p->pos = (raytraced)? ppos0: p->pos; break; } } }
bool CAICheats::IsUnitNeutral(int unitid) { if (!CHECK_UNITID(unitid)) return false; CUnit* unit = uh->units[unitid]; if (unit) { return (unit->IsNeutral()); } return false; }
bool CAICallback::IsUnitNeutral(int unitId) { verify(); if (CHECK_UNITID(unitId)) { CUnit* unit = uh->units[unitId]; if (unit && (unit->losStatus[gs->AllyTeam(team)] & LOS_INLOS)) { if (unit->IsNeutral()) return true; } } return false; }
/** same as TestTrajectoryAllyCone, but looks for neutral units */ bool CGameHelper::TestTrajectoryNeutralCone(const float3& from, const float3& flatdir, float length, float linear, float quadratic, float spread, float baseSize, CUnit* owner) { int quads[1000]; int* endQuad = quads; qf->GetQuadsOnRay(from, flatdir, length, endQuad); for (int* qi = quads; qi != endQuad; ++qi) { const CQuadField::Quad& quad = qf->GetQuad(*qi); for (std::list<CUnit*>::const_iterator ui = quad.units.begin(); ui != quad.units.end(); ++ui) { CUnit* u = *ui; if (u == owner) continue; if (u->IsNeutral()) { if (TestTrajectoryConeHelper(from, flatdir, length, linear, quadratic, spread, baseSize, u)) return true; } } } return false; }
/** same as TestAllyCone, but looks for neutral units */ bool TestNeutralCone(const float3& from, const float3& weaponDir, float length, float spread, CUnit* owner) { int quads[1000]; int* endQuad = quads; qf->GetQuadsOnRay(from, weaponDir, length, endQuad); for (int* qi = quads; qi != endQuad; ++qi) { const CQuadField::Quad& quad = qf->GetQuad(*qi); for (std::list<CUnit*>::const_iterator ui = quad.units.begin(); ui != quad.units.end(); ++ui) { CUnit* u = *ui; if (u == owner) continue; if (u->IsNeutral()) { if (TestConeHelper(from, weaponDir, length, spread, u)) return true; } } } return false; }
// called by {CRifle, CBeamLaser, CLightningCannon}::Fire() float CGameHelper::TraceRay(const float3& start, const float3& dir, float length, float /*power*/, CUnit* owner, CUnit *&hit, int collisionFlags) { float groundLength = ground->LineGroundCol(start, start + dir * length); const bool ignoreAllies = !!(collisionFlags & COLLISION_NOFRIENDLY); const bool ignoreFeatures = !!(collisionFlags & COLLISION_NOFEATURE); const bool ignoreNeutrals = !!(collisionFlags & COLLISION_NONEUTRAL); if (length > groundLength && groundLength > 0) { length = groundLength; } CollisionQuery cq; int quads[1000]; int* endQuad = quads; qf->GetQuadsOnRay(start, dir, length, endQuad); if (!ignoreFeatures) { for (int* qi = quads; qi != endQuad; ++qi) { const CQuadField::Quad& quad = qf->GetQuad(*qi); for (std::list<CFeature*>::const_iterator ui = quad.features.begin(); ui != quad.features.end(); ++ui) { CFeature* f = *ui; if (!f->blocking || !f->collisionVolume) { // NOTE: why check the blocking property? continue; } if (CCollisionHandler::Intersect(f, start, start + dir * length, &cq)) { const float3& intPos = (cq.b0)? cq.p0: cq.p1; const float tmpLen = (intPos - start).Length(); // we want the closest feature (intersection point) on the ray if (tmpLen < length) { length = tmpLen; } } } } } hit = 0; for (int* qi = quads; qi != endQuad; ++qi) { const CQuadField::Quad& quad = qf->GetQuad(*qi); for (std::list<CUnit*>::const_iterator ui = quad.units.begin(); ui != quad.units.end(); ++ui) { CUnit* u = *ui; if (u == owner) continue; if (ignoreAllies && u->allyteam == owner->allyteam) continue; if (ignoreNeutrals && u->IsNeutral()) { continue; } if (CCollisionHandler::Intersect(u, start, start + dir * length, &cq)) { const float3& intPos = (cq.b0)? cq.p0: cq.p1; const float tmpLen = (intPos - start).Length(); // we want the closest unit (intersection point) on the ray if (tmpLen < length) { length = tmpLen; hit = u; } } } } return length; }
void CProjectileHandler::CheckUnitCol() { Projectile_List::iterator psi; static std::vector<CUnit*> tempUnits(uh->MaxUnits(), NULL); static std::vector<CFeature*> tempFeatures(uh->MaxUnits(), NULL); CollisionQuery q; for (psi = ps.begin(); psi != ps.end(); ++psi) { CProjectile* p = (*psi); if (p->checkCol && !p->deleteMe) { const float3 ppos0 = p->pos; const float3 ppos1 = p->pos + p->speed; float speedf = p->speed.Length(); CUnit** endUnit = &tempUnits[0]; CFeature** endFeature = &tempFeatures[0]; qf->GetUnitsAndFeaturesExact(p->pos, p->radius + speedf, endUnit, endFeature); for (CUnit** ui = &tempUnits[0]; ui != endUnit; ++ui) { CUnit* unit = *ui; const bool friendlyShot = (p->owner() && (unit->allyteam == p->owner()->allyteam)); const bool raytraced = (unit->collisionVolume && unit->collisionVolume->GetTestType() == COLVOL_TEST_CONT); // if this unit fired this projectile or (this unit is in the // same allyteam as the unit that shot this projectile and we // are ignoring friendly collisions) if (p->owner() == unit || !unit->collisionVolume || ((p->collisionFlags & COLLISION_NOFRIENDLY) && friendlyShot)) { continue; } if (p->collisionFlags & COLLISION_NONEUTRAL) { if (unit->IsNeutral()) { continue; } } if (CCollisionHandler::DetectHit(unit, ppos0, ppos1, &q)) { // this projectile won't reach the raytraced surface impact pos // until Update() is called (right after we return, same frame) // which is a problem when dealing with fast low-AOE projectiles // since they would do almost no damage if detonated outside the // volume, so smuggle a bit ("rolling back" its pos in Update() // and waiting for the next-frame CheckUnitCol() is problematic // for noExplode projectiles) // const float3& pimpp = (q.b0)? q.p0: q.p1; const float3 pimpp = (q.b0 && q.b1)? (q.p0 + q.p1) * 0.5f: (q.b0 )? (q.p0 + ppos1) * 0.5f: (ppos0 + q.p1) * 0.5f; p->pos = (raytraced)? pimpp: ppos0; p->Collision(unit); p->pos = (raytraced)? ppos0: p->pos; break; } } if (!(p->collisionFlags & COLLISION_NOFEATURE)) { for (CFeature** fi = &tempFeatures[0]; fi != endFeature; ++fi) { CFeature* feature = *fi; const bool raytraced = (feature->collisionVolume && feature->collisionVolume->GetTestType() == COLVOL_TEST_CONT); // geothermals do not have a collision volume, skip them if (!feature->blocking || feature->def->geoThermal || !feature->collisionVolume) { continue; } if (CCollisionHandler::DetectHit(feature, ppos0, ppos1, &q)) { const float3 pimpp = (q.b0 && q.b1)? (q.p0 + q.p1) * 0.5f: (q.b0 )? (q.p0 + ppos1) * 0.5f: (ppos0 + q.p1) * 0.5f; p->pos = (raytraced)? pimpp: ppos0; p->Collision(feature); p->pos = (raytraced)? ppos0: p->pos; break; } } } } } }
// called by {CRifle, CBeamLaser, CLightningCannon}::Fire(), CWeapon::HaveFreeLineOfFire(), and Skirmish AIs float TraceRay( const float3& start, const float3& dir, float length, int collisionFlags, const CUnit* owner, CUnit*& hitUnit, CFeature*& hitFeature ) { const bool ignoreEnemies = ((collisionFlags & Collision::NOENEMIES ) != 0); const bool ignoreAllies = ((collisionFlags & Collision::NOFRIENDLIES) != 0); const bool ignoreFeatures = ((collisionFlags & Collision::NOFEATURES ) != 0); const bool ignoreNeutrals = ((collisionFlags & Collision::NONEUTRALS ) != 0); const bool ignoreGround = ((collisionFlags & Collision::NOGROUND ) != 0); const bool ignoreUnits = ignoreEnemies && ignoreAllies && ignoreNeutrals; hitFeature = NULL; hitUnit = NULL; if (dir == ZeroVector) { return -1.0f; } if (!ignoreFeatures || !ignoreUnits) { GML_RECMUTEX_LOCK(quad); // TraceRay CollisionQuery cq; int* begQuad = NULL; int* endQuad = NULL; qf->GetQuadsOnRay(start, dir, length, begQuad, endQuad); // feature intersection if (!ignoreFeatures) { for (int* quadPtr = begQuad; quadPtr != endQuad; ++quadPtr) { const CQuadField::Quad& quad = qf->GetQuad(*quadPtr); for (std::list<CFeature*>::const_iterator ui = quad.features.begin(); ui != quad.features.end(); ++ui) { CFeature* f = *ui; // NOTE: // if f is non-blocking, ProjectileHandler will not test // for collisions with projectiles so we can skip it here if (!f->blocking) continue; if (CCollisionHandler::DetectHit(f, start, start + dir * length, &cq, true)) { const float3& intPos = (cq.b0)? cq.p0: cq.p1; const float len = (intPos - start).dot(dir); // same as (intPos - start).Length() // we want the closest feature (intersection point) on the ray if (len < length) { length = len; hitFeature = f; } } } } } // unit intersection if (!ignoreUnits) { for (int* quadPtr = begQuad; quadPtr != endQuad; ++quadPtr) { const CQuadField::Quad& quad = qf->GetQuad(*quadPtr); for (std::list<CUnit*>::const_iterator ui = quad.units.begin(); ui != quad.units.end(); ++ui) { CUnit* u = *ui; if (u == owner) continue; if (ignoreAllies && u->allyteam == owner->allyteam) continue; if (ignoreNeutrals && u->IsNeutral()) continue; if (ignoreEnemies && u->allyteam != owner->allyteam) continue; if (CCollisionHandler::DetectHit(u, start, start + dir * length, &cq, true)) { const float3& intPos = (cq.b0)? cq.p0: cq.p1; const float len = (intPos - start).dot(dir); // same as (intPos - start).Length() // we want the closest unit (intersection point) on the ray if (len < length) { length = len; hitUnit = u; } } } } if (hitUnit) hitFeature = NULL; } } if (!ignoreGround) { // ground intersection const float groundLength = ground->LineGroundCol(start, start + dir * length); if (length > groundLength && groundLength > 0) { length = groundLength; hitUnit = NULL; hitFeature = NULL; } } return length; }
// called by {CRifle, CBeamLaser, CLightningCannon}::Fire() and Skirmish AIs float TraceRay(const float3& start, const float3& dir, float length, int collisionFlags, const CUnit* owner, CUnit*& hitUnit, CFeature*& hitFeature) { const bool ignoreEnemies = !!(collisionFlags & Collision::NOENEMIES); const bool ignoreAllies = !!(collisionFlags & Collision::NOFRIENDLIES); const bool ignoreFeatures = !!(collisionFlags & Collision::NOFEATURES); const bool ignoreNeutrals = !!(collisionFlags & Collision::NONEUTRALS); const bool ignoreGround = !!(collisionFlags & Collision::NOGROUND); const bool ignoreUnits = ignoreEnemies && ignoreAllies && ignoreNeutrals; hitFeature = NULL; hitUnit = NULL; if (dir == ZeroVector) { return -1.0f; } CollisionQuery cq; { GML_RECMUTEX_LOCK(quad); // TraceRay const vector<int> &quads = qf->GetQuadsOnRay(start, dir, length); //! feature intersection if (!ignoreFeatures) { for (vector<int>::const_iterator qi = quads.begin(); qi != quads.end(); ++qi) { const CQuadField::Quad& quad = qf->GetQuad(*qi); for (std::list<CFeature*>::const_iterator ui = quad.features.begin(); ui != quad.features.end(); ++ui) { CFeature* f = *ui; if (!f->blocking || !f->collisionVolume) { // NOTE: why check the blocking property? continue; } if (CCollisionHandler::Intersect(f, start, start + dir * length, &cq)) { const float3& intPos = (cq.b0)? cq.p0: cq.p1; const float len = (intPos - start).dot(dir); //! same as (intPos - start).Length() //! we want the closest feature (intersection point) on the ray if (len < length) { length = len; hitFeature = f; } } } } } //! unit intersection if (!ignoreUnits) { for (vector<int>::const_iterator qi = quads.begin(); qi != quads.end(); ++qi) { const CQuadField::Quad& quad = qf->GetQuad(*qi); for (std::list<CUnit*>::const_iterator ui = quad.units.begin(); ui != quad.units.end(); ++ui) { CUnit* u = *ui; if (u == owner) continue; if (ignoreAllies && u->allyteam == owner->allyteam) continue; if (ignoreNeutrals && u->IsNeutral()) continue; if (ignoreEnemies && u->allyteam != owner->allyteam) continue; if (CCollisionHandler::Intersect(u, start, start + dir * length, &cq)) { const float3& intPos = (cq.b0)? cq.p0: cq.p1; const float len = (intPos - start).dot(dir); //! same as (intPos - start).Length() //! we want the closest unit (intersection point) on the ray if (len < length) { length = len; hitUnit = u; } } } } if (hitUnit) hitFeature = NULL; } } //GML_RECMUTEX_LOCK(quad); if (!ignoreGround) { //! ground intersection float groundLength = ground->LineGroundCol(start, start + dir * length); if (length > groundLength && groundLength > 0) { length = groundLength; hitUnit = NULL; hitFeature = NULL; } } return length; }
// called by {CRifle, CBeamLaser, CLightningCannon}::Fire(), CWeapon::HaveFreeLineOfFire(), and Skirmish AIs float TraceRay( const float3& start, const float3& dir, float length, int avoidFlags, const CUnit* owner, CUnit*& hitUnit, CFeature*& hitFeature, CollisionQuery* hitColQuery ) { const bool ignoreEnemies = ((avoidFlags & Collision::NOENEMIES ) != 0); const bool ignoreAllies = ((avoidFlags & Collision::NOFRIENDLIES) != 0); const bool ignoreFeatures = ((avoidFlags & Collision::NOFEATURES ) != 0); const bool ignoreNeutrals = ((avoidFlags & Collision::NONEUTRALS ) != 0); const bool ignoreGround = ((avoidFlags & Collision::NOGROUND ) != 0); const bool ignoreUnits = ignoreEnemies && ignoreAllies && ignoreNeutrals; hitFeature = NULL; hitUnit = NULL; if (dir == ZeroVector) return -1.0f; if (!ignoreFeatures || !ignoreUnits) { GML_RECMUTEX_LOCK(quad); // TraceRay CollisionQuery cq; int* begQuad = NULL; int* endQuad = NULL; quadField->GetQuadsOnRay(start, dir, length, begQuad, endQuad); // locally point somewhere non-NULL; we cannot pass hitColQuery // to DetectHit directly because each call resets it internally if (hitColQuery == NULL) hitColQuery = &cq; // feature intersection if (!ignoreFeatures) { for (int* quadPtr = begQuad; quadPtr != endQuad; ++quadPtr) { const CQuadField::Quad& quad = quadField->GetQuad(*quadPtr); for (std::list<CFeature*>::const_iterator ui = quad.features.begin(); ui != quad.features.end(); ++ui) { CFeature* f = *ui; // NOTE: // if f is non-blocking, ProjectileHandler will not test // for collisions with projectiles so we can skip it here if (!f->HasCollidableStateBit(CSolidObject::CSTATE_BIT_QUADMAPRAYS)) continue; if (CCollisionHandler::DetectHit(f, start, start + dir * length, &cq, true)) { const float len = cq.GetHitPosDist(start, dir); // we want the closest feature (intersection point) on the ray if (len < length) { length = len; hitFeature = f; *hitColQuery = cq; } } } } } // unit intersection if (!ignoreUnits) { for (int* quadPtr = begQuad; quadPtr != endQuad; ++quadPtr) { const CQuadField::Quad& quad = quadField->GetQuad(*quadPtr); for (std::list<CUnit*>::const_iterator ui = quad.units.begin(); ui != quad.units.end(); ++ui) { CUnit* u = *ui; if (u == owner) continue; if (!u->HasCollidableStateBit(CSolidObject::CSTATE_BIT_QUADMAPRAYS)) continue; if (ignoreAllies && u->allyteam == owner->allyteam) continue; if (ignoreNeutrals && u->IsNeutral()) continue; if (ignoreEnemies && u->allyteam != owner->allyteam) continue; if (CCollisionHandler::DetectHit(u, start, start + dir * length, &cq, true)) { const float len = cq.GetHitPosDist(start, dir); // we want the closest unit (intersection point) on the ray if (len < length) { length = len; hitUnit = u; *hitColQuery = cq; } } } } if (hitUnit) hitFeature = NULL; } } if (!ignoreGround) { // ground intersection const float groundLength = ground->LineGroundCol(start, start + dir * length); if (length > groundLength && groundLength > 0.0f) { length = groundLength; hitUnit = NULL; hitFeature = NULL; } } return length; }