1. This site uses cookies. By continuing to use this site, you are agreeing to our use of cookies. Learn More.
  2. Welcome to the new Hive! Be advised that we're still working on the site. There are still many rough edges, so please bear with us.
    Dismiss Notice
  3. The 14th Icon Contest is still in progress (and may be extended). You can still make it in time.
    Dismiss Notice
  4. The 25th Texturing Contest has started! Contestants are to create a skin representing a dark elf person/being or any construct related to it using the vanilla models or the custom ones found on the site.
    Dismiss Notice
  5. Buy it, use it, break it, fix it, trash it, change it, mail - upgrade it. Join (Optionally) Paired Techtree Contest #11 - Techno Magic now!
    Dismiss Notice
  6. Voting squad, line up! Cast your vote on the poll for Modeling Contest #29 - Squads!
    Dismiss Notice
  7. Hero Contest #8 is up and running! This time it's a joint contest between artists and coders. Go here for team matchmaking.
    Dismiss Notice
  8. The poll for the theme of our StarCraft II Terraining Contest is up. Cast your note now!
    Dismiss Notice
  9. The ninth Concept Art Contest has launched. Enter now!
    Dismiss Notice
  10. The member Kam is making HIVE coasters. Take a look. For every coaster you buy, Hive gets $1.
    Dismiss Notice

Warlock Spells 1.3.7

Submitted by Deaod
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

Credits
- Archmage Owenalacaster for all his valuable help and feedback
- WILL THE ALLMIGHTY for his CurseBolt model
- Earth-Fury for his UnitMaxState library
- cohadar for his TextTag library
- Rising_Dusk for his libraries: GroupUtils, UnitIndexingUtils, LastOrder, AbortSpell, Knockback and TerrainPathability
- Anitarf for his SpellEvent library
- Vexorian for JassHelper, TimerUtils, Table, SimError, xe and the dummy model
- PipeDream for Grimoire
- PitzerMike for JassNewGenPack and DestructableLib
- MindWorX for JassNewGenPack
- SFilip for TESH


Changelog
26/08/2009 - Version 1.2.3
- initial public release

27/08/2009 - Version 1.2.4
- removed the custom icon (itll still be in the submission).
- fixed a bug with Meat Puppet if the target was too close to the hero.

28/08/2009 - Version 1.3.0
- Both spells can now target friendly units (Null Blast has received an option whether to damage friendly units or not).
- Meat Puppet has received a Release Puppet sub-ability, which immediately breaks the connection.

28/08/2009 - Version 1.3.1
- made Release Puppet replace Meat Puppet when appropriate
- added an option to disable Release Puppet
- fixed the icon of Null Blast's Buff
- deleted a "\n" from SimError.

04/09/2009 - Version 1.3.2
- made the facing angle of the target unit be changed when swinging it around. This allows impressive chains (testedly).
- dropped the blinding function of Null Blast completely.
- changed the way Meat Puppet handles multiple instances on the same caster (from extended arrays to Table).

05/09/2009 - Version 1.3.3
- fixed a critical bug resulting in Meat Puppet not working at all (yay for releasing untested versions)

08/09/2009 - Version 1.3.4
- fixed a few theoretical bugs related to unlearning.
- added the option of reducing the max state by an amount relative to the current
- made several adjustments so Null Blast works with negative STAT_TAKEN amounts

11/09/2009 - Version 1.3.5
- updated to EFs new version of SetUnitMaxState

25/09/2009 - Version 1.3.6
- the meat puppet now lags slightly behind the casters movement.
- added several new options to both spells
- fixed Null Blast not working under certain conditions (mainly after importing into a new map).

11/11/2009 - Version 1.3.7
- Meat Puppet now works with flying units. Flying units will only knock back other flying units (and ground units will only knock back other ground units).
- Fixed a theoretical bug with Null Blast (in cases where buff and cooldown expire simultaneously and the EndCooldown function gets executed before the Callback function)


In-Game-Commands
- "-reset": spawns some footmen
- "-level <level>": Set the heros level to the level you specified. Note that you cannot decrease it that way.
- "-handleid": creates a location, displays its handlid-0x100001, and then destroys it.
- "-credits": displays credits
- "-commands": displays this list
- "-clear": removes all messages ingame


Meat Puppet
Code (vJASS):
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[/hidden]


Null Blast
Code (vJASS):
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[/hidden]
Contents

Contest Map Deaod 1.3.7 (Map)

  1. 21:06, 29th Aug 2009
    hvo-busterkomo: My only complaint (opinion, meh) is that Null Blast tried to produce too many effects. Anyways, the spells are great, approved.
     
  2. Devalut

    Devalut
    Joined:
    Feb 9, 2009
    Messages:
    270
    0/5
    cant test it no countering units or hero's in the map
    thus Fail
     
  3. Deaod

    Deaod
    Joined:
    Nov 18, 2007
    Messages:
    807
    ah, but you DO see the messages at the beginning, right?

    and the hidden box titled In-Game-Commands isnt too hard to miss either.
     
  4. busterkomo

    busterkomo
    Joined:
    Jun 17, 2007
    Messages:
    1,421
    I'll give these spells a full review tommorow but I'll just say this; if you're aiming at maximum modularity then you might want to use TimedLoop.
     
  5. Deaod

    Deaod
    Joined:
    Nov 18, 2007
    Messages:
    807
    No, thanks. TimedLoop couldnt be used in Null Blast (since instances of Null Blast might go on without being added to the looping). And using TimedLoop might make the looping slower than using, say KeyTimers2 + attaching. Also, TimedLoop doesnt allow the interval to be changed on a per-implementation basis (which might be necessary in bigger maps).
     
  6. Devalut

    Devalut
    Joined:
    Feb 9, 2009
    Messages:
    270

    oh I'm sorry, maybe you should put in your "Hidden box" READ BEFORE PLAYING
    and maybe it might not be so hard to miss and unless theres command in that "Hidden" Box of your's that says Spawn hero then there's no hero so....... yeah.
     
  7. Deuterium

    Deuterium
    Joined:
    Mar 17, 2009
    Messages:
    1,325
    Hehe wow that's alot of systems :p good job on the spells...
    Yet, imports could be avoided you know ;) just gotta use the creative part of your brain :p
     
  8. Dark_Dragon

    Dark_Dragon
    Joined:
    Jul 19, 2007
    Messages:
    585
    well nice spells Deaod!

    they are quite readable and easy editable, but in my option the both spells core should have some more comments...

    using nice amount of librarys, now i know what did you mean back then and yeah its nice and modular way so gj on that.

    we should as well know that one line in vjass can be a far more lines in jass as well as adding extra objects and taking handle id-s! TimedLoop is actually quite slow, even loops by blizzard are extremely slow, the only loops which are fast are ForGroup and such, all this custom functions, loops and stuff in VM are really slow... and slowing them even more is really an bad idea (thats in my option)

    now few things i would chage in this spells are (not needed but would make em little bit more faster):

    insted of having multiple init global array variable functions... i would just make once Setup function and would set all init array variables there, as well as write few comments, then on your init function just call that setup one... well its coz functions take RAM but as i said its crazy so its not needed to chage...

    i would avoid using functions like:

    Code (vJASS):

    call RegisterSpellEffectResponse(AID, CreateProxy)
            call RegisterSpellCastResponse(AID, CheckValidTarget)
     


    this are converted as pointer functions and they take 4 handle id-s (each pointer function takes 4 ids)

    1-trigger
    2-triggeraction
    3-triggercondition
    4-conditionfunc

    well thats bad coz many systems use GetHandleId(h)-MIN_H_ID and that can pass JASS_MAX_ARRAY_SIZE (8191)... we should know that many maps have a lot of trees and / or destructbales of any type... each destructable takes one id! as well as maps which have a lot of units like footman frenzy... i mean this

    well units take id as long as they are in game... even if they are dead.

    as well calling pointer functions is much slower then calling "already slow VM functions"
    as well pointer functions take extra RAM coz they require extra VM functions + global variables which are basically arguments... in your case you have no arguments, so i skip this.


    when assigning value to real or comparing...
    Code (vJASS):

    local real x = 0 //bad
    local real x = 0. //good
    local real x = .0//good
     


    i found that my scripts crashed coz i did not do that! it happend two times in my life, quite rare but true...

    Rating:
    Creativity: 4/5
    Documentation: 3/5
    Coding: 4.5/5
    MUI: true
    Editable: 4.5/5
    Externals: +Extra points

    Overall: 5/5

    once again gj on spells!
    ~DD
     
  9. Deaod

    Deaod
    Joined:
    Nov 18, 2007
    Messages:
    807
    I wouldnt, since the alternative would consume even more handleids (creating a trigger with condition and action plus events). And i wouldnt have the speed advantage of using an event stack.

    My thinking with the Array initializers is that since theyre only used once, they will get inlined. Plus, they are more easily changeable.

    If your script crashes WC3 because of an implicit conversion from integer to real, you have serious problems.
    I know it might not be good style, but w/e. I dont use it in a == or != comparison so its not that crucial.

    Edit: Are there any parts of the script you dont understand? Tell me, and ill add comments to it for the next version.
     
    Last edited: Aug 26, 2009
  10. Dark_Dragon

    Dark_Dragon
    Joined:
    Jul 19, 2007
    Messages:
    585
    no no everything is fine, script is readable but i thought that you might want to add some comments for yourself and "other who may have lesser knowledge of vjass" but for me its readable and code is good.

    ahh about this "pointer functions"! yeah i know what you want to say and its really not needed to update this, it was just "my suggestion"...

    what i wanted to say is that when you load trigger by urself then u create:

    trigger, triggeraction, triggercondition, conditionfunc! same as 1 pointer function... however if you use two of them

    Code (vJASS):

    call RegisterSpellEffectResponse(AID, CreateProxy)
            call RegisterSpellCastResponse(AID, CheckValidTarget)
     


    this is what i did mean! anyway this are two triggers, in jass you could have add this events to only one trigger and use GetTriggerEventId! its more advanced and lesser readable thats why i said its really not needed to change...

    ehh about that real 0.! yeah i still dont know why it crashed but well... it was fixed, i think its blizzards own work with pointers thinking that real was basically integer... since in truth real is same as integer (long) difference is that real is an (long pointer at float) so it might have chage that pointer or smth...

    anyway i think ur spells are good enough already, well maybe as i said add comments and so but not needed for me xD

    as well about functions which set array or smth! i know some of them are inlined but i thought that reducing few lines and not declaring funcs would save a little of RAM... however all stuff i pointed out was unnecessary to change :)

    Greets!
    ~DD
     
  11. Deaod

    Deaod
    Joined:
    Nov 18, 2007
    Messages:
    807
    The problem is: You'd probably use something like TriggerRegisterAnyUnitEventBJ, which creates 16 handles. And youd use it twice. That makes 32 new handles. For events only.
    I think using the event stack is better.
     
  12. Dark_Dragon

    Dark_Dragon
    Joined:
    Jul 19, 2007
    Messages:
    585
    indeed better! and i was just thinking and found solution to all this event and trigger id-s to reduce by max amount!

    Code (vJASS):

    function onSpellEffect takes code func returns nothing
     


    this would create only one trigger action... and i think ill do that, for example ill just create one trigger and attach actions to him... as well ill just once register events and hurry no loops, no global variables, pointer functions...

    the only rule would be not to use sleeps! but anyway pointer functions which are called by .evaluate() as well cant use sleeps, only .execute()

    anyway your pointer functions are deff better then "my suggestion" coz of this extra events.
     
  13. Deaod

    Deaod
    Joined:
    Nov 18, 2007
    Messages:
    807
    Version 1.3.0

    Spells should now allow much better tactical use.

    Version 1.3.1
     
    Last edited: Aug 28, 2009
  14. Archangel_Tidusx

    Archangel_Tidusx
    Joined:
    Feb 24, 2007
    Messages:
    1,076
    Try reading the description next time
     
  15. Deaod

    Deaod
    Joined:
    Nov 18, 2007
    Messages:
    807
    Version 1.3.2
     
  16. Deuterium

    Deuterium
    Joined:
    Mar 17, 2009
    Messages:
    1,325
    I wonder, if two units cast meat puppet on the same target, what would happen? :p
     
  17. Deaod

    Deaod
    Joined:
    Nov 18, 2007
    Messages:
    807
    Code (vJASS):
            private constant string ERROR_TARGET_ALREADY_IN_INSTANCE = "Target already targeted by another instance of this spell!"


    It would display an error.

    Edit:

    Discovered some errors. 1.3.3 underway.

    Edit2:

    Version 1.3.3
     
    Last edited: Sep 5, 2009
  18. Deuterium

    Deuterium
    Joined:
    Mar 17, 2009
    Messages:
    1,325
    Oh I see... smart ;)
     
  19. Deaod

    Deaod
    Joined:
    Nov 18, 2007
    Messages:
    807
    Version 1.3.4