| 12-30-2008, 12:53 PM | #1 |
Made this script for my map. My map uses triggered effects for abilities as a convention so everything is 'under control'. This script just detects when a unit has been hit by the missile of a spell so that the effects of the ability of the missile can be applied upon impact. It works only for single target spells like Cold Arrows and Drunken Haze. It requires that all single-unit-target-missile-spells are based-off some default single-unit-target-missile-spell with a buff - preferable without any stuns/disables. The buff must last >= CHECK_INTERVAL_DURATION. The spell itself isn't supposed to have any effect and the buff is used only for identification; since the effects are triggered. And, yeah, there are very few abilities that have missiles, and apply buffs effects that can be eliminated (set to 0 effect values). Examples are Drunken Haze and Acid Bomb; uou can also use orb effects - or some of them - and just keep them from being auto-casted (trigger that issues "unautocast" ["poisonarrowson", etc.] when it the bearing unit is issued "autocast" ["poisonarrowsoff", etc.]). That's 2, or 7 (there are 5 castable orb abilities) if you include the orbs. This is enough for my map since any unit in my map will have a max of only 5 abilities. EDIT: Apparently, Black Arrow doesn't work on heroes, so that's -1 usable ability. EDIT2: After further testing, I have found out that only Drunken Haze, Acid Bomb, Cold Arrow, Poison Arrow, and Incinerate (Arrow) can be used; and the last 3 deal attack damage, too... suggestions? EDIT3: +Parasite, +Shadow Strike (doesn't show "(dmg)!" if all effect values are set to 0.00) This is an alternative to dummy-missiles-moved-by-triggers. It is to be used for my 'simpler' spells. Criticism? I have not figured out how to do this for AoE missiles (like Cluster Rocket or Pocket Factory). Suggestions? JASS:library SpellMissileImpact initializer Init globals private constant real CHECK_INTERVAL_DURATION = 0.033 private constant integer MAX_IDS = 100 private constant integer MAX_INSTANCES = 8190 //Ability-type (Id) indexes. private integer array gINT_AbilityId[MAX_IDS] private integer array gINT_BuffId[MAX_IDS] private SpellMissileImpactFunc array gFUNC[MAX_IDS] private real array gR_MaxTravelDuration[MAX_IDS] private integer gINT_CountId = 0 //Missile (instance) indexes. private integer array gINT_IdIndex[MAX_INSTANCES] private integer array gINT_Level[MAX_INSTANCES] private unit array gU_Source[MAX_INSTANCES] private unit array gU_Target[MAX_INSTANCES] private real array gR_TravelDuration[MAX_INSTANCES] private integer gINT_CountInst = 0 //Functional objects. private trigger gTRIG_Cast = null private timer gTIM = null endglobals function CreateSpellMissileImpactFunction takes integer abilityId, integer buffId, SpellMissileImpactFunc func, real maxDuration returns boolean local integer INT_Index = 0 if gINT_CountId < MAX_IDS then loop exitwhen INT_Index == gINT_CountId if gINT_AbilityId[INT_Index] == abilityId then return false endif set INT_Index = INT_Index + 1 endloop set gINT_AbilityId[gINT_CountId] = abilityId set gINT_BuffId[gINT_CountId] = buffId set gFUNC[gINT_CountId] = func set gR_MaxTravelDuration[gINT_CountId] = maxDuration set gINT_CountId = gINT_CountId + 1 return true endif return false endfunction function DestroySpellMissileImpactFunction takes integer abilityId, integer buffId, SpellMissileImpactFunc func, real maxDuration returns boolean local integer INT_Index = 0 local integer INT_Index2 = 0 //Find a matching Id. loop exitwhen INT_Index == gINT_CountId if gINT_AbilityId[INT_Index] == abilityId then //Destroy the Id. set gINT_CountId = gINT_CountId - 1 set gINT_AbilityId[INT_Index] = gINT_AbilityId[gINT_CountId] set gINT_BuffId[INT_Index] = gINT_BuffId[gINT_CountId] set gFUNC[INT_Index] = gFUNC[gINT_CountId] set gR_MaxTravelDuration[INT_Index] = gR_MaxTravelDuration[gINT_CountId] //Destroy all missile instances using the Id. loop exitwhen INT_Index2 == gINT_CountInst if gINT_IdIndex[INT_Index2] == INT_Index then set gINT_CountInst = gINT_CountInst - 1 set gINT_IdIndex[INT_Index2] = gINT_IdIndex[gINT_CountInst] set gINT_Level[INT_Index2] = gINT_Level[gINT_CountInst] set gU_Source[INT_Index2] = gU_Source[gINT_CountInst] set gU_Target[INT_Index2] = gU_Target[gINT_CountInst] set gR_TravelDuration[INT_Index2] = gR_TravelDuration[gINT_CountInst] set gU_Source[gINT_CountInst] = null set gU_Target[gINT_CountInst] = null if INT_Index2 < gINT_CountInst - 1 then set INT_Index2 = INT_Index2 - 1 endif endif //Match the gINT_IdIndex of instances using the last Id, which was adjusted, to the Id's index (which is INT_Index). if gINT_IdIndex[INT_Index2] == gINT_CountId then set gINT_IdIndex[INT_Index2] = INT_Index endif set INT_Index2 = INT_Index2 + 1 endloop return true endif set INT_Index = INT_Index + 1 endloop return false endfunction function interface SpellMissileImpactFunc takes unit source, unit target, integer level returns nothing private function UpdateMissile takes nothing returns nothing local integer INT_Index = 0 loop exitwhen INT_Index >= gINT_CountInst set gR_TravelDuration[INT_Index] = gR_TravelDuration[INT_Index] + CHECK_INTERVAL_DURATION if gR_TravelDuration[INT_Index] <= gR_MaxTravelDuration[gINT_IdIndex[INT_Index]] then //Execute. if GetUnitAbilityLevel(gU_Target[INT_Index], gINT_BuffId[gINT_IdIndex[INT_Index]]) > 0 then //Run function. call UnitRemoveAbility(gU_Target[INT_Index], gINT_BuffId[gINT_IdIndex[INT_Index]]) call gFUNC[gINT_IdIndex[INT_Index]].execute(gU_Source[INT_Index], gU_Target[INT_Index], gINT_Level[INT_Index]) //Destroy. set gINT_CountInst = gINT_CountInst - 1 set gINT_IdIndex[INT_Index] = gINT_IdIndex[gINT_CountInst] set gINT_Level[INT_Index] = gINT_Level[gINT_CountInst] set gU_Source[INT_Index] = gU_Source[gINT_CountInst] set gU_Target[INT_Index] = gU_Target[gINT_CountInst] set gR_TravelDuration[INT_Index] = gR_TravelDuration[gINT_CountInst] set gU_Source[gINT_CountInst] = null set gU_Target[gINT_CountInst] = null if INT_Index < gINT_CountInst - 1 then set INT_Index = INT_Index - 1 endif endif else //Destroy. set gINT_IdIndex[gINT_CountInst] = gINT_IdIndex[gINT_CountInst] - 1 set gINT_IdIndex[INT_Index] = gINT_IdIndex[gINT_CountInst] set gINT_Level[INT_Index] = gINT_Level[gINT_CountInst] set gU_Source[INT_Index] = gU_Source[gINT_CountInst] set gU_Target[INT_Index] = gU_Target[gINT_CountInst] set gR_TravelDuration[INT_Index] = gR_TravelDuration[gINT_CountInst] set gU_Source[gINT_CountInst] = null set gU_Target[gINT_CountInst] = null if INT_Index < gINT_CountInst - 1 then set INT_Index = INT_Index - 1 endif endif set INT_Index = INT_Index + 1 endloop endfunction private function SetupImpactFunction takes nothing returns boolean local integer INT_AbilityId = GetSpellAbilityId() local integer INT_Index = 0 if gINT_CountInst < MAX_INSTANCES then loop exitwhen INT_Index == gINT_CountId if gINT_AbilityId[INT_Index] == INT_AbilityId then set gINT_IdIndex[gINT_CountInst] = INT_Index set gU_Source[gINT_CountInst] = GetTriggerUnit() set gU_Target[gINT_CountInst] = GetSpellTargetUnit() set gINT_Level[gINT_CountInst] = GetUnitAbilityLevel(gU_Source[gINT_CountInst], INT_AbilityId) set gR_TravelDuration[gINT_CountInst] = 0.00 set gINT_CountInst = gINT_CountInst + 1 return false endif set INT_Index = INT_Index + 1 endloop endif return false endfunction private function Init takes nothing returns nothing set gTRIG_Cast = CreateTrigger() call TriggerRegisterAnyUnitEventBJ(gTRIG_Cast, EVENT_PLAYER_UNIT_SPELL_EFFECT) call TriggerAddCondition(gTRIG_Cast, Condition(function SetupImpactFunction)) set gTIM = CreateTimer() call TimerStart(gTIM, CHECK_INTERVAL_DURATION, true, function UpdateMissile) endfunction endlibrary |
| 12-30-2008, 03:34 PM | #2 |
For AoE effects (a la Cluster Rockets), I am considering using Pocket Factory: make the factory life duration 0.01 and detect when it dies, then execute functions from this prompt... But this is only 1 ability. Maybe I can also use Healing Spray and check when units in the target AoE get the buff... but this entails working with groups... |
| 12-30-2008, 07:34 PM | #3 | |
Quote:
Oh, and also, there's a lot of arrays there. Perhaps you could use structs instead? |
| 12-31-2008, 02:50 AM | #4 |
Each missile instance is identified by a caster, a target, and a level. It is MUI among casters since the functions can discriminate by the gU_Caster[] element. Now, I consider a caster having casted two missiles simultaneously (both are yet traveling at the some time). But each can be identified by level - if they are so different as by level - and which hits first and which hits next doesn't really matter, I think, as long as the caster gets its ability effect on the target. And I considered using structs are first, but I figured that these aren't really LEGO-block-type objects. But, recently, I have again considered using structs since each abilityId can have multiple impact functions associated with it, and these can be 'put-in-a-box'... |
| 12-31-2008, 10:42 AM | #5 | ||
Quote:
Quote:
I cannot find the part where it does that differentiation. The only check it does seems to be this: JASS:GetUnitAbilityLevel(gU_Target[INT_Index], gINT_BuffId[gINT_IdIndex[INT_Index]]) > 0 If both caster A and caster B uses the same spell, looking for the same buff... (then caster A's function might be run, despite the fact the unit was hit by caster B's missile). Right? |
| 12-31-2008, 03:04 PM | #6 |
Oh, you're right. I thought you meant differentiation in the impact function. I didn't consider discriminating in missile hits. Any suggestions on how to account? What if I use a trigger that detects when the targets take damage and check if they have the spell's buff on them? That way, I will be able to identify the source by GetEventDamageSource(). Most of the 'usable' abilities - all but Drunken Haze - have 'Damage Dealt' fields. Do damage events register for these spells even when these are set to 0.00? |
| 01-01-2009, 07:09 AM | #7 |
This works. But is it good? EDIT: Figured that it isn't MUI in casts from the same unit. Suggestions on how to account for this? EDIT2: Changed moments of calling the SpellMissile.RefreshDamageTrigger(). It is no longer called in SpellMissile.Impact() method which was just contradictory to its purpose and it is now mutually independent of SpellMissile.Cancel() and SpellMissile.Create(). JASS:library AbilityMissileImpact initializer Init ////////////////////////////////////////////////////////////////////////////////////////////////////// // AbilityMissile //==================================================================================================== // // Proven list of base Warcraft III abilities that have missiles, that apply buffs that do not // disable their targets and can be nullified of effects, and that deal damage. // // 1) Acid Bomb // 2) Parasite (Autocast) // 3) Shadow Strike (doesn't show "(Dmg)!" if everything is set to 0.00) // 4) Cold/Frost Arrow (Arrow) (Autocast) // 5) Poison Arrow (Arrow) (Autocast) // 6) Incinerate (Arrow) (Autocast) // // * (Arrow)'s deal the casting unit's attack damage. // * Impact functions of (Arrow) (Autocasts) will not be effected when the // bearing unit uses the ability by attacking. ////////////////////////////////////////////////////////////////////////////////////////////////////// globals private constant integer MAX_DATA = 8190 private constant integer MAX_MISSILES = 8190 private constant real DAMAGE_TRIGGER_REFRESH_INTERVAL_DURATION = 0.033 endglobals function interface AbilityMissileImpactFunc takes unit caster, unit target, integer level returns nothing struct AbilityMissileData[MAX_DATA] public static AbilityMissileData array Inst[MAX_DATA] public static integer intCountInst = 0 private integer intInstIndex public integer intAbilityId public integer intBuffId public AbilityMissileImpactFunc func //Constructor. public static method Create takes integer abilityId, integer buffId, AbilityMissileImpactFunc func returns boolean local integer INT_Index = 0 //Check if the ability already has a missile impact function. If it does, do not register another one for it. loop exitwhen INT_Index == AbilityMissileData.intCountInst if AbilityMissileData.Inst[INT_Index].intAbilityId == abilityId then return false endif set INT_Index = INT_Index + 1 endloop //Create and allocate. set AbilityMissileData.Inst[AbilityMissileData.intCountInst] = AbilityMissileData.allocate() set AbilityMissileData.Inst[AbilityMissileData.intCountInst].intAbilityId = abilityId set AbilityMissileData.Inst[AbilityMissileData.intCountInst].intBuffId = buffId set AbilityMissileData.Inst[AbilityMissileData.intCountInst].func = func set AbilityMissileData.Inst[AbilityMissileData.intCountInst].intInstIndex = AbilityMissileData.intCountInst set AbilityMissileData.intCountInst = AbilityMissileData.intCountInst + 1 return true endmethod //Deconstructor. public static method Destroy takes integer abilityId returns boolean local integer INT_Index = 0 //Find the instance that matches the query, if any, and destroy it. loop exitwhen INT_Index == AbilityMissileData.intCountInst if AbilityMissileData.Inst[INT_Index].intAbilityId == abilityId then //Cancel all pending missile impact function executions of the destroyed. call AbilityMissile.Cancel(AbilityMissileData.Inst[INT_Index]) //Destroy and recycle index. set AbilityMissileData.intCountInst = AbilityMissileData.intCountInst - 1 set AbilityMissileData.Inst[AbilityMissileData.intCountInst].intInstIndex = AbilityMissileData.Inst[INT_Index].intInstIndex set AbilityMissileData.Inst[INT_Index] = AbilityMissileData.Inst[AbilityMissileData.intCountInst] set AbilityMissileData.Inst[AbilityMissileData.intCountInst] = 0 endif set INT_Index = INT_Index + 1 endloop return false endmethod endstruct struct AbilityMissile[MAX_MISSILES] private static AbilityMissile array Inst[MAX_MISSILES] private static integer intCountInst = 0 private integer intInstIndex private unit uCaster private unit uTarget private integer intLevel private AbilityMissileData data //Destroys all AbilityMissile instances of the specified AbilityMissile type. public static method Cancel takes integer data returns nothing local integer INT_Index = 0 loop exitwhen INT_Index >= AbilityMissile.intCountInst if AbilityMissile.Inst[INT_Index].data == data then set AbilityMissile.intCountInst = AbilityMissile.intCountInst - 1 set AbilityMissile.Inst[AbilityMissile.intCountInst].intInstIndex = AbilityMissile.Inst[INT_Index].intInstIndex set AbilityMissile.Inst[INT_Index] = AbilityMissile.Inst[AbilityMissile.intCountInst] set AbilityMissile.Inst[AbilityMissile.intCountInst] = 0 //Have the loop not bypass the new object in the instance. set INT_Index = INT_Index - 1 endif set INT_Index = INT_Index + 1 endloop endmethod //Applies Ability missile impact functions when a Ability missile acquires its target. private static method Impact takes nothing returns boolean local unit U_Target = GetTriggerUnit() local unit U_Caster = GetEventDamageSource() local integer INT_Index = 0 //Find the Ability missile that has impacted - if the damage event is, indeed, that of a Ability missile impact - //and apply the impact function on the target, from the caster, and by the level. loop exitwhen INT_Index == AbilityMissile.intCountInst //If the damage source is the query instance's .uCaster; and the target, the query instance's .uTarget; and the target //has the buff of the Ability; then the event is a Ability missile impact event and the associated impact function //is to be applied. if U_Caster == AbilityMissile.Inst[INT_Index].uCaster and U_Target == AbilityMissile.Inst[INT_Index].uTarget and GetUnitAbilityLevel(U_Target, AbilityMissile.Inst[INT_Index].data.intBuffId) > 0 then //Remove the buff. The ability's buff is not really the effect; it is only a marker. After this use, it is useless. call UnitRemoveAbility(U_Target, AbilityMissile.Inst[INT_Index].data.intBuffId) //Run the impact function on the target, from the caster, and by the level. call AbilityMissile.Inst[INT_Index].data.func.execute(U_Caster, U_Target, AbilityMissile.Inst[INT_Index].intLevel) //Destroy the AbilityMissile instance and recycle it. set AbilityMissile.intCountInst = AbilityMissile.intCountInst - 1 set AbilityMissile.Inst[AbilityMissile.intCountInst].intInstIndex = AbilityMissile.Inst[INT_Index].intInstIndex set AbilityMissile.Inst[INT_Index] = AbilityMissile.Inst[AbilityMissile.intCountInst] set AbilityMissile.Inst[AbilityMissile.intCountInst] = 0 set U_Target = null set U_Caster = null return false endif set INT_Index = INT_Index + 1 endloop set U_Target = null set U_Caster = null return false endmethod //Creates Ability missile impact functions when abilities that have them are casted. private static method Create takes nothing returns boolean local integer INT_AbilityId = GetSpellAbilityId() local integer INT_Index = 0 //Find the Ability missile impact function that corresponds to the casted ability, if any, and create an executor //(AbilityMissile instance) for it. loop exitwhen INT_Index == AbilityMissileData.intCountInst if AbilityMissileData.Inst[INT_Index].intAbilityId == INT_AbilityId then set AbilityMissile.Inst[AbilityMissile.intCountInst] = AbilityMissile.allocate() set AbilityMissile.Inst[AbilityMissile.intCountInst].uCaster = GetTriggerUnit() set AbilityMissile.Inst[AbilityMissile.intCountInst].uTarget = GetSpellTargetUnit() call TriggerRegisterUnitEvent(AbilityMissile.trigDamage, AbilityMissile.Inst[AbilityMissile.intCountInst].uTarget, EVENT_UNIT_DAMAGED) set AbilityMissile.Inst[AbilityMissile.intCountInst].intLevel = GetUnitAbilityLevel(AbilityMissile.Inst[AbilityMissile.intCountInst].uCaster, INT_AbilityId) set AbilityMissile.Inst[AbilityMissile.intCountInst].data = AbilityMissileData.Inst[INT_Index] set AbilityMissile.Inst[AbilityMissile.intCountInst].intInstIndex = AbilityMissile.intCountInst set AbilityMissile.intCountInst = AbilityMissile.intCountInst + 1 return false endif set INT_Index = INT_Index + 1 endloop return false endmethod private static trigger trigDamage = CreateTrigger() //Clears the damage-detecting trigger of events for units that are no longer targets of the //AbilityMissiles, so that AbilityMissile.Impact() will not fire and loop redundantly whenever these take damage. //This function is done outside destroy-actions in the and AbilityMissile.Impact() to minimize crashes - people say //that destroying a trigger during its execution can crash the game... I don't know... private static method RefreshDamageTrigger takes nothing returns nothing local integer INT_Index = 0 call DestroyTrigger(AbilityMissile.trigDamage) set AbilityMissile.trigDamage = CreateTrigger() loop exitwhen INT_Index == AbilityMissile.intCountInst call TriggerRegisterUnitEvent(AbilityMissile.trigDamage, AbilityMissile.Inst[INT_Index].uTarget, EVENT_UNIT_DAMAGED) set INT_Index = INT_Index + 1 endloop call TriggerAddCondition(AbilityMissile.trigDamage, Condition(function AbilityMissile.Impact)) endmethod public static method Initialize takes nothing returns nothing local trigger TRIG_Cast = CreateTrigger() local timer TIM = CreateTimer() call TriggerRegisterAnyUnitEventBJ(TRIG_Cast, EVENT_PLAYER_UNIT_SPELL_EFFECT) call TriggerAddCondition(TRIG_Cast, Condition(function AbilityMissile.Create)) call AbilityMissile.RefreshDamageTrigger() call TimerStart(TIM, DAMAGE_TRIGGER_REFRESH_INTERVAL_DURATION, true, function AbilityMissile.RefreshDamageTrigger) set TRIG_Cast = null set TIM = null endmethod endstruct private function Init takes nothing returns nothing call AbilityMissile.Initialize() endfunction endlibrary |
| 01-01-2009, 07:16 AM | #8 |
couldn't you just change JASS:
private function Init takes nothing returns nothing
call SpellMissile.Initialize()
endfunction
to JASS:private function Init takes nothing returns nothing local trigger TRIG_Cast = CreateTrigger() local timer TIM = CreateTimer() call TriggerRegisterAnyUnitEventBJ(TRIG_Cast, EVENT_PLAYER_UNIT_SPELL_EFFECT) call TriggerAddCondition(TRIG_Cast, Condition(function SpellMissile.Create)) call SpellMissile.RefreshDamageTrigger() call TimerStart(TIM, DAMAGE_TRIGGER_REFRESH_INTERVAL_DURATION, true, function SpellMissile.RefreshDamageTrigger) set TRIG_Cast = null set TIM = null endfunction Then remove Spellsjdksajdksjd.initialize() |
| 01-01-2009, 08:22 AM | #9 |
I did that for the sake of keeping the stuff private to the struct. |
| 01-01-2009, 10:26 AM | #10 | |
Quote:
I think it is, actually. Unless you have the same detection buff for multiple abilities, that is (if you use level 3 storm bolt four times, it doesn't matter which storm bolt function that runs, since they all do the same thing). |
| 01-09-2009, 07:54 AM | #11 |
Changed how it is scripted. And now it actually works with Cluster Rockets and Healing Spray. The missiles of these 2 abilities don't work like ordinary missiles, it seems; they ALWAYS fly for a certain duration (0.75s, estimated). I don't know if I should require the indication of whether or not the abilityId of a type of missile is based of either of these two abilities on 'Register...'. Currently, it just checks if this is true for a missile when the missile is casted by getting the caster's current orderId and comparing it with those of the two abilities. Which method should I adopt? JASS:library AbilityMissileImpact initializer Init globals //Changeable constants. private constant integer MAX_INSTANCES = 8190 private constant real COUNTDOWN_INTERVAL_DURATION = 0.033 private constant integer MAX_DATA = 8190 private integer CLUSTER_ROCKETS_ORDER_ID = 852652 private integer HEALING_SPRAY_ORDER_ID = 852664 private constant real CLUSTERROCKETS_HEALINGSPRAY_MISSILE_DURATION = 0.75 endglobals //IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII// //IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII// //IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII// //IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII// //Wrappers. private keyword Data //Basic wrapper. function RegisterAbilityMissileImpactFunction takes integer abilityId, boolean unitTarget, integer buffId, real missileSpeed, AbilityMissileImpactFunc func returns boolean return Data.Create(abilityId, unitTarget, buffId, missileSpeed, func) endfunction //Specialized wrapper for unit target abilities. function RegisterAbilityMissileImpactFunctionUnitTarget takes integer abilityId, integer buffId, AbilityMissileImpactFunc func returns boolean return Data.Create(abilityId, true, buffId, 0.00, func) endfunction //Specialized wrapper for point target abilities. function RegisterAbilityMissileImpactFunctionPointTarget takes integer abilityId, real missileSpeed, AbilityMissileImpactFunc func returns boolean return Data.Create(abilityId, false, 0, missileSpeed, func) endfunction //Specialized wrapper for point target abilities ***based of Cluster Rockets or Healing Spray***. function RegisterAbilityMissileImpactFunctionPointTargetEx takes integer abilityId, AbilityMissileImpactFunc func returns boolean return Data.Create(abilityId, false, 0, 0.00, func) endfunction //Deconstructor. function UnregisterAbilityMissileImpactFunction takes integer abilityId returns boolean return Data.Destroy(abilityId) endfunction //IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII// //IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII// //IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII// //IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII// private keyword Unit private keyword Point private struct Missile[MAX_INSTANCES] public unit uCaster public integer intLevel public Data data private static method Prepare takes nothing returns boolean local integer INT_AbilityId = GetSpellAbilityId() local unit U_Target = GetSpellTargetUnit() local location LOC_Target = GetSpellTargetLoc() local integer INT_Index = 0 if Unit.intCountInst + Point.intCountInst < MAX_INSTANCES then loop exitwhen INT_Index == Data.intCountInst if Data.Inst[INT_Index].intAbilityId == INT_AbilityId then if Data.Inst[INT_Index].boolUnitTarget then call Unit.Create(GetTriggerUnit(), U_Target, Data.Inst[INT_Index]) else call Point.Create(GetTriggerUnit(), GetLocationX(LOC_Target), GetLocationY(LOC_Target), Data.Inst[INT_Index]) endif set U_Target = null call RemoveLocation(LOC_Target) set LOC_Target = null return false endif set INT_Index = INT_Index + 1 endloop endif set U_Target = null call RemoveLocation(LOC_Target) set LOC_Target = null return false endmethod public static method Initialize takes nothing returns nothing local trigger TRIG = CreateTrigger() call TriggerRegisterAnyUnitEventBJ(TRIG, EVENT_PLAYER_UNIT_SPELL_EFFECT) call TriggerAddCondition(TRIG, Condition(function Missile.Prepare)) set TRIG = null endmethod endstruct private struct Unit extends Missile private static Unit array Inst public static integer intCountInst = 0 public static method Cancel takes Data data returns nothing local integer INT_Index = 0 loop exitwhen INT_Index >= Unit.intCountInst if Unit.Inst[INT_Index].data == data then set Unit.intCountInst = Unit.intCountInst - 1 set Unit.Inst[INT_Index] = Unit.Inst[Unit.intCountInst] set Unit.Inst[Unit.intCountInst] = 0 call Unit.RefreshTrigger() set INT_Index = INT_Index - 1 endif set INT_Index = INT_Index + 1 endloop endmethod private static method Impact takes nothing returns boolean local unit U_Source = GetEventDamageSource() local unit U_Target = GetTriggerUnit() local integer INT_Index = 0 loop exitwhen INT_Index >= Unit.intCountInst if Unit.Inst[INT_Index].uCaster == U_Source and Unit.Inst[INT_Index].uTarget == U_Target and GetUnitAbilityLevel(U_Target, Unit.Inst[INT_Index].data.intBuffId) > 0 then call UnitRemoveAbility(U_Target, Unit.Inst[INT_Index].data.intBuffId) call Unit.Inst[INT_Index].data.func.execute(U_Source, U_Target, 0.00, 0.00, Unit.Inst[INT_Index].intLevel) set Unit.intCountInst = Unit.intCountInst - 1 set Unit.Inst[INT_Index] = Unit.Inst[Unit.intCountInst] set Unit.Inst[Unit.intCountInst] = 0 call Unit.RefreshTrigger.execute() set INT_Index = INT_Index - 1 endif set INT_Index = INT_Index + 1 endloop return false endmethod private static trigger trig = CreateTrigger() private static method RefreshTrigger takes nothing returns nothing local integer INT_Index = 0 call TriggerSleepAction(0.00) call DestroyTrigger(Unit.trig) set Unit.trig = CreateTrigger() loop exitwhen INT_Index == Unit.intCountInst call TriggerRegisterUnitEvent(Unit.trig, Unit.Inst[INT_Index].uTarget, EVENT_UNIT_DAMAGED) set INT_Index = INT_Index + 1 endloop call TriggerAddCondition(Unit.trig, Condition(function Unit.Impact)) endmethod public static method PrepareTrigger takes nothing returns nothing call TriggerAddCondition(Unit.trig, Condition(function Unit.Impact)) endmethod private unit uTarget public static method Create takes unit caster, unit target, Data data returns nothing set Unit.Inst[Unit.intCountInst] = Unit.allocate() set Unit.Inst[Unit.intCountInst].uCaster = caster set Unit.Inst[Unit.intCountInst].intLevel = GetUnitAbilityLevel(caster, data.intAbilityId) set Unit.Inst[Unit.intCountInst].uTarget = target set Unit.Inst[Unit.intCountInst].data = data set Unit.intCountInst = Unit.intCountInst + 1 call TriggerRegisterUnitEvent(Unit.trig, target, EVENT_UNIT_DAMAGED) endmethod endstruct private struct Point extends Missile private static Point array Inst public static integer intCountInst = 0 public static method Cancel takes Data data returns nothing local integer INT_Index = 0 loop exitwhen INT_Index >= Unit.intCountInst if Point.Inst[INT_Index].data == data then set Point.intCountInst = Point.intCountInst - 1 set Point.Inst[INT_Index] = Point.Inst[Point.intCountInst] set Point.Inst[Point.intCountInst] = 0 call Point.ManageTimer(false) set INT_Index = INT_Index - 1 endif set INT_Index = INT_Index + 1 endloop endmethod private static method Impact takes nothing returns nothing local integer INT_Index = 0 loop exitwhen INT_Index >= Point.intCountInst set Point.Inst[INT_Index].rDuration = Point.Inst[INT_Index].rDuration - COUNTDOWN_INTERVAL_DURATION if Point.Inst[INT_Index].rDuration <= 0.00 then call Point.Inst[INT_Index].data.func.execute(Point.Inst[INT_Index].uCaster, null, Point.Inst[INT_Index].rXTarget, Point.Inst[INT_Index].rYTarget, Point.Inst[INT_Index].intLevel) set Point.intCountInst = Point.intCountInst - 1 set Point.Inst[INT_Index] = Point.Inst[Point.intCountInst] set Point.Inst[Point.intCountInst] = 0 call Point.ManageTimer(false) set INT_Index = INT_Index - 1 endif set INT_Index = INT_Index + 1 endloop endmethod private static timer tim = CreateTimer() private static method ManageTimer takes boolean create returns nothing if Point.intCountInst == 0 then call PauseTimer(Point.tim) elseif create and Point.intCountInst == 1 then call TimerStart(Point.tim, COUNTDOWN_INTERVAL_DURATION, true, function Point.Impact) endif endmethod private real rXTarget private real rYTarget private real rDuration public static method Create takes unit caster, real x, real y, Data data returns nothing local integer INT_OrderId = GetUnitCurrentOrder(caster) set Point.Inst[Point.intCountInst] = Point.allocate() set Point.Inst[Point.intCountInst].uCaster = caster set Point.Inst[Point.intCountInst].intLevel = GetUnitAbilityLevel(caster, data.intAbilityId) set Point.Inst[Point.intCountInst].rXTarget = x set Point.Inst[Point.intCountInst].rYTarget = y if INT_OrderId == CLUSTER_ROCKETS_ORDER_ID or INT_OrderId == HEALING_SPRAY_ORDER_ID then set Point.Inst[Point.intCountInst].rDuration = CLUSTERROCKETS_HEALINGSPRAY_MISSILE_DURATION else set Point.Inst[Point.intCountInst].rDuration = SquareRoot(Pow(GetUnitX(caster) - x, 2) + Pow(GetUnitY(caster) - y, 2))/data.rMissileSpeed endif set Point.Inst[Point.intCountInst].data = data set Point.intCountInst = Point.intCountInst + 1 call Point.ManageTimer(true) endmethod endstruct function interface AbilityMissileImpactFunc takes unit caster, unit target, real x, real y, integer level returns nothing private struct Data[MAX_DATA] public static Data array Inst[MAX_DATA] public static integer intCountInst = 0 public integer intAbilityId public boolean boolUnitTarget public integer intBuffId = 0 public real rMissileSpeed = 0.00 public AbilityMissileImpactFunc func public static method Create takes integer abilityId, boolean unitTarget, integer buffId, real missileSpeed, AbilityMissileImpactFunc func returns boolean local integer INT_Index = 0 if Data.intCountInst < MAX_DATA then loop exitwhen INT_Index == Data.intCountInst if Data.Inst[INT_Index].intAbilityId == abilityId then return false endif set INT_Index = INT_Index + 1 endloop set Data.Inst[Data.intCountInst] = Data.allocate() set Data.Inst[Data.intCountInst].intAbilityId = abilityId set Data.Inst[Data.intCountInst].boolUnitTarget = unitTarget if unitTarget then set Data.Inst[Data.intCountInst].intBuffId = buffId else set Data.Inst[Data.intCountInst].rMissileSpeed = missileSpeed endif set Data.Inst[Data.intCountInst].func = func set Data.intCountInst = Data.intCountInst + 1 return true endif return false endmethod public static method Destroy takes integer abilityId returns boolean local integer INT_Index = 0 loop exitwhen INT_Index == Data.intCountInst if Data.Inst[INT_Index].intAbilityId == abilityId then if Data.Inst[INT_Index].boolUnitTarget then call Unit.Cancel(Data.Inst[INT_Index]) else call Point.Cancel(Data.Inst[INT_Index]) endif set Data.intCountInst = Data.intCountInst - 1 set Data.Inst[INT_Index] = Data.Inst[Data.intCountInst] set Data.Inst[Data.intCountInst] = 0 return true endif set INT_Index = INT_Index + 1 endloop return false endmethod endstruct private function Init takes nothing returns nothing call Missile.Initialize() call Unit.PrepareTrigger() endfunction endlibrary |
| 01-09-2009, 08:37 PM | #12 |
I think that triggered projectiles are a better way to go. |
| 01-10-2009, 08:28 AM | #13 |
I made this script for my simpler spells... to keep it simple. |
| 01-10-2009, 09:11 AM | #14 |
How is it simpler if a triggered projectile system would likely take less lines of code? |
| 01-10-2009, 11:58 AM | #15 |
'Coz you just rely on the system to move the missiles instead of moving it manually. |
