1. Are you planning to upload your awesome spell or system to Hive? Please review the rules here.
    Dismiss Notice
  2. The poll for our 11th Music Contest is up! Help us choose the most awesome cinematic tracks by casting a vote!
    Dismiss Notice
  3. Melee Mapping contest #3 - Poll is up! Vote for the best 4v4 melee maps!
    Dismiss Notice
  4. The 30th edition of the Modeling Contest is finally up! The Portable Buildings need your attention, so come along and have a blast!
    Dismiss Notice
  5. The Aftermath has been revealed for the 19th Terraining Contest! Be sure to check out the Results and see what came out of it.
    Dismiss Notice

Revivable Units v1.3.1

Submitted by loktar
This bundle is marked as approved. It works and satisfies the submission rules.
Revivable Unit System v1.3.1
by loktar

  • Limit availability of unit types to 1
  • Create a dissipate effect when those units die
  • Create a hero revive effect when those units are revived/trained

API
GUI

Note that in order to use the GUI configuration, you also need all the triggers in the "Revive Config Functions" category, as well as all the variables and their initial values.
Configure Unit-Types

NOTE: Don't forget that units have to be added to a building (e.g. Altar) manually in order to build them.
  • SetUnitsGUI
    • Events
      • Map initialization
    • Conditions
    • Actions
      • -------- Footman for All Players --------
      • Set UnitType = Footman
      • Set Players = AllPlayers
      • Trigger - Run AddType <gen> (ignoring conditions)
      • -------- Grunt for All Players except Player 2 (blue) --------
      • Set UnitType = Grunt
      • Set Players = AllPlayers
      • Trigger - Run AddType <gen> (ignoring conditions)
      • Set Players = (Player number of Player 2 (Blue))
      • Trigger - Run RemoveType <gen> (ignoring conditions)
      • -------- Ghoul for Player 1 (red) --------
      • Set UnitType = Ghoul
      • Set Players = (Player number of Player 1 (Red))
      • Trigger - Run AddType <gen> (ignoring conditions)
      • -------- Ghoul available normally for other players --------
      • Set AllowAll = True
      • Trigger - Run AllowNormalTraining <gen> (ignoring conditions)
      • -------- Archer for Player 2 (blue) --------
      • Set UnitType = Archer
      • Set Players = (Player number of Player 2 (Blue))
      • Trigger - Run AddType <gen> (ignoring conditions)
      • -------- Fel Stalker for Player 1 & 2 (red & blue) --------
      • Set UnitType = Fel Stalker
      • Set Players = (Player number of Player 1 (Red))
      • Trigger - Run AddType <gen> (ignoring conditions)
      • Set Players = (Player number of Player 2 (Blue))
      • Trigger - Run AddType <gen> (ignoring conditions)
      • -------- Priest (Blood Mage model) for Player 1 (red) - using Dissipate animation --------
      • Set UnitType = Priest
      • Set Players = (Player number of Player 1 (Red))
      • Trigger - Run AddType <gen> (ignoring conditions)
      • Trigger - Run HasDissAnim <gen> (ignoring conditions)


Configure Dissipate

NOTE: Anything left to default can be deleted, since defaults are already set in the code. Only included here for demonstration.
  • ConfigureDissipateGUI
    • Events
      • Map initialization
    • Conditions
    • Actions
      • -------- Configure the Dissipate Effect --------
      • -------- -- These are the default settings --------
      • Set confDelay = 3.00
      • Set confOpacity = 200
      • Set confDuration = 2.00
      • Set confIncrement = 0.03
      • Set confHeight = 530.00
      • Trigger - Run ConfigureDissipate <gen> (ignoring conditions)
      • -------- Set Dissipate Effect per race --------
      • -------- -- These are the default settings --------
      • -------- -- Only Undead has a dissipate model w/ sound --------
      • Set Race = Undead
      • Set EffectString = Objects\Spawnmodels\Undead\UndeadDissipate\UndeadDissipate.mdl
      • Set Sound = No sound
      • Trigger - Run ConfigureDissSFX <gen> (ignoring conditions)
      • -------- -- Rest has no dissipate model, so setup sound --------
      • Set Race = Human
      • Set EffectString = <Empty String>
      • Set Sound = HumanDissipate1 <gen>
      • Trigger - Run ConfigureDissSFX <gen> (ignoring conditions)
      • Set Race = Orc
      • Set EffectString = <Empty String>
      • Set Sound = OrcDissipate1 <gen>
      • Trigger - Run ConfigureDissSFX <gen> (ignoring conditions)
      • Set Race = Night Elf
      • Set EffectString = <Empty String>
      • Set Sound = NightElfDissipate1 <gen>
      • Trigger - Run ConfigureDissSFX <gen> (ignoring conditions)
      • -------- -- Demon doesn't have its own diss sound --------
      • Set Race = Demon
      • Set EffectString = <Empty String>
      • Set Sound = HumanDissipate1 <gen>
      • Trigger - Run ConfigureDissSFX <gen> (ignoring conditions)
      • -------- -- 11 possible races (1 - Human, 2 - Orc, 3 - Undead, 4 - Nightelf, 5 - Demon, 7 - Other, 11 - Naga) --------
      • For each (Integer A) from 6 to 11, do (Actions)
        • Loop - Actions
          • Custom script: set udg_Race = ConvertRace(bj_forLoopAIndex)
          • Set EffectString = <Empty String>
          • Set Sound = HumanDissipate1 <gen>
          • Trigger - Run ConfigureDissSFX <gen> (ignoring conditions)


Configure Revive

NOTE: Anything left to default can be deleted, since defaults are already set in the code. Only included here for demonstration.
  • ConfigureReviveGUI
    • Events
      • Map initialization
    • Conditions
    • Actions
      • -------- Set Revive Effect per race --------
      • -------- -- These are the default settings --------
      • -------- -- Revive effects have sound attached, except Demon --------
      • Set Race = Human
      • Set EffectString = Abilities\Spells\Human\ReviveHuman\ReviveHuman.mdl
      • Set Sound = No sound
      • Trigger - Run ConfigureRevSFX <gen> (ignoring conditions)
      • Set Race = Orc
      • Set EffectString = Abilities\Spells\Orc\ReviveOrc\ReviveOrc.mdl
      • Set Sound = No sound
      • Trigger - Run ConfigureRevSFX <gen> (ignoring conditions)
      • Set Race = Undead
      • Set EffectString = Abilities\Spells\Undead\ReviveUndead\ReviveUndead.mdl
      • Set Sound = No sound
      • Trigger - Run ConfigureRevSFX <gen> (ignoring conditions)
      • Set Race = Night Elf
      • Set EffectString = Abilities\Spells\NightElf\ReviveNightElf\ReviveNightElf.mdl
      • Set Sound = No sound
      • Trigger - Run ConfigureRevSFX <gen> (ignoring conditions)
      • Set Race = Demon
      • Set EffectString = Abilities\Spells\Demon\ReviveDemon\ReviveDemon.mdl
      • Set Sound = ReviveHuman <gen>
      • Trigger - Run ConfigureRevSFX <gen> (ignoring conditions)
      • -------- -- 11 possible races (1 - Human, 2 - Orc, 3 - Undead, 4 - Nightelf, 5 - Demon, 7 - Other, 11 - Naga) --------
      • For each (Integer A) from 6 to 11, do (Actions)
        • Loop - Actions
          • Custom script: set udg_Race = ConvertRace(bj_forLoopAIndex)
          • Set EffectString = Abilities\Spells\Human\ReviveHuman\ReviveHuman.mdl
          • Set Sound = No sound
          • Trigger - Run ConfigureRevSFX <gen> (ignoring conditions)


Configure Abilities

NOTE: Both abilFly AND abilSelect must be set before running ConfigureAbilities. If both are left to default, no need to set them here as long as ConfigureAbilities is not run. The default meld abilities do not need to be added here, so they can be deleted. They are only included here to demonstrate how to add custom meld abilities.
  • ConfigureAbilitiesGUI
    • Events
      • Map initialization
    • Conditions
    • Actions
      • -------- These are the default settings --------
      • -------- Set Fly Ability (used to lift unit during dissipate) --------
      • Set abilFly = Storm Crow Form
      • -------- Set Locust Ability (used to make unit unselectable) --------
      • Custom script: set udg_abilSelect = 'Aloc'
      • Trigger - Run ConfigureAbilities <gen> (ignoring conditions)
      • -------- Add Meld Abilities (removed on death) --------
      • Set abilMeld = Shadow Meld
      • Trigger - Run RemoveAbilOnDeath <gen> (ignoring conditions)
      • Set abilMeld = Shadow Meld (Instant)
      • Trigger - Run RemoveAbilOnDeath <gen> (ignoring conditions)
      • Set abilMeld = Shadow Meld (Akama)
      • Trigger - Run RemoveAbilOnDeath <gen> (ignoring conditions)




JASS

You can use these functions either in a "Custom script" or in plain JASS custom text.
For JASS configuration, you do not need any of the triggers in the "Revive Config Functions" category, nor any of the variables.
Configure Unit-Types

NOTE: Don't forget that units have be added to a building (e.g. Altar) manually in order to build them.
  • SetUnitsJASS
    • Events
      • Map initialization
    • Conditions
    • Actions
      • -------- Footman for All Players --------
      • Custom script: call SetTypeRevivable('hfoo', 16)
      • -------- Grunt for All Players except Player 2 (blue) --------
      • Custom script: call SetTypeRevivable('ogru', 16)
      • Custom script: call UnsetTypeRevivable('ogru', 1)
      • -------- Ghoul for Player 1 (red) --------
      • Custom script: call SetTypeRevivable('ugho', 0)
      • -------- Ghoul available normally for other players --------
      • Custom script: call AllowNormalTraining('ugho', true)
      • -------- Archer for Player 2 (blue) --------
      • Custom script: call SetTypeRevivable('earc', 1)
      • -------- Fel Stalker for Player 1 & 2 (red & blue) --------
      • Custom script: call SetTypeRevivable('nfel', 0)
      • Custom script: call SetTypeRevivable('nfel', 1)
      • -------- Priest (Blood Mage model) for Player 1 (red) - using Dissipate animation --------
      • Custom script: call SetTypeRevivable('hmpr', 0)
      • Custom script: call SetTypeHasDissipate('hmpr', true)


Configure Dissipate

NOTE: Anything left to default can be deleted, since defaults are already set in the code. Only included here for demonstration.
  • ConfigureDissipateJASS
    • Events
      • Map initialization
    • Conditions
    • Actions
      • -------- Configure the Dissipate Effect --------
      • -------- -- These are the default settings --------
      • Custom script: call ConfigureDissipate(3, 200, 2, 0.03, 530)
      • -------- Set Dissipate Effect per race --------
      • -------- -- These are the default settings --------
      • -------- -- Only Undead has a dissipate model w/ sound --------
      • Custom script: call ConfigureDissSFX(RACE_UNDEAD, "Objects\\Spawnmodels\\Undead\\UndeadDissipate\\UndeadDissipate.mdl", null)
      • -------- -- Rest has no dissipate model, so setup sound --------
      • Custom script: call ConfigureDissSFX(RACE_HUMAN, null, gg_snd_HumanDissipate1)
      • Custom script: call ConfigureDissSFX(RACE_ORC, null, gg_snd_OrcDissipate1)
      • Custom script: call ConfigureDissSFX(RACE_NIGHTELF, null, gg_snd_NightElfDissipate1)
      • -------- -- Demon doesn't have its own diss sound --------
      • Custom script: call ConfigureDissSFX(RACE_DEMON, null, gg_snd_HumanDissipate1)
      • -------- -- 11 possible races (1 - Human, 2 - Orc, 3 - Undead, 4 - Nightelf, 5 - Demon, 7 - Other, 11 - Naga) --------
      • For each (Integer A) from 6 to 11, do (Actions)
        • Loop - Actions
          • Custom script: call ConfigureDissSFX(ConvertRace(bj_forLoopAIndex), null, gg_snd_HumanDissipate1)


Configure Revive

NOTE: Anything left to default can be deleted, since defaults are already set in the code. Only included here for demonstration.
  • ConfigureReviveJASS
    • Events
      • Map initialization
    • Conditions
    • Actions
      • -------- Set Revive Effect per race --------
      • -------- -- These are the default settings --------
      • -------- -- Revive effects have sound attached, except Demon --------
      • Custom script: call ConfigureRevSFX(RACE_HUMAN, "Abilities\\Spells\\Human\\ReviveHuman\\ReviveHuman.mdl", null)
      • Custom script: call ConfigureRevSFX(RACE_ORC, "Abilities\\Spells\\Orc\\ReviveOrc\\ReviveOrc.mdl", null)
      • Custom script: call ConfigureRevSFX(RACE_UNDEAD, "Abilities\\Spells\\Undead\\ReviveUndead\\ReviveUndead.mdl", null)
      • Custom script: call ConfigureRevSFX(RACE_NIGHTELF, "Abilities\\Spells\\NightElf\\ReviveNightElf\\ReviveNightElf.mdl", null)
      • Custom script: call ConfigureRevSFX(RACE_DEMON, "Abilities\\Spells\\Demon\\ReviveDemon\\ReviveDemon.mdl", gg_snd_ReviveHuman)
      • -------- -- 11 possible races (1 - Human, 2 - Orc, 3 - Undead, 4 - Nightelf, 5 - Demon, 7 - Other, 11 - Naga) --------
      • For each (Integer A) from 6 to 11, do (Actions)
        • Loop - Actions
          • Custom script: call ConfigureRevSFX(ConvertRace(bj_forLoopAIndex), "Abilities\\Spells\\Human\\ReviveHuman\\ReviveHuman.mdl", null)


Configure Abilities

NOTE: Anything left to default can be deleted, since defaults are already set in the code. Only included here for demonstration.
  • ConfigureAbilitiesJASS
    • Events
    • Conditions
    • Actions
      • -------- These are the default settings --------
      • -------- Add Meld Abilities (removed on death) --------
      • Custom script: call RemoveAbilOnDeath('Ashm')
      • Custom script: call RemoveAbilOnDeath('Sshm')
      • Custom script: call RemoveAbilOnDeath('Ahid')
      • -------- Set Fly Ability (used to lift unit during dissipate) --------
      • Custom script: call SetFlyAbil('Arav')
      • -------- Set Locust Ability (used to make unit unselectable) --------
      • Custom script: call SetSelectAbil('Aloc')




Test Map Info
  • In this test map, the following chat commands are available:
    • "kill footman", "kill grunt", "kill ghoul", "kill archer", "kill stalker", "kill priest" and "kill hero".
  • Use the "warpten" cheat to speed up training
  • There are some enemies north-west of the starting location

Thanks to BPower, Bribe, DracoL1ch, KILLCIDE, PurgeandFire, Wietlol for their help.

Code

Code (vJASS):

//===========================================================================
//
//  Revivable Unit System v1.3.1
//  by loktar
//  -------------------------------------------------------------------------
//  * Limit availability of unit types to 1
//  * Create a dissipate effect when those units die
//  * Create a hero revive effect when those units are revived/trained
//  -------------------------------------------------------------------------
//
//    -------
//    * API *
//    -------
//  *    SetTypeRevivable(integer unitTypeId, integer playerNumber)
//          -> Make a unit type revivable for player(s).
//              -playerNumber: player 0-15; 16 for all players
//              -Players not set through this function will be unable to build the unit by default
//                 -Returns true on success, false on failure.
//
//  *    UnsetTypeRevivable(integer unitTypeId, integer playerNumber)
//            -> Make a unit type not revivable for player(s).
//              -playerNumber: player 0-15; 16 for all players
//                 -Returns true on success, false on failure.
//
//  *    AllowNormalTraining(integer unitTypeId, boolean allow)
//          -> Allow all other players to build the unit normally.
//                 -Returns true on success, false on failure.
//
//  *   SetTypeHasDissipate(integer unitTypeId, boolean hasDiss)
//          -> Enable usage of dissipate animation
//                 -Returns true on success, false on failure.
//
//    *    ConfigureDissipate(    real    Delay,
//                            integer StartingOpacity,
//                            real    Duration,
//                            real    TimerIncrement,
//                            integer Height    )
//            -> Configure the dissipate effect.
//              -Default settings: call ConfigureDissipate(3.0, 200, 2.0, 0.03, 530)
//                 -Input null to keep current setting
//                 -Returns true on success, false on failure.
//
//  *    ConfigureDissSFX(race wRace, string wEffect, sound wSound)
//  *    ConfigureRevSFX(race wRace, string wEffect, sound wSound)
//          -> Set the dissipate and revive effect model and sound per race.
//
//  *   SetFlyAbil(integer newAbilFly)
//  *   SetSelectAbil(integer newAbilSelect)
//          -> Set the flying (lift unit during dissipate) and locust (make unit unselectable) abilities
//
//  *   RemoveAbilOnDeath(integer newAbilMeld)
//          -> Add custom meld abilities (removed on unit death)
//                 -Returns true on success, false on failure.
//
//  ----------------------------------------------
//  ****  Look at the triggers for examples   ****
//  ----------------------------------------------
//
//  !! Note that you still have to add the unit type to the Altar (or any other building) !!
//  -------------------------------------------------------------------------
//
//    -----------------
//     * Test Map Info *
//    -----------------
//  *    In this test map, the following chat commands are available:
//          "kill footman", "kill grunt", "kill ghoul", "kill archer", "kill stalker", "kill priest" and "kill hero".
//
//  *    Use the "warpten" cheat to speed up training
//
//===========================================================================
library RevivableUnits initializer InitRevivable
    globals
        //revivable unit storage
        private integer array        revTypes
        private boolean array        revPlayers
        private integer array        revAllowAll
        private boolean array        revHasDissipate
        private integer                revSize                = 0
        private constant integer    ANY_P               = -1
        private constant integer    ALL_P               = bj_MAX_PLAYER_SLOTS
        private constant integer    INDEX_P             = bj_MAX_PLAYER_SLOTS + 1
        //dissipating unit storage
        private hashtable            dTable                = InitHashtable()
        //sounds & effects
        private sound array            dSound[12]
        private sound array            rSound[12]
        private string array        dEffect[12]
        private string array        rEffect[12]
        //configure dissipate effect
        private real                dissDelay            = 3 //delay after start of death animation before dissipating
        private integer                dissOpacity            = 200 //starting opacity after ^
        private real                dissDuration        = 2 //duration of the dissipate effect
        private real                dissIncrement        = 0.03 //timer duration for the fade effect
        private real                dissHeight            = 530 //flyheight
        private integer             dissOpacityIncr        = R2I(I2R(dissOpacity)*dissIncrement/dissDuration)
        //abilities
        private integer                abilFly                = 'Arav'
        private integer                abilSelect            = 'Aloc'
        private integer array        abilMeld
        private integer                abilMeldSize        = 0
    endglobals
   
    //===========================================================================
    // "Global" functions
    //===========================================================================  
        //===========================================================================
        // Get Player Color
        //===========================================================================
        private function GetPlayerColorString takes integer playerId returns string
            if(playerId == 0) then
                return "00FF0202"
            elseif(playerId == 1) then
                return "000041FF"
            elseif(playerId == 2) then
                return "001BE5B8"
            elseif(playerId == 3) then
                return "00530080"
            elseif(playerId == 4) then
                return "00FFFF00"
            elseif(playerId == 5) then
                return "00FE890D"
            elseif(playerId == 6) then
                return "001FBF00"
            elseif(playerId == 7) then
                return "00E45AAA"
            elseif(playerId == 8) then
                return "00949596"
            elseif(playerId == 9) then
                return "007DBEF1"
            elseif(playerId == 10) then
                return "000F6145"
            elseif(playerId == 11) then
                return "004D2903"
            else
                return "00000000"
            endif
        endfunction

        //===========================================================================
        // Check if unit type is already in array, else return revSize (size of revTypes)
        //===========================================================================      
        private function GetTypeIndex takes integer wTypeId returns integer
            local integer iType = 0
           
            loop
                exitwhen (iType >= revSize or revTypes[iType] == wTypeId)
               
                set iType = iType + 1
            endloop
           
            if(iType > revSize) then
                return revSize
            else
                return iType
            endif          
        endfunction

        //===========================================================================
        // Check if unit type is revivable for players (16 = all players; -1 = any player; players 0-15)
        //===========================================================================
        private function IsTypeRevivable takes integer wTypeId, integer wPlayers returns boolean
            local integer iType = GetTypeIndex(wTypeId)
            local integer iPlayer
           
            //Type is present
            if(wPlayers > -2  and wPlayers < 17 and iType < revSize) then
                //All players set -> always return true
                if(revPlayers[iType*INDEX_P+ALL_P]) then
                    return true
                //Check for ANY player
                elseif(wPlayers == ANY_P) then
                    set iPlayer = 0
                    loop
                        if(revPlayers[iType*INDEX_P+iPlayer]) then
                            return true //player found
                        endif

                        set iPlayer = iPlayer + 1
                        exitwhen iPlayer >= bj_MAX_PLAYER_SLOTS
                    endloop
                   
                    return false //no player found
                //Check for specific player
                else
                    return revPlayers[iType*INDEX_P+wPlayers]
                endif
            endif

            return false
        endfunction
   
    //===========================================================================
    // End "Global" functions
    //===========================================================================  

    //===========================================================================
    // Configuration functions
    //===========================================================================
        //===========================================================================
        // Configure dissipate effect
        //===========================================================================
        function ConfigureDissipate takes real wDelay, integer wOpac, real wDur, real wIncr, real wHeight returns boolean
            if(wDelay < 0 or wOpac < 0 or wOpac > 255 or wDur < 0 or wIncr < 0.01 or wIncr > wDur or wHeight < 0 or wHeight > 1000) then
                return false
            endif
           
            if(wDelay != null) then
                set dissDelay = wDelay
            endif
           
            if(wOpac != null) then
                set dissOpacity = wOpac
            endif
           
            if(wDur != null) then
                set dissDuration = wDur
            endif
           
            if(wIncr != null) then
                set dissIncrement = wIncr
            endif
           
            if(wHeight != null) then
                set dissHeight = wHeight
            endif
           
            set dissOpacityIncr = R2I(I2R(dissOpacity)*dissIncrement/dissDuration)
               
            return true
        endfunction
       
        //===========================================================================
        // Configure dissipate sound & effect
        //===========================================================================
        function ConfigureDissSFX takes race wRace, string wEffect, sound wSound returns nothing
            local integer dRace = GetHandleId(wRace)
           
            set dEffect[dRace] = wEffect
            set dSound[dRace] = wSound
        endfunction
       
        //===========================================================================
        // Configure revive sound & effect
        //===========================================================================
        function ConfigureRevSFX takes race wRace, string wEffect, sound wSound returns nothing
            local integer rRace = GetHandleId(wRace)
           
            set rEffect[rRace] = wEffect
            set rSound[rRace] = wSound
        endfunction

        //===========================================================================
        // Set Flying ability
        //===========================================================================
        function SetFlyAbil takes integer newAbilFly returns nothing
            set abilFly = newAbilFly
        endfunction

        //===========================================================================
        // Set Locus ability (unselectable)
        //===========================================================================
        function SetSelectAbil takes integer newAbilSelect returns nothing
            set abilSelect = newAbilSelect
        endfunction

        //===========================================================================
        // Add meld ability
        //===========================================================================
        function RemoveAbilOnDeath takes integer newAbilMeld returns boolean
            local integer abilMeldIndex = 0
       
            loop
                exitwhen abilMeldIndex >= abilMeldSize
               
                if(abilMeld[abilMeldIndex] == newAbilMeld) then
                    return false
                endif
               
                set abilMeldIndex = abilMeldIndex + 1
            endloop
           
            set abilMeld[abilMeldSize] = newAbilMeld
            set abilMeldSize = abilMeldSize + 1
            return true
        endfunction

        //===========================================================================
        // Make unit type exclusive to players for whom it is revivable
        //===========================================================================
        function AllowNormalTraining takes integer wTypeId, boolean allow returns boolean
            local integer iType = GetTypeIndex(wTypeId)
            local integer iPlayer
           
            //Type is present
            if(iType < revSize) then
                //Set in array
                if(allow) then
                    set revAllowAll[iType] = -1
                else
                    set revAllowAll[iType] = 0
                endif
               
                //Not all players set -> need to set tech
                if(not revPlayers[iType*INDEX_P+ALL_P]) then
                    set iPlayer = 0
                    loop
                        if(not revPlayers[iType*INDEX_P+iPlayer]) then
                            call SetPlayerTechMaxAllowed(Player(iPlayer), wTypeId, revAllowAll[iType])
                        endif
                       
                        set iPlayer = iPlayer + 1
                        exitwhen iPlayer >= bj_MAX_PLAYER_SLOTS
                    endloop
                endif
               
                return true
            endif
           
            return false
        endfunction

        //===========================================================================
        // Set unit type has dissipate animation
        //===========================================================================
        function SetTypeHasDissipate takes integer wTypeId, boolean hasDiss returns boolean
            local integer iType = GetTypeIndex(wTypeId)
           
            if(iType < revSize) then
                set revHasDissipate[iType] = hasDiss
                return true
            endif
           
            return false
        endfunction

        //===========================================================================
        // Make unit type revivable for players (16 = all players; players 0-15)
        //===========================================================================      
        function SetTypeRevivable takes integer wTypeId, integer wPlayers returns boolean
            local integer iType
            local integer iPlayer
            local integer iRevPlayers
            local integer revAllow

            //If type is already revivable, no need to proceed
            if(wPlayers > -1  and wPlayers < 17 and not IsTypeRevivable(wTypeId, wPlayers)) then
                set iType = GetTypeIndex(wTypeId)

                //Unit-type present & only one player to set
                if(iType < revSize and wPlayers != ALL_P) then
                    set revPlayers[iType*INDEX_P+wPlayers] = true
                    call SetPlayerTechMaxAllowed(Player(wPlayers), wTypeId, 1)
                else
                    //New type -> store in revTypes
                    if(iType == revSize) then
                        set revTypes[revSize] = wTypeId
                        set revAllowAll[revSize] = 0 //don't allow other players to build unit by default
                        set revHasDissipate[revSize] = false //set no dissipate anim by default
                        set revSize = revSize + 1
                    endif
                   
                    //All players -> store in revPlayers
                    if(wPlayers == ALL_P) then
                        set revPlayers[iType*INDEX_P+ALL_P] = true
                    endif

                    //Store settings in revPlayers & Set Tech
                    set iPlayer = 0
                    loop
                        set iRevPlayers = iType*INDEX_P+iPlayer
                        if(wPlayers == iPlayer or wPlayers == ALL_P or revPlayers[iRevPlayers]) then
                            set revPlayers[iRevPlayers] = true
                            set revAllow = 1
                        else
                            set revPlayers[iRevPlayers] = false
                            set revAllow = revAllowAll[iType]
                        endif

                        call SetPlayerTechMaxAllowed(Player(iPlayer), wTypeId, revAllow)

                        set iPlayer = iPlayer + 1
                        exitwhen iPlayer >= bj_MAX_PLAYER_SLOTS
                    endloop
                endif
               
                return true
            endif
           
            return false
        endfunction
       
        //===========================================================================
        // Make unit type unrevivable for players (16 = all players; players 0-15)
        //===========================================================================  
        function UnsetTypeRevivable takes integer wTypeId, integer wPlayers returns boolean
            local integer iType
            local integer iPlayer
           
            //If type is not revivable, no need to proceed
            if(IsTypeRevivable(wTypeId, wPlayers) or (wPlayers == ALL_P and IsTypeRevivable(wTypeId, ANY_P))) then
                set iType = GetTypeIndex(wTypeId)
               
                //"All players" has to be unset in all cases
                set revPlayers[iType*INDEX_P+ALL_P] = false

                //Only one player to unset
                if(wPlayers != ALL_P) then
                    set revPlayers[iType*INDEX_P+wPlayers] = false
                    call SetPlayerTechMaxAllowed(Player(wPlayers), wTypeId, revAllowAll[iType])
                //All players to unset
                else
                    set iPlayer = 0
                    loop
                        set revPlayers[iType*INDEX_P+iPlayer] = false
                        call SetPlayerTechMaxAllowed(Player(iPlayer), wTypeId, revAllowAll[iType])

                        set iPlayer = iPlayer + 1
                        exitwhen iPlayer >= bj_MAX_PLAYER_SLOTS
                    endloop
                endif
               
                return true
            endif

            return false
        endfunction
    //===========================================================================
    // End Configuration functions
    //===========================================================================

    //===========================================================================
    // Dissipate Effect
    //===========================================================================
        private function Dissipate_Fade takes nothing returns nothing
            local timer dTimer = GetExpiredTimer()
            local integer dTimerId = GetHandleId(dTimer)
            local unit dUnit = LoadUnitHandle(dTable, dTimerId, 'unit')
            local integer opacity = LoadInteger(dTable, dTimerId, 'opac')
           
            //Fading continues
            if(opacity > dissOpacityIncr) then
                set opacity = opacity - dissOpacityIncr
                call SetUnitVertexColor(dUnit, 255, 255, 255, opacity)
                call SaveInteger(dTable, dTimerId, 'opac', opacity)
            //Fading complete
            else
                call RemoveUnit(dUnit)
                call DestroyTimer(dTimer)
                call FlushChildHashtable(dTable, dTimerId)
            endif

            //Clean up
            set dTimer = null
            set dUnit = null
        endfunction

        private function Dissipate_Effect takes nothing returns nothing
            local timer dTimer = GetExpiredTimer()
            local integer dTimerId = GetHandleId(dTimer)
            local unit dUnit = LoadUnitHandle(dTable, dTimerId, 'unit')
            local boolean hasDiss = revHasDissipate[GetTypeIndex(GetUnitTypeId(dUnit))]
            local integer dRace = GetHandleId(GetUnitRace(dUnit))
            local player dOwner = GetOwningPlayer(dUnit)
            local player localPlayer = GetLocalPlayer()
            local force dAllies = CreateForce()
            local sound dSoundTemp = null
           
            //Set up player allies Force
            call ForceEnumAllies(dAllies, dOwner, null)
           
            //Display death message to owner and allies
            if(IsPlayerInForce(localPlayer, dAllies)) then
                call DisplayTextToPlayer(localPlayer, 0, 0, ("|c" + GetPlayerColorString(GetPlayerId(dOwner)) + GetUnitName(dUnit) + "|r has fallen."))
            endif
           
            //Start fading
            call SaveInteger(dTable, dTimerId, 'opac', dissOpacity)
            call SetUnitVertexColor(dUnit, 255, 255, 255, dissOpacity)
            call TimerStart(dTimer, dissIncrement, true, function Dissipate_Fade)
           
            //Lift unit
            if(hasDiss) then
                call QueueUnitAnimation(dUnit, "Dissipate")
            else
                if UnitAddAbility(dUnit, abilFly) then
                    call UnitRemoveAbility(dUnit, abilFly)
                endif
                call SetUnitFlyHeight(dUnit, dissHeight, dissHeight/dissDuration)
               
                //Play dissipate sound
                if(dSound[dRace] != null) then
                    set dSoundTemp = dSound[dRace]
                    call AttachSoundToUnit(dSoundTemp, dUnit)
                    call StartSound(dSoundTemp)
                    set dSoundTemp = null
                endif
            endif
           
            //Create dissipate effect
            if(dEffect[dRace] != null and dEffect[dRace] != "") then
                call DestroyEffect(AddSpecialEffectTarget(dEffect[dRace], dUnit, "origin"))
            endif
           
            //Clean up
            set dUnit = null
            set dTimer = null
            call DestroyForce(dAllies)
            set dAllies = null
        endfunction
       
        private function Dissipate_Death takes nothing returns boolean
            local unit dyingUnit = GetDyingUnit()
            local integer dyingTypeId = GetUnitTypeId(dyingUnit)
            local player dyingOwner = GetOwningPlayer(dyingUnit)
            local unit dUnit = null
            local timer dTimer = null
            local integer abilMeldIndex
           
            if(IsTypeRevivable(dyingTypeId, GetPlayerId(dyingOwner))) then
                set dTimer = CreateTimer()

                //Replace dying unit
                call ShowUnit(dyingUnit, false)
                set dUnit = CreateUnit(dyingOwner, dyingTypeId, GetUnitX(dyingUnit), GetUnitY(dyingUnit), GetUnitFacing(dyingUnit))
                call PauseUnit(dUnit, true) //pause unit to prevent automatic orders (eg attack enemies)
                call UnitAddAbility(dUnit, abilSelect) //make unit unselectable
                //Remove meld abilities
                set abilMeldIndex = 0
                loop
                    exitwhen abilMeldIndex >= abilMeldSize
                    call UnitRemoveAbility(dUnit, abilMeld[abilMeldIndex])
                    set abilMeldIndex = abilMeldIndex + 1
                endloop

                //Play death animation
                call SetUnitAnimation(dUnit, "Death")
               
                //Store unit & wait for death animation to finish
                call SaveUnitHandle(dTable, GetHandleId(dTimer), 'unit', dUnit)
                call TimerStart(dTimer, dissDelay, false, function Dissipate_Effect)

                //Clean up
                set dUnit = null
                set dTimer = null
            endif

            //Clean up
            set dyingUnit = null
           
            return true
        endfunction
    //===========================================================================
    // End Dissipate Effect
    //===========================================================================

    //===========================================================================
    // Revive Effect
    //===========================================================================
        private function Revive_Effect takes nothing returns boolean
            local unit rUnit = GetTrainedUnit()
            local integer rRace
            local sound rSoundTemp = null
           
            if(IsTypeRevivable(GetUnitTypeId(rUnit), GetPlayerId(GetOwningPlayer(rUnit)))) then
                set rRace = GetHandleId(GetUnitRace(rUnit))
                   
                if(rEffect[rRace] != null and rEffect[rRace] != "") then
                    call DestroyEffect(AddSpecialEffectTarget(rEffect[rRace], rUnit, "origin"))
                endif
                if(rSound[rRace] != null) then
                    set rSoundTemp = rSound[rRace]
                    call AttachSoundToUnit(rSoundTemp, rUnit)
                    call StartSound(rSoundTemp)
                    set rSoundTemp = null
                endif
            endif
           
            //Cleanup
            set rUnit = null
           
            return true
        endfunction

    //===========================================================================
    // Set up triggers
    //===========================================================================
    private function InitRevivable takes nothing returns nothing
        local trigger trg_Dissipate = CreateTrigger()
        local trigger trg_Revive = CreateTrigger()
        local integer iPlayer = 0
        local integer iSFX = 1
        local string dSoundString
        local string dSoundLabelString
        local player regPlayer
       
        //Set sounds & effects arrays defaults
            //Set existing revive effects
            set rEffect[1] = "Abilities\\Spells\\Human\\ReviveHuman\\ReviveHuman.mdl"
            set rEffect[2] = "Abilities\\Spells\\Orc\\ReviveOrc\\ReviveOrc.mdl"
            set rEffect[3] = "Abilities\\Spells\\Undead\\ReviveUndead\\ReviveUndead.mdl"
            set rEffect[4] = "Abilities\\Spells\\NightElf\\ReviveNightElf\\ReviveNightElf.mdl"
            set rEffect[5] = "Abilities\\Spells\\Demon\\ReviveDemon\\ReviveDemon.mdl"
            //DEMON rEffect has no sound
            set rSound[5] = CreateSound("Abilities\\Spells\\Human\\ReviveHuman\\ReviveHuman.wav", false, true, true, 10, 10, "SpellsEAX")
            call SetSoundParamsFromLabel(rSound[5], "ReviveHuman")
            call SetSoundDuration(rSound[5], 3196)
            //UNDEAD: has dissipate model with sound attached
            set dEffect[3] = "Objects\\Spawnmodels\\Undead\\UndeadDissipate\\UndeadDissipate.mdl"
            set dSound[3] = null
       
            loop
                exitwhen iSFX > 11

                if(iSFX != 3) then //NOT UNDEAD: no dissipate model, so setup dSound
                    set dEffect[iSFX] = null
                   
                    if(iSFX == 2) then //ORC
                        set dSoundString = "Sound\\Units\\Orc\\OrcDissipate1.wav"
                        set dSoundLabelString = "OrcDissipate"
                    elseif(iSFX == 4) then //NIGHT ELF
                        set dSoundString = "Sound\\Units\\NightElf\\NightElfDissipate1.wav"
                        set dSoundLabelString = "NightElfDissipate"
                    else //Default to HUMAN
                        set dSoundString = "Sound\\Units\\Human\\HumanDissipate1.wav"
                        set dSoundLabelString = "HumanDissipate"

                        if(iSFX != 5) then //NOT DEMON: use HUMAN rEffect by default
                            set rEffect[iSFX] = rEffect[1]
                        endif
                    endif

                    //Create dSound
                    set dSound[iSFX] = CreateSound(dSoundString, false, true, true, 10, 10, "SpellsEAX")
                    call SetSoundParamsFromLabel(dSound[iSFX], dSoundLabelString)
                    call SetSoundDuration(dSound[iSFX], 2270)
                endif

                //NOT DEMON: Revive models have sound attached
                if(iSFX != 5) then
                    set rSound[iSFX] = null
                endif          
               
                set iSFX = iSFX + 1
            endloop
        //END Setup sounds & effects
       
        //Add meld abilities
        call RemoveAbilOnDeath('Ashm')
        call RemoveAbilOnDeath('Sshm')
        call RemoveAbilOnDeath('Ahid')
           
        //Register events for all players
        loop
            set regPlayer = Player(iPlayer)
            //Dissipate trigger
            call TriggerRegisterPlayerUnitEvent(trg_Dissipate, regPlayer, EVENT_PLAYER_UNIT_DEATH, null)
            //Revive trigger
            call TriggerRegisterPlayerUnitEvent(trg_Revive, regPlayer, EVENT_PLAYER_UNIT_TRAIN_FINISH, null)
           
            set iPlayer = iPlayer + 1
            exitwhen iPlayer >= bj_MAX_PLAYER_SLOTS
        endloop

        //Dissipate trigger
        call TriggerAddCondition(trg_Dissipate, Condition(function Dissipate_Death))
        //Revive trigger
        call TriggerAddCondition(trg_Revive, Condition(function Revive_Effect))
    endfunction
endlibrary
 



Hard-coded version

For people familiar with JASS who can just edit the code as desired without the need for configurables. You can simply copy-paste this into an empty custom text trigger and you're good to go. There are examples of unit-type settings in InitRevivable.
Code (vJASS):

//===========================================================================
//
//  Revivable Unit System v1.3.1
//  by loktar
//  -------------------------------------------------------------------------
//  * Limit availability of unit types to 1
//  * Create a dissipate effect when those units die
//  * Create a hero revive effect when those units are revived/trained
//  -------------------------------------------------------------------------
//
//    -------
//    * API *
//    -------
//  *    SetTypeRevivable(integer unitTypeId, integer playerNumber)
//          -> Make a unit type revivable for player(s).
//              -playerNumber: player 0-15; 16 for all players
//              -Players not set through this function will be unable to build the unit by default
//                 -Returns true on success, false on failure.
//
//  *    UnsetTypeRevivable(integer unitTypeId, integer playerNumber)
//            -> Make a unit type not revivable for player(s).
//              -playerNumber: player 0-15; 16 for all players
//                 -Returns true on success, false on failure.
//
//  *    AllowNormalTraining(integer unitTypeId, boolean allow)
//          -> Allow all other players to build the unit normally.
//                 -Returns true on success, false on failure.
//
//  *   SetTypeHasDissipate(integer unitTypeId, boolean hasDiss)
//          -> Enable usage of dissipate animation
//                 -Returns true on success, false on failure.
//
//  !! Note that you still have to add the unit type to the Altar (or any other building) !!
//
//===========================================================================
library RevivableUnits initializer InitRevivable
    globals
        //revivable unit storage
        private integer array        revTypes
        private boolean array        revPlayers
        private integer array        revAllowAll
        private boolean array        revHasDissipate
        private integer                revSize            = 0
        private constant integer    ANY_P            = -1
        private constant integer    ALL_P            = bj_MAX_PLAYER_SLOTS
        private constant integer    INDEX_P            = bj_MAX_PLAYER_SLOTS + 1
        //dissipating unit storage
        private hashtable            dTable            = InitHashtable()
        //sounds & effects
        private sound array            dSound[12]
        private sound array            rSound[12]
        private string array        dEffect[12]
        private string array        rEffect[12]
        //configure dissipate effect
        private constant real        DISS_DELAY        = 3 //delay after start of death animation before dissipating
        private constant integer    DISS_OPAC        = 200 //starting opacity after ^
        private constant real        DISS_DUR        = 2 //duration of the dissipate effect
        private constant real        DISS_INCR        = 0.03 //timer duration for the fade effect
        private constant real        DISS_HEIGHT        = 530 //flyheight
        private constant integer     DISS_OPAC_INCR    = R2I(I2R(DISS_OPAC)*DISS_INCR/DISS_DUR)
    endglobals
   
    //===========================================================================
    // "Global" functions
    //===========================================================================  
        //===========================================================================
        // Get Player Color
        //===========================================================================
        private function GetPlayerColorString takes integer playerId returns string
            if(playerId == 0) then
                return "00FF0202"
            elseif(playerId == 1) then
                return "000041FF"
            elseif(playerId == 2) then
                return "001BE5B8"
            elseif(playerId == 3) then
                return "00530080"
            elseif(playerId == 4) then
                return "00FFFF00"
            elseif(playerId == 5) then
                return "00FE890D"
            elseif(playerId == 6) then
                return "001FBF00"
            elseif(playerId == 7) then
                return "00E45AAA"
            elseif(playerId == 8) then
                return "00949596"
            elseif(playerId == 9) then
                return "007DBEF1"
            elseif(playerId == 10) then
                return "000F6145"
            elseif(playerId == 11) then
                return "004D2903"
            else
                return "00000000"
            endif
        endfunction

        //===========================================================================
        // Check if unit type is already in array, else return revSize (size of revTypes)
        //===========================================================================      
        private function GetTypeIndex takes integer wTypeId returns integer
            local integer iType = 0
           
            loop
                exitwhen (iType >= revSize or revTypes[iType] == wTypeId)
               
                set iType = iType + 1
            endloop
           
            if(iType > revSize) then
                return revSize
            else
                return iType
            endif          
        endfunction

        //===========================================================================
        // Check if unit type is revivable for players (16 = all players; -1 = any player; players 0-15)
        //===========================================================================
        private function IsTypeRevivable takes integer wTypeId, integer wPlayers returns boolean
            local integer iType = GetTypeIndex(wTypeId)
            local integer iPlayer
           
            //Type is present
            if(wPlayers > -2  and wPlayers < 17 and iType < revSize) then
                //All players set -> always return true
                if(revPlayers[iType*INDEX_P+ALL_P]) then
                    return true
                //Check for ANY player
                elseif(wPlayers == ANY_P) then
                    set iPlayer = 0
                    loop
                        if(revPlayers[iType*INDEX_P+iPlayer]) then
                            return true //player found
                        endif

                        set iPlayer = iPlayer + 1
                        exitwhen iPlayer >= bj_MAX_PLAYER_SLOTS
                    endloop
                   
                    return false //no player found
                //Check for specific player
                else
                    return revPlayers[iType*INDEX_P+wPlayers]
                endif
            endif

            return false
        endfunction
   
    //===========================================================================
    // End "Global" functions
    //===========================================================================  

    //===========================================================================
    // Configuration functions
    //===========================================================================
        //===========================================================================
        // Make unit type exclusive to players for whom it is revivable
        //===========================================================================
        function AllowNormalTraining takes integer wTypeId, boolean allow returns boolean
            local integer iType = GetTypeIndex(wTypeId)
            local integer iPlayer
           
            //Type is present
            if(iType < revSize) then
                //Set in array
                if(allow) then
                    set revAllowAll[iType] = -1
                else
                    set revAllowAll[iType] = 0
                endif
               
                //Not all players set -> need to set tech
                if(not revPlayers[iType*INDEX_P+ALL_P]) then
                    set iPlayer = 0
                    loop
                        if(not revPlayers[iType*INDEX_P+iPlayer]) then
                            call SetPlayerTechMaxAllowed(Player(iPlayer), wTypeId, revAllowAll[iType])
                        endif
                       
                        set iPlayer = iPlayer + 1
                        exitwhen iPlayer >= bj_MAX_PLAYER_SLOTS
                    endloop
                endif
               
                return true
            endif
           
            return false
        endfunction

        //===========================================================================
        // Set unit type has dissipate animation
        //===========================================================================
        function SetTypeHasDissipate takes integer wTypeId, boolean hasDiss returns boolean
            local integer iType = GetTypeIndex(wTypeId)
           
            if(iType < revSize) then
                set revHasDissipate[iType] = hasDiss
                return true
            endif
           
            return false
        endfunction

        //===========================================================================
        // Make unit type revivable for players (16 = all players; players 0-15)
        //===========================================================================      
        function SetTypeRevivable takes integer wTypeId, integer wPlayers returns boolean
            local integer iType
            local integer iPlayer
            local integer iRevPlayers
            local integer revAllow
           
            //If type is already revivable, no need to proceed
            if(wPlayers > -1  and wPlayers < 17 and not IsTypeRevivable(wTypeId, wPlayers)) then
                set iType = GetTypeIndex(wTypeId)

                //Unit-type present & only one player to set
                if(iType < revSize and wPlayers != ALL_P) then
                    set revPlayers[iType*INDEX_P+wPlayers] = true
                    call SetPlayerTechMaxAllowed(Player(wPlayers), wTypeId, 1)
                else
                    //New type -> store in revTypes
                    if(iType == revSize) then
                        set revTypes[revSize] = wTypeId
                        set revAllowAll[revSize] = 0 //don't allow other players to build unit by default
                        set revHasDissipate[revSize] = false //set no dissipate anim by default
                        set revSize = revSize + 1
                    endif
                   
                    //All players -> store in revPlayers
                    if(wPlayers == ALL_P) then
                        set revPlayers[iType*INDEX_P+ALL_P] = true
                    endif

                    //Store settings in revPlayers & Set Tech
                    set iPlayer = 0
                    loop
                        set iRevPlayers = iType*INDEX_P+iPlayer
                        if(wPlayers == iPlayer or wPlayers == ALL_P or revPlayers[iRevPlayers]) then
                            set revPlayers[iRevPlayers] = true
                            set revAllow = 1
                        else
                            set revPlayers[iRevPlayers] = false
                            set revAllow = revAllowAll[iType]
                        endif

                        call SetPlayerTechMaxAllowed(Player(iPlayer), wTypeId, revAllow)

                        set iPlayer = iPlayer + 1
                        exitwhen iPlayer >= bj_MAX_PLAYER_SLOTS
                    endloop
                endif
               
                return true
            endif
           
            return false
        endfunction
       
        //===========================================================================
        // Make unit type unrevivable for players (16 = all players; players 0-15)
        //===========================================================================  
        function UnsetTypeRevivable takes integer wTypeId, integer wPlayers returns boolean
            local integer iType
            local integer iPlayer
           
            //If type is not revivable, no need to proceed
            if(IsTypeRevivable(wTypeId, wPlayers) or (wPlayers == ALL_P and IsTypeRevivable(wTypeId, ANY_P))) then
                set iType = GetTypeIndex(wTypeId)
               
                //"All players" has to be unset in all cases
                set revPlayers[iType*INDEX_P+ALL_P] = false
                   
                //Only one player to unset
                if(wPlayers != ALL_P) then
                    set revPlayers[iType*INDEX_P+wPlayers] = false
                    call SetPlayerTechMaxAllowed(Player(wPlayers), wTypeId, revAllowAll[iType])
                //All players to unset
                else
                    set iPlayer = 0
                    loop
                        set revPlayers[iType*INDEX_P+iPlayer] = false
                        call SetPlayerTechMaxAllowed(Player(iPlayer), wTypeId, revAllowAll[iType])

                        set iPlayer = iPlayer + 1
                        exitwhen iPlayer >= bj_MAX_PLAYER_SLOTS
                    endloop
                endif
               
                return true
            endif

            return false
        endfunction
    //===========================================================================
    // End Configuration functions
    //===========================================================================

    //===========================================================================
    // Dissipate Effect
    //===========================================================================
        private function Dissipate_Fade takes nothing returns nothing
            local timer dTimer = GetExpiredTimer()
            local integer dTimerId = GetHandleId(dTimer)
            local unit dUnit = LoadUnitHandle(dTable, dTimerId, 'unit')
            local integer opacity = LoadInteger(dTable, dTimerId, 'opac')
           
            //Fading continues
            if(opacity > DISS_OPAC_INCR) then
                set opacity = opacity - DISS_OPAC_INCR
                call SetUnitVertexColor(dUnit, 255, 255, 255, opacity)
                call SaveInteger(dTable, dTimerId, 'opac', opacity)
            //Fading complete
            else
                call RemoveUnit(dUnit)
                call DestroyTimer(dTimer)
                call FlushChildHashtable(dTable, dTimerId)
            endif
                   
            //Clean up
            set dTimer = null
            set dUnit = null
        endfunction

        private function Dissipate_Effect takes nothing returns nothing
            local timer dTimer = GetExpiredTimer()
            local integer dTimerId = GetHandleId(dTimer)
            local unit dUnit = LoadUnitHandle(dTable, dTimerId, 'unit')
            local boolean hasDiss = revHasDissipate[GetTypeIndex(GetUnitTypeId(dUnit))]
            local integer dRace = GetHandleId(GetUnitRace(dUnit))
            local player dOwner = GetOwningPlayer(dUnit)
            local player localPlayer = GetLocalPlayer()
            local force dAllies = CreateForce()
            local sound dSoundTemp = null
           
            //Set up player allies Force
            call ForceEnumAllies(dAllies, dOwner, null)
           
            //Display death message to owner and allies
            if(IsPlayerInForce(localPlayer, dAllies)) then
                call DisplayTextToPlayer(localPlayer, 0, 0, ("|c" + GetPlayerColorString(GetPlayerId(dOwner)) + GetUnitName(dUnit) + "|r has fallen."))
            endif
           
            //Start fading
            call SaveInteger(dTable, dTimerId, 'opac', DISS_OPAC)
            call SetUnitVertexColor(dUnit, 255, 255, 255, DISS_OPAC)
            call TimerStart(dTimer, DISS_INCR, true, function Dissipate_Fade)
           
            //Lift unit
            if(hasDiss) then
                call QueueUnitAnimation(dUnit, "Dissipate")
            else
                if UnitAddAbility(dUnit, 'Arav') then
                    call UnitRemoveAbility(dUnit, 'Arav')
                endif
                call SetUnitFlyHeight(dUnit, DISS_HEIGHT, DISS_HEIGHT/DISS_DUR)
               
                //Play dissipate sound
                if(dSound[dRace] != null) then
                    set dSoundTemp = dSound[dRace]
                    call AttachSoundToUnit(dSoundTemp, dUnit)
                    call StartSound(dSoundTemp)
                    set dSoundTemp = null
                endif
            endif
           
            //Create dissipate effect
            if(dEffect[dRace] != null and dEffect[dRace] != "") then
                call DestroyEffect(AddSpecialEffectTarget(dEffect[dRace], dUnit, "origin"))
            endif
           
            //Clean up
            set dUnit = null
            set dTimer = null
            call DestroyForce(dAllies)
            set dAllies = null
        endfunction
       
        private function Dissipate_Death takes nothing returns boolean
            local unit dyingUnit = GetDyingUnit()
            local integer dyingTypeId = GetUnitTypeId(dyingUnit)
            local player dyingOwner = GetOwningPlayer(dyingUnit)
            local unit dUnit = null
            local timer dTimer = null

            if(IsTypeRevivable(dyingTypeId, GetPlayerId(dyingOwner))) then
                set dTimer = CreateTimer()
                   
                //Replace dying unit
                call ShowUnit(dyingUnit, false)
                set dUnit = CreateUnit(dyingOwner, dyingTypeId, GetUnitX(dyingUnit), GetUnitY(dyingUnit), GetUnitFacing(dyingUnit))
                call PauseUnit(dUnit, true) //pause unit to prevent automatic orders (eg attack enemies)
                call UnitAddAbility(dUnit, 'Aloc') //make unit unselectable
                //Remove meld abilities
                call UnitRemoveAbility(dUnit, 'Ashm')
                call UnitRemoveAbility(dUnit, 'Sshm')
                call UnitRemoveAbility(dUnit, 'Ahid')

                //Play death animation
                call SetUnitAnimation(dUnit, "Death")
               
                //Store unit & wait for death animation to finish
                call SaveUnitHandle(dTable, GetHandleId(dTimer), 'unit', dUnit)
                call TimerStart(dTimer, DISS_DELAY, false, function Dissipate_Effect)

                //Clean up
                set dUnit = null
                set dTimer = null
            endif

            //Clean up
            set dyingUnit = null
           
            return true
        endfunction
    //===========================================================================
    // End Dissipate Effect
    //===========================================================================

    //===========================================================================
    // Revive Effect
    //===========================================================================
        private function Revive_Effect takes nothing returns boolean
            local unit rUnit = GetTrainedUnit()
            local integer rRace
            local sound rSoundTemp = null
           
            if(IsTypeRevivable(GetUnitTypeId(rUnit), GetPlayerId(GetOwningPlayer(rUnit)))) then
                set rRace = GetHandleId(GetUnitRace(rUnit))
                   
                if(rEffect[rRace] != null and rEffect[rRace] != "") then
                    call DestroyEffect(AddSpecialEffectTarget(rEffect[rRace], rUnit, "origin"))
                endif
                if(rSound[rRace] != null) then
                    set rSoundTemp = rSound[rRace]
                    call AttachSoundToUnit(rSoundTemp, rUnit)
                    call StartSound(rSoundTemp)
                    set rSoundTemp = null
                endif
            endif
           
            //Cleanup
            set rUnit = null
           
            return true
        endfunction

    //===========================================================================
    // Set up triggers
    //===========================================================================
    private function InitRevivable takes nothing returns nothing
        local trigger trg_Dissipate = CreateTrigger()
        local trigger trg_Revive = CreateTrigger()
        local integer iPlayer = 0
        local integer iSFX = 1
        local string dSoundString
        local string dSoundLabelString
        local player regPlayer
       
        //Set sounds & effects arrays defaults
            //Set existing revive effects
            set rEffect[1] = "Abilities\\Spells\\Human\\ReviveHuman\\ReviveHuman.mdl"
            set rEffect[2] = "Abilities\\Spells\\Orc\\ReviveOrc\\ReviveOrc.mdl"
            set rEffect[3] = "Abilities\\Spells\\Undead\\ReviveUndead\\ReviveUndead.mdl"
            set rEffect[4] = "Abilities\\Spells\\NightElf\\ReviveNightElf\\ReviveNightElf.mdl"
            set rEffect[5] = "Abilities\\Spells\\Demon\\ReviveDemon\\ReviveDemon.mdl"
            //DEMON rEffect has no sound
            set rSound[5] = CreateSound("Abilities\\Spells\\Human\\ReviveHuman\\ReviveHuman.wav", false, true, true, 10, 10, "SpellsEAX")
            call SetSoundParamsFromLabel(rSound[5], "ReviveHuman")
            call SetSoundDuration(rSound[5], 3196)
            //UNDEAD: has dissipate model with sound attached
            set dEffect[3] = "Objects\\Spawnmodels\\Undead\\UndeadDissipate\\UndeadDissipate.mdl"
            set dSound[3] = null
       
            loop
                exitwhen iSFX > 11

                if(iSFX != 3) then //NOT UNDEAD: no dissipate model, so setup dSound
                    set dEffect[iSFX] = null
                   
                    if(iSFX == 2) then //ORC
                        set dSoundString = "Sound\\Units\\Orc\\OrcDissipate1.wav"
                        set dSoundLabelString = "OrcDissipate"
                    elseif(iSFX == 4) then //NIGHT ELF
                        set dSoundString = "Sound\\Units\\NightElf\\NightElfDissipate1.wav"
                        set dSoundLabelString = "NightElfDissipate"
                    else //Default to HUMAN
                        set dSoundString = "Sound\\Units\\Human\\HumanDissipate1.wav"
                        set dSoundLabelString = "HumanDissipate"

                        if(iSFX != 5) then //NOT DEMON: use HUMAN rEffect by default
                            set rEffect[iSFX] = rEffect[1]
                        endif
                    endif

                    //Create dSound
                    set dSound[iSFX] = CreateSound(dSoundString, false, true, true, 10, 10, "SpellsEAX")
                    call SetSoundParamsFromLabel(dSound[iSFX], dSoundLabelString)
                    call SetSoundDuration(dSound[iSFX], 2270)
                endif

                //NOT DEMON: Revive models have sound attached
                if(iSFX != 5) then
                    set rSound[iSFX] = null
                endif          
               
                set iSFX = iSFX + 1
            endloop
        //END Setup sounds & effects
           
        //Configure Units
            // Footman for All Players
            call SetTypeRevivable('hfoo', ALL_P)
            // Grunt for All Players except Player 2 (blue)
            call SetTypeRevivable('ogru', ALL_P)
            call UnsetTypeRevivable('ogru', 1)
            // Ghoul for Player 1 (red)
            call SetTypeRevivable('ugho', 0)
            // Ghoul available normally for other players
            call AllowNormalTraining('ugho', true)
            // Archer for Player 2 (blue)
            call SetTypeRevivable('earc', 1)
            // Fel Stalker for Player 1 & 2 (red & blue)
            call SetTypeRevivable('nfel', 0)
            call SetTypeRevivable('nfel', 1)
            // Priest (Blood Mage model) for Player 1 (red) - using Dissipate animation
            call SetTypeRevivable('hmpr', 0)
            call SetTypeHasDissipate('hmpr', true)
           
        //Register events for all players
        loop
            set regPlayer = Player(iPlayer)
            //Dissipate trigger
            call TriggerRegisterPlayerUnitEvent(trg_Dissipate, regPlayer, EVENT_PLAYER_UNIT_DEATH, null)
            //Revive trigger
            call TriggerRegisterPlayerUnitEvent(trg_Revive, regPlayer, EVENT_PLAYER_UNIT_TRAIN_FINISH, null)
           
            set iPlayer = iPlayer + 1
            exitwhen iPlayer >= bj_MAX_PLAYER_SLOTS
        endloop

        //Dissipate trigger
        call TriggerAddCondition(trg_Dissipate, Condition(function Dissipate_Death))
        //Revive trigger
        call TriggerAddCondition(trg_Revive, Condition(function Revive_Effect))
    endfunction
endlibrary
 



Changelog

1.3.1
  • Fixed a bug that caused the dummy unit to engage enemies instead of playing the death/dissipate sequence (by pausing it)
  • GetPlayerColor renamed to GetPlayerColorString (GetPlayerColor is a native function)
1.3
  • Overhauled dissipate and revive sound and effects (code & config)
  • AllowAllPlayers renamed to AllowNormalTraining
  • AddMeldAbil renamed to RemoveAbilOnDeath
  • Dying unit is now hidden instead of removed
1.2.5
  • Fixed potential leak in Dissipate_Effect
  • Using player variable instead of calling Player(iPlayer) twice in InitRevivable
1.2.4
  • Changed default values for dissipate effect
  • No longer playing Dissipate sound when unit-type has dissipate animation (dissipate animation should have the sound effect in the model)
1.2.3
  • Small changes for very slightly increased efficiency
  • Check for valid player number in IsTypeRevivable (-1 to 16) and SetTypeRevivable (0 to 16)
1.2.2
  • Renamed dissipate configuration variables ("hard-coded" version: made these constants)
  • Replaced revTypes loop with GetTypeId() in several functions
  • Several functions now return boolean for success/failure
  • SetTypeHasDissipate: now checks if unit-type is present in revTypes
1.2.1
  • New global constant: ANY_P (any player = -1)
  • IsTypeRevivable: added ability to check for any player (ANY_P)
  • UnsetTypeRevivable: fixed not being able to unset all players when type not set for all players (i.e. can now be used to unset type when any number of players is set for it)
  • "Hard-coded" version: removed unused local variable
  • Added SetTypeHasDissipate function
1.2
  • Added configuration for effects, sounds and abilities
  • Added GUI configuration
1.1.1
  • Added GetTypeIndex to eliminate double code
  • Renamed some variables
  • Improved readability a bit: ALL_P = bj_MAX_PLAYER_SLOTS & INDEX_P = ALL_P + 1
  • Made opacityIncrement update in ConfigureDissipate()
  • Removed adding of Banish ability leftover from testing
1.1.0
  • Renamed MakeTypeRevivable to SetTypeRevivable
  • Added UnsetTypeRevivable
  • Renamed/redesigned MakeTypeExclusive to AllowAllPlayers
  • Improved unittype/player (-> better (Un)SetTypeRevivable, IsTypeRevivable, etc) storage a lot
  • Replaced TeamColor array with function
1.0.1
  • Renamed MakeUnitRevivable to MakeTypeRevivable
  • IsRevivable -> IsTypeRevivable
  • Added MakeTypeExclusive
  • Some improvements to MakeTypeRevivable
  • Some other improvements


Keywords:
revive, dissipate, unit, revivable, hero
Contents

Revivable Units test (Map)

Reviews
Moderator
20:23, 22nd Mar 2016 BPower: Fair enough. Approved with a rating of useful. Think about what I said concerning race handle ids. I feel it's a bit limitating that you can only revive one unit of a specific type id, no matter how many units of...
  1. 20:23, 22nd Mar 2016
    BPower:
    Fair enough. Approved with a rating of useful.

    Think about what I said concerning race handle ids.

    I feel it's a bit limitating that you can only revive one unit of a specific type id, no matter how many units of that type id died.
     
  2. Wietlol

    Wietlol

    Joined:
    Aug 1, 2013
    Messages:
    4,653
    Resources:
    3
    Spells:
    3
    Resources:
    3
    Nice stuff here.
    Code is very neat and well readable.

    Havent tested it in-game but a few things to improve:

    - TriggerAddAction -> TriggerAddCondition
    Conditions are faster because actions create a new thread per action, conditions do not really care about them so do not have a few things... for example TriggerSleepAction() wont work in conditions.
    However, because you wont be using any of those things, you can just place your actions inside a condition.
    Also, place everything that should run on a trigger in one function, so you only have to add one condition to each trigger at max.

    -
    local location dLoc = GetUnitLoc(dyingUnit)

    You dont need the location if you use coordinates (aka GetUnitX(), GetUnitY()).
    So CreateUnitAtLoc would be CreateUnit.

    - Hashtable -> Table or TimerUtils
    You use a hashtable but do not use very much of it.
    It is recommended to use Table if you want to stick to a hashtable like that (you can make it be optional with static ifs if you dont want to force Table as a requirement).
    You do have to use integers like 0, 1, 2 etc instead of 'unit' and 'opac'.
    You can make global constants with names like "TABLE_ROW_UNIT" and "TABLE_ROW_OPACITY" and set those to 0 and 1 so you can still know what those rows are for.
    An alternative to Table would be TimerUtils and arrays.
    You will have to use an allocater to make indices and an array for the units and for the opacity, then you can remove the hashtable from your list and use the one by TimerUtils.
    This will also slightly improve timer allocation/deallocation.

    - Effects & sound paths -> configurable
    This is not really required but it could be usefull to make these configurables... maybe.

    -
    if(revUnits[iSize] == wUnit and revPlayers[iSize] == iPlayer+1) then

    You may want to keep track of how many units are used by the players... but its not really necessary.
     
  3. Arad MNK

    Arad MNK

    Joined:
    Dec 11, 2014
    Messages:
    1,889
    Resources:
    3
    Maps:
    2
    Spells:
    1
    Resources:
    3
    You can't make a single unit revivable with this system. It only works with unit types. Maybe adding this feature would result in a higher rating.

    Maybe all the system features (Is unit Revivable? or maybe the dissipate effect) would be better to be configurable for both unit types and units.

    I can imagine configuring unit types, and then adding single unit exceptions.
     
  4. loktar

    loktar

    Joined:
    Nov 2, 2004
    Messages:
    482
    Resources:
    23
    Models:
    2
    Icons:
    16
    Packs:
    1
    Tools:
    2
    Spells:
    1
    Tutorials:
    1
    Resources:
    23
    Hmm, I'm making this system for my own uses really, don't want to put more effort into it than needed :p
    But I encourage anyone to take this code, add features, and upload it to the hive.

    Nice, thanks for the feedback.
    I'm not sure how to proceed with the hashtables. I'd prefer not to use any external code if I can avoid it.
    I only chose hashtables over global vars because it's easy to attach information to a specific timer that way (thus avoiding collisions when the dissipate effect is created multiple times at once), and get rid of all that information when I'm done with it.
    I don't think there's a way to do that without hashtables (other than using Table or TimerUtils)?

    Also are these the correct downloads? :p TimerUtils, Table

    Edit: on closer inspection, Table could make unittype/players storage much simpler too with 2D arrays... considering it :D
     
    Last edited: Jan 31, 2016
  5. Wietlol

    Wietlol

    Joined:
    Aug 1, 2013
    Messages:
    4,653
    Resources:
    3
    Spells:
    3
    Resources:
    3
    We have Table here on the hive as well, remade by Bribe (NewTable) iirc.
    I dont really bother about Table because I dont use hashtables at all... at all at all :D
    So I dont really know what the difference is between Table and NewTable... you have to search that for yourself.

    TimerUtils is correct.

    "Hmm, I'm making this system for my own uses really"

    There are two kinds of resources...
    1, The ones that do what you want.
    2, The ones that do what other people want.
    ... 3, Those who dont do what anyone wants.

    Making things for own usage makes things much simpler because you only have to care about the things you care about.
    Making things for public usage requires you to search for all related information, use the latest techniques to make it work in the best way and make it as efficient and readable as possible... and then there are the one thing I hate... configurables :D

    None is really better than the other... except that 1 and 2 are both better than 3 :D
    It is just what you are using/making it for.
    If you have your own project or something like that, then there might be some serious systems that are really useless for public usage but are a requirement to make the map work. You may eventually end up publishing them afterwards.
     
  6. loktar

    loktar

    Joined:
    Nov 2, 2004
    Messages:
    482
    Resources:
    23
    Models:
    2
    Icons:
    16
    Packs:
    1
    Tools:
    2
    Spells:
    1
    Tutorials:
    1
    Resources:
    23
    Oh yeah, I agree, I have been adding things that I don't really need, i.e. all the configurables (which I don't like either :p). Don't even really need a function to make unit types revivable, as it would be much easier to just do it manually for every unit type I need. But when it comes to adding features, I'll leave that up to other people who want to do that :p

    I'm sharing this for two reasons: 1) sharing is caring :D and 2) the feedback you get when you submit something to be used by others is invaluable when you don't really know what you're doing :p

    Will have a look at NewTable etc and see what I can use. Mostly the ability to have 2D arrays seems attractive for storing multiple values/players per unit type. Thanks again!

    Edit: actually, I'm going to turn revPlayers into a boolean array, and reserve 17 slots per unit. I.e. the index for each player per unit will be unitIndex*17+playerId, where playerId 0-15 is just the playerId and 16 is for "all players". This is waaay easier, can't believe I didn't think of that earlier.

    So definitely won't be using Table or anything, still have to take a look at TimerUtils to see if I can be bothered with it :p seeing as the simple hashtable works quite well already

    Edit again: updated :p
     
    Last edited: Feb 1, 2016
  7. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    7,738
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    There exist revive systems based on resurrection on an isolated part of the map which keep you from needing to do this remove unit/create unit stuff. If a unit leaves behind no corpse, that is detectable because the resurrection order will return false.

    Regardless, what you've done with a pseudo-dissipate effect is cool. I recommend you remake this resource specifically for that.

    Sound FX string paths should be configurable. Leave nothing which could be changed by users hardcoded unless there will otherwise be functional flaws.

    Eaually, special effect string art should be configurable.

    Rawcode integers absolutely should be configurable.

    Tip: Make your configurables in GUI like I did with Damage Engine and Spell System. Easier for newer users.
     
  8. loktar

    loktar

    Joined:
    Nov 2, 2004
    Messages:
    482
    Resources:
    23
    Models:
    2
    Icons:
    16
    Packs:
    1
    Tools:
    2
    Spells:
    1
    Tutorials:
    1
    Resources:
    23
    Mm, I think being able to use this on any map without having to designate a hidden part of the map to it is an advantage. I like it better this way ^^

    Sound FX & Effect configurables - will do (other than those I think everything that can be is already configurable).

    Configurables in GUI - do you mean through UDGs?
     
  9. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    7,738
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    Actually, I just checked it again, and it revives on the spot. No hidden point on the map required. http://www.hiveworkshop.com/forums/jass-resources-412/snippet-reviveunit-186696/

    The rawcode integers must be able to be configured. They should not be hardcoded in case the user has modified those raw abilities and needs to use a copied ability in order to fulfill that role. Unlikely to happen, but the idea is to cover the predictable possibilities.

    Yes. Jass2/vJass as the base of the system, but with a separate GUI trigger for the configurable things. This way, for people to use your system they need no vJass knowledge.
     
  10. loktar

    loktar

    Joined:
    Nov 2, 2004
    Messages:
    482
    Resources:
    23
    Models:
    2
    Icons:
    16
    Packs:
    1
    Tools:
    2
    Spells:
    1
    Tutorials:
    1
    Resources:
    23
    O rly. Will have a look at that :D

    Oh right, the abilities, mostly the meld abilities, looked over those.


    Alright, will do. Though I'm not sure how I'm gonna do that for adding units, as a function call is necessary for it. But I'll see what I can do :D

    Thanks for the feedback.
     
  11. loktar

    loktar

    Joined:
    Nov 2, 2004
    Messages:
    482
    Resources:
    23
    Models:
    2
    Icons:
    16
    Packs:
    1
    Tools:
    2
    Spells:
    1
    Tutorials:
    1
    Resources:
    23
    Updated - everything is configurable either through GUI or JASS. Hope this approach is acceptable. Seems the best way to offer both GUI and JASS config in one package.

    About the ReviveUnit snippet - I don't see how I can implement that. It resurrects a unit immediately where it dies, whereas in my system you have to revive it in the altar (or another building) to revive it.
    So a dummy unit isn't of any use. The reason I need to remove the original dying unit and replace it with a new one is that you can't add the fly ability, change flying height, or change opacity on a dead unit. So I need to create a living one and "simulate" death.
     
  12. BPower

    BPower

    Joined:
    Mar 18, 2012
    Messages:
    1,745
    Resources:
    21
    Spells:
    15
    Tutorials:
    1
    JASS:
    5
    Resources:
    21
    Cool system idea.

    Correct me, but what happens if a player loses for example two or more unit of same type ( grunts ).
    Can he revive the same amount or just one?

    The following are just notes and not required for approval.

    As everything is race handle based, you could also use one array for sound / effect per race
    instead of a scalar variables per race. Handles ids:
    Code (vJASS):
    // Reference: 1 - Human, 2 - Orc, 3 - Undead, 4 - Nightelf, 5 - Demon, 7 - Other, 11 - Naga


    I don't understand why you have to replace dying units with new equal units, then kill those.
    Can you explain? I'm curious. :)
    Code (vJASS):
                    set dUnit = CreateUnit(dyingOwner, dyingTypeId, GetUnitX(dyingUnit), GetUnitY(dyingUnit), GetUnitFacing(dyingUnit))
                    call RemoveUnit(dyingUnit)


    I think that
    function AllowAllPlayers takes integer wTypeId, boolean allow returns boolean
    has a too generic function name.

    function AddMeldAbil takes integer newAbilMeld returns boolean
    . This one also.

    I think those two functions are not really necessary.
    Code (vJASS):
            //===========================================================================
            // Set Flying ability
            //===========================================================================
            function SetFlyAbil takes integer newAbilFly returns nothing
                set abilFly = newAbilFly
            endfunction

            //===========================================================================
            // Set Locus ability (unselectable)
            //===========================================================================
            function SetSelectAbil takes integer newAbilSelect returns nothing
                set abilSelect = newAbilSelect
            endfunction


    Config
    Code (vJASS):
            //===========================================================================
            // Configure dissipate effect model
            //===========================================================================
            function ConfigureDissEffect takes string wRace, string dEffect returns boolean        
                if(wRace == "human") then
                    set dEffectH = dEffect
                elseif(wRace == "orc") then
                    set dEffectO = dEffect
                elseif(wRace == "nightelf") then
                    set dEffectNE = dEffect
                elseif(wRace == "undead") then
                    set dEffectU = dEffect
                elseif(wRace == "demon") then
                    set dEffectD = dEffect
                else
                    return false
                endif
               
                return true
            endfunction

            //===========================================================================
            // Configure dissipate sound
            //===========================================================================
            function ConfigureDissSound takes string wRace, sound dSound returns boolean           
                if(wRace == "human") then
                    set dSoundH = dSound
                elseif(wRace == "orc") then
                    set dSoundO = dSound
                elseif(wRace == "nightelf") then
                    set dSoundNE = dSound
                elseif(wRace == "undead") then
                    set dSoundU = dSound
                elseif(wRace == "demon") then
                    set dSoundD = dSound
                else
                    return false
                endif
               
                return true
            endfunction

            //===========================================================================
            // Configure revive effect model
            //===========================================================================
            function ConfigureRevEffect takes string wRace, string rEffect returns boolean         
                if(wRace == "human") then
                    set rEffectH = rEffect
                elseif(wRace == "orc") then
                    set rEffectO = rEffect
                elseif(wRace == "nightelf") then
                    set rEffectNE = rEffect
                elseif(wRace == "undead") then
                    set rEffectU = rEffect
                elseif(wRace == "demon") then
                    set rEffectD = rEffect
                else
                    return false
                endif
               
                return true
            endfunction

            //===========================================================================
            // Configure revive sound
            //===========================================================================
            function ConfigureRevSound takes string wRace, sound rSound returns boolean        
                if(wRace == "human") then
                    set rSoundH = rSound
                elseif(wRace == "orc") then
                    set rSoundO = rSound
                elseif(wRace == "nightelf") then
                    set rSoundNE = rSound
                elseif(wRace == "undead") then
                    set rSoundU = rSound
                elseif(wRace == "demon") then
                    set rSoundD = rSound
                else
                    return false
                endif
               
                return true
            endfunction

    ^Consider what I said about handle ids. You may change the string argument for race.
     
    Last edited: Mar 22, 2016
  13. loktar

    loktar

    Joined:
    Nov 2, 2004
    Messages:
    482
    Resources:
    23
    Models:
    2
    Icons:
    16
    Packs:
    1
    Tools:
    2
    Spells:
    1
    Tutorials:
    1
    Resources:
    23
    Thank you! :)

    If the owner of the unit has been set to revive that unit-type, only one can be revived. If the owner has not been set to revive, either he can't train the unit-type at all, or unlimited like normal if set through AllowAllPlayers().

    Hmm yeah, using arrays would be a lot better. If I'm understanding you correctly I could replace the if-statements with e.g. dSound[dRace] because e.g. RACE_HUMAN resolves to 1?
    Does the race variable type have 11 possible values? Because I thought there were only the ones I used (human/orc/undead/NE/demon).

    I can't apply vertex colouring or lift the unit with the fly ability on a corpse, so I have to create a new, living unit and play the death animation.
    Although thinking about it, I guess I could catch the unit right before it dies? But I'm not sure if that's reliable?

    Hmm, I will see if I can think of something more descriptive :p About the AddMeldAbil and SetSelectAbil, should I replace them with udg's? Because I was asked to make all abilities configurable.

    Yeah, could be a lot shorter with arrays. Same question as above though.



    Thanks for your feedback! I'll implement it as much as possible when I have some time.
     
  14. BPower

    BPower

    Joined:
    Mar 18, 2012
    Messages:
    1,745
    Resources:
    21
    Spells:
    15
    Tutorials:
    1
    JASS:
    5
    Resources:
    21
    Yes.
    Make sure you support the classic races orc/human/undead/nightelf. The others are optional.

    Mmh ok. The dummy unit for vertex color and death effect makes totally sense.
    I think it's better to hide the original unit instead of removing it upon death event.
     
  15. loktar

    loktar

    Joined:
    Nov 2, 2004
    Messages:
    482
    Resources:
    23
    Models:
    2
    Icons:
    16
    Packs:
    1
    Tools:
    2
    Spells:
    1
    Tutorials:
    1
    Resources:
    23
    Thanks for approval! I will definitely implement race handle id based arrays for effects/sounds and their config functions, seems a lot better than what I have now.
    I'll see about implementing an extra "stock" array so each unit that dies adds to the amount that can be revived.

    Hiding the unit instead of deleting - because it gets erased from memory anyway when it has decayed? Makes sense.

    Edit: I'm curious, where can I find a list of all race handle ids? (1-11)? Can't find any documentation on it.
    Edit2: alright, this works quite nicely, and it's so much simpler :D
    Code (vJASS):


                    set rRace = GetHandleId(GetUnitRace(rUnit))
                       
                    if(rEffect[rRace] != null and rEffect[rRace] != "") then
                        call DestroyEffect(AddSpecialEffectTarget(rEffect[rRace], rUnit, "origin"))
                    endif
                    if(rSound[rRace] != null) then
                        set rSoundTemp = rSound[rRace]
                        call AttachSoundToUnit(rSoundTemp, rUnit)
                        call StartSound(rSoundTemp)
                        set rSoundTemp = null
                    endif
     

    still have to update the configuration stuff, and then do the rest of your feedback :p

    Edit: alright, done. Might add the possibility to revive a unit-type for each unit that dies later
     
    Last edited: Mar 23, 2016