HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

[vJass] ReviveUnit

06-28-2010, 10:44 AM#1
WaterKnight
A revive method for normal units, uses the Ancestral Spirit ability.

Code:
scope ReviveUnit initializer Init
    globals
        private constant integer DUMMY_ABILITY_ORDER_ID = 852490  //order id of Ancestral Spirit
        private unit DUMMY_UNIT
        private constant player DUMMY_UNIT_OWNER = Player(PLAYER_NEUTRAL_PASSIVE)
    endglobals

    //! textmacro ReviveUnit_CreateObjects takes doExternal, dummyAbilityRaw, dummyUnitRaw
        globals
            private constant integer DUMMY_ABILITY_ID = '$dummyAbilityRaw$'
            private constant integer DUMMY_UNIT_ID = '$dummyUnitRaw$'
        endglobals

        $doExternal$//! externalblock extension=lua ObjectMerger $FILENAME$
            //! i function set(field, value)
                //! i makechange(current, field, value)
            //! i end

            //! i function setl(field, level, value)
                //! i makechange(current, field, level, value)
            //! i end

            //! i setobjecttype("abilities")
            //! i createobject("Aast", "$dummyAbilityRaw$")
            //! i set("aart", "")
            //! i set("abpx", 0)
            //! i set("abpy", 0)
            //! i set("acat", "")
            //! i setl("acdn", 1, 0)
            //! i set("ahky", "")
            //! i setl("amcs", 1, 0)
            //! i set("anam", "Revive Unit")
            //! i set("arac", "other")
            //! i setl("aran", 1, 99999)
            //! i set("areq", "")
            //! i set("arqa", "")
            //! i setl("ast1", 1, 1)
            //! i setl("ast2", 1, 1)
            //! i setl("atar", 1, "air,dead,ground,invulnerable,player,structure,vulnerable")
            //! i set("atat", "")
            //! i setl("atp1", 1, "")
            //! i setl("aub1", 1, "")

            //! i setobjecttype("units")
            //! i createobject("uplg", "$dummyUnitRaw$")
            //! i set("ua1t", "")
            //! i set("ua2t", "")
            //! i set("uabi", "Aloc,$dummyAbilityRaw$")
            //! i set("uacq", 0)
            //! i set("uarm", "")
            //! i set("ubld", 0)
            //! i set("uble", 0)
            //! i set("ucbs", 0)
            //! i set("uclr", 0)
            //! i set("uclg", 0)
            //! i set("uclb", 0)
            //! i set("ucol", 0)
            //! i set("udro", "\0")
            //! i set("udtm", 0)
            //! i set("udty", "")
            //! i set("udup", 0)
            //! i set("uerd", 0)
            //! i set("ufle", "\0")
            //! i set("uhpm", 0)
            //! i set("uico", "")
            //! i set("uimz", 0)
            //! i set("uine", "\0")
            //! i set("ulpz", 0)
            //! i set("umdl", "")
            //! i set("umvt", "")
            //! i set("umxr", 0)
            //! i set("umxp", 0)
            //! i set("unam", "Revive Unit")
            //! i set("upgr", "")
            //! i set("upoi", 0)
            //! i set("uprw", 0)
            //! i set("urac", "other")
            //! i set("urtm", 0)
            //! i set("urun", 0)
            //! i set("usca", 0)
            //! i set("uscb", "\0")
            //! i set("ushr", "\0")
            //! i set("usma", 0)
            //! i set("usnd", "")
            //! i set("usrg", 0)
            //! i set("ussc", 0)
            //! i set("utar", "")
            //! i set("utc1", 0)
            //! i set("utc2", 0)
            //! i set("util", "")
            //! i set("uwal", 0)
            //! i set("uwu1", "\0")
            //! i set("uwu2", "\0")
        $doExternal$//! endexternalblock
    //! endtextmacro

    //! runtextmacro ReviveUnit_CreateObjects("", "ARev", "uRev")  //change the first parameter to "/" after the objects have been created and map was reloaded.

    function ReviveUnit takes unit whichUnit returns boolean
        local boolean hidden
        local boolean isStructure
        local integer playerFood
        local boolean result
        local integer unitFood
        local boolean useFood
        local player whichUnitOwner

        if (IsUnitType(whichUnit, UNIT_TYPE_DEAD) == false) then
            return false
        endif

        set hidden = IsUnitHidden(whichUnit)
        set isStructure = IsUnitType(whichUnit, UNIT_TYPE_STRUCTURE)
        set unitFood = GetFoodUsed(GetUnitTypeId(whichUnit))
        set whichUnitOwner = GetOwningPlayer(whichUnit)

        set playerFood = GetPlayerState(whichUnitOwner, PLAYER_STATE_RESOURCE_FOOD_USED)
        if (hidden) then
            call ShowUnit(whichUnit, true)
        endif

        call SetUnitOwner(DUMMY_UNIT, whichUnitOwner, false)  //Ancestral Spirit revives for the owner of the caster.
        call SetUnitUseFood(whichUnit, true)  //On revival the units lose some values, among them food, even if it was disabled via SetUnitUseFood beforehand. Interestingly, you can enable food usage in death again, so I use this here.
        call SetUnitX(DUMMY_UNIT, GetUnitX(whichUnit))  //to enable instant cast move the dummy to the reviving unit's location
        call SetUnitY(DUMMY_UNIT, GetUnitY(whichUnit))
        call UnitAddType(whichUnit, UNIT_TYPE_TAUREN)  //Only Tauren-classified units are revived by Ancestral Spirit and this I use for targeting. There should not be any other tauren while ReviveUnit executes. I could pick, remove, pick and add again the classification of other units here, but I think that only draws performance and is rare.
        call UnitShareVision(whichUnit, whichUnitOwner, true)  //Dummy needs sight to cast, dead units actually do not give that but using this function seems to be enough to cast on them, even if they have 0 sight range. I won't restore the old share vision status here as I think it's default to see the own units.

        set useFood = (GetPlayerState(whichUnitOwner, PLAYER_STATE_RESOURCE_FOOD_USED) != playerFood)

        call SetPlayerState(whichUnitOwner, PLAYER_STATE_RESOURCE_FOOD_USED, -unitFood)  //Ancestral Spirit requires the availability of free food the unit type needs, so I shortly set the used supply of the player to the future costs of the unit, in negative, since the food cap should be at least 0 and the supply limit does grab until there.

        set result = IssueImmediateOrderById(DUMMY_UNIT, DUMMY_ABILITY_ORDER_ID)

        if (isStructure or hidden) then
            call ShowUnit(whichUnit, false)
        endif
        call SetPlayerState(whichUnitOwner, PLAYER_STATE_RESOURCE_FOOD_USED, GetPlayerState(whichUnitOwner, PLAYER_STATE_RESOURCE_FOOD_USED) + unitFood + playerFood )
        call SetUnitOwner(DUMMY_UNIT, DUMMY_UNIT_OWNER, false)  //change dummy unit back to neutral passive to prevent ai to get silly ideas
        call SetUnitUseFood(whichUnit, useFood)
        call UnitRemoveType(whichUnit, UNIT_TYPE_TAUREN)

        set whichUnitOwner = null
        if (isStructure and (hidden == false)) then
            call ShowUnit(whichUnit, true)
        endif

        return result
    endfunction

    public function Init takes nothing returns nothing
        set DUMMY_UNIT = CreateUnit(DUMMY_UNIT_OWNER, DUMMY_UNIT_ID, 0., 0., 0.)
    endfunction
endscope

Some conditions and considerations:
- as commented above, there should not be other taurens
- there are some functions that might start events like SetUnitOwner, IssueImmediateOrderById, SetPlayerState --> be careful to not run some events that shouldn't
- UnitShareVision status gets set and not reset
- the function can be immediately used with the Unit-Death-Event
- upkeep will be redisplayed
- the spell works even if the object editor states the unit would not be revivalable but you should consider that non-decaying units have a shorter decay duration (only death time I guess), a unit cannot be revived this way if it already decayed, instantly reviving on death event however is also possible if the unit is non-decaying and has a death time of zero
06-28-2010, 09:26 PM#2
Bobo_The_Kodo
Does this work instantly?
And why is ancestral spirit better than ressurection?
06-28-2010, 11:04 PM#3
WaterKnight
It does work instantly. You may kill the unit again right afterwards if you want.

I did not compare Ancestral Spirit to Resurrection but preferred it because I thought it was more secure to aim the target. Resurrection is targetless and revives the highest-leveled units nearby. I could have reduced the area range when dummy unit is directly ported to the target but did not know whether a tangential value of zero would be enough and however, some units could be lying above each other on the same spot.
06-29-2010, 02:34 AM#4
Thunder_Eye
Quote:
Originally Posted by WaterKnight
but did not know whether a tangential value of zero would be enough and however, some units could be lying above each other on the same spot.

You could move away any unwanted corpses to a temporary location and then back when ressurection is done. Not sure what quirks Ancestral Spirit has though.

Seems like a nice function otherwise, could prove useful, though I have no use for it.
06-29-2010, 01:02 PM#5
WaterKnight
Quote:
Originally Posted by Thunder_Eye
You could move away any unwanted corpses to a temporary location and then back when ressurection is done.

This would take an unknown amount of iterations. Also it could trigger enter/leave events. I like exotic spells :)
07-08-2010, 04:01 PM#6
DioD
actually ancentral spirit is aim casted, you can cast it on selected unit, unlike ressurection that revive multiple random units around.
07-11-2010, 02:25 AM#7
Nestharus
Nice function : D
07-24-2010, 06:05 PM#8
Bobo_The_Kodo
Is it possible to make this able to revive buildings?
07-24-2010, 09:57 PM#9
WaterKnight
It is able to revive buildings. Though, I do not know every value that is lost on death and not restored on revival. For example, the ubersplat of the building does not get recreated.

Update: Added invulnerable and vulnerable as targets as I found out that ancestral spirit does not work on invulnerable units on default.
07-25-2010, 12:54 AM#10
Bobo_The_Kodo
If you hide and show a building it will recreate the ubersplat - you might wanna add that
07-25-2010, 09:14 AM#11
WaterKnight
done, it also now checks if the unit is even dead and works on hidden units
07-25-2010, 03:00 PM#12
Anitarf
I would prefer to see this function use the xe dummy instead of creating its own.
07-25-2010, 10:10 PM#13
Bobo_The_Kodo
And you could probably submit it for scripts section after ;)
07-25-2010, 11:04 PM#14
DioD
note:

this will fail if you hit max food, since you cant increase food value anymore

to evoid this problem you shoud increase food limit by 1 and if this is not possible decrease current food used by 1.
07-26-2010, 11:29 AM#15
WaterKnight
Quote:
Originally Posted by Anitarf
I would prefer to see this function use the xe dummy instead of creating its own.

I've heard of xe cast before but do not use it actually. In my own maps, the function here is alternated anyway, I also use my shared caster I call World Caster.

Code:
scope ReviveUnit initializer Init
    globals
        private constant integer DUMMY_ABILITY_ORDER_ID = 852490  //order id of Ancestral Spirit
        private xecast DUMMY_CASTER
    endglobals

    //! textmacro ReviveUnit_CreateObjects takes doExternal, dummyAbilityRaw
        globals
            private constant integer DUMMY_ABILITY_ID = '$dummyAbilityRaw$'
        endglobals

        $doExternal$//! externalblock extension=lua ObjectMerger $FILENAME$
            //! i function set(field, value)
                //! i makechange(current, field, value)
            //! i end

            //! i function setl(field, level, value)
                //! i makechange(current, field, level, value)
            //! i end

            //! i setobjecttype("abilities")
            //! i createobject("Aast", "$dummyAbilityRaw$")
            //! i set("aart", "")
            //! i set("abpx", 0)
            //! i set("abpy", 0)
            //! i set("acat", "")
            //! i setl("acdn", 1, 0)
            //! i set("ahky", "")
            //! i setl("amcs", 1, 0)
            //! i set("anam", "Revive Unit")
            //! i set("arac", "other")
            //! i setl("aran", 1, 99999)
            //! i set("areq", "")
            //! i set("arqa", "")
            //! i setl("ast1", 1, 1)
            //! i setl("ast2", 1, 1)
            //! i setl("atar", 1, "air,dead,ground,invulnerable,player,structure,vulnerable")
            //! i set("atat", "")
            //! i setl("atp1", 1, "")
            //! i setl("aub1", 1, "")
        $doExternal$//! endexternalblock
    //! endtextmacro

    //! runtextmacro ReviveUnit_CreateObjects("", "ARev")  //change the first parameter to "/" after the objects have been created and map was reloaded.

    function ReviveUnit takes unit whichUnit returns boolean
        local boolean hidden
        local boolean isStructure
        local integer playerFood
        local boolean result
        local integer unitFood
        local boolean useFood
        local player whichUnitOwner

        if (IsUnitType(whichUnit, UNIT_TYPE_DEAD) == false) then
            return false
        endif

        set hidden = IsUnitHidden(whichUnit)
        set isStructure = IsUnitType(whichUnit, UNIT_TYPE_STRUCTURE)
        set unitFood = GetFoodUsed(GetUnitTypeId(whichUnit))
        set whichUnitOwner = GetOwningPlayer(whichUnit)

        set playerFood = GetPlayerState(whichUnitOwner, PLAYER_STATE_RESOURCE_FOOD_USED)
        if (hidden) then
            call ShowUnit(whichUnit, true)
        endif

        set DUMMY_CASTER.owningplayer = whichUnitOwner
        call SetUnitUseFood(whichUnit, true)  //On revival the units lose some values, among them food, even if it was disabled via SetUnitUseFood beforehand. Interestingly, you can enable food usage in death again, so I use this here.
        call UnitAddType(whichUnit, UNIT_TYPE_TAUREN)  //Only Tauren-classified units are revived by Ancestral Spirit and this I use for targeting. There should not be any other tauren while ReviveUnit executes. I could pick, remove, pick and add again the classification of other units here, but I think that only draws performance and is rare.
        call UnitShareVision(whichUnit, whichUnitOwner, true)  //Dummy needs sight to cast, dead units actually do not give that but using this function seems to be enough to cast on them, even if they have 0 sight range. I won't restore the old share vision status here as I think it's default to see the own units.

        set useFood = (GetPlayerState(whichUnitOwner, PLAYER_STATE_RESOURCE_FOOD_USED) != playerFood)

        call SetPlayerState(whichUnitOwner, PLAYER_STATE_RESOURCE_FOOD_USED, -unitFood)  //Ancestral Spirit requires the availability of free food the unit type needs, so I shortly set the used supply of the player to the future costs of the unit, in negative, since the food cap should be at least 0 and the supply limit does grab until there.

        call DUMMY_CASTER.castInPoint(GetUnitX(whichUnit), GetUnitY(whichUnit))
        set result = (IsUnitType(whichUnit, UNIT_TYPE_DEAD) == false)

        if (isStructure or hidden) then
            call ShowUnit(whichUnit, false)
        endif
        call SetPlayerState(whichUnitOwner, PLAYER_STATE_RESOURCE_FOOD_USED, GetPlayerState(whichUnitOwner, PLAYER_STATE_RESOURCE_FOOD_USED) + unitFood + playerFood )
        call SetUnitUseFood(whichUnit, useFood)
        call UnitRemoveType(whichUnit, UNIT_TYPE_TAUREN)

        set whichUnitOwner = null
        if (isStructure and (hidden == false)) then
            call ShowUnit(whichUnit, true)
        endif

        return result
    endfunction

    public function Init takes nothing returns nothing
        set DUMMY_CASTER = xecast.createBasic(DUMMY_ABILITY_ID, DUMMY_ABILITY_ORDER_ID, null)
    endfunction
endscope

So is this okay?

Quote:
Originally Posted by DioD
this will fail if you hit max food, since you cant increase food value anymore

That's why I set the current food in the negative area and make it depend on the unit's own supply costs. It works for me. Though, it redisplays upkeep.

Another note:
The spell works even if the object editor states the unit would not be revivalable but you should consider that non-decaying units have a shorter decay duration (only death time I guess), a unit cannot be revived this way if it already decayed, instantly reviving on death event however is also possible if the unit is non-decaying and has a death time of zero.