Spell Light Burst

BlackRose

Forum User
Reaction score
239
Light Burst v1.02 by BlackRose

Last updated: 25th May 2009​

Compresses nearby light energy. When this energy can no longer be more compressed, the energy explodes outwards, dealing damage to enemy units hit by the light nova and pushing them back.
Channeling


CODE: vJASS
LEAKS: Most likely no.
LAGS: Not for me it does not.
MUI: Yes.

*Needs PUI
*Needs TimerUtils
*Needs NewGen

SCREENSHOT: <Please, screenshots aren't 100%, test it!>
lightburstnew.jpg
CODE:
JASS:
scope LightBurst initializer Init

//-------------------------------------------------------------
//                Light Burst v1.02 by BlackRose                  
//-------------------------------------------------------------

// WHAT is this spell?
//---------------------------
// ... A remake of an old spell I made called Light Burst.

// I want this spell?
//---------------------------
// ...You need NewGen.

// How do I put it in my map?
//---------------------------
// ... 1. Copy this trigger, TimerUtils and PUI (if you don&#039;t have it already) into your map.
// ... 2. Copy the ability and dummy unit into your map.
// ... 3. Press CTRL + D while in Abilities section in Object Editor to find out the rawcode
// .......of the Light Burst you just pasted, change SPELL_ID to what you find, as well as the dummy unit.
// ... 4. You have imported it! HOORAY!

// Changelog:
//---------------------------
// v1.00:
// ... Initial release.

// v1.01:
// ... Now using PUI to trigger the channelling part.
// ... Updated code.

// v1.02:
// ... Small changes to code.
//---------------------------

// Credits:
//---------------------------
// - Cohadar for PUI.
// - Vexorian for TimerUtils.
// - Creators of JassNewGen.
//---------------------------
// TheHelper.net

// Easier testing.
private function DEBUG takes string s returns nothing
    call DisplayTimedTextToPlayer( Player(0), 0, 0, 1.500, s )
endfunction

//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
//                             LIST OF CONFIGURABLES
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
    globals
private constant boolean IS_CHANNEL = true //&lt;-- Is the ability channelling? If false, I suggest you also set the
                                           // field in the ability&#039;s Follow Through Time to smaller.

private constant integer SPELL_ID = &#039;A001&#039; //&lt;-- Object rawcodes.
private constant integer DUMMY_ID = &#039;e000&#039; 
private constant string  SPELL_STRING = &quot;channel&quot; //&lt;-- OrderID of spell.

private constant real TIMER_INTERVAL = 0.03 //&lt;-- Timer interval.

private constant real    COMPRESS_AREA          = 600. //&lt;-- How far the light missiles are from when cast. Only for effect.
private constant integer COMPRESS_LIGHT_AMOUNT  = 12     //&lt;-- How many dummy missiles. Only for effect.
private constant real    COMPRESS_DURATION      = 2.00   //&lt;-- How long it take to spin inwards. How long channel.

private constant real    COMPRESS_SPIRAL_ANGLE  = 60 * bj_DEGTORAD //&lt;-- Recommended to be between 0 - 70.
                                                                   // 0 being straight inwards, while above 80 will look weird.

private constant real      EXPLOSION_AOE         = 100.00 //&lt;-- Radius of lines, enemy units will be dragged by. Common sense.
private constant integer   EXPLOSION_LINE_AMOUNT = 9      //&lt;-- How many lines of booms ther are. Too many might cause lag.
private constant string    EXPLOSION_EFFECT      = &quot;Abilities\\Weapons\\FaerieDragonMissile\\FaerieDragonMissile.mdl&quot;
private constant real      EXPLOSION_DISTANCE    = 700.00 //&lt;-- Distance of explosion. 
private constant real      EXPLOSION_SPEED       = 40.00  //&lt;-- How fast it goes.

// (Below) Damage properties.
private constant boolean ATTACK = true
private constant boolean RANGED = false
private constant attacktype ATK_TYPE = ATTACK_TYPE_NORMAL
private constant damagetype DMG_TYPE = DAMAGE_TYPE_MAGIC
private constant weapontype WPN_TYPE = null
    endglobals
    
// GetDamage: Level * 75 = 75/150/225/300
    private function GetDamage takes integer Level returns real
        return Level * 75.00
    endfunction
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

globals
    private constant real    COMPRESS_SPEED  = ( 2.00 * COMPRESS_AREA ) / ( COMPRESS_DURATION / TIMER_INTERVAL ) + 1.00

    private constant real COMPRESS_ANG_DIFF  = ( 360 / COMPRESS_LIGHT_AMOUNT ) * bj_DEGTORAD
    private constant real EXPLOSION_ANG_DIFF = ( 360 / EXPLOSION_LINE_AMOUNT ) * bj_DEGTORAD

    private player TempPlayer
endglobals

private function Cond takes nothing returns boolean
    return GetSpellAbilityId() == SPELL_ID
endfunction

private function UFilter takes nothing returns boolean
    return IsUnitType( GetFilterUnit(), UNIT_TYPE_DEAD ) == false and IsUnitEnemy( GetFilterUnit(), TempPlayer ) and IsUnitType( GetFilterUnit(), UNIT_TYPE_STRUCTURE ) == false
endfunction

private struct ReleaseData
    unit Caster
    real ExplosionDist = 0.00
    player owner
    group array SlideGroup[COMPRESS_LIGHT_AMOUNT]
    group       TempGroup
    
    timer rt
    real rCX
    real rCY
    
    integer rLevel
    method onDestroy takes nothing returns nothing
        debug call DEBUG( &quot;|cffff0000ReleaseData destroyed!|r&quot; )
    endmethod
    
    static method Release takes nothing returns nothing
        local ReleaseData e = GetTimerData( GetExpiredTimer() )
        
        local integer i = 0
        local real x
        local real y
        
        local unit u
        
        set e.ExplosionDist = e.ExplosionDist + EXPLOSION_SPEED
        debug call DEBUG( R2S( e.ExplosionDist ) )
        set TempPlayer = e.owner
        loop
            exitwhen i == EXPLOSION_LINE_AMOUNT
                                    
            set x = e.rCX + e.ExplosionDist * Cos( i * EXPLOSION_ANG_DIFF )
            set y = e.rCY + e.ExplosionDist * Sin( i * EXPLOSION_ANG_DIFF )
            
            call GroupEnumUnitsInRange( e.SlideGroup<i>, x, y, EXPLOSION_AOE, Condition( function UFilter ) )
            loop
                set u = FirstOfGroup( e.SlideGroup<i> )
                exitwhen u == null
                call GroupRemoveUnit( e.SlideGroup<i>, u )
                call SetUnitPosition( u, x, y )
                
                if IsUnitInGroup( u, e.TempGroup ) == false then
                    call GroupAddUnit( e.TempGroup, u )
                    call UnitDamageTarget( e.Caster, u, GetDamage( e.rLevel ), ATTACK, RANGED, ATK_TYPE, DMG_TYPE, WPN_TYPE )
                endif
            endloop
            
            call DestroyEffect( AddSpecialEffect( EXPLOSION_EFFECT, x, y ) )
            set i = i + 1
        endloop
        if e.ExplosionDist &gt; EXPLOSION_DISTANCE then
            set i = 0
                //------------------------------------------------------
                // Destroy groups.
                loop
                    exitwhen i == EXPLOSION_LINE_AMOUNT
                    call DestroyGroup( e.SlideGroup<i> )
                    set i = i + 1
                endloop
            call DestroyGroup( e.TempGroup )
            call ReleaseTimer( e.rt )
            call e.destroy()
        endif
    endmethod
endstruct

private struct Summon
    //! runtextmacro PUI()
    boolean FirstPhaseActive = true
    
    integer Level
    timer SummonTimer
    real TimeElapsed = 0.00
    player Owner
    unit Caster
    real CX
    real CY
    
    unit array LightMissile[COMPRESS_LIGHT_AMOUNT]
    real CompressDist = COMPRESS_AREA
    method onDestroy takes nothing returns nothing
        local integer i = 0
        debug call DEBUG( &quot;Spin inwards - ondestroy&quot; )
        call ReleaseTimer(.SummonTimer)    
        loop
            exitwhen i == COMPRESS_LIGHT_AMOUNT
            call KillUnit( .LightMissile<i> )
            set i = i + 1
        endloop
    endmethod    

    static method create takes nothing returns Summon
        local Summon d = Summon.allocate()
        local integer i = 0
        local real angDiff = 0
        local real x
        local real y
        
        set d.Caster = GetTriggerUnit()
        set d.SummonTimer = NewTimer()

        set d.Owner = GetOwningPlayer( d.Caster )
        set d.CX = GetUnitX( d.Caster )
        set d.CY = GetUnitY( d.Caster )
    
        set d.Level = GetUnitAbilityLevel( d.Caster, SPELL_ID )

        // Creation of the light missiles.
        loop
            exitwhen i == COMPRESS_LIGHT_AMOUNT
            set x = d.CX + COMPRESS_AREA * Cos( angDiff )
            set y = d.CY + COMPRESS_AREA * Sin( angDiff )
            set d.LightMissile<i> = CreateUnit( d.Owner, DUMMY_ID, x, y, 0.00 )
            // Useless? No.
            call SetUnitPosition( d.LightMissile<i>, x, y )
            
            set angDiff = angDiff + COMPRESS_ANG_DIFF
            set i = i + 1
        endloop
        
        return d
    endmethod
    
    static method Spin takes nothing returns nothing
        local integer i = 0
        
        local real angle
        local real x
        local real y
        local real dumx
        local real dumy

        local Summon d = GetTimerData( GetExpiredTimer() )
        local ReleaseData e
        
        set d.TimeElapsed = d.TimeElapsed + TIMER_INTERVAL        
        set d.CompressDist = d.CompressDist - COMPRESS_SPEED
        
        // Move inwards.
        loop
            exitwhen i == COMPRESS_LIGHT_AMOUNT
            
            set dumx = GetUnitX( d.LightMissile<i> )
            set dumy = GetUnitY( d.LightMissile<i> )
            
            set angle = Atan2( d.CY - dumy, d.CX - dumx ) + COMPRESS_SPIRAL_ANGLE
            set x = dumx + COMPRESS_SPEED * Cos( angle )
            set y = dumy + COMPRESS_SPEED * Sin( angle )
            call SetUnitPosition( d.LightMissile<i>, x, y )
            set i = i + 1
        endloop
        
        // Phase 2 start.
        set i = 0
        if d.TimeElapsed &gt;= COMPRESS_DURATION - 0.04 then
            set d.FirstPhaseActive = false
            loop
                exitwhen i == COMPRESS_LIGHT_AMOUNT
                call KillUnit( d.LightMissile<i> )
                set i = i + 1
            endloop

            
            // New struct things.
            set e = ReleaseData.create()
            set e.Caster = d.Caster
            set e.rt     = NewTimer()
            set e.TempGroup = CreateGroup()
            set e.owner = d.Owner
            set e.rLevel = d.Level
            set e.rCX = d.CX
            set e.rCY = d.CY
            set i = 0
            loop
                exitwhen i == EXPLOSION_LINE_AMOUNT
                set e.SlideGroup<i> = CreateGroup()
                set i = i + 1
            endloop
            
            call SetTimerData( e.rt,e )
            call TimerStart( e.rt, TIMER_INTERVAL, true, function ReleaseData.Release )
            
            //call ReleaseTimer( d.SummonTimer )
            call d.release()
        endif
    endmethod
endstruct

private function Main takes nothing returns nothing
    local integer i = 0
    local real angDiff = 0
    
    local real x
    local real y
    
    local unit u = GetTriggerUnit()

    local Summon d
    
    if GetTriggerEventId() == EVENT_PLAYER_UNIT_SPELL_EFFECT then
        set d = Summon.create()
        set Summon<u> = d
        call SetTimerData( d.SummonTimer, d )
        call TimerStart( d.SummonTimer, TIMER_INTERVAL, true, function Summon.Spin )
    elseif GetTriggerEventId() == EVENT_PLAYER_UNIT_SPELL_ENDCAST and IS_CHANNEL then
        set d = Summon<u>
        if d.FirstPhaseActive then
            call DEBUG( &quot;Spell cancelled!&quot; )
            call d.release()
        endif
    endif
    set u = null
endfunction


private function Init takes nothing returns nothing
    local trigger t = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_ENDCAST )
    call TriggerAddCondition( t, Condition( function Cond ) )
    call TriggerAddAction( t, function Main )
endfunction

endscope</u></u></i></i></i></i></i></i></i></i></i></i></i></i>


JASS:
// Changelog:
//---------------------------
// v1.00:
// ... Initial release.

// v1.01:
// ... Now using PUI to trigger the channelling part.
// ... Updated code.

// v1.02:
// ... Small changes to code.
//---------------------------

// Credits:
//---------------------------
// - Cohadar for PUI.
// - Vexorian for TimerUtils.
// - Creators of JassNewGen.
//---------------------------
 
Why not use EVENT_PLAYER_UNIT_END_CAST and then use PUI to attach data for channeling effects? I think it's much more effective.
 
> I've never used PUI, and I don't know how it works.
Learn :)
 
Hmm, I suggest you try updating your other spell(s) first before submitting a new one that also needs a bit of work.

To be honest, by just looking at the script, I'm wondering if it even works.

JASS:
if IS_CHANNEL then
            if .FirstPhaseActive then
                if GetUnitCurrentOrder( .Caster ) != String2OrderIdBJ( SPELL_STRING ) then
                    loop
                        exitwhen i == COMPRESS_LIGHT_AMOUNT
                        call KillUnit( .LightMissile<i> )
                        set i = i + 1
                    endloop
                    call ReleaseTimer( .SummonTimer )
                    call d.destroy()
                endif
            endif
        endif

</i>


Right there, you destroy your struct, yet underneath you keep using it, either im missing something, or this just shouldn't work.

JASS:
local integer this = GetTimerData( GetExpiredTimer() )
local Summon d = this


Why? Maybe just:

JASS:
local Summon d = GetTimerData(GetExpiredTimer())


Your creating lots of groups for this spell, maybe using a global one will work better?

JASS:
            loop
                exitwhen i == EXPLOSION_LINE_AMOUNT
                set .SlideGroup<i> = CreateGroup() // Whats that, like 9 dynamic groups.
                set i = i + 1
            endloop

</i>


JASS:
elseif .TimeElapsed &gt;= COMPRESS_DURATION - 0.05then // Shwaa?
    set .FirstPhaseActive = false
endif


a) Does that even compile?

b) Whats it used for? Your already setting that member to false above in the if block. If you want to do it your way, you can leave that then add:

JASS:
if .FirstPhaseActive == false then
    // Do all the other stuff here.


You are destroying .tempgroup way to many times:

JASS:
            loop
                exitwhen i == EXPLOSION_LINE_AMOUNT
                call DestroyGroup( .SlideGroup<i> )
                call DestroyGroup( .TempGroup ) // Here, theres only one group, but you destroy it like 9 times.
                set i = i + 1
            endloop

</i>


JASS:
// Useless? No.
        call SetUnitPosition( d.LightMissile<i>, x, y )

</i>


What is its usage btw?

Oh, and same with your last spell, you are using one struct for two entirely different functions of the spell. It is not only ugly, but inefficient.

EDIT:

Oh and whats with the backwards indentation?

JASS:
.
    globals // lol?
private constant boolean IS_CHANNEL = true //&lt;-- Is the ability channelling? If false, I suggest you also set the
                                           // field in the ability&#039;s Follow Through Time to smaller.

private constant integer SPELL_ID = &#039;A001&#039; //&lt;-- Object rawcodes.
private constant integer DUMMY_ID = &#039;e000&#039; 
private constant string  SPELL_STRING = &quot;channel&quot; //&lt;-- OrderID of spell.
 
I found a mistake. You use a dummy unit, to make an effect. You forgot to remove it food cost! i over spammed it and it changed to ent.
 
Right there, you destroy your struct, yet underneath you keep using it, either im missing something, or this just shouldn't work.
Oops, better fix that? Should I juse Skip Remaining Actions thing? 'return'

Why? Maybe just:

JASS:
local Summon d = GetTimerData(GetExpiredTimer())
I've never really used methods and I was trying, 'static' didn't really I understand, but.... it seeemed easy like that.
Your creating lots of groups for this spell, maybe using a global one will work better?
How will I manage that? To push in all angles?

a) Does that even compile?
Hmmm..... it shouldn't be like that? Ooops, no space.
b) Whats it used for? Your already setting that member to false above in the if block. If you want to do it your way, you can leave that then add:
I'll remember that.
You are destroying .tempgroup way to many times:
Another one of my oops.
What is its usage btw?
If I made it on non-air pathable place, it wil move it onto the closest place, thus making it look weird.
Oh, and same with your last spell, you are using one struct for two entirely different functions of the spell. It is not only ugly, but inefficient.
How is it inefficent? It is just easier to manage also.
EDIT:

Oh and whats with the backwards indentation?
...does that really matter -.-
I found a mistake. You use a dummy unit, to make an effect. You forgot to remove it food cost! i over spammed it and it changed to ent.
I know, I just decided to change it after a review, thanks anyway.

I'll work on it after I know how PUI works and how to use it.. AND I won't submit another one until this one is perfect.

--------------------------
Other notes:
With PUI, I do not understand, even after reading that tutorial and viewing test map:

How does it attach data to a unit?
JASS:
//! runtextmacro PUI_PROPERTY(&quot;private&quot;, &quot;real&quot;, &quot;Mana&quot;, &quot;0&quot; )

I tried playing around with it and, what is private? Is the 'real' the type of variable, and 'mana' the name, and '0', the value?

JASS:
scope StructExample initializer Init

//===========================================================================
//  If we want struct to be attachable to units we run PUI macro inside it.
//  Putting 5-6 unit properties inside struct is more efficient 
//  than declaring 5 PUI_PROPERTY each for itself
//===========================================================================
private struct UnitData
    //! runtextmacro PUI()
    integer KillCounter = 0
    
    // you can add any of your own stuff here
endstruct

//===========================================================================
private function Actions takes nothing returns nothing
    local unit killer = GetKillingUnit()
    local UnitData dat = UnitData[killer]
    
    // is struct attached?
    if dat == 0 then
        set dat = UnitData.create() // if not create it
        set UnitData[killer] = dat // and attach it
    endif

    // again it is simple
    set dat.KillCounter = dat.KillCounter + 1
    
    // do some other stuff with your struct...
endfunction

//===========================================================================
//  WARNING: never destroy PUI structs directly use release() method instead
//  In fact it is best that you never destroy them manually,
//  they will be destroyed automatically when unit is removed from game.
//===========================================================================

//===========================================================================
private function Conditions takes nothing returns boolean
    return true
endfunction

//===========================================================================
private function Init takes nothing returns nothing
    local trigger trig = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ( trig, EVENT_PLAYER_UNIT_DEATH )
    call TriggerAddCondition( trig, Condition( function Conditions ) )
    call TriggerAddAction( trig, function Actions )
endfunction

endscope

This code, I really do not understand it.
JASS:
    local UnitData dat = UnitData[killer]

Huh? A struct on a unit?

EDIT: In truth, I didn't understand that PUI tutorial at all.
 
You can make a struct instance for a unit? So when you use PUI GetUnitIndex to get that random integer, the random integer is the struct number?
 
> PUI GetUnitIndex to get that random integer
It doesn't get a 'Random Integer'.
This integer stays with the unit until it's killed.

So you can safely attach things to units using this integer.
 
Just got a new computer, will update code later.... so no more 'why are you still doing that'... not everyone has free time -.-

God's judgment:
1. GUI :(
2. Weird ripple effect.
3. I don't understand those Faerie Balls moving out in a weird angle.
4. It is god! :p
 
Why are you using SetUnitPosition for your missles and not SetUnitX & Y.
Whats with the need for the effects to be units? Couldn't it be just effects or did you wanted to do stuff with them?

I checked and you can just use maths stuffs to use a GroupEnumUnitsInRange to check for them although that maths would be tough but it would
be better.

I have heard that destroying groups is bad not very sure on this case but couldn't you recycle them?

Why are you killing units and not removing them?

You are still not using the PUI method to stop channelling stuffs.
 
Sorry if it sound like I am impatient, or anything. But can someone please look over :)? Even if the page is on top of the zone :(
 
General chit-chat
Help Users
  • No one is chatting at the moment.
  • The Helper The Helper:
    News portal has been retired. Main page of site goes to Headline News forum now
  • The Helper The Helper:
    I am working on getting access to the old news portal under a different URL for those that would rather use that for news before we get a different news view.
  • Ghan Ghan:
    Easily done
    +1
  • The Helper The Helper:
    https://www.thehelper.net/pages/news/ is a link to the old news portal - i will integrate it into the interface somewhere when i figure it out
  • Ghan Ghan:
    Need to try something
  • Ghan Ghan:
    Hopefully this won't cause problems.
  • Ghan Ghan:
    Hmm
  • Ghan Ghan:
    I have converted the Headline News forum to an Article type forum. It will now show the top 20 threads with more detail of each thread.
  • Ghan Ghan:
    See how we like that.
  • The Helper The Helper:
    I do not see a way to go past the 1st page of posts on the forum though
  • The Helper The Helper:
    It is OK though for the main page to open up on the forum in the view it was before. As long as the portal has its own URL so it can be viewed that way I do want to try it as a regular forum view for a while
  • Ghan Ghan:
    Yeah I'm not sure what the deal is with the pagination.
  • Ghan Ghan:
    It SHOULD be there so I think it might just be an artifact of having an older style.
  • Ghan Ghan:
    I switched it to a "Standard" article forum. This will show the thread list like normal, but the threads themselves will have the first post set up above the rest of the "comments"
  • The Helper The Helper:
    I don't really get that article forum but I think it is because I have never really seen it used on a multi post thread
  • Ghan Ghan:
    RpNation makes more use of it right now as an example: https://www.rpnation.com/news/
  • The Helper The Helper:
  • The Helper The Helper:
    What do you think Tom?
  • tom_mai78101 tom_mai78101:
    I will have to get used to this.
  • tom_mai78101 tom_mai78101:
    The latest news feed looks good
  • The Helper The Helper:
    I would like to see it again like Ghan had it the first time with pagination though - without the pagination that view will not work but with pagination it just might...
  • The Helper The Helper:
    This drink recipe I have had more than a few times back in the day! Mind Eraser https://www.thehelper.net/threads/cocktail-mind-eraser.194720/

      The Helper Discord

      Members online

      No members online now.

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top