wc3campaigns
WC3C Homepage - www.wc3c.netUser Control Panel (Requires Log-In)Engage in discussions with other users and join contests in the WC3C forums!Read one of our many tutorials, ranging in difficulty from beginner to advanced!Show off your artistic talents in the WC3C Gallery!Download quality models, textures, spells (vJASS/JASS), systems, and scripts!Download maps that have passed through our rigorous approval process!

Go Back   Wc3C.net > Resources > Code Resources > vJass Spells
User Name
Password
Register Rules Get Hosted! Chat Pastebin FAQ and Rules Members List Calendar



Reply
 
Thread Tools Search this Thread
Old 01-25-2009, 12:27 PM   #1
Flame_Phoenix
retired coder | real ilfe
 
Flame_Phoenix's Avatar
 
Join Date: Mar 2007
Posts: 2,208

Submissions (10)

Flame_Phoenix has a spectacular aura about (90)Flame_Phoenix has a spectacular aura about (90)Flame_Phoenix has a spectacular aura about (90)Flame_Phoenix has a spectacular aura about (90)

Send a message via MSN to Flame_Phoenix
Default Dark Lightning

Ok guys, this is another one of my spell for the spell Olympics. Although I am totally sure I can't win against Anitarf nor Griffen (my freaking Nemesis) I still think this is a spell people should see and I believe some of you may even like it.
The code is well commented, and I feel a system can be explored from the code. Unlike many other spells, I prepared this code for the user, this means the user is not limited only to the SETUP part, I also want the user to enter the code's core so he can also learn from my code and mainly make even better lightning spells. This is mainly why my code has many methods and why it is so divided, so people can actually use an easy and efficient "Divide and Conquer" strategy. This makes sections of the code easier to change.
I would really like to see this spell approved, it is my first working lightning spell, and it passed nearly through hell to make it work for Olympics.
I just feel bad I actually delivered a not perfect version of this spell... The Math formula is not what I exactly pretended, but it was what I could do with the time I had.
Please enjoy and be nice on comments, I put a lot of effort into making this spell for you, the user.

Credits:
This time I feel forced to tell explicitly the people who helped me making this spell. Note that without them this spell would have not been possible to make. Thx to Anitarf and Pyrogasm, both of them wasted many hours seeing my code, I can never thank them enough for what they did. I would also like to thank Daelin for his outdated, but useful math formulas and to Deaod as well.

Description:
A JESP spell that allows the user to create lightning spells with any model he desires. In this sample, the caster sends a dark projectile which will damage enemy units and heal the caster by the amount of damage they received. If an enemy unit dies due this ability, it will return as an Undead to aid the caster, unless it is a summon or a hero.

Click image for larger version

Name:	DarkLightning2.3.jpg
Views:	605
Size:	248.6 KB
ID:	39976

Requirements:
- Jass NewGen Pack (uses vJASS)
- TimerUtils

History:
Hidden information:

History
Versions 1.0, 1.2, 1.3, 1.4:
- This versions all had different approaches to try solving the same problem "How to make a lightning spell ?" but they all proved either to be inefficient or to fail
- Yes, I actually remade from zero the spell 3 times (without counting with version 1.0)

Version 1.5:
- First release to the spell Olympics
- Math formula improved
- Removed a useless timer
- Cleaned and commented the code

Version 1.6:
- The Math formula for damage reduction was still, wrong, so now it is corrected and now most things work as they should

Version 1.7:
- Code optimizations
- Fixed a glitch with the dummy unit, now it actually dies

Version 1.8:
- Made the function Targets easier to use
- Now the code has its own algorithm and re uses groups
- Added new function SetProjectile
- Many other minor code fixes and updates were done
- Added Anitarf to the credits

Version 1.9
- Changed the model of the missile to make it look better and now it works with flying units by changing its flying height properly
- Now the missile always faces the direction of the victim
- Improved the map and the terrain
- Changed the algorithm for picking units, now he picks close units to the target
- Now the effects appear on the units
- Now I also preload the units missile and the dummy

Version 2.0
- Now the abilities for the dummy unit are also preloaded
- Replaced SetUnitPosition with SetUnitX and SetUnitY, now the spell is faster!


Version 2.1
- Replaced some variables in order to make the preloading of the dummy unit faster.
- Cleaned the code and deleted old comments and code fragments as well as eliminated the "data = this" laziness

Version 2.2
- Now the code really works well with 1 single timer. Thx Pyrogasm and Anitarf!
- This spell would have not been possible without the people on the credits, Anitarf, Daelin, Deaod and Pyrogasm, thx to you all!

Version 2.3:
- Fixed a leak in method NextTarget
- Moved the timer code to the struct
- Transformed some of the SETUP functions into constants
- Fixed a spelling mistake , replaced TIMER_CICLE, by TIMER_CYCLE
- Added JESP document

Version 2.31:
- Fixed the tooltips of the spell

Version 2.32:
- Updated for patch 1.24


Collapse JASS:
//===========================================================================
//A JESP spell that allows the user to create lightning spells with any model
//he desires. In this sample, the caster sends a purple projectile which will 
//damage enemy units and heal the caster by an amount of damage they received.
//If an enemy unit dies due this ability, it will return as an Undead to aid 
//the caster, unless it is a summon, a hero or a flying unit.
//
//Requires TimerUtils
//
//@author Flame_Phoenix 
//
//@credits
//- Deaod, for all hi help in the code, with math formulas and advices
//- Anitarf, for math formulas and advices for efficiency
//- Pyrogasm, for giving me the algorithm for making the spell with 1 timer only
//- Daelin, for the math formulas on his outdated spells 
//- Vexorian, for the idea of preloading units and abilities and for TimerUtils
//
//@version 2.32
//===========================================================================
scope DarkLightning initializer Init
//===========================================================================
//=============================SETUP START===================================
//===========================================================================
   globals
        private constant integer AID = 'A000'   //rw of teh ability
        private constant real SPEED = 700.  //speed of the missile
        private constant integer MISSILE_ID = 'h000'    //rw of the missile
        private constant integer DUM_ID = 'h001'    //rw of the dummy unit
        private constant integer DUM_AB = 'A001'    //Ability of the dumy unit (animated dead)
        private constant string DUM_ORDER = "animatedead"   //string order of the dummy unit
        private constant real TIMER_CYCLE = 0.03    //cicles of the timer
        private constant string DRAIN_EFFECT = "Abilities\\Spells\\Demon\\DarkPortal\\DarkPortalTarget.mdl"
        private constant string BLOOD_EFFECT = "Objects\\Spawnmodels\\Orc\\Orcblood\\BattrollBlood.mdl"
        private constant attacktype A_TYPE = ATTACK_TYPE_MAGIC  //the attack type of the spell
        private constant damagetype D_TYPE = DAMAGE_TYPE_UNIVERSAL  //the damage type of the spell
    endglobals
    
    private constant function Range takes integer level returns real
    //If there is more than one Target, a next target will be picked in a 500
    //AOE from the first
        return 500. + (level * 0)   
    endfunction
    
    private constant function Damage takes integer level returns real
    //Damage each Target will take
        return 100. * level
    endfunction
    
    private constant function Heal takes integer level, real damage returns real
    //the heal the caster will get when damaging enemies
    // in this case in level 1 caster gains 33% of damage done, in level
    //2 he gains 66% of damage done and in level 3 he gains 99% of the 
    //damage deal to the target
        return level * 0.33 * damage
    endfunction
    
    private constant function Reduction takes integer level returns real
    //Damage reduction per Target
        return 0.15 + (level * 0)
    endfunction
    
    private constant function TargetsNumber takes integer level returns integer
    //The number of targets
        return 4 + (level * 1)
    endfunction
    
    private function Targets takes unit caster, unit target returns boolean
    //"caster" is the caster of the spell, and "target" is the target being evauated
        return IsUnitEnemy(target, GetOwningPlayer(caster)) and (IsUnitType(target, UNIT_TYPE_STRUCTURE) == false) and (IsUnitType(target, UNIT_TYPE_MAGIC_IMMUNE) == false) and (GetWidgetLife(target) > 0.405)
    endfunction
    
//===========================================================================
//=============================SETUP END=====================================
//===========================================================================

    globals
        private group g
        private boolexpr b
        private unit tmpCaster = null
        private timer t
        private integer instancesCount
    endglobals
    
//===========================================================================
    private function AceptedTargets takes nothing returns boolean
        return Targets(tmpCaster, GetFilterUnit())
    endfunction
//=========================================================================== 
    private struct SpellData 
        //static SpellData array datas    //an array which will contain the instances
    
        unit caster     //our caster !
        unit vic    //the current victim
        integer level   //the level of the ability
        group picked   //saves all targeted units so far, so they don't get picked again
        unit missile    //the missile
        real wait   //tells us how much time we must wait
        integer targNum   //Current number of the target
        real lastDamage //this tells us the last amount of damage a unit received. NOTE: this is NOT the damage a unit is taking.
        boolean done
        
        static method create takes unit caster, unit vic returns SpellData
            local SpellData data = SpellData.allocate()
            
            //setting variables
            set data.caster = caster
            set data.vic = vic
            set data.level = GetUnitAbilityLevel(caster, AID)
            set data.missile = CreateUnit(GetOwningPlayer(caster),  MISSILE_ID, GetUnitX(caster), GetUnitY(caster), 0)  
            set data.wait = 0.
            set data.targNum = 0
            set data.done = false
            
            //we recycle the group 
            if data.picked == null then
                set data.picked = CreateGroup()
            endif
            
            return data
        endmethod
        
        method SetProjectile takes nothing returns nothing
            local real a = GetUnitX(.missile) - GetUnitX(.vic)
            local real b = GetUnitY(.missile) - GetUnitY(.vic)
            local real d = SquareRoot(a*a + b*b) //the distance between "a" and "b"
            
            set .wait = d / SPEED
            
            //we adapt the fly height of the missile to the height of the target!
            call SetUnitFlyHeight(.missile, GetUnitFlyHeight(.vic), (GetUnitFlyHeight(.missile) - GetUnitFlyHeight(.vic)) / .wait)
        endmethod
        
        method TargetEffect takes nothing returns nothing 
            local unit dum
        
            //the hp the enemies will lose and that the caster will win
            if (.targNum == 0) then
                set .lastDamage = Damage(.level)
            else
                set .lastDamage = .lastDamage - (.lastDamage * Reduction(.level))
            endif
            
            //here we damage the bad guy ! Die you bastard !!!!
            //we also created the effects for both targets and caster
            call UnitDamageTarget(.caster, .vic, .lastDamage, true, false, A_TYPE, D_TYPE, null)
            call DestroyEffect(AddSpecialEffectTarget(DRAIN_EFFECT, .vic, "origin"))
            call DestroyEffect(AddSpecialEffectTarget(BLOOD_EFFECT, .caster, "origin"))
            
            //Heal the caster
            call SetWidgetLife(.caster, GetWidgetLife(.caster) + Heal(.level, .lastDamage))
            
            if (GetWidgetLife(.vic) < 0.405)  and (IsUnitType(.vic, UNIT_TYPE_HERO) == false) and (IsUnitType(.vic, UNIT_TYPE_SUMMONED) == false) and (IsUnitType(.vic, UNIT_TYPE_FLYING) == false) then
                set dum = CreateUnit(GetOwningPlayer(.caster), DUM_ID, GetUnitX(.vic), GetUnitY(.vic), 0)
                call UnitAddAbility(dum, DUM_AB)
                call SetUnitAbilityLevel(dum, DUM_AB, .level)
                call IssueImmediateOrder(dum, DUM_ORDER)
                call UnitApplyTimedLife(dum, 'BTLF', 1.)
            endif
            
            set dum = null
        endmethod
        
        method NextTarget takes nothing returns nothing
            local unit f = null
            local unit ret = null
            
            //the position of the current target
            local real cX = GetUnitX(.vic)
            local real cY = GetUnitY(.vic)
            
            //the position of our new target
            local real nX
            local real nY
            
            //by saving the minimal distance, this will help us choose
            //the closest new target to our current target
            //note we start it with the biggest value possible, so
            //we can find the minimum after
            //Also note that I am calculating minDist^2 so I can avoid
            //the use of a squareroot which will save speed. Know that if
            //minDist <, >, <=, >= d then minDis^2 <, >, <=, >= d^2 and vice-versa
            local real minDist = Range(.level)*Range(.level)
            
            local real d //this will be a temporary variable for the distances we will calculate
            
            //here we pick all units near the last target
            set tmpCaster = .caster
            call GroupEnumUnitsInRange(g, GetUnitX(.vic), GetUnitY(.vic), Range(.level), b)
            
            loop
                set f = FirstOfGroup(g)
                exitwhen(f == null)
                call GroupRemoveUnit(g, f)
                if (IsUnitInGroup(f, .picked) == false) then
                    set nX = GetUnitX(f)
                    set nY = GetUnitY(f)
                    
                    //now we calculate the distance between f and our current target
                    //note that: d^2 = (x2-x1)^2 + (y2-y1)^2
                    //I avoid using a square to make this faster
                    set d = (nX - cX)*(nX - cX) + (nY - cY)*(nY - cY)
                    
                    //if this new distance is the smallest, then we update our target
                    if (d < minDist) then
                        set minDist = d
                        set ret = f
                    endif
                endif
            endloop
            
            set .vic = ret
            set ret = null
            
        endmethod
        
        method ChainEffect takes nothing returns nothing
            //we add the victim to the victims groups, so we won't pick it twice
            call GroupAddUnit(.picked, .vic)
            
            //here we call the function responsable for the bad things we do to the bad guys xD
            call .TargetEffect()
            
            //now we increase the counter to know how many units we hit           
            set .targNum = .targNum + 1

            //if the number of our current target is lower than the maximum amount
            //of targets we can hit, we continue, else we end everything
            if (.targNum < TargetsNumber(.level)) then 
                
                //pick new target !
                call .NextTarget()
                
                //if the new unit is not null, we repeat this step, else we end
                if (.vic != null) then
                   call .SetProjectile()
                else
                    set .done = true
                endif
            else
                set .done = true
            endif
        endmethod
        
        method MoveMissile takes nothing returns nothing
            local real x1 = GetUnitX(.missile)
            local real x2 = GetUnitX(.vic)
            local real y1 = GetUnitY(.missile)
            local real y2 = GetUnitY(.vic)
            
            local real dx = TIMER_CYCLE * (x2 - x1) / .wait  
            local real dy = TIMER_CYCLE * (y2 - y1) / .wait
         
            call SetUnitX(.missile, x1 + dx)
            call SetUnitY(.missile, y1 + dy)
            
           //here we set the facing of the missile
            call SetUnitFacing(.missile, bj_RADTODEG * Atan2(y2 - y1, x2 - x1))
            
            set .wait = .wait - TIMER_CYCLE
            
            //this is when the missile gets to the unit
            if .wait < TIMER_CYCLE then
                
                call SetUnitX(.missile, x2)
                call SetUnitY(.missile, y2)
            
                //This runs the ChainEffect function again!
                call .ChainEffect()
            endif
        endmethod
        
        method onDestroy takes nothing returns nothing
            //we clear the gorup so we can use it later
            call GroupClear(.picked)

            //we destroy the projectile
            call ShowUnit(.missile, false)
            call KillUnit(.missile)
        endmethod
    endstruct
//=========================================================================== 
    globals
        private SpellData array datas
    endglobals
//=========================================================================== 
    private function Periodic takes nothing returns nothing
        local integer currentIndex = 0
        local SpellData currentInstance
        
        
        loop
            set currentInstance = datas[currentIndex]
            call currentInstance.MoveMissile() 
            
            //if our instance is done, we decrement the number of total instances
            //and then we check if there are any more instances being run
            if (currentInstance.done) then
                set instancesCount = instancesCount - 1
                
                //if there are, then we update our instance to the next of the array
                //and we correct the index
                if (instancesCount > 0) then
                    set datas[currentIndex] = datas[instancesCount]
                    set currentIndex = currentIndex - 1
                //else we just release the timer
                else
                    call ReleaseTimer(t)
                endif
                
                //now before we leave current instance for good, we destroy it!
                call currentInstance.destroy()
            
            endif
                
            set currentIndex = currentIndex + 1
            exitwhen currentIndex >= instancesCount
            
        endloop
    endfunction 
//===========================================================================    
    private function Conditions takes nothing returns boolean
        if GetSpellAbilityId() == AID then
            
            //if there are no instances of the spell then it means the timer does not
            //exist, so we create it and start it!
            if instancesCount == 0 then
                set t = NewTimer()
                call TimerStart(t, TIMER_CYCLE, true, function Periodic)
            endif
            
            set datas[instancesCount] = SpellData.create(GetTriggerUnit(), GetSpellTargetUnit())
            call datas[instancesCount].SetProjectile()
            set instancesCount = instancesCount + 1
        
        endif

        return false
    endfunction
//===========================================================================
    private function Init takes nothing returns nothing
        local trigger LightningTrg = CreateTrigger(  )
        call TriggerRegisterAnyUnitEventBJ( LightningTrg, EVENT_PLAYER_UNIT_SPELL_EFFECT )
        call TriggerAddCondition( LightningTrg, Condition( function Conditions ) )
                
        //setting globals
        set b = Condition(function AceptedTargets)
        set g = CreateGroup()
        set instancesCount = 0
        
        //Preload the effects
        call Preload(DRAIN_EFFECT)
        call Preload(BLOOD_EFFECT)
        
        //preloading units and spells
        set bj_lastCreatedUnit = CreateUnit(Player(0), DUM_ID, 0, 0, 0)
        call UnitAddAbility(bj_lastCreatedUnit, DUM_AB)
        call KillUnit(bj_lastCreatedUnit)
        
        call KillUnit(CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), MISSILE_ID, 0, 0, 0))
    endfunction
endscope
Attached Files
File Type: w3x Dark Lightning 2.32.w3x (39.5 KB, 92 views)
__________________
Check out my tutorials at:
1-Creating a Hero Tavern
2-Complete Icon Tutorial - ALL about Icons
3-Making a spell in vJass - Practice Session 1
Check out all my current spells at here
Finally, check my project:
Castle vs Castle Flame Edition

Last edited by Flame_Phoenix : 08-06-2009 at 09:50 AM.
Flame_Phoenix is offline   Reply With Quote
Sponsored Links - Login to hide this ad!
Old 01-25-2009, 12:29 PM   #2
Flame_Phoenix
retired coder | real ilfe
 
Flame_Phoenix's Avatar
 
Join Date: Mar 2007
Posts: 2,208

Submissions (10)

Flame_Phoenix has a spectacular aura about (90)Flame_Phoenix has a spectacular aura about (90)Flame_Phoenix has a spectacular aura about (90)Flame_Phoenix has a spectacular aura about (90)

Send a message via MSN to Flame_Phoenix
Default

NOTE: No, I didn't moved Period to inside the structure. Yet Version 2.3 was heavily improved, I changed everything as required! I even did things I didn't need to do, just to please the damn mods !!! This should already be approved by Moyack, but I decided to follow an evil path and to make more modifications so it could be better. I would like a LOT if some one could finally approve this damn spell for once without forcing me to quote Moyack. Please keep in mind the heavy modifications this spell suffered ...
__________________
Check out my tutorials at:
1-Creating a Hero Tavern
2-Complete Icon Tutorial - ALL about Icons
3-Making a spell in vJass - Practice Session 1
Check out all my current spells at here
Finally, check my project:
Castle vs Castle Flame Edition

Last edited by Flame_Phoenix : 01-25-2009 at 12:31 PM.
Flame_Phoenix is offline   Reply With Quote
Old 02-08-2009, 11:11 PM   #3
Anitarf
Procrastination Incarnate


Development Director
 
Join Date: Feb 2004
Posts: 8,075

Submissions (19)

Anitarf has a brilliant future (888)Anitarf has a brilliant future (888)Anitarf has a brilliant future (888)Anitarf has a brilliant future (888)Anitarf has a brilliant future (888)Anitarf has a brilliant future (888)Anitarf has a brilliant future (888)Anitarf has a brilliant future (888)

2008 Spell olympics - Fire - SilverApproved Map: Old School Alliance TacticsHero Contest #2 - 3rd PlaceSpell making session 2 winner

Default

Fine, whatever. Approved. It's still ugly code and I might feel disinclined to approve similar stuff in the future.

Your tooltips are incorrect, by the way.
__________________
Anitarf is offline   Reply With Quote
Old 02-09-2009, 06:50 PM   #4
Flame_Phoenix
retired coder | real ilfe
 
Flame_Phoenix's Avatar
 
Join Date: Mar 2007
Posts: 2,208

Submissions (10)

Flame_Phoenix has a spectacular aura about (90)Flame_Phoenix has a spectacular aura about (90)Flame_Phoenix has a spectacular aura about (90)Flame_Phoenix has a spectacular aura about (90)

Send a message via MSN to Flame_Phoenix
Default

Quote:
Your tooltips are incorrect, by the way.
Will change this asap.

Quote:
It's still ugly code
Just to make sure, you say that because of the function periodic thing correct ?

EDIT EDIT EDIT


Spell was updated, tooltips are now correct.
I am going to be as honest as I can be now... Truth is that I look at this spell almost as defeat. I know that Anitarf felt quite forced to approve this, I am definitely not proud of it. Sometimes I just can't have it all I guess. On the other hand, I can now move with my life to a new stage and submit more spells to the community, better spells I hope.

I will start by the seal spell, I will fix stuff Pyro asked and re-submit it. After that I have a channel spell in mind that I hope people like. And I don't feel inclined to make all things with only 1 timer so far.
__________________
Check out my tutorials at:
1-Creating a Hero Tavern
2-Complete Icon Tutorial - ALL about Icons
3-Making a spell in vJass - Practice Session 1
Check out all my current spells at here
Finally, check my project:
Castle vs Castle Flame Edition

Last edited by Flame_Phoenix : 02-09-2009 at 06:57 PM.
Flame_Phoenix is offline   Reply With Quote
Old 08-06-2009, 09:51 AM   #5
Flame_Phoenix
retired coder | real ilfe
 
Flame_Phoenix's Avatar
 
Join Date: Mar 2007
Posts: 2,208

Submissions (10)

Flame_Phoenix has a spectacular aura about (90)Flame_Phoenix has a spectacular aura about (90)Flame_Phoenix has a spectacular aura about (90)Flame_Phoenix has a spectacular aura about (90)

Send a message via MSN to Flame_Phoenix
Default

Updated for patch 1.24. The script does not need any changes, I only updated TimerUtils.
__________________
Check out my tutorials at:
1-Creating a Hero Tavern
2-Complete Icon Tutorial - ALL about Icons
3-Making a spell in vJass - Practice Session 1
Check out all my current spells at here
Finally, check my project:
Castle vs Castle Flame Edition
Flame_Phoenix is offline   Reply With Quote
Reply


Thread Tools Search this Thread
Search this Thread:

Advanced Search

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off


All times are GMT. The time now is 09:21 AM.


Donate

Affiliates
The Hubb http://bylur.com - Warcraft, StarCraft, Diablo and DotA Blog & Forums The JASS Vault Clan WEnW Campaign Creations Clan CBS GamesModding Flixreel Videos

Powered by vBulletin (Copyright ©2000 - 2014, Jelsoft Enterprises Ltd).
Hosted by www.OICcam.com
IT Support and Services provided by Executive IT Services