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 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); }
float3 CBeamLaser::GetFireDir(bool sweepFire, bool scriptCall) { float3 dir = targetPos - weaponMuzzlePos; if (!sweepFire) { if (scriptCall) { dir = dir.SafeNormalize(); } else { if (onlyForward && owner->unitDef->IsStrafingAirUnit()) { // [?] StrafeAirMovetype cannot align itself properly, change back when that is fixed dir = owner->frontdir; } else { if (salvoLeft == salvoSize - 1) { oldDir = (dir = dir.SafeNormalize()); } else if (weaponDef->beamburst) { dir = dir.SafeNormalize(); } else { dir = oldDir; } } dir += SalvoErrorExperience(); dir.SafeNormalize(); // NOTE: // on units with (extremely) long weapon barrels the muzzle // can be on the far side of the target unit such that <dir> // would point away from it if ((targetPos - weaponMuzzlePos).dot(targetPos - owner->aimPos) < 0.0f) { dir = -dir; } } } else { // need to emit the sweeping beams from the right place // NOTE: // this way of implementing sweep-fire is extremely bugged // the intersection points traced by rays from the turning // weapon piece do NOT describe a fluid arc segment between // old and new target positions (nor even a straight line) // --> animation scripts cannot be relied upon to smoothly // vary pitch of the weapon muzzle piece so use workaround // dir = sweepFireState.GetSweepTempDir(); dir.Normalize2D(); const float3 tgtPos = float3(dir.x * sweepFireState.GetTargetDist2D(), 0.0f, dir.z * sweepFireState.GetTargetDist2D()); const float tgtHgt = CGround::GetHeightReal(weaponMuzzlePos.x + tgtPos.x, weaponMuzzlePos.z + tgtPos.z); // NOTE: INTENTIONALLY NOT NORMALIZED HERE dir = (tgtPos + UpVector * (tgtHgt - weaponMuzzlePos.y)); } return dir; }
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); }
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 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); }