void CBombDropper::FireImpl() { if (targetType == Target_Unit) { // aim at base of unit instead of middle and ignore uncertainity targetPos = targetUnit->pos; } if (dropTorpedoes) { float3 speed = owner->speed; if (dynamic_cast<CHoverAirMoveType*>(owner->moveType)) { speed = (targetPos - weaponPos).Normalize(); speed *= 5.0f; } int ttl = weaponDef->flighttime; if (ttl == 0) { ttl = int((range / projectileSpeed) + 15 + predict); } ProjectileParams params = GetProjectileParams(); params.pos = weaponPos; params.speed = speed; params.ttl = ttl; params.tracking = tracking; assert(weaponDef->projectileType == WEAPON_TORPEDO_PROJECTILE); WeaponProjectileFactory::LoadProjectile(params); } else { // fudge a bit better lateral aim to compensate for imprecise aircraft steering float3 dif = targetPos-weaponPos; dif.y = 0; float3 dir = owner->speed; dir.SafeNormalize(); // add a random spray dir += (gs->randVector() * sprayAngle+salvoError) * (1 - (owner->limExperience * 0.9f)); dir.y = std::min(0.0f, dir.y); dir.SafeNormalize(); dif -= dir * dif.dot(dir); dif /= std::max(0.01f,predict); const float size = dif.Length(); if (size > 1.0f) { dif /= size * 1.0f; } ProjectileParams params = GetProjectileParams(); params.pos = weaponPos; params.speed = owner->speed + dif; params.ttl = 1000; params.gravity = (weaponDef->myGravity == 0)? mapInfo->map.gravity: -weaponDef->myGravity; assert(weaponDef->projectileType == WEAPON_EXPLOSIVE_PROJECTILE); 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); }
void CCannon::FireImpl() { float3 diff = targetPos - weaponMuzzlePos; float3 dir = (diff.SqLength() > 4.0) ? GetWantedDir(diff) : diff; // prevent vertical aim when emit-sfx firing the weapon dir += (gs->randVector() * sprayAngle + salvoError) * (1.0f - owner->limExperience * weaponDef->ownerExpAccWeight); dir.SafeNormalize(); int ttl = 0; float sqSpeed2D = dir.SqLength2D() * projectileSpeed * projectileSpeed; int predict = (int)math::ceil((sqSpeed2D == 0) ? (-2 * projectileSpeed * dir.y / gravity) : math::sqrt(diff.SqLength2D() / sqSpeed2D)); if(weaponDef->flighttime > 0) { ttl = weaponDef->flighttime; } else if(selfExplode) { ttl = (int)(predict + gs->randFloat() * 2.5f - 0.5f); } else if((weaponDef->groundBounce || weaponDef->waterBounce) && weaponDef->numBounce > 0) { ttl = (int)(predict * (1 + weaponDef->numBounce * weaponDef->bounceRebound)); } else { ttl=predict*2; } ProjectileParams params = GetProjectileParams(); params.pos = weaponMuzzlePos; params.speed = dir * projectileSpeed; params.ttl = ttl; new CExplosiveProjectile(params, damageAreaOfEffect, gravity); }
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); }
void CTorpedoLauncher::FireImpl() { float3 dir = (targetPos - weaponMuzzlePos).Normalize(); if (weaponDef->trajectoryHeight > 0) { dir.y += weaponDef->trajectoryHeight; dir.Normalize(); } ProjectileParams params = GetProjectileParams(); params.speed = ((weaponDef->fixedLauncher)? weaponDir: dir) * weaponDef->startvelocity; params.pos = weaponMuzzlePos; params.end = targetPos; params.ttl = (weaponDef->flighttime == 0)? (int) (range / projectileSpeed + 25): weaponDef->flighttime; params.tracking = tracking; 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 CTorpedoLauncher::FireImpl(const bool scriptCall) { float3 dir = currentTargetPos - weaponMuzzlePos; float3 vel; const float dist = dir.LengthNormalize(); if (weaponDef->fixedLauncher) { vel = weaponDir * weaponDef->startvelocity; } else { dir += (UpVector * std::max(0.0f, weaponDef->trajectoryHeight)); vel = dir.Normalize() * weaponDef->startvelocity; } ProjectileParams params = GetProjectileParams(); params.speed = vel; params.pos = weaponMuzzlePos; params.end = currentTargetPos; params.ttl = (weaponDef->flighttime == 0)? std::ceil(std::max(dist, range) / projectileSpeed + 25): weaponDef->flighttime; params.tracking = tracking; 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() { float3 curPos = weaponMuzzlePos; float3 hitPos; float3 curDir = (targetPos - curPos).Normalize(); float3 newDir = curDir; curDir += (gs->randVector() * sprayAngle + salvoError) * (1.0f - owner->limExperience * weaponDef->ownerExpAccWeight); curDir.Normalize(); CUnit* hitUnit = NULL; CFeature* hitFeature = NULL; CPlasmaRepulser* hitShield = NULL; float boltLength = TraceRay::TraceRay(curPos, curDir, range, collisionFlags, owner, hitUnit, hitFeature); 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); } hitPos = curPos + curDir * boltLength; if (hitUnit != NULL) { hitUnit->SetLastAttackedPiece(hitUnit->localModel->GetRoot(), 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 = { hitPos, curDir, damageArray, weaponDef, owner, hitUnit, hitFeature, craterAreaOfEffect, damageAreaOfEffect, weaponDef->edgeEffectiveness, weaponDef->explosionSpeed, 0.5f, // gfxMod weaponDef->impactOnly, weaponDef->noExplode || weaponDef->noSelfDamage, // ignoreOwner false // damageGround }; helper->Explosion(params); ProjectileParams pparams = GetProjectileParams(); pparams.pos = curPos; pparams.end = curPos + curDir * (boltLength + 10.0f); pparams.ttl = 10; new CLightningProjectile(pparams, color); }
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); }