跳到正文
Hiveworkshop· Macielos·· 3 小时前AI 评分18

LUA 无限单位语音集 0.0.6

LUA Unlimited Unit Sound Sets 0.0.6

AI 导读

Macielos 发布 LUA 无限单位语音集 0.0.6,可将自定义语音分配给地图单位类型而不覆盖原有音效。该系统由 Baradé 的 JASS 版移植并重写,支持 .mp3/.wav/.flac 格式与技能触发语音,每种声音类型支持几乎无限的文件数,但仅限单人使用,多人会导致 desync,源代码已公开于 Github。

正文

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 ;).

WARN: It was written for single player use. It causes desyncs on multi.

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

The source code is available on Github:

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:

war3mapImported/PaladinReady1.mp3
war3mapImported/PaladinDeath1.mp3
war3mapImported/PaladinWarcry1.mp3
war3mapImported/PaladinWhat1.mp3
war3mapImported/PaladinWhat2.mp3
war3mapImported/PaladinWhat3.mp3
war3mapImported/PaladinYes1.mp3
war3mapImported/PaladinYes2.mp3
war3mapImported/PaladinYesAttack1.mp3
war3mapImported/PaladinYesAttack2.mp3
war3mapImported/PaladinPissed1.mp3
war3mapImported/PaladinPissed2.mp3
war3mapImported/PaladinPissed3.mp3
war3mapImported/PaladinPissed4.mp3

- The following sound types are supported:

Death sounds:
Death1

On-click sounds:
What1
What2
...

On-order (move, patrol etc.) sounds:
Yes1
Yes2
...

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

You can now define a custom condition to decide whether to play custom sounds. I use it in combination with my screenplay system so that sounds don't overlap with sounds from screenplays:

Lua:

UnitSoundSets:addCustomCondition(function() return not ScreenplaySystem:isActive() 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)

--Add a custom condition (a boolean-returning function) whether to play a custom sounds. Whenever a unit is selected/ordered/etc., this function is called and when it returns false, a sound won't be played. It's best to call this on map initialization, of course after initializing sound sets.
UnitSoundSets:addCustomCondition(boolReturningFunc)

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)

www.hiveworkshop.com

Lua Debug Utils (incl. Ingame Console)

Debug Utils 2.6 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...

www.hiveworkshop.com www.hiveworkshop.com

- Total Initialization:

www.hiveworkshop.com

[Lua] - Total Initialization

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 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

0.0.5
- Added a feature to add a custom condition to decide when to play custom sound. I use it in combination with my screenplay system so that sounds don't overlap with sounds from screenplays:
UnitSoundSets:addCustomCondition(function() return not ScreenplaySystem:isActive() end)
- Cleaned up some code in UnitSoundSets and FakeBars - removed use of handles and hashtables. I didn't need them anyway as I only use the library for local player

0.0.6

  • Fixed crashes on Reforged 3.0 (caused by Blizz now destroying frames to handle 12/24 unit selection)
  • Allow to refer to speaking unit in custom condition, e.g.:

UnitSoundSets:addCustomCondition(function(unit) return GetUnitTypeId(unit) ~= SOME_TYPE end)
-
Failed attempt to make it run on multiplayer - removed some code that may have caused desyncs, but desync still occur. I may return to this later.

Credits:
- Baradé - for his original JASS system that used to be here:

www.hiveworkshop.com

Baradé's Unit Sound Sets 1.0

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 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.

来源:Hiveworkshop · hiveworkshop.com