• 🏆 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!

vJass Spells by Roflcoptor v1.0d

This bundle is marked as useful / simple. Simplicity is bliss, low effort and/or may contain minor bugs.
  • Like
Reactions: MortAr
Well, these are my first three vJass spells.

Their rather basic, and some might not be very creative, still they feature great configurability.

Please notify me if I have overlooked any leaks, or made mistakes.

[highlight]IMPORTANT: You need the JassNewGenPack in order to edit this spell.[/code]

Spell Descriptions
Blink Strike

The caster instantly teleports to his target, dealing every unit around it, and if it is a foe, damaging the target.
Armageddon

Summons essences of lightning around the caster damaging every enemy.
Charge
Charges towards the target at high speed, dealing damage on impact.

Blink Strike
JASS:
//====================================================================================================\\
//=====================================================SETUP==========================================\\
//====================================================================================================\\
scope BlinkStrike initializer Init
    globals
        private constant integer SpellID            = 'A001'
        private constant string  CasterEffect       = "Abilities\\Spells\\Undead\\ReplenishMana\\ReplenishManaCasterOverhead.mdl" 
        private constant string  TargetEffect       = "Abilities\\Spells\\Other\\Stampede\\StampedeMissileDeath.mdl" 
        private constant real    ImpactDamagePerLvl = 100
        private constant real    GroupDamagePerLvl  = 50
        private constant real    GroupAoE           = 300
    endglobals

    private function UnitsAoE takes unit target returns boolean
        return (GetWidgetLife(target) > 0.405) and (IsUnitType(target, UNIT_TYPE_STRUCTURE) == false) and (IsUnitType(target, UNIT_TYPE_MAGIC_IMMUNE) == false) and (IsUnitType(target, UNIT_TYPE_MECHANICAL) == false) and (IsUnitEnemy(target, GetTriggerPlayer()) == true)
    endfunction
//====================================================================================================\\
//====================================================SETUP END=======================================\\
//====================================================================================================\\
    globals
        private boolexpr b
    endglobals
    
    private function Conditions takes nothing returns boolean
        return GetSpellAbilityId() == SpellID
    endfunction

    private function Targets takes nothing returns boolean
        return UnitsAoE(GetFilterUnit())
    endfunction
    
    private function GroupDamage takes nothing returns nothing
        call UnitDamageTarget(GetTriggerUnit(), GetEnumUnit(), GroupDamagePerLvl, true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_UNKNOWN, WEAPON_TYPE_WHOKNOWS)
    endfunction
    
    private function Actions takes nothing returns nothing
        local unit c = GetSpellAbilityUnit()
        local unit t = GetSpellTargetUnit()
        local real x = GetUnitX(c)
        local real y = GetUnitY(c)
        local group g = CreateGroup()
        call DestroyEffect(AddSpecialEffect(CasterEffect,x,y))
        set x = GetUnitX(t)
        set y = GetUnitY(t)
        call SetUnitX(c, x)
        call SetUnitY(c, y)
        call DestroyEffect(AddSpecialEffect(TargetEffect,x,y))
        if IsUnitEnemy(t, GetTriggerPlayer()) then
            call UnitDamageTarget(c, t, I2R(GetUnitAbilityLevel(c,SpellID))*ImpactDamagePerLvl, true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_UNKNOWN, WEAPON_TYPE_WHOKNOWS)
            else
        endif
        call GroupEnumUnitsInRange(g, x, y, GroupAoE, b) 
        call ForGroup(g, function GroupDamage)
        set c = null
        set t = null
    endfunction

    private constant function NoLeakFilter takes nothing returns boolean
        return true
    endfunction

    private function Init takes nothing returns nothing
        local trigger t = CreateTrigger(  )
        local integer i = 0
        local filterfunc ff = Filter(function NoLeakFilter)
        loop
            call TriggerRegisterPlayerUnitEvent(t, Player(i), EVENT_PLAYER_UNIT_SPELL_EFFECT, ff)
            set i = i + 1
            exitwhen i == bj_MAX_PLAYER_SLOTS
        endloop
        set i = 0
        set b = Condition(function Targets)
        call TriggerAddCondition( t, Condition( function Conditions ) )
        call TriggerAddAction( t, function Actions )
        call Preload(CasterEffect)
        call Preload(TargetEffect)
        call PreloadStart()
        call DestroyFilter(ff)
        set ff = null
    endfunction
endscope
Armageddon
JASS:
//====================================================================================================\\
//=====================================================SETUP==========================================\\
//====================================================================================================\\
scope Armageddon initializer Init
    globals
        private constant integer SpellID            = 'A002'
        private constant string  CasterEffect       = "Abilities\\Spells\\Orc\\Purge\\PurgeBuffTarget.mdl" 
        private constant string  TargetEffect       = "Abilities\\Spells\\Items\\AIlb\\AIlbSpecialArt.mdl" 
        private constant real    GroupAoE           = 300
        private constant integer EffectsPerLvl      = 3
        private constant real    AoE                = 1200
        private string array Effect [2]
    endglobals

    private function GroupDamageSetup takes integer l returns real
        return I2R(l * 100)
    endfunction
    
    private function UnitsAoE takes unit target returns boolean
        return (GetWidgetLife(target) > 0.405) and (IsUnitType(target, UNIT_TYPE_STRUCTURE) == false) and (IsUnitType(target, UNIT_TYPE_MAGIC_IMMUNE) == false) and (IsUnitType(target, UNIT_TYPE_MECHANICAL) == false) and (IsUnitEnemy(target, GetTriggerPlayer()) == true)
    endfunction

    private function ReturnStrings takes nothing returns nothing
        set Effect[1] = "Abilities\\Spells\\Demon\\RainOfFire\\RainOfFireTarget.mdl"
        set Effect[2] = "Units\\Demon\\Infernal\\InfernalBirth.mdl" 
        set Effect[3] = "Units\\Demon\\Infernal\\InfernalBirth.mdl"
    endfunction
//====================================================================================================\\
//====================================================SETUP END=======================================\\
//====================================================================================================\\
    globals
        private boolexpr b
    endglobals

    private function Targets takes nothing returns boolean
        return UnitsAoE(GetFilterUnit())
    endfunction
    
    private function GroupDamage takes nothing returns nothing
        call UnitDamageTarget(GetTriggerUnit(), GetEnumUnit(), GroupDamageSetup(GetUnitAbilityLevel(GetTriggerUnit(), SpellID)), true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_UNKNOWN, WEAPON_TYPE_WHOKNOWS)
        call DestroyEffect(AddSpecialEffectTarget(TargetEffect, GetEnumUnit(), "chest"))
    endfunction
        
    private function Conditions takes nothing returns boolean
        return GetSpellAbilityId() == SpellID 
    endfunction


    private function Actions takes nothing returns nothing
        local unit c = GetTriggerUnit()
        local real X1 = GetUnitX(c)
        local real X2
        local real Y1 = GetUnitY(c)
        local real Y2
        local group g = CreateGroup()
        local integer i = 0
        local integer r = 0
        local integer e = (GetUnitAbilityLevel(c, SpellID)*EffectsPerLvl)
        call DestroyEffect(AddSpecialEffect(CasterEffect, X1, Y1))
        loop
            exitwhen i == e
            set i = i + 1
            set X2 = GetRandomReal((X1 - (AoE/2)), (X1 + (AoE/2)))
            set Y2 = GetRandomReal((Y1 - (AoE/2)), (Y1 + (AoE/2)))
            set r = GetRandomInt(1, 3)
            call DestroyEffect(AddSpecialEffect(Effect[r], X2, Y2))
            call GroupEnumUnitsInRange(g, X2, Y2, GroupAoE, b)
            call ForGroup(g, function GroupDamage)
        endloop
        set i = 0
        set c = null
        set r = 0
        set e = 0
    endfunction

    private constant function NoLeakFilter takes nothing returns boolean
        return true
    endfunction

    private function Init takes nothing returns nothing
        local trigger t = CreateTrigger(  )
        local integer i = 0
        local filterfunc ff = Filter(function NoLeakFilter)
        loop
            call TriggerRegisterPlayerUnitEvent(t, Player(i), EVENT_PLAYER_UNIT_SPELL_EFFECT, ff)
            set i = i + 1
            exitwhen i == bj_MAX_PLAYER_SLOTS
        endloop
        set i = 0
        set b = Condition(function Targets)
        call ReturnStrings()
        set i = 0
        call TriggerAddCondition( t, Condition( function Conditions ) )
        call TriggerAddAction( t, function Actions )
        call Preload(CasterEffect)
        call Preload(TargetEffect)
        call PreloadStart()
        call DestroyFilter(ff)
        set ff = null
    endfunction
endscope
Charge
JASS:
scope RealisticCharge initializer Start
    globals
//!*******************************SETTINGS*************************!\\
        //! general settings
        private constant integer SpellID        = 'A000'
            //^The spell's rawcode
        private constant real    TimerInterval  = 0.02
            //^The interval used by the timer. I suggest leaving it at 0.02
        private constant integer  Speed         = 30
            //^The speed, with which the caster charges
        private constant string   ChargeSFX     = "Environment\\LargeBuildingFire\\LargeBuildingFire1.mdl"
            //^The SFX occuring every (TimerInterval) seconds at the caster's position
        private constant string   TargetSFX     = "Abilities\\Spells\\Other\\Stampede\\StampedeMissileDeath.mdl"
            //^The SFX created upon impact
        private constant string   CastAttach    = "Abilities\\Weapons\\PhoenixMissile\\Phoenix_Missile_mini.mdl"
            //^The attachment created at the AttachPt
        private constant string   AttachPt      = "weapon"
            //^The point where the attachment is created.
        private constant string   CastAnim      = "attack slam"
            //^The animation played every "AnimInterval" 
        private constant real     AnimInterval  = 0.5
            //^The Interval in which the animation is played.
        private constant boolean  PathingCheck  = false
            //^Determines whether the caster should stop, when he has reached an unpathable point
        private constant boolean  DestroyTrees  = true
            //^This sets if trees are destroyed.
        private constant real     TreesAoE      = 200
            //^Sets the radius in which trees are destroyed.
    endglobals
    
    private function Damage takes integer level returns integer
        return (level*100)
            //^The damage dealt upon impact. If you don't want the level to influence the damage,
            //^simply return the wanted damage
    endfunction

//!*******************************SETTINGS END*********************!\\
    
    globals
        private boolexpr b
    endglobals
    
       //Filter for leakless Event
    private constant function NoLeakFilter takes nothing returns boolean
        return true
    endfunction
    
        //The condition ;D
    private function condition takes nothing returns boolean
        return GetSpellAbilityId() == SpellID
    endfunction
    
    private function KillTrees takes nothing returns nothing
        call KillDestructable(GetEnumDestructable())
    endfunction
    
        //Main Struct
    private struct Spell
            //Needed variables
        private unit    caster  //The casting unit
        private unit    target  //The target ;P
        private real    x       //The X-Coordinate, which determines the new position of the caster
        private real    y       //The Y-Coordinate, which determines the new position of the caster
        private real    x1      //The X-Coordinate, which determines the start position of the caster
        private real    y1      //The Y-Coordinate, which determines the start position of the caster
        private real    x2      //The X-Coordinate, which determines the position of the target
        private real    y2      //The Y-Coordinate, which determines the position of the target
        private integer steps   //This will be the number of single moves I have to do
        private integer level   //The level for damage calculation reasons
        private integer curstep //The counter, which step is currently being run
        private integer animstep//Tells the game when the animation should be played
        private effect  attach  //The attachment
        private boolean finished//Neccesary, for not damaging target, if unit stopped, because of unpathable terrain.
        private rect    r       //Needed for tree destroy
       
            //Neccesary struct variables
        private static Spell array indx
        private static integer     counter = 0
        private static timer       time    = CreateTimer()
                
                //The "real" actions done every Interval
            static method Execution takes nothing returns nothing
                local Spell d
                local integer i = 0
                loop
                    exitwhen i >= Spell.counter
                    set d = Spell.indx[i]
                        //Counting the curstep one up, so it will stop when it has reached steps
                    set d.curstep = d.curstep + 1
                    if d.curstep < d.steps then
                            //Setting new X and Y to old X/Y + (distance between cast and target X/Y) / steps 
                        set d.x = d.x + ((d.x2 - d.x1) / d.steps)
                        set d.y = d.y + ((d.y2 - d.y1) / d.steps)
                        if DestroyTrees == true then
                            set d.r = Rect(d.x - TreesAoE, d.y - TreesAoE, d.x + TreesAoE, d.y + TreesAoE)
                            call EnumDestructablesInRect(d.r, b, function KillTrees)
                            call RemoveRect(d.r)
                            set d.r = null
                        endif    
                        if PathingCheck == true then
                            if IsTerrainPathable(d.x, d.y, PATHING_TYPE_WALKABILITY) == false then
                                call SetUnitX(d.caster, d.x)
                                call SetUnitY(d.caster, d.y)
                                    //SFX
                                call DestroyEffect(AddSpecialEffect(ChargeSFX, d.x, d.y))
                                if d.animstep < R2I(AnimInterval / TimerInterval) then
                                    set d.animstep = d.animstep + 1
                                else
                                    set d.animstep = 0
                                    call SetUnitAnimation(d.caster, CastAnim)
                                endif
                            else
                                set d.curstep = d.steps
                                set d.finished = false
                            endif
                        else
                            call SetUnitX(d.caster, d.x)
                            call SetUnitY(d.caster, d.y)
                                //SFX
                            call DestroyEffect(AddSpecialEffect(ChargeSFX, d.x, d.y))
                            if d.animstep < R2I(AnimInterval / TimerInterval) then
                                set d.animstep = d.animstep + 1
                            else
                                set d.animstep = 0
                                call SetUnitAnimation(d.caster, CastAnim)
                            endif
                        endif
                    else
                            //Damaging the target, unpausing units, giving them pathing
                        if d.finished == true then
                            call UnitDamageTarget(d.caster, d.target, Damage(d.level), true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_UNKNOWN, null)
                            call DestroyEffect(AddSpecialEffect(TargetSFX, d.x, d.y))
                        endif
                        call SetUnitPathing(d.caster, true)
                        call PauseUnit(d.caster, false)
                        call PauseUnit(d.target, false)
                        call DestroyEffect(d.attach)
                        call SetUnitAnimation(d.caster, "walk")
                            //Struct actions
                        set d.curstep = 0
                        set d.animstep = 0
                        call d.destroy()
                        set d.counter = d.counter - 1
                        set d.indx[i] = d.indx[d.counter]
                        set i = i - 1
                    endif
                    set i = i + 1
                endloop
                set i = 0
                    //Pause Timer if no instance is running
                if Spell.counter == 0 then
                    call PauseTimer(Spell.time)
                endif
            endmethod
            
                //Setting variables at spellcast
            static method Set takes unit c, unit t, integer l returns nothing
                local Spell d = Spell.allocate()
                
                    //Obvious settings ;P
                set d.caster       = c
                set d.target       = t
                set d.x1           = GetUnitX(c)
                set d.y1           = GetUnitY(c)
                set d.x2           = GetUnitX(t)
                set d.y2           = GetUnitY(t)
                set d.x            = d.x1
                set d.y            = d.y1
                set d.level        = l
                set d.finished     = true        //Standard settubg is true, so it damages the target.
                    //Now I calculate how many steps it takes, by dividing distance between start and end point through speed
                set d.steps        = R2I((SquareRoot((Pow((d.x2-d.x1), 2.00)+Pow((d.y2-d.y1), 2.00)))) / Speed)
                    //Pathing and Pausing
                call SetUnitPathing(c, false)
                call PauseUnit(c, true)
                call PauseUnit(t, true)
                call SetUnitFacing(c, bj_RADTODEG * Atan2(d.y2 - d.y1, d.x2 - d.x1))
                call SetUnitAnimation(c, CastAnim)
                set d.attach = AddSpecialEffectTarget(CastAttach, c, "weapon")
                
                    //Starting Timer if no instance is already running
                if Spell.counter   == 0 then
                    call TimerStart(Spell.time,TimerInterval,true, function Spell.Execution)
                endif
                set Spell.indx[Spell.counter] = d
                set Spell.counter = Spell.counter + 1
            endmethod
    endstruct

        //Function that initializes the actions
    private function VInput takes nothing returns nothing
        call Spell.Set(GetTriggerUnit(),GetSpellTargetUnit(),GetUnitAbilityLevel(GetTriggerUnit(),SpellID))
    endfunction
    
    private function Start takes nothing returns nothing
        local trigger t = CreateTrigger()
        local integer i = 0
        local filterfunc ff = Filter(function NoLeakFilter)
            //No leaks through TriggerRegisterAnyUnitEventBJ
        loop
            call TriggerRegisterPlayerUnitEvent(t, Player(i), EVENT_PLAYER_UNIT_SPELL_EFFECT, ff)
            set i = i + 1
            exitwhen i == bj_MAX_PLAYER_SLOTS
        endloop
        set i = 0
        call TriggerAddCondition(t, Condition(function condition))
        call TriggerAddAction(t, function VInput)
        call DestroyFilter(ff)
        call Preload(ChargeSFX)
        call Preload(TargetSFX)
        call PreloadStart()
        set ff = null
        set b = Condition(function NoLeakFilter)
    endfunction
endscope

v1.0b: Added correct hidden tags ~.~
v1.0c: Implemented ingame help on how to import and added credits.
Changed some SFX to make it more like "Armageddon"

v1.0d: Added link to JNGP

Readme:

This pack contains three spells of mine (my first three vJass spells):
1.: Blink Strike (A simple Blink Strike With AoE damage
2.: Armageddon (Summons a bucnh of several random SFX in a certain AoE dealing damage)
3.: A charge (finally learned how to use structs [thanks to Anachron {direct help}, Dynasti {tutorial}
and Paladon {direct help}]. The caster has always the same speed, not like in my
previous versions. (I can show you on demand)

Simply import the triggers and the spells and make sure that the rawcodes still are correct
(ctrl+D ind Object Editor and look at spells, then compare to the one in the code)
Change values in setup sections to whatever pleases you.

Further credits go to THW for many tutorials (can't list all the people sry)


Keywords:
Charge, Blink, Strike, Armageddon, Pack, Rofl, Copter, Jass, MUI
Contents

Noch eine WARCRAFT-III-Karte (Map)

Reviews
00:09, 31st Dec 2009 TriggerHappy: Basically the same stuff for each spell. Charge Please don't inline the spell event. Spell.Set -> Spell.create VInput is useless, just use create directly. I suggest using a timer attachment system...

Moderator

M

Moderator

00:09, 31st Dec 2009
TriggerHappy:

Basically the same stuff for each spell.

Charge
  • Please don't inline the spell event.
  • Spell.Set -> Spell.create
  • VInput is useless, just use create directly.
  • I suggest using a timer attachment system instead of looping through indices.
Blink Strike
  • Please re-use a global group.
  • NoLeakFilter is not needed anymore.
  • (GetWidgetLife(target) > 0.405) to UnitAlive.
  • Don't inline event.
Armageddon
  • Use UnitAlive.
  • Global group.
  • Don't inline event.
 
Level 4
Joined
Jun 1, 2009
Messages
87
why is every great GUI coders doing VJASS now =(
on the main topic...

the charge strike is the best spell in the pack, its fun to use and the effects are well done.
more visuals can be added to lightning spell because right now its not very eye catchy.
finally i think the blink strike needs to be triggered faster, i have done some testing there appears to be a 0.5 seconds delay when casting the spell.

i have pulled some creeps and waited till they are about to retreat, i then casted blink strike, the caster begins the casting animation but the creeps moves out of range n the spell cancels itself.
overall great pack! 4/5 !
ps, i still like ur gui spells better =)
 
Level 6
Joined
Nov 10, 2006
Messages
181
JASS:
   loop
            call TriggerRegisterPlayerUnitEvent(t, Player(i), EVENT_PLAYER_UNIT_SPELL_EFFECT, ff)
            set i = i + 1
            exitwhen i == bj_MAX_PLAYER_SLOTS
        endloop

Does not leak.

JASS:
   private constant real    TimerInterval  = 0.02
            //^The interval used by the timer. I suggest leaving it at 0.02
        private constant integer  Speed         = 30

Timerinterval should be around 0.03? and Speed should be moved speed per second for user to configure it easily.

You also don't check whether the destructable is a tree or not, some people don't want other people killing their gates for no reason.

If you are calling forgroup for enuming only, just use the loop exitwhen u == null method.
 
Level 17
Joined
Mar 17, 2009
Messages
1,349
Wow, someone is learning REALLY FAST!
Ummm, ok I didn't read the scripts fully, but it looks good.

But about the ideas, humm... not very original.

Oh and btw:
JASS:
private string array Effect [2]
I don't think you need to assign the array until you need it, ie:
JASS:
private string array Effect

(It's a long time I used arrays in Jass, so I might be wrong)
 
Level 16
Joined
Oct 12, 2008
Messages
1,570
GUI > Jass....
Long Live GUI :D.

You are right! Oh, wait, no that is your name,,

After a quick look at the code (i only checked the blink thing, ya im lazy), i found 1 bug.
You use GetTriggerUnit() to check wether the target is an enemy. There is no triggering player, only an owner of a triggering unit.

I was too lazy to look at the rest
 
Level 9
Joined
Nov 25, 2008
Messages
194
You are right! Oh, wait, no that is your name,,

After a quick look at the code (i only checked the blink thing, ya im lazy), i found 1 bug.
You use GetTriggerUnit() to check wether the target is an enemy. There is no triggering player, only an owner of a triggering unit.

I was too lazy to look at the rest

Huh? You mean I use GetTriggerPlayer(), or what?

Sry, I don't quite get you point.

The spell works fine, it only damages the units it should Oo
 
Level 16
Joined
Oct 12, 2008
Messages
1,570
JASS:
call DestroyEffect(AddSpecialEffect(TargetEffect,x,y))
        if IsUnitEnemy(t, GetTriggerPlayer()) then
            call UnitDamageTarget(c, t, I2R(GetUnitAbilityLevel(c,SpellID))*ImpactDamagePerLvl, true, false[COLOR=Maroon][/COLOR], ATTACK_TYPE_NORMAL, DAMAGE_TYPE_UNKNOWN, WEAPON_TYPE_WHOKNOWS)
            else
        endif
        call GroupEnumUnitsInRange(g, x, y, GroupAoE, b)

Eiter the code on the thread is not up-to-date, or somehow GetTriggerPlayer() returns the owner of the triggering unit o_O (I always only check code on thread unless i am feeling weird.)
if IsUnitEnemy(t, GetTriggerPlayer) then that is where you use GetTriggerPlayer()
 
Top