• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

Hero Wound Sounds

A small library to enable on-damage wound sounds for heroes in your RPG or other hero map. Included in the map is a large collection of wound sounds from various races from World of Warcraft as well as a tutorial on how to import additional wound sounds.

JASS:
library HeroWoundSounds initializer Init

/*
    ===========================================================================================
                                        Hero Wound Sounds
                                            by Antares
   
            Easily add on-damage "ouch"-sounds to heroes for your rpg or other hero map.
           
                                            How to import:
    Copy this library into your map. Extract the imported sound files into an empty folder, then
    import those that you need into your map. Saving your map in folder mode might make the
    import process more convenient.
   
    Edit the parameters in the config section to your liking.
    
    To import additional wound sounds from World of Warcraft characters, go to www.wowhead.com, then
    go to Database -> Sounds. In the Name field, enter either "Wound" or the name of your race/gender
    combination, for example "TaurenFemale." The names of the wound sound files are not entirely
    consistent and it might take some experimentation to find the ones you are looking for.
    
    Download the sound files, then you need to convert the .ogg files to .flac or .mp3. You can
    do this via online tools or with Audacity (freeware). To do so, select all sound files in your
    explorer, then drag them into the Audacity window. On the left, increase the volume of each
    file by roughly 3 db. Then go to File -> Export -> Export Multiple and select .mp3 as the
    output format.
    
    Rename the files to
    YourNameHit1.mp3
    YourNameHit2.mp3
    ...
    YourNameCrit.mp3
    
    Import the sound files into the specified subfolder and add a line to the Init function to
    tell the library how many normal wound sounds exist. For example, if you import TaurenFemale
    and it has 4 wound sounds plus a crit sound, it should be:
    call SaveInteger(hash, StringHash("TaurenFemale"), 0, 4)

    ===========================================================================================
    API
    ===========================================================================================
   
    AddWoundSoundsToUnit takes unit whichUnit, string whichSoundPath returns nothing
    RemoveWoundSoundsFromUnit takes unit whichUnit returns nothing
    PauseWoundSoundsForUnit takes unit whichUnit returns nothing
    AdjustWoundSoundVolumeForUnit takes unit whichUnit, real volumePercent returns nothing

    ===========================================================================================
    */

globals
    //=========================================================================================
    //Config
    //=========================================================================================

    private constant real WOUND_SOUND_COOLDOWN              = 1.25              //Minimum time between to wound sounds. Crit sounds will play even when normal wound sounds are on cooldown.
    
    private constant boolean ENABLE_HIT_SOUNDS              = true              //Disable if you only want wound crit sounds.
    private constant real HIT_SOUND_THRESHOLD               = 3                 //Damage required to trigger a normal wound sound.
    private constant boolean ENABLE_CRIT_SOUNDS             = true              //Disable if you only want normal wound sounds.
    private constant real CRIT_SOUND_THRESHOLD              = 10                //Damage required to trigger a wound crit sound.
    private constant boolean THRESHOLD_IN_PERCENT           = true              //true if thresholds are given in percentage of hero max hp. false if thresholds are given in absolute values.
    
    private constant boolean TRIGGER_ON_ATTACK_DAMAGE       = true              //Disable if attacks should not trigger wound sounds.
    private constant boolean TRIGGER_ON_NON_ATTACK_DAMAGE   = true              //Disable if only attacks should trigger wound sounds.

    private constant boolean PLAY_ONLY_FOR_OWNER            = true              //Disable if every player should hear the wound sounds, not just the owner.
    private constant boolean PLAY_AS_3D_SOUND               = false             //Enable if wound sounds should be played as a 3D sound on the hero's location.
    private constant real SOUND_3D_MIN_DIST                 = 600               //Only if PLAY_AS_3D_SOUND is enabled.
    private constant real SOUND_3D_MAX_DIST                 = 4000              //Only if PLAY_AS_3D_SOUND is enabled.
	
	private constant boolean AUTO_CLEANUP_ON_DEATH			= false				//Enable if memory leaks should automatically be handled on death of a nonhero unit. Otherwise, leaks must be
																				//manually removed with RemoveWoundSoundsFromUnit.

    private constant string SOUND_FILE_SUBFOLDER            = "WoundSounds\\"   //The subfolder where the imported wound sounds are stored in.
    
    //=========================================================================================

    private hashtable hash = InitHashtable()
endglobals

private function Init takes nothing returns nothing
    //=========================================================================================
    //Here, the number of wound sounds for each race/gender combination are stored. When you
    //import new sound files, add them here.
    //=========================================================================================
    static if ENABLE_HIT_SOUNDS then
        call SaveInteger(hash, StringHash("BloodElfFemale"), 0,     4)
        call SaveInteger(hash, StringHash("BloodElfMale"), 0,       4)
        call SaveInteger(hash, StringHash("Cat"), 0,                3)
        call SaveInteger(hash, StringHash("DraeneiMale"), 0,        5)
        call SaveInteger(hash, StringHash("DwarfFemale"), 0,        5)
        call SaveInteger(hash, StringHash("DwarfMale"), 0,          4)
        call SaveInteger(hash, StringHash("GnomeFemale"), 0,        3)
        call SaveInteger(hash, StringHash("GnomeMale"), 0,          4)
        call SaveInteger(hash, StringHash("GoblinFemale"), 0,       5)
        call SaveInteger(hash, StringHash("GoblinMale"), 0,         4)
        call SaveInteger(hash, StringHash("HumanFemale"), 0,        4)
        call SaveInteger(hash, StringHash("HumanMale"), 0,          3)
        call SaveInteger(hash, StringHash("NagaFemale"), 0,         4)
        call SaveInteger(hash, StringHash("NightElfFemale"), 0,     4)
        call SaveInteger(hash, StringHash("NightElfMale"), 0,       4)
        call SaveInteger(hash, StringHash("OrcMale"), 0,            5)
        call SaveInteger(hash, StringHash("PandarenMale"), 0,       4)
        call SaveInteger(hash, StringHash("TaurenMale"), 0,         3)
        call SaveInteger(hash, StringHash("TrollMale"), 0,          5)
        call SaveInteger(hash, StringHash("UndeadFemale"), 0,       3)
        call SaveInteger(hash, StringHash("UndeadMale"), 0,         3)
        call SaveInteger(hash, StringHash("VoidElfFemale"), 0,      4)
        call SaveInteger(hash, StringHash("VrykulFemale"), 0,       5)
    endif
endfunction

static if AUTO_CLEANUP_ON_DEATH then
	private function OnDeath takes nothing returns nothing
		local unit u = GetTriggerUnit()
		local woundSound whichWoundSound = LoadInteger(hash, GetHandleId(u), 0)
		call whichWoundSound.destroy()
		set u = null
	endfunction
endif

private function PlayWoundSound takes string soundPath, real volumePercent, unit whichUnit returns nothing
	local real volume = volumePercent
	local sound s = CreateSound(soundPath, false, PLAY_AS_3D_SOUND, PLAY_AS_3D_SOUND, 10, 10, "DefaultEAXON")
	
    static if PLAY_ONLY_FOR_OWNER then
        if GetLocalPlayer() != GetOwningPlayer(whichUnit) then
            set volume = 0
        endif
    endif

    static if PLAY_AS_3D_SOUND then
        call SetSoundPosition( s, GetUnitX(whichUnit), GetUnitY(whichUnit), 50)
        call SetSoundDistances( s , SOUND_3D_MIN_DIST , SOUND_3D_MAX_DIST )
    endif
        
	call SetSoundVolumeBJ(s, volume)
	call StartSound(s)
	call KillSoundWhenDone(s)
	set s = null
endfunction

struct woundSound
    unit hero
    string soundPath
    timer woundTimer
    timer woundCritTimer
    trigger damageTrigger
	trigger deathTrigger
    integer numSounds
    boolean isPaused
    integer lastSound
    real volumePercent

    static method OnDamage takes nothing returns nothing
        local unit u = BlzGetEventDamageTarget()
        local real amount = GetEventDamage()
        local woundSound this
        local string fullPath
        
        if amount > GetUnitState(u, UNIT_STATE_LIFE) then
            set u = null
            return
        endif
        
        static if not TRIGGER_ON_ATTACK_DAMAGE then
            if BlzGetEventIsAttack() then
                set u = null
                return
            endif
        endif
        
        static if not TRIGGER_ON_NON_ATTACK_DAMAGE then
            if not BlzGetEventIsAttack() then
                set u = null
                return
            endif
        endif
        
        static if ENABLE_HIT_SOUNDS then
            if (amount > HIT_SOUND_THRESHOLD and not THRESHOLD_IN_PERCENT) or (amount > HIT_SOUND_THRESHOLD*BlzGetUnitMaxHP(u)/100 and THRESHOLD_IN_PERCENT) then
                set this = LoadInteger(hash, GetHandleId(u), 0)
                if .isPaused then
                    set u = null
                    return
                endif
                
                static if ENABLE_CRIT_SOUNDS then
                    if ((amount > CRIT_SOUND_THRESHOLD and not THRESHOLD_IN_PERCENT) or (amount > CRIT_SOUND_THRESHOLD*BlzGetUnitMaxHP(u)/100 and THRESHOLD_IN_PERCENT)) then
                        if TimerGetRemaining(.woundCritTimer) == 0 then
                            set fullPath = .soundPath + "Crit.mp3"
                            if WOUND_SOUND_COOLDOWN > 0 then
                                call TimerStart(.woundTimer, WOUND_SOUND_COOLDOWN, false, null)
                                call TimerStart(.woundCritTimer, WOUND_SOUND_COOLDOWN, false, null)
                            endif
                            call PlayWoundSound(fullPath, .volumePercent, u)
                        endif
                    elseif TimerGetRemaining(.woundTimer) == 0 then
                        set .lastSound = ModuloInteger(.lastSound + GetRandomInt(0,.numSounds-2), .numSounds) + 1
                        set fullPath = .soundPath + "Hit" + I2S(.lastSound) + ".mp3"
                        if WOUND_SOUND_COOLDOWN > 0 then
                            call TimerStart(.woundTimer, WOUND_SOUND_COOLDOWN, false, null)
                        endif
                        call PlayWoundSound(fullPath, .volumePercent, u)
                    endif
                else
                    if TimerGetRemaining(.woundTimer) == 0 then
                        set .lastSound = ModuloInteger(.lastSound + GetRandomInt(0,.numSounds-2), .numSounds) + 1
                        set fullPath = .soundPath + "Hit" + I2S(.lastSound) + ".mp3"
                        if WOUND_SOUND_COOLDOWN > 0 then
                            call TimerStart(.woundTimer, WOUND_SOUND_COOLDOWN, false, null)
                        endif
                        call PlayWoundSound(fullPath, .volumePercent, u)
                    endif
                endif
            endif
        else
            static if ENABLE_CRIT_SOUNDS then
                if (amount > CRIT_SOUND_THRESHOLD and not THRESHOLD_IN_PERCENT) or (amount > CRIT_SOUND_THRESHOLD*BlzGetUnitMaxHP(u)/100 and THRESHOLD_IN_PERCENT) then
                    set this = LoadInteger(hash, GetHandleId(u), 0)
                    if .isPaused then
                        set u = null
                        return
                    endif
                    if TimerGetRemaining(.woundCritTimer) == 0 then
                        set fullPath = .soundPath + "Crit.mp3"
                        if WOUND_SOUND_COOLDOWN > 0 then
                            call TimerStart(.woundCritTimer, WOUND_SOUND_COOLDOWN, false, null)
                        endif
                        call PlayWoundSound(fullPath, .volumePercent, u)
                    endif
                endif
            endif
        endif
        set u = null
    endmethod
    
    static method create takes unit whichUnit, string whichSoundPath returns woundSound
        local woundSound this = woundSound.allocate()
        set .hero = whichUnit
        set .soundPath = SOUND_FILE_SUBFOLDER + whichSoundPath
        set .damageTrigger = CreateTrigger()
        static if ENABLE_HIT_SOUNDS then
            if WOUND_SOUND_COOLDOWN > 0 then
                set .woundTimer = CreateTimer()
            endif
            set .numSounds = LoadInteger(hash, StringHash(whichSoundPath), 0)
        endif
        static if ENABLE_CRIT_SOUNDS then
            if WOUND_SOUND_COOLDOWN > 0 then
                set .woundCritTimer = CreateTimer()
            endif
        endif
        set .isPaused = false
        set .lastSound = 0
        set .volumePercent = 100
        
        call TriggerAddAction(.damageTrigger, function woundSound.OnDamage)
        call TriggerRegisterUnitEvent(.damageTrigger, .hero, EVENT_UNIT_DAMAGED)
        
        call SaveInteger(hash, GetHandleId(.hero), 0, this)
		
		static if AUTO_CLEANUP_ON_DEATH then
			if not IsUnitType(whichUnit, UNIT_TYPE_HERO)
				set .deathTrigger = CreateTrigger()
				call TriggerAddAction(.deathTrigger, function OnDeath)
				call TriggerRegisterUnitEvent(.deathTrigger, whichUnit, EVENT_UNIT_DEATH)
			endif
		endif

        return this
    endmethod
    
    method onDestroy takes nothing returns nothing
        if WOUND_SOUND_COOLDOWN > 0 then
            call DestroyTimer(.woundTimer)
            call DestroyTimer(.woundCritTimer)
        endif
        call DestroyTrigger(.damageTrigger)
        call RemoveSavedInteger(hash, GetHandleId(.hero), 0)
		static if AUTO_CLEANUP_ON_DEATH then
			if not IsUnitType(.hero, UNIT_TYPE_HERO) then
				call DestroyTrigger(.deathTrigger)
			endif
		endif
    endmethod
endstruct

function AddWoundSoundsToUnit takes unit whichUnit, string whichSoundPath returns nothing
    call woundSound.create(whichUnit,whichSoundPath)
endfunction

function RemoveWoundSoundsFromUnit takes unit whichUnit returns nothing
    local woundSound whichWoundSound = LoadInteger(hash, GetHandleId(whichUnit), 0)
    call whichWoundSound.destroy()
endfunction

function PauseWoundSoundsForUnit takes unit whichUnit, boolean pause returns nothing
    local woundSound whichWoundSound = LoadInteger(hash, GetHandleId(whichUnit), 0)
    set whichWoundSound.isPaused = pause
endfunction

function AdjustWoundSoundVolumeForUnit takes unit whichUnit, real percentage returns nothing
    local woundSound whichWoundSound = LoadInteger(hash, GetHandleId(whichUnit), 0)
    set whichWoundSound.volumePercent = RMinBJ(100,percentage)
endfunction

endlibrary

Lua:
do
    --[[
    ===========================================================================================
                                        Hero Wound Sounds
                                            by Antares
  
            Easily add on-damage "ouch"-sounds to heroes for your rpg or other hero map.
          
                                            How to import:
    Copy this library into your map. Extract the imported sound files into an empty folder, then
    import those that you need into your map. Saving your map in folder mode might make the
    import process more convenient.
  
    Edit the parameters in the config section to your liking.
   
    To import additional wound sounds from World of Warcraft characters, go to www.wowhead.com, then
    go to Database -> Sounds. In the Name field, enter either "Wound" or the name of your race/gender
    combination, for example "TaurenFemale." The names of the wound sound files are not entirely
    consistent and it might take some experimentation to find the ones you are looking for.
   
    Download the sound files, then you need to convert the .ogg files to .flac or .mp3. You can
    do this via online tools or with Audacity (freeware). To do so, select all sound files in your
    explorer, then drag them into the Audacity window. On the left, increase the volume of each
    file by roughly 3 db. Then go to File -> Export -> Export Multiple and select .mp3 as the
    output format.
    
    Rename the files to
    YourNameHit1.mp3
    YourNameHit2.mp3
    ...
    YourNameCrit.mp3
     
    Import the sound files into the specified subfolder and add a line to the Init function to
    tell the library how many normal wound sounds exist. For example, if you import TaurenFemale
    and it has 4 wound sounds plus a crit sound, it should be:
    numberOfWoundSounds.TaurenFemale = 4
 
    ===========================================================================================
    API
    ===========================================================================================
 
    AddWoundSoundsToUnit(whichUnit, whichSoundPath)
    RemoveWoundSoundsFromUnit(whichUnit)
    PauseWoundSoundsForUnit(whichUnit)
    AdjustWoundSoundVolumeForUnit(whichUnit, volumePercent)

    ===========================================================================================
    ]]
 
    --=========================================================================================
    --Config
    --=========================================================================================
       
    local WOUND_SOUND_COOLDOWN              = 1.25                  --Minimum time between to wound sounds. Crit sounds will play even when normal wound sounds are on cooldown.
   
    local ENABLE_HIT_SOUNDS                 = true                  --Disable if you only want wound crit sounds.
    local HIT_SOUND_THRESHOLD               = 3                     --Damage required to trigger a normal wound sound.
    local ENABLE_CRIT_SOUNDS                = true                  --Disable if you only want normal wound sounds.
    local CRIT_SOUND_THRESHOLD              = 10                    --Damage required to trigger a wound crit sound.
    local THRESHOLD_IN_PERCENT              = true                  --true if thresholds are given in percentage of hero max hp. false if thresholds are given in absolute values.
   
    local TRIGGER_ON_ATTACK_DAMAGE          = true                  --Disable if attacks should not trigger wound sounds.
    local TRIGGER_ON_NON_ATTACK_DAMAGE      = true                  --Disable if only attacks should trigger wound sounds.
 
    local PLAY_ONLY_FOR_OWNER               = true                  --Disable if every player should hear the wound sounds, not just the owner.
    local PLAY_AS_3D_SOUND                  = false                 --Enable if wound sounds should be played as a 3D sound on the hero's location.
    local SOUND_3D_MIN_DIST                 = 600                   --Only if PLAY_AS_3D_SOUND is enabled.
    local SOUND_3D_MAX_DIST                 = 4000                  --Only if PLAY_AS_3D_SOUND is enabled.
	
	local AUTO_CLEANUP_ON_DEATH			    = false				    --Enable if memory leaks should automatically be handled on death of a nonhero unit. Otherwise, leaks must be
																	--manually removed with RemoveWoundSoundsFromUnit.
 
    local SOUND_FILE_SUBFOLDER              = "WoundSounds\\"       --The subfolder where the imported wound sounds are stored in.
   
    --=========================================================================================

    local numberOfWoundSounds = {}
    local woundSound = {}
 
    --=========================================================================================
    --Here, the number of wound sounds for each race/gender combination are stored. When you
    --import new sound files, add them here.
    --=========================================================================================
    if ENABLE_HIT_SOUNDS then
        numberOfWoundSounds.BloodElfFemale =    4
        numberOfWoundSounds.BloodElfFemale =    4
        numberOfWoundSounds.BloodElfMale =      4
        numberOfWoundSounds.Cat =                3
        numberOfWoundSounds.DraeneiMale =        5
        numberOfWoundSounds.DwarfFemale =        5
        numberOfWoundSounds.DwarfMale =            4
        numberOfWoundSounds.GnomeFemale =        3
        numberOfWoundSounds.GnomeMale =            4
        numberOfWoundSounds.GoblinFemale =        5
        numberOfWoundSounds.GoblinMale =        4
        numberOfWoundSounds.HumanFemale =        4
        numberOfWoundSounds.HumanMale =            3
        numberOfWoundSounds.NagaFemale =        4
        numberOfWoundSounds.NightElfFemale =    4
        numberOfWoundSounds.NightElfMale =        4
        numberOfWoundSounds.OrcMale =            5
        numberOfWoundSounds.PandarenMale =        4
        numberOfWoundSounds.TaurenMale =        3
        numberOfWoundSounds.TrollMale =            5
        numberOfWoundSounds.UndeadFemale =        3
        numberOfWoundSounds.UndeadMale =        3
        numberOfWoundSounds.VoidElfFemale =        4
        numberOfWoundSounds.VrykulFemale =        5
    end

    local function OnDeath()
		RemoveWoundSoundsFromUnit(GetTriggerUnit())
    end
 
    local function PlayWoundSound(soundPath, volumePercent, whichUnit)
        local volume = volumePercent
        local s = CreateSound(soundPath, false, PLAY_AS_3D_SOUND, PLAY_AS_3D_SOUND, 10, 10, "DefaultEAXON")
       
        if PLAY_ONLY_FOR_OWNER and GetLocalPlayer() ~= GetOwningPlayer(whichUnit) then
            volume = 0
        end

        if PLAY_AS_3D_SOUND then
            SetSoundPosition( s, GetUnitX(whichUnit), GetUnitY(whichUnit), 50)
            SetSoundDistances( s , SOUND_3D_MIN_DIST , SOUND_3D_MAX_DIST )
        end
           
        SetSoundVolumeBJ(s, volume)
        StartSound(s)
        KillSoundWhenDone(s)
    end
 
    local function WoundSoundOnDamage()
        local u = BlzGetEventDamageTarget()
        local amount = GetEventDamage()
           
        if amount > GetUnitState(u, UNIT_STATE_LIFE) then
            return
        end
       
        if not TRIGGER_ON_ATTACK_DAMAGE and BlzGetEventIsAttack() then
            return
        end
       
        if not TRIGGER_ON_NON_ATTACK_DAMAGE and not BlzGetEventIsAttack() then
            return
        end
           
        local w = woundSound[u]
        if w.isPaused then
            return
        end
               
        if ENABLE_CRIT_SOUNDS and ((amount > CRIT_SOUND_THRESHOLD and not THRESHOLD_IN_PERCENT) or (amount > CRIT_SOUND_THRESHOLD*BlzGetUnitMaxHP(u)/100 and THRESHOLD_IN_PERCENT)) then
            if WOUND_SOUND_COOLDOWN == 0 or TimerGetRemaining(w.woundCritTimer) == 0 then
                local fullPath = SOUND_FILE_SUBFOLDER .. w.soundPath .. "Crit.mp3"
                if WOUND_SOUND_COOLDOWN > 0 then
                    TimerStart(w.woundTimer, WOUND_SOUND_COOLDOWN, false, nil)
                    TimerStart(w.woundCritTimer, WOUND_SOUND_COOLDOWN, false, nil)
                end
                PlayWoundSound(fullPath, w.volumePercent, u)
            end
        elseif ENABLE_HIT_SOUNDS and (amount > HIT_SOUND_THRESHOLD and not THRESHOLD_IN_PERCENT) or (amount > HIT_SOUND_THRESHOLD*BlzGetUnitMaxHP(u)/100 and THRESHOLD_IN_PERCENT) then
            if WOUND_SOUND_COOLDOWN == 0 or TimerGetRemaining(w.woundTimer) == 0 then
                w.lastSound = ModuloInteger(w.lastSound + math.random(0,numberOfWoundSounds[w.soundPath] - 2), numberOfWoundSounds[w.soundPath]) + 1
                local fullPath = SOUND_FILE_SUBFOLDER .. w.soundPath .. "Hit" .. w.lastSound .. ".mp3"

                if WOUND_SOUND_COOLDOWN > 0 then
                    TimerStart(w.woundTimer, WOUND_SOUND_COOLDOWN, false, nil)
                end
                PlayWoundSound(fullPath, w.volumePercent, u)
            end
        end
    end
 
    ---@param whichUnit unit
    ---@param whichSoundPath string
    function AddWoundSoundsToUnit(whichUnit, whichSoundPath)
        woundSound[whichUnit] = {}
        woundSound[whichUnit].soundPath = whichSoundPath
        woundSound[whichUnit].damageTrigger = CreateTrigger()

        if WOUND_SOUND_COOLDOWN > 0 then
            if ENABLE_HIT_SOUNDS then
                woundSound[whichUnit].woundTimer = CreateTimer()
            end
            if ENABLE_CRIT_SOUNDS then
                woundSound[whichUnit].woundCritTimer = CreateTimer()
            end
        end
 
        woundSound[whichUnit].isPaused = false
        woundSound[whichUnit].lastSound = 0
        woundSound[whichUnit].volumePercent = 100
       
        TriggerAddAction(woundSound[whichUnit].damageTrigger, WoundSoundOnDamage)
        TriggerRegisterUnitEvent(woundSound[whichUnit].damageTrigger, whichUnit, EVENT_UNIT_DAMAGED)

        if AUTO_CLEANUP_ON_DEATH and not IsUnitType(whichUnit, UNIT_TYPE_HERO) then
            woundSound[whichUnit].deathTrigger = CreateTrigger()
            TriggerAddAction(woundSound[whichUnit].deathTrigger, OnDeath)
            TriggerRegisterUnitEvent(woundSound[whichUnit].deathTrigger, whichUnit, EVENT_UNIT_DEATH)
        end
    end
 
    ---@param whichUnit unit
    function RemoveWoundSoundsFromUnit(whichUnit)
        if WOUND_SOUND_COOLDOWN > 0 then
            if ENABLE_HIT_SOUNDS then
                DestroyTimer(woundSound[whichUnit].woundTimer)
            end
            if ENABLE_CRIT_SOUNDS then
                DestroyTimer(woundSound[whichUnit].woundCritTimer)
            end
        end
        DestroyTrigger(woundSound[whichUnit].damageTrigger)
		if AUTO_CLEANUP_ON_DEATH and not IsUnitType(whichUnit, UNIT_TYPE_HERO) then
			DestroyTrigger(woundSound[whichUnit].deathTrigger)
		end
        woundSound[whichUnit] = nil
    end
 
    ---@param whichUnit unit
    ---@param pause boolean
    function PauseWoundSoundsForUnit(whichUnit, pause)
        woundSound[whichUnit].isPaused = pause
    end
 
    ---@param whichUnit unit
    ---@param percentage number
    function AdjustWoundSoundVolumeForUnit(whichUnit, percentage)
        woundSound[whichUnit].volumePercent = math.min(100,percentage)
    end
 
end
Contents

Hero Wound Sounds (Map)

HeroWoundSoundsLua (Map)

Reviews
Wrda
You could use EVENT_PLAYER_UNIT_DAMAGED since it combined with the AnyUnitBJ would result in just 1 trigger for all units registered. This is easier to do in Lua as if not woundSound[u] then would then mean unit isn't registered at all, then skip. Due...

Wrda

Spell Reviewer
Level 26
Joined
Nov 18, 2012
Messages
1,888
You could use EVENT_PLAYER_UNIT_DAMAGED since it combined with the AnyUnitBJ would result in just 1 trigger for all units registered. This is easier to do in Lua as if not woundSound[u] then would then mean unit isn't registered at all, then skip. Due to fact that the system doesn't clean the removal of units from the hashtable/table when they don't exist anymore, it would a good idea to put a note that it requires manual removal (wound sounds can also be applied to non-hero units). You should upload another map, with the lua version code in it, it's faster to test, and even copy (if the map contained libraries).
Otherwise, this is pretty nice.

Approved
 

Wrda

Spell Reviewer
Level 26
Joined
Nov 18, 2012
Messages
1,888
That's the case with blizzard.j transpiled Lua. bj_lastStartedTimer used in StartTimerBJ desyncs. So using GUI and starting timers, you go poof.
1709252411591.png


Only most critical thing not to do inside of lua root (whether inside of do ... end or not) is to never create wc3 objects at this point, since most Wc3 functions arent' available. FourCC is one of the few.
If you do create objects like that, Wc3 invites you to take a swim to the deep down ocean of the game main menu.
 
You could use EVENT_PLAYER_UNIT_DAMAGED since it combined with the AnyUnitBJ would result in just 1 trigger for all units registered. This is easier to do in Lua as if not woundSound[u] then would then mean unit isn't registered at all, then skip. Due to fact that the system doesn't clean the removal of units from the hashtable/table when they don't exist anymore, it would a good idea to put a note that it requires manual removal (wound sounds can also be applied to non-hero units). You should upload another map, with the lua version code in it, it's faster to test, and even copy (if the map contained libraries).
Otherwise, this is pretty nice.

Approved
I uploaded a Lua test map and renamed the functions from DoXToHero to DoXToUnit, to show that the unit doesn't necessarily have to be a hero. I added an option to automatically clear leaks on a nonhero unit's death. I still think it's better to have a trigger for each unit separately, because if it's going to be used on nonhero units, it's probably only going to be a few, and having a specific trigger for those units prevents it from firing on every damage event, which I assume must be more of a performance concern than a few memory leaks here and there.

Thanks for the review!
 
Top