void CProjectileHandler::CheckGroundCollisions(ProjectileContainer& pc) { ProjectileContainer::iterator pci; for (pci = pc.begin(); pci != pc.end(); ++pci) { CProjectile* p = *pci; if (!p->checkCol) { continue; } // NOTE: if <p> is a MissileProjectile and does not // have selfExplode set, it will never be removed (!) if (p->GetCollisionFlags() & Collision::NOGROUND) { continue; } // NOTE: don't add p->radius to groundHeight, or most // projectiles will collide with the ground too early const float groundHeight = ground->GetHeightReal(p->pos.x, p->pos.z); const bool belowGround = (p->pos.y < groundHeight); const bool insideWater = (p->pos.y <= 0.0f && !belowGround); const bool ignoreWater = p->ignoreWater; if (belowGround || (insideWater && !ignoreWater)) { // if position has dropped below terrain or into water // where we cannot live, adjust it and explode us now // (if the projectile does not set deleteMe = true, it // will keep hugging the terrain) p->pos.y = belowGround? groundHeight: 0.0f; p->Collision(); } } }
void CProjectileHandler::DrawProjectiles(const ProjectileContainer& pc, bool drawReflection, bool drawRefraction) { for (ProjectileContainer::render_iterator pci = pc.render_begin(); pci != pc.render_end(); ++pci) { CProjectile* pro = *pci; 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(); bool stunned = owner? owner->stunned: false; if (owner && stunned && dynamic_cast<CShieldPartProjectile*>(pro)) { // if the unit that fired this projectile is 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 // // also prevents shields being drawn at unit's pre-pickup position // (since CPlasmaRepulser::Update() is responsible for updating // CShieldPartProjectile::centerPos) if the unit is in a non-fireplatform // transport 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->type == MODELTYPE_S3O) { unitDrawer->QueS3ODraw(pro, pro->s3domodel->textureType); } else { pro->DrawUnitPart(); } } pro->tempdist = pro->pos.dot(camera->forward); distset.insert(pro); } } }
void CProjectileHandler::CheckGroundCollisions(ProjectileContainer& pc) { ProjectileContainer::iterator pci; for (pci = pc.begin(); pci != pc.end(); ++pci) { CProjectile* p = *pci; if (p->checkCol) { // too many projectiles seem to impact the ground before // actually hitting so don't subtract the projectile radius if (ground->GetHeight(p->pos.x, p->pos.z) > p->pos.y /* - p->radius*/) { p->Collision(); } } } }
void CProjectileHandler::DrawProjectilesShadow(const ProjectileContainer& pc) { for (ProjectileContainer::render_iterator pci = pc.render_begin(); pci != pc.render_end(); ++pci) { CProjectile* p = *pci; if ((gu->spectatingFullView || loshandler->InLos(p, gu->myAllyTeam) || (p->owner() && teamHandler->Ally(p->owner()->allyteam, gu->myAllyTeam)))) { if (p->s3domodel) { p->DrawUnitPart(); } else if (p->castShadow) { p->Draw(); } } } }
void CProjectileHandler::CheckUnitFeatureCollisions(ProjectileContainer& pc) { static std::vector<CUnit*> tempUnits(uh->MaxUnits(), NULL); static std::vector<CFeature*> tempFeatures(uh->MaxUnits(), NULL); for (ProjectileContainer::iterator pci = pc.begin(); pci != pc.end(); ++pci) { CProjectile* p = *pci; if (p->checkCol && !p->deleteMe) { const float3 ppos0 = p->pos; const float3 ppos1 = p->pos + p->speed; const float speedf = p->speed.Length(); CUnit** endUnit = &tempUnits[0]; CFeature** endFeature = &tempFeatures[0]; qf->GetUnitsAndFeaturesExact(p->pos, p->radius + speedf, endUnit, endFeature); CheckUnitCollisions(p, tempUnits, endUnit, ppos0, ppos1); CheckFeatureCollisions(p, tempFeatures, endFeature, ppos0, ppos1); } } }
void CProjectileHandler::DrawProjectilesMiniMap(const ProjectileContainer& pc) { if (pc.render_size() > 0) { CVertexArray* lines = GetVertexArray(); CVertexArray* points = GetVertexArray(); lines->Initialize(); lines->EnlargeArrays(pc.render_size() * 2, 0, VA_SIZE_C); points->Initialize(); points->EnlargeArrays(pc.render_size(), 0, VA_SIZE_C); for (ProjectileContainer::render_iterator pci = pc.render_begin(); pci != pc.render_end(); ++pci) { CProjectile* p = *pci; if ((p->owner() && (p->owner()->allyteam == gu->myAllyTeam)) || gu->spectatingFullView || loshandler->InLos(p, gu->myAllyTeam)) { p->DrawOnMinimap(*lines, *points); } } lines->DrawArrayC(GL_LINES); points->DrawArrayC(GL_POINTS); } }
void CProjectileHandler::UpdateProjectileContainer(ProjectileContainer& pc, bool synced) { ProjectileContainer::iterator pci = pc.begin(); while (pci != pc.end()) { CProjectile* p = *pci; if (p->deleteMe) { if (p->synced) { assert(synced); if(p->weapon || p->piece) { //! iterator is always valid const ProjectileMap::iterator it = syncedProjectileIDs.find(p->id); const ProjectileMapPair& pp = it->second; eventHandler.ProjectileDestroyed(pp.first, pp.second); syncedProjectileIDs.erase(it); if (p->id != 0) { freeIDs.push_back(p->id); } } //! push_back this projectile for deletion pci = pc.erase_delete_synced(pci); } else { assert(!synced); pci = pc.erase_delete(pci); } } else { p->Update(); GML_GET_TICKS(p->lastProjUpdate); ++pci; } } }
void CProjectileHandler::CheckGroundCollisions(ProjectileContainer& pc) { for (size_t i = 0; i < pc.size(); ++i) { CProjectile* p = pc[i]; if (!p->checkCol) continue; // NOTE: // if <p> is a MissileProjectile and does not have // selfExplode set, tbis will cause it to never be // removed (!) if (p->GetCollisionFlags() & Collision::NOGROUND) continue; // don't collide with ground yet if last update scheduled a bounce if (p->weapon && static_cast<const CWeaponProjectile*>(p)->HasScheduledBounce()) continue; // NOTE: // don't add p->radius to groundHeight, or most (esp. modelled) // projectiles will collide with the ground one or more frames // too early const float groundHeight = CGround::GetHeightReal(p->pos.x, p->pos.z); const bool belowGround = (p->pos.y < groundHeight); const bool insideWater = (p->pos.y <= 0.0f && !belowGround); const bool ignoreWater = p->ignoreWater; if (belowGround || (insideWater && !ignoreWater)) { // if position has dropped below terrain or into water // where we can not live, adjust it and explode us now // (if the projectile does not set deleteMe = true, it // will keep hugging the terrain) p->SetPosition(p->pos * XZVector + UpVector * groundHeight * belowGround); p->Collision(); } } }
void CProjectileHandler::CheckUnitFeatureCollisions(ProjectileContainer& pc) { static std::vector<CUnit*> tempUnits; static std::vector<CFeature*> tempFeatures; static std::vector<CPlasmaRepulser*> tempRepulsers; for (size_t i = 0; i < pc.size(); ++i) { CProjectile* p = pc[i]; if (!p->checkCol) continue; if ( p->deleteMe) continue; const float3 ppos0 = p->pos; const float3 ppos1 = p->pos + p->speed; quadField->GetUnitsAndFeaturesColVol(p->pos, p->radius + p->speed.w, tempUnits, tempFeatures, &tempRepulsers); CheckShieldCollisions(p, tempRepulsers, ppos0, ppos1); tempRepulsers.clear(); CheckUnitCollisions(p, tempUnits, ppos0, ppos1); tempUnits.clear(); CheckFeatureCollisions(p, tempFeatures, ppos0, ppos1); tempFeatures.clear(); } }
void CProjectileHandler::UpdateProjectileContainer(ProjectileContainer& pc, bool synced) { ProjectileContainer::iterator pci = pc.begin(); #define VECTOR_SANITY_CHECK(v) \ assert(!math::isnan(v.x) && !math::isinf(v.x)); \ assert(!math::isnan(v.y) && !math::isinf(v.y)); \ assert(!math::isnan(v.z) && !math::isinf(v.z)); #define MAPPOS_SANITY_CHECK(v) \ assert(v.x >= -(float3::maxxpos * 16.0f)); \ assert(v.x <= (float3::maxxpos * 16.0f)); \ assert(v.z >= -(float3::maxzpos * 16.0f)); \ assert(v.z <= (float3::maxzpos * 16.0f)); \ assert(v.y >= -MAX_PROJECTILE_HEIGHT); \ assert(v.y <= MAX_PROJECTILE_HEIGHT); #define PROJECTILE_SANITY_CHECK(p) \ VECTOR_SANITY_CHECK(p->pos); \ MAPPOS_SANITY_CHECK(p->pos); while (pci != pc.end()) { CProjectile* p = *pci; if (p->deleteMe) { ProjectileMap::iterator pIt; if (p->synced) { //! iterator is always valid pIt = syncedProjectileIDs.find(p->id); eventHandler.ProjectileDestroyed((pIt->second).first, (pIt->second).second); syncedProjectileIDs.erase(pIt); freeSyncedIDs.push_back(p->id); //! push_back this projectile for deletion pci = pc.erase_delete_synced(pci); } else { #if UNSYNCED_PROJ_NOEVENT eventHandler.UnsyncedProjectileDestroyed(p); #else pIt = unsyncedProjectileIDs.find(p->id); eventHandler.ProjectileDestroyed((pIt->second).first, (pIt->second).second); unsyncedProjectileIDs.erase(pIt); freeUnsyncedIDs.push_back(p->id); #endif #if DETACH_SYNCED pci = pc.erase_detach(pci); #else pci = pc.erase_delete(pci); #endif } } else { PROJECTILE_SANITY_CHECK(p); p->Update(); qf->MovedProjectile(p); PROJECTILE_SANITY_CHECK(p); GML_GET_TICKS(p->lastProjUpdate); ++pci; } } }
void CProjectileHandler::UpdateProjectileContainer(ProjectileContainer& pc, bool synced) { // WARNING: // we can't use iterators here because ProjectileCreated // and ProjectileDestroyed events may add new projectiles // to the container! for (size_t i = 0; i < pc.size(); /*no-op*/) { CProjectile* p = pc[i]; assert(p != nullptr); assert(p->synced == synced); #ifdef USING_CREG assert(p->synced == !!(p->GetClass()->binder->flags & creg::CF_Synced)); #endif // creation if (p->callEvent) { if (synced || !UNSYNCED_PROJ_NOEVENT) eventHandler.ProjectileCreated(p, p->GetAllyteamID()); eventHandler.RenderProjectileCreated(p); p->callEvent = false; } // deletion (FIXME: move outside of loop) if (p->deleteMe) { pc[i] = pc.back(); pc.pop_back(); eventHandler.RenderProjectileDestroyed(p); if (synced) { eventHandler.ProjectileDestroyed(p, p->GetAllyteamID()); syncedProjectileIDs[p->id] = nullptr; freeSyncedIDs.push_back(p->id); ASSERT_SYNCED(p->pos); ASSERT_SYNCED(p->id); } else { #if !UNSYNCED_PROJ_NOEVENT eventHandler.ProjectileDestroyed(p, p->GetAllyteamID()); unsyncedProjectileIDs[p->id] = nullptr; freeUnsyncedIDs.push_back(p->id); #endif } projMemPool.free(p); continue; } // neither ++i; } SCOPED_TIMER("Sim::Projectiles::Update"); // WARNING: same as above but for p->Update() for (size_t i = 0; i < pc.size(); ++i) { CProjectile* p = pc[i]; assert(p != nullptr); MAPPOS_SANITY_CHECK(p->pos); p->Update(); quadField->MovedProjectile(p); MAPPOS_SANITY_CHECK(p->pos); } }