1. Are you planning to upload your awesome spell or system to Hive? Please review the rules here.
    Dismiss Notice
  2. The Aftermath has been revealed for the 19th Terraining Contest! Be sure to check out the Results and see what came out of it.
    Dismiss Notice
  3. Melee Mapping Contest #3 - Results are out! Congratulate the winners and check plenty of new 4v4 melee maps designed for this competition!
    Dismiss Notice
  4. The winners of our cinematic soundtrack competition have been decided! Step by the Music Contest #11 - Results to check the entries and congratulate the winners!
    Dismiss Notice

IllusionSight v1.00

Submitted by Flux
This bundle is marked as approved. It works and satisfies the submission rules.
Code (vJASS):

library IllusionSight uses/*

                          Illusion Sight v1.00
                                by Flux
     
        An IllusionSight grants a unit the ability to distinguish enemy illusions.
     
  */
optional RegisterPlayerUnitEvent /*

     
    *********************************
                   API
    *********************************
     
    struct IllusionSight
     
        static method create takes unit u, real radius, real duration returns IllusionSight
            Add an Illusion Sight to <unit u> revealing if units nearby are illusions within <real radius>
            for <real duration> second(s). For infinite duration, use 0.
     
        static method addToItem takes integer itemId, real radius returns nothing
            Make all items of rawcode <integer itemId> have IllusionSight having <real radius>.
         
        static method defaultColor takes integer unitId, integer red, integer green, integer blue returns nothing
            Tells the system that all units with rawcode <integer unitId> has a vertex color of
            <integer red>, <integer greenn>, <integer blue> configured in Object Editor.
     
        method destroy takes nothing returns nothing
            Destroy an IllusionSight instance. You mostly won't need this if the duration is not set to 0.
 
      CREDITS:
        Magtheridon96   - RegisterPlayerUnitEvent
 
    */

 
    globals
        //Recommended Value: 0.1 to 0.2
        private constant real TIMEOUT = 0.1
     
        //If certain items will have a passive Illusion Sight, set this to true
        //else set it to false to have lesser compiled code
        private constant boolean WILL_USE_ON_ITEMS = true
    endglobals
 
    native UnitAlive takes unit u returns boolean
 
    private function TargetFilter takes unit u, player owner returns boolean
        return UnitAlive(u) and IsUnitEnemy(u, owner)
    endfunction
 
    private struct Color
     
        readonly integer id
        readonly integer r
        readonly integer g
        readonly integer b
     
        method destroy takes nothing returns nothing
            call this.deallocate()
        endmethod
     
        static method create takes integer unitId, integer red, integer green, integer blue returns thistype
            local thistype this = thistype.allocate()
            set this.id = unitId
            set this.r = red
            set this.g = green
            set this.b = blue
            return this
        endmethod
     
    endstruct
 
    struct IllusionSight
     
        private unit u
        private player owner
        private group visible
        private boolean inf
        private real duration
        private real radius
     
        private thistype next
        private thistype prev
     
        private static group g = CreateGroup()
        private static group swap = CreateGroup()
        private static group tempG
        private static timer t = CreateTimer()
     
        private static hashtable hash = InitHashtable()
     
        private method reset takes unit u returns nothing
            local integer id = GetHandleId(u)
            local integer count = LoadInteger(thistype.hash, id, 0) - 1
            local Color c
            call SaveInteger(thistype.hash, id, 0, count)
            if count == 0 then
                if IsUnitAlly(this.u, GetLocalPlayer()) then
                    if HaveSavedInteger(thistype.hash, GetUnitTypeId(u), 0) then
                        set c = LoadInteger(thistype.hash, GetUnitTypeId(u), 0)
                        call SetUnitVertexColor(u, c.r, c.g, c.b, 255)
                    else
                        call SetUnitVertexColor(u, 255, 255, 255, 255)
                    endif
                endif
                call RemoveSavedInteger(thistype.hash, id, 0)
            endif
        endmethod

        method destroy takes nothing returns nothing
            local unit u
            set this.prev.next = this.next
            set this.next.prev = this.prev
            if thistype(0).next == 0 then
                call PauseTimer(thistype.t)
            endif
            loop
                set u = FirstOfGroup(this.visible)
                exitwhen u == null
                call GroupRemoveUnit(this.visible, u)
                call this.reset(u)
            endloop
            call DestroyGroup(this.visible)
            set this.visible = null
            set this.u = null
            call this.deallocate()
        endmethod
     
        //! textmacro ILLLUSION_SIGHT_PERIODIC_UPDATE
            if not this.inf then
                set this.duration = this.duration - TIMEOUT
            endif
            if this.duration > 0 then
                call GroupEnumUnitsInRange(thistype.g, GetUnitX(this.u), GetUnitY(this.u), this.radius, null)
                loop
                    set u = FirstOfGroup(thistype.g)
                    exitwhen u == null
                    call GroupRemoveUnit(thistype.g, u)
                    if IsUnitIllusion(u) and TargetFilter(u, this.owner) and not IsUnitInGroup(u, this.visible) then
                        call GroupAddUnit(this.visible, u)
                        set id = GetHandleId(u)
                        call SaveInteger(thistype.hash, id, 0, LoadInteger(thistype.hash, id, 0) + 1)
                    endif
                endloop
                loop
                    set u = FirstOfGroup(this.visible)
                    exitwhen u == null
                    call GroupRemoveUnit(this.visible, u)
                    if IsUnitInRange(u, this.u, this.radius) then
                        call GroupAddUnit(thistype.swap, u)
                        if IsUnitAlly(this.u, GetLocalPlayer()) then
                            call SetUnitVertexColor(u, 0, 0, 255, 255)
                        endif
                    else
                        call this.reset(u)
                    endif
                endloop
                set thistype.tempG = this.visible
                set this.visible = thistype.swap
                set thistype.swap = thistype.tempG
            else
                call this.destroy()
            endif
        //! endtextmacro
     
        private static method pickAll takes nothing returns nothing
            local thistype this = thistype(0).next
            local integer id
            local unit u
            loop
                exitwhen this == 0
                //! runtextmacro ILLLUSION_SIGHT_PERIODIC_UPDATE()
                set this = this.next
            endloop
        endmethod

        static method create takes unit u, real radius, real duration returns thistype
            local thistype this = thistype.allocate()
            set this.u = u
            set this.owner = GetOwningPlayer(u)
            set this.radius = radius
            if duration == 0 then
                set this.inf = true
                set this.duration = TIMEOUT
            else
                set this.inf = false
                set this.duration = duration
            endif
            set this.visible = CreateGroup()
            set this.next = 0
            set this.prev = thistype(0).prev
            set this.next.prev = this
            set this.prev.next = this
            if this.prev == 0 then
                call TimerStart(thistype.t, TIMEOUT, true, function thistype.pickAll)
            endif
            return this
        endmethod
     
        static method defaultColor takes integer unitId, integer red, integer green, integer blue returns nothing
            call SaveInteger(thistype.hash, unitId, 0, Color.create(unitId, red, green, blue))
        endmethod
         
     
        static if WILL_USE_ON_ITEMS then
     
            private static method drop takes nothing returns nothing
                local item it = GetManipulatedItem()
                local integer id = GetItemTypeId(it)
                if HaveSavedReal(thistype.hash, id, 0) then
                    call thistype(LoadInteger(thistype.hash, GetHandleId(GetTriggerUnit()), GetHandleId(it))).destroy()
                endif
                set it = null
            endmethod
         
            private static method pick takes nothing returns nothing
                local item it = GetManipulatedItem()
                local integer id = GetItemTypeId(it)
                local unit u
                if HaveSavedReal(thistype.hash, id, 0) then
                    set u = GetTriggerUnit()
                    call SaveInteger(thistype.hash, GetHandleId(u), GetHandleId(it), thistype.create(u, LoadReal(thistype.hash, id, 0), 0))
                    set u = null
                endif
                set it = null
            endmethod
         
            static method addToItem takes integer itemId, real radius returns nothing
                call SaveReal(thistype.hash, itemId, 0, radius)
            endmethod
         
            private static method onInit takes nothing returns nothing
                static if LIBRARY_RegisterPlayerUnitEvent then
                    call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_PICKUP_ITEM, function thistype.pick)
                    call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_DROP_ITEM, function thistype.drop)
                else
                    local trigger pickTrg = CreateTrigger()
                    local trigger dropTrg = CreateTrigger()
                    local code c1 = function thistype.pick
                    local code c2 = function thistype.drop
                    call TriggerRegisterAnyUnitEventBJ(pickTrg, EVENT_PLAYER_UNIT_PICKUP_ITEM)
                    call TriggerRegisterAnyUnitEventBJ(dropTrg, EVENT_PLAYER_UNIT_DROP_ITEM)
                    call TriggerAddCondition(pickTrg, Filter(c1))
                    call TriggerAddCondition(dropTrg, Filter(c2))
                endif
            endmethod
     
        endif

    endstruct
 
endlibrary
 


DEMO:
Basic Demo

  • Demo Initialize
    • Events
      • Time - Elapsed game time is 0.10 seconds
    • Conditions
    • Actions
      • Set Blademaster = Blademaster 0003 <gen>
      • Unit - Order Blademaster to Orc Blademaster - Mirror Image
      • Set Blademaster = Blademaster of Blackrock Clan 0001 <gen>
      • Unit - Order Blademaster to Orc Blademaster - Mirror Image
      • -------- Tells the system that the configured Vertex Color of corrupted Blademaster has changed --------
      • -------- 200, 0, 0 is the configured RGB in Object Editor --------
      • Custom script: call IllusionSight.defaultColor(GetUnitTypeId(udg_Blademaster), 200, 0, 0)
      • -------- --------------------------------------------------------------------------- --------
      • -------- Add the IllusionSight to the items --------
      • Custom script: call IllusionSight.addToItem('Igm1', 500)
      • Custom script: call IllusionSight.addToItem('Igm2', 1000)
      • Custom script: call IllusionSight.addToItem('Igm3', 1200)
      • Game - Display to (All players) the text: Casting |cffffcc00R...



Changelog:
v1.00 - [11 July 2016]
- Initial Release
Contents

IllusionSight v1.00 (Map)

Reviews
KILLCIDE
Not only did this system work flawlessly (from my tests), but the mechanic is extremely unique. I have never seen a WC3 map utilize a mechanic like this before. However, there is just one problem I personally have: the ugly red...
  1. Almia

    Almia

    Joined:
    Apr 24, 2012
    Messages:
    4,839
    Resources:
    35
    Spells:
    30
    Tutorials:
    4
    JASS:
    1
    Resources:
    35
    isn't it expensive and slow to use timers?

    I think you can use a lib like AuraStruct or UnitInRangeEvent(yes, I am advertising Nestharus' resources)
     
  2. Flux

    Flux

    Joined:
    Feb 6, 2014
    Messages:
    2,334
    Resources:
    28
    Maps:
    1
    Spells:
    19
    Tutorials:
    2
    JASS:
    6
    Resources:
    28
    It runs at a timeout of 0.1 second.

    And when did timers became "slow"? Nearly all spells submitted here use timers at a heavier performance timeout of 0.03125.
     
  3. Almia

    Almia

    Joined:
    Apr 24, 2012
    Messages:
    4,839
    Resources:
    35
    Spells:
    30
    Tutorials:
    4
    JASS:
    1
    Resources:
    35
    By slow I mean in comparison with the unit in range event.

    ok nvm, i think that's a minor problem
     
  4. Flux

    Flux

    Joined:
    Feb 6, 2014
    Messages:
    2,334
    Resources:
    28
    Maps:
    1
    Spells:
    19
    Tutorials:
    2
    JASS:
    6
    Resources:
    28
    If I recall correctly, Unit in Range event internally uses a periodic.timer checking any new unit in range because when it triggers, it has an inconsistent delay.

    I appreciate the suggestion though.
     
  5. KILLCIDE

    KILLCIDE

    Administrator

    Joined:
    Jul 22, 2015
    Messages:
    3,473
    Resources:
    20
    Models:
    2
    Icons:
    10
    Spells:
    7
    Tutorials:
    1
    Resources:
    20
    Not only did this system work flawlessly (from my tests), but the mechanic is extremely unique. I have never seen a WC3 map utilize a mechanic like this before. However, there is just one problem I personally have: the ugly red
    TriggerRegisterAnyUnitEventBJ()
    :p why have it loop through twice when you can just use one loop? For readability?

    Other than that, the code is clean with easy to remember API. A well deserved 5/5. Approved.
     
  6. KILLCIDE

    KILLCIDE

    Administrator

    Joined:
    Jul 22, 2015
    Messages:
    3,473
    Resources:
    20
    Models:
    2
    Icons:
    10
    Spells:
    7
    Tutorials:
    1
    Resources:
    20
    Sorry for the update. Just updating the tags.