This was my submission to the Spell Olympics last year, I figure it's about time I submitted it. The pack consists of seven spells, for their descriptions read the documentation included with their code or download the testmap and try them out.
scopeIlluminateinitializerInit//*****************************************************************//* spell - Illuminate//*//* written by: Anitarf//* requires: -xebasic//* -xepreload//* -xecast//* -xedamage//* -ABuff (requires periodic events)//*//* -a unit or area target or instant triggerer ability//* -a unit target buff ability//* -a buff used by the buff ability//*//* description: Creates a buff on all enemy units in an area//* that gives you vision of those units and deals//* damage to them over time.//*//* technical: The vision is granted by a faerie-fire based spell//* that is cast by dummy casters, since these casters//* must be owned by the player who cast the spell this//* spell is unable to affect units that the owner of//* the casting unit can't see.//*//*****************************************************************globals//Spell settingsprivateconstantintegerSPELL_ABILITY = 'A007'//the spell that applies the ABuffprivateconstantintegerBUFF_SPELL = 'A009'//the buff spell used by the dummy casterprivateconstantstringBUFF_SPELL_ORDER = "faeriefire"privateconstantintegerBUFF_BUFF = 'B002'//the buff applied by the buff spellendglobalsprivateconstantfunctionInitialDamagetakesintegerlevelreturnsrealreturn0.0//the damage dealt when the spell is castendfunctionprivateconstantfunctionBuffApplyRadiustakesintegerlevelreturnsrealreturn100.0+50.0*level//the area in which the buff is appliedendfunctionprivateconstantfunctionBuffDurationtakesintegerlevelreturnsrealreturn30.0//the duration of the buffendfunctionprivateconstantfunctionBuffDPStakesintegerlevelreturnsrealreturn1.0+2.0*level//the damage per second that the buff dealsendfunctionprivatefunctionDamageOptionstakesxedamagespellDamagereturnsnothing//useful read: [url]http://www.wc3campaigns.net/showpost.php?p=1030046&postcount=19[/url]setspellDamage.dtype=DAMAGE_TYPE_UNIVERSALsetspellDamage.atype=ATTACK_TYPE_NORMALsetspellDamage.tag=0//the tag attached to the damage by xedamage//in this case xedamage is also used to determine which units are valid targets for the buffsetspellDamage.exception=UNIT_TYPE_STRUCTURE//deal no damage to structuressetspellDamage.visibleOnly=true//since dummy casters can't target invisible units, this spell can't affect themendfunction// END OF CALIBRATION SECTION// ================================================================globalsprivateaBuffTypeabtprivatexecastxecprivatexedamagexedendglobals//ABuff functionsprivatefunctionCreateRefreshtakesaBuffeventBuffreturnsnothingsetxec.owningplayer=GetOwningPlayer(eventBuff.caster)
setxec.level=eventBuff.levelcallxec.castOnTarget(eventBuff.target.u)
endfunctionprivatefunctionPeriodictakesaBuffeventBuffreturnsnothingcallxed.damageTarget(eventBuff.caster, eventBuff.target.u, BuffDPS(eventBuff.level)*ABuff_PERIODIC_EVENT_PERIOD)
endfunctionprivatefunctionCleanuptakesaBuffeventBuffreturnsnothinglocalintegeri=0loop//in case the buff is destroyed immediately when it's applied, wait until the buff spell is castexitwhenGetUnitAbilityLevel(eventBuff.target.u, BUFF_BUFF)>0//safety timeout, in case the buff spell was already cast but then the buff was removed somehowexitwheni>5callTriggerSleepAction(0.0)
seti=i+1endloopcallUnitRemoveAbility(eventBuff.target.u, BUFF_BUFF)
endfunction// ================================================================globalsprivategrouptempg = CreateGroup()
privateboolexprtempbxprivateunittempcasterprivateintegertemplevelendglobalsprivatefunctionTargetstakesnothingreturnsbooleanlocalunitu=GetFilterUnit()
ifIsUnitInRangeXY(u, GetUnitX(u), GetUnitY(u), BuffApplyRadius(templevel)) andxed.allowedTarget(tempcaster,u) thencallxed.damageTarget(tempcaster, u, InitialDamage(templevel))
callABuffApply(abt, u, tempcaster, BuffDuration(templevel), templevel, 0 )
endifsetu=nullreturnfalseendfunctionprivatefunctionSpellEffecttakesnothingreturnsnothinglocalunitulocallocationlifGetSpellAbilityId() == SPELL_ABILITYthensettempcaster=GetTriggerUnit()
settemplevel=GetUnitAbilityLevel(tempcaster, SPELL_ABILITY)
setu = GetSpellTargetUnit()
ifu == nullthensetl = GetSpellTargetLoc()
ifGetLocationX(l)==0.0andGetLocationY(l)==0.0then//no-target castcallGroupEnumUnitsInRange(tempg,GetUnitX(tempcaster),GetUnitY(tempcaster),BuffApplyRadius(templevel)+XE_MAX_COLLISION_SIZE,tempbx)
else//point-target castcallGroupEnumUnitsInRange(tempg,GetLocationX(l),GetLocationY(l),BuffApplyRadius(templevel)+XE_MAX_COLLISION_SIZE,tempbx)
endifcallRemoveLocation(l)
setl = nullelse//unit-target castcallGroupEnumUnitsInRange(tempg,GetUnitX(u),GetUnitY(u),BuffApplyRadius(templevel)+XE_MAX_COLLISION_SIZE,tempbx)
setu = nullendifendifendfunctionprivatefunctionInittakesnothingreturnsnothing//init spellcast triggerlocaltriggertr = CreateTrigger()
callTriggerRegisterAnyUnitEventBJ( tr, EVENT_PLAYER_UNIT_SPELL_EFFECT )
callTriggerAddAction( tr, functionSpellEffect )
//init buff filtersettempbx=Condition(functionTargets)
//init xecast setxec=xecast.create()
setxec.abilityid=BUFF_SPELLsetxec.orderstring=BUFF_SPELL_ORDER//init xedamagesetxed=xedamage.create()
callDamageOptions(xed)
//init aBuffTypecallXE_PreloadAbility(BUFF_SPELL)
setabt=aBuffType.create()
setabt.eventCreate = ABuffEvent_Create.CreateRefreshsetabt.eventRefresh = ABuffEvent_Refresh.CreateRefreshsetabt.eventCleanup = ABuffEvent_Cleanup.Cleanupsetabt.eventPeriodic = ABuffEvent_Periodic.Periodicendfunctionendscope
Glacial Wall:
scopeGlacialWallinitializerInit//*****************************************************************//* spell - Glacial Wall//*//* written by: Anitarf//* requires: -xebasic//* -xepreload//* -TimerUtils//* -LineSegment//* -ABuff//*//* -a point target triggerer ability//* -a unit to be used as the wall//* -a chaos ability that makes the wall unit//* -a self-targetting aura for displaying the buff//* -a buff that gets displayed by the aura//*//* description: Creates a wall of ice at a target point that//* is perpendicular to the facing of the caster.//* Enemy units approaching the wall will be slowed//* for a short duration.//*//* technical: The unit used to make the wall may be created at//* an offset from the intended point due to pathing.//* For that reason, the unit gets forcefuly moved to//* the intended position after it is created; for that//* to work, the unit must have a movement speed. Since//* it has a movement speed, players can order it to//* move. In order to prevent players from doing that,//* it must be made unselectable, but if we use locust//* to do that then the unit will no longer be treated//* as an obstacle. Luckily, a trick exists that allows//* locusted units to still be treated as obstacles//* (and also be attacked), it works by adding locust//* to a unit after it has been transformed with a//* chaos ability, this spell uses this trick.//*//*****************************************************************globals//Spell settingsprivateconstantintegerSPELL_ABILITY = 'A004'//the spell abilityprivateconstantrealSPELL_PERIOD = 0.5//how often is wall's buff applied on nearby unitsprivateconstantintegerCHAOS_ABILITY = 'S000'//the chaos transform ability that makes wall unitsprivateconstantintegerBUFF_ABILITY = 'A005'//the self-targetting aura that displays the buff and slows unitsprivateconstantintegerBUFF_BUFF = 'Bfro'//the buff applied by the self-targetting aura//Visual settingsprivateconstantrealWALL_UNIT_MAX_OFFSET = 48.0//adds a bit of randomness to wall unit placementprivateconstantbooleanWALL_UNIT_RANDOM_FACING = true//if true, will choose a random facing angle for wall unitsprivateconstantrealWALL_UNIT_FACING = 0.0//the facing of wall units relative to the caster's (in degrees)endglobals//spell statsprivateconstantfunctionWallDurationtakesintegerlevelreturnsrealreturn3.0+3.0*level//how long the wall lasts; should be longer than the time it takes to spawn all the wall unitsendfunctionprivateconstantfunctionWallLengthtakesintegerlevelreturnsrealreturn250.0+level*250.0//the distance between the two farthest wall unitsendfunctionprivateconstantfunctionWallUnitIntervaltakesintegerlevelreturnsrealreturn0.1//the time between wall units being createdendfunctionprivateconstantfunctionWallUnitCounttakesintegerlevelreturnsintegerreturn5+4*level//number of units per wall, must be more than 1endfunctionprivatefunctionBuffTargetstakesunitinRange, playerwallOwnerreturnsbooleanreturnnot(IsUnitType(inRange, UNIT_TYPE_FLYING)) andIsUnitEnemy(inRange, wallOwner) andGetWidgetLife(inRange)>0.405//units affected by the buffendfunctionprivateconstantfunctionBuffApplyRadiustakesintegerlevelreturnsrealreturn128.0//the distance from the wall at which the buff is appliedendfunctionprivateconstantfunctionBuffDurationtakesintegerlevelreturnsrealreturn3.0//the duration of the buffendfunction// END OF CALIBRATION SECTION// ================================================================privatekeywordinstanceglobalsprivateaBuffTypeabtprivatetimerslowprivateinstancearrayinstancesprivateintegerinstanceCount = 0privategrouptempg = CreateGroup()
privateboolexprtempbxprivateinstancetempsiendglobals//Applying the wall buffprivatefunctionTargetstakesnothingreturnsbooleanreturnBuffTargets(GetFilterUnit(), tempsi.owner)
endfunctionprivatefunctionPeriodictakesnothingreturnsnothinglocalintegeri=0localrealrlocalunituloopexitwheni>=instanceCountsettempsi = instances[i]
setr = tempsi.segments/2.0callGroupEnumUnitsInRangeOfSeg(tempg, tempsi.x-tempsi.dx*r,tempsi.y-tempsi.dy*r, tempsi.x+tempsi.dx*r,tempsi.y+tempsi.dy*r, BuffApplyRadius(tempsi.level), tempbx)
loopsetu=FirstOfGroup(tempg)
exitwhenu==nullcallABuffApply(abt, u, tempsi.caster, BuffDuration(tempsi.level), tempsi.level, 0 )
callGroupRemoveUnit(tempg, u)
endloopseti=i+1endloopendfunction// ================================================================//ABuff functionsprivatefunctionCreatetakesaBuffeventBuffreturnsnothingcallUnitAddAbility(eventBuff.target.u, BUFF_ABILITY)
callSetUnitAbilityLevel(eventBuff.target.u, BUFF_ABILITY, eventBuff.level)
callUnitMakeAbilityPermanent(eventBuff.target.u, true, BUFF_ABILITY) //prevent morphing from removing the abilityendfunctionprivatefunctionRefreshtakesaBuffeventBuffreturnsnothingcallSetUnitAbilityLevel(eventBuff.target.u, BUFF_ABILITY, eventBuff.level)
endfunctionprivatefunctionCleanuptakesaBuffeventBuffreturnsnothingcallUnitRemoveAbility(eventBuff.target.u, BUFF_ABILITY)
callUnitRemoveAbility(eventBuff.target.u, BUFF_BUFF)
endfunction// ================================================================//Wall unitsprivatefunctionSpellExpiretakesnothingreturnsnothinglocalinstancesi = instance(GetTimerData(GetExpiredTimer()))
callsi.destroy() //timer gets cleaned up in the onDestroy methodendfunctionprivatefunctionSpawnUnittakesinstancesi, realfacing, integersidereturnsnothinglocalrealoffset=GetRandomReal(-WALL_UNIT_MAX_OFFSET, WALL_UNIT_MAX_OFFSET)
localrealx=si.x+si.dx*si.segments/2.0*side+Cos(si.angle)*offsetlocalrealy=si.y+si.dy*si.segments/2.0*side+Sin(si.angle)*offsetlocalunituifWALL_UNIT_RANDOM_FACINGthensetfacing=GetRandomReal(0,360)
endifsetu=CreateUnit(si.owner, XE_DUMMY_UNITID, x, y, facing)
callUnitAddAbility(u, CHAOS_ABILITY) //chaos transformcallUnitAddAbility(u, 'Aloc') //locust; makes unit unselectable, but thanks to chaos transformation it still blocks pathingcallIssueImmediateOrder(u, "holdposition")
callSetUnitX(u, x) //override unit's pathing to put it in positioncallSetUnitY(u, y)
callGroupAddUnit(si.wallunits, u)
setu = nullendfunctionprivatefunctionSpawnSegmenttakesnothingreturnsnothinglocalinstancesi = instance(GetTimerData(GetExpiredTimer()))
localrealfacing = si.angle*bj_RADTODEG+WALL_UNIT_FACINGcallSpawnUnit(si, facing, 1)
ifsi.segments!=0thencallSpawnUnit(si, facing, -1)
endififsi.segments>=WallUnitCount(si.level)-1then//wait the remaining durationcallTimerStart(si.t, WallDuration(si.level)-((si.segments/2)*WallUnitInterval(si.level)), false, functionSpellExpire)
else//wait for the next intervalsetsi.segments=si.segments+2callTimerStart(si.t, WallUnitInterval(si.level), false, functionSpawnSegment)
endifendfunctionprivatefunctionDestroyWallEnumtakesnothingreturnsnothinglocalunitu = GetEnumUnit()
localrealx = GetUnitX(u)
localrealy = GetUnitY(u)
callKillUnit(u) //this triggers a pathing check it seemscallSetUnitX(u, x) //override unit's pathing to put it back in positioncallSetUnitY(u, y)
setu= nullendfunction// ================================================================//Spell instanceprivatestructinstanceprivateintegerindexrealxrealyrealdxrealdyrealangleintegersegments=0unitcasterplayerownerintegerleveltimertgroupwallunitsstaticmethodcreatetakesunitcaster, integerlevel, realtargetx, realtargetyreturnsinstancelocalinstancesi=instance.allocate()
localreallength=WallLength(level)
localrealcount=WallUnitCount(level)
setsi.angle=Atan2(targety-GetUnitY(caster), targetx-GetUnitX(caster))
setsi.caster=castersetsi.owner=GetOwningPlayer(caster)
setsi.level=levelsetsi.x=targetxsetsi.y=targetysetsi.dx=Sin(si.angle)*length/(count-1) //offsets between unitssetsi.dy=-Cos(si.angle)*length/(count-1)
ifcount/2==count/2.0then//even, start with one segmentsetsi.segments=1endififsi.wallunits==nullthen//create a group if this is the first time this instance id is usedsetsi.wallunits=CreateGroup()
endifsetsi.t=NewTimer()
callSetTimerData(si.t, integer(si))
callTimerStart(si.t, 0.0, false, functionSpawnSegment)
ifinstanceCount==0then//slow timersetslow=NewTimer()
callTimerStart(slow, SPELL_PERIOD, true, functionPeriodic)
endifsetinstances[instanceCount]=sisetsi.index=instanceCountsetinstanceCount=instanceCount+1returnsiendmethodmethodonDestroytakesnothingreturnsnothingsetinstanceCount=instanceCount-1setinstances[this.index]=instances[instanceCount]
setinstances[instanceCount].index=this.indexcallForGroup(this.wallunits, functionDestroyWallEnum)
callGroupClear(this.wallunits) //the next time this instance id is used we won't need to create a groupcallReleaseTimer(this.t)
ifinstanceCount==0thencallReleaseTimer(slow)
endifendmethodendstruct// ================================================================privatefunctionSpellEffecttakesnothingreturnsnothinglocalintegerlvllocallocationlifGetSpellAbilityId() == SPELL_ABILITYthensetlvl =GetUnitAbilityLevel(GetTriggerUnit(), SPELL_ABILITY)
setl = GetSpellTargetLoc()
callinstance.create(GetTriggerUnit(), lvl, GetLocationX(l), GetLocationY(l))
callRemoveLocation(l)
setl = nullendifendfunctionprivatefunctionInittakesnothingreturnsnothing//init spellcast triggerlocaltriggertr = CreateTrigger()
callTriggerRegisterAnyUnitEventBJ( tr, EVENT_PLAYER_UNIT_SPELL_EFFECT )
callTriggerAddAction( tr, functionSpellEffect )
//init buff filtersettempbx=Condition(functionTargets)
//init aBuffTypecallXE_PreloadAbility(BUFF_ABILITY)
callXE_PreloadAbility(CHAOS_ABILITY)
setabt=aBuffType.create()
setabt.ignoreAsBuff = truesetabt.eventCreate = ABuffEvent_Create.Createsetabt.eventRefresh = ABuffEvent_Refresh.Refreshsetabt.eventCleanup = ABuffEvent_Cleanup.Cleanupendfunctionendscope
Lightning Blast:
scopeLightningBlastinitializerInit//*****************************************************************//* spell - Lightning Blast//*//* written by: Anitarf//* requires: -GroupGet//* -xebasic//* -xedamage//* -TimerUtils//*//* -a unit target triggerer ability//*//* description: Summons lightning from the sky which forks until//* it hits all of the affected units, damaging them.//*//*****************************************************************globals//Spell settingsprivateconstantintegerSPELL_ABILITY = 'A006'//the spell abilityprivateconstantintegerMAX_TARGETS = 7//the maximum number of units ever affected by the spell//Visual settingsprivateconstantstringLIGHTNING_TYPE = "CHIM"//the type of lightning usedprivateconstantrealLIGHTNING_DURATION = 0.25//how long the lightning effect persistsprivateconstantrealMAX_START_OFFSET = 256.0//how far away from the target can a lightning startprivateconstantrealMAX_NODE_OFFSET = 128.0//randomization of node positionsprivateconstantrealSTART_HEIGHT = 1024.0//the height where the lightning strikes from// At what percentage of the height of the previous node does a node forkprivateconstantrealFORK_HEIGHT_FACTOR = 0.55//should be more than 0 and less than 1// When lightning forks, each fork gets a random number of targets, this ensures a more even split when there are many targetsprivateconstantintegerMAX_FORK_TARGETCOUNT_DIFFERENCE = 2//should be greater than 0endglobalsprivateconstantfunctionTargetRadiustakesintegerlevelreturnsrealreturn512.0//The radius in which to look for targetsendfunctionprivateconstantfunctionTargetMaxCounttakesintegerlevelreturnsintegerreturnlevel*2+1//Maximum number of targets affectedendfunctionprivateconstantfunctionDamagetakesintegerlevelreturnsrealreturn50.0+25.0*levelendfunctionprivatefunctionDamageOptionstakesxedamagespellDamagereturnsnothing//useful read: [url]http://www.wc3campaigns.net/showpost.php?p=1030046&postcount=19[/url]setspellDamage.dtype=DAMAGE_TYPE_UNIVERSALsetspellDamage.atype=ATTACK_TYPE_NORMALsetspellDamage.tag=0//the tag attached to the damage by xedamagesetspellDamage.exception=UNIT_TYPE_STRUCTUREcallspellDamage.useSpecialEffect(GetAbilityEffectById(SPELL_ABILITY, EFFECT_TYPE_TARGET, 0), "origin")
endfunction// END OF CALIBRATION SECTION// ================================================================//lightning nodesprivatekeywordinstanceglobalsprivatexedamagexedendglobalsprivatestructnodeprivatenodeparent=0privatenodechildA=0privatenodechildB=0privaterealx=0.0privaterealy=0.0privaterealh=0.0privatelightninglightstaticmethodcreatetakesinstancesi, nodeparent, integerstart, integerend, realheightreturnsnodelocalnoden=node.allocate()
localintegeri=startsetn.parent=parent//get average target positionloopsetn.x=n.x+GetUnitX(si.targets[i])
setn.y=n.y+GetUnitY(si.targets[i])
exitwheni==endseti=i+1endloopsetn.x=n.x/(end-start+1)
setn.y=n.y/(end-start+1)
ifstart!=endthen//to be split further//correct position for starting offsetsetn.h=height*FORK_HEIGHT_FACTORsetn.x=n.x+(si.startx-si.hitx)*n.h/si.starthsetn.y=n.y+(si.starty-si.hity)*n.h/si.starth//randomize positionsetn.h=n.h+GetRandomReal(-MAX_NODE_OFFSET, MAX_NODE_OFFSET)*n.h/si.starthsetn.x=n.x+GetRandomReal(-MAX_NODE_OFFSET, MAX_NODE_OFFSET)*n.h/si.starthsetn.y=n.y+GetRandomReal(-MAX_NODE_OFFSET, MAX_NODE_OFFSET)*n.h/si.starth//split the targetsifMAX_FORK_TARGETCOUNT_DIFFERENCE>=(end-start+1-2) thenseti=GetRandomInt(start, end-1)
elseseti=(end-start+1-2-MAX_FORK_TARGETCOUNT_DIFFERENCE)
seti=GetRandomInt(start+(i/2), end-1-(i/2))
endif//create child nodessetn.childA=node.create(si,n, start,i, n.h )
setn.childB=node.create(si,n, i+1,end, n.h )
else//terminal nodesetn.h=0//damage targetcallxed.damageTarget(si.caster, si.targets[start], Damage(si.level))
endif//create lightningifparent==0thensetn.light=AddLightningEx(LIGHTNING_TYPE, false, si.startx,si.starty,si.starth, n.x,n.y,n.h)
elsesetn.light=AddLightningEx(LIGHTNING_TYPE, false, parent.x,parent.y,parent.h, n.x,n.y,n.h)
endifreturnnendmethodmethodonDestroytakesnothingreturnsnothingif.childA!=0then//if childA exists so must childBcall.childA.destroy()
call.childB.destroy()
endifcallDestroyLightning(.light)
endmethodendstruct// ================================================================globalsprivategrouptempg = CreateGroup()
privateboolexprtempbxprivateinstancetempsiprivateGroupGet_DistancedistanceendglobalsprivatefunctionTargetstakesnothingreturnsbooleanlocalunitu=GetFilterUnit()
ifxed.allowedTarget(tempsi.caster,u) andIsUnitInRangeXY(u, tempsi.hitx, tempsi.hity, TargetRadius(tempsi.level)) thensetu=nullreturntrueendifsetu=nullreturnfalseendfunctionprivatefunctionEndLightningtakesnothingreturnsnothingcallinstance(GetTimerData(GetExpiredTimer())).destroy()
endfunctionprivatestructinstanceunitarraytargets[MAX_TARGETS]
realhitx=0.0realhity=0.0realstartx=0.0realstarty=0.0realstarth=0.0nodefirstintegerlevelunitcastertimertstaticmethodcreatetakesunitcaster, integerlevel, unittargetreturnsinstancelocalinstancesi = instance.allocate()
localintegeri = 0setsi.hitx=GetUnitX(target)
setsi.hity=GetUnitY(target)
setsi.level=levelsetsi.caster=castersettempsi=sisetsi.startx=si.hitx+GetRandomReal(-MAX_START_OFFSET,MAX_START_OFFSET)
setsi.starty=si.hity+GetRandomReal(-MAX_START_OFFSET,MAX_START_OFFSET)
setsi.starth=START_HEIGHTcallGroupEnumUnitsInRange(tempg,GetUnitX(target),GetUnitY(target),TargetRadius(level) + XE_MAX_COLLISION_SIZE , tempbx)
callGroupRemoveUnit(tempg, target) //the target is selected by default, no need to keep it in groupsetbj_groupCountUnits = 0callForGroup(tempg, functionCountUnitsInGroupEnum) //Get number of units in grouploopexitwhenbj_groupCountUnits<TargetMaxCount(level) //Remove random units from the group until we get to number of targets-1setbj_groupRandomConsidered = 0setbj_groupRandomCurrentPick = nullcallForGroup(tempg, functionGroupPickRandomUnitEnum)
callGroupRemoveUnit(tempg, bj_groupRandomCurrentPick)
setbj_groupCountUnits=bj_groupCountUnits-1endloopcallGroupAddUnit(tempg, target) //readd the target to the groupsetdistance.x=GetUnitX(target)
setdistance.y=GetUnitY(target)
setsi.targets[0]=GroupGetHighestEx(tempg, distance) //get the furthest unit from the target and start the array with itloopexitwheni>=bj_groupCountUnits//sort the units into the arraycallGroupRemoveUnit(tempg, si.targets[i])
setdistance.x=GetUnitX(si.targets[i])
setdistance.y=GetUnitY(si.targets[i])
seti=i+1setsi.targets[i]=GroupGetLowestEx(tempg, distance)
endloopsetsi.first=node.create(si, 0, 0,bj_groupCountUnits, si.starth) //initiate node creation sequencesetsi.t=NewTimer()
callSetTimerData(si.t, integer(si))
callTimerStart(si.t, LIGHTNING_DURATION, false, functionEndLightning)
returnsiendmethodmethodonDestroytakesnothingreturnsnothingcallReleaseTimer(.t)
call.first.destroy() //initiate node destruction sequenceset.first=0endmethodendstruct// ================================================================privatefunctionSpellEffecttakesnothingreturnsnothinglocalintegerlvllocalunituifGetSpellAbilityId() == SPELL_ABILITYthensetlvl =GetUnitAbilityLevel(GetTriggerUnit(), SPELL_ABILITY)
setu = GetSpellTargetUnit()
callinstance.create(GetTriggerUnit(), lvl, u)
setu = nullendifendfunctionprivatefunctionInittakesnothingreturnsnothing//init spellcast triggerlocaltriggertr = CreateTrigger()
callTriggerRegisterAnyUnitEventBJ( tr, EVENT_PLAYER_UNIT_SPELL_EFFECT )
callTriggerAddAction( tr, functionSpellEffect )
//init GroupGet comparisonsetdistance=GroupGet_Distance.create()
//init buff filtersettempbx=Condition(functionTargets)
//init xedamagesetxed=xedamage.create()
callDamageOptions(xed)
endfunctionendscope
Thornwine Armour:
scopeThornwineArmourinitializerInit//*****************************************************************//* spell - Thornwine Armour//*//* written by: Anitarf//* requires: -xepreload//* -xedamage//* -TimerUtils//* -ADamage//* -ABuff (requires damage and periodic events)//*//* -a unit target triggerer ability//* -a self-targetting aura for displaying the buff//* -a buff that gets displayed by the aura//*//* description: Creates a regenerative armor around a target unit//* that blocks incoming damage and deals damage back//* to melee attackers until it's time or life runs//* out.//*//*****************************************************************globals//Spell settingsprivateconstantintegerSPELL_ABILITY = 'A001'//the spell that applies the ABuffprivateconstantintegerBUFF_ABILITY = 'A002'//the self-targetting aura that displays the buffprivateconstantintegerBUFF_BUFF = 'B000'//the buff applied by the self-targetting auraprivateconstantintegerDAMAGE_SHIELD_PRIORITY = 5//determines the order in which shields prevent damage//Dynamicaly timed effect parametersprivateconstantstringEFFECT = "Objects\\Spawnmodels\\NightElf\\EntBirthTarget\\EntBirthTarget.mdl"privateconstantrealEFFECT_PERIOD_MINLIFE = 0.8//slowest effect spawningprivateconstantrealEFFECT_PERIOD_MAXLIFE = 0.2//fastest effect spawningprivateconstantrealEFFECT_MAXLIFE = 500.0//at what life is fastest effect spawning reachedendglobals//spell statsprivateconstantfunctionShieldStartLifetakesintegerlevelreturnsrealreturn100.0//starting shield lifeendfunctionprivateconstantfunctionShieldMaxLifetakesintegerlevelreturnsrealreturn200.0 + 100.0*level//how far the shield will regenerateendfunctionprivateconstantfunctionShieldRegenerationtakesintegerlevelreturnsrealreturn1.0+3.0*level//shield life regeneration per secondendfunctionprivateconstantfunctionShieldRefreshLifetakesintegerlevel, realremainingLifereturnsrealreturnremainingLife+ShieldStartLife(level) //what the life of a shield will be after it's applied on a unit that already has itendfunctionprivateconstantfunctionShieldDurationtakesintegerlevelreturnsrealreturn0.0//the duration of the shield, use 0.0 to make it permanent (it will still be removed if it's life runs out)endfunctionprivateconstantfunctionDamagePreventedtakesintegerlevel, realdamage, realshieldLifereturnsrealreturn20.0//don't worry if the returned value is higher than actual damage, spell code will reduce it in that caseendfunctionprivateconstantfunctionReturnDamagetakesbooleantriggered, integertagreturnsbooleanreturnnot(triggered) //does the shield return damage, this is a good way to avoid recursionendfunctionprivateconstantfunctionDamageReturnedtakesintegerlevel, realshieldLife, realpreventedreturnsrealreturnshieldLife*0.1//how much damage does the shield returnendfunctionprivatefunctionDamageOptionstakesxedamagereturnedDamagereturnsnothing//useful read: [url]http://www.wc3campaigns.net/showpost.php?p=1030046&postcount=19[/url]setreturnedDamage.dtype=DAMAGE_TYPE_UNIVERSALsetreturnedDamage.atype=ATTACK_TYPE_NORMALsetreturnedDamage.tag=0//the tag attached to the damage by xedamagesetreturnedDamage.damageAllies=truesetreturnedDamage.required=UNIT_TYPE_MELEE_ATTACKER//this example only returns melee damageendfunction// END OF CALIBRATION SECTION// ================================================================//Variable timed effectprivatekeywordshdprivatefunctionTimedEffecttakesnothingreturnsnothinglocalshds = shd(GetTimerData(GetExpiredTimer()))
localrealr = s.shdlife/EFFECT_MAXLIFE//factor between 0 and 1ifr>1.0thensetr=1.0//safety check due to unreliable user inputendifsetr=EFFECT_PERIOD_MINLIFE/((EFFECT_PERIOD_MINLIFE/EFFECT_PERIOD_MAXLIFE)*r + 1*(1-r))
callDestroyEffect( AddSpecialEffectTarget(EFFECT, s.u, "origin"))
callTimerStart(s.t, r, false, functionTimedEffect)
endfunction// ================================================================//ADamage shield structprivatestructshdextendsADamage_shield//shield that doubles as a data structtimertunituintegerlevel=0realshdlife=0realprevented=0staticmethodcreatetakesunitu, integerlevelreturnsshdlocalshds = shd.allocate(u, DAMAGE_SHIELD_PRIORITY)
sets.shdlife=ShieldStartLife(level)
sets.level = levelsets.u=usets.t=NewTimer()
callSetTimerData(s.t, integer(s))
callTimerStart(s.t, 0.0, false, functionTimedEffect)
returnsendmethodmethodrefreshtakesintegerlevelreturnsnothingset.level=levelset.shdlife=ShieldRefreshLife(level, .shdlife)
if.shdlife>ShieldMaxLife(level) thenset.shdlife=ShieldMaxLife(level)
endifendmethodmethodregentakesintegerlevelreturnsnothingset.shdlife=.shdlife+ShieldRegeneration(level)*ABuff_PERIODIC_EVENT_PERIODif.shdlife>ShieldMaxLife(level) thenset.shdlife=ShieldMaxLife(level)
endifendmethodmethoddamagedtakesunitdamageSource, realdamagereturnsrealset.prevented = DamagePrevented(.level, damage, .shdlife)
if.prevented > damagethenset.prevented = damageendifif.prevented > .shdlifethenset.prevented = .shdlifeendifset.shdlife=.shdlife-.preventedreturn.preventedendmethodmethodonDestroytakesnothingreturnsnothingcallPauseTimer(.t)
callReleaseTimer(this.t)
endmethodendstruct// ================================================================//Delayed damage return//we delay it so all damage triggers finish running before we deal new damage//all triggered spells that deal damage in response to damage being dealt should do thisglobalsprivatexedamagexedendglobalsprivatestructdamageUnitunittargetunitsourcerealdamageendstructprivatefunctionDelayedDamagetakesnothingreturnsnothinglocaldamageUnitd = damageUnit(GetTimerData(GetExpiredTimer()))
callReleaseTimer(GetExpiredTimer())
callxed.damageTarget(d.source, d.target, d.damage)
calld.destroy()
endfunction// ================================================================//ABuff functionsglobalsprivateaBuffTypeabtendglobalsprivatefunctionCreatetakesaBuffeventBuffreturnsnothingseteventBuff.data=integer(shd.create(eventBuff.target.u, eventBuff.level))
callUnitAddAbility(eventBuff.target.u, BUFF_ABILITY)
callSetUnitAbilityLevel(eventBuff.target.u, BUFF_ABILITY, eventBuff.level)
callUnitMakeAbilityPermanent(eventBuff.target.u, true, BUFF_ABILITY) //prevent morphing from removing the abilityendfunctionprivatefunctionRefreshtakesaBuffeventBuffreturnsnothingseteventBuff.data=eventBuff.olddatacallshd(eventBuff.data).refresh(eventBuff.level)
callSetUnitAbilityLevel(eventBuff.target.u, BUFF_ABILITY, eventBuff.level)
endfunctionprivatefunctionCleanuptakesaBuffeventBuffreturnsnothingcallshd(eventBuff.data).destroy()
callUnitRemoveAbility(eventBuff.target.u, BUFF_ABILITY)
callUnitRemoveAbility(eventBuff.target.u, BUFF_BUFF)
endfunctionprivatefunctionPeriodictakesaBuffeventBuffreturnsnothingcallshd(eventBuff.data).regen(eventBuff.level)
endfunctionprivatefunctionDamagedtakesaBuffeventBuff, realdamage, unitdamageSourcereturnsnothinglocalshds = shd(eventBuff.data)
localdamageUnitdlocaltimertifs.prevented>0.0andxed.allowedTarget(eventBuff.target.u, damageSource) andReturnDamage(xedamage.isInUse(), xedamage.CurrentDamageTag) thensett = NewTimer()
setd = damageUnit.create()
setd.damage = DamageReturned(eventBuff.level, s.shdlife+s.prevented, s.prevented)
setd.target = damageSourcesetd.source = s.ucallSetTimerData(t, integer(d))
callTimerStart(t, 0.0, false, functionDelayedDamage)
endififs.shdlife<=0.0thencallABuffDestroy(eventBuff)
endifendfunction// ================================================================privatefunctionSpellEffecttakesnothingreturnsnothinglocalintegerlvlifGetSpellAbilityId() == SPELL_ABILITYthensetlvl = GetUnitAbilityLevel(GetTriggerUnit(), SPELL_ABILITY)
callABuffApply(abt, GetSpellTargetUnit(), GetTriggerUnit(), ShieldDuration(lvl), lvl, 0 )
endifendfunctionprivatefunctionInittakesnothingreturnsnothing//init spellcast triggerlocaltriggertr = CreateTrigger()
callTriggerRegisterAnyUnitEventBJ( tr, EVENT_PLAYER_UNIT_SPELL_EFFECT )
callTriggerAddAction( tr, functionSpellEffect )
//init xedamagesetxed=xedamage.create()
callDamageOptions(xed)
//init aBuffTypecallXE_PreloadAbility(BUFF_ABILITY)
setabt=aBuffType.create()
setabt.ignoreAsBuff = truesetabt.eventCreate = ABuffEvent_Create.Createsetabt.eventRefresh = ABuffEvent_Refresh.Refreshsetabt.eventCleanup = ABuffEvent_Cleanup.Cleanupsetabt.eventPeriodic = ABuffEvent_Periodic.Periodicsetabt.eventDamaged = ABuffEvent_BUnitDamaged.Damagedendfunctionendscope
Razor Gale:
scopeRazorGaleinitializerInit//*****************************************************************//* spell - Razor Gale//*//* written by: Anitarf//* requires: -xebasic//* -xedamage//* -TimerUtils//*//* -a point and/or unit target channeling triggerer ability//*//* description: The casting unit glides towards a target point as//* long as it continues channeling or until it//* reaches the target point or max distance and//* deals damage to enemy units along the way.//*//* technical: The spell is designed specifically for the Warden//* model. It also disables spell sounds for the//* duration of the spell because the sound attached to//* the Warden's spell slam animation is too annoying//* otherwise. If a custom warden model is used with//* this sound removed, then the sound disabling//* feature of this spell may be turned off.//*//*****************************************************************globals//Spell settingsprivateconstantintegerSPELL_ABILITY = 'A003'//the spell abilityprivateconstantrealSPELL_PERIOD = XE_ANIMATION_PERIOD//the speed of the periodic timer that moves the casterprivateconstantintegerDAMAGE_PERIOD_FACTOR = 3//optimization option, every how many periods are new targets affected//Animation optionsprivateconstantrealANIM_SPEED_START = 1.2//the animation speed at the start of the spellprivateconstantrealANIM_SPEED_END = 0.9//the animation speed at the end of the spellprivateconstantbooleanDISABLE_SPELL_SOUND = true//may be set to false with a custom warden modelendglobals//spell statsprivateconstantfunctionGlideSpeedtakesintegerlevelreturnsrealreturn750.0//the distance that the caster travles per secondendfunctionprivateconstantfunctionGlideMaxDistancetakesintegerlevelreturnsrealreturn1000.0//what is the furthest the caster can glideendfunctionprivateconstantfunctionGlideMinDistancetakesintegerlevelreturnsrealreturn1000.0//if the target point is closer than this, the caster will still glide this farendfunctionprivateconstantfunctionDamagetakesintegerlevelreturnsrealreturn50.0+50.0*levelendfunctionprivateconstantfunctionDamageRadiustakesintegerlevelreturnsrealreturn128.0endfunctionprivatefunctionDamageOptionstakesxedamagespellDamagereturnsnothing//useful read: [url]http://www.wc3campaigns.net/showpost.php?p=1030046&postcount=19[/url]setspellDamage.dtype=DAMAGE_TYPE_UNIVERSALsetspellDamage.atype=ATTACK_TYPE_NORMALsetspellDamage.tag=0//the tag attached to the damage by xedamagesetspellDamage.exception=UNIT_TYPE_STRUCTURE//deal no damage to structuresendfunction// END OF CALIBRATION SECTION// ================================================================//Sliding and damageprivatekeywordinstanceglobalsprivatexedamagexedprivatetimerslideprivateinstancearrayinstancesprivateintegerinstanceCount = 0privategrouptempg = CreateGroup()
privateboolexprtempbxprivaterealtempxprivaterealtempyprivateinstancetempsiendglobalsprivatefunctionTargetstakesnothingreturnsbooleanlocalunitu=GetFilterUnit()
ifnot(IsUnitInGroup(u, tempsi.affected)) andIsUnitInRangeXY(u, tempx, tempy, DamageRadius(tempsi.level)) thencallGroupAddUnit(tempsi.affected, u)
setu=nullreturntrueendifsetu=nullreturnfalseendfunctionprivatefunctionPeriodictakesnothingreturnsnothinglocalintegeri=0loopexitwheni>=instanceCountsettempsi = instances[i]
settempx = GetUnitX(tempsi.caster)+tempsi.dxsettempy = GetUnitY(tempsi.caster)+tempsi.dysettempsi.time=tempsi.time-SPELL_PERIODiftempsi.time>0.0andIsTerrainWalkable(tempx,tempy) thencallSetUnitX(tempsi.caster, tempx)
callSetUnitY(tempsi.caster, tempy)
iftempsi.whenToDamage <= 0thensettempsi.whenToDamage=DAMAGE_PERIOD_FACTORcallGroupEnumUnitsInRange(tempg,tempx,tempy,DamageRadius(tempsi.level) + XE_MAX_COLLISION_SIZE , tempbx)
callxed.damageGroup(tempsi.caster, tempg, Damage(tempsi.level)) //empties the groupendifsettempsi.whenToDamage=tempsi.whenToDamage-1seti=i+1else//if instance is on the list it should still be active so it's safe to call finishcalltempsi.finish()
endifendloopendfunction// ================================================================//AnimationsprivatefunctionAnimation_Finishtakesnothingreturnsnothinglocalinstancesi=instance(GetTimerData(GetExpiredTimer()))
callSetUnitTimeScale(si.caster, 1.0)
callsi.destroy() //this will release the expired timer, so no need to do it here endfunctionprivatefunctionAnimation_Childtakesnothingreturnsnothinglocalinstancesi=instance(GetTimerData(GetExpiredTimer()))
callSetUnitFlyHeight(si.caster, 0.0, 48.0/0.125*si.timescale)
callReleaseTimer(GetExpiredTimer())
endfunctionprivatefunctionAnimationtakesnothingreturnsnothinglocalinstancesi=instance(GetTimerData(GetExpiredTimer()))
localtimertifsi.activethen//go into next animation cyclesett = NewTimer()
callSetTimerData(t, integer(si))
callSetUnitTimeScale(si.caster, si.timescale)
callSetUnitAnimationByIndex(si.caster, 6)
callSetUnitFlyHeight(si.caster, 48.0, 48.0/0.125*si.timescale)
callTimerStart(t, 0.125/si.timescale, false, functionAnimation_Child) //run secondary function in this intervalcallTimerStart(GetExpiredTimer(), 0.35/si.timescale, false, functionAnimation) //run next intervalsetsi.timescale=si.timescale+si.dtimescale*0.35/si.timescale//how the time scale changes during the intervalelse//let the animation finishcallSetUnitTimeScale(si.caster, 1.5)
callTimerStart(GetExpiredTimer(), 0.45, false, functionAnimation_Finish) //allow animation to finishendifendfunction// ================================================================//Spell instanceprivatestructinstanceprivateintegerindexprivatetimert//this timer needs to be on a per-caster basisrealtimescalerealdtimescalebooleanactive = truebooleaninterrupted = falseintegerwhenToDamage = DAMAGE_PERIOD_FACTORunitcasterintegerlevelrealdxrealdygroupaffectedrealtimestaticmethodcreatetakesunitcaster, integerlevel, realtargetx, realtargetyreturnsinstancelocalinstancesi=instance.allocate()
localrealdistancelocalrealfactor=1.0callUnitAddAbility(caster, XE_HEIGHT_ENABLER)
callUnitRemoveAbility(caster, XE_HEIGHT_ENABLER)
setsi.caster=castersetsi.level=levelsetsi.dx=targetx-GetUnitX(caster)
setsi.dy=targety-GetUnitY(caster)
setdistance = SquareRoot(si.dx*si.dx+si.dy*si.dy)+1.0//to avoid division by zero laterifdistance>GlideMaxDistance(level) thensetfactor = GlideMaxDistance(level)/distanceelseifdistance<GlideMinDistance(level) thensetfactor = GlideMinDistance(level)/distanceendifsetsi.time = factor*distance/GlideSpeed(level) //duration of the spellsetfactor = factor/si.time*SPELL_PERIODsetsi.dx=si.dx*factor//distance traveled per intervalsetsi.dy=si.dy*factorsetsi.timescale=ANIM_SPEED_STARTsetsi.dtimescale=(ANIM_SPEED_END-ANIM_SPEED_START)/(GlideMaxDistance(level)/GlideSpeed(level)) //animation speed change per secondifsi.affected == nullthen//create a group if this is the first time this instance id is usedsetsi.affected=CreateGroup()
endifsetsi.t=NewTimer() //animation timercallSetTimerData(si.t, integer(si))
callTimerStart(si.t, 0.0, false, functionAnimation)
//neccessary evil, the spell sounds like crap if the warden's animation sounds are allowed to playifDISABLE_SPELL_SOUNDthencallVolumeGroupSetVolume(SOUND_VOLUMEGROUP_SPELLS, 0.0)
endififinstanceCount==0then//slide timersetslide=NewTimer()
callTimerStart(slide, SPELL_PERIOD, true, functionPeriodic)
endifsetinstances[instanceCount]=sisetsi.index=instanceCountsetinstanceCount=instanceCount+1returnsiendmethodstaticmethodgettakesunitureturnsinstancelocalintegeri=0loopexitwheni==instanceCountifinstances[i].caster==uthenreturninstances[i]
endifseti=i+1endloopreturn0endmethodmethodfinishtakesnothingreturnsnothingsetthis.active=false//spell stopped, wait for animation to finish and then end itsetinstanceCount=instanceCount-1setinstances[this.index]=instances[instanceCount]
setinstances[instanceCount].index=this.indexifinstanceCount==0thencallReleaseTimer(slide)
//end of evilifDISABLE_SPELL_SOUNDthencallVolumeGroupReset()
endifendifendmethodmethodonDestroytakesnothingreturnsnothingifnot(this.interrupted) then//if the channeling wasn't interrupted by the player...callIssueImmediateOrder(this.caster, "stop") //...then stop it nowendifcallReleaseTimer(this.t)
callGroupClear(this.affected) //the next time this instance id is used we won't need to create a groupendmethodendstruct// ================================================================privatefunctionSpellEffecttakesnothingreturnsnothinglocalintegerlvllocalunitulocallocationlifGetSpellAbilityId() == SPELL_ABILITYthensetlvl =GetUnitAbilityLevel(GetTriggerUnit(), SPELL_ABILITY)
setu = GetSpellTargetUnit()
ifu == nullthen//point-target castsetl = GetSpellTargetLoc()
callinstance.create(GetTriggerUnit(), lvl, GetLocationX(l), GetLocationY(l))
callRemoveLocation(l)
setl = nullelse//unit-target castcallinstance.create(GetTriggerUnit(), lvl, GetUnitX(u), GetUnitY(u))
setu = nullendifendifendfunctionprivatefunctionSpellStoptakesnothingreturnsnothinglocalinstancesiifGetSpellAbilityId() == SPELL_ABILITYthensetsi = instance.get(GetTriggerUnit())
ifsi != 0thencallsi.finish()
setsi.interrupted=trueendifendifendfunctionprivatefunctionInittakesnothingreturnsnothing//init spellcast triggerlocaltriggertr = CreateTrigger()
callTriggerRegisterAnyUnitEventBJ( tr, EVENT_PLAYER_UNIT_SPELL_EFFECT )
callTriggerAddAction( tr, functionSpellEffect )
settr = CreateTrigger()
callTriggerRegisterAnyUnitEventBJ( tr, EVENT_PLAYER_UNIT_SPELL_ENDCAST )
callTriggerAddAction( tr, functionSpellStop )
//init damage filtersettempbx=Condition(functionTargets)
//init xedamagesetxed=xedamage.create()
callDamageOptions(xed)
endfunctionendscope
Lurking Shadow:
scopeLurkingShadowinitializerInit//*****************************************************************//* spell - Lurking Shadow//*//* written by: Anitarf//* requires: -xepreload//* -xedamage//* -ABuff//*//* -a unit target triggerer ability//* -a self-targetting aura for displaying the buff//* -a buff that gets displayed by the aura//*//* description: Creates a buff on the targeted unit that deals//* damage to enemy units in an area around the//* target whenever it casts a spell or is//* targeted by a spell.//*//*****************************************************************globals//Spell settingsprivateconstantintegerSPELL_ABILITY = 'A00A'//the spell that applies the ABuffprivateconstantintegerBUFF_ABILITY = 'A008'//the self-targetting aura that displays the buffprivateconstantintegerBUFF_BUFF = 'B001'//the buff applied by the self-targetting auraprivateconstantbooleanDAMAGE_WHEN_CASTING = true//if true, damage is dealt whenever the buffed unit casts a spellprivateconstantbooleanDAMAGE_WHEN_TARGETED = true//if true, damage is dealt whenever the buffed unit is targeted by a spell//Visual settingsprivateconstantstringDAMAGE_EFFECT_MAIN = "Abilities\\Spells\\Undead\\OrbOfDeath\\OrbOfDeathMissile.mdl"//the effect on the buffed unit when the spell deals damageprivateconstantstringDAMAGE_EFFECT_MAIN_AP = "origin"//the attachment point for the effectprivateconstantstringDAMAGE_EFFECT_UNIT = "Abilities\\Spells\\Undead\\CarrionSwarm\\CarrionSwarmDamage.mdl"//the effect on the units the spell damagesprivateconstantstringDAMAGE_EFFECT_UNIT_AP = "origin"//the attachment point for the effectendglobalsprivateconstantfunctionBuffDurationtakesintegerlevelreturnsrealreturn30.0//the duration of the buffendfunctionprivateconstantfunctionDamagetakesintegerlevelreturnsrealreturn25.0+25.0*levelendfunctionprivateconstantfunctionDamageRadiustakesintegerlevelreturnsrealreturn100.0+50.0*levelendfunctionprivatefunctionDamageOptionstakesxedamagespellDamagereturnsnothing//useful read: [url]http://www.wc3campaigns.net/showpost.php?p=1030046&postcount=19[/url]setspellDamage.dtype=DAMAGE_TYPE_UNIVERSALsetspellDamage.atype=ATTACK_TYPE_NORMALsetspellDamage.tag=0//the tag attached to the damage by xedamagesetspellDamage.exception=UNIT_TYPE_STRUCTURE//deal no damage to structuresendfunction// END OF CALIBRATION SECTION// ================================================================globalsprivateaBuffTypeabtprivatexedamagexedendglobals//ABuff functionsprivatefunctionCreatetakesaBuffeventBuffreturnsnothingcallUnitAddAbility(eventBuff.target.u, BUFF_ABILITY)
callSetUnitAbilityLevel(eventBuff.target.u, BUFF_ABILITY, eventBuff.level)
callUnitMakeAbilityPermanent(eventBuff.target.u, true, BUFF_ABILITY) //prevent morphing from removing the abilityendfunctionprivatefunctionRefreshtakesaBuffeventBuffreturnsnothingcallSetUnitAbilityLevel(eventBuff.target.u, BUFF_ABILITY, eventBuff.level)
endfunctionprivatefunctionCleanuptakesaBuffeventBuffreturnsnothingcallUnitRemoveAbility(eventBuff.target.u, BUFF_ABILITY)
callUnitRemoveAbility(eventBuff.target.u, BUFF_BUFF)
endfunctionprivatefunctionSpellCasttakesaBuffeventBuff, integerspellId, unittargetreturnsnothingcallDestroyEffect(AddSpecialEffectTarget(DAMAGE_EFFECT_MAIN, eventBuff.target.u, DAMAGE_EFFECT_MAIN_AP))
callxed.damageAOE(eventBuff.caster, GetUnitX(eventBuff.target.u),GetUnitY(eventBuff.target.u),DamageRadius(eventBuff.level),Damage(eventBuff.level) )
endfunctionprivatefunctionSpellTargetedtakesaBuffeventBuff, integerspellId, unitcasterreturnsnothingcallDestroyEffect(AddSpecialEffectTarget(DAMAGE_EFFECT_MAIN, eventBuff.target.u, DAMAGE_EFFECT_MAIN_AP))
callxed.damageAOE(eventBuff.caster, GetUnitX(eventBuff.target.u),GetUnitY(eventBuff.target.u),DamageRadius(eventBuff.level),Damage(eventBuff.level) )
endfunction// ================================================================privatefunctionSpellEffecttakesnothingreturnsnothinglocalintegerlvlifGetSpellAbilityId() == SPELL_ABILITYthensetlvl = GetUnitAbilityLevel(GetTriggerUnit(), SPELL_ABILITY)
callABuffApply(abt, GetSpellTargetUnit(), GetTriggerUnit(), BuffDuration(lvl), lvl, 0 )
endifendfunctionprivatefunctionInittakesnothingreturnsnothing//init spellcast triggerlocaltriggertr = CreateTrigger()
callTriggerRegisterAnyUnitEventBJ( tr, EVENT_PLAYER_UNIT_SPELL_EFFECT )
callTriggerAddAction( tr, functionSpellEffect )
//init xedamagesetxed=xedamage.create()
callDamageOptions(xed)
callxed.useSpecialEffect(DAMAGE_EFFECT_UNIT,DAMAGE_EFFECT_UNIT_AP)
//init aBuffTypecallXE_PreloadAbility(BUFF_ABILITY)
setabt=aBuffType.create()
setabt.eventCreate = ABuffEvent_Create.Createsetabt.eventRefresh = ABuffEvent_Refresh.Refreshsetabt.eventCleanup = ABuffEvent_Cleanup.CleanupifDAMAGE_WHEN_CASTINGthensetabt.eventSpellCast = ABuffEvent_BUnitSpellCast.SpellCastendififDAMAGE_WHEN_TARGETEDthensetabt.eventSpellTargeted = ABuffEvent_BUnitSpellTargeted.SpellTargetedendifendfunctionendscope
Sacred Ground:
scopeSacredGroundinitializerInit//*****************************************************************//* spell - Sacred Ground//*//* written by: Anitarf//* requires: -xepreload//* -TimerUtils//* -ADamage//* -ABuff (requires damage events)//* -ABuffAuras//*//* -an instant (no-target) channeling triggerer ability//* -a self-targetting aura for displaying the buff//* -a buff that gets displayed by the aura//*//* description: As long as the spell is being channeled, all//* damage dealt to nearby friendly units will be//* reduced by a percentage and those units will//* regenerate mana equal to a percentage of damage//* prevented.//*//* technical: The spell is designed with the Demon Hunter model//* in mind, but the animation code is calibratable//* so it might be possible to get interesting results//* with other model animations as well. The channeling//* spell should have a duration equal or greater than//* the duration set in the calibration functions.//*//*****************************************************************globals//Spell settingsprivateconstantintegerSPELL_ABILITY = 'A000'//the spell abilityprivateconstantintegerBUFF_ABILITY = 'A00C'//the self-targetting aura that displays the buffprivateconstantintegerBUFF_BUFF = 'B003'//the buff applied by the self-targetting auraprivateconstantintegerDAMAGE_SHIELD_PRIORITY = 10//Animation optionsprivateconstantintegerANIM_ID = 3//the id of the animation to play during the spellprivateconstantrealANIM_SPEED_START = 1.25//the animation speed at the start of the spellprivateconstantrealANIM_DURATION_START = 2.0//how long the starting phase should lastprivateconstantrealANIM_SPEED_LOOP = 0.25//the animation speed while the spell is loopingprivateconstantrealANIM_DURATION_LOOP = 0.75//how quickly to switch between positive and negative animation speed while loopingprivateconstantrealANIM_SPEED_END = 2.25//the animation speed at the end of the spellprivateconstantrealANIM_DURATION_END = 1.0//how long the ending phase should lastendglobalsprivateconstantfunctionSpellDurationtakesintegerlevelreturnsrealreturn10.0endfunctionprivateconstantfunctionDamagePreventedFactortakesintegerlevelreturnsrealreturn0.05+0.15*level//0.2, 0.35, 0.5endfunctionprivateconstantfunctionPreventedToManaFactortakesintegerlevelreturnsrealreturn0.5endfunctionprivateconstantfunctionAuraRangetakesintegerlevelreturnsrealreturn500.0endfunctionprivatefunctionAuraTargetstakesunitaffected, unitauraGiverreturnsbooleanreturnIsUnitAlly(affected, GetOwningPlayer(auraGiver))
endfunction// END OF CALIBRATION SECTION// ================================================================//ADamage shield structprivatestructshdextendsADamage_shieldintegerlevelrealpreventedstaticmethodcreatetakesunitu, integerlevelreturnsshdlocalshds = shd.allocate(u, DAMAGE_SHIELD_PRIORITY)
sets.level = levelreturnsendmethodmethoddamagedtakesunitdamageSource, realdamagereturnsrealset.prevented = damage*DamagePreventedFactor(.level)
return.preventedendmethodendstruct// ================================================================//ABuff functionsglobalsprivateaBuffTypeabtendglobalsprivatefunctionCreatetakesaBuffeventBuffreturnsnothingseteventBuff.data=shd.create(eventBuff.target.u, eventBuff.level)
callUnitAddAbility(eventBuff.target.u, BUFF_ABILITY)
callSetUnitAbilityLevel(eventBuff.target.u, BUFF_ABILITY, eventBuff.level)
callUnitMakeAbilityPermanent(eventBuff.target.u, true, BUFF_ABILITY) //prevent morphing from removing the abilityendfunctionprivatefunctionRefreshtakesaBuffeventBuffreturnsnothingseteventBuff.data=eventBuff.olddatasetshd(eventBuff.data).level=eventBuff.levelcallSetUnitAbilityLevel(eventBuff.target.u, BUFF_ABILITY, eventBuff.level)
endfunctionprivatefunctionCleanuptakesaBuffeventBuffreturnsnothingcallshd(eventBuff.data).destroy()
callUnitRemoveAbility(eventBuff.target.u, BUFF_ABILITY)
callUnitRemoveAbility(eventBuff.target.u, BUFF_BUFF)
endfunctionprivatefunctionDamagedtakesaBuffeventBuff, realdamage, unitdamageSourcereturnsnothinglocalrealrestore = shd(eventBuff.data).prevented*PreventedToManaFactor(eventBuff.level)
callSetUnitState(eventBuff.target.u, UNIT_STATE_MANA, GetUnitState(eventBuff.target.u, UNIT_STATE_MANA)+restore)
endfunction// ================================================================//AnimationsprivatekeywordinstanceglobalsprivateinstancearrayinstancesprivateintegerinstanceCount = 0endglobalsprivatefunctionAnimation_Finishtakesnothingreturnsnothinglocalinstancesi = GetTimerData(GetExpiredTimer())
callsi.destroy()
endfunctionprivatefunctionAnimationtakesnothingreturnsnothinglocalinstancesi = GetTimerData(GetExpiredTimer())
setsi.time=si.time-ANIM_DURATION_LOOPifsi.time>ANIM_DURATION_ENDthencallSetUnitTimeScale(si.caster, si.timescale)
setsi.timescale=-si.timescalecallTimerStart(GetExpiredTimer(), ANIM_DURATION_LOOP, false, functionAnimation)
elsesetsi.time=si.time+ANIM_DURATION_LOOPcallSetUnitTimeScale(si.caster, ANIM_SPEED_END*(ANIM_DURATION_END/si.time))
callTimerStart(GetExpiredTimer(), si.time, false, functionAnimation_Finish)
endifendfunction// ================================================================//Spell instanceprivatestructinstanceprivateintegerindexprivatetimert//this timer needs to be on a per-caster basisbooleaninterrupted = falseunitcasterintegerlevelrealtimerealtimescalestaticmethodcreatetakesunitcaster, integerlevelreturnsinstancelocalinstancesi=instance.allocate()
setsi.caster=castersetsi.level=levelsetsi.time=SpellDuration(level)-ANIM_DURATION_STARTsetsi.timescale=ANIM_SPEED_LOOPsetsi.t=NewTimer() //animation timercallSetTimerData(si.t, integer(si))
callSetUnitTimeScale(caster, ANIM_SPEED_START)
callSetUnitAnimationByIndex(caster, ANIM_ID)
callTimerStart(si.t, ANIM_DURATION_START, false, functionAnimation)
callUnitSetABuffAura(caster, abt, level, ABuffAuraTargets.AuraTargets, AuraRange(level))
setinstances[instanceCount]=sisetsi.index=instanceCountsetinstanceCount=instanceCount+1returnsiendmethodstaticmethodgettakesunitureturnsinstancelocalintegeri=0loopexitwheni==instanceCountifinstances[i].caster==uthenreturninstances[i]
endifseti=i+1endloopreturn0endmethodmethodonDestroytakesnothingreturnsnothingsetinstanceCount=instanceCount-1setinstances[this.index]=instances[instanceCount]
setinstances[instanceCount].index=this.indexcallUnitRemoveABuffAura(this.caster, abt)
callReleaseTimer(this.t)
callSetUnitTimeScale(this.caster, 1.0)
ifnot(this.interrupted) then//if the channeling wasn't interrupted by the player...callIssueImmediateOrder(this.caster, "stop") //...then stop it nowendifendmethodendstruct// ================================================================privatefunctionSpellEffecttakesnothingreturnsnothinglocalintegerlvlifGetSpellAbilityId() == SPELL_ABILITYthensetlvl =GetUnitAbilityLevel(GetTriggerUnit(), SPELL_ABILITY)
callinstance.create(GetTriggerUnit(), lvl)
endifendfunctionprivatefunctionSpellStoptakesnothingreturnsnothinglocalinstancesiifGetSpellAbilityId() == SPELL_ABILITYthensetsi = instance.get(GetTriggerUnit())
ifsi != 0thensetsi.interrupted=truecallsi.destroy()
endifendifendfunctionprivatefunctionInittakesnothingreturnsnothing//init spellcast triggerlocaltriggertr = CreateTrigger()
callTriggerRegisterAnyUnitEventBJ( tr, EVENT_PLAYER_UNIT_SPELL_EFFECT )
callTriggerAddAction( tr, functionSpellEffect )
settr = CreateTrigger()
callTriggerRegisterAnyUnitEventBJ( tr, EVENT_PLAYER_UNIT_SPELL_ENDCAST )
callTriggerAddAction( tr, functionSpellStop )
//init aBuffTypesetabt=aBuffType.create()
setabt.ignoreAsBuff = true//since this is an aura buff, we can't have other spells destroy itsetabt.eventCreate = ABuffEvent_Create.Createsetabt.eventRefresh = ABuffEvent_Refresh.Refreshsetabt.eventCleanup = ABuffEvent_Cleanup.Cleanupsetabt.eventDamaged = ABuffEvent_BUnitDamaged.Damagedendfunctionendscope
Anyways, you misspelled LurkingShadow in the scope's name. It also still drives me nuts that you 'shamelessly copy' Ammorth's line segments' code as opposed to just using it for Glacial Wall. I'm pretty sure he updated it to be usable, even in your case here. Otherwise, everything looks good and in order. Thank you for making your spells easy to follow and with sensible looking code, it goes a long way to make reading over it smoother.
Anyways, you misspelled LurkingShadow in the scope's name. It also still drives me nuts that you 'shamelessly copy' Ammorth's line segments' code as opposed to just using it for Glacial Wall.
Right, I forgot I had that in there; like I said, it's been a while. Fixed now.
Well, I was testing the ice ability, and I got pissed with a bug:
- Why the hell can the units attack the ice blocks ? Shouldn't they be invulnerable or have locust or something?
Not necessarily. In fact, I'd say you want the ice blocks destructible; makes sense, as a bunch of warriors trapped in ice would attempt to chip their way out.
Not necessarily. In fact, I'd say you want the ice blocks destructible; makes sense, as a bunch of warriors trapped in ice would attempt to chip their way out.
Mmmm good point, didn't think of that.
Well, one more spell for my project with credits to Anitarf =p
If you don't want your wall units to get attacked, you can always make them invulnerable in the object editor...
I know that, but if it was a bug, it would be my obligation to report it. Other users may not have the same level of knowledge you do (or I do) and it could be a nice fix for them.