1. Fill your cup and take your pick among the maps best suited for this year's Hive Cup. The 6th Melee Mapping Contest Poll is up!
    Dismiss Notice
  2. Shoot to thrill, play to kill. Sate your hunger with the 33rd Modeling Contest!
    Dismiss Notice
  3. Do you hear boss music? It's the 17th Mini Mapping Contest!
    Dismiss Notice
  4. 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.

[vJASS] [System] Illusion

Discussion in 'JASS Resources' started by Flux, Mar 2, 2016.

  1. Flux

    Flux

    Joined:
    Feb 6, 2014
    Messages:
    2,333
    Resources:
    28
    Maps:
    1
    Spells:
    19
    Tutorials:
    2
    JASS:
    6
    Resources:
    28
    Code (vJASS):

    library Illusion /*

                             Illusion v1.33
                                by Flux
             
                Allows easy creation of Illusion with any damage factor.
     
        */
    requires DamageEvent, DamageModify/*
        http://www.hiveworkshop.com/threads/damagepackage.287101/
        Required to manipulate damage given and damage taken by illusions.
     
     
        */
    optional Table /*
        If not found, the system will create a hashtable. You cannot create more than 256 hashtable
        per map.

     
        ********************************************************************************
        *************************************** API ************************************
        ********************************************************************************
     
        Illusion.create(player, unitSource, x, y)
            - Create an Illusion based on <unitSource>, owned by <player>, positioned at (<x>, <y>)
         
        this.duration = <timedLife>
            - Add a timer to an illusion.
            - Cannot be overwritten once set.
         
        Illusion.get(unit)
            - Return the 'Illusion instance' based on <unit> parameter.
           
        this.unit
            - Refers to the actual illusion unit
         
        this.damageGiven
            - Determines damage dealt factor.
         
        this.damageTaken
            - Determines damage received factor.
     
     
        CREDITS:
            Bribe         - Table
            Flux          - DamageEvent and DamageModify
         
    */

        //===================================================================
        //========================= CONFIGURATION ===========================
        //===================================================================
        globals
            //Rawcode of Illusion Ability based on "Item Illusions"
            private constant integer ILLUSION_SPELL = 'AILS'
         
            private constant integer DUMMY_ID = 'dumi'
         
            private constant integer REFRESH_COUNT = 30
            //Dummy unit owner
            private constant player DUMMY_OWNER = Player(PLAYER_NEUTRAL_PASSIVE)
        endglobals
        //===================================================================
        //======================= END CONFIGURATION =========================
        //===================================================================
     
        native UnitAlive takes unit u returns boolean
     
        struct Illusion
         
            readonly unit unit
            public real damageTaken
            public real damageGiven
         
            static if LIBRARY_Table then
                private static Table tb
            else
                private static hashtable hash = InitHashtable()
            endif
         
            private static trigger deathTrg = CreateTrigger()
            private static group g = CreateGroup()
            private static timer t = CreateTimer()
            private static integer count = 0
            private static unit dummy
            private static unit illu
         
            static method get takes unit u returns thistype
                static if LIBRARY_Table then
                    return thistype.tb[GetHandleId(u)]
                else
                    return LoadInteger(thistype.hash, GetHandleId(u), 0)
                endif
            endmethod
         
            private static method reAdd takes nothing returns nothing
                call TriggerRegisterUnitEvent(thistype.deathTrg, GetEnumUnit(), EVENT_UNIT_DEATH)
            endmethod
         
            private static method onDeath takes nothing returns boolean
                call thistype(thistype.get(GetTriggerUnit())).destroy()
                return false
            endmethod
         
            method destroy takes nothing returns nothing
                if UnitAlive(this.unit) then
                    call KillUnit(this.unit)
                endif
                static if LIBRARY_Table then
                    call thistype.tb.remove(GetHandleId(this.unit))
                else
                    call RemoveSavedInteger(thistype.hash, GetHandleId(this.unit), 0)
                endif
                call GroupRemoveUnit(thistype.g, this.unit)
                //Death trigger refresh
                set thistype.count = thistype.count + 1
                if thistype.count >= REFRESH_COUNT then
                    call DestroyTrigger(thistype.deathTrg)
                    set thistype.deathTrg = CreateTrigger()
                    call TriggerAddCondition(thistype.deathTrg, Condition(function thistype.onDeath))
                    call ForGroup(thistype.g, function thistype.reAdd)
                    set thistype.count = 0
                endif
                set this.unit = null
                call this.deallocate()
            endmethod
         
         
            private static method onDamage takes nothing returns nothing
                //If source is illusion
                if IsUnitInGroup(Damage.source, thistype.g) then
                    set Damage.amount = Damage.amount*thistype.get(Damage.source).damageGiven
                endif
                //If target is illusion
                if IsUnitInGroup(Damage.target, thistype.g) then
                    set Damage.amount = Damage.amount*thistype.get(Damage.target).damageTaken
                endif
            endmethod
         
            private static method entered takes nothing returns boolean
                set thistype.illu = GetSummonedUnit()
                return false
            endmethod
         
            method operator duration= takes real time returns nothing
                call UnitApplyTimedLife(this.unit, 'BTLF', time)
            endmethod
         
            static method create takes player owner, unit source, real x, real y returns thistype
                local thistype this
                set thistype.illu = null
                //Create the Illusion Unit
                if source != null and UnitAlive(source) then
                    call SetUnitX(thistype.dummy, GetUnitX(source))
                    call SetUnitY(thistype.dummy, GetUnitY(source))
                    call SetUnitOwner(thistype.dummy, GetOwningPlayer(source), false)
                    if IssueTargetOrderById(thistype.dummy, 852274, source) then
                        if thistype.illu != null then
                            call SetUnitOwner(thistype.illu, owner, true)
                            if IsUnitType(source, UNIT_TYPE_STRUCTURE) then
                                call SetUnitPosition(thistype.illu, x, y)
                            else
                                call SetUnitX(thistype.illu, x)
                                call SetUnitY(thistype.illu, y)
                            endif
                        else
                            debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 3600, "[Illusion] No illusion created")
                            call SetUnitOwner(thistype.dummy, DUMMY_OWNER, false)
                            return 0
                        endif
                    else
                        debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 3600, "[Illusion] Issued illusion create order failed")
                        call SetUnitOwner(thistype.dummy, DUMMY_OWNER, false)
                        return 0
                    endif
                    call SetUnitOwner(thistype.dummy, DUMMY_OWNER, false)
                else
                    debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 3600, "[Illusion] Source unit dead or non-existing")
                    return 0
                endif
                //Initialize struct
                set this = thistype.allocate()
                set this.unit = thistype.illu
                set this.damageTaken = 1.0
                set this.damageGiven = 1.0
                call SetUnitAnimation(thistype.illu, "stand")
                call GroupAddUnit(thistype.g, this.unit)
                call TriggerRegisterUnitEvent(thistype.deathTrg, this.unit, EVENT_UNIT_DEATH)
                static if LIBRARY_Table then
                    set thistype.tb[GetHandleId(this.unit)] = this
                else
                    call SaveInteger(thistype.hash, GetHandleId(this.unit), 0, this)
                endif
                set thistype.illu = null
                return this
            endmethod
         
            private static method onInit takes nothing returns nothing
                local trigger t = CreateTrigger()
                set thistype.dummy = CreateUnit(DUMMY_OWNER, DUMMY_ID, 0, 0, 0)
                call TriggerRegisterUnitEvent(t, thistype.dummy, EVENT_UNIT_SUMMON)
                call TriggerAddCondition(t, Condition(function thistype.entered))
                call TriggerAddCondition(thistype.deathTrg, Condition(function thistype.onDeath))
                call UnitAddAbility(thistype.dummy, ILLUSION_SPELL)
                static if LIBRARY_Table then
                    set thistype.tb = Table.create()
                endif
                call Damage.registerModifier(function thistype.onDamage)
            endmethod
         
        endstruct
     
    endlibrary
     


    Example of using the system:
    Show

    Code (vJASS):

    scope Test1 initializer Init
     
     
        private function BeginTest takes nothing returns nothing
            local Illusion illu1 = Illusion.create(Player(0), tinker, 0, 0)
            local Illusion illu2 = Illusion.create(Player(0), firelord, 0, 0)
            local Illusion illu3 = Illusion.create(Player(0), tank, 0, 0)
            local Illusion illu4 = Illusion.create(Player(0), flying, 0, 0)
         
            //Set Duration
            set illu1.duration = 30
            set illu2.duration = 30
            set illu3.duration = 30
            set illu4.duration = 30
         
            //Set Damage Manipulation of each illusion
            set illu1.damageTaken = 1   //Receives the same damage
            set illu1.damageGiven = 10  //Deals 10 times the original damage
         
            set illu2.damageTaken = 3   //Receives thrice damage
            set illu2.damageGiven = 1.5 //Deals 1.5 times the original damage
         
            set illu3.damageTaken = 0   //Will not receive any damage
            set illu3.damageGiven = 0   //Will not deal any damage
         
            set illu4.damageTaken = 0   //Will not receive any damage
            set illu4.damageGiven = 2.0 //Deals twice the original damage
         
            //Order the illusions to move to the corners of the map
            call IssuePointOrder(illu1.unit, "move", 3000, 3000)
            call IssuePointOrder(illu2.unit, "move", -3000, -3000)
            call IssuePointOrder(illu3.unit, "move", -3000, 3000)
            call IssuePointOrder(illu4.unit, "move", 3000, -3000)
        endfunction
     
     

        private function Init takes nothing returns nothing
            local trigger t = CreateTrigger()
            local string s = "1"
            call TriggerRegisterPlayerChatEvent(t, Player(0), s, true)
            call TriggerAddAction(t, function BeginTest)
            call BJDebugMsg("Type '" + s + "' to create 4 Illusions owned by Player(0) at (0,0)")
        endfunction

    endscope
     



    Changelog:
    v1.00 - [2 March 2016]
    - Initial Release

    v1.10 - [15 March 2016]
    - Optimized the code.
    - Made the Debug Messages more specific.
    - Added RegisterPlayerUnitEvent as an optional requirement.
    - Improved documentation.

    v1.20 - [10 June 2016]
    - Revised most of the code.
    - Only one object editor ability based on Item Illusion is needed.
    - Damage dealt, damage received and duration can now be manipulated dynamically.

    v1.30 - [2 November 2016]
    - Fixed bug involving damage source and damage target are both illusion.
    - Now requires DamageEvent and DamageModify.
    - Renamed "static method instance" to "static method get".
    - Optimized refreshing system.
    - Removed duration parameter in "static method create"
    - Added "method operator duration="

    v1.31 - [2 November 2016]
    - Fixed potential leak with refresh system.
    - Improved configuration.

    v1.32 - [4 February 2017]
    - Replaced periodic trigger cleanup with unused event counter cleanup.
    - Added debug messages.
    - Illusions created now automatically plays "stand" animation.
    - Shortened onDamage function.

    v1.33 - [11 February 2017]
    - Reorder a function placement to avoid trigger evaluation.
    - Removed unnecessary function call.
    - Dummy changes ownership back to NEUTRAL upon failing creation of Illusion.
    - Improved debug messages.
     

    Attached Files:

    Last edited: Feb 11, 2017
  2. WereElf

    WereElf

    Joined:
    Jan 2, 2016
    Messages:
    962
    Resources:
    1
    Spells:
    1
    Resources:
    1
    Not bad. It's clean and simple :)
    Just add some describtion in this thread :D
     
  3. Bannar

    Bannar

    Joined:
    Mar 19, 2008
    Messages:
    3,099
    Resources:
    20
    Spells:
    5
    Tutorials:
    1
    JASS:
    14
    Resources:
    20
    Division of doc into multiline comment & novjass block kills its readability.
    6 lines of text seem too much for just one function which is rather self-explanatory.

    I'm against having NEUTRAL & ILLUSION_ORDER globals. You won't be configuring those, no matter what.

    Code (vJASS):
    if source != null and UnitAlive(source) then
    ->>
    if UnitAlive(source) then

    Snippet - Illusion.

    Add some newlines withing that function, it's a block of text right now.
    IsUnitIllusion()
    should be replaced with
    dummy == GetSummoningUnit()
    check for more safety. The nulling of 'illu' prior the process is uneccessary anyway, it should be added after the action instead (if ever).

    The debugging messages seem to be wrong e.g.:
    debug call BJDebugMsg("|cffffcc00[Illusion]|r: Invalid integer abilityId!")
    should never happen if
    IssueTargetOrderById(dummy, ILLUSION_ORDER, source)
    evaluates to true. Even if it does, the message itself is not valid.

    RegisterPlayerUnitEvent anyone?
     
  4. Flux

    Flux

    Joined:
    Feb 6, 2014
    Messages:
    2,333
    Resources:
    28
    Maps:
    1
    Spells:
    19
    Tutorials:
    2
    JASS:
    6
    Resources:
    28
    Readability seems fine in my opinion. Plus I need to explain the abilityId so I went and explain the rest of the input arguments as well. What's wrong with providing sufficient documentation?

    Yeah, I have a feeling that ILLUSION_ORDER does not need a constant variable. I just did it for readability, Ill remove it then. I'll put NEUTRAL in the non-configuration globals block.

    Right, I don't know why I add the source != null in the first place.

    I admit, I don't know such a similar resource exist. But I find some flaws in that resource.
    1. You can't create illusions based on enemies, even if Targets Allowed have 'Enemies' ticked. Mine does without needing to Modify Targets Allowed to include 'Enemies'
    2. It lacks debug messages. If something is not working, you won't know what part is not working.
    3. It lacks safety features, Example, if you wanted to create an illusion based on a Structure but you forgot to include it in Targets Allowed, then an illusion won't be created and LastCreatedIllusion will still point to the previous illusion.
    So if I do something like
    Code (vJASS):

    local unit illu1 = UnitCreateIllusion(tinker, Player(0), 'A000', 1)
    local unit illu2 = UnitCreateIllusion(tower, Player(0), 'A000', 1)
    call IssuePointOrder(illu1, "move", 3000, 3000)
    call IssuePointOrder(illu2, "move", -3000, -3000)
     

    Tinker illusion will actually move to (-3000, -3000) if the user forgot to include 'Structures' in Targets Allowed.

    But there are white spaces between functions. I don't know where are you referring to.

    Good suggestion, that means I don't have to Enable-Disable the trigger.

    That
    set illu=null
    is actually critical because it is a global variable so if an incorrect abilityId (e.g. ability not based on Item Illusions) is used, an illusion won't be created and
    unit illu
    will still point to the last created illusion thus it will return the previous illusion created.


    Yes it can, example, your abilityId is based on Storm bolt, IssueTargetOrderById will return true but no illusion will be created so it will result to error message "Invalid integer abilityId!".

    I try to avoid requirements as much as possible. But maybe I'll add them as optional requirements.
     
    Last edited: Mar 3, 2016
  5. Bannar

    Bannar

    Joined:
    Mar 19, 2008
    Messages:
    3,099
    Resources:
    20
    Spells:
    5
    Tutorials:
    1
    JASS:
    14
    Resources:
    20
    Sure, but if you read carefully - you would see that "illu" reset should be after the action, not prior. If nothing is found, dont null.
    Tho the global declaration should change in such case:
    private unit illu = null
    .

    Just remind yourself about custom event calling:
    Code (vJASS):
    // granted we have trigger handle already prepared and iniliatized

    // private real caller = 0
    // body of invoker:
    (...)
    set caller = MY_EVENT_CONSTANT
    set caller = 0
    (...)
    Additional
    set caller = 0
    at the top would be completely unnecessary.

    It's a very minor thing, dont really need to change that. Just stating some facts.
     
  6. Flux

    Flux

    Joined:
    Feb 6, 2014
    Messages:
    2,333
    Resources:
    28
    Maps:
    1
    Spells:
    19
    Tutorials:
    2
    JASS:
    6
    Resources:
    28
    I can't null it after because I need to return it.
    Code (vJASS):

    ...
    return illu
    set illu = null   //won't happen
     
     
  7. Almia

    Almia

    Joined:
    Apr 24, 2012
    Messages:
    4,842
    Resources:
    35
    Spells:
    30
    Tutorials:
    4
    JASS:
    1
    Resources:
    35
    Wait what, Dirac made this resource long time ago? I should be visiting TheHelper more often.

    Tho in my opinion, I think this is a bit better than Dirac's, but it lacks the RegisterPlayerUnitEvent lib (which is a go) and the fact that Dirac's resource forces the users to use the Wand Ability rather than the system itself must do it.

    Also, I think GetSummonedUnit() is much better or you could not just check if it is an illusion because as long as the function executes, the event is executed as well. I don't think that another summon ability will affect the method once it is cast at the same time the function is executed(that is a very rare case)
     
  8. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    8,346
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    Your CreateIllusion function can be simplified:

    Code (vJASS):

        function CreateIllusion takes player owner, unit source, real x, real y returns unit    
            set illu = null //makes it so that the illusion will always be null or be the required unit.
            if source != null and UnitAlive(source) then
                call SetUnitX(dummy, GetUnitX(source))
                call SetUnitY(dummy, GetUnitY(source))
                call SetUnitOwner(dummy, GetOwningPlayer(source), false)
                call UnitAddAbility(dummy, ABIL_ID) //ABIL_ID should be a constant, set by the library, instead of per-call.
                call EnableTrigger(findIllusion)
                if not IssueTargetOrderById(dummy, ILLUSION_ORDER, source) then
                    debug call BJDebugMsg("|cffffcc00[Illusion]|r: Unable to target unit source!")
                endif
                call DisableTrigger(findIllusion)
                call UnitRemoveAbility(dummy, ABIL_ID)
                call SetUnitOwner(dummy, NEUTRAL, false)
                if illu != null then
                    call SetUnitOwner(illu, owner, true) //Moved to within this if/else block.
                    if IsUnitType(source, UNIT_TYPE_STRUCTURE) then
                        call SetUnitPosition(illu, x, y)
                    else
                        call SetUnitX(illu, x)
                        call SetUnitY(illu, y)
                    endif
                debug else
                    debug call BJDebugMsg("|cffffcc00[Illusion]|r: Invalid integer ABIL_ID!")
                endif
            debug else
                debug call BJDebugMsg("|cffffcc00[Illusion]|r: Invalid unit source!")
            endif
            return illu //only one return statement is needed.
        endfunction

       


    Also, if you will not use RegisterPlayerUnitEvent, at least use TriggerRegisterVariableEventBJ. That is the most useless function in the world to inline as saving map file size is more relevant than saving a function call during map init.
     
  9. Flux

    Flux

    Joined:
    Feb 6, 2014
    Messages:
    2,333
    Resources:
    28
    Maps:
    1
    Spells:
    19
    Tutorials:
    2
    JASS:
    6
    Resources:
    28
    ^Noted, thanks.

    No, it is not a universal illusion creator that handles the damage dealt and damage taken part via DDS. I think it's much simpler to create abilities based on Item Illusion and the input argument rawcode in CreateIllusion will determine the damage dealt, damage taken and duration of the illusion.


    Actually I was using TriggerRegisterAnyUnitEventBJ at first but I decided to register another event (To make it only 1 Item Illusion ability is needed) so I end up using 2 TriggerRegisterAnyUnitEventBJ that's why I inlined it. Then after some time, I removed the other event (since it's mostly a dead end path anyway) and decided to not change back what I inlined. Anyway, yeah I will use TriggerRegisterAnyUnitEventBJ in something like this form in the next update.
    Code (vJASS):

    static if LIBRARY_RegisterPlayerUnitEvent
        call RegisterPlayerUnitEvent(...)
    else
        call TriggerRegisterAnyUnitEventBJ(...)
    endif
     
     
  10. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    8,346
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    OK, I've ran some tests and, in the environment you provided, everything seems to work correctly.

    To improve the demo map, I suggest making the on-screen text either last longer or only get displayed after a 0 second wait/timer so it appears in the message log.

    The description doesn't cover the installation steps. The user has to create the ability or copy it from your map, and that's not documented anywhere.

    With those changes, this resource looks acceptable.
     
  11. Flux

    Flux

    Joined:
    Feb 6, 2014
    Messages:
    2,333
    Resources:
    28
    Maps:
    1
    Spells:
    19
    Tutorials:
    2
    JASS:
    6
    Resources:
    28
    Updated!

    I would have did this, but TriggerSleepAction doesn't work with it so I leave it as is. Using a 0 second timer is just too much work.

    Done
     
  12. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    8,346
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    If you change Test1 from a scope to a library, you can use TriggerSleepAction without any issues.

    Approv├ęd
     
  13. Almia

    Almia

    Joined:
    Apr 24, 2012
    Messages:
    4,842
    Resources:
    35
    Spells:
    30
    Tutorials:
    4
    JASS:
    1
    Resources:
    35
    Would have been better if this supports damage modification for illusions :D
     
  14. Flux

    Flux

    Joined:
    Feb 6, 2014
    Messages:
    2,333
    Resources:
    28
    Maps:
    1
    Spells:
    19
    Tutorials:
    2
    JASS:
    6
    Resources:
    28
    You mean only 1 Item Illusion ability used and it is up to the DDS to modify the damages? That was what I'm doing at first but I've ran into problems which I currently can't remember at the moment.
     
  15. Almia

    Almia

    Joined:
    Apr 24, 2012
    Messages:
    4,842
    Resources:
    35
    Spells:
    30
    Tutorials:
    4
    JASS:
    1
    Resources:
    35
    I'm going to make a lib for it then :D
     
  16. Flux

    Flux

    Joined:
    Feb 6, 2014
    Messages:
    2,333
    Resources:
    28
    Maps:
    1
    Spells:
    19
    Tutorials:
    2
    JASS:
    6
    Resources:
    28
    Though I already made one but didn't bother to upload it because I did not know what DDS to use. But in this example I used PDDS.
    Code (vJASS):

    library IllusionSystem /*
       
                        IllusionSystem v1.00
                            by Flux
           
            This system allows you to create an Illusion with dynamic damage dealt and damage
            taken.
           
        -----------------------------------
        API:
        -----------------------------------
          struct Illusion
               
            - real damageTaken
            - real damageDealt
               
            - static method create takes player owner, unit source, real x, real y, real duration returns thistype
        -----------------------------------
       
        */
    requires /*
        */
    Illusion /*       [url]http://www.hiveworkshop.com/forums/jass-resources-412/snippet-illusion-276304/[/url]
        */
    DamageEvent /*    [url]http://www.hiveworkshop.com/forums/jass-resources-412/system-physical-damage-detection-228456/[/url]
       
        */
    optional Table /* [url]http://www.hiveworkshop.com/forums/jass-resources-412/snippet-new-table-188084/[/url]
       
        Credits:
            looking_for_help - DamageEvent
            Bribe            - Table
       
        */

       
        //====================== CONFIGURATION ====================
        globals
            //Ability based on Item Illusion that has Damage Dealt = 1, Damage Received = 1, Duration = 0
            private constant integer ILLUSION_ABILITY = 'Ailu'
        endglobals
        //=================== END CONFIGURATION ===================
       
       
        struct Illusion
           
            readonly unit unit
            real damageDealt
            real damageTaken
           
            private static group g = CreateGroup()
           
            static if LIBRARY_Table then
                private static Table tb
            else
                private hashtable hash = InitHashtable()
            endif
           
            method destroy takes nothing returns nothing
                static if LIBRARY_Table then
                    call tb.remove(GetHandleId(.unit))
                else
                    call FlushChildHashtable(hash, GetHandleId(.unit))
                endif
                call GroupRemoveUnit(g, .unit)
                call RemoveUnit(.unit)
                set .unit = null
                call .deallocate()
            endmethod
           
            private static method onDamage takes nothing returns nothing
                local thistype this
                if IsUnitInGroup(PDDS.source, g) then
                    static if LIBRARY_Table then
                        set this = tb[GetHandleId(PDDS.source)]
                    else
                        set this = LoadInteger(hash, GetHandleId(PDDS.source), 0)
                    endif
                    set PDDS.amount = .damageDealt*PDDS.amount
                elseif IsUnitInGroup(PDDS.target, g) then
                    static if LIBRARY_Table then
                        set this = tb[GetHandleId(PDDS.target)]
                    else
                        set this = LoadInteger(hash, GetHandleId(PDDS.target), 0)
                    endif
                    set PDDS.amount = .damageTaken*PDDS.amount
                endif
            endmethod
           
            private static method onDeath takes nothing returns nothing
                static if LIBRARY_Table then
                    local thistype this = tb[GetHandleId(GetTriggerUnit())]
                else
                    local thistype this = LoadInteger(hash, GetHandleId(GetTriggerUnit()), 0)
                endif
                if this > 0 then
                    call .destroy()
                endif
            endmethod
           
            static method create takes player owner, unit source, real x, real y, real duration returns thistype
                local thistype this = .allocate()
                set .unit = CreateIllusion(owner, source, ILLUSION_ABILITY, x, y)
                //Initialized both damage modifiers to 1
                set .damageDealt = 1
                set .damageTaken = 1
                call GroupAddUnit(g, .unit)
                if duration > 0 then
                    call UnitApplyTimedLife(.unit, 'BTLF', duration)
                endif
                static if LIBRARY_Table then
                    set tb[GetHandleId(.unit)] = this
                else
                    call SaveInteger(hash, GetHandleId(.unit), 0, this)
                endif
                return this
            endmethod
           
            private static method onInit takes nothing returns nothing
                static if LIBRARY_RegisterPlayerUnitEvent then
                    call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_DEATH, function thistype.onDeath)
                else
                    local trigger t = CreateTrigger()
                    call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_DEATH)
                    call TriggerAddCondition(t, Condition(function thistype.onDeath))
                endif
                static if LIBRARY_Table then
                    set tb = Table.create()
                endif
                call AddDamageHandler(function thistype.onDamage)
            endmethod
           
        endstruct
       
    endlibrary

     


    Example of using it:
    Code (vJASS):

    scope Test5 initializer Init
       
       
        private function BeginTest takes nothing returns nothing
            local Illusion illu = Illusion.create(Player(0), tinker, 0, 0, 15)
            set illu.damageDealt = 0.5
            set illu.damageTaken = 2.0
        endfunction
       
       

        private function Init takes nothing returns nothing
            local trigger t = CreateTrigger()
            local string s = "5"
            call TriggerRegisterPlayerChatEvent(t, Player(0), s, true)
            call TriggerAddAction(t, function BeginTest)
            call BJDebugMsg("Type '" + s + "' to create a Tinker Illusion with that deals 50% damage and takes 200% damage")
        endfunction

    endscope
     
     

    Attached Files:

  17. Flux

    Flux

    Joined:
    Feb 6, 2014
    Messages:
    2,333
    Resources:
    28
    Maps:
    1
    Spells:
    19
    Tutorials:
    2
    JASS:
    6
    Resources:
    28
    Bump.

    Made a huge update with internal DDS in case users initially have no DDS. I need feedback of someone expert in DDS (Bribe, looking_for_help, anyone) if I implemented damage manipulation properly.
     
  18. Iph

    Iph

    Joined:
    Jul 31, 2016
    Messages:
    39
    Resources:
    0
    Resources:
    0
    I hope you can implement a way to check the number of illusions count per unit (the ones conjured by a unit) and a way to limit illusions per unit by destroying the one with the least expire time. By the way, is it even possible to detect expire time?
     
  19. Flux

    Flux

    Joined:
    Feb 6, 2014
    Messages:
    2,333
    Resources:
    28
    Maps:
    1
    Spells:
    19
    Tutorials:
    2
    JASS:
    6
    Resources:
    28
    The user can do that if he would build a wrapper function out of this one. Example (using Bribe's Table):
    Code (vJASS):

    function CustomFuncIllusion takes unit ....
        local unit id = GetHandleId(unit)
        if counter[id] < limit then
            call Illusion.create(....)
            set counter[id] = counter[id] + 1
        endif
    endfunction
     


    Add a queued linked list to the wrapper function so that the first added is easily accessible upon reaching the limit. However, that only works for same timers for all Illusions.

    It's possible, though I haven't created an API for it. The method would be is to add a timer member to the struct and detecting the time left by using TimerGetRemaining.
     
  20. Iph

    Iph

    Joined:
    Jul 31, 2016
    Messages:
    39
    Resources:
    0
    Resources:
    0
    I'm not versed in Jass but I fiddled with it anyway and could create two variables: the first to check whether the created image is an illusion, but the originator won't have this variable; the second is for the number of illusions which has the shortcoming of refering to the number of illusions of this image-source rather than the very originator, so ,for instance, if an illusion creates an illusion, the first will have 2 illusion count, the second will also have 2 illusion count (assigned from its duplicator,) whereas the originator will have 1 illusion count (could't assign the second illusion's illusion count to it.)

    I also tried to count units in group g but its private and I'm not sure whether each originator has its own unique g group or the group is used by all originators in the system.