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. The 15th Mini-Mapping Contest came to an end. The Secrets of Warcraft 3 are soon to be revealed! Come and vote in the public poll for your favorite maps.
    Dismiss Notice
  4. The 12th incarnation of the Music Contest is LIVE! The theme is Synthwave. Knight Rider needs a song to listen to on his journey. You should definitely have some fun with this theme!
    Dismiss Notice
  5. Join other hivers in a friendly concept-art contest. The contestants have to create a genie coming out of its container. We wish you the best of luck!
    Dismiss Notice
  6. 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.

HeroReviveCancelEvent v1.1

Submitted by IcemanBo, ZiBitheWand3r3r
This bundle is marked as approved. It works and satisfies the submission rules.
A little system to detect hero cancels that are done via mouse click on the hero icon from the altar's training queue.
There's a normal version, and a addon which supports GUI usage.

Thanks to @ZiBitheWand3r3r , both are authors.

Code (vJASS):

 library HeroReviveCancelEvent /* v1.1 -- hiveworkshop.com/threads/herorevivecancelevent.293491/#resource-72550

     Information
    ¯¯¯¯¯¯¯¯¯¯¯¯

        Provides an event that fires when a player cancels a hero revival at the altar.
        When using this system, you don't need the default event EVENT_PLAYER_HERO_REVIVE_CANCEL anymore.
       
       
     Mechanics
    ¯¯¯¯¯¯¯¯¯¯
   
        (Issue:)
       
        The default problem is that EVENT_PLAYER_HERO_REVIVE_CANCEL will only fire when using
        the "Cancel" spell, but not when directly clicking on the hero icon in training queue.
       
        (Solution:)
       
        When a hero starts reviving process, so enters the queue, we periodicly order the altar to try to 'revive'
        the hero. Normaly this order will return "false", as the hero is already being revived, but once it returns "true"
        we know, that the hero must not be in queue anymore. Then we fire the event.
       
        This method will work for both, when the "Cancel" spell is used, and when the hero icon is clicked in queue.
 */


//  ====API ====
//! novjass

    struct HeroReviveCancelEvent
   
    // register code
   
        static method register takes code c returns nothing
        static method unregister takes code c returns nothing
       
    // Inside registered code, you can access data:
   
       static thistype instance
           readonly unit hero       // hero that was reviving
           readonly unit building   // altar for revival
       

//! endnovjass
//  ===== End API ====

native UnitAlive takes unit u returns boolean
                                                 
    struct HeroReviveCancelEvent
       
        private static constant integer REVIVE_ORDER_ID_OFFSET = 852027
        private static constant integer CANCEL_ORDER_ID        = 851976
        private static constant integer MAX_HERO_AMOUNT        = 4
       
        private static constant real    TIMEOUT                = 0.1
       
        private static hashtable hash    = InitHashtable()
        private static trigger   handler = CreateTrigger()
       
        readonly static thistype instance
        readonly        unit     hero
        readonly        unit     building
       
        private timer clock
        private player owner
        private boolean exists
       
        private method destroy takes nothing returns nothing
            if .exists then
                set .exists = false
               
                call RemoveSavedInteger(hash, GetHandleId(.clock), 0)
                call RemoveSavedInteger(hash, GetHandleId(.hero), 0)
                call DestroyTimer(.clock)
               
                set .clock    = null
                set .hero     = null
                set .building = null
               
                call .deallocate()
            endif
        endmethod
       
        private static method callback takes nothing returns nothing
            local integer food
            local integer gold
            local integer lumber
           
            local thistype this = LoadInteger(hash, GetHandleId(GetExpiredTimer()), 0)
            if not UnitAlive(.building) or UnitAlive(.hero) then
                 call .destroy()
            endif
           
            // resources backup
            set gold   = GetPlayerState(.owner, PLAYER_STATE_RESOURCE_GOLD)
            set lumber = GetPlayerState(.owner, PLAYER_STATE_RESOURCE_LUMBER)
            set food   = GetPlayerState(.owner, PLAYER_STATE_RESOURCE_FOOD_USED)
           
            // give some conditions that will hopefully be always enough to successfuly order the revive order
            call SetPlayerState(.owner, PLAYER_STATE_RESOURCE_GOLD, 1000000)
            call SetPlayerState(.owner, PLAYER_STATE_RESOURCE_LUMBER, 1000000)
            call SetPlayerState(.owner, PLAYER_STATE_RESOURCE_FOOD_USED, 0)
           
            if IssueTargetOrder(.building, "revive", .hero) then
               
                //
                call IssueImmediateOrderById(.building, CANCEL_ORDER_ID)
               
                // retrieve original resources before the handler is fired
                call SetPlayerState(.owner, PLAYER_STATE_RESOURCE_GOLD, gold)
                call SetPlayerState(.owner, PLAYER_STATE_RESOURCE_LUMBER, lumber)
                call SetPlayerState(.owner, PLAYER_STATE_RESOURCE_FOOD_USED, food)
               
                // fire event
                set instance = this
                call TriggerEvaluate(.handler)
                call .destroy()
                return
            endif
           
            // retrieve original resources
            call SetPlayerState(.owner, PLAYER_STATE_RESOURCE_GOLD, gold)
            call SetPlayerState(.owner, PLAYER_STATE_RESOURCE_LUMBER, lumber)
            call SetPlayerState(.owner, PLAYER_STATE_RESOURCE_FOOD_USED, food)
        endmethod

        private static method create takes unit b, unit h returns thistype
            local thistype this = allocate()
           
            set .exists   = true
            set .clock    = CreateTimer()
            set .hero     = h
            set .building = b
            set .owner    = GetOwningPlayer(h)
           
            call TimerStart(.clock, TIMEOUT, true, function thistype.callback)
            call SaveInteger(hash, GetHandleId(.clock), 0, this)
            call SaveInteger(hash, GetHandleId(.hero), 0, this)
           
            return this
        endmethod
       
        // hero gets into revive queue, so now we start periodicaly watching it
        private static method onReviveStart takes nothing returns boolean
            local integer orderId = GetIssuedOrderId()
            if (orderId >= REVIVE_ORDER_ID_OFFSET and orderId <= REVIVE_ORDER_ID_OFFSET + MAX_HERO_AMOUNT) then
                call create(GetOrderedUnit(), GetOrderTargetUnit())
            endif
            return false
        endmethod
       
        // instantly destroy instance when unit finished reviving
        private static method onReviveFinish takes nothing returns boolean
            call thistype(LoadInteger(hash, GetHandleId(GetTriggerUnit()), 0)).destroy()
            return false
        endmethod
       
        private static method onInit takes nothing returns nothing
            local trigger t
            set t = CreateTrigger()
            call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER )
            call TriggerAddCondition(t, Condition(function thistype.onReviveStart))
           
            set t = CreateTrigger()
            call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_HERO_REVIVE_FINISH )
            call TriggerAddCondition(t, Condition(function thistype.onReviveFinish))
        endmethod
       
        // for API
       
        public static method register takes code c returns nothing
            local boolexpr bx = Condition(c)
            call SaveTriggerConditionHandle(hash, GetHandleId(bx), 0, TriggerAddCondition(handler, bx))
            set bx = null
        endmethod
       
        public static method unregister takes code c returns nothing
            local boolexpr bx = Condition(c)
            call TriggerRemoveCondition(handler, LoadTriggerConditionHandle(hash, GetHandleId(bx), 0))
            call FlushChildHashtable(hash, GetHandleId(bx))
            call DestroyBoolExpr(bx)
            set bx = null
        endmethod

    endstruct
endlibrary
 


Code (vJASS):
scope HeroReviveCancelEventGUI /* v1.1 -- hiveworkshop.com/threads/herorevivecancelevent.293491/#resource-72550
*/

    struct HeroReviveCancelEventGUI extends array
       
        private static method onHeroReviveCancel takes nothing returns nothing
            local HeroReviveCancelEvent this = HeroReviveCancelEvent.instance
            set udg_HRCE_Building = this.building
            set udg_HRCE_Hero = this.hero
            set udg_HRCE_State = 1.
            set udg_HRCE_State = 0.
        endmethod
       
        private static method onInit takes nothing returns nothing
            call HeroReviveCancelEvent.register(function thistype.onHeroReviveCancel)
        endmethod
   
    endstruct
endscope
 


How To Import A System
Contents

HeroReviveCancelEvent (Map)

Reviews
KILLCIDE
Awesome trick for detecting all possible ways of cancelling Hero Revive. System seems to work from my tests. The only issue I can find is the "double" upkeep sound you hear when reaching the upkeep boundaries. Also, the core of the system is in vJASS,...
  1. KILLCIDE

    KILLCIDE

    Administrator

    Joined:
    Jul 22, 2015
    Messages:
    3,491
    Resources:
    20
    Models:
    2
    Icons:
    10
    Spells:
    7
    Tutorials:
    1
    Resources:
    20
    Awesome trick for detecting all possible ways of cancelling Hero Revive. System seems to work from my tests. The only issue I can find is the "double" upkeep sound you hear when reaching the upkeep boundaries. Also, the core of the system is in vJASS, so keep the tags as such. I would recommend putting something like "GUI-Friendly" in the title name somewhere.

    Needs Fixed

    • Nothing

    Suggestions

    • I only favor
      TriggerRegisterAnyUnitEventBJ()
      when you are adding one event. I think it's okay to use the native here so that you only have one loop instead of two
    • What's the purpose of setting .exists to false onDestroy?
    • Why must the instance member be public?

    Status


    Approved
     
  2. Flux

    Flux

    Joined:
    Feb 6, 2014
    Messages:
    2,334
    Resources:
    28
    Maps:
    1
    Spells:
    19
    Tutorials:
    2
    JASS:
    6
    Resources:
    28
    I think the .exist is there for double free protection. Though structs already support that by default which prints something like "Double free error".

    HeroReviveCancelEvent.instance should be readonly as pointed^.
    And the API would be more convenient if it takes code argument instead.
     
  3. IcemanBo

    IcemanBo

    Joined:
    Sep 6, 2013
    Messages:
    6,107
    Resources:
    22
    Maps:
    3
    Spells:
    11
    Template:
    1
    Tutorials:
    4
    JASS:
    3
    Resources:
    22
    Thanks, KILLCIDE. Made the instance
    readonly
    .

    .exists
    is just for protection, in case a unit is revived without entering the revive queue.

    Flux, changed it. Now:
    Code (vJASS):
    static method register takes code c returns nothing
    static method unregister takes code c returns nothing


    Updated.