| 07-16-2010, 07:33 AM | #1 |
A library that catches when a hero learns or unlearns an ability and allows execution of code whenever such an event happens. This uses vJass, AutoIndex, Table, TimerUtils, LinkedList, SkillLearnable and optionally SpellEvent. Library code:/** * ABILITY EVENT * by Deaod * * A library that allows execution of code whenever * a unit learns, unlearns or changes the level of * an ability. * * NOTES: * Because of the way this library currently works, * I'd like to ask all users of this library to * make sure all calls to SetUnitAbilityLevel * that affect a hero and a learnable ability * contain a valid new level for the ability, even * if its on the second call (ie. its okay to call * SetUnitAbilityLevel more than once to verify * the desired new level). * Such a verification could look like this: * * call SetUnitAbilityLevel(whichUnit, whichAbility, SetUnitAbilityLevel(whichUnit, whichAbility, desiredLevel)) * * The same applies to IncUnitAbilityLevel. Don't * use it under unverified circumstances. Better * yet, don't use it at all, and use * SetUnitAbilityLevel instead. * * Should Vexorian decide to include support for * calling a function hooked in one of its hooks, * the need for verification on the users end * becomes unnecessary (after an update to this * library of course). * * This library makes heavy use of Tables. You * might want to increase MAX_INSTANCES in Table. * This does not affect read or write speed for * Tables. * * KNOWN PROBLEMS: * This library is probably the longest library I * have ever written. It's goal is to detect any * and all possibilities through which a unit * could learn an ability. Sadly, there are so * many ways that detecting all of them would be * too complex and/or too slow. * This library does not detect usage of Chaos * based abilities. * It also does not detect learning an ability * through the fulfillment of tech dependencies. * It fails to detect a unit morphing into a unit * of the same type (TypeId, eg. 'h000'). * * CREDITS: * - Ammorth (LinkedList) * - Anitarf (SpellEvent) * - grim001 (AutoIndex) * - Vexorian (Table, JassHelper) * - MindWorX (JassNewGenPack) * - PitzerMike (JassNewGenPack) * - PipeDream (Grimoire) * - SFilip (TESH) * * Learning API: * * function interface LearnCallback takes unit learningUnit, integer learnedAbility returns nothing * Parameters: * learningUnit - the unit which learned learnedAbility * learnedAbility - the ability learningUnit learned * * function OnAbilityLearn takes integer abilityID, LearnCallback callback returns nothing * Parameters: * abilityID - whenever abilityID is learned by a unit, callback is executed * callback - function executed whenever a unit learns abilityID * * * Unlearning API: * * function interface UnlearnCallback takes unit unlearningUnit, integer unlearnedAbility returns nothing * Parameters: * unlearningUnit - the unit which unlearned unlearnedAbility * unlearnedAbility - the ability unlearningUnit unlearned * * function OnAbilityUnlearn takes integer abilityID, UnlearnCallback callback returns nothing * Parameters: * abilityID - whenever abilityID is unlearned by a unit, callback is executed * callback - function executed whenever a unit unlearns abilityID * * * Level Change API: * * function interface LevelChangeCallback takes unit levelingUnit, integer leveledAbility, integer newLevel returns nothing * Parameters: * levelingUnit - the unit that changed its ability level * leveledAbility - the ability that changed its level * newLevel - the new level of the ability * * function OnAbilityLevelChange takes integer abilityID, LevelChangeCallback callback returns nothing * Parameters: * abilityID - whenever a unit changes the level of this ability, callback is executed * callback - function executed whenever a unit changes the level of abilityID * */ library AbilityEvent requires AutoIndex, Table, TimerUtils, LinkedList, SkillLearnable, optional SpellEvent // CONFIGURATION CONSTANTS // It's okay, go ahead and modify those. globals private constant integer MAXIMUM_HERO_ABILITIES = 5 // valid values range from 1 to 5, more than that is impossible in the current version of WC3 (hardcoded limit) // only decrease when you are absolutely sure no hero will have more than this many abilities he can learn. private constant integer RETRAINING_ABILITY = 'Aret' // As custom abilities based off of 'Aret' wont work, you should never change this. endglobals // Don't touch anything blow. function interface LearnCallback takes unit learningUnit, integer abilityLearned returns nothing function interface UnlearnCallback takes unit unlearningUnit, integer abilityUnlearned returns nothing function interface LevelChangeCallback takes unit levelingUnit, integer abilityLeveled, integer newLevel returns nothing globals private Table AbilityLearnEvents private Table AbilityUnlearnEvents private Table AbilityLevelChangeEvents private integer array Ability private integer AbilityCount=0 private Table AbilityTable private rect WorldBounds private group InitGroup private integer TempAbilityID private LearnCallback TempLearnCallback private LevelChangeCallback TempLevelChangeCallback endglobals private function AddAbilityToStack takes integer abilityID returns nothing if not AbilityTable.exists(abilityID) then set Ability[AbilityCount]=abilityID set AbilityTable[abilityID]=AbilityCount set AbilityCount=AbilityCount+1 endif endfunction // struct for all heroes // only detects hero abilities private struct Hero integer array ability[MAXIMUM_HERO_ABILITIES] integer array abilityLevel[MAXIMUM_HERO_ABILITIES] integer abilityCount=0 Table abilityTable private boolean selectedSkill=false private integer selectedSkillId private static method createFilter takes unit u returns boolean return IsUnitType(u, UNIT_TYPE_HERO) and not IsCheckingHeroSkill() endmethod private method onCreate takes nothing returns nothing local integer i set abilityTable=Table.create() set i=0 loop exitwhen i>=AbilityCount if HeroCanLearnSkill(me, Ability[i]) and GetUnitAbilityLevel(me, Ability[i])>0 then call learnAbility(Ability[i], 1) call learnAbility(Ability[i], GetUnitAbilityLevel(me, Ability[i])) endif set i=i+1 endloop endmethod private method onDestroy takes nothing returns nothing local integer i set i=abilityCount-1 loop exitwhen i<0 call unlearnAbility(ability[i]) set i=i-1 endloop call abilityTable.destroy() endmethod method learnAbility takes integer abilityID, integer newLevel returns nothing local List list local Link link if not hasLearned(abilityID) then set ability[abilityCount]=abilityID set abilityLevel[abilityTable[abilityID]]=newLevel set abilityTable[abilityID]=abilityCount set abilityCount=abilityCount+1 // fire learn events set list=List(AbilityLearnEvents[abilityID]) if list>0 then set link=list.first loop exitwhen link==0 call LearnCallback(link.data).evaluate(me, abilityID) set link=link.next endloop endif elseif abilityLevel[abilityTable[abilityID]]!=newLevel then set abilityLevel[abilityTable[abilityID]]=newLevel // fire level change events set list=List(AbilityLevelChangeEvents[abilityID]) if list>0 then set link=list.first loop exitwhen link==0 call LevelChangeCallback(link.data).evaluate(me, abilityID, newLevel) set link=link.next endloop endif endif endmethod method unlearnAbility takes integer abilityID returns nothing local integer index=abilityTable[abilityID] local List list local Link link // fire unlearn events set list=List(AbilityUnlearnEvents[abilityID]) if list>0 then set link=list.first loop exitwhen link==0 call UnlearnCallback(link.data).evaluate(me, abilityID) set link=link.next endloop endif set abilityCount=abilityCount-1 set ability[index]=ability[abilityCount] set abilityLevel[index]=abilityLevel[abilityCount] set abilityLevel[abilityCount]=0 call abilityTable.flush(abilityID) endmethod method hasLearned takes integer abilityID returns boolean return abilityTable.exists(abilityID) endmethod private static method OnLearnAbility takes nothing returns nothing local thistype s=thistype[GetTriggerUnit()] call s.learnAbility(GetLearnedSkill(), GetLearnedSkillLevel()) endmethod private static method AfterSelectSkill takes nothing returns nothing local thistype s=thistype(GetTimerData(GetExpiredTimer())) if s.selectedSkill==false then // request succeeded else // request failed set s.selectedSkill=false endif call ReleaseTimer(GetExpiredTimer()) endmethod private static method OnSelectSkill takes unit u, integer abilityID returns nothing local thistype s local timer t if HeroCanLearnSkill(u, abilityID) then set s=thistype[u] set s.selectedSkill=true set s.selectedSkillId=abilityID set t=NewTimer() call SetTimerData(t, s) call TimerStart(t, 0, false, function thistype.AfterSelectSkill) endif endmethod private static method OnSpellEffect takes nothing returns nothing local unit u local integer abilityId local thistype s local integer i static if LIBRARY_SpellEvent then set u=SpellEvent.CastingUnit set abilityId=SpellEvent.AbilityId else set u=GetTriggerUnit() set abilityId=GetSpellAbilityId() endif if abilityId==RETRAINING_ABILITY then set s=thistype[u] // no need to check s, only heroes with learned abilities can cast this ability, // and thats a subset of all units this struct has an instance for set i=s.abilityCount-1 loop exitwhen i<0 call s.unlearnAbility(s.ability[i]) set i=i-1 endloop set u=null elseif abilityId==0 then set s=thistype[u] if s.selectedSkill and GetUnitAbilityLevel(u, s.selectedSkillId)-1==s.abilityLevel[s.abilityTable[s.selectedSkillId]] then // SelectHeroSkill was used, most certainly set s.selectedSkill=false call s.learnAbility(s.selectedSkillId, GetUnitAbilityLevel(u, s.selectedSkillId)) else // cant tell for sure, but most probably the unit learned a skill over the menu, which has its separate event endif endif set u=null endmethod private static method onInit takes nothing returns nothing local trigger t set t=CreateTrigger() call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_HERO_SKILL) call TriggerAddAction(t, function thistype.OnLearnAbility) static if LIBRARY_SpellEvent then call RegisterSpellEffectResponse(0, thistype.OnSpellEffect) else set t=CreateTrigger() call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT) call TriggerAddAction(t, function thistype.OnSpellEffect) endif set AbilityLearnEvents=Table.create() set AbilityUnlearnEvents=Table.create() set AbilityLevelChangeEvents=Table.create() set AbilityTable=Table.create() set WorldBounds=GetWorldBounds() static if LIBRARY_GroupUtils then set InitGroup=ENUM_GROUP else set InitGroup=CreateGroup() endif endmethod implement AutoCreate implement AutoDestroy endstruct // hero specifc hook. hook SelectHeroSkill Hero.OnSelectSkill // struct for all units // detects all unit abilities private struct Unit Table ability Table abilityLevel integer abilityCount=0 Table abilityTable private static method createFilter takes unit u returns boolean return not IsCheckingHeroSkill() endmethod private method onCreate takes nothing returns nothing local integer i set ability=Table.create() set abilityLevel=Table.create() set abilityTable=Table.create() // which abilities does the unit have set i=0 loop exitwhen i>=AbilityCount if not HeroCanLearnSkill(me, Ability[i]) and GetUnitAbilityLevel(me, Ability[i])>0 then call learnAbility(Ability[i], 1) call learnAbility(Ability[i], GetUnitAbilityLevel(me, Ability[i])) endif set i=i+1 endloop endmethod private method onDestroy takes nothing returns nothing local integer i set i=abilityCount-1 loop exitwhen i<0 call unlearnAbility(ability[i]) set i=i-1 endloop call ability.destroy() call abilityLevel.destroy() call abilityTable.destroy() endmethod method learnAbility takes integer abilityID, integer newLevel returns nothing local List list local Link link if not hasLearned(abilityID) then set ability[abilityCount]=abilityID set abilityLevel[abilityTable[abilityID]]=newLevel set abilityTable[abilityID]=abilityCount set abilityCount=abilityCount+1 // fire learn events set list=List(AbilityLearnEvents[abilityID]) if list>0 then set link=list.first loop exitwhen link==0 call LearnCallback(link.data).evaluate(me, abilityID) set link=link.next endloop endif elseif abilityLevel[abilityTable[abilityID]]!=newLevel then set abilityLevel[abilityTable[abilityID]]=newLevel // fire level change events set list=List(AbilityLevelChangeEvents[abilityID]) if list>0 then set link=list.first loop exitwhen link==0 call LevelChangeCallback(link.data).evaluate(me, abilityID, newLevel) set link=link.next endloop endif endif endmethod method unlearnAbility takes integer abilityID returns nothing local integer index=abilityTable[abilityID] local List list local Link link // fire unlearn events set list=List(AbilityUnlearnEvents[abilityID]) if list>0 then set link=list.first loop exitwhen link==0 call UnlearnCallback(link.data).evaluate(me, abilityID) set link=link.next endloop endif set abilityCount=abilityCount-1 set ability[index]=ability[abilityCount] set abilityLevel[index]=abilityLevel[abilityCount] set abilityLevel[abilityCount]=0 call abilityTable.flush(abilityID) endmethod method hasLearned takes integer abilityID returns boolean return abilityTable.exists(abilityID) endmethod implement AutoCreate implement AutoDestroy endstruct // hook responses private function OnAddAbility takes unit u, integer abilityID returns nothing if not IsCheckingHeroSkill() and GetUnitAbilityLevel(u, abilityID)<=0 then if HeroCanLearnSkill(u, abilityID) then call Hero[u].learnAbility(abilityID, 1) else call Unit[u].learnAbility(abilityID, 1) endif endif endfunction private function OnSetAbilityLevel takes unit u, integer abilityID, integer newLevel returns nothing if newLevel>0 and GetUnitAbilityLevel(u, abilityID)>0 then if HeroCanLearnSkill(u, abilityID) then call Hero[u].learnAbility(abilityID, newLevel) else call Unit[u].learnAbility(abilityID, newLevel) endif endif endfunction private function OnRemoveAbility takes unit u, integer abilityID returns nothing if not IsCheckingHeroSkill() and GetUnitAbilityLevel(u, abilityID)>0 then if HeroCanLearnSkill(u, abilityID) then call Hero[u].unlearnAbility(abilityID) else call Unit[u].unlearnAbility(abilityID) endif endif endfunction private function OnIncAbilityLevel takes unit u, integer abilityID returns nothing if GetUnitAbilityLevel(u, abilityID)>0 then if HeroCanLearnSkill(u, abilityID) then call Hero[u].learnAbility(abilityID, GetUnitAbilityLevel(u, abilityID)+1) else call Unit[u].learnAbility(abilityID, GetUnitAbilityLevel(u, abilityID)+1) endif endif endfunction private function OnDecAbilityLevel takes unit u, integer abilityID returns nothing if GetUnitAbilityLevel(u, abilityID)>1 then if HeroCanLearnSkill(u, abilityID) then call Hero[u].learnAbility(abilityID, GetUnitAbilityLevel(u, abilityID)-1) else call Unit[u].learnAbility(abilityID, GetUnitAbilityLevel(u, abilityID)-1) endif endif endfunction // fuck BJs private function OnAddAbilityBJ takes integer abilityID, unit u returns nothing call OnAddAbility(u, abilityID) endfunction private function OnSetAbilityLevelBJ takes integer abilityID, unit u, integer newLevel returns nothing call OnSetAbilityLevel(u, abilityID, newLevel) endfunction private function OnRemoveAbilityBJ takes integer abilityID, unit u returns nothing call OnRemoveAbility(u, abilityID) endfunction private function OnIncAbilityLevelBJ takes integer abilityID, unit u returns nothing call OnIncAbilityLevel(u, abilityID) endfunction private function OnDecAbilityLevelBJ takes integer abilityID, unit u returns nothing call OnDecAbilityLevel(u, abilityID) endfunction // register relevant hooks hook UnitAddAbility OnAddAbility hook SetUnitAbilityLevel OnSetAbilityLevel hook UnitRemoveAbility OnRemoveAbility hook IncUnitAbilityLevel OnIncAbilityLevel hook DecUnitAbilityLevel OnDecAbilityLevel hook UnitAddAbilityBJ OnAddAbilityBJ hook SetUnitAbilityLevelSwapped OnSetAbilityLevelBJ hook UnitRemoveAbilityBJ OnRemoveAbilityBJ hook UnitRemoveBuffBJ OnRemoveAbilityBJ hook IncUnitAbilityLevelSwapped OnIncAbilityLevelBJ hook DecUnitAbilityLevelSwapped OnDecAbilityLevelBJ // Morph events globals private integer array UnitType endglobals private function MorphSpellCast takes nothing returns nothing local unit u local integer id static if LIBRARY_SpellEvent then set u=SpellEvent.CastingUnit else set u=GetTriggerUnit() endif set id=GetUnitId(u) set UnitType[id]=GetUnitTypeId(u) set u=null endfunction private function MorphSpellEndCast takes nothing returns nothing local unit u local integer id local integer i local Unit U local Hero h static if LIBRARY_SpellEvent then set u=SpellEvent.CastingUnit else set u=GetTriggerUnit() endif set id=GetUnitId(u) if UnitType[id]!=GetUnitTypeId(GetTriggerUnit()) then // morph event // currently disregards units morphing into themselves, feel free to contribute your thoughts on how to detect that with as little user interaction as possible. // which abilities did the unit lose set U=Unit[u] set i=0 loop exitwhen i>=U.abilityCount if GetUnitAbilityLevel(u, U.ability[i])<=0 then call U.unlearnAbility(U.ability[i]) endif set i=i+1 endloop set h=Hero[u] set i=0 loop exitwhen i>=h.abilityCount if GetUnitAbilityLevel(u, h.ability[i])<=0 then call h.unlearnAbility(h.ability[i]) endif set i=i+1 endloop // which abilities did the unit gain set i=0 loop exitwhen i>=AbilityCount if GetUnitAbilityLevel(u, Ability[i])>0 then if HeroCanLearnSkill(u, Ability[i]) then if not h.hasLearned(Ability[i]) then call h.learnAbility(Ability[i], 1) endif call h.learnAbility(Ability[i], GetUnitAbilityLevel(u, Ability[i])) else if not U.hasLearned(Ability[i]) then call U.learnAbility(Ability[i], 1) endif call U.learnAbility(Ability[i], GetUnitAbilityLevel(u, Ability[i])) endif endif set i=i+1 endloop endif endfunction private function UpgradeFinish takes nothing returns nothing local unit u=GetTriggerUnit() local integer i local Unit U local Hero h // which abilities did the unit lose set U=Unit[u] set i=0 loop exitwhen i>=U.abilityCount if GetUnitAbilityLevel(u, U.ability[i])<=0 then call U.unlearnAbility(U.ability[i]) endif set i=i+1 endloop set h=Hero[u] set i=0 loop exitwhen i>=h.abilityCount if GetUnitAbilityLevel(u, h.ability[i])<=0 then call h.unlearnAbility(h.ability[i]) endif set i=i+1 endloop // which abilities did the unit gain set i=0 loop exitwhen i>=AbilityCount if GetUnitAbilityLevel(u, Ability[i])>0 then if HeroCanLearnSkill(u, Ability[i]) then if not h.hasLearned(Ability[i]) then call h.learnAbility(Ability[i], 1) endif call h.learnAbility(Ability[i], GetUnitAbilityLevel(u, Ability[i])) else if not U.hasLearned(Ability[i]) then call U.learnAbility(Ability[i], 1) endif call U.learnAbility(Ability[i], GetUnitAbilityLevel(u, Ability[i])) endif endif set i=i+1 endloop endfunction private function Init takes nothing returns nothing local trigger t static if LIBRARY_SpellEvent then call RegisterSpellCastResponse(0, MorphSpellCast) call RegisterSpellEndCastResponse(0, MorphSpellEndCast) else set t=CreateTrigger() call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_CAST) call TriggerAddAction(t, function MorphSpellCast) set t=CreateTrigger() call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_ENDCAST) call TriggerAddAction(t, function MorphSpellEndCast) endif set t=CreateTrigger() call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_UPGRADE_FINISH) call TriggerAddAction(t, function UpgradeFinish) endfunction // Learn Event registration private function AbilityLearnEventAddedEnum takes nothing returns boolean local unit u=GetFilterUnit() local Hero h local Unit U if GetUnitAbilityLevel(u, TempAbilityID)>0 then if HeroCanLearnSkill(u, TempAbilityID) then set h=Hero[u] if h.hasLearned(TempAbilityID) then call TempLearnCallback.evaluate(u, TempAbilityID) // fire learn event directly else call h.learnAbility(TempAbilityID, GetUnitAbilityLevel(u, TempAbilityID)) // fire learn event indirectly endif else set U=Unit[u] if U.hasLearned(TempAbilityID) then call TempLearnCallback.evaluate(u, TempAbilityID) else call U.learnAbility(TempAbilityID, GetUnitAbilityLevel(u, TempAbilityID)) endif endif endif set u=null return false endfunction function OnAbilityLearn takes integer abilityID, LearnCallback callback returns nothing local List list=List(AbilityLearnEvents[abilityID]) if list<=0 then set list=List.create() call Link.create(list, callback) set AbilityLearnEvents[abilityID]=list else call Link.createLast(list, callback) endif call AddAbilityToStack(abilityID) set TempAbilityID=abilityID set TempLearnCallback=callback call GroupEnumUnitsInRect(InitGroup, WorldBounds, function AbilityLearnEventAddedEnum) endfunction // Unlearn Event registration function OnAbilityUnlearn takes integer abilityID, UnlearnCallback callback returns nothing local List list=List(AbilityUnlearnEvents[abilityID]) if list<=0 then set list=List.create() call Link.create(list, callback) set AbilityUnlearnEvents[abilityID]=list else call Link.createLast(list, callback) endif call AddAbilityToStack(abilityID) endfunction // Level Change Event registration private function AbilityLevelChangeEventAddedEnum takes nothing returns boolean local unit u=GetFilterUnit() local Hero h local Unit U if GetUnitAbilityLevel(u, TempAbilityID)>0 then if HeroCanLearnSkill(u, TempAbilityID) then set h=Hero[u] if h.hasLearned(TempAbilityID) then call TempLevelChangeCallback.evaluate(u, TempAbilityID, GetUnitAbilityLevel(u, TempAbilityID)) // fire level change event directly else call h.learnAbility(TempAbilityID, 1) call h.learnAbility(TempAbilityID, GetUnitAbilityLevel(u, TempAbilityID)) // fire level change event indirectly endif else set U=Unit[u] if U.hasLearned(TempAbilityID) then call TempLevelChangeCallback.evaluate(u, TempAbilityID, GetUnitAbilityLevel(u, TempAbilityID)) else call U.learnAbility(TempAbilityID, 1) call U.learnAbility(TempAbilityID, GetUnitAbilityLevel(u, TempAbilityID)) endif endif endif set u=null return false endfunction function OnAbilityLevelChange takes integer abilityID, LevelChangeCallback callback returns nothing local List list=List(AbilityLevelChangeEvents[abilityID]) if list<=0 then set list=List.create() call Link.create(list, callback) set AbilityLevelChangeEvents[abilityID]=list else call Link.createLast(list, callback) endif call AddAbilityToStack(abilityID) set TempAbilityID=abilityID set TempLevelChangeCallback=callback call GroupEnumUnitsInRect(InitGroup, WorldBounds, function AbilityLevelChangeEventAddedEnum) endfunction endlibrary There is also a wrapper module made specifically for spells. Module code:/** * AUTO MODULE * for ABILITY EVENT * by Deaod * * NOTES: * This library is meant as a convenience wrapper * for spells that want to use AbilityEvent. * It shares the restrictions of AutoData and * AutoCreate (both of which are modules that * come with AutoIndex). * * CREDITS: * - grim001 (AutoIndex) * - Vexorian (TimerUtils, JassHelper) * - PitzerMike (JassNewGenPack) * - Pipedream (Grimoire) * - MindWorX (JassNewGenPack) * - SFilip (TESH) * * EXAMPLE: * struct AutoAbilityEventsTest * // you need to implement this member * private static constant integer AbilityEventAbilityId = 'A000' * * private static method create takes unit u returns thistype * // in case your struct was extended from another struct * // or interface that has a custom create method, implement * // this method * return 0 * endmethod * * private method onCreate takes nothing returns nothing * // run when a unit acquired AbilityEventAbilityId * endmethod * * private method onLevelChange takes integer newLevel returns nothing * // run when a unit that already has AbilityEventAbilityId * // changes the level of the ability * endmethod * * private method onDestroy takes nothing returns nothing * // run when a unit loses AbilityEventAbilityId, through * // dying or retraining or any of the many ways a unit can * // lose an ability. * endmethod * * // make sure to implement the module below any methods the * // module runs. * implement AbilityEventAutoModule * * endstruct */ library AbilityEventAutoModule requires AbilityEvent, AutoIndex, TimerUtils module AbilityEventAutoModule implement AutoData private static method Learner takes unit learningUnit, integer learnedAbility returns nothing local thistype this static if thistype.create.exists then set this = create(learningUnit) else set this = allocate() endif set me = learningUnit static if thistype.onCreate.exists then call onCreate() endif endmethod private static method Unlearner takes unit unlearningUnit, integer unlearnedAbility returns nothing call thistype[unlearningUnit].destroy() endmethod private static method Leveler takes unit levelingUnit, integer levelingAbility, integer newLevel returns nothing call thistype[levelingUnit].onLevelChange(newLevel) endmethod private static method onInitCallback takes nothing returns nothing call ReleaseTimer(GetExpiredTimer()) call OnAbilityLearn(thistype.AbilityEventAbilityId, thistype.Learner) call OnAbilityUnlearn(thistype.AbilityEventAbilityId, thistype.Unlearner) static if thistype.onLevelChange.exists then call OnAbilityLevelChange(thistype.AbilityEventAbilityId, thistype.Leveler) endif endmethod private static method onInit takes nothing returns nothing if thistype.AbilityEventAbilityId!=0 then call TimerStart(NewTimer(), 0, false, function thistype.onInitCallback) // fuck module initializers else call BJDebugMsg("AbilityEvents: AbilityEventAutoModule: AbilityEventsAbilityId for struct "+I2S(thistype.typeId)+" is invalid") endif endmethod endmodule endlibrary |
| 07-16-2010, 08:22 PM | #2 |
dont understand what problem with blabla = GetUnitAbilityLevel() RemoveAbility AddSkillPoints (blabla) ??? |
| 07-17-2010, 08:56 AM | #3 |
This wasnt meant to replace (un)learning an ability. It was meant to detect when a hero (un)learns a specific ability. This could be used by passive abilities. |
| 07-17-2010, 09:39 AM | #4 |
It would be sufficient for passive abilities to just check for when a hero uses an item of the retraining type, which is an incredibly simple check since most maps will only have one item of such type. A few dozen lines of code total. Instead, you have over a hundred lines of code with 5 library requirements to accomplish the same task. This just seems like overkill. |
| 07-17-2010, 11:04 AM | #5 |
Well, in order to accurately tell which abilities a hero unlearned when he used Aret, youd have to know which abilities he has learned. Also, a hero can learn an ability via script just as well as via the menu (you can use SelectHeroSkill and UnitAddAbiliy). The difference is, that only learning an ability over the menu will trigger the event EVENT_PLAYER_HERO_SKILL (or EVENT_UNIT_HERO_SKILL). SelectHeroSkill will trigger a spell event, with triggering unit being the unit that was passed to SelectHeroSkill. GetSpellAbilityId() will return 0. Such a spell event will also be triggered by manually selecting a skill over the menu. I dont know what else could also be triggering such an event, so i rely on checking if the level increased by one. That in turn requires keeping track of an ability's level. I just noticed a bug with SetUnitAbilityLevel or DecUnitAbilityLevel. Will fix it ASAP. |
| 07-17-2010, 11:21 AM | #6 | |
Quote:
Let's say I want to make a triggered passive ability. All I really need to check is the learn event and the item use event. In a large majority of maps, that will be enough. Even in maps where it isn't, it would be a lot less work for the author of that map to tweak my code to work there than it would be for everyone else using this triggered skill to import all these libraries into their map. |
| 07-19-2010, 08:31 AM | #7 |
Alright, updated. Now detects all "unit learns specifc ability" events. Other events have been removed. Now detects "unit changes level of specifc ability" events. Actually works. |
| 08-08-2010, 04:52 PM | #8 |
Another update. Could some moderator please change the actual thread title? |
| 01-15-2011, 02:52 PM | #9 |
call TimerStart(NewTimer(), 0, false, function thistype.onInitCallback) // fuck module initializers NewTimer() returns null from struct/module initializers because TimerUtils initializes through a library. Your futile attempt to bypass has failed :P |
