void CGameHelper::Explosion(const ExplosionParams& params) {
	const float3& pos = params.pos;
	const float3& dir = params.dir;
	const DamageArray& damages = params.damages;

	// if weaponDef is NULL, this is a piece-explosion
	// (implicit damage-type -DAMAGE_EXPLOSION_DEBRIS)
	const WeaponDef* weaponDef = params.weaponDef;
	const int weaponDefID = (weaponDef != NULL)? weaponDef->id: -CSolidObject::DAMAGE_EXPLOSION_DEBRIS;

	const float3 expPos = pos;

	CUnit* owner = params.owner;
	CUnit* hitUnit = params.hitUnit;
	CFeature* hitFeature = params.hitFeature;

	const float craterAOE = std::max(1.0f, params.craterAreaOfEffect);
	const float damageAOE = std::max(1.0f, params.damageAreaOfEffect);
	const float edgeEffectiveness = params.edgeEffectiveness;
	const float expSpeed = params.explosionSpeed;
	const float gfxMod = params.gfxMod;
	const float realHeight = ground->GetHeightReal(expPos.x, expPos.z);
	const float altitude = expPos.y - realHeight;

	const bool impactOnly = params.impactOnly;
	const bool ignoreOwner = params.ignoreOwner;
	const bool damageGround = params.damageGround;
	const bool noGfx = eventHandler.Explosion(weaponDefID, expPos, owner);

	if (luaUI) {
		if (weaponDef != NULL && weaponDef->cameraShake > 0.0f) {
			luaUI->ShockFront(weaponDef->cameraShake, expPos, damageAOE);
		}
	}

	if (impactOnly) {
		if (hitUnit) {
			DoExplosionDamage(hitUnit, owner, expPos, damageAOE, expSpeed, edgeEffectiveness, ignoreOwner, damages, weaponDefID);
		} else if (hitFeature) {
			DoExplosionDamage(hitFeature, expPos, damageAOE, damages, weaponDefID);
		}
	} else {
		{
			// damage all units within the explosion radius
			const vector<CUnit*>& units = qf->GetUnitsExact(expPos, damageAOE);
			bool hitUnitDamaged = false;

			for (vector<CUnit*>::const_iterator ui = units.begin(); ui != units.end(); ++ui) {
				CUnit* unit = *ui;

				if (unit == hitUnit) {
					hitUnitDamaged = true;
				}

				DoExplosionDamage(unit, owner, expPos, damageAOE, expSpeed, edgeEffectiveness, ignoreOwner, damages, weaponDefID);
			}

			// HACK: for a unit with an offset coldet volume, the explosion
			// (from an impacting projectile) position might not correspond
			// to its quadfield position so we need to damage it separately
			if (hitUnit != NULL && !hitUnitDamaged) {
				DoExplosionDamage(hitUnit, owner, expPos, damageAOE, expSpeed, edgeEffectiveness, ignoreOwner, damages, weaponDefID);
			}
		}

		{
			// damage all features within the explosion radius
			const vector<CFeature*>& features = qf->GetFeaturesExact(expPos, damageAOE);
			bool hitFeatureDamaged = false;

			for (vector<CFeature*>::const_iterator fi = features.begin(); fi != features.end(); ++fi) {
				CFeature* feature = *fi;

				if (feature == hitFeature) {
					hitFeatureDamaged = true;
				}

				DoExplosionDamage(feature, expPos, damageAOE, damages, weaponDefID);
			}

			if (hitFeature != NULL && !hitFeatureDamaged) {
				DoExplosionDamage(hitFeature, expPos, damageAOE, damages, weaponDefID);
			}
		}

		// deform the map if the explosion was above-ground
		// (but had large enough radius to touch the ground)
		if (altitude >= -1.0f) {
			if (damageGround && !mapDamage->disabled && (craterAOE > altitude) && (damages.craterMult > 0.0f)) {
				// limit the depth somewhat
				const float craterDepth = damages.GetDefaultDamage() * (1.0f - (altitude / craterAOE));
				const float damageDepth = std::min(craterAOE * 10.0f, craterDepth);
				const float craterStrength = (damageDepth + damages.craterBoost) * damages.craterMult;
				const float craterRadius = craterAOE - altitude;

				mapDamage->Explosion(expPos, craterStrength, craterRadius);
			}
		}
	}

	if (!noGfx) {
		// use CStdExplosionGenerator by default
		IExplosionGenerator* explosionGenerator = stdExplosionGenerator;

		if (weaponDef != NULL && weaponDef->explosionGenerator != NULL) {
			explosionGenerator = weaponDef->explosionGenerator;
		}

		explosionGenerator->Explosion(0, expPos, damages.GetDefaultDamage(), damageAOE, owner, gfxMod, hitUnit, dir);
	}

	CExplosionEvent explosionEvent(expPos, damages.GetDefaultDamage(), damageAOE, weaponDef);
	FireExplosionEvent(explosionEvent);
}
Example #2
0
void CGameHelper::Explosion(
	float3 expPos, const DamageArray& damages,
	float expRad, float edgeEffectiveness,
	float expSpeed, CUnit* owner,
	bool damageGround, float gfxMod,
	bool ignoreOwner, bool impactOnly,
	IExplosionGenerator* explosionGenerator,
	CUnit* hitUnit,
	const float3& impactDir, int weaponId,
	CFeature* hitFeature
) {
	const WeaponDef* wd = weaponDefHandler->GetWeaponById(weaponId);

	if (luaUI) {
		if (wd != NULL && wd->cameraShake > 0.0f) {
			luaUI->ShockFront(wd->cameraShake, expPos, expRad);
		}
	}

#ifdef TRACE_SYNC
	tracefile << "Explosion: ";
	tracefile << expPos.x << " " << damages[0] <<  " " << expRad << "\n";
#endif

	const bool noGfx = eventHandler.Explosion(weaponId, expPos, owner);
	const float h2 = ground->GetHeightReal(expPos.x, expPos.z);

	expPos.y = std::max(expPos.y, h2);
	expRad = std::max(expRad, 1.0f);

	if (impactOnly) {
		if (hitUnit) {
			DoExplosionDamage(hitUnit, expPos, expRad, expSpeed, ignoreOwner, owner, edgeEffectiveness, damages, weaponId);
		} else if (hitFeature) {
			DoExplosionDamage(hitFeature, expPos, expRad, damages);
		}
	} else {
		float height = std::max(expPos.y - h2, 0.0f);

		{
			// damage all units within the explosion radius
			const vector<CUnit*>& units = qf->GetUnitsExact(expPos, expRad);
			bool hitUnitDamaged = false;

			for (vector<CUnit*>::const_iterator ui = units.begin(); ui != units.end(); ++ui) {
				CUnit* unit = *ui;

				if (unit == hitUnit) {
					hitUnitDamaged = true;
				}

				DoExplosionDamage(unit, expPos, expRad, expSpeed, ignoreOwner, owner, edgeEffectiveness, damages, weaponId);
			}

			// HACK: for a unit with an offset coldet volume, the explosion
			// (from an impacting projectile) position might not correspond
			// to its quadfield position so we need to damage it separately
			if (hitUnit != NULL && !hitUnitDamaged) {
				DoExplosionDamage(hitUnit, expPos, expRad, expSpeed, ignoreOwner, owner, edgeEffectiveness, damages, weaponId);
			}
		}

		{
			// damage all features within the explosion radius
			const vector<CFeature*>& features = qf->GetFeaturesExact(expPos, expRad);
			bool hitFeatureDamaged = false;

			for (vector<CFeature*>::const_iterator fi = features.begin(); fi != features.end(); ++fi) {
				CFeature* feature = *fi;

				if (feature == hitFeature) {
					hitFeatureDamaged = true;
				}

				DoExplosionDamage(feature, expPos, expRad, damages);
			}

			if (hitFeature != NULL && !hitFeatureDamaged) {
				DoExplosionDamage(hitFeature, expPos, expRad, damages);
			}
		}

		// deform the map
		if (damageGround && !mapDamage->disabled && (expRad > height) && (damages.craterMult > 0.0f)) {
			float damage = damages[0] * (1.0f - (height / expRad));
			if (damage > (expRad * 10.0f)) {
				damage = expRad * 10.0f; // limit the depth somewhat
			}
			mapDamage->Explosion(expPos, (damage + damages.craterBoost) * damages.craterMult, expRad - height);
		}
	}

	// use CStdExplosionGenerator by default
	if (!noGfx) {
		if (explosionGenerator == NULL) {
			explosionGenerator = stdExplosionGenerator;
		}
		explosionGenerator->Explosion(0, expPos, damages[0], expRad, owner, gfxMod, hitUnit, impactDir);
	}

	CExplosionEvent explosionEvent(expPos, damages[0], expRad, wd);
	FireExplosionEvent(explosionEvent);
}