• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.
  • It's time for the first HD Modeling Contest of 2025. Join the theme discussion for Hive's HD Modeling Contest #7! Click here to post your idea!

Skyfall Boulders v1.0

Description:
292042-0b16312a4ad3a7be9989ee69b2640105.png


Previews:
292040-3836d458991318672dfebc28445a0c86.gif

292041-1a5e1807cb268c5222c504a949d6c5d6.gif


JASS:
//---------------------------------------------------------------------------
//
//                             Rheiko presents
//                          Skyfall Boulders v1.0
//
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
//        Description:
//             Conjures a number of boulders up in the sky and creates two 
//             portals, one right below the boulders and another right above 
//             the target location, connected to each other, increasing the
//             fall duration, allowing the boulders to hit the ground
//             with lethal impact,  causing 2 seconds stun to the nearby 
//             enemies units and knocking them back.
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
//        Credits:
//             - AlienAtSystem for CircleOutBuff.mdx and
//               CircleOutBuff_Portrait.mdx
//             - WILL THE ALMIGHTY for NewDirtEXNofire.mdx
//             - Bribe for Knockback2D System
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
//
//                          ==  HOW TO IMPORT   ==
//
//          1. Copy Skyfall Boulders ability from Object Editor and paste it
//             into your map.
//          2. Copy Skyfall Boulders (Stun) ability from Object Editor and 
//             paste it into your map.
//          3. Copy SB_Dummy1, SB_Dummy2, SB_Dummy3, SB_Dummy4, SB_DummyCaster
//             unit from Object Editor and paste them into your map.
//          4. Make sure your World Editor has "Automatically Create Unknown 
//             Variable" enabled. You can find it in File => Preferences
//          5. Copy the trigger in Skyfall Boulders folder and in Knockback2D
//             folder (Optional) and paste them into your map.
//          6. Double check to make sure everything works properly.
//
//          Note:
//              If you prefer to not import Knockback2D and does not want to
//              utilize the Knockback aspect of the spell, simply import the
//              triggers in Skyfall Boulders. You can leave the triggers in
//              Knockback2D out. But make sure you change the value of:
//              
//                  constant function SKBO_KBTrigger takes nothing returns trigger 
//                      return gg_trg_Knockback_2D
//                  endfunction
//
//              you can find it configuration down below, change it to null like this:
//
//                  constant function SKBO_KBTrigger takes nothing returns trigger 
//                      return null
//                  endfunction
//
//              it is recommended that you also disable SKBO_Knockback function by
//              setting it to false. You should now be good to go!! ^_^
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------

//***************************************************************************
//  Configuration Starts Here
//***************************************************************************
//---------------------------------------------------------------------------
//  The AbilityID for Skyfall Boulders Spell, make sure it has the same
//  value as in Object Editor (to check, press CTRL+D in Object Editor)
//---------------------------------------------------------------------------
constant function SKBO_AbilityID takes nothing returns integer
    return 'A000'
endfunction

//---------------------------------------------------------------------------
//  The AbilityID for Dummy to cast, 
//  make sure it has the same value as in Object Editor
//  (to check, press CTRL+D in Object Editor)
//  - First one is for stun purpose (based on firebolt)
//  - Second one is for destroying tree purpose (harvest)
//---------------------------------------------------------------------------
constant function SKBO_StunAbilityID takes nothing returns integer
    return 'A001'
endfunction

constant function SKBO_HarvestAbilityID takes nothing returns integer 
    return 'Ahrl'
endfunction

//---------------------------------------------------------------------------
//  This is the Order ID of DummyAbility, this is required to cast the 
//  ability itself. Check out this list for a reference:
//  https://github.com/nestharus/JASS/blob/master/jass/Systems/OrderIds/script.j
//---------------------------------------------------------------------------
constant function SKBO_DOrderID takes nothing returns integer 
    return 852231
endfunction

//---------------------------------------------------------------------------
//  The DummyID for Skyfall Boulders Spell, make sure it has the same
//  value as in Object Editor (to check, press CTRL+D in Object Editor)
//  - Dummy1 = The boulder
//  - Dummy2 = The Portal in the ground
//  - Dummy3 = The Portal in the sky
//  - Dummy4 = The feral spirit effect for spawn effect
//---------------------------------------------------------------------------
constant function SKBO_Dummy1ID takes nothing returns integer 
    return 'h000'
endfunction

constant function SKBO_Dummy2ID takes nothing returns integer 
    return 'h001'
endfunction

constant function SKBO_Dummy3ID takes nothing returns integer 
    return 'h002'
endfunction

constant function SKBO_Dummy4ID takes nothing returns integer 
    return 'h003'
endfunction

constant function SKBO_DummyCasterID takes nothing returns integer 
    return 'h004'
endfunction

//---------------------------------------------------------------------------
//  This determines the owner for the dummy units 
//  You can set it to Neutral Passive to not mess up with game scoreboard
//---------------------------------------------------------------------------
constant function SKBO_DummyOwner takes nothing returns player 
    return Player(PLAYER_NEUTRAL_PASSIVE)
endfunction

//---------------------------------------------------------------------------
//  This determines the size/scale of the dummy
//  - Dummy1 = The boulder
//  - Dummy2 = The Portal in the ground
//  - Dummy3 = The Portal in the sky
//  - Dummy4 = The feral spirit effect for spawn effect
//---------------------------------------------------------------------------
constant function SKBO_Dummy1Size takes nothing returns real
    return 1.5
endfunction

constant function SKBO_Dummy2Size takes nothing returns real
    return 1.0
endfunction

constant function SKBO_Dummy3Size takes nothing returns real
    return 1.0
endfunction

constant function SKBO_Dummy4Size takes nothing returns real
    return 1.0
endfunction

//---------------------------------------------------------------------------
//  This determines the starting height of the dummy
//  - Dummy1 = The boulder
//  - Dummy2 = The Portal in the ground
//  - Dummy3 = The Portal in the sky
//  - Dummy4 = The feral spirit effect for spawn effect
//---------------------------------------------------------------------------
constant function SKBO_Dummy1Height takes nothing returns real
    return 500.0
endfunction

constant function SKBO_Dummy2Height takes nothing returns real
    return 0.0
endfunction

constant function SKBO_Dummy3Height takes nothing returns real
    return 500.0
endfunction

constant function SKBO_Dummy4Height takes nothing returns real
    return 500.0
endfunction

//---------------------------------------------------------------------------
//  This determines the special effect model we will use in the spell
//  - Appears when the boulder explodes
//---------------------------------------------------------------------------
constant function SKBO_EffectString takes nothing returns string 
    return "war3mapImported\\NewDirtEXNofire.mdx"
endfunction

//---------------------------------------------------------------------------
//  How many boulders will be conjured and be used throughout the spell?
//  Feel free to play around with the number
//---------------------------------------------------------------------------
constant function SKBO_BaseNumber takes nothing returns integer
    return 1
endfunction

constant function SKBO_NumberPerLvl takes nothing returns integer 
    return 2
endfunction

//---------------------------------------------------------------------------
//  How much damage should the ability deal?
//  The calculation will be something like this:
//  Dmg = BaseDmg + (DmgPerLvl * Lvl)
//---------------------------------------------------------------------------
constant function SKBO_BaseDmg takes nothing returns real
    return 100.0
endfunction

constant function SKBO_DmgPerLvl takes nothing returns real
    return 75.0
endfunction

//---------------------------------------------------------------------------
//  This determines the Attack Type, Damage Type, and Weapon Type for the spell
//  It can be impactful if you consider how each type interact with armor type
//  You can ignore these if they do not concern you
//---------------------------------------------------------------------------
constant function SKBO_AT takes nothing returns attacktype
    return ATTACK_TYPE_NORMAL
endfunction

constant function SKBO_DT takes nothing returns damagetype
    return DAMAGE_TYPE_NORMAL
endfunction

constant function SKBO_WT takes nothing returns weapontype
    return WEAPON_TYPE_WHOKNOWS
endfunction

//---------------------------------------------------------------------------
//  How big of a radius will be affected by the damage?
//  This is also known as Area of Effect
//  The calculation will be something like this:
//  Aoe = BaseAoe + (AoePerLvl * Lvl)
//---------------------------------------------------------------------------
constant function SKBO_BaseAoe takes nothing returns real
    return 350.0
endfunction

constant function SKBO_AoePerLvl takes nothing returns real
    return 0.0
endfunction

//---------------------------------------------------------------------------
//  How fast should the boulder fall?
//---------------------------------------------------------------------------
constant function SKBO_BaseSpeed takes nothing returns real
    return 30.0
endfunction

constant function SKBO_SpeedPerLvl takes nothing returns real
    return 0.0
endfunction

//---------------------------------------------------------------------------
//  This is the acceleration value for the falling boulders
//---------------------------------------------------------------------------
constant function SKBO_BaseAccel takes nothing returns real
    return 1.5
endfunction

constant function SKBO_AccelPerLvl takes nothing returns real
    return 0.0
endfunction

//---------------------------------------------------------------------------
//  This allows you toggle the supported features
//  - return true to enable 
//  - return false to disable
//---------------------------------------------------------------------------
constant function SKBO_TreeDestroy takes nothing returns boolean
    return true 
endfunction

constant function SKBO_Knockback takes nothing returns boolean
    return true 
endfunction

constant function SKBO_Stun takes nothing returns boolean
    return true
endfunction

//---------------------------------------------------------------------------
//  Here you can adjust the settings of your knockback
//  You can ignore these if SKBO_Knockback is disabled
//---------------------------------------------------------------------------
constant function SKBO_KBDistance takes nothing returns real
    return 300.0 
endfunction

constant function SKBO_KBTime takes nothing returns real
    return 0.70
endfunction

//---------------------------------------------------------------------------
//  This is the trigger for Bribe's Knockback2D system
//  In case you do not want to import KB2D system into your map
//  You can just change this to return null
//  Don't forget to disable SKBO_Knockback as well
//---------------------------------------------------------------------------
constant function SKBO_KBTrigger takes nothing returns trigger 
    return gg_trg_Knockback_2D
endfunction

//---------------------------------------------------------------------------
//  The Timer Interval for Loop Function
//  Don't change this value unless you know what you are doing
//---------------------------------------------------------------------------
constant function SKBO_Interval takes nothing returns real
    return 0.0312500
endfunction

//---------------------------------------------------------------------------
//  This is Target Filter, you can modify this to suit your preferences
//---------------------------------------------------------------------------
constant function SKBO_TargetFilter takes unit u, player p returns boolean
    return not(IsUnitType(u, UNIT_TYPE_STRUCTURE)) and (IsUnitEnemy(u, p)) and not(IsUnitType(u, UNIT_TYPE_DEAD))
endfunction

//***************************************************************************
//  End of Configuration
//***************************************************************************

//---------------------------------------------------------------------------
//  RecycleNode Function
//---------------------------------------------------------------------------
function SKBO_RecycleNode takes integer Node returns nothing
    set udg_SKBO_RecycledStack[udg_SKBO_RecycledSize] = Node
    set udg_SKBO_RecycledSize = udg_SKBO_RecycledSize + 1
    set udg_SKBO_NodeNext[udg_SKBO_NodePrev[Node]] = udg_SKBO_NodeNext[Node]
    set udg_SKBO_NodePrev[udg_SKBO_NodeNext[Node]] = udg_SKBO_NodePrev[Node]
endfunction

//---------------------------------------------------------------------------
//  CreateNode Function
//---------------------------------------------------------------------------
function SKBO_CreateNode takes nothing returns integer 
    local integer Node
    
    if (udg_SKBO_RecycledSize == 0) then
        set udg_SKBO_MaxIndex = udg_SKBO_MaxIndex + 1
        set Node = udg_SKBO_MaxIndex
    else
        set udg_SKBO_RecycledSize = udg_SKBO_RecycledSize - 1
        set Node = udg_SKBO_RecycledStack[udg_SKBO_RecycledSize]
    endif
        
    set udg_SKBO_NodeNext[Node] = 0
    set udg_SKBO_NodeNext[udg_SKBO_NodePrev[0]] = Node
    set udg_SKBO_NodePrev[Node] = udg_SKBO_NodePrev[0]
    set udg_SKBO_NodePrev[0] = Node
    
    return Node
endfunction

//---------------------------------------------------------------------------
//  Destroy Tree Snippet
//---------------------------------------------------------------------------
function SKBO_IsDestTree takes destructable d returns boolean
    return IssueTargetOrderById(udg_SKBO_DummyCaster,852018,d) and IssueImmediateOrderById(udg_SKBO_DummyCaster,851972)
endfunction

function SKBO_DestroyTree takes destructable d returns nothing
    if SKBO_IsDestTree(d) and GetWidgetLife(d) > 0.405 then
        call KillDestructable(d)
    endif
endfunction

function SKBO_TreeFilter takes nothing returns boolean
    call SKBO_DestroyTree(GetFilterDestructable())
    return false
endfunction

//---------------------------------------------------------------------------
//  Loop Function
//---------------------------------------------------------------------------
function SKBO_Loop takes nothing returns nothing
    local integer Node = 0
    local integer i = 0
    local integer index = 0
    local real angle 
    local real x
    local real y
    local unit u
    local group g = CreateGroup()
    local rect r
    
    loop
        set Node = udg_SKBO_NodeNext[Node]  
        exitwhen Node == null
             
        //  Stage 1 handles the fall
        if (udg_SKBO_Stage[Node] == 1) then           
            if (udg_SKBO_CurrentHeight[Node] > 0) then
                set udg_SKBO_CurrentHeight[Node] = udg_SKBO_CurrentHeight[Node] - udg_SKBO_Velocity[Node]
                set udg_SKBO_Velocity[Node] = udg_SKBO_Velocity[Node] + udg_SKBO_Accel[Node]
                call SetUnitFlyHeight(udg_SKBO_DummyUnit[Node], udg_SKBO_CurrentHeight[Node], 0)         
                
            else                                
                if (udg_SKBO_Teleported[Node] == true) then
                    set udg_SKBO_Stage[Node] = 2
                    
                else                    
                    call KillUnit(udg_SKBO_DummyUnit2[Node]) 
                    call KillUnit(udg_SKBO_DummyUnit3[Node])                     
                    
                    set udg_SKBO_CurrentHeight[Node] = SKBO_Dummy1Height()
                    call SetUnitFlyHeight(udg_SKBO_DummyUnit[Node], udg_SKBO_CurrentHeight[Node], 0)          
                    set udg_SKBO_Teleported[Node] = true
                    
                    //  Move the boulders
                    call SetUnitX(udg_SKBO_DummyUnit[Node], udg_SKBO_DTargetX[Node])
                    call SetUnitY(udg_SKBO_DummyUnit[Node], udg_SKBO_DTargetY[Node])
                    
                endif         
            endif
        
        // Stage 2 handles damage and recycle
        elseif (udg_SKBO_Stage[Node] == 2) then
            call KillUnit(udg_SKBO_DummyUnit[Node])
            call DestroyEffect(AddSpecialEffect(SKBO_EffectString(), udg_SKBO_DTargetX[Node], udg_SKBO_DTargetY[Node])) 
            
            //  Deal damage and stuff only if it's the instance of Parent Node
            if (udg_SKBO_Parent[Node] == 0) then
                                
                call GroupEnumUnitsInRange(g, udg_SKBO_STargetX[Node], udg_SKBO_STargetY[Node], udg_SKBO_Aoe[Node], null)
                loop
                    set u = FirstOfGroup(g)
                    exitwhen u == null
                
                    if (SKBO_TargetFilter(u, udg_SKBO_Player[Node])) then
                        if (SKBO_Stun()) then
                            call IssueTargetOrderById(udg_SKBO_DummyCaster, SKBO_DOrderID(), u)
                            
                        endif
                        if ((GetWidgetLife(u) - udg_SKBO_Dmg[Node]) <= 0.405) then
                            call SetUnitExploded( u, true )
                        endif
                        call UnitDamageTarget(udg_SKBO_Caster[Node], u, udg_SKBO_Dmg[Node], false, false, SKBO_AT(), SKBO_DT(), SKBO_WT())         
                        if (SKBO_Knockback()) then
                            set x = GetUnitX(u)
                            set y = GetUnitY(u)
                            set udg_Knockback2DAngle = bj_RADTODEG * Atan2(y - udg_SKBO_STargetY[Node], x - udg_SKBO_STargetX[Node])                        
                            set udg_Knockback2DDistance = SKBO_KBDistance()
                            set udg_Knockback2DTime = SKBO_KBTime()
                            set udg_Knockback2DUnit = u
                            call TriggerExecute( SKBO_KBTrigger() )
                            
                        endif
                    endif
                    
                    call GroupRemoveUnit(g, u)
                endloop              
                
                //  Check if user wants to destroy tree
                if (SKBO_TreeDestroy()) then
                    set r = Rect(udg_SKBO_STargetX[Node] - udg_SKBO_Aoe[Node], udg_SKBO_STargetY[Node] - udg_SKBO_Aoe[Node], udg_SKBO_STargetX[Node] + udg_SKBO_Aoe[Node], udg_SKBO_STargetY[Node] + udg_SKBO_Aoe[Node])
                    call EnumDestructablesInRect(r, Condition(function SKBO_TreeFilter), null)
                    call RemoveRect(r)
                endif
            
            endif         
            
            //  Recycle
            call SKBO_RecycleNode(Node)           
            
            //  Turn off when there's no running instance left
            if (udg_SKBO_NodePrev[0] == 0) then
                call PauseTimer(udg_SKBO_Timer)
            endif
        endif                    
    
    endloop
    
    call DestroyGroup(g)
    set g = null
endfunction

//---------------------------------------------------------------------------
//  CreateBoulder Function
//---------------------------------------------------------------------------
function SKBO_CreateBoulder takes integer parent, real x, real y, real angle returns nothing
    local integer Node = SKBO_CreateNode()
    local unit u
    
    //  Set up the necessary data for boulder dummy
    set udg_SKBO_Parent[Node] = parent            
    set udg_SKBO_CurrentHeight[Node] = udg_SKBO_CurrentHeight[udg_SKBO_Parent[Node]]
    set udg_SKBO_Stage[Node] = udg_SKBO_Stage[udg_SKBO_Parent[Node]]
    set udg_SKBO_Teleported[Node] = udg_SKBO_Teleported[udg_SKBO_Parent[Node]]
    set udg_SKBO_Velocity[Node] = udg_SKBO_Velocity[udg_SKBO_Parent[Node]]
    set udg_SKBO_Accel[Node] = udg_SKBO_Accel[udg_SKBO_Parent[Node]]
    set udg_SKBO_Aoe[Node] = udg_SKBO_Aoe[udg_SKBO_Parent[Node]]
    set udg_SKBO_DTargetX[Node] = udg_SKBO_DTargetX[udg_SKBO_Parent[Node]]
    set udg_SKBO_DTargetY[Node] = udg_SKBO_DTargetY[udg_SKBO_Parent[Node]]
    
    //  Create the boulder
    set udg_SKBO_DummyUnit[Node] = CreateUnit(SKBO_DummyOwner(), SKBO_Dummy1ID(), x, y, angle)    
  
    call SetUnitScale(udg_SKBO_DummyUnit[Node], SKBO_Dummy1Size(), SKBO_Dummy1Size(), SKBO_Dummy1Size())
    call SetUnitFlyHeight(udg_SKBO_DummyUnit[Node], SKBO_Dummy1Height(), 0)
    
    //  Create the spawn effect 
    set udg_SKBO_DummyUnit2[Node] = CreateUnit(SKBO_DummyOwner(), SKBO_Dummy2ID(), x, y, angle)                    
    call SetUnitScale(udg_SKBO_DummyUnit2[Node], SKBO_Dummy2Size(), SKBO_Dummy2Size(), SKBO_Dummy2Size())
    call SetUnitFlyHeight(udg_SKBO_DummyUnit2[Node], SKBO_Dummy2Height(), 0)
    
    set u = CreateUnit(SKBO_DummyOwner(), SKBO_Dummy4ID(), x, y, angle)
    call SetUnitScale(u, SKBO_Dummy4Size(), SKBO_Dummy4Size(), SKBO_Dummy4Size())
    call SetUnitFlyHeight(u, SKBO_Dummy4Height(), 0)
    call KillUnit(u)
    set u = null
    
    //  Create the teleported effect 
    set udg_SKBO_DummyUnit3[Node] = CreateUnit(SKBO_DummyOwner(), SKBO_Dummy3ID(), udg_SKBO_DTargetX[Node], udg_SKBO_DTargetY[Node], angle)                    
    call SetUnitScale(udg_SKBO_DummyUnit3[Node], SKBO_Dummy3Size(), SKBO_Dummy3Size(), SKBO_Dummy3Size())
    call SetUnitFlyHeight(udg_SKBO_DummyUnit3[Node], SKBO_Dummy3Height(), 0)
    
endfunction

//---------------------------------------------------------------------------
//  Main Function
//---------------------------------------------------------------------------
function SKBO_Main takes nothing returns boolean
    //  Declare locals
    local integer Node = 0
    local integer loopInt = 0
    local integer iLevel
    local integer index = 0
    local real rLevel
    local real x1
    local real y1
    local real x2
    local real y2
    local real x3
    local real y3
    local real angle 
    local unit u
    
    if (GetSpellAbilityId() == SKBO_AbilityID()) then        
        //  set up node
        set Node = SKBO_CreateNode()
        
        //  Mark this instance as a parent
        set udg_SKBO_Parent[Node] = 0
        
        //  Assign data to locals
        set u = GetTriggerUnit()
        set iLevel = GetUnitAbilityLevel(u, SKBO_AbilityID())
        set rLevel = I2R(iLevel)
        set x1 = GetUnitX(u)
        set y1 = GetUnitY(u)
        set x2 = GetSpellTargetX()
        set y2 = GetSpellTargetY()
        set angle = bj_RADTODEG * Atan2(y2-y1, x2-x1)        
        
        //  Assign data to globals
        set udg_SKBO_Caster[Node] = u
        set udg_SKBO_Player[Node] = GetTriggerPlayer()
        set udg_SKBO_Dmg[Node] = SKBO_BaseDmg() + (SKBO_DmgPerLvl() * rLevel)
        set udg_SKBO_Aoe[Node] = SKBO_BaseAoe() + (SKBO_AoePerLvl() * rLevel)
        set udg_SKBO_Velocity[Node] = SKBO_BaseSpeed() + (SKBO_SpeedPerLvl() * rLevel)
    set udg_SKBO_Accel[Node] = SKBO_BaseAccel() + (SKBO_AccelPerLvl() * rLevel)
        set udg_SKBO_Number[Node] = SKBO_BaseNumber() + (SKBO_NumberPerLvl() * iLevel)
        set udg_SKBO_CurrentHeight[Node] = SKBO_Dummy1Height()
        set udg_SKBO_Stage[Node] = 1        
        set udg_SKBO_Teleported[Node] = false
        set udg_SKBO_STargetX[Node] = x2
        set udg_SKBO_STargetY[Node] = y2
        
        //  Start timer if this is the only instance
        if (udg_SKBO_NodePrev[Node] == 0) then
            call TimerStart(udg_SKBO_Timer, SKBO_Interval(), true, function SKBO_Loop)
        endif
                
        if (udg_SKBO_Number[Node] > 1) then
            loop
                set loopInt = loopInt + 1
                exitwhen loopInt > udg_SKBO_Number[Node]            
                
                set angle = I2R(loopInt) * (360.0 / udg_SKBO_Number[Node])
                
                //  Set up the location of boulder spawn
                set x3 = x1 + udg_SKBO_Aoe[Node] / 2 * Cos(angle * bj_DEGTORAD)
                set y3 = y1 + udg_SKBO_Aoe[Node] / 2 * Sin(angle * bj_DEGTORAD)           
                                  
                //  Set up the location where the boulder will teleport
                set udg_SKBO_DTargetX[Node] = x2 + udg_SKBO_Aoe[Node] / 2 * Cos(angle * bj_DEGTORAD)
                set udg_SKBO_DTargetY[Node] = y2 + udg_SKBO_Aoe[Node] / 2 * Sin(angle * bj_DEGTORAD)
                
                //  Create the boulder
                call SKBO_CreateBoulder(Node, x3, y3, angle)   
            endloop
        else
            //  Set up the location of boulder spawn
            set x3 = x1 + udg_SKBO_Aoe[Node] / 2 * Cos(angle * bj_DEGTORAD)
            set y3 = y1 + udg_SKBO_Aoe[Node] / 2 * Sin(angle * bj_DEGTORAD)    
            
            //  Set up the location where the boulder will teleport
            set udg_SKBO_DTargetX[Node] = x2
            set udg_SKBO_DTargetY[Node] = y2
            
            //  Create the boulder
            call SKBO_CreateBoulder(Node, x3, y3, angle)
        endif
        
        set u = null  
    endif
    
    return false
endfunction

//===========================================================================
function InitTrig_Skyfall_Boulders takes nothing returns nothing
    local trigger SKBO_Trig = CreateTrigger()
    local integer index = 0
    local real x = 0
    local real y = 0
    
    //  Register event
    loop
        call TriggerRegisterPlayerUnitEvent(SKBO_Trig, Player(index), EVENT_PLAYER_UNIT_SPELL_EFFECT, null)

        set index = index + 1
        exitwhen index == bj_MAX_PLAYER_SLOTS
    endloop
    
    //  Register Main Function
    call TriggerAddCondition(SKBO_Trig, Condition(function SKBO_Main))
    
    //  Create Dummy Caster
    set udg_SKBO_DummyCaster = CreateUnit(SKBO_DummyOwner(), SKBO_DummyCasterID(), x, y, 0)
    call UnitAddAbility(udg_SKBO_DummyCaster, SKBO_StunAbilityID())
    call UnitAddAbility(udg_SKBO_DummyCaster, SKBO_HarvestAbilityID())
endfunction


v1.0
  • First Public Release
Previews
Contents

Skyfall Boulders v1.0 (Map)

Reviews
Antares
Cool looking, creative spell! I am setting the base speed to 0 so it really looks like they're free-falling. Not a big fan of how the config is set up. I can't imagine there are a lot of people modding for 1.26 who aren't either using GUI or JNGP...
First of all, Vanilla JASS 🫠.

Knockback thing is fixed now, very nice!

Apart from that, what strikes me the most is that the entire flavor of your spell is that you have these portals to increase the falling distance and therefore impact speed of the boulders, but the boulders do not accelerate during their fall. This would be an easy thing to add by increasing the falling speed at each step and I think would make the spell look cooler.

There's the BJ function TriggerRegisterAnyUnitEventBJ, which does exactly what you're doing.

Memory leaks seem to be cleaned up, no problem there. The code structure looks very GUI like, but it works. MUI works without problems.
 

Rheiko

Spell Reviewer
Level 25
Joined
Aug 27, 2013
Messages
4,122
There's the BJ function TriggerRegisterAnyUnitEventBJ, which does exactly what you're doing.
BJs are bad! I mean they're the same but at least I saved one function call. (which is not much!)

First of all, Vanilla JASS 🫠.
I love vanilla.

Apart from that, what strikes me the most is that the entire flavor of your spell is that you have these portals to increase the falling distance and therefore impact speed of the boulders, but the boulders do not accelerate during their fall. This would be an easy thing to add by increasing the falling speed at each step and I think would make the spell look cooler.
Edit: Added Acceleration!
 
Last edited:
Level 13
Joined
Mar 29, 2012
Messages
542
Hey, @Ofel
You might want to check this out. I'm sorry if it's nothing fancy, though!

This is my very first JASS spell released into public, hopefully it's not that bad!
Any feedbacks and criticisms are welcomed!
Love to see you're a jazz player now, pure vanilla. :)

Didn't do thorough reading, just quick one.
If you intend to avoid using BJs, you can also replace those
JASS:
bj_MAX_PLAYER_SLOTS
,
JASS:
bj_DEGTORAD
, etc.
by putting it into constants, like you did with the trigger constant.

I love the idea. It made both the target area and the caster looks cool. It's simple and to me, the effects are fair. It brings me the Warcraft / DotA vibes, though the explosion looks (kinda) a little bigger I suppose.

5/5
 
Level 18
Joined
Oct 17, 2012
Messages
849
Love to see you're a jazz player now, pure vanilla. :)

Didn't do thorough reading, just quick one.
If you intend to avoid using BJs, you can also replace those
JASS:
bj_MAX_PLAYER_SLOTS
,
JASS:
bj_DEGTORAD
, etc.
by putting it into constants, like you did with the trigger constant.

I love the idea. It made both the target area and the caster looks cool. It's simple and to me, the effects are fair. It brings me the Warcraft / DotA vibes, though the explosion looks (kinda) a little bigger I suppose.

5/5
Not all BJs are meant to be avoided. Not to mention, these are just variables, not function calls. Besides, these are already constants themselves.


Regarding the spell itself, some of the constant functions could use a level argument instead for extended possibilities, so that the Hiver could do the following:
JASS:
constant function SKBO_DmgPerLvl takes integer level returns real
    if level == 1 then
        return 80.0
    elseif level == 2 then
        return 100 + someVariable*90
    endif
endfunction
If the Hiver wanted a constant, the following will get the job done:
JASS:
constant function SKBO_DmgPerLvl takes integer level returns real
    return level*75.0
endfunction
 

Rheiko

Spell Reviewer
Level 25
Joined
Aug 27, 2013
Messages
4,122
Didn't do thorough reading, just quick one.
If you intend to avoid using BJs, you can also replace those
As Elprede said. I'll keep those.

I love the idea. It made both the target area and the caster looks cool. It's simple and to me, the effects are fair. It brings me the Warcraft / DotA vibes, though the explosion looks (kinda) a little bigger I suppose.

5/5
Thanks! Glad you like it. I was hoping to find a dirt explosion that isn't too big but impactful enough. Unfortunately, I couldn't find one that could satisfy me other than WILL's dirt explosion. I love his explosion effects. <3

Regarding the spell itself, some of the constant functions could use a level argument instead for extended possibilities, so that the Hiver could do the following:
That's a lovely suggestion, thank you!
 
Cool looking, creative spell! I am setting the base speed to 0 so it really looks like they're free-falling.

Not a big fan of how the config is set up. I can't imagine there are a lot of people modding for 1.26 who aren't either using GUI or JNGP. And I think even if I were a JASS user, I would prefer the config to be in simple GUI if this is the alternative. I think the better way (if it has to be in JASS) would be to have one function that sets a bunch of udg_ variables.

Nonetheless,

Approved
 
Top