HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

Practicing Structs

09-25-2008, 02:12 AM#1
Joker
Collapse JASS:
library HR

globals
    private constant real BASE = 5.
    private constant real LVL_FACTOR = 1.5
endglobals

    public struct Revive
        unit a
        real x
        real y
        timer t = CreateTimer()
        boolean eyecandy = true

        static method Callback takes nothing returns nothing
            local Revive dat = Revive.allocate()
            call ReviveHero(dat.a, dat.x, dat.y, dat.eyecandy)
        endmethod
       
        method Set takes unit a, real x, real y returns nothing
            local Revive dat = Revive.create()
            local real Lvl = R2I(GetHeroLevel(a))
            set dat.a = a
            set dat.y = y
            set dat.x = x
            call TimerStart(dat.t, BASE+(LVL_FACTOR*Lvl), false, function Revive.Callback)
        endmethod
        
        method Eyecandy takes boolean b returns nothing
            set this.eyecandy = b
        endmethod
    endstruct
endlibrary

I don't know if the static part is used correctly and I'm pretty sure I'm missing something on the timers. In fact, I think i messed the entire thing up. Some help?

Also, are static methods are method that other methods can call? Are non-static methods ever allowed to be called by another method?

Operator Q:
Collapse example from jass maunal:
    struct operatortest
        string str=""

        method operator [] takes integer i returns string
            return SubString(.str,i,i+1)
        endmethod

        method operator[]= takes integer i, string ch returns nothing
            set .str=SubString(.str,0,i)+ch+SubString(.str,i+1,StringLength(.str)-i)
        endmethod

    endstruct


    function test takes nothing returns nothing
     local operatortest x=operatortest.create()
        set x.str="Test"
        call BJDebugMsg( x[1])
        call BJDebugMsg( x[0]+x[3])

        set x[1] = "."
        call BJDebugMsg( x.str)
    endfunction
I don't understand when the "operator[]=" gets called
09-25-2008, 02:35 AM#2
PurplePoot
operator[]= is called when you set x[1] = ".".

As for the revive thing, you need a storage method (TimerUtils, gamecache, etc) to pass the struct to the callback.
09-25-2008, 02:47 AM#3
Joker
Collapse JASS:
library HR uses TimerUtils

globals
    private constant real BASE = 5.
    private constant real LVL_FACTOR = 1.5
endglobals

    public struct Revive
        unit a
        real x
        real y
        timer t
        boolean eyecandy
        
        static method Callback takes nothing returns nothing
            local Revive dat = GetTimerData(GetExpiredTimer())
            call ReviveHero(dat.a, dat.x, dat.y, dat.eyecandy)
            call dat.destroy() //Do I need this?
            call ReleaseTimer(dat.t)
        endmethod
        
        static method Set takes unit a, real x, real y, boolean eyecandy returns Revive
            local Revive dat = Revive.create()
            local real Lvl = R2I(GetHeroLevel(a))
            set dat.a = a
            set dat.y = y
            set dat.x = x
            set dat.t = NewTimer()
            set dat.eyecandy = eyecandy
            call SetTimerData(dat.t, dat)
            call TimerStart(dat.t, BASE+(LVL_FACTOR*Lvl), false, function Revive.Callback)
            return dat
        endmethod
    endstruct
endlibrary
Well, this works, but am I doing everything correctly?
09-25-2008, 03:02 AM#4
Zerzax
To answer your question about static methods: Any static struct members (methods, objects, variables, etc) are those that behave like global variables in that they don't associate themselves with one struct instance and you can do whatever you want with their values indefinitely. The reason that timer callbacks in structs are static are because code arguments have to be unrelated to a specific structural instances. So, like PurplePoot said, you have to associate an integer (your struct instance) with every timer that you create for the callback method to recognize the proper instance (which you've done).

You can always call static methods whenever you want, think of them as regular old JASS functions. Static methods can't refer to specific instances, but on the other hand regular methods are restricted by the instance you call them with (instancename.method()) and can't do anything without knowing the right instance. Your methods and struct look fine.
09-25-2008, 09:45 PM#5
PurplePoot
Yeah, that's all right.
09-26-2008, 01:42 AM#6
Joker
This is not related to the trigger above:

Is it not possible for static methods to get instances without some passer function like ABC?
09-26-2008, 01:46 AM#7
PurplePoot
No, they're just functions.

Also, as far as attachment systems go, I'd recommend not bothering with ABC. Either go TimerUtils or just gamecache the struct (then there are better methods for fast-running timers for spells and such).
09-26-2008, 01:48 AM#8
Joker
I need one for passing through triggers.
09-26-2008, 01:52 AM#9
fX_
can he, alternatively, just find w/c Revive instance's timer == the expired timer to identify the Revive instance associated with the timer expiration event? is this less efficient?

also, what is the point of keeping the callback function inside the struct? "packaging"? what if i use a global timer? would "packaging" be good in this case or should i keep the callback as a function outside the struct since the timer is global?

also, GetTimerData() and ReleaseTimerData() don't show up green in my editor. they extra natives or part of some system?
09-26-2008, 02:01 AM#10
PurplePoot
In that case, I'd just use gamecache, Joker.

However, why you need to pass data through triggers is beyond me. You shouldn't ever have to.

--

Quote:
can he, alternatively, just find w/c Revive instance's timer == the expired timer to identify the Revive instance associated with the timer expiration event? is this less efficient?
You mean looping through an array? If so, yes.

Quote:
also, what is the point of keeping the callback function inside the struct? "packaging"? what if i use a global timer? would "packaging" be good in this case or should i keep the callback as a function outside the struct since the timer is global?
Preference.

Quote:
also, GetTimerData() and ReleaseTimerData() don't show up green in my editor. they extra natives or part of some system?
TimerUtils, check the scripts section.
09-26-2008, 02:20 AM#11
Joker
>However, why you need to pass data through triggers is beyond me. You shouldn't ever have to.

Well, I'm trying to make a temp damage detection for things like shields, damage amp, etc. so that I don't have to mess with big damage detection systems.
09-26-2008, 02:34 AM#12
Zerzax
Yes fx_ you can use array lookup, but as Vexorian pointed out to me it is O(n) time complex and is much more taxing on the processor than simply looking up the array with TimerUtils hashtable. I dunno Joker, I've heard coding damage detection systems is very difficult, but its up to you if you can figure it out :P.
09-26-2008, 03:48 AM#13
chobibo
Depends on how he retrieves his data (algorithm), linear searches are O(n) but a good hashing algorithm would only take O(1) time, like what's being used in TimerUtils (an exception is when the handle count gets past 1056766 (0x 000F FFFF + [0x 0FFF * 2])

EDIT: wait I was wrong in my statement about TimerUtils lol
Taken from the TimerUtils thread:

The flavors:

Blue TimerUtils
- Uses CSData's attaching method
- Function call, subtraction, O(log(n)) comparisons, array lookup + H2I
- The only condition for it to work in your map is that you never exceed 408000 handle ids in it. This limit is very big.

Red TimerUtils
- Uses the mass timers at init method.
- array lookup, subtraction, H2I (Get/SetTimerData get inlined)
- Therefore Red flavor is much faster.
- However, Red flavor has a timer limit of 256, once you exceed this limit, it might fail to work.
- It also requires to create 256 timers in the initializer.
09-26-2008, 06:11 AM#14
fX_
simple damage detection i made for my map. all it does is register for all EVENT_UNIT_DAMAGED damaged triggers the event for each unit.
Collapse JASS:
/////DAMAGE DETECTION SYSTEM START/////
scope DamageDetectionSystem

globals
    private trigger gTRIG_EnterMap
    
    private group gUG_Target //index of all EVENT_UNIT_DAMAGED event-concerned units
    
    private trigger array gTRIG_Damage
    private integer gINT_CountTrigger = 0 //count of 'EventDamageTrigger's duh...
endglobals

    //Updates index of all EVENT_UNIT_DAMAGED concerned units. (so redundant unit event triggers won't be
    //appended to triggers.
    private function UpdateTargetGroup takes nothing returns nothing
        local unit U_Target
        local group UG_Target = CreateGroup()
        
        call GroupAddGroup(gUG_Target, UG_Target)
        call GroupClear(gUG_Target)
        loop
            set U_Target = FirstOfGroup(UG_Target)
            exitwhen U_Target == null
            if U_Target != null and IsUnitInRegion(gREG_Map, U_Target) then
                call GroupAddUnit(gUG_Target, U_Target)
            endif
            call GroupRemoveUnit(UG_Target, U_Target)
        endloop

        call DestroyGroup(UG_Target)
        set UG_Target = null
        set U_Target = null
    endfunction

    //Updates index concerned units with the new entering unit and appends the EVENT_UNIT_DAMAGED event for it to all
    //instanciated triggers.
    private function gTRIG_EnterMap_Actions takes nothing returns nothing
        local unit U_Target = GetTriggerUnit()
        local integer INT_CountTrigger = 0
        
        //Update the group then index unit into it.
        call UpdateTargetGroup()
        call GroupAddUnit(gUG_Target, U_Target)
        
        //Appends EVENT_UNIT_DAMAGED for new unit to all concerned triggers (if there currently are any).
        if gINT_CountTrigger > 0 then
            loop
                set INT_CountTrigger = INT_CountTrigger + 1
                call TriggerRegisterUnitEvent(gTRIG_Damage[INT_CountTrigger], U_Target, EVENT_UNIT_DAMAGED)
                exitwhen INT_CountTrigger == gINT_CountTrigger
            endloop
        endif

        set U_Target = null
    endfunction

    //Instanciates a trigger and appends to it EVENT_UNIT_DAMAGED events for all concerned (indexed) units.
    function AddEventDamageTrigger takes trigger whichTrigger returns nothing
        local unit U_Target
        local group UG_Target = CreateGroup()
        
        set gINT_CountTrigger = gINT_CountTrigger + 1
        set gTRIG_Damage[gINT_CountTrigger] = whichTrigger
        
        //Appends EVENT_UNIT_DAMAGED to added trigger for all concerned units (if there currently are any).
        call GroupAddGroup(gUG_Target, UG_Target)
        loop
            set U_Target = FirstOfGroup(UG_Target)
            exitwhen U_Target == null
            call TriggerRegisterUnitEvent(whichTrigger, U_Target, EVENT_UNIT_DAMAGED)
            call GroupRemoveUnit(UG_Target, U_Target)
        endloop

        call DestroyGroup(UG_Target)
        set UG_Target = null
        set U_Target = null
    endfunction

    //Initializer; Indexes all concerned units in the map and sets-up the trigger that indexes
    //units that enter the map.
    public function Init takes nothing returns nothing
        set gTRIG_EnterMap = CreateTrigger()
        call TriggerRegisterEnterRegion(gTRIG_EnterMap, gREG_Map, null)
        call TriggerAddAction(gTRIG_EnterMap, function gTRIG_EnterMap_Actions)
        
        set gUG_Target = CreateGroup()
        call GroupEnumUnitsInRect(gUG_Target, gRECT_Map, null)
    endfunction

endscope
/////DAMAGE DETECTION SYSTEM END/////

Collapse JASS:
...
private function DamageSpellInit takes nothing returns nothing
local trigger spell = CreateTrigger()
call AddEventDamageTrigger(spell)
call TriggerAddCondition(...)
call TriggerAddAction(...)
endfunction
...
09-26-2008, 10:40 AM#15
Anitarf
Quote:
Originally Posted by Joker
Well, I'm trying to make a temp damage detection for things like shields, damage amp, etc. so that I don't have to mess with big damage detection systems.
So you'd rather mess with dynamic triggers? That's nonsense.
Quote:
simple damage detection i made for my map. all it does is register for all EVENT_UNIT_DAMAGED damaged triggers the event for each unit.
And why would anyone want to use that instead of any of the (much better) DD systems from our resource section?