1. Are you planning to upload your awesome spell or system to Hive? Please review the rules here.
    Dismiss Notice
  2. The 30th edition of the Modeling Contest is finally up! The Portable Buildings need your attention, so come along and have a blast!
    Dismiss Notice
  3. 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
  4. Melee Mapping Contest #3 - Results are out! Congratulate the winners and check plenty of new 4v4 melee maps designed for this competition!
    Dismiss Notice
  5. 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

TrueSight v1.10

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

library TrueSight /*

                     True Sight v1.10
                         by Flux
       
    Allows dynamically assigning a True Sight to a unit at any range
    using only 1 ability.
       
    */
requires /*
       nothing
       
    */
optional DummyRecycler /*
        if not found, the system will create a new dummy every time a unit passing the filter is within range.
        Highly recommended.
       
    */
optional RegisterPlayerUnitEvent /*
        if not found, it will create a new trigger with the Item Picked and Item Dropped event.
   
   
    Known Issues:
        - Actual sight radius sometimes become "radius + 64" if a unit outside radius is close enough to a revealed unit.
        - Minimum acceptable radius is 64
               
                       
    *********************************
                   API
    *********************************
       
    struct TrueSight
       
        public real radius
            You can edit the radius of a TrueSight instance anytime.
       
        static method create takes unit u, real radius returns TrueSight
            Add a True Sight to <unit u> revealing invisible units within <real radius> for
            <real duration> second(s).
       
        method operator duration= takes real time returns nothing
            Add a duration counter to a TrueSight instance.
           
        static method addToItem takes integer itemId, real radius returns nothing
            Make all items of rawcode <integer itemId> have TrueSight having <real radius>.
       
        method destroy takes nothing returns nothing
            Destroy a TrueSight instance. You mostly won't need this if duration is not zero.
 
         
    CREDITS:
        Flux                - DummyRecycler
        Magtheridon96       - RegisterPlayerUnitEvent
           
    */

   
    globals
        //A True Sight Ability with 64 Cast Range
        private constant integer TRUE_SIGHT_ABILITY = 'ATSS'
       
        //If DummyRecycler is not found, it will create units using this rawcode
        private constant integer DUMMY_ID = 'dumi'
       
        //Recommended Value: 0.05 to 0.25
        //If value is too high, revealed unit may sometimes flicker (switching between visible and invisible)
        //Lower value = Better detection
        //Higher value = Better performance
        private constant real TIMEOUT = 0.05
       
        //If certain items will have a passive TrueSight, 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 IsUnitInvisibleFilter takes unit u returns boolean
        return GetUnitAbilityLevel(u, 'Apiv') > 0 or GetUnitAbilityLevel(u, 'Agho') or ...
                                        --- or ---
        return IsUnitInGroup(u, invisibleUnits) //where invisibleUnits contains all invisible units in the map
    endfunction
    ^User could add a filter to make it more selective resulting to improved performance.
   
    */

   
    private function TargetFilter takes unit u, player owner returns boolean
        return UnitAlive(u) and IsUnitEnemy(u, owner) //and IsUnitInvisibleFilter(u)
    endfunction
   
    private struct SightSource
       
        readonly unit u
        readonly unit target
       
        readonly thistype next
        readonly thistype prev
       
        method destroy takes nothing returns nothing
            set this.prev.next = this.next
            set this.next.prev = this.prev
            if this.u != null then
                static if LIBRARY_DummyRecycler then
                    call UnitRemoveAbility(this.u, TRUE_SIGHT_ABILITY)
                    call RecycleDummy(this.u)
                else
                    call RemoveUnit(this.u)
                endif
                set this.u = null
            endif
            set this.target = null
            call this.deallocate()
        endmethod
       
        static method create takes thistype head, unit target, player owner returns thistype
            local thistype this = thistype.allocate()
            set this.target = target
            static if LIBRARY_DummyRecycler then
                set this.u = GetRecycledDummyAnyAngle(GetUnitX(target), GetUnitY(target), 0)
                call PauseUnit(this.u, false)
                call SetUnitOwner(this.u, owner, false)
            else
                set this.u = CreateUnit(owner, DUMMY_ID, GetUnitX(target), GetUnitY(target), 0)
            endif
            call UnitAddAbility(this.u, TRUE_SIGHT_ABILITY)
            set this.next = head
            set this.prev = head.prev
            set this.next.prev = this
            set this.prev.next = this
            return this
        endmethod
       
        static method head takes nothing returns thistype
            local thistype this = thistype.allocate()
            set this.next = this
            set this.prev = this
            return this
        endmethod
       
    endstruct
   
    struct TrueSight
       
        public real radius
       
        readonly unit u
        readonly player owner
       
        private SightSource sightHead
        private real dur
        private group visible
        private boolean inf
       
        private thistype next
        private thistype prev
       
        private static group g = CreateGroup()
        private static timer t = CreateTimer()
       
        method destroy takes nothing returns nothing
            local SightSource sight = this.sightHead.next
            set this.prev.next = this.next
            set this.next.prev = this.prev
            if thistype(0).next == 0 then
                call PauseTimer(thistype.t)
            endif
            //Destroy all SightSource
            loop
                exitwhen sight == this.sightHead
                call sight.destroy()
                set sight = sight.next
            endloop
            call this.sightHead.destroy()
            call DestroyGroup(this.visible)
            set this.visible = null
            set this.u = null
            call this.deallocate()
        endmethod
       

        private static method pickAll takes nothing returns nothing
            local thistype this = thistype(0).next
            local SightSource sight
            local player newOwner
            local boolean b
            local unit u
            loop
                exitwhen this == 0
               
                set newOwner = GetOwningPlayer(this.u)
                set b = newOwner != this.owner
                if b then
                    set this.owner = newOwner
                endif
                if not this.inf then
                    set this.dur = this.dur - TIMEOUT
                endif
               
                if (this.dur > 0 or this.inf) and UnitAlive(this.u) then
                    //Find new invisible units
                    call GroupEnumUnitsInRange(thistype.g, GetUnitX(this.u), GetUnitY(this.u), this.radius + 128, null)
                    loop
                        set u = FirstOfGroup(thistype.g)
                        exitwhen u == null
                        call GroupRemoveUnit(thistype.g, u)
                       
                        if not IsUnitInGroup(u, this.visible) and IsUnitInRange(this.u, u, this.radius) and TargetFilter(u, this.owner) then
                            call GroupAddUnit(this.visible, u)
                            call SightSource.create(this.sightHead, u, this.owner)
                        endif
                    endloop
                    //Update SightSources
                    set sight = this.sightHead.next
                    loop
                        exitwhen sight == this.sightHead
                        if IsUnitInRange(this.u, sight.target, this.radius) and TargetFilter(sight.target, this.owner) then
                            call SetUnitX(sight.u, GetUnitX(sight.target))
                            call SetUnitY(sight.u, GetUnitY(sight.target))
                            if b then
                                call SetUnitOwner(sight.u, this.owner, false)
                            endif
                        else
                            call GroupRemoveUnit(this.visible, sight.target)
                            call sight.destroy()
                        endif
                        set sight = sight.next
                    endloop
                else
                    call this.destroy()
                endif
               
                set this = this.next
            endloop
        endmethod
       
        static method create takes unit u, real radius returns thistype
            local thistype this = thistype.allocate()
            set this.u = u
            set this.owner = GetOwningPlayer(u)
            set this.radius = radius
            set this.sightHead = SightSource.head()
            set this.inf = true
            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
       
        method operator duration takes nothing returns real
            return this.dur
        endmethod
       
        method operator duration= takes real time returns nothing
            set this.inf = false
            set this.dur = time
        endmethod
       
        static if WILL_USE_ON_ITEMS then
           
            private static hashtable hash = InitHashtable()
       
            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)))
                    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
 


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

v1.01 - [13 August 2016]
- Dying units now automatically loses a True Sight.
From
if this.duration > 0 then
to
if this.duration > 0 and UnitAlive(this.u) then


v1.02 - [14 August 2016]
- Struct member radius can now be modified. From
readonly real radius
to
real radius

- The hashtable will only be needed if WILL_USE_ON_ITEMS is true.

v1.10 - [27 December 2016]
- Changed API, static method create no longer has a duration parameter.
- Added method operator duration allowing an instance to have a timed duration.
- Fixed bug where changing unit owner does not change TrueSight to the new owner.
Contents

TrueSight v1.10 (Map)

Reviews
KILLCIDE
A fairly simple system in terms of concept, but the idea and execution are pretty clever. The ability to define a range for every sight instance is a huge plus for me. The easy to use API, extremely clean code by none other than Flux, and "one"...
  1. Flux

    Flux

    Joined:
    Feb 6, 2014
    Messages:
    2,334
    Resources:
    28
    Maps:
    1
    Spells:
    19
    Tutorials:
    2
    JASS:
    6
    Resources:
    28
    Bump?
     
  2. Jay-B

    Jay-B

    Joined:
    Aug 5, 2016
    Messages:
    41
    Resources:
    0
    Resources:
    0
    This is actually good :D keep going
     
  3. KILLCIDE

    KILLCIDE

    Administrator

    Joined:
    Jul 22, 2015
    Messages:
    3,473
    Resources:
    20
    Models:
    2
    Icons:
    10
    Spells:
    7
    Tutorials:
    1
    Resources:
    20
    Did you post the code on Firefox? There is no indentation in your code :p if you did post it on firefox, make sure to use the Plain Text Editor (paper + wrench icon on the top right of the textbox) when you post code / triggers.
     
  4. Flux

    Flux

    Joined:
    Feb 6, 2014
    Messages:
    2,334
    Resources:
    28
    Maps:
    1
    Spells:
    19
    Tutorials:
    2
    JASS:
    6
    Resources:
    28
    Was that the reason why other submissions got reviewed/moderated first before this even if this was uploaded way earlier?
     
  5. KILLCIDE

    KILLCIDE

    Administrator

    Joined:
    Jul 22, 2015
    Messages:
    3,473
    Resources:
    20
    Models:
    2
    Icons:
    10
    Spells:
    7
    Tutorials:
    1
    Resources:
    20
    A fairly simple system in terms of concept, but the idea and execution are pretty clever. The ability to define a range for every sight instance is a huge plus for me. The easy to use API, extremely clean code by none other than Flux, and "one" ability would make this a highly recommended for me. My only suggestion for users is to make sure to use Flux's DummyRecycler, despite it being optional. The system works by creating a dummy unit per every unit enumerated by
    GroupEnumUnitsInRange()
    , so you will definitely want the improved efficiency.

    Well done, Flux. I always get excited to see a new system by you! Approved
     
  6. Quilnez

    Quilnez

    Joined:
    Oct 12, 2011
    Messages:
    3,169
    Resources:
    37
    Icons:
    2
    Tools:
    1
    Maps:
    7
    Spells:
    21
    Tutorials:
    2
    JASS:
    4
    Resources:
    37
    Use Wand of Shadowsight ability instead (item). It gives true sight upon a specific unit.
    And this is its order id: 852570
     
  7. Flux

    Flux

    Joined:
    Feb 6, 2014
    Messages:
    2,334
    Resources:
    28
    Maps:
    1
    Spells:
    19
    Tutorials:
    2
    JASS:
    6
    Resources:
    28
    Why would I use that instead of Shade's TrueSight ability?
     
  8. Quilnez

    Quilnez

    Joined:
    Oct 12, 2011
    Messages:
    3,169
    Resources:
    37
    Icons:
    2
    Tools:
    1
    Maps:
    7
    Spells:
    21
    Tutorials:
    2
    JASS:
    4
    Resources:
    37
    Using Wand of Shadowsight doesn't require you to use any dummy unit to follow the revealed unit all the time. You only need one dummy unit to cast the ability on revealed unit once. And it does not reveal nearby units too (does not have AoE).

    But the downside is, it applies buff iirc.
     
  9. Flux

    Flux

    Joined:
    Feb 6, 2014
    Messages:
    2,334
    Resources:
    28
    Maps:
    1
    Spells:
    19
    Tutorials:
    2
    JASS:
    6
    Resources:
    28
    But that would give a small vision radius around the target. It's like Faerie Fire without the armor reduction part. And as you mentioned, it applied a buff.

    Thanks for the suggesting though.
     
  10. Quilnez

    Quilnez

    Joined:
    Oct 12, 2011
    Messages:
    3,169
    Resources:
    37
    Icons:
    2
    Tools:
    1
    Maps:
    7
    Spells:
    21
    Tutorials:
    2
    JASS:
    4
    Resources:
    37
    As far as I noticed, no it does not give vision. But yeah, applying buff is not really good.

    Just out of curiosity. What's the minimum radius of the system? Wouldn't it be 100? Does that mean any invisible units within 100 units from the revealed unit will get revealed too? Or do you have work around for it?
     
  11. Flux

    Flux

    Joined:
    Feb 6, 2014
    Messages:
    2,334
    Resources:
    28
    Maps:
    1
    Spells:
    19
    Tutorials:
    2
    JASS:
    6
    Resources:
    28
    You're right and no I don't have a work around for it. I first tried value of "1" but appears there's minimum true sight radius because even if the distance of the dummy to the target is >1, it is still revealed so I just put a "100" there. Do you think the extra "100" radius is significant enough or is it negligible?