| 08-25-2009, 11:18 PM | #1 | ||||
These are two spells I made for a spell contest at a german WC3 modding site. The first spell, Meat Puppet, turns the target unit into a puppet to be flung around by the hero. When it collides with other units, those units take damage are knocked back. The second spell, Null Blast ignores the time the unit would take to cast the spell because of its Cast Point. It instantly damages the target unit and weakens it (resulting in a decrease of maximum health). After the effects wear off a second blast is released which damages all surrounding units. These two spells require/use: - CurseBolt (model): http://www.wc3c.net/showthread.php?t=107151 - UnitMaxState: http://www.wc3c.net/showthread.php?t=107451 - TextTag: http://www.wc3c.net/showthread.php?t=101963 - GroupUtils: http://www.wc3c.net/showthread.php?t=104464 - UnitIndexingUtils: http://www.wc3c.net/showthread.php?t=101350 - LastOrder and AbortSpell: http://www.wc3c.net/showthread.php?t=104175 - Knockback: http://www.wc3c.net/showthread.php?t=99720 - TerrainPathability: http://www.wc3c.net/showthread.php?t=103862 - SpellEvent: http://www.wc3c.net/showthread.php?t=105374 - TimerUtils: http://www.wc3c.net/showthread.php?t=101322 - Table: http://www.wc3c.net/showthread.php?t=101246 - SimError: http://www.wc3c.net/showthread.php?t=101260 - xe: http://www.wc3c.net/showthread.php?t=101150 - DestructableLib: http://www.wc3c.net/showthread.php?t=103927
Meat Puppet:library MeatPuppet initializer Init uses LastOrder, GroupUtils, DestructableLib, SpellEvent, Table,/* */UnitIndexingUtils, AbortSpell, Knockback, TimerUtils private keyword Data // DO NOT CHANGE! globals private constant integer LEARN_AID = 'A006' private constant integer CAST_AID = 'A000' private constant integer BID = 'B000' // the buff placed by CAST_AID on the target unit. private constant real TICK = 1./32 private constant integer RELEASE_AID = 'A005' // ability is based off of Berserk private constant integer RELEASE_BID = 'B002' // placed on caster by RELEASE_AID private constant boolean RELEASE_ALLOW = true // if true, RELEASE_AID gets added after casting the spell private constant boolean RELEASE_REPLACE_CAST_AID = true // if true and MAX_INSTANCES_PER_CASTER equals 1, CAST_AID gets removed before placing RELEASE_AID private constant string LIGHTNING_ID = "INIT" // Ordinary Lightning private constant real LIGHTNING_RED = 1.0 private constant real LIGHTNING_GREEN = 0.2 private constant real LIGHTNING_BLUE = 0.7 private constant real LIGHTNING_ALPHA = 0.8 // put "0." in here if you dont want the lightning private constant string COLLISION_FX = "" // empty, but someone might want to use it private constant real COLLISION_AOE = 95. private real array COLLISION_DAMAGE // When the puppet hits an enemy unit, how much damage does the unit hit take private real array COLLISION_PUPPET_DAMAGE // When the puppet hits another unit, how much damage does the puppet take private constant boolean COLLISION_DAMAGE_FRIENDLY = false // should a friendly puppet take damage from colliding? private constant attacktype COLLISION_ATTACK_TYPE = ATTACK_TYPE_NORMAL private constant damagetype COLLISION_DAMAGE_TYPE = DAMAGE_TYPE_NORMAL private constant weapontype COLLISION_SOUND = WEAPON_TYPE_METAL_MEDIUM_BASH private constant weapontype COLLISION_TREE_SOUND = WEAPON_TYPE_WOOD_MEDIUM_BASH private real array COLLISION_KB_DISTANCE // how far is the unit hit pushed back private real array COLLISION_KB_DURATION // for how long is the unit hit pushed back // values less than or equal to 0 disable knocking back private constant boolean KB_UNITS_KILL_TREES = true private constant boolean KB_UNITS_KNOCK_OTHERS = false private constant boolean KB_UNITS_CHAIN = false // should units knocked back by units knocked back by this spell be able to knock other units back // refer to the manual of Knockback for information private constant real MIN_RANGE = 200. private constant real MIN_RANGE_TIME = 0.25 // 0. or less is instant private constant real ANGLE_CHANGE_SPEED = 0.2 // from 0.0 to 1.0, please // closer to 0: longer time to sync with casters facing, closer to 1: less time to sync with casters facing (1 being instant syncing) private constant real DISTANCE_CHANGE_SPEED = 0.1 // from 0.0 to 1.0, please private constant integer MAX_INSTANCES_PER_CASTER = 1 private constant string ERROR_TARGET_ALREADY_IN_INSTANCE = "Target already targeted by another instance of this spell!" private constant string ERROR_TOO_MANY_INSTANCES = "Too many instances of this spell for the caster!" private constant string ABILITY_HOTKEY = "T" // hotkey triggering AID endglobals // Damage private function SetUpCOLLISION_DAMAGE takes nothing returns nothing set COLLISION_DAMAGE[1]=125. set COLLISION_DAMAGE[2]=200. set COLLISION_DAMAGE[3]=300. endfunction private function Collision_Damage takes integer level returns real return COLLISION_DAMAGE[level] endfunction // Puppet Damage private function SetUpCOLLISION_PUPPET_DAMAGE takes nothing returns nothing set COLLISION_PUPPET_DAMAGE[1]=0. set COLLISION_PUPPET_DAMAGE[2]=0. set COLLISION_PUPPET_DAMAGE[3]=0. endfunction private function Collision_Puppet_Damage takes integer level returns real return COLLISION_PUPPET_DAMAGE[level] endfunction // Distance private function SetUpCOLLISION_KB_DISTANCE takes nothing returns nothing set COLLISION_KB_DISTANCE[1]=125. set COLLISION_KB_DISTANCE[2]=175. set COLLISION_KB_DISTANCE[3]=225. endfunction private function Collision_Kb_Distance takes integer level returns real return COLLISION_KB_DISTANCE[level] endfunction // Duration private function SetUpCOLLISION_KB_DURATION takes nothing returns nothing set COLLISION_KB_DURATION[1]=1.0 set COLLISION_KB_DURATION[2]=1.2 set COLLISION_KB_DURATION[3]=1.4 endfunction private function Collision_Kb_Duration takes integer level returns real return COLLISION_KB_DURATION[level] endfunction // Validate Target private function ValidTarget takes unit u, Data s returns boolean return IsUnitType(u, UNIT_TYPE_DEAD)==false/* */ and IsUnitType(u, UNIT_TYPE_STRUCTURE)==false/* */ and IsUnitType(u, UNIT_TYPE_FLYING)==IsUnitType(s.t, UNIT_TYPE_FLYING)/* */ and IsUnitEnemy(u, GetOwningPlayer(s.caster))/* */ and (not IsKnockedBack(u))/* */ and u!=s.t/* */ and GetUnitMoveSpeed(u)>0/* */ endfunction // private struct Data unit caster unit t // Target lightning l // Link integer level integer instanceofcaster real a // angleOffset real d // distanceOffset real dd // deltadistance // MIN_RANGE real tf // targets facing, relative real c // counter static Table InstanceOfCaster static integer array Instances static thistype array InstanceOfUnit // Linking Back, so instances dont conflict with each other static boolexpr Collision static boolexpr TreeFilter static thistype tmps static rect R static location LocZ=Location(0,0) private integer i private static thistype array Structs private static timer T=CreateTimer() private static integer Count=0 static method GetLocZ takes real x, real y returns real call MoveLocation(.LocZ, x,y) return GetLocationZ(.LocZ) endmethod method onDestroy takes nothing returns nothing local integer id=GetUnitId(.caster) // Recycle instance of this caster properly set .Instances[id]=.Instances[id]-1 set .InstanceOfCaster[id*MAX_INSTANCES_PER_CASTER+.instanceofcaster]=.InstanceOfCaster[id*MAX_INSTANCES_PER_CASTER+.Instances[id]] set thistype(.InstanceOfCaster[id*MAX_INSTANCES_PER_CASTER+.instanceofcaster]).instanceofcaster=.instanceofcaster call .InstanceOfCaster.flush(id*MAX_INSTANCES_PER_CASTER+.Instances[id]) if RELEASE_ALLOW and .Instances[id]==0 then call UnitRemoveAbility(.caster, RELEASE_AID) if MAX_INSTANCES_PER_CASTER==1 and RELEASE_REPLACE_CAST_AID and GetUnitAbilityLevel(.caster, LEARN_AID)>0 then call UnitAddAbility(.caster, CAST_AID) call SetUnitAbilityLevel(.caster, CAST_AID, GetUnitAbilityLevel(.caster, LEARN_AID)) call UnitMakeAbilityPermanent(.caster, true, CAST_AID) endif endif // Normal cleanup set .InstanceOfUnit[GetUnitId(.t)]=0 call SetUnitPosition(.t, GetUnitX(.t), GetUnitY(.t)) // avoid getting stuck call IssueLastOrder(.t) // avoid not carrying out the last order set .t=null set .caster=null call DestroyLightning(.l) set .l=null // clean your struct here set .Count=.Count-1 set .Structs[.i]=.Structs[.Count] set .Structs[.i].i=.i if .Count==0 then call PauseTimer(.T) endif endmethod private static method CollisionFunc takes nothing returns boolean local unit u=GetFilterUnit() local real a local real v if ValidTarget(u, .tmps) then if UnitDamageTarget(.tmps.caster, u, Collision_Damage(.tmps.level), true, false, COLLISION_ATTACK_TYPE, COLLISION_DAMAGE_TYPE, COLLISION_SOUND) then // should the unit for some reason be immune to damage, dont continue if COLLISION_DAMAGE_FRIENDLY or IsUnitEnemy(.tmps.t, GetOwningPlayer(.tmps.caster)) then call UnitDamageTarget(.tmps.caster, .tmps.t, Collision_Puppet_Damage(.tmps.level), true, false, COLLISION_ATTACK_TYPE, COLLISION_DAMAGE_TYPE, null) // sound has already been played endif call DestroyEffect(AddSpecialEffect(COLLISION_FX, GetUnitX(.tmps.t), GetUnitY(.tmps.t))) if Collision_Kb_Duration(.tmps.level)>0 then // if the user doesnt want the units hit to be knocked back, dont do it set a=(2*Collision_Kb_Distance(.tmps.level))/(Collision_Kb_Duration(.tmps.level)*Collision_Kb_Duration(.tmps.level)) // s=a/2*t^2 --> a=2*s/(t^2) set v=a*Collision_Kb_Duration(.tmps.level) // v=a*t call KnockbackTarget(.tmps.caster, u, Atan2(GetUnitY(u)-GetUnitY(.tmps.t), GetUnitX(u)-GetUnitX(.tmps.t))*bj_RADTODEG, v, a, KB_UNITS_KILL_TREES, KB_UNITS_KNOCK_OTHERS, KB_UNITS_CHAIN) endif endif endif set u=null return false endmethod private static method TreeFilterFunc takes nothing returns boolean local destructable d=GetFilterDestructable() local real dx=GetWidgetX(d)-GetUnitX(.tmps.t) local real dy=GetWidgetY(d)-GetUnitY(.tmps.t) if (not IsDestructableDead(d)) and IsDestructableTree(d) and (dx*dx+dy*dy<=COLLISION_AOE*COLLISION_AOE) then if COLLISION_DAMAGE_FRIENDLY or IsUnitEnemy(.tmps.t, GetOwningPlayer(.tmps.caster)) then call UnitDamageTarget(.tmps.caster, .tmps.t, Collision_Puppet_Damage(.tmps.level), true, false, COLLISION_ATTACK_TYPE, COLLISION_DAMAGE_TYPE, COLLISION_TREE_SOUND) endif call KillDestructable(d) endif set d=null return false endmethod private static method Callback takes nothing returns nothing local integer i=.Count-1 local thistype s local real a local real x local real y local real dx local real dy local real r loop exitwhen i<0 set s=.Structs[i] if GetUnitAbilityLevel(s.t, BID)==0 or IsUnitType(s.caster, UNIT_TYPE_DEAD)==true then call s.destroy() endif if s.d<MIN_RANGE then // move the unit to MIN_RANGE set s.d=s.d+s.dd/MIN_RANGE_TIME*TICK endif set x=GetUnitX(s.caster) set y=GetUnitY(s.caster) set dx=GetUnitX(s.t)-x set dy=GetUnitY(s.t)-y set r=Atan2(dy, dx) // current angle from caster to target if r<0 then // map the angle from -Pi..Pi to 0..2*Pi set r=2*bj_PI+r endif set a=ModuloReal(((GetUnitFacing(s.caster)*bj_DEGTORAD+s.a)-r), 2*bj_PI) // calculate the difference between current angle between caster and target and the current facing of the caster if a>bj_PI then // map the delta angle from 0..2*Pi to -Pi..Pi set a=-2*bj_PI+a endif set a=r+a*ANGLE_CHANGE_SPEED // calculate the new angle in which the target if offset from the caster set r=SquareRoot(dx*dx+dy*dy) // calculate the distance between caster and target set r=r+(s.d-r)*DISTANCE_CHANGE_SPEED // calculate the new distance the target is offset from the caster set x=Cos(a)*r+x set y=Sin(a)*r+y call SetUnitX(s.t, x) call SetUnitY(s.t, y) call SetUnitFacing(s.t, GetUnitFacing(s.caster)+s.tf) call MoveLightningEx(s.l, true, GetUnitX(s.caster), GetUnitY(s.caster), .GetLocZ(GetUnitX(s.caster), GetUnitY(s.caster))+GetUnitFlyHeight(s.caster), x, y, .GetLocZ(x, y)+GetUnitFlyHeight(s.t)) set .tmps=s call GroupEnumUnitsInRange(ENUM_GROUP, x,y, COLLISION_AOE, .Collision) call MoveRectTo(.R, x,y) call EnumDestructablesInRect(.R, .TreeFilter, null) // do your things here, dont forget to call s.destroy() somewhen // set i=i-1 endloop endmethod private static method PlaceRelease takes nothing returns nothing local thistype s=thistype(GetTimerData(GetExpiredTimer())) if MAX_INSTANCES_PER_CASTER==1 and RELEASE_REPLACE_CAST_AID then call UnitRemoveAbility(s.caster, CAST_AID) endif call UnitAddAbility(s.caster, RELEASE_AID) call UnitMakeAbilityPermanent(s.caster, true, RELEASE_AID) call ReleaseTimer(GetExpiredTimer()) endmethod static method create takes nothing returns thistype local thistype s=.allocate() local real dx local real dy local real r local integer id local timer t // Populate the struct set s.caster=SpellEvent.CastingUnit set s.t=SpellEvent.TargetUnit set .InstanceOfUnit[GetUnitId(s.t)]=s set s.level=GetUnitAbilityLevel(s.caster, CAST_AID) // SetUp the relative position set dx=GetUnitX(s.t)-GetUnitX(s.caster) set dy=GetUnitY(s.t)-GetUnitY(s.caster) set r=Atan2(dy, dx) if r<0 then set r=2*bj_PI+r endif set s.a=r-GetUnitFacing(s.caster)*bj_DEGTORAD set s.d=SquareRoot(dx*dx+dy*dy) if s.d<MIN_RANGE then set s.dd=MIN_RANGE-s.d if MIN_RANGE_TIME<=0 then set s.d=MIN_RANGE endif endif set s.tf=GetUnitFacing(s.t)-GetUnitFacing(s.caster) // SetUp the lightning set s.l=AddLightningEx(LIGHTNING_ID, true, GetUnitX(s.caster), GetUnitY(s.caster), .GetLocZ(GetUnitX(s.caster), GetUnitY(s.caster))+GetUnitFlyHeight(s.caster), GetUnitX(s.t), GetUnitY(s.t), .GetLocZ(GetUnitX(s.t), GetUnitY(s.t))+GetUnitFlyHeight(s.t)) call SetLightningColor(s.l, LIGHTNING_RED, LIGHTNING_GREEN, LIGHTNING_BLUE, LIGHTNING_ALPHA) set id=GetUnitId(s.caster) set .InstanceOfCaster[id*MAX_INSTANCES_PER_CASTER+.Instances[id]]=s set s.instanceofcaster=.Instances[id] if RELEASE_ALLOW and .Instances[id]==0 then set t=NewTimer() call SetTimerData(t, s) call TimerStart(t, 0, false, function thistype.PlaceRelease) endif set .Instances[id]=.Instances[id]+1 // initialize the struct here set .Structs[.Count]=s set s.i=.Count if .Count==0 then call TimerStart(.T, TICK, true, function thistype.Callback) endif set .Count=.Count+1 return s endmethod private static method onInit takes nothing returns nothing set .InstanceOfCaster=Table.create() set .Collision=Condition(function thistype.CollisionFunc) set .TreeFilter=Condition(function thistype.TreeFilterFunc) set .R=Rect(-COLLISION_AOE, -COLLISION_AOE, COLLISION_AOE, COLLISION_AOE) if MAX_INSTANCES_PER_CASTER<=0 then call BJDebugMsg(SCOPE_PREFIX+": MAX_INSTANCES_PER_CASTER is too low (<=0)!") endif if ANGLE_CHANGE_SPEED<=0 then call BJDebugMsg(SCOPE_PREFIX+": ANGLE_CHANGE_SPEED is too low (<=0)!") elseif ANGLE_CHANGE_SPEED>1 then call BJDebugMsg(SCOPE_PREFIX+": ANGLE_CHANGE_SPEED is too high (>1)!") endif if DISTANCE_CHANGE_SPEED<=0 then call BJDebugMsg(SCOPE_PREFIX+": DISTANCE_CHANGE_SPEED is too low (<=0)!") elseif DISTANCE_CHANGE_SPEED>1 then call BJDebugMsg(SCOPE_PREFIX+": DISTANCE_CHANGE_SPEED is too high (>1)!") endif // call SetUpCOLLISION_DAMAGE() call SetUpCOLLISION_PUPPET_DAMAGE() call SetUpCOLLISION_KB_DISTANCE() call SetUpCOLLISION_KB_DURATION() endmethod endstruct private function CreateProxy takes nothing returns nothing call Data.create() endfunction private function CheckValidTarget takes nothing returns nothing if Data.InstanceOfUnit[GetUnitId(SpellEvent.TargetUnit)]!=0 then call AbortSpell(SpellEvent.CastingUnit, "\n"+ERROR_TARGET_ALREADY_IN_INSTANCE, ABILITY_HOTKEY) endif if Data.Instances[GetUnitId(SpellEvent.CastingUnit)]>=MAX_INSTANCES_PER_CASTER then call AbortSpell(SpellEvent.CastingUnit, "\n"+ERROR_TOO_MANY_INSTANCES, ABILITY_HOTKEY) endif endfunction private function Release takes nothing returns nothing local integer id=GetUnitId(SpellEvent.CastingUnit) call UnitRemoveAbility(SpellEvent.CastingUnit, RELEASE_BID) call UnitRemoveAbility(Data(Data.InstanceOfCaster[id*MAX_INSTANCES_PER_CASTER+Data.Instances[id]-1]).t, BID) endfunction globals private integer array LearnedAbilityLevel endglobals private function IsLearnAbility takes nothing returns boolean return GetLearnedSkill()==LEARN_AID endfunction private function Learning takes nothing returns nothing local unit u=GetLearningUnit() if GetLearnedSkillLevel()==1 then call UnitAddAbility(u, CAST_AID) call UnitMakeAbilityPermanent(u, true, CAST_AID) endif if not(RELEASE_REPLACE_CAST_AID and MAX_INSTANCES_PER_CASTER==1 and Data.Instances[GetUnitId(u)]==1) then call SetUnitAbilityLevel(u, CAST_AID, GetLearnedSkillLevel()) endif set LearnedAbilityLevel[GetUnitId(u)]=GetLearnedSkillLevel() set u=null endfunction private function UnlearnedAbility takes nothing returns boolean return GetUnitAbilityLevel(GetTriggerUnit(), LEARN_AID)!=LearnedAbilityLevel[GetUnitId(GetTriggerUnit())] endfunction private function ResetAbility takes nothing returns nothing call UnitRemoveAbility(GetTriggerUnit(), CAST_AID) endfunction private function Init takes nothing returns nothing local trigger t=CreateTrigger() call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_HERO_SKILL) call TriggerAddCondition(t, Condition(function IsLearnAbility)) call TriggerAddAction(t, function Learning) set t=CreateTrigger() // just in case someone uses that damn tome of relearning // untested code call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_PICKUP_ITEM) call TriggerAddCondition(t, Condition(function UnlearnedAbility)) call TriggerAddAction(t, function ResetAbility) call RegisterSpellEffectResponse(CAST_AID, CreateProxy) call RegisterSpellCastResponse(CAST_AID, CheckValidTarget) call RegisterSpellEffectResponse(RELEASE_AID, Release) endfunction endlibrary Null Blast:library NullBlast initializer Init uses LastOrder, SpellEvent, UnitIndexingUtils, UnitMaxState,/* */ TimerUtils, xecast, xepreload, TextTag // This spell ignores the time the hero takes to reach the cast point. // Achieving this without potentially breaking, requires some more sophisticated work, // since players could potentially abort the casting of the ability after SPELL_CAST has been reached. private keyword Data // DO NOT CHANGE! globals private constant real TICK = 1./32 // interval to check for the buff BLINDNESS_DUMMY_BID private constant integer LEARN_AID = 'A001' // Dummy Ability for learning interface private constant integer CAST_AID = 'A002' // Dummy Ability for casting only private constant integer DUMMY_AID = 'A003' // the mana cost of CAST_AID and DUMMY_AID must match // dummy ability for mana cost and cooldown private constant string DUMMY_ORDER = "berserk" private constant integer DUMMY_BID = 'B002' // placed by DUMMY_AID on the caster private constant integer BLINDNESS_DUMMY_AID = 'A004' // changes duration of spell and % to miss private constant string BLINDNESS_DUMMY_ORDER = "curse" private constant integer BLINDNESS_DUMMY_BID = 'B001' // placed by BLINDNESS_DUMMY_AID on the target unit private constant string FX = "war3mapImported\\CurseBolt.mdx" // blast effect private constant string FX_ATTPT = "head" private real array COOLDOWN // must match the cooldown time given in DUMMY_AID private real array DAMAGE // damage dealt by the primary blast private real array SECONDARY_DAMAGE // damage dealt by the secondary blast private constant real SECONDARY_DAMAGE_AOE = 192. // units in this area around the target get damaged when the spell ends private constant boolean DAMAGE_FRIENDLY_UNITS = false private constant boolean DEBUFF_FRIENDLY_UNITS = true private constant attacktype ATTACK_TYPE = ATTACK_TYPE_MAGIC private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_MAGIC private constant weapontype WEAPON_TYPE = WEAPON_TYPE_WHOKNOWS // sound effect when damaging private constant weapontype SECONDARY_WEAPON_TYPE = WEAPON_TYPE_WHOKNOWS // sound effect when damaging through second blast private real array STAT_TAKEN // max hp or max mana taken away for a period of time by this ability // values less than 1 turn this into a relative loss instead of an absolute private constant unitstate STAT_MODIFIED = UNIT_STATE_MAX_LIFE // either UNIT_STATE_MAX_LIFE or UNIT_STATE_MAX_MANA private constant boolean SHOW_TT = true // should a texttag be shown private constant string TT_PATTERN = "&d HP!" // place "&d" where the amount taken away should be (case insensitive) private constant string TT_NEGATIVE_HP_BONUS_STRING = "-" private constant string TT_POSITIVE_HP_BONUS_STRING = "+" private constant string TT_COLOR = "|cffA00030" // color of the text in the texttag; standard WC3 formatting (|cAARRGGBB) endglobals // Cooldown private function SetUpCOOLDOWN takes nothing returns nothing set COOLDOWN[1]=10. set COOLDOWN[2]=10. set COOLDOWN[3]=10. endfunction private function Cooldown takes integer level returns real return COOLDOWN[level] endfunction // Damage private function SetUpDAMAGE takes nothing returns nothing set DAMAGE[1]=150. set DAMAGE[2]=200. set DAMAGE[3]=250. endfunction private function Damage takes integer level returns real return DAMAGE[level] endfunction // Secondary Damage private function SetUpSECONDARY_DAMAGE takes nothing returns nothing set SECONDARY_DAMAGE[1]=100. set SECONDARY_DAMAGE[2]=125. set SECONDARY_DAMAGE[3]=150. endfunction private function Secondary_Damage takes integer level returns real return SECONDARY_DAMAGE[level] endfunction // HP Taken private function SetUpSTAT_TAKEN takes nothing returns nothing set STAT_TAKEN[1]=150. set STAT_TAKEN[2]=200. set STAT_TAKEN[3]=250. endfunction private function Stat_Taken takes integer level returns real return STAT_TAKEN[level] endfunction // Validate Target private function ValidTarget takes unit u, Data s returns boolean return IsUnitType(u, UNIT_TYPE_DEAD)==false/* */ and IsUnitType(u, UNIT_TYPE_STRUCTURE)==false/* */ and IsUnitType(u, UNIT_TYPE_MAGIC_IMMUNE)==false/* */ and (DAMAGE_FRIENDLY_UNITS or IsUnitEnemy(u, GetOwningPlayer(s.caster)))/* */ and s.target!=u/* */ endfunction // globals private integer PatternLength=StringLength(TT_PATTERN) endglobals private struct Data unit caster unit target timer t integer level real hptaken boolean cooldown=true boolean inloop=true static boolean array CooldownActive static xecast BlindnessDummy static boolexpr DamageFilter static thistype tmps private integer i private static thistype array Structs private static timer T=CreateTimer() private static integer Count=0 private method onDestroy takes nothing returns nothing set .target=null set .caster=null set .t=null endmethod private method RemoveFromLoop takes nothing returns nothing if not .cooldown then call .destroy() endif // clean your struct here set .Count=.Count-1 set .Structs[.i]=.Structs[.Count] set .Structs[.i].i=.i if .Count==0 then call PauseTimer(.T) endif set .inloop=false endmethod private static method EndCooldown takes nothing returns nothing local thistype s=thistype(GetTimerData(GetExpiredTimer())) // switch from dummy ability to casting ability if GetUnitAbilityLevel(s.caster, LEARN_AID)>0 then call UnitRemoveAbility(s.caster, DUMMY_AID) call UnitAddAbility(s.caster, CAST_AID) call UnitMakeAbilityPermanent(s.caster, true, CAST_AID) // to avoid removal through morphing abilities call SetUnitAbilityLevel(s.caster, CAST_AID, GetUnitAbilityLevel(s.caster, LEARN_AID)) endif call ReleaseTimer(s.t) // clean up set .CooldownActive[GetUnitId(s.caster)]=false // mark the caster as "not in cooldown" set s.cooldown=false if GetUnitAbilityLevel(s.target, BLINDNESS_DUMMY_BID)==0 and (not s.inloop) then call s.destroy() // if the cooldown is longer than the buff lasts, destroy this instance endif endmethod private static method DamageFilterFunc takes nothing returns boolean local unit u=GetFilterUnit() if ValidTarget(u, .tmps) then call UnitDamageTarget(.tmps.caster, u, Secondary_Damage(.tmps.level), true, false, ATTACK_TYPE, DAMAGE_TYPE, SECONDARY_WEAPON_TYPE) endif set u=null return false endmethod private static method Callback takes nothing returns nothing local integer i=.Count-1 local thistype s loop exitwhen i<0 set s=.Structs[i] if GetUnitAbilityLevel(s.target, BLINDNESS_DUMMY_BID)==0 then //buff got removed/expired set .tmps=s call GroupEnumUnitsInRange(ENUM_GROUP, GetUnitX(s.target), GetUnitY(s.target), SECONDARY_DAMAGE_AOE, .DamageFilter) call AddUnitMaxState(s.target, STAT_MODIFIED, s.hptaken) call s.RemoveFromLoop() // if the buff lasts longer than the cooldown, destroy the instance endif // do your things here, dont forget to call s.destroy() somewhen // set i=i-1 endloop endmethod static method create takes nothing returns thistype local thistype s=.allocate() local integer i=0 local string str="" set s.t=NewTimer() set s.caster=SpellEvent.CastingUnit set s.target=SpellEvent.TargetUnit set s.level=GetUnitAbilityLevel(s.caster, LEARN_AID) call AbortOrder(s.caster) // cancel the spell before it goes into cooldown and costs mana // apply the blindness set .BlindnessDummy.level=s.level //set .BlindnessDummy.owningplayer=GetOwningPlayer(s.caster) // comment out this line to allow targeting friendly units call .BlindnessDummy.castOnTarget(s.target) // some nice FX call DestroyEffect(AddSpecialEffectTarget(FX, s.target, FX_ATTPT)) if DAMAGE_FRIENDLY_UNITS or IsUnitEnemy(s.target, GetOwningPlayer(s.caster)) then call UnitDamageTarget(s.caster, s.target, Damage(s.level), true, false, ATTACK_TYPE, DAMAGE_TYPE, WEAPON_TYPE) // damage the target endif // switch from casting ability to dummy ability call UnitRemoveAbility(s.caster, CAST_AID) call UnitAddAbility(s.caster, DUMMY_AID) call UnitMakeAbilityPermanent(s.caster, true, DUMMY_AID) call SetUnitAbilityLevel(s.caster, DUMMY_AID, s.level) call IssueImmediateOrder(s.caster, DUMMY_ORDER) // apply mana cost and cooldown call UnitRemoveAbility(s.caster, DUMMY_BID) // remove the buff placed by DUMMY_AID set .CooldownActive[GetUnitId(s.caster)]=true call SetTimerData(s.t, s) call TimerStart(s.t, Cooldown(s.level), false, function thistype.EndCooldown) if DEBUFF_FRIENDLY_UNITS or IsUnitEnemy(s.target, GetOwningPlayer(s.caster)) then if Stat_Taken(s.level)>=1. or Stat_Taken(s.level)<=-1. then // absolute max hp loss if GetUnitState(s.target, STAT_MODIFIED)<=Stat_Taken(s.level) then // if the current max hp of the target are smaller than the amount taken away // Reduce the amount taken away so that it leaves the unit with exactly 1 max hp. set s.hptaken=GetUnitState(s.target, STAT_MODIFIED)-1 // store the amount taken away call SetUnitMaxState(s.target, STAT_MODIFIED, 1) else set s.hptaken=Stat_Taken(s.level) // store the amount taken away call AddUnitMaxState(s.target, STAT_MODIFIED, -Stat_Taken(s.level)) endif else // relative max hp loss if GetUnitState(s.target, STAT_MODIFIED)*(1.-Stat_Taken(s.level))<1. then set s.hptaken=GetUnitState(s.target, STAT_MODIFIED)-1 // store the amount taken away call SetUnitMaxState(s.target, STAT_MODIFIED, 1) else set s.hptaken=Stat_Taken(s.level)*GetUnitState(s.target, STAT_MODIFIED) call AddUnitMaxState(s.target, STAT_MODIFIED, -s.hptaken) endif endif endif if SHOW_TT and s.hptaken!=0. then loop exitwhen i>=PatternLength if SubString(TT_PATTERN, i, i+1)=="&" and i+1<PatternLength then if SubString(TT_PATTERN, i+1, i+2)=="d" or SubString(TT_PATTERN, i+1, i+2)=="D" then if s.hptaken<0. then set str=str+TT_POSITIVE_HP_BONUS_STRING else set str=str+TT_NEGATIVE_HP_BONUS_STRING endif set str=str+I2S(R2I(RAbsBJ(s.hptaken))) set i=i+1 else set str=str+"&" endif else set str=str+SubString(TT_PATTERN, i, i+1) endif set i=i+1 endloop call TextTag_Unit(s.target, str, TT_COLOR) endif // initialize the struct here set .Structs[.Count]=s set s.i=.Count if .Count==0 then call TimerStart(.T, TICK, true, function thistype.Callback) endif set .Count=.Count+1 return s endmethod private static method onInit takes nothing returns nothing set .BlindnessDummy=xecast.create() set .BlindnessDummy.abilityid=BLINDNESS_DUMMY_AID set .BlindnessDummy.orderstring=BLINDNESS_DUMMY_ORDER set .BlindnessDummy.recycledelay=1.0 set .DamageFilter=Condition(function thistype.DamageFilterFunc) call Preload(FX) call PreloadStart() call XE_PreloadAbility(CAST_AID) call XE_PreloadAbility(DUMMY_AID) call XE_PreloadAbility(BLINDNESS_DUMMY_AID) call SetUpCOOLDOWN() call SetUpDAMAGE() call SetUpSECONDARY_DAMAGE() call SetUpSTAT_TAKEN() endmethod endstruct private function Cast takes nothing returns nothing call Data.create() endfunction globals private integer array LearnedAbilityLevel endglobals private function IsLearnAbility takes nothing returns boolean return GetLearnedSkill()==LEARN_AID endfunction private function Learning takes nothing returns nothing local unit u=GetLearningUnit() if GetLearnedSkillLevel()==1 then call UnitAddAbility(u, CAST_AID) call UnitMakeAbilityPermanent(u, true, CAST_AID) endif if Data.CooldownActive[GetUnitId(u)] then call SetUnitAbilityLevel(u, DUMMY_AID, GetLearnedSkillLevel()) else call SetUnitAbilityLevel(u, CAST_AID, GetLearnedSkillLevel()) endif set LearnedAbilityLevel[GetUnitId(u)]=GetLearnedSkillLevel() set u=null endfunction private function UnlearnedAbility takes nothing returns boolean return GetUnitAbilityLevel(GetTriggerUnit(), LEARN_AID)!=LearnedAbilityLevel[GetUnitId(GetTriggerUnit())] endfunction private function ResetAbility takes nothing returns nothing call UnitRemoveAbility(GetTriggerUnit(), CAST_AID) call UnitRemoveAbility(GetTriggerUnit(), DUMMY_AID) endfunction private function Init takes nothing returns nothing local trigger t=CreateTrigger() call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_HERO_SKILL) call TriggerAddCondition(t, Condition(function IsLearnAbility)) call TriggerAddAction(t, function Learning) set t=CreateTrigger() // just in case someone uses that damn tome of relearning // untested code call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_PICKUP_ITEM) call TriggerAddCondition(t, Condition(function UnlearnedAbility)) call TriggerAddAction(t, function ResetAbility) call RegisterSpellCastResponse(CAST_AID, Cast) // why trigger on cast? -- // well, when using SPELL_EFFECT the hero can be interrupted before actually // reaching the Cast Point (where SPELL_EFFECT is triggered). // This spell ignores the time the hero takes to reach the cast point. endfunction endlibrary |
| 08-25-2009, 11:56 PM | #2 |
Do you have permission to redistribute WILL's models and that icon from the Hive? This cannot be approved if you do not. |
| 08-26-2009, 12:04 AM | #3 |
Redistributing models from THW > Here seems fine to me, but not vice versa. I think it's fine that he's using them in spellpack, especially since he's credited. |
| 08-26-2009, 12:22 AM | #4 |
Permission is required for all redistribution of work. That's something we've always been strict in enforcing. |
| 08-26-2009, 12:48 AM | #5 |
Wait, what? Its a public resource in the Models section of WC3C. People shouldnt submit resources, if they dont want them to be used (or only be used after approval of the creator). You see, i wouldnt want to ask for permission everytime i use a public vJass library. Also, im not offering the model as a separate download (which in my eyes would be redistributing). |
| 08-26-2009, 12:51 AM | #6 |
What if someone downloads your spell, is an idiot and doesn't realize that you didn't make the special effect, and then credits you for it? This happens, frequently. Only with the original author's permission can you resubmit someone else's work. Period. |
| 08-26-2009, 01:07 AM | #7 |
I see your point, however, wouldnt that also apply to maps (protected or not)? |
| 08-26-2009, 01:13 AM | #8 |
No, because the resources in the database are all designed with the end-result of being used in a map in mind. Thus by submitting said resources to the database, you confer the right of redistribution (assuming proper accreditation) within maps and campaigns. The same rights do not exist implicitly for spells or spell packs, especially because maps are not downloaded to be cut apart and used in other maps, but spells are. |
| 08-26-2009, 01:19 AM | #9 |
We should prohibit idiots access to the internet. |
| 08-26-2009, 01:21 AM | #10 |
If that were possible, you'd better believe that I'd do it. |
| 08-26-2009, 01:27 AM | #11 |
fine, I already contacted WILL requesting permission. But getting permission for the icon to be redistributed would be kind of troublesome, since the creator of the icon hasnt visited THW since 08/02/09. Is permission for that icon really needed? If yes, ill probably just delete it from the map. Do you want me to remove the map until WILL gives his Okay? |
| 08-26-2009, 01:32 AM | #12 |
It's okay to leave it in, otherwise it's a pain in the ass for you. (More than it already is, anyways) I would recommend just ditching the icon and using something in-game, honestly. I mean, it's not like the icon is even that great. |
| 08-26-2009, 01:41 AM | #13 |
alright, i removed the icon. Any feedback for the spells? Like them? Hate them? |
| 08-26-2009, 02:33 AM | #14 |
I really like Meat Puppet. It's pretty creative and seems to work well. The other one is okay, but it seems like the delay between damage should be reduced, and the AoE on the second burst should be increased. I'm sure a map maker could easily change those if needed, though. You might consider adding a blood effect to the Meat Puppet target, like some blood spewing out as he's dragged around. |
| 08-26-2009, 03:37 AM | #15 |
Deaod explained to me over IRC that the cool thing about Null Blast is that it allows you to bypass "ministuns" and the like while at the same time preventing the player from "abusing" the spell. Now that is quite a nifty application in AoS type games in my opinion. Meat Puppet is cool too. |
