1. Are you planning to upload your awesome spell or system to Hive? Please review the rules here.
    Dismiss Notice
  2. Updated Resource Submission Rules: All model & skin resource submissions must now include an in-game screenshot. This is to help speed up the moderation process and to show how the model and/or texture looks like from the in-game camera.
    Dismiss Notice
  3. DID YOU KNOW - That you can unlock new rank icons by posting on the forums or winning contests? Click here to customize your rank or read our User Rank Policy to see a list of ranks that you can unlock. Have you won a contest and still havn't received your rank award? Then please contact the administration.
    Dismiss Notice
  4. The poll for Hive's 12th Concept Art Contest is up! Go cast your vote for your favourite genie!
    Dismiss Notice
  5. Travel to distant realms and encounter scenes unknown to the common folk. The Greatest of Adventures is upon us with the 8th Cinematic Contest. Join in on a fun ride.
    Dismiss Notice
  6. The 18th Icon Contest is ON! Choose any ingame unit and give him/her Hero abilities. Good luck to all.
    Dismiss Notice
  7. Contestants are to create a scene set in the Stone Age. Come and see what you can come up with. We wish you the best of luck!
    Dismiss Notice
  8. Colour outside the lines! Techtree Contest #13 is a go. The contest is optionally paired.
    Dismiss Notice
  9. Greetings cerebrates, our Swarm needs new spawners that will have numerous children. Join the HIVE's 31st Modeling Contest - Spawners and Spawned! The contest is optionally paired.
    Dismiss Notice
  10. Check out the Staff job openings thread.
    Dismiss Notice
Dismiss Notice
60,000 passwords have been reset on July 8, 2019. If you cannot login, read this.

Revivable Units v1.3.2

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

  • Toggle any unit type per player or for all players
  • Option to allow normal training for other players
  • Option to show a configurable dissipate effect on death (default)
  • Show a configurable revive effect
  • Revivable unit types are limited to one unit

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
  • Chat commands:
    • "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.2
//  by loktar
//  -------------------------------------------------------------------------
// * Toggle any unit type per player or for all players
// * Option to allow normal training for other players
// * Option to show a configurable dissipate effect on death (default)
// * Show a configurable revive effect
// * Revivable unit types are limited to one unit
//  -------------------------------------------------------------------------
//
//    -------
//    * API *
//    -------
//  *    SetTypeRevivable(integer unitTypeId, integer playerNumber)
//          - Make a unit type revivable for player(s).
//          -- playerNumber: ALL_P = 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: ALL_P = 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 model's 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) !!
//
//  ----------------------------------------------
//  ****  Look at the triggers for examples   ****
//  ----------------------------------------------
//
//   -----------------
//    * Test Map Info *
//   -----------------
//  *   Chat commands:
//          - "kill footman", "kill grunt", "kill ghoul", "kill archer", "kill stalker", "kill priest", "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         = ALL_P + 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 "00ff0303"
           elseif(playerId == 1) then
               return "000042ff"
           elseif(playerId == 2) then
               return "001ce6b9"
           elseif(playerId == 3) then
               return "00540081"
           elseif(playerId == 4) then
               return "00fffc00"
           elseif(playerId == 5) then
               return "00fe8a0e"
           elseif(playerId == 6) then
               return "0020c000"
           elseif(playerId == 7) then
               return "00e55bb0"
           elseif(playerId == 8) then
               return "00959697"
           elseif(playerId == 9) then
               return "007ebff1"
           elseif(playerId == 10) then
               return "00106246"
           elseif(playerId == 11) then
               return "004e2a04"
           elseif(playerId == 12) then
               return "009b0000"
           elseif(playerId == 13) then
               return "000000c3"
           elseif(playerId == 14) then
               return "0000eaff"
           elseif(playerId == 15) then
               return "00be00fe"
           elseif(playerId == 16) then
               return "00ebcd87"
           elseif(playerId == 17) then
               return "00f8a48b"
           elseif(playerId == 18) then
               return "00bfff80"
           elseif(playerId == 19) then
               return "00dcb9eb"
           elseif(playerId == 20) then
               return "00282828"
           elseif(playerId == 21) then
               return "00ebf0ff"
           elseif(playerId == 22) then
               return "0000781e"
           elseif(playerId == 23) then
               return "00a46f33"
           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 (ALL_P = all players; ANY_P = any player)
       //===========================================================================
       private function IsTypeRevivable takes integer wTypeId, integer wPlayers returns boolean
           local integer iType = GetTypeIndex(wTypeId)
           local integer iPlayer
         
           //Type is present
           if(wPlayers >= ANY_P  and wPlayers <= ALL_P 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 >= ALL_P
                   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 >= ALL_P
                   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 (ALL_P = all players; players 0-27)
       //===========================================================================      
       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 >= 0  and wPlayers <= ALL_P 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 >= ALL_P
                   endloop
               endif
             
               return true
           endif
         
           return false
       endfunction
     
       //===========================================================================
       // Make unit type unrevivable for players (ALL_P = all players; players 0-27)
       //===========================================================================  
       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 >= ALL_P
                   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 >= ALL_P
        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.2
//  by loktar
//  -------------------------------------------------------------------------
// * Toggle any unit type per player or for all players
// * Option to allow normal training for other players
// * Option to show a configurable dissipate effect on death (default)
// * Show a configurable revive effect
// * Revivable unit types are limited to one unit
//  -------------------------------------------------------------------------
//
//    -------
//    * API *
//    -------
//  *    SetTypeRevivable(integer unitTypeId, integer playerNumber)
//          - Make a unit type revivable for player(s).
//          -- playerNumber: ALL_P = 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: ALL_P = 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 model's 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         = ALL_P + 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 for dissipate
        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 "00ff0303"
            elseif(playerId == 1) then
                return "000042ff"
            elseif(playerId == 2) then
                return "001ce6b9"
            elseif(playerId == 3) then
                return "00540081"
            elseif(playerId == 4) then
                return "00fffc00"
            elseif(playerId == 5) then
                return "00fe8a0e"
            elseif(playerId == 6) then
                return "0020c000"
            elseif(playerId == 7) then
                return "00e55bb0"
            elseif(playerId == 8) then
                return "00959697"
            elseif(playerId == 9) then
                return "007ebff1"
            elseif(playerId == 10) then
                return "00106246"
            elseif(playerId == 11) then
                return "004e2a04"
            elseif(playerId == 12) then
                return "009b0000"
            elseif(playerId == 13) then
                return "000000c3"
            elseif(playerId == 14) then
                return "0000eaff"
            elseif(playerId == 15) then
                return "00be00fe"
            elseif(playerId == 16) then
                return "00ebcd87"
            elseif(playerId == 17) then
                return "00f8a48b"
            elseif(playerId == 18) then
                return "00bfff80"
            elseif(playerId == 19) then
                return "00dcb9eb"
            elseif(playerId == 20) then
                return "00282828"
            elseif(playerId == 21) then
                return "00ebf0ff"
            elseif(playerId == 22) then
                return "0000781e"
            elseif(playerId == 23) then
                return "00a46f33"
            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 (ALL_P = all players; ANY_P = any player; players 0-27)
        //===========================================================================
        private function IsTypeRevivable takes integer wTypeId, integer wPlayers returns boolean
            local integer iType = GetTypeIndex(wTypeId)
            local integer iPlayer
         
            //Type is present
            if(wPlayers >= ANY_P  and wPlayers <= ALL_P 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 >= ALL_P
                    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 >= ALL_P
                    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 (ALL_P = all players; players 0-27)
        //===========================================================================    
        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 > 0  and wPlayers <= ALL_P 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 >= ALL_P
                    endloop
                endif
             
                return true
            endif
         
            return false
        endfunction
     
        //===========================================================================
        // Make unit type unrevivable for players (ALL_P = all players; players 0-27)
        //===========================================================================
        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 >= ALL_P
                    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
         
        //** UNIT TYPE CONFIG EXAMPLES **//
            // 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 >= ALL_P
        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.2
  • 24-player support
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:
    715
    Resources:
    25
    Models:
    3
    Icons:
    16
    Packs:
    1
    Tools:
    2
    Spells:
    2
    Tutorials:
    1
    Resources:
    25
    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:
    715
    Resources:
    25
    Models:
    3
    Icons:
    16
    Packs:
    1
    Tools:
    2
    Spells:
    2
    Tutorials:
    1
    Resources:
    25
    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:
    8,018
    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:
    715
    Resources:
    25
    Models:
    3
    Icons:
    16
    Packs:
    1
    Tools:
    2
    Spells:
    2
    Tutorials:
    1
    Resources:
    25
    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:
    8,018
    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:
    715
    Resources:
    25
    Models:
    3
    Icons:
    16
    Packs:
    1
    Tools:
    2
    Spells:
    2
    Tutorials:
    1
    Resources:
    25
    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:
    715
    Resources:
    25
    Models:
    3
    Icons:
    16
    Packs:
    1
    Tools:
    2
    Spells:
    2
    Tutorials:
    1
    Resources:
    25
    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,741
    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:
    715
    Resources:
    25
    Models:
    3
    Icons:
    16
    Packs:
    1
    Tools:
    2
    Spells:
    2
    Tutorials:
    1
    Resources:
    25
    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,741
    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:
    715
    Resources:
    25
    Models:
    3
    Icons:
    16
    Packs:
    1
    Tools:
    2
    Spells:
    2
    Tutorials:
    1
    Resources:
    25
    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