Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
LUA Unlimited Unit Sound Set System Originally created in JASS by Baradé
Ported to LUA and extended by Macielos
Using custom unit voices (self-recorded, imported from WoW or other games, generated using Elevenlabs etc.) has always had lots of limitations. You had to override sound files of existing characters and only had limited number of voicelines of each sound type.
For some time I've been working on a system to overcome those limitations. Long ago I found an old system in JASS by Baradé, took some parts of it and rewrote in LUA, vastly extended, refactored and added some new features. And I believe I can finally show it for the community to use. So here it is .
Features
Source Code
Usage
Notes & Credits
• Native unit sounds logic (on click voices, on order voices, pissed voices etc.) recreated in LUA
• Works for player units as well as allies with shared control
• Assign custom voicelines to unit types with single lines of script, without overiding existing sounds. The files only need to follow a naming convention (your prefix + sound type + number) and you can just mass import them to your map
• (Practically) unlimited number of voiceline files for each sound type. You're so creative that you recorded 20 pissed sounds or 8 attack sounds for your unit? Now you can use them all!
• New voiceline type - voicelines played on usage of specific ability by a unit type (randomly if multiple ones are configured for one ability)
• You can add multiple sound sets additively and reuse voicelines for multiple unit types, creating sound sets that share some of the lines, e.g. for a specific maps in the campaign or for different variants of the same unit
• Extra utility function to track player's main selected unit
Contribute to Macielos/Warcraft3LuaLibs development by creating an account on GitHub.
github.com
1. Import the library
Make sure your map has scripting language set to LUA in map options
Copy Libraries folder from test map's triggers to your map - OR if you prefer to copy dependencies separately (or you have them already), just copy UnitSoundSetsAndUtils folder
If you also use my Dialog System, you will find some scripts that are shared across the two. They are all in UnitSoundSets/Utils folder. They are (or at least should be) backward-compatible, so, just take them from the resource that’s newer (based on asset update date on Hive)
2. Import your sound files
- Supported extensions are .mp3, .wav and .flac. Best to use .mp3 format because of its small file size.
- Sound files for each unit type should start with the same prefix (it can contain a path, e.g. war3mapImported/Paladin), followed by a sound type and a sound number. Generally it follows Blizzard’s convention. Here's how the example sound set files could be named:
On-attack sounds (2 alternative suffixes, you can use either Attack or YesAttack):
Attack1
Attack2
...
OR
YesAttack1
YesAttack2
...
Sounds played when unit's trained/hired/revived:
Ready1
Sounds played with a certain chance when attacking a hero:
Warcry1
Secret/Hidden/Pissed sounds (again, several alternatives available because Nyctaeus and I couldn't stick to a single convention when recording our sound sets):
Pissed1
Pissed2
...
OR
Hidden1
Hidden2
...
OR
Gag1
Gag2
...
Ability sounds: played when a unit casts a specific ability. Here you can give any name if there's only one sound. If you specify multiple sounds for an ability, they should share a prefix and be ended with a proper number:
war3mapImported/PaladinHolyLight1.mp3
war3mapImported/PaladinHolyLight2.mp3
war3mapImported/PaladinHolyLight3.mp3
Yes, we implemented Margoth's Torrent of Profanity on this in Exodus 2.
3. Assign sound sets to unit types
Make sure your unit's sound set in object editor is set to NONE (so that it doesn't overlap with the custom one)
Place a script that will add sound sets on map initialization. Here are some examples (using Total Initialization library, but you can also call UnitSoundSets methods in your own scripts or triggers):
Lua:
do
OnInit.final(function()
--import sound set for a Paladin taking files imported as war3mapImported\Paladin<soundType><number>
UnitSoundSets:addUnitSoundSet(FourCC('Hpal'), 'war3mapImported\\Paladin')
--import ability single sound for a Paladin casting Divine Shield
UnitSoundSets:addUnitAbilitySingleSound(FourCC('Hpal'), FourCC('AHds'), 'war3mapImported\\PaladinDivineShield')
--import ability multiple sounds for a Paladin casting Holy Light - this will search for files war3mapImported\PaladinHolyLight1, war3mapImported\PaladinHolyLight2 etc. for the consequtive numbers unless no sound is found
UnitSoundSets:addUnitAbilityMultipleSounds(FourCC('Hpal'), FourCC('AHhb'), 'war3mapImported\\PaladinHolyLight')
end)
end
Lua:
do
OnInit.final(function()
--a more complex example - You have footman versions for every kingdom. A footman can have a basic sound set,
--but you can also define some voices specific for different kingdoms
local FOOTMAN_LORDAERON = FourCC('U123')
local FOOTMAN_STROMGARDE = FourCC('U124')
local FOOTMAN_GILNEAS = FourCC('U125')
UnitSoundSets:addUnitSoundSet(FOOTMAN_LORDAERON, 'war3mapImported\\FootmanBase')
UnitSoundSets:addUnitSoundSet(FOOTMAN_STROMGARDE, 'war3mapImported\\FootmanBase')
UnitSoundSets:addUnitSoundSet(FOOTMAN_GILNEAS, 'war3mapImported\\FootmanBase')
UnitSoundSets:addUnitSoundSet(FOOTMAN_LORDAERON, 'war3mapImported\\FootmanLordaeron')
UnitSoundSets:addUnitSoundSet(FOOTMAN_STROMGARDE, 'war3mapImported\\FootmanStromgarde')
UnitSoundSets:addUnitSoundSet(FOOTMAN_GILNEAS, 'war3mapImported\\FootmanGilneas')
end)
end
Lua:
-- an example from the demo map which adds custom voices for Reynart and Inaylia as well as for Inaylia's abilities:
do
--your unit codes from object editor
local REYNART = FourCC('H000')
local INAYLIA = FourCC('H001')
--your ability codes from object editor
local KNIVES = FourCC('AEfk')
local SHIFT = FourCC('AEbl')
local STEALTH = FourCC('AOwk')
local LIQUIDATION = FourCC('ANfd')
local PREFIX = 'war3mapImported\\'
--just some utility functions so you don't have to repeat the full path for every unit
local function addSoundSet(unitId, filePrefix)
UnitSoundSets:addUnitSoundSet(unitId, PREFIX .. filePrefix)
end
local function addAbilitySoundSet(unitId, abilityId, filePrefix)
UnitSoundSets:addUnitAbilitySingleSound(unitId, abilityId, PREFIX .. filePrefix)
end
--your function, it will be called on map initialization
OnInit.final(function()
print('InitDemoSoundSets START')
addSoundSet(REYNART, 'Reynart')
addSoundSet(INAYLIA, 'Inaylia')
addAbilitySoundSet(INAYLIA, KNIVES, 'InalkaKnives')
addAbilitySoundSet(INAYLIA, SHIFT, 'InalkaShift')
addAbilitySoundSet(INAYLIA, STEALTH, 'InalkaStealth')
addAbilitySoundSet(INAYLIA, LIQUIDATION, 'InalkaLiquidation')
print('InitDemoSoundSets DONE')
end)
end
Below you can find a basic API.
Lua:
--Add custom sound set for given unit type, reading sound files with given prefix
UnitSoundSets:addUnitSoundSet(unitTypeId, filePathPrefix)
--Remove custom sound sets from all units
UnitSoundSets:removeAllUnitSoundSets()
--Remove a sound set from a unit type
UnitSoundSets:removeUnitSoundSet(unitTypeId)
--true if a unit type has custom sound set defined
UnitSoundSets:hasUnitSoundSet(unitTypeId)
--true if a unit type has custom sound defined when casting a specific ability
UnitSoundSets:hasAbilitySoundSet(unitTypeId, abilityCode)
--Add custom sound when a unit of specific type casts a specific ability, in that case a number is not added to filePath
UnitSoundSets:addUnitAbilitySingleSound(unitTypeId, abilityId, filePath)
--Add custom sounds when a unit of specific type casts a specific ability
UnitSoundSets:addUnitAbilityMultipleSounds(unitTypeId, abilityId, filePathPrefix)
Unsupported features from Baradé's system:
Syncing user selections in a multiplayer game (to be honest, I couldn't really figure out what the point was of that code)
Tracking user selections per player - now I only track the local player
Unit sounds per unit instance
Shop's custom sound set
Limitations:
The library was made mainly with singleplayer in mind, it hasn't been tested in multiplayer so far, there may be desyncs
The library creates custom UI frames - health and mana "fake bars" to display when original ones are hidden. If you use customized UI, these fake bars' positions may not match the original (so they are in a different position when a unit is speaking). If that's the case, you may need to adjust these constants in FakeBars script:
local FAKE_BAR_X = 0.2145
local FAKE_HP_BAR_Y = 0.027
local FAKE_MANA_BAR_Y = 0.0135
Width/height is adjusted automatically, but x/y positions are hardcoded because there's no native to get them.
Dependencies:
- Debug Utils (in theory you don't need them, but IMO they're a must for anyone working with LUA)
Debug Utils 2.5b A debug library for Lua mapmaking that will save you tons of time. Feature Overview: Automatic ingame error messages. Ingame Console (execute code via Wc3 ingame chat). Debug-Library functions for error handling and investigation. Automatic warnings upon reading nil-globals...
Total Initialization Allows you to initialize your script in a safe, easy-to-debug environment. Allows your map's triggers to be arranged in any order* (similar to JassHelper). Enables functionality similar to Lua modules and vJass libraries. Provides helpful tools to hook into certain key...
www.hiveworkshop.com
Changelog:
0.0.1
- Initial version
0.0.2
- Rearranged folders in trigger editor
0.0.3
Removed limits for the numbers of sound files - the lib will search for files with given prefix, e.g PaladinWhat1, PaladinWhat2 etc. and stop when no sound is found
Sounds no longer play when a unit is paused or sleeping
Sounds no longer play in cutscenes (it previously happened when a player had a unit selected, a cinematic started and a unit received an order via trigger)
Sounds no longer play when another sound is playing (so when we quickly select different units, we don't hear overlapping sounds)
0.0.4
- Made FakeBars use dynamic size from original health/mana bar frames. Adjusted x/y positions to better match originals
Credits:
- Baradé - for his original JASS system that used to be here:
Motivation This system helps you using custom unit sound sets without replacing existing ones. Usually, you would import the sound set files and use paths of an existing sound set, making the existing one unavailable for your map. This system allows you registering unit sound sets with different...
www.hiveworkshop.com
Despite heavy changes and new features I still use some parts of his solution rewritten in LUA. Unfortunately he's no longer on Hive and didn't leave any contact info, so I couldn't reach him for permission, but my changes are so significant that I hope it won't be a problem. If he returns one day, I'll surely contact him.
TL;DR: Say goodbye to replacing Blizzard lines, messing with filenames, fixed amount of vanilla lines and all the stupid stuff that disencourage people from making custom unit voicelines in their projects.
Why does the user have to import all these debug functions? Please remove everything that isn't needed from the folder that's supposed to be imported. Similarly, virtually everyone has DebugUtils installed. There's no need to put that into the same folder.
If you set the UI scale differently it shifts the HP.
Other questions :
☼ Is it synced multiplayer ?
☼ How do you check for sound length without calling the async GetSoundLength (can't remember the name) ?
☼ Does it work with localized sounds ?
Why does the user have to import all these debug functions? Please remove everything that isn't needed from the folder that's supposed to be imported. Similarly, virtually everyone has DebugUtils installed. There's no need to put that into the same folder.
Hmm, I see. Right now the positions are hardcoded as in the original system, so you can adjust them at the top of FakeBars script. But I'll see if I can get these positions from the current UI frames.
What do you mean? There's no data synced between players, voicelines are played for the local player only. But to be honest, I never made a multiplayer map, so maybe there are some issues I need to learn about.
Yes, I had the exact issue. It immediately throws a desync with everyone ; even if all players have the same locale.
I had to hardcode a 1.5s default duration ; too lazy yet to insert all durations one by one (and it gets more complicated if localized soundsets are implemented...).
What do you mean? There's no data synced between players, voicelines are played for the local player only. But to be honest, I never made a multiplayer map, so maybe there are some issues I need to learn about.
If we have to use a fake health and mana bar some of the time, why are we not just using it all of the time?
The MAX_SOUNDS constants are completely unnecessary, as far as I can tell. You already have logic to determine how many sound files of a certain type are imported, so there is no need to hardcode a limit.
The SOUND_DEATH, SOUND_WHAT etc. constants should also not be in the config because there is not reason to change them. These could also just be string literals instead of integers actually, but that's just a nitpick/personal preference.
Why are you using colon syntax? You're not using the self parameter in any of the functions.
Okay, I made a version that takes these coordinates from frame. I could only take width/height, since there is no native to get x/y positions of frame points. X/Y positions I adjusted manually by some magic numbers stuff. You can try if it's better now.
Although it could be dependent on screen resolution - previous hardcoded numbers were working for me both on wide and laptop monitor. And of course, if you use customized fdf files for these UI elements, they won't match - you'd have to adjust the positions in my script.
I'd have to hide the original bars, and I'd rather avoid doing that because blizz will change UI structure and my script will break the game like it happened already in the past. Maybe I could display mine on top of the original, but then their positions would have to match anyway.
The MAX_SOUNDS constants are completely unnecessary, as far as I can tell. You already have logic to determine how many sound files of a certain type are imported, so there is no need to hardcode a limit.
The SOUND_DEATH, SOUND_WHAT etc. constants should also not be in the config because there is not reason to change them. These could also just be string literals instead of integers actually, but that's just a nitpick/personal preference.
Yeah, they're constants as you said. It should be more clear now after I removed the limits, so in fact there's no config someone might want to modify in standard cases.
Okay, I made a version that takes these coordinates from frame. I could only take width/height, since there is no native to get x/y positions of frame points. X/Y positions I adjusted manually by some magic numbers stuff. You can try if it's better now.
Although it could be dependent on screen resolution - previous hardcoded numbers were working for me both on wide and laptop monitor. And of course, if you use customized fdf files for these UI elements, they won't match - you'd have to adjust the positions in my script.
The mismatch seems to be resolution-dependent, so I guess there's no way to make it perfect. RIP
I'd have to hide the original bars, and I'd rather avoid doing that because blizz will change UI structure and my script will break the game like it happened already in the past. Maybe I could display mine on top of the original, but then their positions would have to match anyway.
This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
By continuing to use this site, you are consenting to our use of cookies.