void CWeaponProjectile::Explode( CUnit* hitUnit, CFeature* hitFeature, float3 impactPos, float3 impactDir ) { const DamageArray& damageArray = damages->GetDynamicDamages(startPos, impactPos); const CExplosionParams params = { impactPos, impactDir.SafeNormalize(), damageArray, weaponDef, owner(), hitUnit, hitFeature, damages->craterAreaOfEffect, damages->damageAreaOfEffect, damages->edgeEffectiveness, damages->explosionSpeed, weaponDef->noExplode? 0.3f: 1.0f, // gfxMod weaponDef->impactOnly, weaponDef->noExplode || weaponDef->noSelfDamage, // ignoreOwner true, // damgeGround static_cast<unsigned int>(id) }; helper->Explosion(params); if (!weaponDef->noExplode || TraveledRange()) { // remove ourselves from the simulation (otherwise // keep traveling and generating more explosions) CProjectile::Collision(); } }
bool CWeapon::AdjustTargetVectorLength( CUnit* targetUnit, float3& targetPos, float3& targetVec, float3& targetDir) const { bool retCode = false; const float tbScale = math::fabsf(targetBorder); CollisionVolume* cvOld = targetUnit->collisionVolume; CollisionVolume cvNew = CollisionVolume(targetUnit->collisionVolume); CollisionQuery cq; // test for "collision" with a temporarily volume // (scaled uniformly by the absolute target-border // factor) cvNew.RescaleAxes(tbScale, tbScale, tbScale); cvNew.SetTestType(CollisionVolume::COLVOL_HITTEST_DISC); targetUnit->collisionVolume = &cvNew; if (CCollisionHandler::DetectHit(targetUnit, weaponMuzzlePos, ZeroVector, NULL)) { // our weapon muzzle is inside the target unit's volume; this // means we do not need to make any adjustments to targetVec targetVec = ZeroVector; } else { targetDir.SafeNormalize(); // otherwise, perform a raytrace to find the proper length correction // factor for non-spherical coldet volumes based on the ray's ingress // (for positive TB values) or egress (for negative TB values) position; // this either increases or decreases the length of <targetVec> but does // not change its direction cvNew.SetTestType(CollisionVolume::COLVOL_HITTEST_CONT); // make the ray-segment long enough so it can reach the far side of the // scaled collision volume (helps to ensure a ray-intersection is found) // // note: ray-intersection is NOT guaranteed if the volume itself has a // non-zero offset, since here we are "shooting" at the target UNIT's // midpoint const float3 targetOffset = targetDir * (cvNew.GetBoundingRadius() * 2.0f); const float3 targetRayPos = targetPos + targetOffset; if (CCollisionHandler::DetectHit(targetUnit, weaponMuzzlePos, targetRayPos, &cq)) { if (targetBorder > 0.0f) { targetVec -= (targetDir * ((targetPos - cq.p0).Length())); } if (targetBorder < 0.0f) { targetVec += (targetDir * ((cq.p1 - targetPos).Length())); } } retCode = true; } targetUnit->collisionVolume = cvOld; // true indicates we took the else-branch and targetDir is now normalized return retCode; }
void CBeamLaser::FireInternal(float3 dir, bool sweepFire) { // fix negative damage when hitting big spheres float actualRange = range; float rangeMod = 1.0f; if (dynamic_cast<CBuilding*>(owner) == NULL) { // help units fire while chasing rangeMod = 1.3f; } if (owner->fpsControlPlayer != NULL) { rangeMod = 0.95f; } float maxLength = range * rangeMod; float curLength = 0.0f; float3 curPos = weaponMuzzlePos; float3 hitPos; dir += ((gs->randVector() * sprayAngle * (1.0f - owner->limExperience * weaponDef->ownerExpAccWeight))); dir.SafeNormalize(); bool tryAgain = true; // increase range if targets are searched for in a cylinder if (cylinderTargetting > 0.01f) { const float verticalDist = owner->radius * cylinderTargetting * dir.y; const float maxLengthModSq = maxLength * maxLength + verticalDist * verticalDist; maxLength = math::sqrt(maxLengthModSq); } // increase range if targetting edge of hitsphere if (targetType == Target_Unit && targetUnit && targetBorder != 0) { maxLength += (targetUnit->radius * targetBorder); } // unit at the end of the beam CUnit* hitUnit = NULL; CFeature* hitFeature = NULL; CPlasmaRepulser* hitShield = NULL; for (int tries = 0; tries < 5 && tryAgain; ++tries) { tryAgain = false; float length = TraceRay::TraceRay(curPos, dir, maxLength - curLength, collisionFlags, owner, hitUnit, hitFeature); if (hitUnit && teamHandler->AlliedTeams(hitUnit->team, owner->team) && sweepFire) { // never damage friendlies with sweepfire lastFireFrame = 0; return; } float3 newDir; const float shieldLength = interceptHandler.AddShieldInterceptableBeam(this, curPos, dir, length, newDir, hitShield); if (shieldLength < length) { length = shieldLength; tryAgain = hitShield->BeamIntercepted(this, damageMul); // repulsed } hitPos = curPos + dir * length; const float baseAlpha = weaponDef->intensity * 255.0f; const float startAlpha = (1.0f - (curLength ) / (range * 1.3f)) * baseAlpha; const float endAlpha = (1.0f - (curLength + length) / (range * 1.3f)) * baseAlpha; if (weaponDef->largeBeamLaser) { new CLargeBeamLaserProjectile(curPos, hitPos, color, weaponDef->visuals.color2, owner, weaponDef); } else { new CBeamLaserProjectile(curPos, hitPos, startAlpha, endAlpha, color, owner, weaponDef); } curPos = hitPos; curLength += length; dir = newDir; } if (hitUnit) { if (hitUnit->unitDef->usePieceCollisionVolumes) { // getting the actual piece here is probably overdoing it // TODO change this if we really need propper flanking bonus support // for beam-lasers hitUnit->SetLastAttackedPiece(hitUnit->localmodel->GetRoot(), gs->frameNum); } if (targetBorder > 0) { actualRange += hitUnit->radius * targetBorder; } } // 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)); if (curLength < maxLength) { // Dynamic Damage DamageArray dynDamages; if (weaponDef->dynDamageExp > 0) { dynDamages = weaponDefHandler->DynamicDamages( weaponDef->damages, weaponMuzzlePos, curPos, weaponDef->dynDamageRange > 0? weaponDef->dynDamageRange: weaponDef->range, weaponDef->dynDamageExp, weaponDef->dynDamageMin, weaponDef->dynDamageInverted ); } DamageArray damageArray = weaponDef->dynDamageExp > 0? dynDamages * (hitIntensity * damageMul): weaponDef->damages * (hitIntensity * damageMul); CGameHelper::ExplosionParams params = { hitPos, dir, damageArray, weaponDef, owner, hitUnit, hitFeature, areaOfEffect, weaponDef->edgeEffectiveness, weaponDef->explosionSpeed, 1.0f, // gfxMod weaponDef->impactOnly, weaponDef->noExplode || weaponDef->noSelfDamage, // ignoreOwner true // damageGround }; helper->Explosion(params); } if (targetUnit) { lastFireFrame = gs->frameNum; } }
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); } }