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

[Snippet] Illusion

Kazeon

Hosted Project: EC
Level 33
Joined
Oct 12, 2011
Messages
3,449

Description
Allows you to create any unit's illusion (mirror image). This snippet has one weakness: not working for magic immune units.​
Requirements
- Bribe's UnitIndexer
- My DDS
- JNGP​
How to install
- Copy "Code" trigger folder into your map
- Install all required libraries properly
- Import dummy.mdx into your map
- Copy all object editor datas into your map
- Make sure ILLUSION_ITEM's ability is ILLUSION_ABILITY_1
Code
JASS:
library Illusion /* v2.1.0 -by Dalvengyr


        Description
        ¯¯¯¯¯¯¯¯¯¯¯
            Allows you to freely create illusion (mirror image) of any non magic immune unit
            
            
        Requirements
        ¯¯¯¯¯¯¯¯¯¯¯¯
            - Bribe's UnitIndexer   | hiveworkshop.com/forums/spells-569/gui-unit-indexer-1-2-0-2-a-197329/
            - Dalve's DDS           | hiveworkshop.com/forums/submissions-414/system-damagedetectsystem-261912/
        
        
        How to use
        ¯¯¯¯¯¯¯¯¯¯
            struct Illusion
                1. Create illusion version of a unit type
                    | static method create takes player id, integer unitid, real x, real y, real face returns thistype
                    
                2. Create illusion based from an existing unit
                    | static method copy takes player id, unit whichUnit, real x, real y, real face returns thistype
                    
                Struct members
                    | readonly unit unit        => unit instance of illusion
                    |          real dealtFactor => dealt damage multiplier for the illusion
                    |          real takenFactor => taken damage multiplier for the illusion
            
            
        Link
        ¯¯¯¯
            hiveworkshop.com/forums/submissions-414/snippet-illusion-256397/

*/

    // ===================================================================== //
    //                                                                       //
    //                           Configuration                               //
                            
        globals
            // Based on their rawcode at Object Editor
            private constant integer ITEM_ID  = 'I000'  // ILLUSION_ITEM
            private constant integer SPELL_ID = 'A001'  // ILLUSION_ABILITY_2
            private constant integer DUMMY_ID = 'h000'  // Dummy
        endglobals
    
    //                                                                       //
    // ===================================================================== //
            
    // Furthermore, edit them by your own risk!
        
    globals
        private item Item
        private unit Dummy
        private boolean Detect = false
        private constant player PASSIVE = Player(PLAYER_NEUTRAL_PASSIVE)
    endglobals
    
    native UnitAlive takes unit id returns boolean
    
    struct Illusion
    
        real dealtFactor
        real takenFactor
        readonly unit unit
        
        private static real LocX
        private static real LocY
        
        private static thistype TempDex
        private static thistype array Index
        
        // Create illusion based on existing unit
        static method copy takes player id, unit whichUnit, real x, real y, real face returns thistype
            
            // If target is valid
            if whichUnit != null and UnitAlive(whichUnit) then
                
                // If target is not magic immune
                if not(IsUnitType(whichUnit, UNIT_TYPE_MAGIC_IMMUNE)) then
                    
                    // Prepare to detect created illusion
                    set TempDex = allocate()
                    set LocX = x
                    set LocY = y
                    set Detect  = true
                    
                    // Create illusion
                    call SetUnitPosition(Dummy, GetUnitX(whichUnit), GetUnitY(whichUnit))
                    call SetUnitOwner(Dummy, id, false)
                    call UnitUseItemTarget(Dummy, Item, whichUnit)
                    
                    return TempDex
                    
                debug else
                    debug call BJDebugMsg("Failed to create Illusion :: Target is magic immune.")
                endif
                
            debug else
                debug call BJDebugMsg("Failed to create Illusion :: Target is invalid.")
            endif
            
            return 0
        endmethod
    
        // Create new illusion
        static method create takes player id, integer unitId, real x, real y, real face returns thistype
            
            local unit dummy = CreateUnit(PASSIVE, unitId, 0, 0, face)
            local thistype this = copy(id, dummy, x, y, face)
            
            call RemoveUnit(dummy)
            set dummy = null
            
            return this
        endmethod
        
        // On damage event
        private static method onDamage takes nothing returns boolean
            
            local thistype data
            
            // If damage source is a custom illusion
            set data = GetUnitUserData(udg_DDS__Event__DamageSource)
            if Index[data] != 0 then
                set udg_DDS__Event__DamageAmount = udg_DDS__Event__DamageAmount*Index[data].dealtFactor
            endif
            
            // If damage target is a custom illusion
            set data = GetUnitUserData(udg_DDS__Event__DamageTarget)
            if Index[data] != 0 then
                set udg_DDS__Event__DamageAmount = udg_DDS__Event__DamageAmount*Index[data].takenFactor
            endif
            
            return false
        endmethod
        
        // When unit is indexed
        private static method onIndex takes nothing returns boolean
            
            if Detect and IsUnitIllusion(udg_UDexUnits[udg_UDex]) then
            
                set TempDex.unit = udg_UDexUnits[udg_UDex]
                
                // Set default value
                set TempDex.takenFactor = 1
                set TempDex.dealtFactor = 1
                
                set Index[GetUnitUserData(udg_UDexUnits[udg_UDex])] = TempDex
                call SetUnitPosition(udg_UDexUnits[udg_UDex], TempDex.LocX, TempDex.LocY)
                
                set Detect = false
            endif
            
            return false
        endmethod
        
        // When unit is deindexed
        private static method onDeindex takes nothing returns boolean
        
            local integer data = GetUnitUserData(GetTriggerUnit())
            
            if Index[data] != 0 then
                call Index[data].destroy()
                set Index[data].unit = null
                set Index[data] = 0
            endif
            
            return false
        endmethod
        
        private static method onInit takes nothing returns nothing
            
            local trigger t
            
            // Prepare the dummy caster
            set Dummy = CreateUnit(PASSIVE, DUMMY_ID, 0, 0, 0)
            set Item  = CreateItem(ITEM_ID, 0, 0)
            call UnitAddAbility(Dummy, SPELL_ID)
            call UnitAddItem(Dummy, Item)
            
            // On damage trigger
            set t = CreateTrigger()
            call TriggerRegisterVariableEvent(t, "udg_DDS__Event__Trigger", EQUAL, 1.00)
            call TriggerAddCondition(t, Condition(function thistype.onDamage))
            
            // On index trigger
            set t = CreateTrigger()
            call TriggerRegisterVariableEvent(t, "udg_UnitIndexEvent", EQUAL, 1.00)
            call TriggerAddCondition(t, Condition(function thistype.onIndex))
            
            // On deindex trigger
            set t = CreateTrigger()
            call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_DEATH)
            call TriggerAddCondition(t, Condition(function thistype.onDeindex))
            
            set t = null
            
        endmethod
    endstruct
    
endlibrary
Demo
JASS:
scope test

    // I hope this demo can explain everything you need to know

    function onDamage takes nothing returns boolean
        
        call CreateTextTagUnitBJ(I2S(R2I(udg_DDS__Event__DamageAmount)), udg_DDS__Event__DamageTarget, 0, 10, 100, 100, 100, 0)
        call SetTextTagVelocityBJ(GetLastCreatedTextTag(), 128.00, 90)
        call SetTextTagPermanentBJ(GetLastCreatedTextTag(), false)
        call SetTextTagLifespanBJ(GetLastCreatedTextTag(), 2.00)
        call SetTextTagFadepointBJ(GetLastCreatedTextTag(), 1.00)
        
        return false
    endfunction

    function create takes nothing returns nothing

        local unit c = GetEnumUnit()
        local Illusion u
            
        if IsUnitType(c, UNIT_TYPE_HERO) then
            // copy method is useful to copy entire data on a unit like level, carried items, attributes, etc.
            set u = Illusion.copy(GetOwningPlayer(c), c, GetUnitX(c), GetUnitY(c), GetUnitFacing(c))
        else
            // while create is used to create entirely new illusion
            set u = Illusion.create(GetOwningPlayer(c), GetUnitTypeId(c), GetUnitX(c), GetUnitY(c), GetUnitFacing(c))
        endif
        
        // Custom image is BIG
        call SetUnitScale(u.unit, 2, 0, 0)
        
        // Illusion takes 4x damage and deals half damage
        set u.takenFactor = 4
        set u.dealtFactor = 0.5
        
        set c = null
        
    endfunction

    function Trig_Untitled_Trigger_001_Copy_Actions takes nothing returns nothing

        local group g = GetUnitsSelectedAll(GetTriggerPlayer())
        
        call ForGroup(g, function create)
        call DestroyGroup(g)
        set g = null
        
    endfunction

    function InitTrig_test takes nothing returns nothing
        
        local trigger t = CreateTrigger()

        call FogEnable(false)
        call FogMaskEnable(false)
        set gg_trg_test = CreateTrigger()
        
        call TriggerRegisterPlayerEventEndCinematic(gg_trg_test, Player(0))
        call TriggerAddAction(gg_trg_test, function Trig_Untitled_Trigger_001_Copy_Actions)
        
        call TriggerRegisterVariableEvent(t, "udg_DDS__Event__Trigger", EQUAL, 1.00)
        call TriggerAddCondition(t, Condition(function onDamage))
        
        call DisplayTimedTextToPlayer(Player(0), 0, 0, 0, "Press 'esc' to create selected unit's illusion")
        call DisplayTimedTextToPlayer(Player(0), 0, 0, 0, "- Create new illusion if unit is not a hero, will copy if hero")
        call DisplayTimedTextToPlayer(Player(0), 0, 0, 0, "- Custom illusion takes 4x damage and deals half damage")
        call DisplayTimedTextToPlayer(Player(0), 0, 0, 0, "- Using wand of illusion will create one additional illusion")
        call DisplayTimedTextToPlayer(Player(0), 0, 0, 0, "- Custom illusions are BIG and don't have timed life")
        
    endfunction

endscope
 

Attachments

  • Illusion.w3x
    63.9 KB · Views: 144
  • [Snippet] Illusion2.w3x
    68.9 KB · Views: 101
Last edited:

Bannar

Code Reviewer
Level 26
Joined
Mar 19, 2008
Messages
3,140
Idea is nice, however, I'd rather use wand of illusion as a base because I always get what I want insteado of messing with custom type.

Also, you don't have to worry about heroes - it always works.

Edit: after further evaluation I might be wrong. I'll try to test/think about this more as soon as I can spare some time.
Btw, why do you use item? You just need the ability.

Most of those private functions are unnecessary - GetIndexedItem is available in most indexers - and the DD wrappers should be rather wrapped via static ifs.

Edit2: This -> instances (this is bad name for an array, let it be more informative).
You don't need IllusionGroup - just check is instance is found for given unit (illusion) when it is being deindexed.

I'm not into additional events for this - DDS already brings the damage events.
Shorten your approach by using instance array to your advantage:

JASS:
        private static method onDeindex takes nothing returns boolean
            local thistype this = instances[GetIndexedUnitId()]

            if ( this != 0 ) then
                call destroy()
            endif

            return false
        endmethod
JASS:
        private static method onDamage takes nothing returns boolean
            local thistype this = instances[GetUnitId(getDamageTarget())]
			local thistype this2 = instances[GetUnitId(getDamageSource())]

			if ( this != 0 ) then
				// stuff
			endif

			if ( this2 != 0 ) then
				// stuff
			endif
You don't need "Timer" variable too. Don't allocate new instance untill all conditions are met.

The thing which I mentioned: the overhead of create/remove unit might be too much if this is spammed - thats I prefer to use unit reference directly.
 
Last edited:

Bannar

Code Reviewer
Level 26
Joined
Mar 19, 2008
Messages
3,140
Sorry for double post, but I get why you wanted item here now ^)^

Btw, this isn't working properly because there is delay between UnitUseItemTarget and moment when illusion actually enters the map. You can check it via:
JASS:
                    set Detect = true
					call BJDebugMsg("before")
                    call UnitUseItemTarget(Caster, Item, u)
					call BJDebugMsg("after")
                    set Detect = false

//
            if Detect and IsUnitIllusion(u) then
                set index.unit = u
				call BJDebugMsg("within")
And see the output.

I've written much simpler Illusion snippet (because I'm more into CreateIllusion function rather then whole system within unnecessary overhead) but tomorrow if I find some free time, I'll definitely help you with this. Meaby even fix my small code, post it here so you could see what I'm talking about and meaby extend it for additional features.

Good night.
 

Kazeon

Hosted Project: EC
Level 33
Joined
Oct 12, 2011
Messages
3,449
Idea is nice, however, I'd rather use wand of illusion as a base because I always get what I want insteado of messing with custom type.
Yes I do use wand of illusion ability

Also, you don't have to worry about heroes - it always works.
It's indeed working, but the illusion will not copy hero's level, items, and abilities.

Most of those private functions are unnecessary - GetIndexedItem is available in most indexers - and the DD wrappers should be rather wrapped via static ifs.
I'm affraid different libraries have different function names.

This -> instances (this is bad name for an array, let it be more informative).
You don't need IllusionGroup - just check is instance is found for given unit (illusion) when it is being deindexed.
This => shorter => better ?

I've written much simpler Illusion snippet (because I'm more into CreateIllusion function rather then whole system within unnecessary overhead) but tomorrow if I find some free time, I'll definitely help you with this. Meaby even fix my small code, post it here so you could see what I'm talking about and meaby extend it for additional features.
I need some helps to fix those glitches indeed. Thank you.

My library is very simple and doesn't contain any overhead. I included damage detection especiallly for illusion because normal DDS can only detect the "true" damage while damage on illusion will be calculated based on their taken/dealt damage factor.

I also add those private functions because I'm affraid that some DDS/indexer has different function names. As comparasion: cokey's SDD use GetEventDamage() to obtain damage amount, while lfh's DDS uses udg_amount if I'm not mistaken.

Correct me if I'm wrong :) Thank you


updated

It's indeed working, but the illusion will not copy hero's level, items, and abilities.
I forgot one thing here, demo code is create a new illusion, not copying from units lol

updated with a new function. I hope I'm doing right thing at deindexing.
 
Last edited:
Level 19
Joined
Mar 18, 2012
Messages
1,716
Looks good, but I have to deal with your DDS also.

Can you turn off Bribes Unit Indexer when creating the dummy caster?

I guess the dummy needs the inventory ability, so you should state in the description,
that users have to make sure the dummy is correctly set up.
UnitAddItem returns a boolean, you could check that on init to ensure everything is working.

Set the owner back to neutral passive, you never know when a users wish to use
GroupEnumUnitsOfPlayer() :p
Cheap function call for extra safety
 

Kazeon

Hosted Project: EC
Level 33
Joined
Oct 12, 2011
Messages
3,449
I guess the dummy needs the inventory ability, so you should state in the description,
that users have to make sure the dummy is correctly set up.
Inventory ability is added via trigger, if I remember correctly. All they need to do is import all ability data and configure the library correctly.

Can you turn off Bribes Unit Indexer when creating the dummy caster?
Mkay.

Set the owner back to neutral passive, you never know when a users wish to use
I think it's better not to revert the owner since it would means 1 extra function call :p Except if there is a case where the player (AI) can control the dummy, but I think it's not possible since the dummy has locust.

GroupEnumUnitsOfPlayer() :p
Cheap function call for extra safety
In which part should I use it?

Since there is really no delay between call UnitUseItemTarget(Dummy, Item, whichUnit) and the index event (the illusion is created very instantly), I think boolean Detect is no longer needed, wonder would that mean less safety. Eventho it's a little bit faster to avoid single function call on every index event :p if Detect and IsUnitIllusion(udg_UDexUnits[udg_UDex]) then (I became speed nerdy here). What do you think?
 
Level 12
Joined
Feb 22, 2010
Messages
1,115
Why instead of directly adding ability to unit and order to use, you add ability to item and order dummy unit to use item?
 

Kazeon

Hosted Project: EC
Level 33
Joined
Oct 12, 2011
Messages
3,449
@Quilnez

He's not saying to you use the GroupEnumUnitsOfPlayer(), he's saying that people can do it and change the dummy in someway that they shouldn't.

No, units with locust ability can't be enumerated iirc.

Thanks for clarifying btw.

EDIT: Aww silly me, how could I didn't understand that part :<
 
Last edited:

Kazeon

Hosted Project: EC
Level 33
Joined
Oct 12, 2011
Messages
3,449
Why shouldn't I use my own DDS? What's better of PDD compared to mine? And I still wonder why my DDS can't be approved yet? Because there are already 3 other DDS? Nestharus' DDS is not even available here.

Sorry, I barely got times for my code stuffs.

EDIT:
Missed one of your post. Was kind of distracted by your next post.
 
Level 23
Joined
Apr 16, 2012
Messages
4,041
There is no point in having the exact same resource, which achieves the exact same thing, by very similar or exact same methods in hive. One of them is redundant. I did not dig too deep to say if it truly is or isnt, but this is one of criteria for not making DDS(the other is it is very likely you will get it wrong somewhere)

I dont know why this isnt locked since its gyd. The dds debate can move to dds thread
 
Top