Index: binaries/data/mods/public/simulation/components/Attack.js =================================================================== --- binaries/data/mods/public/simulation/components/Attack.js +++ binaries/data/mods/public/simulation/components/Attack.js @@ -575,12 +575,6 @@ } let id = cmpProjectileManager.LaunchProjectileAtPoint(launchPoint, realTargetPosition, horizSpeed, gravity, actorName, impactActorName, impactAnimationLifetime); - - let attackImpactSound = ""; - let cmpSound = Engine.QueryInterface(this.entity, IID_Sound); - if (cmpSound) - attackImpactSound = cmpSound.GetSoundGroup("attack_impact_" + type.toLowerCase()); - let data = { "type": type, "attackData": this.GetAttackEffectsData(type), @@ -590,7 +584,6 @@ "position": realTargetPosition, "direction": missileDirection, "projectileId": id, - "attackImpactSound": attackImpactSound, "splash": this.GetSplashData(type), "friendlyFire": this.template[type].Projectile.FriendlyFire == "true", }; Index: binaries/data/mods/public/simulation/components/DelayedDamage.js =================================================================== --- binaries/data/mods/public/simulation/components/DelayedDamage.js +++ binaries/data/mods/public/simulation/components/DelayedDamage.js @@ -39,9 +39,6 @@ if (!data.position) return; - let cmpSoundManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_SoundManager); - if (cmpSoundManager && data.attackImpactSound) - cmpSoundManager.PlaySoundGroupAtPosition(data.attackImpactSound, data.position); // Do this first in case the direct hit kills the target. if (data.splash) @@ -65,12 +62,15 @@ if (cmpMirage) target = cmpMirage.GetParent(); + let hitTarget = PositionHelper.TestCollision(target, data.position, lateness) && + Attacking.HandleAttackEffects(target, data.type, data.attackData, data.attacker, data.attackerOwner); + // Deal direct damage if we hit the main target // and we could handle the attack. - if (PositionHelper.TestCollision(target, data.position, lateness) && - Attacking.HandleAttackEffects(target, data.type, data.attackData, data.attacker, data.attackerOwner)) + if (hitTarget) { cmpProjectileManager.RemoveProjectile(data.projectileId); + this.PlayImpactSound(data.attacker, data.type, data.position, true); return; } @@ -78,6 +78,8 @@ let ents = PositionHelper.EntitiesNearPoint(Vector2D.from3D(data.position), this.MISSILE_HIT_RADIUS, Attacking.GetPlayersToDamage(data.attackerOwner, data.friendlyFire)); + let hitSideTarget = false; + for (let ent of ents) { if (!PositionHelper.TestCollision(ent, data.position, lateness) || @@ -85,8 +87,26 @@ continue; cmpProjectileManager.RemoveProjectile(data.projectileId); + hitSideTarget = true; break; } + + this.PlayImpactSound(data.attacker, data.type, data.position, hitSideTarget); }; +DelayedDamage.prototype.PlayImpactSound = function(attacker, type, position, hitTarget) +{ + let cmpSound = Engine.QueryInterface(attacker, IID_Sound) + if (!cmpSound) + return; + + let soundGroup = cmpSound.GetSoundGroup("attack_impact_" + type + (hitTarget ? "_missed" : "")); + if (!soundGroup) + return; + + let cmpSoundManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_SoundManager); + if (cmpSoundManager) + cmpSoundManager.PlaySoundGroupAtPosition(soundGroup, position); +} + Engine.RegisterSystemComponentType(IID_DelayedDamage, "DelayedDamage", DelayedDamage); Index: binaries/data/mods/public/simulation/components/Identity.js =================================================================== --- binaries/data/mods/public/simulation/components/Identity.js +++ binaries/data/mods/public/simulation/components/Identity.js @@ -28,6 +28,11 @@ "" + "" + "" + + "" + + "" + + "" + + "" + + "" + "" + "" + "" + @@ -186,6 +191,11 @@ return this.template.GenericName; }; +Identity.prototype.GetMaterial = function() +{ + return this.template.Material; +}; + Identity.prototype.IsUndeletable = function() { return this.template.Undeletable == "true"; Index: binaries/data/mods/public/simulation/helpers/Attacking.js =================================================================== --- binaries/data/mods/public/simulation/helpers/Attacking.js +++ binaries/data/mods/public/simulation/helpers/Attacking.js @@ -301,7 +301,9 @@ Attacking.prototype.HandleAttackEffects = function(target, attackType, attackData, attacker, attackerOwner, bonusMultiplier = 1) { let cmpResistance = Engine.QueryInterface(target, IID_Resistance); - if (cmpResistance && cmpResistance.IsInvulnerable()) + let blocked = cmpResistance && cmpResistance.IsInvulnerable(); + PlayEntityHitSoundGroup(attackType, target, blocked); + if (blocked) return false; bonusMultiplier *= !attackData.Bonuses ? 1 : this.GetAttackBonus(attacker, target, attackType, attackData.Bonuses); Index: binaries/data/mods/public/simulation/helpers/Sound.js =================================================================== --- binaries/data/mods/public/simulation/helpers/Sound.js +++ binaries/data/mods/public/simulation/helpers/Sound.js @@ -10,4 +10,41 @@ cmpSound.PlaySoundGroup(name); } +/** + * Gets the matching impact sound group depending on the . + * @param {string} type - The type of damage. + * @param {number} target - The entity id of the target. + * @param {number} attacker - The entity id of the attacker. + * @return {string} - The impact sound group. + */ +function PlayEntityHitSoundGroup(type, target, blocked) +{ + let cmpSound = Engine.QueryInterface(target, IID_Sound); + if (!cmpSound) + return; + + let attackImpactType = "hit_" + type.toLowerCase(); + if (blocked) + { + cmpSound.PlaySoundGroup(attackImpactType + "_blocked" || attackImpactType); + return; + } + + let cmpIdentity = Engine.QueryInterface(target, IID_Identity); + if (!cmpIdentity) + { + cmpSound.PlaySoundGroup(attackImpactType); + return; + } + + let material = cmpIdentity.GetMaterial(); + if (!material) + { + cmpSound.PlaySoundGroup(attackImpactType); + return; + } + cmpSound.PlaySoundGroup(attackImpactType + "_" + material || attackImpactType); +} + +Engine.RegisterGlobal("PlayEntityHitSoundGroup", PlayEntityHitSoundGroup); Engine.RegisterGlobal("PlaySound", PlaySound); Index: binaries/data/mods/public/simulation/templates/template_structure.xml =================================================================== --- binaries/data/mods/public/simulation/templates/template_structure.xml +++ binaries/data/mods/public/simulation/templates/template_structure.xml @@ -43,6 +43,7 @@ gaia + stone Structure Structure false @@ -149,6 +150,10 @@ interface/alarm/alarm_attacked_gaia.xml interface/alarm/alarm_attackplayer.xml interface/alarm/alarm_attacked_gaia.xml + attack/impact/arrow_metal.xml + attack/impact/arrow_metal.xml + attack/impact/arrow_impact.xml + attack/impact/arrow_impact.xml Index: binaries/data/mods/public/simulation/templates/template_unit.xml =================================================================== --- binaries/data/mods/public/simulation/templates/template_unit.xml +++ binaries/data/mods/public/simulation/templates/template_unit.xml @@ -35,6 +35,7 @@ gaia Unit Organic ConquestCritical Unit + flesh special/formations/null special/formations/box Index: binaries/data/mods/public/simulation/templates/template_unit_ship.xml =================================================================== --- binaries/data/mods/public/simulation/templates/template_unit_ship.xml +++ binaries/data/mods/public/simulation/templates/template_unit_ship.xml @@ -25,6 +25,7 @@ Ship + wood -Organic Ship @@ -63,6 +64,10 @@ actor/ship/warship_move_01.xml actor/ship/warship_move_01.xml actor/ship/warship_death.xml + attack/impact/shield_wooden.xml + attack/impact/shield_wooden.xml + attack/impact/arrow_metal.xml + attack/impact/arrow_impact.xml