HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

MUI Spells

09-21-2008, 01:55 PM#1
WNxCryptic
One of my spells I've ported into XE (and the nice methods of scoping / library):

Collapse JASS:
library BoostMoral initializer init requires xecast
//*************************************************************************
//* Boost Moral - Forsaken
//* ---------
//* Temporarily boosts spirit (gold) and moral (mana) for a targeted friendly
//* unit, then removes those bonuses and checks for death (gold or mana < 0)
//*************************************************************************
    globals
        private constant integer SPELL_ID = 'A00C'  // rawcode for spell
        private unit u          // target unit
        private player p        // owner of u
        private real new        // amount of mana/spirit to add on and for how long
                                // the boost lasts (level of Boost Moral * 20)
    endglobals
    
    private function spellIdMatch takes nothing returns boolean
       return (GetSpellAbilityId()==SPELL_ID)
    endfunction
    
    private function onSpellEnd takes nothing returns nothing
        local timer t = GetExpiredTimer()
        
        // make changes to gold and unit's mana
        call SetUnitState(u, UNIT_STATE_MANA, GetUnitState( u, UNIT_STATE_MANA ) - new)
        call SetPlayerState(p, PLAYER_STATE_RESOURCE_GOLD, R2I( GetPlayerState( p, PLAYER_STATE_RESOURCE_GOLD )  - new))
        // check if death should ensue
        if(GetPlayerState(p, PLAYER_STATE_RESOURCE_GOLD ) <= 0) then
            call KillUnit(u)
        endif
        if ( GetUnitState(u, UNIT_STATE_MANA ) <=0) then
            call KillUnit(u)
        endif
        
        call DestroyTimer(t)    //cleanup
        set t = null
    endfunction
    
    private function onSpellEffect takes nothing returns nothing
        local timer t = CreateTimer()
        call SetUnitState( u, UNIT_STATE_MANA, GetUnitState( u, UNIT_STATE_MANA ) + new )
        call SetPlayerState( p, PLAYER_STATE_RESOURCE_GOLD, R2I( GetPlayerState( p, PLAYER_STATE_RESOURCE_GOLD )  + new) )
        
        // begin timer to remove boosts accurately
        call TimerStart( t, new, false, function onSpellEnd)
    endfunction
    
    private function init takes nothing returns nothing
        local trigger t = CreateTrigger()
        set u = GetSpellTargetUnit()
        set p = GetOwningPlayer(u)
        set new = I2R( 20 * GetUnitAbilityLevel( GetSpellAbilityUnit(), SPELL_ID ) )
        
        call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_EFFECT)
        call TriggerAddCondition(t, Condition(function spellIdMatch))
        call TriggerAddAction(t, function onSpellEffect)
        set t=null
    endfunction
endlibrary

The more I think about it, the less I'm sure this spell is MUI. Are a local copy of the globals created upon each triggering of this spell (EVENT_PLAYER_UNIT_SPELL_EFFECT matching conditions of spellIdMatch) ? Or are 1 copy of the globals created at some point when the game starts or the first time the library is used?
09-21-2008, 01:59 PM#2
Bobo_The_Kodo
They are globals, they aren't local. Private only changes their name on compile to something like: <libraryname>___<variablename>

To store that data, make it in the actions, and store it to a struct attached to the timer.
09-21-2008, 04:06 PM#3
WNxCryptic
nooo I wanted to circumvent attaching to stuff and stuff...
09-21-2008, 04:09 PM#4
Here-b-Trollz
Quote:
Originally Posted by Bobo_The_Kodo
They are globals, they aren't local. Private only changes their name on compile to something like: <libraryname>___<variablename>

For the record, I believe it is more like <libraryname><randomnumber>___<variablename>

Attaching is a fine thing. TimerUtils is pretty awesome for this. A stack could work as well, though, if you set it up correctly. Be a damn waste, but it could work.
09-21-2008, 04:12 PM#5
Anitarf
Quote:
Originally Posted by WNxCryptic
nooo I wanted to circumvent attaching to stuff and stuff...
That was optimistic. :)
09-21-2008, 04:33 PM#6
WNxCryptic
I know i know...but wouldn't that be great? To have variables global to a particular instance of a spell? It would eliminate the need to attach stuff in pretty much every spell anyone would ever write.
09-21-2008, 04:37 PM#7
PurplePoot
Quote:
Originally Posted by Here-b-Trollz
For the record, I believe it is more like <libraryname><randomnumber>___<variablename>

Attaching is a fine thing. TimerUtils is pretty awesome for this. A stack could work as well, though, if you set it up correctly. Be a damn waste, but it could work.
I've actually had proper compiling on multiple saves with:

Collapse JASS:
scope someScope
private function test takes nothing returns nothing
endfunction
endscope
//...
call someScope___test()

I'd imagine the random number idea was scrapped since it's pointless (I recall it used to be there); if people really want to break a system by calling private functions, they can just remove the 'private' keyword. They're more for having functions unique to the scope for compiling purposes, such as ease of copying standard triggers (private function Actions ... and the like) in my experience.

Quote:
I know i know...but wouldn't that be great? To have variables global to a particular instance of a spell? It would eliminate the need to attach stuff in pretty much every spell anyone would ever write.
The one problem with that is that it makes absolutely no sense, since by definition, globals are... global.

The thing that fits that description as best you can is... attaching, via gamecache, arrays, or otherwise! (:P)
09-21-2008, 04:43 PM#8
WNxCryptic
Alright well not globals of an instance of a spell, but a variable INSIDE a particular scope declared as a local that's created everytime the spell is initialized:

Collapse JASS:
scope somescope
   local unit u
   local player p
   private function rar takes nothing returns nothing
       set u = getTriggeringUnit()
   endfunction
endscope

If that worked for EVERY instance of a spell...
09-21-2008, 06:08 PM#9
Blubb-Tec
what you want are structs.

Collapse JASS:
scope somescope
       struct SpellData
             unit u
             player p
       endstruct

       private function rar takes nothing returns nothing
             local SpellData sd = SpellData.create()
             set sd.u= GetTriggeringUnit()
       endfunction
endscope

But DO NOT forget to clean up your struct calling the .destroy() method!
Read more about structs in the vJass manual, if I remember correctly the links are still in your last thread.
09-21-2008, 06:12 PM#10
WNxCryptic
Can't you define an onDestroy method of the struct that is called automatically?

Furthermore, for a local struct instance, do you need to set it to null

(Such as in your example, setting sd = null)

Edit: Structs are ints, they don't leak, nevermind. The 'onDestroy' question is still active though, anyone?
09-21-2008, 06:31 PM#11
Blubb-Tec
well, the onDestroy() method is called whenever you call destroy() on an object.
but you still have to call the .destroy() method yourself.