Spell Phase Nova (Need name)

BlackRose

Forum User
Reaction score
239
Phase Nova v1.00 by BlackRose

Last updated: Never​

Big screenie!
phasenova.jpg

When you learn it, you get an ability called Angle Setter.
When you cast it you disappear, birdies go outwards, after xxx distance they disappear and become illusions of the caster unit, they move outwards to confuse enemies.
You then move instantly to some angle, based on the level of Angle Setter.

When you cast Angle Setter, it changes angle of which you will teleport.



The General

Code: vJASS
Lags: Unsure.
Leaks: Don't think so.

Requirements


Coding:
JASS:
scope PhaseNova initializer Init
//-----------------------------------------
//      Phase Nova v1.00 by BlackRose
//-----------------------------------------

// Last updated: 1st June 2009

// Sends out birds, after they fly some distance, you morph into one of them. Illusions are made to confuse enemy <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" class="smilie smilie--sprite smilie--sprite1" alt=":)" title="Smile    :)" loading="lazy" data-shortname=":)" />

//-----------------------------------------
// Does this lag? Not for me?
// Is this fully MUI? You can only have up to 400 instances...

//-----------------------------------------
// How to import to your map:

// 1. Copy this trigger, and TimerUtils into your map.
// 2. Copy the objects into your map.
// 3. Find the rawcode of objects pasted and replace them here. Bird rawcodes are at the bottom in Init function.
// 4. Done, don&#039;t remove &quot;Phase Nova v1.xx&quot; by BlackRose if you use it.

// RESOURCE CREDITS:
// - Vexorian: TimerUtils
// - NewGen.

//-----------------------------------------
// TheHelper.net
// clanmapz.com
// wc3campaigns.net

// BJDebugMsg last for 60 seconds, hateful!
private function DEBUG takes string s returns nothing
debug    call DisplayTimedTextToPlayer( Player(0), 0, 0, 1.500, s )
endfunction

// Quicker.
private function TT takes string s, location l returns nothing
debug     local texttag tt = CreateTextTag()
debug     call SetTextTagPermanent( tt, false )
debug     call SetTextTagTextBJ( tt, s, 10.00 )
debug     call SetTextTagPosBJ( tt, l, 0 )
debug     call SetTextTagVelocityBJ( tt, 64.00, 90.00 )
debug     call SetTextTagLifespan( tt, 3.50 )
debug     call SetTextTagFadepoint( tt, 3.00 )
    
debug     set tt = null
endfunction

//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
//                             LIST OF CONFIGURABLES
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
globals
    private constant integer SPELL_ID = &#039;A001&#039; //&lt;-- Object rawcodes. Main spell.
    private constant integer LEARN_ID = &#039;A000&#039; //&lt;-- Angle setter rawcode.
    private integer array BIRD_ID   //&lt;-- Dummy bird&#039;s id, set in Init function.

    
    private constant boolean SELECT_HERO  = true //&lt;-- Reselects the hero after he shifts.
    private constant boolean SHIFT_CAMERA = false //&lt;-- Shifts camera to position of hero if on.

    private constant real TIMER_INTERVAL   = 0.04 //&lt;-- Timer interval.
    private constant real BIRD_SPEED       = 20   //&lt;-- Move bird 20 every TIMER_INTERVAL in angle.
    private constant real ANGLE_VARIATIONS = 12   //&lt;-- How many angle variations there can be. You need to edit the
                                                  //    object editor if you change this.
    
    private constant integer BIRD_AMOUNT = 20   //&lt;-- How many birdies <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" class="smilie smilie--sprite smilie--sprite1" alt=":)" title="Smile    :)" loading="lazy" data-shortname=":)" />
    // Effect shown for angle setting.
    private constant string MODEL = &quot;Abilities\\Spells\\Human\\Polymorph\\PolyMorphDoneGround.mdl&quot;

    // (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
    
// How far birdy go and teleport distance.
private function GetDistance takes integer level returns real
    return level * 200. + 400.
endfunction
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

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

globals
    private constant real ANGLE_DIFF = 360 / BIRD_AMOUNT
    private group LearnGroup = CreateGroup()
    private constant real ANGLE_VAR = 360 / ANGLE_VARIATIONS
endglobals

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


private struct PhaseNova
    unit array Bird[BIRD_AMOUNT]
    unit Caster
    player Owner
    real cx
    real cy
    
    integer uLevel
    timer t
    
    real DistanceTrav
    real Distance = 0
    static method create takes nothing returns PhaseNova
        local PhaseNova d = PhaseNova.allocate()
        local integer i = 0
        local integer btype = 0
        local real ang = 0
        
        set d.t = NewTimer()
        set d.Caster = GetTriggerUnit()
        set d.Owner = GetOwningPlayer( d.Caster )
        set d.cx = GetUnitX( d.Caster )
        set d.cy = GetUnitY( d.Caster )
        
        set d.uLevel = GetUnitAbilityLevel( d.Caster, SPELL_ID )
        set d.DistanceTrav = GetDistance( d.uLevel )
        
        call ShowUnit( d.Caster, false )
        
        loop
            exitwhen i == BIRD_AMOUNT
            set d.Bird<i> = CreateUnit( d.Owner, BIRD_ID[btype], d.cx, d.cy, ang )
            set btype = btype + 1
            if btype == 4 then
                set btype = 0
            endif
            set ang = ang + ANGLE_DIFF
            set i = i + 1
        endloop
        return d
    endmethod
    
    static method MoveOut takes nothing returns nothing
        local PhaseNova d = GetTimerData( GetExpiredTimer() )
        local integer i = 0
        local real newx
        local real newy
        local real angle
        local integer level
        local unit u
        
        loop
            exitwhen i == BIRD_AMOUNT
            
            set newx = GetUnitX( d.Bird<i> ) + BIRD_SPEED * Cos( bj_DEGTORAD * GetUnitFacing( d.Bird<i> ) )
            set newy = GetUnitY( d.Bird<i> ) + BIRD_SPEED * Sin( bj_DEGTORAD * GetUnitFacing( d.Bird<i> ) )
            call SetUnitPosition( d.Bird<i>, newx, newy )
            
            set i = i + 1
        endloop
        
        set d.Distance = d.Distance + BIRD_SPEED
        if d.DistanceTrav &lt; d.Distance then
            call ShowUnit( d.Caster, true )
            if (GetLocalPlayer() == d.Owner) and SELECT_HERO then
                // Use only local code (no net traffic) within this block to avoid desyncs.
                call ClearSelection()
                call SelectUnit(d.Caster, true)
            endif
            set level = GetUnitAbilityLevel( d.Caster, LEARN_ID )
            set newx = d.cx + d.DistanceTrav * Cos( (( level * 30 ) - 30 ) * bj_DEGTORAD )
            set newy = d.cy + d.DistanceTrav * Sin( (( level * 30 ) - 30 ) * bj_DEGTORAD )
            call SetUnitX( d.Caster, newx )
            call SetUnitY( d.Caster, newy )

            if (GetLocalPlayer() == d.Owner) and SHIFT_CAMERA then
                // Use only local code (no net traffic) within this block to avoid desyncs.
                call PanCameraTo( GetUnitX( d.Caster ), GetUnitY( d.Caster ) )
            endif
            set i = 0
            loop
                exitwhen i == BIRD_AMOUNT

                call KillUnit( d.Bird<i> )
                set i = i + 1
            endloop
            set i = 0
            loop
                exitwhen i == ANGLE_VARIATIONS
                set newx = d.cx + d.DistanceTrav * Cos( bj_DEGTORAD * ( i * ANGLE_VAR ) ) 
                set newy = d.cy + d.DistanceTrav * Sin( bj_DEGTORAD * ( i * ANGLE_VAR ) ) 
                
                set angle = bj_RADTODEG * Atan2( d.cy - newy, d.cx - newx )
                
                set u = CreateUnit( d.Owner, BIRD_ID[0], newx, newy, 0 )
                call SetUnitUserData( u, R2I(angle) )

                call ShowUnit( u, false )

                call UnitApplyTimedLife( u, &#039;BTLF&#039;, 0.50 )
                call IssueTargetOrderById( u, 852274, d.Caster )

                set i = i + 1
            endloop

            call ReleaseTimer( d.t )
            call d.destroy()
        endif
        set u = null
    endmethod
endstruct

private function Main takes nothing returns nothing
    local PhaseNova d = PhaseNova.create()
    
    call TimerStart( d.t, TIMER_INTERVAL, true, function PhaseNova.MoveOut )
    call SetTimerData( d.t, d )
endfunction

//-------------------------------------------------------------------------------

// If the learning skill is Angle Setter, level of main skill is &gt; 0 and
// Learning unit is not in the group LearnGroup.
private function LearnCond takes nothing returns boolean
    return GetLearnedSkill() == SPELL_ID and GetUnitAbilityLevel( GetTriggerUnit(), SPELL_ID ) &gt; 0 and IsUnitInGroup( GetTriggerUnit(), LearnGroup ) == false
endfunction

// When learned the main skill, add Angle Setter to caster.
private function LearnAdd takes nothing returns nothing
    local unit u = GetTriggerUnit()
    call UnitAddAbility( u, LEARN_ID )
    call GroupAddUnit( LearnGroup, u )
    set u = null
endfunction

// Is the spell casted Angle Setter?
private function AngCond takes nothing returns boolean
    return GetSpellAbilityId() == LEARN_ID
endfunction

private function AngAct takes nothing returns nothing
    local unit u = GetTriggerUnit()
    local integer level = GetUnitAbilityLevel( u, LEARN_ID )
    local integer mainLevel = GetUnitAbilityLevel( u, SPELL_ID )
    // Reals dont take up much do they? This is for easier reading.
    local real x
    local real y
    local real angle
    
    // Shows the angle in which you will phase out on next cast. For caster only.
    if GetLocalPlayer() == GetOwningPlayer( u ) then
        set angle = bj_DEGTORAD * ( level * ANGLE_VAR )
        set x = GetUnitX( u ) + GetDistance( mainLevel ) * Cos( angle )
        set y = GetUnitY( u ) + GetDistance( mainLevel ) * Sin( angle )
        call DestroyEffect( AddSpecialEffect( MODEL, x, y ) )
    endif
    
    // If level is 12, reset angle to 0/360.
    if level == ANGLE_VARIATIONS then
        call SetUnitAbilityLevel( u, LEARN_ID, 1 )   
    // Otherwise just set it to level + 1.
    else
        call SetUnitAbilityLevel( u, LEARN_ID, level + 1 )
    endif
    set u = null
endfunction

//-------------------------------------------------------------------------------

//...lol.
private function SummonCond takes nothing returns boolean
    return GetUnitTypeId( GetSummoningUnit() ) == BIRD_ID[0]
endfunction

private function SummonAct takes nothing returns nothing
    local unit imageunit = GetSummonedUnit()
    local unit summer = GetSummoningUnit()
    local integer angle = GetUnitUserData( summer ) + 180
    
    local location l
    local location moveLoc
    
    // Move the image right back at its caster&#039;s foot.
    call SetUnitPosition( imageunit, GetUnitX( summer ), GetUnitY( summer ) )
    // Now order it to move.
    // Working in angles as radians stuff up here...
    set l = GetUnitLoc( imageunit )
    set moveLoc = PolarProjectionBJ( l, 600., angle )
    call IssuePointOrderLoc( imageunit, &quot;move&quot;, moveLoc )
    debug call TT( R2S( angle ), GetUnitLoc( summer ) )
    
    call RemoveLocation( l )
    call RemoveLocation( moveLoc )
    set l = null
    set moveLoc = null
    set imageunit = null
    set summer = null
endfunction

//=============================================================================================
private function Init takes nothing returns nothing
    local trigger t = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( t, Condition( function Cond ) )
    call TriggerAddAction( t, function Main )
    
//-------------------------------------------------------------------------------
    
    set t = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_HERO_SKILL )
    call TriggerAddCondition( t, Condition( function LearnCond ) )
    call TriggerAddAction( t, function LearnAdd )
    
    set t = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( t, Condition( function AngCond ) )
    call TriggerAddAction( t, function AngAct )
    
//-------------------------------------------------------------------------------
    
    set t = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SUMMON )
    call TriggerAddCondition( t, Condition( function SummonCond ) )
    call TriggerAddAction( t, function SummonAct )
    
    // Set bird raw codes here. 
    // Blue, red, green , purple.
    set BIRD_ID[0] = &#039;e000&#039;
    set BIRD_ID[1] = &#039;e001&#039;
    set BIRD_ID[2] = &#039;e002&#039;
    set BIRD_ID[3] = &#039;e003&#039;
endfunction

endscope</i></i></i></i></i></i></i>
 
Reaction score
341
  • You do know SetUnitPosition doesn't ignore pathing, right? This may create some awkward circles.
  • Store your birds Cos/Sin in the create method, this will save a lot of function calls.
    JASS:
    BIRD_SPEED * Cos( bj_DEGTORAD * GetUnitFacing( d.Bird<i> ) )
    BIRD_SPEED * Sin( bj_DEGTORAD * GetUnitFacing( d.Bird<i> ) )
    </i></i>

    [*]
    JASS:
    call PanCameraTo( GetUnitX( d.Caster ), GetUnitY( d.Caster ) )
    You may want to make that timed/configurable.
    [*]In your SummonAct function, why are you using locations?
    [*]This spell looks more like a cinematic one than one that can actually be used in a game, but I haven't actually tested it yet.
    [*]Why is this in the configurables block?
    JASS:
    private integer array BIRD_ID   //&lt;-- Dummy bird&#039;s id, set in Init function.

 

BlackRose

Forum User
Reaction score
239
> You do know SetUnitPosition doesn't ignore pathing, right? This may create some awkward circles.
Crash and burn for map boundary?

JASS:
call PanCameraTo( GetUnitX( d.Caster ), GetUnitY( d.Caster ) )

> You may want to make that timed/configurable.
Ok.

> In your SummonAct function, why are you using locations?
I use custom value (userData), because it can only be integer, and reals are rounded off, radians stuff up. So using location and degrees is easier. I'm still not sure how to use PUI properly either.

> This spell looks more like a cinematic one than one that can actually be used in a game, but I haven't actually tested it yet.
It looks.................. why don't people test.

> Why is this in the configurables block?
JASS:
private integer array BIRD_ID   //&lt;-- Dummy bird&#039;s id, set in Init function.

Why not?
 

wraithseeker

Tired.
Reaction score
122
JASS:
852274.......


Hardcoded much?

JASS:
0.50


Same.

JASS:
+ 30


Same.

600.
180.
"Move"

Grrrr....

What's with the Show and Hide Unit stuff? Why do you even need to hide them?
JASS:

local integer angle = GetUnitUserData( summer ) + 180


What! Why GetUnitUserData? Use a indexing system or do some kind of wrappers.
 

Flare

Stops copies me!
Reaction score
662
You do know SetUnitPosition doesn't ignore pathing, right? This may create some awkward circles.
What about trees? I can't remember if I've tested SetUnitX/Y vs. SetUnitPosition near trees, but I think SetUnitPosition would 'push' you out of the trees (assumng they are bunched closely together), while SetUnitX/Y would leave you stuck there - usability and functionality > visuals

Hardcoded much?
While hardcoded stuff is lame, there probably isn't many other abilities anyone would want to use here. Wand of Illusion is probably the only ability that would need to be cast multiple times for the desired effect (why would you want 11x Bloodlust one one unit? ;))

From the looks of it, that's for an expiration timer and is not a big deal.

What! Why GetUnitUserData? Use a indexing system or do some kind of wrappers.
Agreed, may I suggest AutoIndex?
It's only a dummy that's using it - nothing outside the spell will be accessing those dummies, so it doesn't really matter. If it was the caster unit, then it'd be a good idea to use some unit indexing system but until then, it's not really necessary
 

simonake

New Member
Reaction score
72
Very original but, you should add:

When all units are created, pan camera on the good one.
 

Viikuna

No Marlo no game.
Reaction score
265
I like those colourfull birds. The spell idea is also pretty nice.


And dont do any camera pans. if you have many units casting these at the same time, it will be kinda horrible.
 

simonake

New Member
Reaction score
72
I meant when all the clones are created, it's hard to know the right one. Yes, he's selected but, we have to find his location. Panning camera would make it lot more enjoyable.
 

BlackRose

Forum User
Reaction score
239
You people do mean SetUnitPosition for the bird's movement right ( the only one I can see is used there and in dummy making)? Isn't flying movement allowed over anything?

> Hardcoded much?
Any solution?

Display the whole in rather than just a simple '.50' please.
Do you mean:
JASS:
call UnitApplyTimedLife( u, &#039;BTLF&#039;, 0.50 )


And.... 600, 180? WHAT? Can you translate that? The unit dummy casting illusion is the blue bird, I don't want it being shown :p

> Very original but, you should add:

When all units are created, pan camera on the good one.

Isn't it already panning onto the real copy? Or did I set the camera pan boolean to false in test map :p

> I like those colourfull birds. The spell idea is also pretty nice.


And dont do any camera pans. if you have many units casting these at the same time, it will be kinda horrible.

:) That is why I set a boolean for camera pan.

Thanks for feedback everyone!
 

Viikuna

No Marlo no game.
Reaction score
265
Use only one global dummy for casting those illusions. CreateUnit is shit ass slow function, so it will lagg if this spell is spammed a lot.

If you just create one global dummy in map init, and then use SetUnitX&Y to move it around and maybe change its owner before casting. It should work more smoothly. ( And yes, it is MUI, no need to ask. Just set spell casting time to 0 and dummies casting animation time thingies to 0 too )
 

BlackRose

Forum User
Reaction score
239
Use only one global dummy for casting those illusions. CreateUnit is shit ass slow function, so it will lagg if this spell is spammed a lot.

If you just create one global dummy in map init, and then use SetUnitX&Y to move it around and maybe change its owner before casting. It should work more smoothly. ( And yes, it is MUI, no need to ask. Just set spell casting time to 0 and dummies casting animation time thingies to 0 too )
It doesn't work! It only makes like 2 or 3 illusions. Yes, Animation Backswing and Castpoint to 0.00, casting time also 0.00.

(I haven't updated the code version (like the v1.00 below))
(I create "Dummy" in Init function)
JASS:
scope PhaseNova initializer Init
//-----------------------------------------
//      Phase Nova v1.00 by BlackRose
//-----------------------------------------

// Last updated: 1st June 2009

// Sends out birds, after they fly some distance, you morph into one of them. Illusions are made to confuse enemy <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" class="smilie smilie--sprite smilie--sprite1" alt=":)" title="Smile    :)" loading="lazy" data-shortname=":)" />

//-----------------------------------------
// Does this lag? Not for me?
// Is this fully MUI? You can only have up to 400 instances...

//-----------------------------------------
// How to import to your map:

// 1. Copy this trigger, and TimerUtils into your map.
// 2. Copy the objects into your map.
// 3. Find the rawcode of objects pasted and replace them here. Bird rawcodes are at the bottom in Init function.
// 4. Done, don&#039;t remove &quot;Phase Nova v1.xx&quot; by BlackRose if you use it.

// RESOURCE CREDITS:
// - Vexorian: TimerUtils
// - NewGen.

//-----------------------------------------
// TheHelper.net
// clanmapz.com
// wc3campaigns.net

// BJDebugMsg last for 60 seconds, hateful!
private function DEBUG takes string s returns nothing
debug    call DisplayTimedTextToPlayer( Player(0), 0, 0, 1.500, s )
endfunction

// Quicker.
private function TT takes string s, location l returns nothing
debug     local texttag tt = CreateTextTag()
debug     call SetTextTagPermanent( tt, false )
debug     call SetTextTagTextBJ( tt, s, 10.00 )
debug     call SetTextTagPosBJ( tt, l, 0 )
debug     call SetTextTagVelocityBJ( tt, 64.00, 90.00 )
debug     call SetTextTagLifespan( tt, 3.50 )
debug     call SetTextTagFadepoint( tt, 3.00 )
    
debug     set tt = null
endfunction

//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
//                             LIST OF CONFIGURABLES
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
globals
    private constant integer SPELL_ID = &#039;A001&#039; //&lt;-- Object rawcodes. Main spell.
    private constant integer LEARN_ID = &#039;A000&#039; //&lt;-- Angle setter rawcode.
    
    private constant boolean SELECT_HERO  = true //&lt;-- Reselects the hero after he shifts.
    private constant boolean SHIFT_CAMERA = false //&lt;-- Shifts camera to position of hero if on.

    private constant real TIMER_INTERVAL   = 0.04 //&lt;-- Timer interval.
    private constant real BIRD_SPEED       = 20   //&lt;-- Move bird 20 every TIMER_INTERVAL in angle.
    private constant real ANGLE_VARIATIONS = 12   //&lt;-- How many angle variations there can be. You need to edit the
                                                  //    object editor if you change this.
    
    private constant integer BIRD_AMOUNT = 20   //&lt;-- How many birdies <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" class="smilie smilie--sprite smilie--sprite1" alt=":)" title="Smile    :)" loading="lazy" data-shortname=":)" />
    // Effect shown for angle setting.
    private constant string MODEL = &quot;Abilities\\Spells\\Human\\Polymorph\\PolyMorphDoneGround.mdl&quot;

    // (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
    
// How far birdy go and teleport distance.
private function GetDistance takes integer level returns real
    return level * 100. + 400.
endfunction
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

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

globals
    private integer array BIRD_ID   //&lt;-- Dummy bird&#039;s id, set in Init function.

    private constant real ANGLE_DIFF = 360 / BIRD_AMOUNT
    private group LearnGroup = CreateGroup()
    private constant real ANGLE_VAR = 360 / ANGLE_VARIATIONS
    
    private unit Dummy
endglobals

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


private struct PhaseNova
    unit array Bird[BIRD_AMOUNT]
    unit Caster
    player Owner
    real cx
    real cy
    
    real array cos[BIRD_AMOUNT]
    real array sin[BIRD_AMOUNT]
    
    integer uLevel
    timer t
    
    real DistanceTrav
    real Distance = 0
    static method create takes nothing returns PhaseNova
        local PhaseNova d = PhaseNova.allocate()
        local integer i = 0
        local integer btype = 0
        local real ang = 0
        
        set d.t = NewTimer()
        set d.Caster = GetTriggerUnit()
        set d.Owner = GetOwningPlayer( d.Caster )
        set d.cx = GetUnitX( d.Caster )
        set d.cy = GetUnitY( d.Caster )
        
        set d.uLevel = GetUnitAbilityLevel( d.Caster, SPELL_ID )
        set d.DistanceTrav = GetDistance( d.uLevel )
        
        call ShowUnit( d.Caster, false )
        
        loop
            exitwhen i == BIRD_AMOUNT
            set d.Bird<i> = CreateUnit( d.Owner, BIRD_ID[btype], d.cx, d.cy, ang )
            set d.cos<i> = Cos( bj_DEGTORAD * GetUnitFacing( d.Bird<i> ) )
            set d.sin<i> = Sin( bj_DEGTORAD * GetUnitFacing( d.Bird<i> ) )
            set btype = btype + 1
            if btype == 4 then
                set btype = 0
            endif
            set ang = ang + ANGLE_DIFF
            set i = i + 1
        endloop
        return d
    endmethod
    
    static method MoveOut takes nothing returns nothing
        local PhaseNova d = GetTimerData( GetExpiredTimer() )
        local integer i = 0
        local real newx
        local real newy
        local real angle
        local integer level
        local unit u
        

        
        loop
            exitwhen i == BIRD_AMOUNT
            
            set newx = GetUnitX( d.Bird<i> ) + BIRD_SPEED * d.cos<i>
            set newy = GetUnitY( d.Bird<i> ) + BIRD_SPEED * d.sin<i>
            call SetUnitPosition( d.Bird<i>, newx, newy )
            
            set i = i + 1
        endloop
        
        set d.Distance = d.Distance + BIRD_SPEED
        if GetDistance( d.uLevel ) &lt; d.Distance then
            call BJDebugMsg(&quot;Ended&quot; )
            call ShowUnit( d.Caster, true )
            if (GetLocalPlayer() == d.Owner) and SELECT_HERO then
                // Use only local code (no net traffic) within this block to avoid desyncs.
                call ClearSelection()
                call SelectUnit(d.Caster, true)
            endif
            set level = GetUnitAbilityLevel( d.Caster, LEARN_ID )
            set newx = d.cx + d.DistanceTrav * Cos( (( level * 30 ) - 30 ) * bj_DEGTORAD )
            set newy = d.cy + d.DistanceTrav * Sin( (( level * 30 ) - 30 ) * bj_DEGTORAD )
            call SetUnitX( d.Caster, newx )
            call SetUnitY( d.Caster, newy )

            if (GetLocalPlayer() == d.Owner) and SHIFT_CAMERA then
                // Use only local code (no net traffic) within this block to avoid desyncs.
                call PanCameraTo( GetUnitX( d.Caster ), GetUnitY( d.Caster ) )
            endif
            set i = 0
            loop
                exitwhen i == BIRD_AMOUNT

                call KillUnit( d.Bird<i> )
                set i = i + 1
            endloop
            set i = 0
            loop
                exitwhen i == ANGLE_VARIATIONS
                set newx = d.cx + d.DistanceTrav * Cos( bj_DEGTORAD * ( i * ANGLE_VAR ) ) 
                set newy = d.cy + d.DistanceTrav * Sin( bj_DEGTORAD * ( i * ANGLE_VAR ) ) 
                
                set angle = ( bj_RADTODEG * Atan2( d.cy - newy, d.cx - newx ) ) + 180.00
                
                //set u = CreateUnit( d.Owner, BIRD_ID[0], newx, newy, 0 )
                call SetUnitX( Dummy, newx )
                call SetUnitY( Dummy, newy )
                call SetUnitOwner( Dummy, d.Owner, true )
                call SetUnitUserData( Dummy, R2I(angle) )

                //call ShowUnit( u, false )

                //call UnitApplyTimedLife( u, &#039;BTLF&#039;, 0.50 )
                call IssueTargetOrderById( Dummy, 852274, d.Caster )

                set i = i + 1
            endloop
            call ReleaseTimer( d.t )
            call d.destroy()
            endif

        set u = null
    endmethod
endstruct

private function Main takes nothing returns nothing
    local PhaseNova d = PhaseNova.create()
    
    call TimerStart( d.t, TIMER_INTERVAL, true, function PhaseNova.MoveOut )
    call SetTimerData( d.t, d )
endfunction

//-------------------------------------------------------------------------------

// If the learning skill is Angle Setter, level of main skill is &gt; 0 and
// Learning unit is not in the group LearnGroup.
private function LearnCond takes nothing returns boolean
    return GetLearnedSkill() == SPELL_ID and GetUnitAbilityLevel( GetTriggerUnit(), SPELL_ID ) &gt; 0 and IsUnitInGroup( GetTriggerUnit(), LearnGroup ) == false
endfunction

// When learned the main skill, add Angle Setter to caster.
private function LearnAdd takes nothing returns nothing
    local unit u = GetTriggerUnit()
    call UnitAddAbility( u, LEARN_ID )
    call GroupAddUnit( LearnGroup, u )
    set u = null
endfunction

// Is the spell casted Angle Setter?
private function AngCond takes nothing returns boolean
    return GetSpellAbilityId() == LEARN_ID
endfunction

private function AngAct takes nothing returns nothing
    local unit u = GetTriggerUnit()
    local integer level = GetUnitAbilityLevel( u, LEARN_ID )
    local integer mainLevel = GetUnitAbilityLevel( u, SPELL_ID )
    // Reals dont take up much do they? This is for easier reading.
    local real x
    local real y
    local real angle
    
    // Shows the angle in which you will phase out on next cast. For caster only.
    if GetLocalPlayer() == GetOwningPlayer( u ) then
        set angle = bj_DEGTORAD * ( level * ANGLE_VAR )
        set x = GetUnitX( u ) + GetDistance( mainLevel ) * Cos( angle )
        set y = GetUnitY( u ) + GetDistance( mainLevel ) * Sin( angle )
        call DestroyEffect( AddSpecialEffect( MODEL, x, y ) )
    endif
    
    // If level is 12, reset angle to 0/360.
    if level == ANGLE_VARIATIONS then
        call SetUnitAbilityLevel( u, LEARN_ID, 1 )   
    // Otherwise just set it to level + 1.
    else
        call SetUnitAbilityLevel( u, LEARN_ID, level + 1 )
    endif
    set u = null
endfunction

//-------------------------------------------------------------------------------

//...lol.
private function SummonCond takes nothing returns boolean
    return GetUnitTypeId( GetSummoningUnit() ) == BIRD_ID[0]
endfunction

private function SummonAct takes nothing returns nothing
    local unit imageunit = GetSummonedUnit()
    local unit summer = GetSummoningUnit()
    local integer angle = GetUnitUserData( summer )
    
    local location l
    local location moveLoc
    
    // Move the image right back at its caster&#039;s foot.
    call SetUnitPosition( imageunit, GetUnitX( summer ), GetUnitY( summer ) )
    // Now order it to move.
    // Working in angles as radians stuff up here...
    set l = GetUnitLoc( imageunit )
    set moveLoc = PolarProjectionBJ( l, 600., angle )
    call IssuePointOrderLoc( imageunit, &quot;move&quot;, moveLoc )
    debug call TT( R2S( angle ), GetUnitLoc( summer ) )
    
    call RemoveLocation( l )
    call RemoveLocation( moveLoc )
    set l = null
    set moveLoc = null
    set imageunit = null
    set summer = null
endfunction

//=============================================================================================
private function Init takes nothing returns nothing
    local trigger t = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( t, Condition( function Cond ) )
    call TriggerAddAction( t, function Main )
    
//-------------------------------------------------------------------------------
    
    set t = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_HERO_SKILL )
    call TriggerAddCondition( t, Condition( function LearnCond ) )
    call TriggerAddAction( t, function LearnAdd )
    
    set t = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( t, Condition( function AngCond ) )
    call TriggerAddAction( t, function AngAct )
    
//-------------------------------------------------------------------------------
    
    set t = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SUMMON )
    call TriggerAddCondition( t, Condition( function SummonCond ) )
    call TriggerAddAction( t, function SummonAct )
    
    // Set bird raw codes here. 
    // Blue, red, green , purple.
    set BIRD_ID[0] = &#039;e000&#039;
    set BIRD_ID[1] = &#039;e001&#039;
    set BIRD_ID[2] = &#039;e002&#039;
    set BIRD_ID[3] = &#039;e003&#039;
    set Dummy = CreateUnit( Player(15), BIRD_ID[0], 0.00, 0.00, 0.00 )
    call ShowUnit( Dummy, false )
endfunction

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



Map is attached.
 

BlackRose

Forum User
Reaction score
239
And good work using UserData instead of an indexing system for dummy units. :)
*Cough*.... sarcasm right?

Someone says it was sarcasm -.- (rekeeshtiarw)

esorkcalb <-- I like that name.
 

Jesus4Lyf

Good Idea™
Reaction score
397
>*Cough*.... sarcasm right?
Nup. It's what I'd probably do if I knew of a reason to attach to dummy units.

Say, why are you doing that? *Examines code...*

Edit:
On closer inspection it appears to be due to a lack of dynamic triggers (which is fine). My advice is to store the angle on the dummy unit's health, or its facing, or its mana or something else... Just in case some retarded indexing system auto-indexes dummy units. >_>
 

BlackRose

Forum User
Reaction score
239
...wraithseeker said it was sarcasm the way you say:
Health - 0 HP means it becomes dead.
Facing - Used for the illusion movement.
Mana - Could.

BTW, did you check the top code or the latest one at the bottom?
 

Viikuna

No Marlo no game.
Reaction score
265
One dummy method should work just fine. Check my AssAssTidies test map: link
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      The Helper Discord

      Staff online

      Members online

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top