| 12-29-2008, 05:32 PM | #1 |
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. 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). 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 |
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 | |
Quote:
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 |
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. JASS:library CT initializer init uses Index globals private integer array callbacks endglobals ///Converts an integer to a code variable that doesn't work private function I2UnsafeCode takes integer i returns code return i return null endfunction ///Converts an integer to a code variable private function I2Code takes integer i returns code local code c = I2UnsafeCode(i) return c endfunction ///Converts a code variable to an integer private function Code2I takes code c returns integer return c return 0 endfunction private function casted takes nothing returns nothing local integer i = Index_Get(GetSpellAbilityId()) if i >= 0 then call ForForce(bj_FORCE_PLAYER[0], I2Code(callbacks[i])) endif endfunction public function register takes code callback, integer abil_id returns boolean local integer i = Index_Get(abil_id) if i >= 0 then return false endif set i = Index_Create(abil_id) set callbacks[i] = Code2I(callback) return true endfunction private function init takes nothing returns nothing local trigger t = CreateTrigger() call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT) call TriggerAddAction(t, function casted) endfunction endlibrary library Index initializer init globals public constant boolean SHOW_DEBUG_MESSAGES = true //show debug messages (only in debug mode)? public constant boolean SHOW_ERROR_MESSAGES = true //show error messages when things go obviously wrong? endglobals //=============================================================================== //=============================================================================== //=============================================================================== globals private constant integer CAPACITY = 6000 //maximum allowed number of indexed keys private constant integer TABLE_SIZE = 8191 private constant integer PROBE_JUMP = 7 //the number of slots probing skips backwards over private integer array counts //usage count; -1 indicates empty but in probe chain private integer array keys private integer num_keys = 0 endglobals public function Get takes integer key returns integer local integer j = -1 local integer i = key - (key/TABLE_SIZE)*TABLE_SIZE loop //keep in range if i < 0 then set i = i + TABLE_SIZE endif //return match if found if keys[i] == key and counts[i] > 0 then return i endif //remember the first open slot if j == -1 and counts[i] <= 0 then set j = i endif //exit when the probe chain ends exitwhen counts[i] == 0 //next slot set i = i - PROBE_JUMP endloop return -1-j //[j is guaranteed to be a valid index by this point] endfunction public function Create takes integer key returns integer local integer i = Get(key) //Just increase usage count if already exists if i >= 0 then set counts[i] = counts[i] + 1 return i endif //Fail if at capacity if num_keys >= CAPACITY then if SHOW_ERROR_MESSAGES then call BJDebugMsg("Index: Couldn't index key[" + I2S(key) + "] due to maximum capacity.") endif return -1 endif //Index the key set i = -1-i set keys[i] = key set counts[i] = 1 set num_keys = num_keys + 1 debug if SHOW_DEBUG_MESSAGES then debug call BJDebugMsg("Index: Added key[" + I2S(key) + "] => " + I2S(i) + " [total:" + I2S(num_keys) + "]") debug endif return i endfunction public function Destroy takes integer key returns nothing local integer i = Get(key) if i < 0 then return elseif counts[i] > 1 then set counts[i] = counts[i] - 1 return endif //Destroy the index set counts[i] = -1 set num_keys = num_keys - 1 debug if SHOW_DEBUG_MESSAGES then debug call BJDebugMsg("Index: Removed key[" + I2S(key) + "] => " + I2S(i) + " [total:" + I2S(num_keys) + "]") debug endif //Check if the following slot is in a probe tail set i = i - PROBE_JUMP if i < 0 then set i = i + TABLE_SIZE endif if counts[i] != 0 then return endif //We're at the end of a probe tail; unwind it loop set i = i + PROBE_JUMP if i >= TABLE_SIZE then set i = i - TABLE_SIZE endif exitwhen counts[i] != -1 set counts[i] = 0 endloop endfunction //=== Initialization ================================================ private function init takes nothing returns nothing debug if SHOW_DEBUG_MESSAGES then debug call BJDebugMsg("Initialized Index with SHOW_DEBUG_MESSAGES") debug endif endfunction endlibrary |
| 12-30-2008, 07:27 PM | #5 |
Interesting method of calling an arbitrary function. If the spell response too would have a ForForce, wouldn't that cause trouble? 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 | |
Quote:
ForGroup chains correctly, I guess I've never actually tested ForForce. |
| 12-31-2008, 05:46 AM | #7 |
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)
endfunctionI 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 | |
Quote:
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 | |
Quote:
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 |
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: JASS:CASTING_EVENTS[sid - MINIMUM_SPELL_HANDLE_ID()] // 'Amls' - 'A000' = 4013123 // Won't fit in array |
| 01-01-2009, 11:08 PM | #11 |
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 | |
Quote:
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 | |
Quote:
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 |
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 | |
Quote:
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. |
