HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

MindFlay Spell

12-11-2008, 10:09 AM#1
akolyt0r
MindFlay is a spell of WoW, which works like life drain (and i based it on lifedrain therefore), but additionally slows the target while casting ..

It would be nice if someone could look through my triggers and tell me if i could improve anything...

uses Table-System

tried without worked pretty well, but if units walked out of mindflay range, the debuff was not removed so i added Table stuff

Start Trigger:
Collapse JASS:
globals
Table MindFlayArr
endglobals

function MindFlayStartCond takes nothing returns boolean
return ( GetSpellAbilityId() == 'A000' ) //MindFlay Spell id
endfunction    

function Trig_Mind_Flay_Start_Actions takes nothing returns nothing
    local unit caster=GetTriggerUnit()
    local unit target=GetSpellTargetUnit()
    local location l=GetUnitLoc(target)
    local player p=GetOwningPlayer(caster)
    local unit cdummy=CreateUnitAtLoc(p,'u000',l,0.)
    call IssueTargetOrder(cdummy,"slow",target)
    call UnitApplyTimedLife( cdummy, 'BTLF', 3.00)
    set MindFlayArr[H2I(caster)]=H2I(target)
    //===========================================================================
    //cleanup
    set caster=null
    set target=null
    set cdummy=null
    set l=null
    call RemoveLocation(l)
    
endfunction

//===========================================================================
function InitTrig_Mind_Flay_Start takes nothing returns nothing
    local integer index
    set gg_trg_Mind_Flay_Start = CreateTrigger(  )
    set index = 0
    loop
        call TriggerRegisterPlayerUnitEvent(gg_trg_Mind_Flay_Start, Player(index), EVENT_PLAYER_UNIT_SPELL_EFFECT, null)
        set index = index + 1
        exitwhen index == 9
    endloop
    call TriggerAddAction( gg_trg_Mind_Flay_Start, function Trig_Mind_Flay_Start_Actions )
    call TriggerAddCondition( gg_trg_Mind_Flay_Start, Condition( function MindFlayStartCond ))
endfunction

MindFlay End Trigger
Collapse JASS:
function InitTrig_Mind_Flay_Canceled takes nothing returns nothing
    local integer index
    set gg_trg_Mind_Flay_Canceled = CreateTrigger(  )
    set index = 0
    loop
        call TriggerRegisterPlayerUnitEvent(gg_trg_Mind_Flay_Canceled, Player(index), EVENT_PLAYER_UNIT_SPELL_ENDCAST, null)
        call TriggerRegisterPlayerUnitEvent(gg_trg_Mind_Flay_Canceled, Player(index), EVENT_PLAYER_UNIT_SPELL_FINISH, null)
        set index = index + 1
        exitwhen index == 9
    endloop
    call TriggerAddCondition( gg_trg_Mind_Flay_Canceled, Condition( function MindFlayCancelCond ) )
    call TriggerAddAction( gg_trg_Mind_Flay_Canceled, function Trig_Mind_Flay_Actions )
endfunction

any critics ??
Thanks ;)
12-13-2008, 09:57 PM#2
akolyt0r
bump
any suggestions...any way around table stuff, attachments ..?
12-13-2008, 11:13 PM#3
Here-b-Trollz
The end trigger is just an Init_Trig...

Also, function TriggerRegisterAnyUnitEventBJ <--- okay to use

You should know that EVENT_PLAYER_UNIT_SPELL_ENDCAST fires when the spell is canceled AND when it is finished. So, if you register both taht and SPELL_FINISH, you will be running an action twice if he actually finishes the spell.

It could all be compiled into one trigger, for one. If you're using vJass, then lets stop this atrocity of code:

Collapse JASS:
scope MindFlay
    globals
        private HandleTable T
    endglobals

    private function startconditions takes nothing returns boolean
        return ( GetSpellAbilityId() == 'A000' ) //MindFlay Spell id
    endfunction

    private function oncast takes nothing returns nothing
        local unit caster=GetTriggerUnit()
        local unit target=GetSpellTargetUnit()
        local location l=GetUnitLoc(target)
        local player p=GetOwningPlayer(caster)
        local unit cdummy=CreateUnitAtLoc(p,'u000',l,0.)
        call IssueTargetOrder(cdummy,"slow",target)
        call UnitApplyTimedLife( cdummy, 'BTLF', 3.00)
        
        //I'm not sure what this is used for, but it looks unsafe :<
        set T[caster]=H2I(target)
         
        //cleanup
        
        //note that we destroy the location, THEN null it.
        //otherwise, we are destroying a null location.        
        call RemoveLocation(l)
        set l=null
        
        //however, a location isn't even necessary, and
        //could be replaced with reals (x and y).

        set caster=null
        set target=null
        set cdummy=null
    endfunction

    private function onfinish takes nothing returns nothing
        //whatever you are doing here
    endfunction

    private function cancelconditions takes nothing returns boolean
        //some condition here
    endfunction

    private function init takes nothing returns nothing
        local trigger t=CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_EFFECT)
        call TriggerAddCondition(t,Condition(function startconditions))
        call TriggerAddAction(t,function oncast)
        set t=CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_ENDCAST)
        call TriggerAddCondition(t,Condition(function cancelconditons))
        call TriggerAddAction(t,function onfinish)
        set T=Table.create()
    endfunction
endscope
12-13-2008, 11:31 PM#4
akolyt0r
im currently on a holiday weekend with some other guys (=im drunk)
i will check this tomorrow or so ..

anyway thanks for your answer
12-14-2008, 07:31 PM#5
akolyt0r
This is what i come up with today:
Collapse JASS:
scope MindFlay initializer init
    globals
        private HandleTable MF
    endglobals

    private function SpellId takes nothing returns boolean
        return ( GetSpellAbilityId() == 'A000' ) //MindFlay Spell id
    endfunction

    private function oncast takes nothing returns nothing
        local unit caster=GetTriggerUnit()
        local unit target=GetSpellTargetUnit()
        local location l=GetUnitLoc(target)
        local player p=GetOwningPlayer(caster)
        local unit cdummy=CreateUnitAtLoc(p,'u000',l,0.)
        call IssueTargetOrder(cdummy,"slow",target)
        call UnitApplyTimedLife( cdummy, 'BTLF', 3.00)
        set MF[caster]=H2I(target)
        call RemoveLocation(l)
        set l=null
        set caster=null
        set target=null
        set cdummy=null
    endfunction

    private function onfinish takes nothing returns nothing
    local unit caster=GetTriggerUnit()
    local unit target=GetSpellTargetUnit()
    if(target==null)and(MF.exists(caster)) then
        set target=I2U(MF[caster])
    endif
    if not( target==null) then
        call UnitRemoveAbility(target, 'B000') 
    endif
    call MF.flush(caster)
    set caster=null
    set target=null
    endfunction


    private function init takes nothing returns nothing
        local trigger t=CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_EFFECT)
        call TriggerAddAction(t,function oncast)        
        call TriggerAddCondition(t,Condition(function SpellId))
        set t=CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_ENDCAST)
        call TriggerAddAction(t,function onfinish)        
        call TriggerAddCondition(t,Condition(function SpellId))
        set MF=Table.create()
    endfunction
endscope

set T[caster]=H2I(target) //I'm not sure what this is used for, but it looks unsafe Here i save the target unit, for the onfinish trigger, where i remove the buff.

Collapse JASS:
    local unit target=GetSpellTargetUnit()
    if(target==null)and(MF.exists(caster)) then
        set target=I2U(MF[caster])
    endif
    if not( target==null) then //dunno if i still need that if.. here
        call UnitRemoveAbility(target, 'B000') 
    endif
 

when the spell is canceled (e.g. owner of unit cancels it, or casting unit gets stunned) local unit target=GetSpellTargetUnit() will get the target unit.
However, when the spell finishes normally, target will be null...
so i get the unit from the table, where i saved the unitid of the target in trigger oncast...

hmm i just thought about this once a again and i come to the conclusion, i dont need this...
I just need to adjust Duration of my dummy-slow-spell to normal duration of Mindflay, so i only need to cover any cancel effect, so i can get rid of the table stuff...

EDIT: GetSpellTargetUnit() will return the target unit for most cancels, but not if the target unit walks out of range, so i still need the attachment stuff


thanks Here-b-Trollz

PS: you forgot to add initializer to your scope :>
12-14-2008, 08:12 PM#6
Zerzax
Don't use table in this manner. Use a struct or array indexing via Cohadar's PUI or something like it, and Here-b-Trollz is correct in that use of a table like that is unsafe, especially since you are using I2U which is an outdated and dangerous way of typecasting.

EDIT: You should also indent properly and space out sections of your code so it's more readable for other people.
12-14-2008, 08:15 PM#7
akolyt0r
a struct for 1 unit ...seems a bit odd for me dunno...
Collapse JASS:
struct
   unit oddunit
endstruct

well how does the background of structs work then ??
does it use global unit arrays or what ?

and what should be used to typecast these days then ?

(i want pointers and references :/ Jass is so friggin limited)
12-14-2008, 08:25 PM#8
Zerzax
What you really want is a unit array of targets, you can typecast using H2I for the caster's memory address but it will return the target unit. I'll edit this post and tell you more about structs.

Struct refers to structure, or a custom object type that arrays each of its members. Each of these members becomes a global array, whose individual indexes hold the data that pertains to each struct instance. When you "create" a struct instance you are allocating the data for each arrayed member of that instance. So when you declare a local "struct", you're really just declaring the index that pertains to a certain set of struct members. Make sense?

I'll show you what I mean about the global unit array:
Collapse JASS:
scope MindFlay initializer init

    globals
        private unit array targets[8190]
        private constant integer MIN_HANDLE_ID=0x100000
    endglobals

    private function H2I takes handle h returns integer
        return h
        return 0
    endfunction
    
    private function oncast takes nothing returns nothing
        local unit caster=GetTriggerUnit()
        local unit target=GetSpellTargetUnit()
        local unit cdummy=CreateUnit(GetOwningPlayer(caster), 'u000', GetUnitX(target), GetUnitY(target), 0.)
        
        call IssueTargetOrder(cdummy,"slow",target)
        call UnitApplyTimedLife( cdummy, 'BTLF', 3.00)
        set targets[H2I(caster)-MIN_HANDLE_ID]=target
        
        set caster=null
        set target=null
        set cdummy=null
    endfunction

    private function onfinish takes nothing returns nothing
        call UnitRemoveAbility(targets[H2I(GetTriggerUnit())-MIN_HANDLE_ID], 'B000') 
    endfunction
    
    private function SpellId takes nothing returns boolean
        return ( GetSpellAbilityId() == 'A000' ) //MindFlay Spell id
    endfunction
    
    private function init takes nothing returns nothing
        local trigger t=CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_EFFECT)
        call TriggerAddAction(t,function oncast)        
        call TriggerAddCondition(t,Condition(function SpellId))
        
        set t=CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_ENDCAST)
        call TriggerAddAction(t,function onfinish)        
        call TriggerAddCondition(t,Condition(function SpellId))

    endfunction
    
endscope

And I ended up rewriting the whole script for you. Does it make sense?
What I showed you I think is not a totally safe way of indexing your target, especially since the indices aren't getting recycled. That's where PUI's GetUnitIndex would come in.
12-14-2008, 08:59 PM#9
akolyt0r
Thanks...

well but doesnt this leak a unit:
call UnitRemoveAbility(targets[H2I(GetTriggerUnit())-MIN_HANDLE_ID], 'B000') ... GetTriggerUnit()..?

hmm question:
what is when H2I(GetTriggerUnit())-MIN_HANDLE_ID is greater than 8190 ?
is that possible ?
i dont know how unitids work...if i lets say 8190 units ...the next one should have id 8191 ? ...and
then i remove 100 units...and create another one ...will it have id 8091 or something like that ?
12-14-2008, 09:45 PM#10
Zerzax
No that does not leak a unit, it merely gives you a pointer to the triggering unit. I think you are getting confused with locations, where if you refer to GetSpellTargetLoc or the like you are actually creating a new location every time, which needs to be removed and nulled.

All handles are assigned a memory address, the 8191'th handle created at a certain point in the game will have that memory address. Subtracting the minimum handle ID drastically lowers the integer and lets you put it into a regularly sized array, that's why I (and Vexorian, whom I imitated) did it.

Unit ID's aren't memory addresses, they refer to the type of unit. 'u000' is a Unit ID. It refers to the first custom undead unit you made in the editor. You're thinking that units have their own table of memory addresses, but I believe all handle addresses are lumped into one giant hash table for the game, which is why you subtract the mind handle id...
12-14-2008, 09:50 PM#11
akolyt0r
so i could use GetTriggerUnit (and GetSpellTargetUnit and so on) as often as i would like and it would not leak ? ..thats good to hear :>
but i assume those function calls are slow and when i use those functions more then once (or twice ...?) local units are better speed-wise ?

i did not meant something like 'u000' but something like H2I(unit)...with unit ids ...hmm maybe my wording was a lil bit unclear :>
12-14-2008, 10:10 PM#12
Zerzax
Yes, 2 or more uses of a function like those pretty much necessitate using a local.

That's what I'm trying to say, you're mixing terminologies. u000 is a unit ID, and you're thinking of memory addresses.
12-15-2008, 04:33 AM#13
akolyt0r
i would rather call something like 'u000' unittype id, because it doesnt specify a specific unit, but an unittype ...

what should i call H2I(handle) then ? for this is a identifier of a handle (unit)
so i called it unitid -.-

i didnt got this question answered,yet :(
Quote:
what is when H2I(GetTriggerUnit())-MIN_HANDLE_ID is greater than 8190 ?
is that possible ?
i dont know how unitids work...if i lets say 8190 units ...the next one should have H2I(unit-MIN_HANDLE_ID)==8191 ? ...and
then i remove 100 units...and create another one ...will it have H2I(unit-MIN_HANDLE_ID)==8091 or something like that ?
12-15-2008, 04:47 AM#14
Zerzax
That array limit will be reached when your map has more than 8190 handles altogether existing at the same time, and your question was a bit vague at first. If you removed 100 units from the game and then created no handles after that point in time, the next unit's memory address would probably be 8091, yes. But that's a shitload of handles.

Quote:
Originally Posted by Zerzax
All handles are assigned a memory address, the 8191'th handle created at a certain point in the game will have that memory address.

Think of the "unit ID" as an integer pointer assigned to that unit, though these integers are not just assigned to units, they are assigned consecutively to all handles created during the course of the game. That's what I mean by memory address.
12-15-2008, 10:22 AM#15
PandaMine
H2I's give memory address for handles. These memory address's are theoritically reused in a stack like manner (i.e. if a unit is removed that handle is recycled and a new one is used).

8190 is the limit for arrays, not for handles. Im not sure what limit handles has, but its probably as many as you're computer can handle. Unit ID's are just an identification of a unit (i.e. the type of unit) and has nothing to do with handles specifically