• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

Volty Crush 1.30 by Adiktuz

  • Like
Reactions: deepstrasz
Volty Crush


Casts a magical curse at the target unit which causes a powerful explosion after a set time damaging nearby enemy units. Deals 10 damage to the target every second of the duration.

Level 1 - 75 explosion damage, lasts 2 seconds.
Level 2 - 150 explosion damage, lasts 3 seconds.
Level 3 - 225 explosion damage, lasts 4 seconds.
JASS:
/*
      Easy Editables:
 Special effects

 Duration of curse

 AoE of explosion

 Damage during the duration 

 Damage of explosion to target

 Damage of explosion to AoE

 Attack type of the spell

 Damage type of the spell

 Buff
*/

JASS:
/*
-------------Volty Crush 1.3 by Adiktuz---------------------------------

 What is Volty Crush?
    -By default its an ability which inflicts the target with a magical curse which explodes after a set time
     damaging the target, and any unit near it. Also deals damage to the target during the duration
 
 Requirements
     -Warcraft 3 - TFT (1.24e and up)
     -NewGenWE (comes with the JNGP)
     -Jass Helper (0.A.2.A or 0.A.2.B)
     -Basic editor knowledge
     
 How to import
    -Open your map in NewGenWE
    -Create a new trigger and convert it to custom script
    -Copy this library and paste it into the new trigger, replacing anything inside that trigger
    -Modify the modifiable globals and functions to fit the new map or settings that you would like
    -You can copy the ability or just make a new one
    -Copy the buff ability and the buff and modify the rawcodes here (if you would create a new buff, Use slow aura as a base for the buff ability)
    
 How to use
    -Once you have done the steps on importing, you're good to go.
    
 Credits:
    -Vexorian For TimerUtils
    -Bribe for Table
*/
library VoltyCrush requires TimerUtils, Table

    globals
        //Rawcode of the spell
        private constant integer SPELL_ID = 'A000'
        
        //Rawcode of the buff ability
        private constant integer BUFF_SPELL_ID = 'A001'
        
        //Rawcode of the buff itself
        private constant integer BUFF_ID = 'B000'
        
        //The path to the special effect which is shown on the unit every tick 
        private constant string TICK_FX = "Abilities\\Spells\\Items\\AIlb\\AIlbSpecialArt.mdl"
        
        //The path to the special effect when the ability explodes
        private constant string EXPLODE_FX = "Abilities\\Spells\\Human\\ThunderClap\\ThunderClapCaster.mdl"
        
        //The path to the special effect shown on the units that will get hit by the explosiong
        private constant string TARGET_FX = "Abilities\\Spells\\Human\\StormBolt\\StormBoltMissile.mdl"
        
        //The attachment point of TICK_FX
        private constant string TICK_AP = "chest"
        
        //The attachment point of EXPLODE_FX
        private constant string EXPLODE_AP = "origin"
        
        //The attachment point of TARGET_FX
        private constant string TARGET_AP = "chest"
        
        //this is the timer interval in which duration damage takes place
        private constant real TICK = 0.50
        
        //The attack type of the spell
        private constant attacktype AT = ATTACK_TYPE_MAGIC
        
        //The damage type of the spell
        private constant damagetype DT = DAMAGE_TYPE_MAGIC
        
        //Checks whether the curses will explode upon death of target or just fade away
        private constant boolean ExUponDeath = false
        
        private Table VoltyTab
    endglobals
    
    //Use this function to calculate the duration of the curse before it explodes
    private function GetTime takes integer level returns real
        return level + 1.00
    endfunction
    
    //Use this function to get the explosion radius
    private function GetRadius takes integer level returns real
        return 100.00 + level*(25)
    endfunction
    
    //Use this function to calculate the damage per tick
    private function GetTickDamage takes integer level returns real
        return 10*TICK
    endfunction
    
    //Use this function to calculate the explosion damage to the caster
    private function GetExploTargetDamage takes integer level returns real
        return level*75.00
    endfunction
    
    //Use this function to calculate the explosion damage to the units around the target
    private function GetExploAoeDamage takes integer level returns real
        return level*75.00
    endfunction
    
    //The initialization module... I used a module for init because Nestharus said that its better
    private module init
    
        private static method onInit takes nothing returns nothing
            local trigger t = CreateTrigger()
            local integer i = 0
            loop
                exitwhen i > 15
                call TriggerRegisterPlayerUnitEvent(t, Player(i), EVENT_PLAYER_UNIT_SPELL_EFFECT, null)
                set i = i + 1
            endloop
            call TriggerAddCondition(t, Condition(function thistype.FilterFunction))
            set VoltyTab = Table.create()
            set t = null
        endmethod
    
    endmodule
    
    //The struct for the spell
    private struct VoltyStruct
        
        //We implement the module here
        implement init
        
        //The struct data holders
        static thistype data
        static thistype datum
        static thistype datab
        static group dgroup = CreateGroup()
        static unit triggerunit = null
        unit target
        unit caster
        player owner
        integer level
        real time
        
        //This method runs when a curse finishes and checks if the unit has other instances of the spell, if none it removes the buff
        method RemoveBuff takes nothing returns nothing
            if VoltyTab[GetHandleId(this.target)] == 0 then
                /*I remove both the ability and the buff since if only the ability is removed,
                  the buff will stay for about 2 seconds...
                */
                call UnitRemoveAbility(this.target, BUFF_SPELL_ID)
                call UnitRemoveAbility(this.target, BUFF_ID)
            endif
        endmethod
        
        //This method is for dealing the damage into the unit
        method OnHit takes unit fu returns nothing
            if fu != this.target then
                call DestroyEffect(AddSpecialEffectTarget(TARGET_FX, fu, TARGET_AP))
                call UnitDamageTarget(this.caster, fu, GetExploAoeDamage(this.level), false, false, AT, DT, null)
            else
                call UnitDamageTarget(this.caster, fu, GetExploTargetDamage(this.level), false, false, AT, DT, null)
            endif
        endmethod
        
        //The filter for the group pick, I merged the actions to the group filter for better performance
        static method OnHitFilter takes nothing returns boolean
            local unit fu = GetFilterUnit()
            if IsUnitEnemy(fu, datab.owner) and /*
            */ GetWidgetLife(fu) > .405 and /*
            just for double checking...
            */ (not IsUnitType(fu, UNIT_TYPE_DEAD)) and /*
            */ not IsUnitType(fu, UNIT_TYPE_STRUCTURE) then
                call datab.OnHit(fu)
            endif
            set fu = null
            return false
        endmethod
        
        //the method run when the timer reaches 0.00 or when the target dies
        method OnExplode takes boolean dead returns nothing
            //This checks if the unit is dead and whether death is registered as a cause of explosion
            if not (dead and not ExUponDeath) then
                call GroupEnumUnitsInRange(thistype.dgroup, GetUnitX(this.target), GetUnitY(this.target), GetRadius(this.level), Condition(function thistype.OnHitFilter) )
                call DestroyEffect(AddSpecialEffectTarget(EXPLODE_FX, this.target, EXPLODE_AP))
            endif
        endmethod
        
        //The method which is run after each interval
        private static method Refresh takes nothing returns nothing
            local boolean dead = false
            set datab = GetTimerData(GetExpiredTimer())
            set datab.time = datab.time - TICK
            set dead = GetWidgetLife(datab.target) < .405
            call DestroyEffect(AddSpecialEffectTarget(TICK_FX, datab.target, TICK_AP))
            call UnitDamageTarget(datab.caster, datab.target, GetTickDamage(datab.level), false, false, AT, DT, null)
            if datab.time <= 0.00 or dead then
                set VoltyTab[GetHandleId(datab.target)] = VoltyTab[GetHandleId(datab.target)] - 1
                call datab.OnExplode(dead)
                call datab.RemoveBuff()
                call datab.destroy()
                call ReleaseTimer(GetExpiredTimer())
            endif
        endmethod
        
        //The method run when the spell VoltyCrush is used
        private static method create takes unit caster, unit target, integer level , player owner returns thistype
            local timer t = NewTimer()
            set data = thistype.allocate()
            set data.target = target
            set data.level = level
            set data.owner = owner
            set data.caster = caster
            set data.time = GetTime(level)
            call UnitAddAbility(target, BUFF_SPELL_ID)
            call SetTimerData(t, data)
            call TimerStart(t, TICK, true, function thistype.Refresh) 
            set VoltyTab[GetHandleId(target)] = VoltyTab[GetHandleId(target)] + 1
            set t = null
            return data
        endmethod
        
        //The filter function used by the trigger, this also contains the actions for the spell
        private static method FilterFunction takes nothing returns boolean
            set thistype.triggerunit = GetTriggerUnit()
            if GetSpellAbilityId() == SPELL_ID then
                call thistype.create( thistype.triggerunit, GetSpellTargetUnit(), GetUnitAbilityLevel(thistype.triggerunit, SPELL_ID), GetOwningPlayer(thistype.triggerunit))
            endif
            return false
        endmethod
        
    endstruct
    
endlibrary
Changelog

JASS:
/*

 Version 1.1 - changed the library into a scope
                 - added a check for UNIT_TYPE_DEAD, for double checking
                 - set the triggering unit into a static member on the filter function
 Version 1.2 - made the struct private
                 - added a buff to the curse
                 - made the filter function return false

 Version 1.3 - Made it run TimerUtils (by Vexorian) and Table (by Bribe)
*/
Keywords:
lightning, vJASS, gui, jass, warcraft, arcane, curse, voodoo, magic, electric, rai, over time, damage, timers, adiktuz, elements,
Contents

Volty Crush 1.3 (Map)

Reviews
Bribe There is some room for efficiency which I will get to telling you about in the future, but this is really quality material. Approved.

Moderator

M

Moderator

Bribe

There is some room for efficiency which I will get to telling you about in the future, but this is really quality material. Approved.
 
Level 22
Joined
Nov 14, 2008
Messages
3,256
in the filter method you probably should use a local for GetTriggerUnit

why is the struct not private?

I probably would have added IsUnitType(whichunit, UNIT_TYPE_DEAD) into the death check

why are you using a library :p yet it doesnt matters

this is great anyway :) nice to see some stuff from you again
 
Level 14
Joined
Nov 18, 2007
Messages
1,084
I didn't made the struct private because is gives me an error (the module cannot access the method inside the struct)
Use thistype instead of the struct name.

It's a better idea to put the implement init at the end of the struct order.

In the Filter Function, you should always return false since there is no action for the trigger to run.

The spell itself is rather simple. It would be nice if it supported a buff to show the curse.
 
Level 15
Joined
Oct 16, 2010
Messages
941
Out of curiousity, what is the benefit of using the player-specific effect event instead of the generic one?

JASS:
private static method onInit takes nothing returns nothing
            local trigger t = CreateTrigger()
            local integer i = 0
            loop
                exitwhen i > 15
                call TriggerRegisterPlayerUnitEvent(t, Player(i), EVENT_PLAYER_UNIT_SPELL_EFFECT, null)
                set i = i + 1
            endloop
            call TriggerAddCondition(t, Condition(function VoltyStruct.FilterFunction))
            set t = null
        endmethod
 
Level 8
Joined
Jul 14, 2010
Messages
235
ok, my first time importing Jass and using JNGP. When I do the Syntax Check, this gives me 286 Script Errors, still the spell is working 95% of the times. And yes, sometimes its not working at all, is all this normal?
I really need to learn importing this, so I can import other Jass spells without problems ;)

Thanks!
 
Level 8
Joined
Jul 14, 2010
Messages
235
The JassHelper is version 0.A.2.B and WC3 is 1.25.1.6397, and the "Disable WE syntax check" is checked. Should I just ignore Script Errors that pops up when clicking "Syntax Check"? The spell is working despite of all these massive errors :p

Also, http://www.hiveworkshop.com/forums/spells-569/sun-ray-1-5-1-a-103778/ gives a lot of lag when imported but not when casting it in the test map. Could this lag be result of something I do wrong?
 
hmmm... so that's why... Dont use Syntax check... its outdated... just save the map, since ur using JNGP, jass helper will do a syntax check while compiling the map...

Updated! It now runs using Vexorian's TimerUtils and Bribe's Table

EDIT: This spell might see some updates in the near future, mainly for cleaning up the code and some optimization... I might also make it run under T32 instead of TimerUtils...
 
Top