void CUnitScript::EmitSfx(int sfxType, int piece) { #ifndef _CONSOLE if (!PieceExists(piece)) { ShowScriptError("Invalid piecenumber for emit-sfx"); return; } if (ph->particleSaturation > 1.0f && sfxType < SFX_CEG) { // skip adding (unsynced!) particles when we have too many return; } // Make sure wakes are only emitted on water if ((sfxType >= SFX_WAKE) && (sfxType <= SFX_REVERSE_WAKE_2)) { if (ground->GetApproximateHeight(unit->pos.x, unit->pos.z) > 0.0f) { return; } } float3 relPos = ZeroVector; float3 relDir = UpVector; if (!GetEmitDirPos(piece, relPos, relDir)) { ShowScriptError("emit-sfx: GetEmitDirPos failed"); return; } relDir.SafeNormalize(); const float3 pos = unit->pos + unit->frontdir * relPos.z + unit->updir * relPos.y + unit->rightdir * relPos.x; const float3 dir = unit->frontdir * relDir.z + unit->updir * relDir.y + unit->rightdir * relDir.x; float alpha = 0.3f + gu->usRandFloat() * 0.2f; float alphaFalloff = 0.004f; float fadeupTime = 4; const UnitDef* ud = unit->unitDef; const MoveDef* md = ud->moveDef; // hovercraft need special care if (md != NULL && md->moveType == MoveDef::Hover_Move) { fadeupTime = 8.0f; alpha = 0.15f + gu->usRandFloat() * 0.2f; alphaFalloff = 0.008f; } switch (sfxType) { case SFX_REVERSE_WAKE: case SFX_REVERSE_WAKE_2: { //reverse wake new CWakeProjectile( pos + gu->usRandVector() * 2.0f, dir * 0.4f, 6.0f + gu->usRandFloat() * 4.0f, 0.15f + gu->usRandFloat() * 0.3f, unit, alpha, alphaFalloff, fadeupTime ); break; } case SFX_WAKE_2: //wake 2, in TA it lives longer.. case SFX_WAKE: { //regular ship wake new CWakeProjectile( pos + gu->usRandVector() * 2.0f, dir * 0.4f, 6.0f + gu->usRandFloat() * 4.0f, 0.15f + gu->usRandFloat() * 0.3f, unit, alpha, alphaFalloff, fadeupTime ); break; } case SFX_BUBBLE: { //submarine bubble. does not provide direction through piece vertices.. float3 pspeed = gu->usRandVector() * 0.1f; pspeed.y += 0.2f; new CBubbleProjectile( pos + gu->usRandVector() * 2.0f, pspeed, 40.0f + gu->usRandFloat() * 30.0f, 1.0f + gu->usRandFloat() * 2.0f, 0.01f, unit, 0.3f + gu->usRandFloat() * 0.3f ); } break; case SFX_WHITE_SMOKE: //damaged unit smoke new CSmokeProjectile(pos, gu->usRandVector() * 0.5f + UpVector * 1.1f, 60, 4, 0.5f, unit, 0.5f); break; case SFX_BLACK_SMOKE: //damaged unit smoke new CSmokeProjectile(pos, gu->usRandVector() * 0.5f + UpVector * 1.1f, 60, 4, 0.5f, unit, 0.6f); break; case SFX_VTOL: { const float3 speed = unit->speed * 0.7f + unit->frontdir * 0.5f * relDir.z + unit->updir * 0.5f * -math::fabs(relDir.y) + unit->rightdir * 0.5f * relDir.x; CHeatCloudProjectile* hc = new CHeatCloudProjectile( pos, speed, 10 + gu->usRandFloat() * 5, 3 + gu->usRandFloat() * 2, unit ); hc->size = 3; break; } default: { if (sfxType & SFX_CEG) { // emit defined explosiongenerator const unsigned index = sfxType - SFX_CEG; if (index >= unit->unitDef->sfxExplGens.size() || unit->unitDef->sfxExplGens[index] == NULL) { ShowScriptError("Invalid explosion generator index for emit-sfx"); break; } IExplosionGenerator* explGen = unit->unitDef->sfxExplGens[index]; explGen->Explosion(0, pos, unit->cegDamage, 1, unit, 0, 0, dir); } else if (sfxType & SFX_FIRE_WEAPON) { // make a weapon fire from the piece const unsigned index = sfxType - SFX_FIRE_WEAPON; if (index >= unit->weapons.size() || unit->weapons[index] == NULL) { ShowScriptError("Invalid weapon index for emit-sfx"); break; } CWeapon* weapon = unit->weapons[index]; const float3 targetPos = weapon->targetPos; const float3 weaponMuzzlePos = weapon->weaponMuzzlePos; weapon->targetPos = pos + dir; weapon->weaponMuzzlePos = pos; weapon->Fire(); weapon->weaponMuzzlePos = weaponMuzzlePos; weapon->targetPos = targetPos; } else if (sfxType & SFX_DETONATE_WEAPON) { const unsigned index = sfxType - SFX_DETONATE_WEAPON; if (index >= unit->weapons.size() || unit->weapons[index] == NULL) { ShowScriptError("Invalid weapon index for emit-sfx"); break; } // detonate weapon from piece const WeaponDef* weaponDef = unit->weapons[index]->weaponDef; CGameHelper::ExplosionParams params = { pos, ZeroVector, weaponDef->damages, weaponDef, unit, // owner NULL, // hitUnit NULL, // hitFeature weaponDef->craterAreaOfEffect, weaponDef->damageAreaOfEffect, weaponDef->edgeEffectiveness, weaponDef->explosionSpeed, 1.0f, // gfxMod weaponDef->impactOnly, weaponDef->noSelfDamage, // ignoreOwner true, // damageGround }; helper->Explosion(params); } } break; } #endif }
void CGameHelper::Explosion(const ExplosionParams& params) { const float3& dir = params.dir; const float3 expPos = params.pos; 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; 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 expEdgeEffect = 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, params.projectileID, 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, expEdgeEffect, ignoreOwner, damages, weaponDefID, params.projectileID); } else if (hitFeature) { DoExplosionDamage(hitFeature, expPos, damageAOE, expEdgeEffect, damages, weaponDefID, params.projectileID); } } else { { // damage all units within the explosion radius const vector<CUnit*>& units = quadField->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, expEdgeEffect, ignoreOwner, damages, weaponDefID, params.projectileID); } // 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, expEdgeEffect, ignoreOwner, damages, weaponDefID, params.projectileID); } } { // damage all features within the explosion radius const vector<CFeature*>& features = quadField->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, expEdgeEffect, damages, weaponDefID, params.projectileID); } if (hitFeature != NULL && !hitFeatureDamaged) { DoExplosionDamage(hitFeature, expPos, damageAOE, expEdgeEffect, damages, weaponDefID, params.projectileID); } } // 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); CExplosionCreator::FireExplosionEvent(explosionEvent); #if (PLAY_SOUNDS == 1) if (weaponDef != NULL) { const GuiSoundSet& soundSet = weaponDef->hitSound; const unsigned int soundFlags = CCustomExplosionGenerator::GetFlagsFromHeight(expPos.y, altitude); const int soundNum = ((soundFlags & (CCustomExplosionGenerator::SPW_WATER | CCustomExplosionGenerator::SPW_UNDERWATER)) != 0); const int soundID = soundSet.getID(soundNum); if (soundID > 0) { Channels::Battle.PlaySample(soundID, expPos, soundSet.getVolume(soundNum)); } } #endif }
void CUnitScript::EmitSfx(int sfxType, int piece) { #ifndef _CONSOLE if (!PieceExists(piece)) { ShowScriptError("Invalid piecenumber for emit-sfx"); return; } if (ph->particleSaturation > 1.0f && sfxType < SFX_CEG) { // skip adding (unsynced!) particles when we have too many return; } // Make sure wakes are only emitted on water if ((sfxType >= SFX_WAKE) && (sfxType <= SFX_REVERSE_WAKE_2)) { if (ground->GetApproximateHeight(unit->pos.x, unit->pos.z) > 0.0f) { return; } } float3 relPos = ZeroVector; float3 relDir = UpVector; if (!GetEmitDirPos(piece, relPos, relDir)) { ShowScriptError("emit-sfx: GetEmitDirPos failed"); return; } relDir.SafeNormalize(); const float3 pos = unit->pos + unit->frontdir * relPos.z + unit->updir * relPos.y + unit->rightdir * relPos.x; const float3 dir = unit->frontdir * relDir.z + unit->updir * relDir.y + unit->rightdir * relDir.x; float alpha = 0.3f + gu->usRandFloat() * 0.2f; float alphaFalloff = 0.004f; float fadeupTime = 4; // Hovers need special care if (unit->unitDef->canhover) { fadeupTime = 8.0f; alpha = 0.15f + gu->usRandFloat() * 0.2f; alphaFalloff = 0.008f; } switch (sfxType) { case SFX_REVERSE_WAKE: case SFX_REVERSE_WAKE_2: { //reverse wake new CWakeProjectile( pos + gu->usRandVector() * 2.0f, dir * 0.4f, 6.0f + gu->usRandFloat() * 4.0f, 0.15f + gu->usRandFloat() * 0.3f, unit, alpha, alphaFalloff, fadeupTime ); break; } case SFX_WAKE_2: //wake 2, in TA it lives longer.. case SFX_WAKE: { //regular ship wake new CWakeProjectile( pos + gu->usRandVector() * 2.0f, dir * 0.4f, 6.0f + gu->usRandFloat() * 4.0f, 0.15f + gu->usRandFloat() * 0.3f, unit, alpha, alphaFalloff, fadeupTime ); break; } case SFX_BUBBLE: { //submarine bubble. does not provide direction through piece vertices.. float3 pspeed = gu->usRandVector() * 0.1f; pspeed.y += 0.2f; new CBubbleProjectile( pos + gu->usRandVector() * 2.0f, pspeed, 40.0f + gu->usRandFloat() * 30.0f, 1.0f + gu->usRandFloat() * 2.0f, 0.01f, unit, 0.3f + gu->usRandFloat() * 0.3f ); } break; case SFX_WHITE_SMOKE: //damaged unit smoke new CSmokeProjectile(pos, gu->usRandVector() * 0.5f + UpVector * 1.1f, 60, 4, 0.5f, unit, 0.5f); break; case SFX_BLACK_SMOKE: //damaged unit smoke new CSmokeProjectile(pos, gu->usRandVector() * 0.5f + UpVector * 1.1f, 60, 4, 0.5f, unit, 0.6f); break; case SFX_VTOL: { const float3 speed = unit->speed * 0.7f + unit->frontdir * 0.5f * relDir.z + unit->updir * 0.5f * -fabs(relDir.y) + unit->rightdir * 0.5f * relDir.x; CHeatCloudProjectile* hc = new CHeatCloudProjectile( pos, speed, 10 + gu->usRandFloat() * 5, 3 + gu->usRandFloat() * 2, unit ); hc->size = 3; break; } default: { if (sfxType & SFX_CEG) { // emit defined explosiongenerator const unsigned index = sfxType - SFX_CEG; if (index >= unit->unitDef->sfxExplGens.size() || unit->unitDef->sfxExplGens[index] == NULL) { ShowScriptError("Invalid explosion generator index for emit-sfx"); break; } IExplosionGenerator* explGen = unit->unitDef->sfxExplGens[index]; explGen->Explosion(0, pos, unit->cegDamage, 1, unit, 0, 0, dir); } else if (sfxType & SFX_FIRE_WEAPON) { // make a weapon fire from the piece const unsigned index = sfxType - SFX_FIRE_WEAPON; if (index >= unit->weapons.size() || unit->weapons[index] == NULL) { ShowScriptError("Invalid weapon index for emit-sfx"); break; } CWeapon* weapon = unit->weapons[index]; const float3 targetPos = weapon->targetPos; const float3 weaponMuzzlePos = weapon->weaponMuzzlePos; // don't override the weapon's target position // if it was not set internally (so that force- // fire keeps working as expected) if (!weapon->haveUserTarget) { weapon->targetPos = pos + dir; } weapon->weaponMuzzlePos = pos; weapon->Fire(); weapon->weaponMuzzlePos = weaponMuzzlePos; weapon->targetPos = targetPos; } else if (sfxType & SFX_DETONATE_WEAPON) { const unsigned index = sfxType - SFX_DETONATE_WEAPON; if (index >= unit->weapons.size() || unit->weapons[index] == NULL) { ShowScriptError("Invalid weapon index for emit-sfx"); break; } // detonate weapon from piece const WeaponDef* weaponDef = unit->weapons[index]->weaponDef; if (weaponDef->soundhit.getID(0) > 0) { Channels::Battle.PlaySample(weaponDef->soundhit.getID(0), unit, weaponDef->soundhit.getVolume(0)); } helper->Explosion( pos, weaponDef->damages, weaponDef->areaOfEffect, weaponDef->edgeEffectiveness, weaponDef->explosionSpeed, unit, true, 1.0f, weaponDef->noSelfDamage, weaponDef->impactOnly, weaponDef->explosionGenerator, NULL, ZeroVector, weaponDef->id ); } } break; } #endif }