void CCannon::FireImpl(const bool scriptCall) { float3 diff = currentTargetPos - weaponMuzzlePos; float3 dir = (diff.SqLength() > 4.0f) ? GetWantedDir(diff) : diff; // prevent vertical aim when emit-sfx firing the weapon dir += (gsRNG.NextVector() * SprayAngleExperience() + SalvoErrorExperience()); dir.SafeNormalize(); int ttl = 0; const float sqSpeed2D = dir.SqLength2D() * projectileSpeed * projectileSpeed; const int predict = math::ceil((sqSpeed2D == 0.0f) ? (-2.0f * projectileSpeed * dir.y / gravity): math::sqrt(diff.SqLength2D() / sqSpeed2D)); if (weaponDef->flighttime > 0) { ttl = weaponDef->flighttime; } else if (weaponDef->selfExplode) { ttl = (predict + gsRNG.NextFloat() * 2.5f - 0.5f); } else if ((weaponDef->groundBounce || weaponDef->waterBounce) && weaponDef->numBounce > 0) { ttl = (predict * (1 + weaponDef->numBounce * weaponDef->bounceRebound)); } else { ttl = predict * 2; } ProjectileParams params = GetProjectileParams(); params.pos = weaponMuzzlePos; params.end = currentTargetPos; params.speed = dir * projectileSpeed; params.ttl = ttl; params.gravity = gravity; WeaponProjectileFactory::LoadProjectile(params); }
void CMissileLauncher::FireImpl(bool scriptCall) { float3 dir = targetPos - weaponMuzzlePos; const float dist = dir.LengthNormalize(); if (onlyForward) { dir = owner->frontdir; } else if (weaponDef->fixedLauncher) { dir = weaponDir; } else if (weaponDef->trajectoryHeight > 0.0f) { dir.y += weaponDef->trajectoryHeight; dir.Normalize(); } dir += (gs->randVector() * SprayAngleExperience() + SalvoErrorExperience()); dir.Normalize(); float3 startSpeed = dir * weaponDef->startvelocity; // NOTE: why only for SAMT units? if (onlyForward && owner->unitDef->IsStrafingAirUnit()) startSpeed += owner->speed; ProjectileParams params = GetProjectileParams(); params.pos = weaponMuzzlePos; params.end = targetPos; params.speed = startSpeed; params.ttl = weaponDef->flighttime == 0? std::ceil(std::max(dist, range) / projectileSpeed + 25 * weaponDef->selfExplode): weaponDef->flighttime; WeaponProjectileFactory::LoadProjectile(params); }
void CDGunWeapon::FireImpl(const bool scriptCall) { float3 dir = wantedDir; dir += (gsRNG.NextVector() * SprayAngleExperience() + SalvoErrorExperience()); dir.Normalize(); ProjectileParams params = GetProjectileParams(); params.pos = weaponMuzzlePos; params.end = currentTargetPos; params.speed = dir * projectileSpeed; params.ttl = 1; WeaponProjectileFactory::LoadProjectile(params); }
void CStarburstLauncher::FireImpl(bool scriptCall) { const float3 speed = ((weaponDef->fixedLauncher)? weaponDir: UpVector) * weaponDef->startvelocity; const float3 aimError = (gs->randVector() * SprayAngleExperience() + SalvoErrorExperience()); ProjectileParams params = GetProjectileParams(); params.pos = weaponMuzzlePos + UpVector * 2.0f; params.end = targetPos; params.speed = speed; params.error = aimError; params.ttl = 200; //??? params.tracking = tracking; params.maxRange = (weaponDef->flighttime > 0 || weaponDef->fixedLauncher)? MAX_PROJECTILE_RANGE: range; WeaponProjectileFactory::LoadProjectile(params); }
void CFlameThrower::FireImpl(bool scriptCall) { float3 dir = targetPos - weaponMuzzlePos; const float dist = dir.LengthNormalize(); const float3 spread = (gs->randVector() * SprayAngleExperience() + SalvoErrorExperience()) - (dir * 0.001f); ProjectileParams params = GetProjectileParams(); params.pos = weaponMuzzlePos; params.speed = dir * projectileSpeed; params.spread = spread; params.ttl = std::ceil(std::max(dist, range) / projectileSpeed * weaponDef->duration); WeaponProjectileFactory::LoadProjectile(params); }
bool CCannon::HaveFreeLineOfFire(const float3& pos, bool userTarget, const CUnit* unit) const { // assume we can still fire at partially submerged targets if (!weaponDef->waterweapon && TargetUnitOrPositionUnderWater(pos, unit)) return false; if (projectileSpeed == 0) { return true; } float3 dif(pos - weaponMuzzlePos); float3 dir(GetWantedDir2(dif)); if (dir.SqLength() == 0) { return false; } float3 flatDir(dif.x, 0, dif.z); float flatLength = flatDir.Length(); if (flatLength == 0) { return true; } flatDir /= flatLength; const float linear = dir.y; const float quadratic = gravity / (projectileSpeed * projectileSpeed) * 0.5f; const float groundDist = ((avoidFlags & Collision::NOGROUND) == 0)? CGround::TrajectoryGroundCol(weaponMuzzlePos, flatDir, flatLength - 10, linear, quadratic): -1.0f; const float spread = (AccuracyExperience() + SprayAngleExperience()) * 0.6f * 0.9f; if (groundDist > 0.0f) { return false; } //FIXME add a forcedUserTarget (a forced fire mode enabled with meta key or something) and skip the test below then if (TraceRay::TestTrajectoryCone(weaponMuzzlePos, flatDir, flatLength, dir.y, quadratic, spread, owner->allyteam, avoidFlags, owner)) { return false; } return true; }
void CEmgCannon::FireImpl(const bool scriptCall) { float3 dir = currentTargetPos - weaponMuzzlePos; const float dist = dir.LengthNormalize(); if (onlyForward && owner->unitDef->IsStrafingAirUnit()) { // [?] StrafeAirMoveType cannot align itself properly, change back when that is fixed dir = owner->frontdir; } dir += (gs->randVector() * SprayAngleExperience() + SalvoErrorExperience()); dir.Normalize(); ProjectileParams params = GetProjectileParams(); params.pos = weaponMuzzlePos; params.speed = dir * projectileSpeed; params.ttl = std::ceil(std::max(dist, range) / projectileSpeed); WeaponProjectileFactory::LoadProjectile(params); }
void CBeamLaser::FireInternal(float3 curDir) { float actualRange = range; float rangeMod = 1.0f; if (!owner->unitDef->IsImmobileUnit()) { // help units fire while chasing rangeMod = 1.3f; } if (owner->UnderFirstPersonControl()) { rangeMod = 0.95f; } bool tryAgain = true; bool doDamage = true; float maxLength = range * rangeMod; float curLength = 0.0f; float3 curPos = weaponMuzzlePos; float3 hitPos; float3 newDir; // objects at the end of the beam CUnit* hitUnit = NULL; CFeature* hitFeature = NULL; CPlasmaRepulser* hitShield = NULL; CollisionQuery hitColQuery; if (!sweepFireState.IsSweepFiring()) { curDir += (gs->randVector() * SprayAngleExperience()); curDir.SafeNormalize(); // increase range if targets are searched for in a cylinder if (cylinderTargeting > 0.01f) { const float verticalDist = owner->radius * cylinderTargeting * curDir.y; const float maxLengthModSq = maxLength * maxLength + verticalDist * verticalDist; maxLength = math::sqrt(maxLengthModSq); } // adjust range if targetting edge of hitsphere if (targetType == Target_Unit && targetUnit != NULL && targetBorder != 0.0f) { maxLength += (targetUnit->radius * targetBorder); } } else { // restrict the range when sweeping maxLength = std::min(maxLength, sweepFireState.GetTargetDist3D() * 1.125f); } for (int tries = 0; tries < 5 && tryAgain; ++tries) { float beamLength = TraceRay::TraceRay(curPos, curDir, maxLength - curLength, collisionFlags, owner, hitUnit, hitFeature, &hitColQuery); if (hitUnit != NULL && teamHandler->AlliedTeams(hitUnit->team, owner->team)) { if (sweepFireState.IsSweepFiring() && !sweepFireState.DamageAllies()) { doDamage = false; break; } } if (!weaponDef->waterweapon) { // terminate beam at water surface if necessary if ((curDir.y < 0.0f) && ((curPos.y + curDir.y * beamLength) <= 0.0f)) { beamLength = curPos.y / -curDir.y; } } // if the beam gets intercepted, this modifies newDir // // we do more than one trace-iteration and set dir to // newDir only in the case there is a shield in our way const float shieldLength = interceptHandler.AddShieldInterceptableBeam(this, curPos, curDir, beamLength, newDir, hitShield); if (shieldLength < beamLength) { beamLength = shieldLength; tryAgain = hitShield->BeamIntercepted(this, salvoDamageMult); } else { tryAgain = false; } // same as hitColQuery.GetHitPos() if no water or shield in way hitPos = curPos + curDir * beamLength; { const float baseAlpha = weaponDef->intensity * 255.0f; const float startAlpha = (1.0f - (curLength ) / maxLength); const float endAlpha = (1.0f - (curLength + beamLength) / maxLength); ProjectileParams pparams = GetProjectileParams(); pparams.pos = curPos; pparams.end = hitPos; pparams.ttl = weaponDef->beamLaserTTL; pparams.startAlpha = Clamp(startAlpha * baseAlpha, 0.0f, 255.0f); pparams.endAlpha = Clamp(endAlpha * baseAlpha, 0.0f, 255.0f); WeaponProjectileFactory::LoadProjectile(pparams); } curPos = hitPos; curDir = newDir; curLength += beamLength; } if (!doDamage) return; if (hitUnit != NULL) { hitUnit->SetLastAttackedPiece(hitColQuery.GetHitPiece(), gs->frameNum); if (targetBorder > 0.0f) { actualRange += (hitUnit->radius * targetBorder); } } if (curLength < maxLength) { const DamageArray& baseDamages = (weaponDef->dynDamageExp <= 0.0f)? weaponDef->damages: weaponDefHandler->DynamicDamages( weaponDef->damages, weaponMuzzlePos, curPos, (weaponDef->dynDamageRange > 0.0f)? weaponDef->dynDamageRange: weaponDef->range, weaponDef->dynDamageExp, weaponDef->dynDamageMin, weaponDef->dynDamageInverted ); // make it possible to always hit with some minimal intensity (melee weapons have use for that) const float hitIntensity = std::max(minIntensity, 1.0f - curLength / (actualRange * 2.0f)); const DamageArray damages = baseDamages * (hitIntensity * salvoDamageMult); const CGameHelper::ExplosionParams params = { hitPos, curDir, damages, weaponDef, owner, hitUnit, hitFeature, craterAreaOfEffect, damageAreaOfEffect, weaponDef->edgeEffectiveness, weaponDef->explosionSpeed, 1.0f, // gfxMod weaponDef->impactOnly, weaponDef->noExplode || weaponDef->noSelfDamage, // ignoreOwner true, // damageGround -1u // projectileID }; helper->Explosion(params); } }
void CLightningCannon::FireImpl(bool scriptCall) { float3 curPos = weaponMuzzlePos; float3 curDir = (targetPos - curPos).Normalize(); float3 newDir = curDir; curDir += (gs->randVector() * SprayAngleExperience() + SalvoErrorExperience()); curDir.Normalize(); CUnit* hitUnit = NULL; CFeature* hitFeature = NULL; CPlasmaRepulser* hitShield = NULL; CollisionQuery hitColQuery; float boltLength = TraceRay::TraceRay(curPos, curDir, range, collisionFlags, owner, hitUnit, hitFeature, &hitColQuery); if (!weaponDef->waterweapon) { // terminate bolt at water surface if necessary if ((curDir.y < 0.0f) && ((curPos.y + curDir.y * boltLength) <= 0.0f)) { boltLength = curPos.y / -curDir.y; } } const float shieldLength = interceptHandler.AddShieldInterceptableBeam(this, curPos, curDir, range, newDir, hitShield); if (shieldLength < boltLength) { boltLength = shieldLength; hitShield->BeamIntercepted(this); } if (hitUnit != NULL) { hitUnit->SetLastAttackedPiece(hitColQuery.GetHitPiece(), gs->frameNum); } const DamageArray& damageArray = (weaponDef->dynDamageExp <= 0.0f)? weaponDef->damages: weaponDefHandler->DynamicDamages( weaponDef->damages, weaponMuzzlePos, targetPos, (weaponDef->dynDamageRange > 0.0f)? weaponDef->dynDamageRange: weaponDef->range, weaponDef->dynDamageExp, weaponDef->dynDamageMin, weaponDef->dynDamageInverted ); const CGameHelper::ExplosionParams params = { curPos + curDir * boltLength, // hitPos (same as hitColQuery.GetHitPos() if no water or shield in way) curDir, damageArray, weaponDef, owner, hitUnit, hitFeature, craterAreaOfEffect, damageAreaOfEffect, weaponDef->edgeEffectiveness, weaponDef->explosionSpeed, 0.5f, // gfxMod weaponDef->impactOnly, weaponDef->noExplode || weaponDef->noSelfDamage, // ignoreOwner false, // damageGround -1u // projectileID }; helper->Explosion(params); ProjectileParams pparams = GetProjectileParams(); pparams.pos = curPos; pparams.end = curPos + curDir * (boltLength + 10.0f); pparams.ttl = 10; WeaponProjectileFactory::LoadProjectile(pparams); }
void CLightningCannon::FireImpl(const bool scriptCall) { float3 curPos = weaponMuzzlePos; float3 curDir = (currentTargetPos - weaponMuzzlePos).SafeNormalize(); curDir += (gs->randVector() * SprayAngleExperience() + SalvoErrorExperience()); curDir.Normalize(); CUnit* hitUnit = NULL; CFeature* hitFeature = NULL; CollisionQuery hitColQuery; float boltLength = TraceRay::TraceRay(curPos, curDir, range, collisionFlags, owner, hitUnit, hitFeature, &hitColQuery); if (!weaponDef->waterweapon) { // terminate bolt at water surface if necessary if ((curDir.y < 0.0f) && ((curPos.y + curDir.y * boltLength) <= 0.0f)) { boltLength = curPos.y / -curDir.y; hitUnit = NULL; hitFeature = NULL; } } static std::vector<TraceRay::SShieldDist> hitShields; hitShields.clear(); TraceRay::TraceRayShields(this, curPos, curDir, range, hitShields); for (const TraceRay::SShieldDist& sd: hitShields) { if(sd.dist < boltLength && sd.rep->IncomingBeam(this, curPos)) { boltLength = sd.dist; hitUnit = NULL; hitFeature = NULL; break; } } if (hitUnit != NULL) { hitUnit->SetLastHitPiece(hitColQuery.GetHitPiece(), gs->frameNum); } const DamageArray& damageArray = damages->GetDynamicDamages(weaponMuzzlePos, currentTargetPos); const CGameHelper::ExplosionParams params = { curPos + curDir * boltLength, // hitPos (same as hitColQuery.GetHitPos() if no water or shield in way) curDir, damageArray, weaponDef, owner, hitUnit, hitFeature, damages->craterAreaOfEffect, damages->damageAreaOfEffect, damages->edgeEffectiveness, damages->explosionSpeed, 0.5f, // gfxMod weaponDef->impactOnly, weaponDef->noExplode || weaponDef->noSelfDamage, // ignoreOwner false, // damageGround -1u // projectileID }; helper->Explosion(params); ProjectileParams pparams = GetProjectileParams(); pparams.pos = curPos; pparams.end = curPos + curDir * (boltLength + 10.0f); pparams.ttl = weaponDef->beamLaserTTL; WeaponProjectileFactory::LoadProjectile(pparams); }