HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

Cache/Table spell event

12-29-2008, 05:32 PM#1
Themerion
Vexorian made a function for the CasterSystem, which hashed function names with ability id's as keys (it was called something like OnAbilityEffect). This allowed for using a single trigger for all EVENT_PLAYER_UNIT_SPELL_EFFECT triggers.
Collapse The system looked something like this...:
//! don't parse as valid jass. It is only for showing what I mean.

public function OnAbilityEffect takes integer id, string func returns nothing
    call GameCacheStore(id,S2I(func))
endfunction

private function Actions takes nothing returns nothing
    local integer si = GameCacheGet(GetSpellAbilityId())
    if si!=0 then
        call ExecuteFunc(I2S(si))
    endif
endfunction

function Init takes nothing returns nothing
    local trigger trg=CreateTrigger()
    call TriggerAddAction(trg,function Actions)
    call TriggerRegisterAnyUnitEventBJ(trg,EVENT_PLAYER_UNIT_SPELL_EFFECT)
endfunction

Coding spells was made shorter this way, and also more efficient if used in a sufficiently large scale (he claimed back then).

Collapse Sample usage:
function InitTrig_MySpell takes nothing returns nothing
    call OnAbilityEffect(SPELL_ID,"MySpell_Actions")
endfunction

xe doesn't have a function like this. Has this method "become" evil, or is it just forgotten?
12-29-2008, 06:13 PM#2
cohadar
It is not evil, just plain dumb.
I think Vex realized that eventually and decided to drop it.
12-29-2008, 06:28 PM#3
Themerion
Quote:
Originally Posted by cohadar
It is not evil, just plain dumb.
I think Vex realized that eventually and decided to drop it.

I remember somebody who was complaining a lot about people not providing arguments for their claims (look up ABC's thread :)

Why is it dumb? Is it that much slower than average usage? I mean, comparing with having a 100 separate triggers all responding to EVENT_PLAYER_UNIT_SPELL_EFFECT checking their conditions; this would seem more... elegant.

ExecuteFunc is somewhat dumb, yah. But that we can avoid by using function pointers, right?
12-29-2008, 08:18 PM#4
Strilanc
Totally untested, limited to start-of-effect, but it should be close to what you mean.

It won't be more efficient if you're just doing a couple of spells. But if you have a couple dozen, I think it would be.

Expand JASS:
12-30-2008, 07:27 PM#5
Themerion
Interesting method of calling an arbitrary function. If the spell response too would have a ForForce, wouldn't that cause trouble?

Collapse JASS:
if i >= 0 then
    call ForForce(bj_FORCE_PLAYER[0], I2Code(callbacks[i]))
endif

Would using vJASS function pointers be slower?
12-30-2008, 11:44 PM#6
Strilanc
Quote:
Originally Posted by Themerion
Interesting method of calling an arbitrary function. If the spell response too would have a ForForce, wouldn't that cause trouble?

Collapse JASS:
if i >= 0 then
    call ForForce(bj_FORCE_PLAYER[0], I2Code(callbacks[i]))
endif

Would using vJASS function pointers be slower?

ForGroup chains correctly, I guess I've never actually tested ForForce.
12-31-2008, 05:46 AM#7
Blackroot
Personally I use;
Code:
struct CASTING_EVENT
    integer sid
    trigger use
endstruct

globals
    CASTING_EVENT array CASTING_EVENTS
    unit CASTING_EVENT_CASTER
endglobals

constant function MINIMUM_SPELL_HANDLE_ID takes nothing returns integer
    return 'A000'
endfunction

function RegisterSpellCastingEvent takes integer sid, trigger use returns nothing
    local CASTING_EVENT cs = CASTING_EVENT.create()
    
    set cs.sid = sid
    set cs.use = use
    set CASTING_EVENTS[sid - MINIMUM_SPELL_HANDLE_ID()] = cs
endfunction

function GetRegisteredSpellEvent takes integer sid returns trigger
    return CASTING_EVENTS[sid -  MINIMUM_SPELL_HANDLE_ID()].use
endfunction

//==========TRIG
function Trig_CastingHandler_Actions takes nothing returns nothing
    local unit cast = GetTriggerUnit()
    local integer sid = GetSpellAbilityId()
    local trigger t = GetRegisteredSpellEvent(sid)

    if(t == null)then
        return
    endif
    set CASTING_EVENT_CASTER = cast
    call TriggerExecute(t)
endfunction

//===========================================================================
function InitTrig_CastingHandler takes nothing returns nothing
    set gg_trg_CastingHandler = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(gg_trg_CastingHandler, EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddAction(gg_trg_CastingHandler, function Trig_CastingHandler_Actions)
endfunction

I have no idea if vex's is more efficient, but I agree with you Themerion, logic would agree that this system should be more efficient then having a billion different things firing spell events. Although my system has an inline target system so I only need to pass the caster, it could get bad if you need to pass X/Y + Caster or Caster + Target.

Who knows though, maybe it's a slower method. Someone should benchmark a map with a lot of spells using both systems.
12-31-2008, 10:32 AM#8
Themerion
Quote:
Originally Posted by Blackroot
Although my system has an inline target system so I only need to pass the caster, it could get bad if you need to pass X/Y + Caster or Caster + Target.

Are you certain you need to pass those? I thought TriggerExecute would preserve GetTriggerUnit() / GetSpellTargetLoc() / etc. (ExecuteFunc did).
01-01-2009, 08:44 AM#9
Blackroot
Quote:
Originally Posted by Themerion
Are you certain you need to pass those? I thought TriggerExecute would preserve GetTriggerUnit() / GetSpellTargetLoc() / etc. (ExecuteFunc did).

Tested it, you are correct. Saves me some time and energy :P. With that, I see no reason why this would be less efficient even in small sample sizes.
01-01-2009, 10:18 AM#10
Themerion
I'm not really at home with the 'AXXX'-integer values, but isn't there a chance they'll exceed the upper array bounds? Let's say you use the ability Amls:

Collapse JASS:
CASTING_EVENTS[sid -  MINIMUM_SPELL_HANDLE_ID()]
// 'Amls' - 'A000' = 4013123
// Won't fit in array
01-01-2009, 11:08 PM#11
Blackroot
It only works for custom spells. It should never come to a point where there are 8191+ spells in a map. (I would hope) But non-custom spells will exceed or undermine the boundary. Still working on a way around it, I'm fairly sure the minimum-maximum range of pre-set spells dont range past 8190 either, although dont quote me on that.
01-01-2009, 11:25 PM#12
Strilanc
Quote:
Originally Posted by Blackroot
It only works for custom spells. It should never come to a point where there are 8191+ spells in a map. (I would hope) But non-custom spells will exceed or undermine the boundary. Still working on a way around it, I'm fairly sure the minimum-maximum range of pre-set spells dont range past 8190 either, although dont quote me on that.

NewGen let's you use custom ability IDs. You *need* to use a hash table to guarantee it will work.
01-02-2009, 02:17 AM#13
Blackroot
Quote:
Originally Posted by Strilanc
NewGen let's you use custom ability IDs. You *need* to use a hash table to guarantee it will work.

Indeed you are right, the distance between min-max is over 200,000. The difference of the lowest to the second lowest is 280, however dividing min-max by 280 returns 10073 which falls out of array range. It probably has other issues to it too, so it seems that for premade spells you either rewrite the .slk ids to be consecutive or use a hash table or some other method.
01-02-2009, 06:32 AM#14
Themerion
Custom abilities' IDs tend to all come at the same spot: 'A000' and forward. Since there's no prime factor in the hash, can't we set the PROBE_SKIP variable to a much higher value than 7 (to prevent unnecessary chain-probing)?
01-02-2009, 03:02 PM#15
Strilanc
Quote:
Originally Posted by Themerion
Custom abilities' IDs tend to all come at the same spot: 'A000' and forward. Since there's no prime factor in the hash, can't we set the PROBE_SKIP variable to a much higher value than 7 (to prevent unnecessary chain-probing)?

The table is already a prime size, and adding a prime factor to the hash has an equivalent effect on collisions to changing the skip.

It actually already skips 7 backwards, not forwards, to try to keep probing collisions from stacking up. You can set it to any value you want, as long as it's less than 8191.