• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

IllusionSight v1.00

JASS:
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:

  • 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...
Level 37
Joined
Jul 22, 2015
Messages
3,485
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.
 
Top