![]() |
#1 |
Procrastination Incarnate
Development Director
|
![]() I feel the documentation in the library explains it well enough, so there's not much I can add here.
__________________It requires Table, by the way. ![]() library SpellEvent initializer Init requires Table //***************************************************************** //* SPELL EVENT LIBRARY 1.3 //* //* written by: Anitarf //* requires: -Table //* //* Maps with many triggered spells require many triggers that run //* on spell events. Whenever a spell is cast, all those triggers //* need to be evaluated by the game even though only one actually //* needs to run. This library has been written to reduce the //* number of triggers in such maps; instead of having a trigger //* per spell, this library contains a single trigger which then //* runs only the code associated with the spell that's actually //* being cast. //* //* Perhaps more significant than the marginal speed gain is the //* feature that allows you to access all the spell event //* responses from all spell events, something that the native //* functions senselessly do not support. With this system you can //* for example easily get the target unit of the spell on the //* casting finish event. //* //* All functions following the Response function interface that //* is defined at the start of this library can be used to respond //* to spell events. You can register a response with one of the //* following functions, each for a different spell event: //* //* function RegisterSpellChannelResponse takes integer spellId, Response r returns nothing //* function RegisterSpellCastResponse takes integer spellId, Response r returns nothing //* function RegisterSpellEffectResponse takes integer spellId, Response r returns nothing //* function RegisterSpellFinishResponse takes integer spellId, Response r returns nothing //* function RegisterSpellEndCastResponse takes integer spellId, Response r returns nothing //* //* The first event occurs at the very start of the spell, when //* the spell's casting time begins; most spells have 0 casting //* time, so in most cases this first event occurs at the same //* time as the second one, which runs when the unit actually //* begins casting a spell by starting its spell animation. The //* third event occurs when the spell effect actually takes place, //* which happens sometime into the unit's spell animation //* depending on the unit's "Animation - Cast Point" property. //* The fourth event runs if the unit finishes casting the spell //* uninterrupted, which might be important for channeling spells. //* The last event runs when the unit stops casting the spell, //* regardless of whether it finished casting or was interrupted. //* //* If you specify a spell id when registering a response then //* that response will only run when that ability is cast; only //* one function per ability per event is supported, if you //* register more responses then only the last one registered will //* be called. If, however, you pass 0 as the ability id parameter //* then the registered function will run for all spells. Up to //* 8190 functions can be registered this way for each event. //* These functions will be called before the ability's specific //* function in the order they were registered. //* //* This library provides its own event responses that work //* better than the Blizzard's bugged native cast event responses. //* They still won't work after a wait, but unlike Blizzard's //* natives they will work on all spell events. //* //* Here are usage examples for all event responses: //* //* local integer a = SpellEvent.AbilityId //* local unit u = SpellEvent.CastingUnit //* local unit t = SpellEvent.TargetUnit //* local item i = SpellEvent.TargetItem //* local destructable d = SpellEvent.TargetDestructable //* local location l = SpellEvent.TargetLoc //* local real x = SpellEvent.TargetX //* local real y = SpellEvent.TargetY //* local boolean b = SpellEvent.CastFinished //* //* SpellEvent.TargetLoc is provided for odd people who insist on //* using locations, note that if you use it you have to cleanup //* the returned location yourself. //* //* SpellEvent.CastFinished boolean is intended only for the //* EndCast event as it tells you whether the spell finished or //* was interrupted. //* //* //* Note that a few spells such as Berserk and Wind Walk behave //* somewhat differently from regular spells: they are cast //* instantly without regard for cast animation times, they do not //* interrupt the unit's current order, as well as any spell it //* may be casting. SpellEvent 1.1 now handles such spells without //* errors provided they are truly instant (without casting time). //* //* It also turned out that a few rare abilities like Charge Gold //* & Lumber trigger a spell effect event, but not any other. //* SpellEvent 1.2 no longer ignores these lone effect events. //* //* It also turned out that removing the spell ability would run //* the endcast event, so if the ability was removed from one of //* the effect callbacks the spell event data would get cleaned //* up prematurely, SpellEvent 1.3 fixes this issue. //***************************************************************** // use the RegisterSpell*Response functions to add spell event responses to the library public function interface Response takes nothing returns nothing // ================================================================ private keyword effectDone private keyword init private keyword get private keyword destroy private struct spellEvent private static HandleTable casterTable boolean effectDone=false integer AbilityId unit CastingUnit unit TargetUnit item TargetItem=null destructable TargetDestructable=null real TargetX=0.0 real TargetY=0.0 boolean CastFinished=false readonly boolean destroyWhenDone=false // Some abilities like Berserk can be cast instantly without interrupting // the caster's current order, which includes any spells the caster may // already be casting. The following member allows the system to recover // the original spellEvent when such an instant spell overwrites it. private spellEvent interrupt method operator TargetLoc takes nothing returns location return Location(.TargetX, .TargetY) endmethod private static method create takes nothing returns spellEvent return spellEvent.allocate() endmethod static method init takes nothing returns spellEvent local spellEvent s=spellEvent.allocate() set s.AbilityId = GetSpellAbilityId() set s.CastingUnit = GetTriggerUnit() set s.TargetUnit = GetSpellTargetUnit() if s.TargetUnit != null then set s.TargetX = GetUnitX(s.TargetUnit) set s.TargetY = GetUnitY(s.TargetUnit) else set s.TargetDestructable = GetSpellTargetDestructable() if s.TargetDestructable != null then set s.TargetX = GetDestructableX(s.TargetDestructable) set s.TargetY = GetDestructableY(s.TargetDestructable) else set s.TargetItem = GetSpellTargetItem() if s.TargetItem != null then set s.TargetX = GetItemX(s.TargetItem) set s.TargetY = GetItemY(s.TargetItem) else set s.TargetX = GetSpellTargetX() set s.TargetY = GetSpellTargetY() endif endif endif set s.interrupt = spellEvent.casterTable[s.CastingUnit] set spellEvent.casterTable[s.CastingUnit]=integer(s) return s endmethod method destroy takes nothing returns nothing if SpellEvent!=0 then // the library is in the middle of running callbacks, this can happen if // the spell ability gets removed from the unit in one of the callbacks set .destroyWhenDone=true return endif if .interrupt!=0 then // this spell interrupted another spell, some instant spells can do this set spellEvent.casterTable[.CastingUnit]=.interrupt else call spellEvent.casterTable.flush(.CastingUnit) endif set .CastingUnit=null call .deallocate() endmethod static method get takes unit caster returns spellEvent return spellEvent(spellEvent.casterTable[caster]) endmethod static method onInit takes nothing returns nothing set .casterTable=HandleTable.create() endmethod endstruct globals spellEvent SpellEvent=0 endglobals // ================================================================ //! textmacro spellEvent_make takes name globals private Response array $name$CallList private integer $name$CallCount=0 private Table $name$Table endglobals private function $name$Calls takes integer id returns nothing local integer i=0 local spellEvent previous=SpellEvent set SpellEvent=spellEvent.get(GetTriggerUnit()) loop exitwhen i>=$name$CallCount call $name$CallList[i].evaluate() set i=i+1 endloop if $name$Table.exists(id) then call Response($name$Table[id]).evaluate() endif if SpellEvent.destroyWhenDone then set SpellEvent=0 call spellEvent.get(GetTriggerUnit()).destroy() endif set SpellEvent=previous endfunction function RegisterSpell$name$Response takes integer spellId, Response r returns nothing if spellId==0 then set $name$CallList[$name$CallCount]=r set $name$CallCount=$name$CallCount+1 else set $name$Table[spellId]=integer(r) endif endfunction //! endtextmacro //! runtextmacro spellEvent_make("Channel") //! runtextmacro spellEvent_make("Cast") //! runtextmacro spellEvent_make("Effect") //! runtextmacro spellEvent_make("Finish") //! runtextmacro spellEvent_make("EndCast") // ================================================================ globals // Morph abilities like Metamorphosis will cause an additional spell effect // event to run when the caster morphs back to its original form. To avoid // such duplicates, SpellEvent is designed to ignore any effect event that // does not have a matching channel event preceding it. // However, there are also rare abilities, like Charge Gold&Lumber, which // only cause an effect event to run, so these events must not be ignored // even though they occur without a matching channel event. This Table // tracks ability IDs of spells that did cause a channel event so that when // a spell is cast that doesn't cause one, its effect event is not ignored. private Table CastAfterChannel endglobals private function Channel takes nothing returns nothing call spellEvent.init() call ChannelCalls(GetSpellAbilityId()) endfunction private function Cast takes nothing returns nothing call CastCalls(GetSpellAbilityId()) endfunction private function Effect takes nothing returns nothing local spellEvent s=spellEvent.get(GetTriggerUnit()) local integer id=GetSpellAbilityId() if s!=0 and not s.effectDone then set s.effectDone=true call EffectCalls(id) if not CastAfterChannel.exists(id) then set CastAfterChannel[id]=1 endif elseif not CastAfterChannel.exists(id) then set s = spellEvent.init() call EffectCalls(id) call s.destroy() endif endfunction private function Finish takes nothing returns nothing set spellEvent.get(GetTriggerUnit()).CastFinished=true call FinishCalls(GetSpellAbilityId()) endfunction private function EndCast takes nothing returns nothing call EndCastCalls(GetSpellAbilityId()) call spellEvent.get(GetTriggerUnit()).destroy() endfunction // ================================================================ private function InitTrigger takes playerunitevent e, code c returns nothing local trigger t=CreateTrigger() call TriggerRegisterAnyUnitEventBJ( t, e ) call TriggerAddAction(t, c) set t=null endfunction private function Init takes nothing returns nothing set ChannelTable=Table.create() set CastTable=Table.create() set EffectTable=Table.create() set FinishTable=Table.create() set EndCastTable=Table.create() call InitTrigger(EVENT_PLAYER_UNIT_SPELL_CHANNEL, function Channel) call InitTrigger(EVENT_PLAYER_UNIT_SPELL_CAST, function Cast) call InitTrigger(EVENT_PLAYER_UNIT_SPELL_EFFECT, function Effect) call InitTrigger(EVENT_PLAYER_UNIT_SPELL_FINISH, function Finish) call InitTrigger(EVENT_PLAYER_UNIT_SPELL_ENDCAST, function EndCast) set CastAfterChannel=Table.create() endfunction endlibrary ![]() ![]() Last edited by Anitarf : 03-09-2016 at 07:22 PM. |
![]() |
![]() |
Sponsored Links - Login to hide this ad! |
|
![]() |
#2 |
Procrastination Incarnate
Development Director
|
![]() I agree, getting all cast event responses to work for Finish and EndCast events is the main advantage here, not the speed. Regarding that, this system will definitely be faster at a certain number of spells, but it might take a lot of spells and even then the advantage will be marginal. I guess my point regarding speed is that it won't be slower (except in maps with too few spells to matter) than an individual trigger per spell, so the features the script brings don't cost you anything in terms of map performance.
__________________Edit: Added a usage example to the first post. Last edited by Anitarf : 04-08-2009 at 02:29 AM. |
![]() |
![]() |
![]() |
#3 |
Obscurity, the Art
|
![]() I really dislike the naming of "SpellEvent_GetTargetUnit()" and stuff, but I'm awful glad it flows so well with standard WC3 naming for functions. I think it might be better if instead of RegisterFinishResponse(ABILITY_ID, function OnCast), it had something like RegisterSpellResponse(ABILITY_ID, RESPONSE_TYPE_FINISHED, function OnCast). I realize it's a bit more typing, but I think it's more appropriate to change the integer in there than to change the function call you're using.
__________________Outside of that, this is awesome. Approved. EDIT: You mispelled the keyword 'library' in the first post. Please fix that. (I could do it for you, but I would prefer you to) Last edited by Rising_Dusk : 04-08-2009 at 03:10 AM. |
![]() |
![]() |
![]() |
#4 | |
oO
Join Date: Jul 2008
Posts: 580
![]() ![]() ![]() ![]() |
![]() Quote:
I agree with this The overly long function names associated with SpellEvent_blahblahblah is a pain Also, with the constant as ALL_CAPS_LIKE_THIS fits more with the warcraft event natives, which is probably a good thing to keep :) |
|
![]() |
![]() |
![]() |
#5 | |
Procrastination Incarnate
Development Director
|
![]() Quote:
|
|
![]() |
![]() |
![]() |
#6 |
User
Join Date: Apr 2009
Posts: 173
![]() |
![]() nice, does this works like vexorian's caster system spell events? which i stopped using because it seems like there is a blizzard bug which causes my spell to malfunction? or is this better?
__________________ |
![]() |
![]() |
![]() |
#7 | |
Free Software Terrorist
Technical Director
|
![]() Quote:
I don't like the blizz event response-like madness or that it has to save all event responses everytime, even though most of them are not needed or are empty. If the syntax for using event responses will be the ugly blizz-like one, I'd prefer to just use blizz' event responses rather than have to use those new hard to remember event responses that also consume extra time during events, I mean really, at least it would make it easier to port old spells to this. Not a fan of making it SpellEvent_RegisterEventResponse instead of just RegisterEventResponse, it is a little large, and public stuff are already non-scoped, but oh well. |
|
![]() |
![]() |
![]() |
#8 |
Rehabbing
|
![]() why the need for the SpellEvent prefix ?
__________________ |
![]() |
![]() |
![]() |
#9 | |
Obscurity, the Art
|
![]() Quote:
You also still haven't fixed the spelling of library in the beginning of the script. |
|
![]() |
![]() |
![]() |
#10 | |||||
Procrastination Incarnate
Development Director
|
![]() Quote:
There's also the option of only storing the event responses that don't work on the Finish and EndCast events and for the rest letting the user call the natives as needed, but the problem is that the only event responses that work on those events are GetSpellAbilityId (which the script needs to call anyway) and GetTriggerUnit (which pretty much every spell needs to call), so it's both faster to store those too as well as it standardizes all event responses to the same new format. As for this format, I think wrapper functions which resemble the original event responses and get inlined anyway are better than direct struct access; I could do something with methods/method operators, where the user functions would take the struct as a parameter and then users would call them, something to the effect of set u = GetSpell.TargetUnit()//optional@ , but that doesn't look particularly different from wrapper functions to me. Quote:
Well, in any case, I'm open for syntax suggestions, that's why this is here. Quote:
Quote:
Quote:
Last edited by Anitarf : 04-10-2009 at 01:57 PM. |
|||||
![]() |
![]() |
![]() |
#11 | |||||
Free Software Terrorist
Technical Director
|
![]() Quote:
Quote:
Quote:
Quote:
What annoys me the most is thelocation call, the others are in theory not that bad. Also, why store it as location? It could at least be helpful if it converted it to x,y already. It would be nice if you could tell it not to call the location thing. Quote:
Guess spellId and caster are constants, but there are only 4 other things, it is not that bad: ![]() struct targetType extends array static constant integer Unit = 1 static constant integer Item = 2 static constant integer Destructable = 4 static constant integer Widget = 7 static constant integer Point = 8 endstruct //... call SpellEvent_RegisterFinishResponse(ABILITY_ID, targetType.Unit +targetType.Item) //maybe it hits both things? //... local spellEvent s=spellEvent.allocate() set s.spellId=GetSpellAbilityId() set s.caster=GetTriggerUnit() if ( flag >= 8) then set s.targetLoc=GetSpellTargetLoc() set flag=flag-8 endif if ( flag >= 4) then set s.targetDest=GetSpellTargetDestructable() set flag=flag-4 endif if ( flag >= 2) then set s.targetItem=GetSpellTargetItem() set flag=flag-2 endif if ( flag >= 1) then set s.target=GetSpellTargetUnit() endif set s.finished=false set spellEvent.casterTable[s.caster]=integer(s) return s Just suggesting here. |
|||||
![]() |
![]() |
![]() |
#12 | |||
Procrastination Incarnate
Development Director
|
![]() Quote:
Quote:
Quote:
What about something like this: ![]() set s.targetUnit = GetSpellTargetUnit() if s.targetUnit == null then set s.targetDest = GetSpellTargetDestructable() if s.targetDest == null then set s.targetItem = GetSpellTargetItem() if s.targetItem == null then set l = GetSpellTargetLoc() set s.targetX = GetLocationX() set s.targetY = GetLocationY() call RemoveLocation(l) set l = null endif endif else //optional, would also be there for items and destructables but I don't feel like typing it set s.targetX = GetUnitX() set s.targetY = GetUnitY() endif |
|||
![]() |
![]() |
![]() |
#13 |
Free Software Terrorist
Technical Director
|
![]() it is annoying, it does handle allocation and deallocation, that's annoying.
__________________It is vJass, you only need one gc call. |
![]() |
![]() |
![]() |
#14 | ||
Procrastination Incarnate
Development Director
|
![]() Quote:
Quote:
Anyway, what about my last example? Would a GetSpellTargetLoc() call for point-target spells (where it's needed anyway) and no-target spells (where it isn't), but not for widget-target spells be acceptable? |
||
![]() |
![]() |
![]() |
#15 |
Free Software Terrorist
Technical Director
|
![]() Ok, do the Loc stuff.
__________________ |
![]() |
![]() |
![]() |
Thread Tools | Search this Thread |
|
|