• 🏆 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!

Illusion System - v1.00e

This bundle is marked as useful / simple. Simplicity is bliss, low effort and/or may contain minor bugs.
This system is used to create illusions of target units via one line of code.
There are several parameters with which you can change the illusions values.

I created this system because I thought it was useful and I saw no other similar system on the whole hive workshop. =)
Besides that I needed an illusion system for my map 'Guilds of Hyppos' to make it easier to code spells with illusions. ;-)

Everything could be done with Gui, too but I like vJass much more so I am sorry if you won't use this system because of vJass ...

To install this system on your map you need the newest JNGP with the newest JassHelper version.

Hopefully, you will find this useful

- and sorry for my bad english. ;-)

Requirements:
List Module
Jass New Gen Pack




- improved coding.
- added hints to the two API functions.
- improved the level calculation.



- improved coding.
- standalized the code.
- added an API.



- optimized the code slightly.



- the debug message is now only activated in debug mode.
- the initial dummy unit now gets removed.
- changed 'GetSummonedUnit()' to 'GetTriggerUnit()'.



initial release!




JASS:
library IllusionSystem requires ListModule

/****************************************************/****************************************************************************************
System coded by Robbepop.
Please give credits if you use it in your map! ;-)

Requires:
- List Module
- One object data ability. (just copy and paste)
- One global dummy unit. (... if you don't already have one)
- vJass knowledge. (The system's coding is possible with Gui, too.)

This system allows you to create variable illusions with just one object of an ability.
See in the 'test' trigger how to do so.

Via the global variable 'lastCreatedIllusion' you have access to the illsion you have
lastly created with this system and are able to perform several actions.
This is also shown in the 'test' trigger.

There are some variable factors for illusions.
However, due to the wc3 mechanics not too many ...

You can scale the duration of an illusion,
the damage factor and the hit factor.

There are 3 possible values for the damage factor, 1.0, 2.0 or 3.0 to decide if your
illusion shell takes damage as normal (1.0) or with a factor of 2.0 or 3.0.

Possible values for the hit factor are 0, 0.1, 0.2, 0.3 ... 0.9 and 1.0.
This is the value which decides how much attack damage your illusion will deal.

To create illusion without a timed life just use a negative duration as parameter e.g. -1.

Im sorry that there are not more options and possibilities but I hope this system allows 
illusions which are variable enough to make this system useful for you. =)
*******************************************************************************************/

globals
    private constant integer DUMMY_ID = 'n000' //Configure this to your dummy's id.
    private constant integer ILLU_ABIL = 'A000' //Change this value to your copy's id.

    unit lastCreatedIllusion
endglobals

struct Illusion
    implement List

    private unit Dummy
    private real IlluDuration

    private method destroy takes nothing returns nothing
        //Var Clearing
        call KillUnit(.Dummy)
        set .Dummy = null

        //List Modul
        call .listRemove()

        //Deallocate
        call .deallocate()
    endmethod

    static method create takes unit target, real dmgfactor, real hitfactor, real dur returns thistype
        //Var Init
        local thistype this = 0

        //Check for possible values:
        if dmgfactor >= 1. and dmgfactor <= 3. and hitfactor >= 0. and hitfactor <= 1. then
            //Var Setting
            set this = thistype.allocate()

            //List Modul
            call .listAdd()

            //Var Setting
            set .Dummy = CreateUnit(GetOwningPlayer(target), DUMMY_ID, GetUnitX(target), GetUnitY(target), 0)
            set .IlluDuration = dur

            //Create Illusion
            call UnitAddAbility(.Dummy, ILLU_ABIL)
            call SetUnitAbilityLevel(.Dummy, ILLU_ABIL, R2I((dmgfactor * 11) + (hitfactor * 10) - 10))
            call IssueTargetOrderById(.Dummy, 852274, target)

        //Error: forbidden values:
        else
            debug call BJDebugMsg("|cffff0000Error:|r Used forbidden values to create an illusion!")
        endif

        //Return
        return this
    endmethod

    private static method GetDummyId takes unit check returns thistype
        local thistype this = thistype.first
        loop
            exitwhen this == 0
            if check == .Dummy then
                return this
            endif
            set this = this.next
        endloop
        return 0
    endmethod

    private static method main takes nothing returns nothing
        //Var Init
        local unit dummy = GetSummoningUnit()
        local thistype this = GetDummyId(dummy)

        //Check for true illusion:
        if this > 0 then

            //Var Setting
            set lastCreatedIllusion = GetTriggerUnit()

            //Set Illu Duration
            if .IlluDuration >= 0 then
                call UnitApplyTimedLife(lastCreatedIllusion, 'Blil', .IlluDuration)
            endif

            //Remove Dummy
            call this.destroy()

        endif

        //Clear
        set dummy = null
    endmethod

    private static method onSummoning takes nothing returns boolean
        if IsUnitIllusion(GetTriggerUnit()) then
            call thistype.main()
        endif
        return false
    endmethod

    private static method onInit takes nothing returns nothing
        //Var Init
        local trigger t

        //Trigger Init
        set t = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SUMMON)
        call TriggerAddCondition(t, Condition(function thistype.onSummoning))

        //Ability Init
        set bj_lastCreatedUnit = CreateUnit(Player(12), DUMMY_ID, 0, 0, 0)
        call UnitAddAbility(bj_lastCreatedUnit, ILLU_ABIL)
        call RemoveUnit(bj_lastCreatedUnit)

        //Clear
        set t = null
    endmethod

endstruct

/****************************************************/****************************************************************************************
You can use these two functions to create an illusion with just one line of code in
vJass and to get the last created illusion with this system. Note: illusions created
from other sources won't get saved as a 'lastCreatedIllusion'!
****************************************************************************************/
function GetLastCreatedIllusion takes nothing returns unit
    return lastCreatedIllusion
endfunction

function CreateIllusion takes unit target, real d, real h, real dur returns Illusion
    local Illusion 

[b]Keywords:[/b]
illusion, vJass, create, unit, copy, paste, illu, system
Contents

Noch eine WARCRAFT-III-Karte (Map)

Reviews
12th Dec 2015 IcemanBo: Too long as NeedsFix. Rejected. 24th July 2012 Magtheridon96: Your CreateIllusion function can simply return Illusion.create(target, d, h, dur). You can totally forget about using a linked list here :p You only...

Moderator

M

Moderator

12th Dec 2015
IcemanBo: Too long as NeedsFix. Rejected.

24th July 2012
Magtheridon96:

  • Your CreateIllusion function can simply return Illusion.create(target, d, h, dur).
  • You can totally forget about using a linked list here :p
    You only need a global variable that gets set to the current instance
    because the function that executes upon summoning the illusion runs directly after the IssueTargetOrder call in the create function.
    Just store 'this' into a global variable and use that global variable inside the 'main' function as if it were this and it would work.
    But, this could bug in rare cases. The safer solution is to use a Table.
    Store 'this' while using the handle id of this.Dummy as the index.
    You would load it in the main function.
  • Struct members should be camel-cased. Refer to this tutorial by Bribe to learn more about JASS convention ^.^

9th Nov 2011
Bribe: Who wrote this ListModule? It fails pretty hard compared to some of our other list modules.

I can't approve this while it requires such a weird resource. You could maybe inline the ListModule yourself and remove the requirement, but its name can easily clash with other superior LinkedList modules.
 
Level 11
Joined
Mar 6, 2008
Messages
898
okay thank - I will do what has to be done. ;-)

(very nooby from me not to remove the dummy unit :-O)

I will implement the hashbased search later - no time atm.

Robbepop


edit: i really don't know if it will be better for the performance if I chance the search loop to a hashtable key because mostly there are just very few instances of illusion types at the same time ...
 
Last edited:
Level 14
Joined
Nov 18, 2007
Messages
1,084
The system looks like it works well.


  • JASS:
    //Remove this block if you already have got a global dummy unit!
    doesn't really make sense. You should just say that the global should be configured to the dummy id the user uses.
  • You could implement some kind of dummy unit recycling.
    When a struct is first used, a dummy unit would be created like normal. However, when the struct is destroyed, hide Dummy somewhere else instead of killing it. Then when that struct gets used again, the Dummy unit would already be there, only needing to be shown and to have an owner change.
  • The local variable owner is unnecessary in New.
  • onDestroy is kind of bad. Change the name to destroy and put .deallocate() at the end of that method.
  • You could do everything in method main in method condition instead.
  • I would use a local variable instead of gg_trg_Illusion_Sys so the user won't have to worry about naming the trigger to be "Illusion Sys."
  • There's really no point in removing the ability from the dummy unit when you preload the ability. Also, you should just use RemoveUnit instead of KillUnit
I was also thinking that it would be nice if you also allowed the user to use his own custom ability instead of the default ability you provide so that he could use values that are out of the range you specify.
 
Level 11
Joined
Mar 6, 2008
Messages
898
hiho,

thank you for your time. =)

I go through your points and say sth, k? =)

1. ok, I will do it like that.
2. the dummy is the link to the summoned illusion so I need one dummy for every illusion creation which isn't that bad as I instantly clear up the dummy when I summon the illusion shortly after the dummy creation. =)
3. I got teached that player variables do leak. that's why I always use them to clear them after their usage.
4. also learned onDestroy the way you see it - but I will change it if it is better. =)
5. I like to have a condition and a main. If everythink was in the main I would initialize all variables in the main just to check if the summoned unit is an illusion which sounds bad to me.
6. ok, using a local variable is perhaps better in this situation. ;-)
7. I didn't know that it is enough to just add the ability - thanks.^^

Your last points sounds nice to me but it would be too lavish for normal user to use their own illusion spell object.
However, I could extend my ability to create more factors for you. =)

Robbepop
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,468
>> I like to have a condition and a main. If everythink was in the main I would initialize all variables in the main just to check if the summoned unit is an illusion which sounds bad to me.

Then I recommend for you splitting it into two functions, but there is no reason you
should have both conditions and actions assigned to this trigger.
 
Your GetDummyId is search-based, when it should be UnitUserData based or hashtable based.

Hashtable-based is better
UnitUserData would ONLY work if you have the right filters in UnitIndexer or AIDS :p

Few things:

  • Move the onInit method outside the struct and put it in a module that gets implemented
  • You must follow vJass convention ( Struct members are written like this: structMember). Seriously, do it :p
  • Don't use onDestroy, use the destroy method and call .deallocate() at the end
  • The spacing and the comments really decrease readability :(
  • Use the private/read only keywords to encapsulate struct members that no one is supposed to change/access.
  • The reals x and y in your "New" method are useless..

Oh and Bribe, GetSummonedUnit() isnt the same as GetTriggerUnit :p
If you check the event in GUI, it says: "A unit spawns a summoned unit"
So technically, the triggering unit is the spawner :D
 
Level 11
Joined
Mar 6, 2008
Messages
898
hiho,

what is the sense of moving the onInit method out of my struct and into a module to implement it !? 0.o
hmm, ... did you ever code in C#?
i really like that syntax and the "New" instead of the "create" - that's why my code doesn't look like other vJass codes. :-(
do you really have problems reading this code?
hmm, how could I improve the readability in your opinion?

Thanks so far! ;-)

Robbepop
 
Well, when it comes to the convention, I feel your pain :p
C++ convention is like the worst >.<
I've never coded C# though :p

To improve readability, you could start out by making all the members follow the same convention.

Oh and about the 'moving the method into a module and implementing it' thing, it's actually because what you put in an onInit method inside a module is initialized before any onInit method inside a struct ^^

edit

Much better now, but still they could be improved:

Your entire system could be composed of one function and 6 globals ;)

Instead of registering an event to a trigger that checks when a unit is summoned, why dont you do the actions of that trigger in the function "New" :D
 
JASS:
library IllusionSystem
/***************************************************
*    function CreateIllusion:                      *
*        takes:                                    *
*            unit target                           *
*            real unitX                            *
*            real unitY                            *
*            real damageFactor                     *
*            real hitFactor                        *
*            real duration                         *
*        returns:                                  *
*            unit                                  *
***************************************************/
    globals
        // The id of the dummy unit in your map
        private constant integer DUMMY = 'n000'
        // The id of the illusion ability
        private constant integer ILLUSION = 'A000'
        // Last created illusion
        private unit l
    endglobals
    
    function CreateIllusion takes unit u, real x, real y, real d, real h, real t returns unit
        local unit v
        set l = null
        if d >= 1. and d <= 3. and h >= 0. and h <= 1. then
            set v = CreateUnit(GetOwningPlayer(u),DUMMY,x,y,0)
            call UnitAddAbility(v,ILLUSION)
            call SetUnitAbilityLevel(v,ILLUSION,R2I(d*11+h*10-10))
            call IssueTargetOrderById(v,852274,u)
            if t>0 then
                call UnitApplyTimedLife(v,'Blil',t)
            endif
            set l = v
        endif
        set v = null
        return l
    endfunction
    
    function GetLastCreatedIllusion takes nothing returns unit
        return l
    endfunction
    
    private module Init
        private static method onInit takes nothing returns nothing
            local unit u = CreateUnit(Player(15),DUMMY,0,0,0)
            call UnitAddAbility(u,ILLUSION)
            call RemoveUnit(u)
            set u = null
        endmethod
    endmodule
    private struct Inits extends array
        implement Init
    endstruct
endlibrary

This is what I would do :p

edit
Fixed.
 
Level 14
Joined
Nov 18, 2007
Messages
1,084
2. the dummy is the link to the summoned illusion so I need one dummy for every illusion creation which isn't that bad as I instantly clear up the dummy when I summon the illusion shortly after the dummy creation. =)
Hm, I actually meant something different. I was talking about recycling the dummy units.

Something like this:
JASS:
            if .Dummy == null then // This means .Dummy doesn't refer to a dummy unit, so create one.
                set .Dummy = CreateUnit(owner, DUMMY_ID, x, y, 0)
                call UnitAddAbility(.Dummy, ILLU_ABIL)                
            else // Just reuse the dummy unit from before                
                call SetUnitOwner(.Dummy, owner, false)
                // I suppose you could also set the dummy unit's coordinates to the target, but you don't really need to.
            endif
You wouldn't kill the dummy unit in the destroy method.


If you check the event in GUI, it says: "A unit spawns a summoned unit"
GUI is dumb and misleading. :p
GetTriggerUnit is the same, so you should replace GetSummonedUnit with it in the onSummoning method.

You should set trigger t to CreateTrigger on the same line you declare it in onInit.

EDIT:
@Magtheridon96: You don't null u in the onInit method. Or v in CreateIllusion
Your method doesn't even make sense. You're making the dummy unit get treated as the illusion. o_O
 
I'm fixing it. Will post the code here in a minute.

edit
Meh.. Nothing's working correctly..

Suggestions:
1-
JASS:
    private constant integer ILLU_ORDER = 852274
    private constant integer ILLU_BUFF = 'Blil'

These aren't really configurable, so just inline them
2- Use your own linked list like this:
JASS:
// Requires:
private static integer array next
private static integer array prev
// How to add to list
set next[this] = 0
set prev[this] = prev[0]
set next[prev[0]] = this
set prev[0] = this
// How to remove from list
set next[prev[this]] = next[this]
set prev[next[this]] = prev[this]
// How to get first element
set ___ = next[0]
// How to iterate
local integer i = next[0]
loop
    exitwhen i == 0
    // ....
    set i = next[i]
endloop
3-
if IsUnitIllusion(GetSummonedUnit()) then -> if IsUnitIllusion(GetTriggerUnit()) then
4- Try to combine functions main and onSummoning
5- Read number 3 and again and just deprecate ListModule
6- Change the function name "New" to "create"
7- Add an API (CreateIllusion and GetLastCreatedIllusion)
8- [highlight]Remove ALL the locals except 'this' from function New and just inline them since they're all used only once[/code]
 
Last edited:
Level 11
Joined
Mar 6, 2008
Messages
898
hiho,

yeah but for a normal mirror image you need an ability object for every single mirrir image and with this system you can create different mirror images with one object and one line of code!

besides that it supports GetLastCreatedIllusion(). =)

Robbepop
 
There's always room for improvement :p

Suggestions:

- Use your own linked list!! ( My post on the second page explains how )
- Make this require Table and use a table to store the struct instance with the index of the handle Id of the dummy unit so that you can directly have the instance with a hashtable lookup (With the Table instance)
- Shorten the variable names
- Remove the spacing between each line
- Change "New" to "create"
- Change "Dummy" to "u"
- Change "IllDuration" to "d"
- Follow Jass convention damn it!
 
Last edited:
- Use your own linked list!! ( My post on the second page explains how )
- Make this require Table and use a table to store the struct instance with the index of the handle Id of the dummy unit so that you can directly have the instance with a hashtable lookup (With the Table instance)
- Shorten the variable names
- Remove the spacing between each line
- Change "New" to "create"
- Change "Dummy" to "u"
- Change "IllDuration" to "d"
- Follow Jass convention damn it!

This... for the third time -_-
 
Level 11
Joined
Mar 6, 2008
Messages
898
hiho,

why do you dislike the ListModule?
it doesn't make sense to make an own list for the dummy get method if I shall use table to save the units struct instance. 0.o doublesave?

shortened var names just make everything confusing - I like nice-looking codes where I see after a few seconds what the snippet of code does exactly.
with var names like "u", "a" and "i" you first have to find out what they stand for ...

could you please show me where to find the Jass conventions?

Robbepop
 
Seriously maddeem, just accept it :p
I know you're the kind of guy who judges things by their negatives, but just this once, say "I vote for approval" :p

This is great because it gives you a way to manage illusions in Jass :)

Until vJass ceases to exist I WILL APPOSE IT!
Anyway I really like the "GetLastIllusion" thingy, could possibly be useful. Then again you could do that with normal mirror image. Just have a few lines of code n bam!
 
Top