void CBeamLaser::FireInternal(float3 dir, bool sweepFire) { float rangeMod = 1.0f; if (dynamic_cast<CBuilding*>(owner) == NULL) { // help units fire while chasing rangeMod = 1.3f; } if (owner->directControl) { rangeMod = 0.95f; } float maxLength = range * rangeMod; float curLength = 0.0f; float3 curPos = weaponMuzzlePos; float3 hitPos; dir += gs->randVector() * sprayAngle * (1 - owner->limExperience * 0.7f); dir.ANormalize(); bool tryAgain = true; // unit at the end of the beam CUnit* hit = 0; // increase range if targets are searched for in a cylinder if (cylinderTargetting > 0.01f) { // const float3 up(0, owner->radius*cylinderTargetting, 0); // const float uplen = up.dot(dir); const float uplen = owner->radius * cylinderTargetting * dir.y; maxLength = streflop::sqrtf(maxLength * maxLength + uplen * uplen); } // increase range if targetting edge of hitsphere if (targetType == Target_Unit && targetUnit && targetBorder != 0) { maxLength += targetUnit->radius * targetBorder; } for (int tries = 0; tries < 5 && tryAgain; ++tries) { tryAgain = false; hit = 0; float length = helper->TraceRay( curPos, dir, maxLength - curLength, weaponDef->damages[0], owner, hit, collisionFlags ); if (hit && hit->allyteam == owner->allyteam && sweepFire) { // never damage friendlies with sweepfire lastFireFrame = 0; return; } float3 newDir; CPlasmaRepulser* shieldHit = 0; const float shieldLength = interceptHandler.AddShieldInterceptableBeam(this, curPos, dir, length, newDir, shieldHit); if (shieldLength < length) { length = shieldLength; if (shieldHit->BeamIntercepted(this, damageMul)) { // repulsed tryAgain = true; } } 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, weaponDef->visuals.color2, owner, weaponDef->thickness, weaponDef->corethickness, weaponDef->laserflaresize, weaponDef, weaponDef->visuals.beamttl, weaponDef->visuals.beamdecay ); } curPos = hitPos; curLength += length; dir = newDir; } // fix negative damage when hitting big spheres float actualRange = range; if (hit) { if (hit->unitDef->usePieceCollisionVolumes) { // getting the actual piece here is probably overdoing it hit->SetLastAttackedPiece(hit->localmodel->pieces[0], gs->frameNum); } if (targetBorder > 0) { actualRange += hit->radius * targetBorder; } } // make it possible to always hit with some minimal intensity (melee weapons have use for that) const float intensity = 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 ); } helper->Explosion( hitPos, weaponDef->dynDamageExp > 0? dynDamages * (intensity * damageMul): weaponDef->damages * (intensity * damageMul), areaOfEffect, weaponDef->edgeEffectiveness, weaponDef->explosionSpeed, owner, true, 1.0f, weaponDef->noExplode || weaponDef->noSelfDamage, /*false*/ weaponDef->impactOnly, /*false*/ weaponDef->explosionGenerator, hit, dir, weaponDef->id ); } if (targetUnit) { lastFireFrame = gs->frameNum; } }