void CFlameProjectile::Update() { if (!luaMoveCtrl) { SetPosition(pos + speed); UpdateGroundBounce(); SetVelocityAndSpeed(speed + spread); } UpdateInterception(); radius = radius + weaponDef->sizeGrowth; sqRadius = radius * radius; drawRadius = radius * weaponDef->collisionSize; curTime += invttl; if (curTime > physLife) { checkCol = false; } if (curTime > 1) { curTime = 1; deleteMe = true; } explGenHandler->GenExplosion(cegID, pos, speed, curTime, 0.0f, 0.0f, NULL, NULL); }
void CGeoThermSmokeProjectile::UpdateDir() { if (geo == NULL) return; const CSolidObject* obj = geo->solidOnTop; if (obj == NULL) return; if (!obj->HasCollidableStateBit(CSolidObject::CSTATE_BIT_SOLIDOBJECTS)) return; float3 geoVector = pos - obj->pos; if (geoVector.SqLength() == 0.0f) return; if (geoVector.SqLength() >= (obj->radius * obj->radius)) return; geoVector *= (obj->radius * fastmath::isqrt_sse(geoVector.SqLength())); SetPosition(pos * 0.3f + (obj->pos + geoVector) * 0.7f); if (geoVector.y >= (obj->radius * 0.4f)) return; // escape sideways if covered by geothermal object (unreliable) const float3 orthoDir = float3(geoVector.z, 0.0f, -geoVector.x); const float3 newDir = (geoVector.cross(orthoDir)).ANormalize(); SetVelocityAndSpeed(newDir * speed.w); }
void CFlareProjectile::Update() { CUnit* owner = CProjectile::owner(); if (gs->frameNum == activateFrame) { if (owner != NULL) { SetPosition(owner->pos); CWorldObject::SetVelocity(owner->speed); CWorldObject::SetVelocity(speed + (owner->rightdir * owner->unitDef->flareDropVector.x)); CWorldObject::SetVelocity(speed + (owner->updir * owner->unitDef->flareDropVector.y)); CWorldObject::SetVelocity(speed + (owner->frontdir * owner->unitDef->flareDropVector.z)); SetVelocityAndSpeed(speed); } else { deleteMe = true; } } if (gs->frameNum >= activateFrame) { SetPosition(pos + speed); SetVelocityAndSpeed((speed * 0.95f) + (UpVector * mygravity)); //FIXME: just spawn new flares, if new missiles incoming? if(owner && lastSub < (gs->frameNum - owner->unitDef->flareSalvoDelay) && numSub<owner->unitDef->flareSalvoSize) { subPos[numSub] = owner->pos; float3 s = owner->speed; s += owner->rightdir * owner->unitDef->flareDropVector.x; s += owner->updir * owner->unitDef->flareDropVector.y; s += owner->frontdir * owner->unitDef->flareDropVector.z; subSpeed[numSub] = s; ++numSub; lastSub = gs->frameNum; for (CMissileProjectile* missile: owner->incomingMissiles) { if (gsRNG.NextFloat() < owner->unitDef->flareEfficiency) { missile->SetTargetObject(this); missile->AddDeathDependence(this, DEPENDENCE_DECOYTARGET); } } } for (int a = 0; a < numSub; ++a) { subPos[a] += subSpeed[a]; subSpeed[a] *= 0.95f; subSpeed[a].y += mygravity; } } deleteMe |= (gs->frameNum >= deathFrame); }
void CProjectile::Update() { if (luaMoveCtrl) return; SetPosition(pos + speed); SetVelocityAndSpeed(speed + (UpVector * mygravity)); }
void CFlameProjectile::Collision() { const float3 norm = ground->GetNormal(pos.x, pos.z); const float ns = speed.dot(norm); SetVelocityAndSpeed(speed - (norm * ns)); SetPosition(pos + UpVector * 0.05f); curTime += 0.05f; }
void CGenericParticleProjectile::Update() { SetPosition(pos + speed); SetVelocityAndSpeed((speed + gravity) * airdrag); life += decayrate; size = size * sizeMod + sizeGrowth; deleteMe |= (life > 1.0f); }
void CPieceProjectile::Update() { if (!luaMoveCtrl) { SetVelocityAndSpeed((speed += (UpVector * mygravity)) * 0.997f); SetPosition(pos + speed); } spinParams.y += spinParams.x; age += 1; checkCol |= (age > 10); if ((explFlags & PF_NoCEGTrail) == 0) { // TODO: pass a more sensible ttl to the CEG (age-related?) explGenHandler->GenExplosion(cegID, pos, speed, 100, 0.0f, 0.0f, nullptr, nullptr); return; } if ((explFlags & PF_Fire) != 0) { for (int a = NUM_TRAIL_PARTS - 2; a >= 0; --a) { fireTrailPoints[a + 1] = fireTrailPoints[a]; } CMatrix44f m(pos); m.Rotate(spinParams.y * math::DEG_TO_RAD, spinVector); m.Translate(mix(modelPiece->mins, modelPiece->maxs, float3(guRNG.NextFloat(), guRNG.NextFloat(), guRNG.NextFloat()))); fireTrailPoints[0] = {m.GetPos(), 1.0f + guRNG.NextFloat()}; } if ((explFlags & PF_Smoke) != 0) { if (smokeTrail != nullptr) smokeTrail->UpdateEndPos(oldSmokePos = pos, oldSmokeDir = dir); if ((age % 8) == 0) { // need the temporary to avoid an undefined ref const unsigned int smokeTime = TRAIL_SMOKE_TIME; smokeTrail = projMemPool.alloc<CSmokeTrailProjectile>( owner(), projectileDrawer->smoketrailtex, pos, oldSmokePos, dir, oldSmokeDir, smokeTime, 14.0f, 0.5f, age == (NUM_TRAIL_PARTS - 1), false ); useAirLos = smokeTrail->useAirLos; } } }
int CExplosiveProjectile::ShieldRepulse(const float3& shieldPos, float shieldForce, float shieldMaxSpeed) { if (luaMoveCtrl) return 0; const float3 rdir = (pos - shieldPos).Normalize(); if (rdir.dot(speed) < shieldMaxSpeed) { SetVelocityAndSpeed(speed + (rdir * shieldForce)); return 2; } return 0; }
void CDirtProjectile::Update() { SetVelocityAndSpeed((speed * slowdown) + (UpVector * mygravity)); SetPosition(pos + speed); alpha = std::max(alpha - alphaFalloff, 0.0f); size += sizeExpansion; if (ground->GetApproximateHeight(pos.x, pos.z, false) - 40.0f > pos.y) { deleteMe = true; } if (alpha <= 0.0f) { deleteMe = true; } }
void CPieceProjectile::Collision() { Collision(nullptr, nullptr); if (gsRNG.NextFloat() < 0.666f) { // give it a small chance to `ground bounce` CProjectile::Collision(); return; } // ground bounce const float3& norm = CGround::GetNormal(pos.x, pos.z); const float ns = speed.dot(norm); SetVelocityAndSpeed(speed - (norm * ns * 1.6f)); SetPosition(pos + (norm * 0.1f)); }
void CWeaponProjectile::UpdateGroundBounce() { // projectile is not allowed to bounce on either surface if (!weaponDef->groundBounce && !weaponDef->waterBounce) return; // max bounce already reached? if ((bounces + 1) > weaponDef->numBounce) return; if (luaMoveCtrl) return; if (ttl <= 0) return; // water or ground bounce? float3 normal; bool bounced = false; const float distWaterHit = (pos.y > 0.0f && speed.y < 0.0f) ? (pos.y / -speed.y) : -1.0f; const bool intersectWater = (distWaterHit >= 0.0f) && (distWaterHit <= 1.0f); if (intersectWater && weaponDef->waterBounce) { pos += speed * distWaterHit; pos.y = 0.5f; normal = CGround::GetNormalAboveWater(pos.x, pos.z); bounced = true; } else { const float distGroundHit = CGround::LineGroundCol(pos, pos + speed); //TODO use traj one for traj weapons? const bool intersectGround = (distGroundHit >= 0.0f); if (intersectGround && weaponDef->groundBounce) { static const float dontTouchSurface = 0.99f; pos += dir * distGroundHit * dontTouchSurface; normal = CGround::GetNormal(pos.x, pos.z); bounced = true; } } if (!bounced) return; // spawn CEG before bouncing, otherwise we might be too // far up in the air if it has the (under)water flag set explGenHandler->GenExplosion(weaponDef->bounceExplosionGeneratorID, pos, normal, speed.w, 1.0f, 1.0f, owner(), NULL); ++bounces; const float dot = math::fabs(speed.dot(normal)); CWorldObject::SetVelocity(speed - (speed + normal * dot) * (1 - weaponDef->bounceSlip )); CWorldObject::SetVelocity( speed + normal * dot * (1 + weaponDef->bounceRebound)); SetVelocityAndSpeed(speed); }
void CFlameProjectile::Update() { if (!luaMoveCtrl) { SetPosition(pos + speed); UpdateGroundBounce(); SetVelocityAndSpeed(speed + spread); } UpdateInterception(); radius = radius + weaponDef->sizeGrowth; sqRadius = radius * radius; drawRadius = radius * weaponDef->collisionSize; curTime += invttl; checkCol &= (curTime <= physLife); curTime = std::min(curTime, 1.0f); deleteMe |= (curTime >= 1.0f); explGenHandler->GenExplosion(cegID, pos, speed, curTime, 0.0f, 0.0f, nullptr, nullptr); }
void CProjectile::Init(const CUnit* owner, const float3& offset) { if (owner != NULL) { // must be set before the AddProjectile call ownerID = owner->id; teamID = owner->team; allyteamID = teamHandler->IsValidTeam(teamID)? teamHandler->AllyTeam(teamID): -1; } if (!hitscan) { SetPosition(pos + offset); SetVelocityAndSpeed(speed); } if (!weapon && !piece) { // NOTE: // new CWeapon- and CPieceProjectile*'s add themselves // to CProjectileHandler (other code needs to be able // to dyna-cast CProjectile*'s to those derived types, // and adding them here would throw away too much RTTI) projectileHandler->AddProjectile(this); } if (synced && !weapon) { quadField->AddProjectile(this); } }
CWeaponProjectile::CWeaponProjectile(const ProjectileParams& params) : CProjectile(params.pos, params.speed, params.owner, true, true, false, false) , damages(nullptr) , weaponDef(params.weaponDef) , target(params.target) , ttl(params.ttl) , bounces(0) , targeted(false) , startPos(params.pos) , targetPos(params.end) { projectileType = WEAPON_BASE_PROJECTILE; assert(weaponDef != nullptr); if (weaponDef->IsHitScanWeapon()) { hitscan = true; // the else-case (default) is handled in CProjectile::Init // // ray projectiles must all set this to false because their collision // detection is handled by the weapons firing them, ProjectileHandler // will skip any tests for these checkCol = false; // type has not yet been set by derived ctor's at this point // useAirLos = (projectileType != WEAPON_LIGHTNING_PROJECTILE); useAirLos = true; // NOTE: // {BeamLaser, Lightning}Projectile's do NOT actually move (their // speed is never added to pos) and never alter their speed either // they additionally override our ::Update (so CProjectile::Update // is also never called) which means assigning speed a non-zerovec // value should have no side-effects SetPosition(startPos); SetVelocityAndSpeed(targetPos - startPos); // ProjectileDrawer vis-culls by pos == startPos, but we // want to see the beam even if camera is near targetPos // --> use full distance for drawRadius SetRadiusAndHeight((targetPos - startPos).Length(), 0.0f); } collisionFlags = weaponDef->collisionFlags; weaponNum = params.weaponNum; alwaysVisible = weaponDef->visuals.alwaysVisible; ignoreWater = weaponDef->waterweapon; CSolidObject* so = NULL; CWeaponProjectile* po = NULL; if ((so = dynamic_cast<CSolidObject*>(target)) != NULL) { AddDeathDependence(so, DEPENDENCE_WEAPONTARGET); } if ((po = dynamic_cast<CWeaponProjectile*>(target)) != NULL) { po->SetBeingIntercepted(po->IsBeingIntercepted() || weaponDef->interceptSolo); AddDeathDependence(po, DEPENDENCE_INTERCEPTTARGET); } if (params.model != NULL) { model = params.model; } else { model = weaponDef->LoadModel(); } if (params.owner == NULL) { // the else-case (default) is handled in CProjectile::Init ownerID = params.ownerID; teamID = params.teamID; allyteamID = teamHandler->IsValidTeam(teamID)? teamHandler->AllyTeam(teamID): -1; } if (ownerID != -1u && weaponNum != -1u) { const CUnit* owner = unitHandler->GetUnit(ownerID); if (owner != nullptr && weaponNum < owner->weapons.size()) { damages = DynDamageArray::IncRef(owner->weapons[weaponNum]->damages); } } if (damages == nullptr) damages = DynDamageArray::IncRef(&weaponDef->damages); if (params.cegID != -1u) { cegID = params.cegID; } else { cegID = weaponDef->ptrailExplosionGeneratorID; } // must happen after setting position and velocity projectileHandler->AddProjectile(this); quadField->AddProjectile(this); ASSERT_SYNCED(id); if (weaponDef->targetable) { interceptHandler.AddInterceptTarget(this, targetPos); } }
void CTorpedoProjectile::Update() { // tracking only works when we are underwater if (!weaponDef->submissile && pos.y > 0.0f) { if (!luaMoveCtrl) { // must update dir and speed.w here SetVelocityAndSpeed(speed + (UpVector * mygravity)); } } else { if (--ttl > 0) { if (!luaMoveCtrl) { float3 targetVel; if (speed.w < maxSpeed) speed.w += std::max(0.2f, tracking); if (target != NULL) { const CSolidObject* so = dynamic_cast<const CSolidObject*>(target); const CWeaponProjectile* po = dynamic_cast<const CWeaponProjectile*>(target); targetPos = target->pos; if (so != NULL) { targetPos = so->aimPos; targetVel = so->speed; if (allyteamID != -1 && pos.SqDistance(so->aimPos) > Square(150.0f)) { const CUnit* u = dynamic_cast<const CUnit*>(so); if (u != NULL) { targetPos = u->GetErrorPos(allyteamID, true); } } } if (po != NULL) { targetVel = po->speed; } } if (!weaponDef->submissile && targetPos.y > 0.0f) { targetPos.y = 0.0f; } const float3 targetLeadVec = targetVel * (pos.distance(targetPos) / maxSpeed) * 0.7f; const float3 targetLeadDir = (targetPos + targetLeadVec - pos).Normalize(); float3 targetDirDif = targetLeadDir - dir; if (targetDirDif.Length() < tracking) { dir = targetLeadDir; } else { // <tracking> is the projectile's turn-rate targetDirDif = (targetDirDif - (dir * targetDirDif.dot(dir))).SafeNormalize(); dir = (dir + (targetDirDif * tracking)).SafeNormalize(); } // do not need to update dir or speed.w here CWorldObject::SetVelocity(dir * speed.w); } explGenHandler->GenExplosion(cegID, pos, speed, ttl, areaOfEffect, 0.0f, NULL, NULL); } else { if (!luaMoveCtrl) { // must update dir and speed.w here SetVelocityAndSpeed((speed * 0.98f) + (UpVector * mygravity)); } } } if (!luaMoveCtrl) { SetPosition(pos + speed); } if (pos.y < -2.0f) { --nextBubble; if (nextBubble == 0) { nextBubble = 1 + (int) (gs->randFloat() * 1.5f); const float3 pspeed = (gs->randVector() * 0.1f) + float3(0.0f, 0.2f, 0.0f); new CBubbleProjectile( owner(), pos + gs->randVector(), pspeed, 40 + gs->randFloat() * GAME_SPEED, 1 + gs->randFloat() * 2, 0.01f, 0.3f + gs->randFloat() * 0.3f ); } } UpdateGroundBounce(); UpdateInterception(); }