Unit Illusion System

Dirac

22710180
Reaction score
147
Provides the UnitCreateIllusion function (takes unit whichUnit, boolexpr action)
JASS:
library Illusion initializer onInit
    // external ObjectMerger w3a AIil UIEa anam "Illusion Create" atar 1 "notself" atat "" aart "" ahdu 1 0 adur 1 0 aran 1 100000
    globals
        private constant integer DUMMY='e000'
        
        private constant integer OFFSET=0x100000
        private trigger array callback
        public unit Get
    endglobals
    function UnitCreateIllusion takes unit whichUnit,boolexpr action returns nothing
        local unit u=CreateUnit(GetOwningPlayer(whichUnit),DUMMY,GetUnitX(whichUnit),GetUnitY(whichUnit),0)
        local integer id=GetHandleId(u)-OFFSET
        set callback[id]=CreateTrigger()
        call UnitAddAbility(u,'UIEa')
        call IssueTargetOrderById(u,852274,whichUnit)
        call TriggerAddCondition(callback[id],action)
        set u=null
    endfunction
    private function onCreate takes nothing returns boolean
        local unit c=GetSummoningUnit()
        local unit t=GetSummonedUnit()
        local integer id
        if GetUnitTypeId(c)==DUMMY then
            set id=GetHandleId(c)-OFFSET
            set Get=t
            call TriggerEvaluate(callback[id])
            call DestroyTrigger(callback[id])
        endif
        return false
    endfunction
    
    private function onInit takes nothing returns nothing
        local trigger trig=CreateTrigger()
        local integer i=0
        call TriggerAddCondition(trig,Condition(function onCreate))
        loop
            call TriggerRegisterPlayerUnitEvent(trig,Player(i),EVENT_PLAYER_UNIT_SUMMON,null)
            set i=i+1
            exitwhen i==bj_MAX_PLAYER_SLOTS
        endloop
        set trig=null
    endfunction
endlibrary

Example usage
JASS:
private function forIllusion takes nothing returns boolean
call DestroyEffect(AddSpecialEffect("boom",GetUnitX(Illusion_Get),GetUnitY(Illusion_Get))
return false
endfunction
private function create takes nothing returns nothing
call UnitCreateIllusion(someUnit,Filter(function forIllusion))
endfunction

Will display effect "boom" everytime an illusion is created.

Yes, it uses trigger evaluations, that's just awful
Yes, it's very inaccurate, not my fault, most likely the editor's fault for not having this as a native.
Yes, the action function fired when an illusion is created must return a boolean, any way to fix this without creating leaks.

PLEASE feedback
 

Jedi

New Member
Reaction score
63
I can get illusion next line with this.
JASS:
//No need to create dummy for each call, 1 dummy unit is enough.
globals
    unit lastCreatedIllusion = null
    
    constant integer ILLUSION_ABILITY_ID = 'AIil'
    constant integer DUMMY_ID = 'e000'
endglobals

function CatchIllusion takes nothing returns nothing
    call BJDebugMsg("Summon fired!")
    if GetUnitTypeId(GetSummoningUnit()) == DUMMY_ID then
        set lastCreatedIllusion = GetSummonedUnit()
    endif
endfunction

function Test takes nothing returns nothing
    local unit dummy
    set bj_lastCreatedUnit = CreateUnit(Player(0), 'hfoo', 0, 0, 270)
    set dummy = CreateUnit(Player(0),DUMMY_ID, 0, 0, 0)
    call UnitAddAbility(dummy, ILLUSION_ABILITY_ID)
    call SetUnitX(dummy, GetUnitX(bj_lastCreatedUnit))
    call SetUnitY(dummy, GetUnitY(bj_lastCreatedUnit))
    call IssueTargetOrderById(dummy, 852274, bj_lastCreatedUnit)
    call BJDebugMsg(GetUnitName(lastCreatedIllusion))
endfunction

//===========================================================================
function InitTrig_Melee_Initialization takes nothing returns nothing
    local trigger catch = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(catch, EVENT_PLAYER_UNIT_SUMMON)
    call TriggerAddAction(catch, function CatchIllusion)
    call TimerStart(CreateTimer(), 1.00, false, function Test)
endfunction


Edit:Wait... this only works if my footman has ghost(visible) ability lol.

Edit2:yeah yeah my mistake, updated code.
 

tooltiperror

Super Moderator
Reaction score
231
JASS:
function UnitCreateIllusion takes unit caster returns unit
    local unit hook  = CreateUnit(Player(0), 'hfoo', 0, 0, 270)  // Hook now points to the newest Handle Id, for example's sake 0x100A193
    call RemoveUnit(hook)                                        // Hook still points to 0x100A193, but 0x100A193 is now free

    call MakeUnitCastSummon(caster)                              // Blademaster casts illusion, the illusion gets next free Handle Id: 0x100A193
    
    return hook
endfunction

function IllusionTest_Actions takes nothing returns boolean
    local unit illusion  = CreateUnit(Player(0), 'Oblm', 0, 0, 270)  // Make a blademaster
    set illusion         = UnitCreateIllusion(blademaster)           // illusion is now its illusion

    // Do anything with "illusion"
    // hacking by handle Id is fun

    return false
endfunction

function InitTrig_IllusionTest takes nothing returns nothing
    local trigger trig = CreateTrigger()
    
    call TriggerAddCondition(trig, Condition(function IllusionTest_Actions))
    call TriggerEvaluate(trig)
endfunction


I've tested a similar concept with Images, only to find that a fellow from WC3C had discovered the hack before I did (damn TTE is always late) a friend of mine discovered a similar hack with lightning.

Untested, but I believe it would work. I may make this into my own system, that returns a dynamic array.
 

Nestharus

o-o
Reaction score
84
That would work if the handle id was recycled immediately, but it's recycled right after the JASS code, so you'd get an invalid handle id right there.


And to Dirac, don't use offsets.


This should also be using LUA_GET_VAR_OBJECT
-> [ljass]//! external ObjectMerger w3a AIil UIEa anam "Illusion Create" atar 1 "notself" atat "" aart "" ahdu 1 0 adur 1 0 aran 1 100000[/ljass]


Unless you like unsafe object generation
 

Jedi

New Member
Reaction score
63
^^How much time did you edit that :p

Final version
JASS:
library Illusion initializer Init

globals

    private constant integer DUMMY_ID = 'e000'

    private unit Dummy
    private unit lastCreatedIllusion

endglobals

function UnitCreateIllusion takes unit u, integer abilityId returns unit
    call UnitAddAbility(Dummy, abilityId)
    call SetUnitX(Dummy, GetUnitX(u))
    call SetUnitY(Dummy, GetUnitY(u))
    call SetUnitOwner(Dummy, GetOwningPlayer(u), false)
    call IssueTargetOrderById(Dummy, 852274, u)
    call UnitRemoveAbility(Dummy, abilityId)
    return lastCreatedIllusion
endfunction

private function DetectIllusion takes nothing returns boolean
    if GetUnitTypeId(GetSummoningUnit()) == DUMMY_ID then
        set lastCreatedIllusion = GetSummonedUnit()
    endif
    return false
endfunction

private function Init takes nothing returns nothing
    local trigger detect = CreateTrigger()
    set Dummy = CreateUnit(Player(15), DUMMY_ID, 0, 0, 0)
    call TriggerRegisterAnyUnitEventBJ(detect, EVENT_PLAYER_UNIT_SUMMON)
    call TriggerAddCondition(detect, Condition(function DetectIllusion))
endfunction

endlibrary

View attachment Illusion.w3x
 

Dirac

22710180
Reaction score
147
@Jedi, your code doesn't work, the illusion isn't created when you order the unit to cast it, it's created a few moments after that. That's why my code is like that, test it yourself

@tooltiperror if the handle of the illusion is used before the blademaster finishes casting the illusion it would bug the system. For this system instead of a blademaster should be used a item illusion ability i believe right?
the MakeUnitCastSummon is something you typed instead of actually ordering the unit to use the ability? or is that function supposed to do something lol.

@Nestharus not everyone uses LUA. Another option would be using Table to safely store unit's handles. On your comment to tooltiperror, the handle IS recycled since the illusion isn't casted along the code, it's casted shortly after that, so it returns the handle of a nonexistant unit for later use, would cause bugs if you intend to do actions to your illusions right after you created, this is the reason of why i'm using trigger evaluations, to actually wait for the illusion to be created.

Im sorry but all of the feedback posted above serves me no use, but Nes's suggestion to avoid unsafe handle offset.
 

Dirac

22710180
Reaction score
147
EDIT: hmm it seems to work fine.
Maybe creating the dummy everytime the illusion needs to be created slows down the whole "cast illusion on unit" action.
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      The Helper Discord

      Staff online

      Members online

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top