• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

Bone Blade v1.6


Description
Launches a bone blade at targeted enemy unit. The bone blade will attack the target several times. Each successful landed attack deals pure damage.​


Requirements
None​


How to import
1. Import all models and icons at Import Manager to your map
2. Copy dummy unit and spell to your map
3. Copy "Spell" trigger folder to your map​


Code
JASS:
    // **************************************************************************** //
    //                                                                              //
    //                              Bone Blade                                      //
    //                                      v1.6                                    //
    //                                 by: Dalvengyr                                //
    //                                                                              //
    // Description                                                                  //
    //      Launches a bone blade at targeted enemy unit. The bone blade will       //
    //      attack the target several times. Each successful landed attack deals    //
    //      pure damage.                                                            //
    //                                                                              //
    // Requirements                                                                 //
    //      -                                                                       //
    //                                                                              //
    // How to import                                                                //
    //      1. Import all models and icons at Import Manager to your map            //
    //      2. Copy both dummy unit and spell to your map                           //
    //      3. Copy this folder to your map                                         //
    //      4. Delete the variable creator trigger                                  //
    //                                                                              //
    // Credits                                                                      //
    //      1. BTNBonespear by Peekay                                               //
    //      2. BoneSpear.mdx by Peekay                                              //
    //      3. dummy.mdx by Vexorian                                                //
    //                                                                              //
    // Link                                                                         //
    //      hiveworkshop.com/forums/spells-569/bone-blade-v1-0-a-255874/            //
    //                                                                              //
    // **************************************************************************** //

    // **************************************************************************** //
    //                                                                              //
    //                              CONFIGURATION                                   //
    //                                                                              //
    
    // 1. Dummy unit's raw code at Object Editor                                    //
    constant function BB__DummyID takes nothing returns integer
        return 'h000'
    endfunction
    
    // 2. Main spell raw code at Object Editor                                      //
    constant function BB__SpellID takes nothing returns integer
        return 'A000'
    endfunction
    
    // 3. Accuracy of the spell. Just leave it                                      //
    constant function BB__Accuracy takes nothing returns real
        return 0.0312500
    endfunction
    
    // 4. File path of the missile                                                  //
    constant function BB__MissileSfx takes nothing returns string
        return "war3mapImported\\PeeKay's BoneSpear.mdx"
    endfunction
    
    // 5. Attached sfx upon hit                                                     //
    constant function BB__HitSfx takes nothing returns string
        return "Abilities\\Weapons\\HydraliskImpact\\HydraliskImpact.mdl"
    endfunction
    
    // 6. Attachment point for hit sfx                                              //
    constant function BB__HitSfxPt takes nothing returns string
        return "origin"
    endfunction
    
    // 7. Normal height Z for missiles                                              //
    constant function BB__MissileZ takes nothing returns real
        return 40.0
    endfunction
    
    // 8. Higher toleration makes the missile become less precise                   //
    // Toleration is additional angle to tolerate the missiles when they are aiming //
    // for their target. So they can start dashing earlier even before they are     //
    // successfully facing at the target                                            //
    constant function BB__Toleration takes nothing returns real
        return 0.0
    endfunction
    
    // 9. Hit radius of each missile. Maximum radius for missiles to hit/attack     //
    // their targets                                                                //
    constant function BB__HitRadius takes nothing returns real
        return 100.0
    endfunction
    
    // 10. Turn rate of missiles when they are dashing at target (at high speed)    //
    constant function BB__MissileTurnRateMin takes integer level returns real
        return 1.0
    endfunction
    
    // 11. Turn rate of missiles when they are turning around (at low speed)        //
    // Logically, this one should have higher value than the other one (minimum)    //
    constant function BB__MissileTurnRateMax takes integer level returns real
        return 4.0
    endfunction
    
    // 12. Speed of missiles when they are turning (aiming) for the target          //
    constant function BB__MissileSpeedMin takes integer level returns real
        return 10.0
    endfunction
    
    // 13. Max speed of missiles when they are dashing at their targets             //
    constant function BB__MissileSpeedMax takes integer level returns real
        return 50.0
    endfunction
    
    // 14. Acceleration rate after missiles are turning (aiming)                    //
    constant function BB__MissileAcceleration takes integer level returns real
        return 2.5
    endfunction
    
    // 15. Deacceleration (braking) rate after missiles hit/miss their target       //
    constant function BB__MissileDeacceleration takes integer level returns real
        return 1.5
    endfunction
    
    // 16. Distance extension when missile starts to brake                          //
    constant function BB__MissileBrakePoint takes integer level returns real
        return 200.0
    endfunction
    
    // 17. Dealt damage on hit                                                      //
    constant function BB__DealtDamage takes integer level returns real
        return 75.0
    endfunction
    
    // 18. Lifespan duration for each missile                                       //
    constant function BB__Duration takes integer level returns real
        return 18.0
    endfunction
    
    // 19. Maximum number of attacks before missiles are expired                    //
    constant function BB__AttackCount takes integer level returns integer
        return 1 + level
    endfunction
    
    // 20. Damage type of dealt damage                                              //
    constant function BB__DamageType takes nothing returns damagetype
        return DAMAGE_TYPE_NORMAL
    endfunction
    
    // 21. Attack type of dealt damage                                              //
    constant function BB__AttackType takes nothing returns attacktype
        return ATTACK_TYPE_CHAOS
    endfunction
    
    // 22. Weapon type of dealt damage                                              //
    constant function BB__WeaponType takes nothing returns weapontype
        return WEAPON_TYPE_WHOKNOWS
    endfunction
    
    // 23. Decay time for missiles when death                                       //
    constant function BB__DecayTime takes nothing returns real
        return 5.0
    endfunction
    
    // 24. Additional effect attached to the target on hit                          //
    function BB__AdditionalEffect takes unit target returns nothing
        // Put here //
    endfunction
    
    //                                                                              //
    //                              END OF CONFIGURATION                            //
    //                                                                              //
    // **************************************************************************** //
    
    // Furthermore, edit them by your own risk!
    
    // Function to get distance between given coordinates
    function BB__GetDistance takes real x, real y, real xt, real yt returns real
        return SquareRoot((xt - x) * (xt - x) + (yt - y) * (yt - y))
    endfunction
    
    // Function to return facing angle between coordinates (in radian)
    function BB__GetAngle takes real x, real y, real xt, real yt returns real
        return Atan2(yt - y, xt - x)
    endfunction
    
    // Function to check whether a unit is alive or not
    function IsUnitAlive takes unit id returns boolean
        return not IsUnitType(id, UNIT_TYPE_DEAD) and GetUnitTypeId(id) != 0
    endfunction
    
    function BB__Deindex takes integer i returns nothing
    
        set udg_BB__Caster[i] = udg_BB__Caster[udg_BB__Total]
        set udg_BB__Player[i] = udg_BB__Player[udg_BB__Total]
        set udg_BB__Target[i] = udg_BB__Target[udg_BB__Total]
        set udg_BB__Angle[i] = udg_BB__Angle[udg_BB__Total]
        set udg_BB__Missile[i] = udg_BB__Missile[udg_BB__Total]
        set udg_BB__Sfx[i] = udg_BB__Sfx[udg_BB__Total]
        set udg_BB__Dx[i] = udg_BB__Dx[udg_BB__Total]
        set udg_BB__Dc[i] = udg_BB__Dc[udg_BB__Total]
        set udg_BB__State[i] = udg_BB__State[udg_BB__Total]
        set udg_BB__SpeedMin[i] = udg_BB__SpeedMin[udg_BB__Total]
        set udg_BB__SpeedMax[i] = udg_BB__SpeedMax[udg_BB__Total]
        set udg_BB__Acceleration[i] = udg_BB__Acceleration[udg_BB__Total]
        set udg_BB__Deacceleration[i] = udg_BB__Deacceleration[udg_BB__Total]
        set udg_BB__BrakePoint[i] = udg_BB__BrakePoint[udg_BB__Total]
        set udg_BB__TurnRateMax[i] = udg_BB__TurnRateMax[udg_BB__Total]
        set udg_BB__TurnRateMin[i] = udg_BB__TurnRateMin[udg_BB__Total]
        set udg_BB__Speed[i] = udg_BB__Speed[udg_BB__Total]
        set udg_BB__Damage[i] = udg_BB__Damage[udg_BB__Total]
        set udg_BB__Count[i] = udg_BB__Count[udg_BB__Total]
        set udg_BB__Duration[i] = udg_BB__Duration[udg_BB__Total]
        
    endfunction

    function BB__onPeriodic takes nothing returns nothing
        
        local integer i = 1
        local real a
        local real x
        local real y
        local real x2
        local real y2
        local unit f
        
        loop
            exitwhen i > udg_BB__Total

            set x  = GetUnitX(udg_BB__Missile[i])
            set y  = GetUnitY(udg_BB__Missile[i])
            set x2 = GetUnitX(udg_BB__Target[i])
            set y2 = GetUnitY(udg_BB__Target[i])
            set a  = BB__GetAngle(x, y, x2, y2)
            set udg_BB__Duration[i] = udg_BB__Duration[i] - BB__Accuracy()
            
            if udg_BB__State[i] then
                if udg_BB__Count[i] > 0 then
                    // Detect the target as long as missile is still in dashing state
                    call GroupEnumUnitsInRange(udg_BB__Group, x, y, BB__HitRadius(), null)
                    loop
                        set f = FirstOfGroup(udg_BB__Group)
                        exitwhen f == null
                        if f == udg_BB__Target[i] and IsUnitAlive(udg_BB__Target[i]) then
                            set udg_BB__Count[i] = udg_BB__Count[i] - 1
                            call UnitDamageTarget(udg_BB__Caster[i], udg_BB__Target[i], udg_BB__Damage[i], false, false, BB__AttackType(), BB__DamageType(), BB__WeaponType())
                            call DestroyEffect(AddSpecialEffectTarget(BB__HitSfx(), udg_BB__Target[i], BB__HitSfxPt()))
                            call BB__AdditionalEffect(udg_BB__Target[i])
                            if udg_BB__Count[i] > 0 then
                                set udg_BB__State[i] = false
                            endif
                        endif
                        call GroupRemoveUnit(udg_BB__Group, f)
                    endloop
                else
                    set udg_BB__Duration[i] = 0.0
                endif
                
                if udg_BB__Dc[i] < udg_BB__Dx[i] then
                
                    set udg_BB__Speed[i] = udg_BB__Speed[i] + udg_BB__Acceleration[i]
                    set udg_BB__Dc[i] = udg_BB__Dc[i] + udg_BB__Speed[i]
                    if udg_BB__Speed[i] > udg_BB__SpeedMax[i] then
                        set udg_BB__Speed[i] = udg_BB__SpeedMax[i]
                    endif
                    
                    // Adjust missile's facing
                    if udg_BB__TurnRateMin[i] > 0 and Cos(udg_BB__Angle[i]-a) < Cos(udg_BB__TurnRateMin[i]) then
                        if Sin(a-udg_BB__Angle[i]) >= 0 then
                            set udg_BB__Angle[i] = udg_BB__Angle[i] + udg_BB__TurnRateMin[i]
                        else
                            set udg_BB__Angle[i] = udg_BB__Angle[i] - udg_BB__TurnRateMin[i]
                        endif
                    else
                        set udg_BB__Angle[i] = a
                    endif
                else
                    set udg_BB__State[i] = false
                endif
            else
                if udg_BB__Speed[i] > udg_BB__SpeedMin[i] then
                    set udg_BB__Speed[i] = udg_BB__Speed[i] - udg_BB__Deacceleration[i]
                    if udg_BB__Speed[i] < udg_BB__SpeedMin[i] then
                        set udg_BB__Speed[i] = udg_BB__SpeedMin[i]
                    endif
                else
                    if udg_BB__TurnRateMax[i] > 0 and Cos(udg_BB__Angle[i]-a) < Cos(udg_BB__TurnRateMax[i]) then
                        if Sin(a-udg_BB__Angle[i]) >= 0 then
                            set udg_BB__Angle[i] = udg_BB__Angle[i] + udg_BB__TurnRateMax[i]
                        else
                            set udg_BB__Angle[i] = udg_BB__Angle[i] - udg_BB__TurnRateMax[i]
                        endif
                    else
                        set udg_BB__Angle[i] = a
                        set udg_BB__Dc[i] = 0.0
                        set udg_BB__Dx[i] = BB__GetDistance(x, y, x2, y2) + udg_BB__BrakePoint[i]
                        set udg_BB__State[i] = true
                    endif
                endif
            endif
            
            set x = x + udg_BB__Speed[i] * Cos(udg_BB__Angle[i])
            set y = y + udg_BB__Speed[i] * Sin(udg_BB__Angle[i])
            call SetUnitFacing(udg_BB__Missile[i], udg_BB__Angle[i] * bj_RADTODEG)
            
            if x < udg_BB__BoundMaxX and x > udg_BB__BoundMinX and y < udg_BB__BoundMaxY and y > udg_BB__BoundMinY then
                call SetUnitX(udg_BB__Missile[i], x)
                call SetUnitY(udg_BB__Missile[i], y)
            else
                // Kill the missile if not in map boudaries
                set udg_BB__Duration[i] = 0.0
            endif
            
            if udg_BB__Duration[i] <= 0.0 then
                call DestroyEffect(udg_BB__Sfx[i])
                call UnitApplyTimedLife(udg_BB__Missile[i], 'BTLF', BB__DecayTime())
                
                if i != udg_BB__Total then
                    call BB__Deindex(i)
                endif
                
                set udg_BB__Total = udg_BB__Total - 1
                if udg_BB__Total == 0 then
                    call PauseTimer(udg_BB__Timer)
                else
                    set i = i - 1
                endif
            endif
            set i = i + 1
        endloop
        
    endfunction

    function BB__onCast takes nothing returns boolean
    
        local real x
        local real y
        local integer l
        
        if GetSpellAbilityId() == BB__SpellID() then
        
            set udg_BB__Total = udg_BB__Total + 1
            set udg_BB__Caster[udg_BB__Total] = GetTriggerUnit()
            set udg_BB__Player[udg_BB__Total] = GetTriggerPlayer()
            
            set x = GetUnitX(udg_BB__Caster[udg_BB__Total])
            set y = GetUnitY(udg_BB__Caster[udg_BB__Total])
            set l = GetUnitAbilityLevel(udg_BB__Caster[udg_BB__Total], BB__SpellID())
            
            set udg_BB__Target[udg_BB__Total] = GetSpellTargetUnit()
            set udg_BB__Angle[udg_BB__Total] = GetUnitFacing(udg_BB__Caster[udg_BB__Total]) * bj_DEGTORAD
            set udg_BB__Missile[udg_BB__Total] = CreateUnit(udg_BB__Player[udg_BB__Total], BB__DummyID(), x, y, udg_BB__Angle[udg_BB__Total] * bj_RADTODEG)
            set udg_BB__Sfx[udg_BB__Total] = AddSpecialEffectTarget(BB__MissileSfx(), udg_BB__Missile[udg_BB__Total], "origin")
            set udg_BB__Dx[udg_BB__Total] = 0.0
            set udg_BB__Dc[udg_BB__Total] = 0.0
            
            set udg_BB__State[udg_BB__Total] = false
            set udg_BB__SpeedMin[udg_BB__Total] = BB__MissileSpeedMin(l)
            set udg_BB__SpeedMax[udg_BB__Total] = BB__MissileSpeedMax(l)
            set udg_BB__Acceleration[udg_BB__Total] = BB__MissileAcceleration(l)
            set udg_BB__Deacceleration[udg_BB__Total] = BB__MissileDeacceleration(l)
            set udg_BB__BrakePoint[udg_BB__Total] = BB__MissileBrakePoint(l)
            
            set udg_BB__TurnRateMax[udg_BB__Total] = BB__MissileTurnRateMax(l) * bj_DEGTORAD
            set udg_BB__TurnRateMin[udg_BB__Total] = BB__MissileTurnRateMin(l) * bj_DEGTORAD
            set udg_BB__Speed[udg_BB__Total] = udg_BB__SpeedMin[udg_BB__Total]
            set udg_BB__Damage[udg_BB__Total] = BB__DealtDamage(l)
            set udg_BB__Count[udg_BB__Total] = BB__AttackCount(l)
            set udg_BB__Duration[udg_BB__Total] = BB__Duration(l)
            
            if UnitAddAbility(udg_BB__Missile[udg_BB__Total], 'Amrf') and UnitRemoveAbility(udg_BB__Missile[udg_BB__Total], 'Amrf') then
            endif
            call SetUnitFlyHeight(udg_BB__Missile[udg_BB__Total], BB__MissileZ(), 0.0)
            
            if udg_BB__Total == 1 then
                call TimerStart(udg_BB__Timer, BB__Accuracy(), true, function BB__onPeriodic)
            endif
        endif
        
        return false
    endfunction

    function InitTrig_BoneBlade takes nothing returns nothing

        set gg_trg_BoneBlade = CreateTrigger()
        set udg_BB__Timer = CreateTimer()
        set udg_BB__Group = CreateGroup()
        set udg_BB__TAU = 2.0 * bj_PI
        
        // Set the map bound for later use
        set udg_BB__BoundMaxX = GetRectMaxX(bj_mapInitialPlayableArea)
        set udg_BB__BoundMinX = GetRectMinX(bj_mapInitialPlayableArea)
        set udg_BB__BoundMaxY = GetRectMaxY(bj_mapInitialPlayableArea)
        set udg_BB__BoundMinY = GetRectMinY(bj_mapInitialPlayableArea)
        
        call TriggerRegisterAnyUnitEventBJ(gg_trg_BoneBlade, EVENT_PLAYER_UNIT_SPELL_EFFECT)
        call TriggerAddCondition(gg_trg_BoneBlade, Condition(function BB__onCast))
        
    endfunction

Credits
1. BTNBonespear by Peekay
2. BoneSpear.mdx by Peekay
3. dummy.mdx by Vexorian​


Keywords:
bone, blade, homing, missile
Contents

Bone Blade (Map)

Reviews
21:19, 11th Aug 2014 BPower: The code looks good to me. Approved and recommended.

Moderator

M

Moderator

21:19, 11th Aug 2014
BPower:
The code looks good to me. Approved and recommended.
 

Bannar

Code Reviewer
Level 26
Joined
Mar 19, 2008
Messages
3,140
I'm impressed. You're getting better and better. Documentation is almost flawness, no unnecessary code-bloat.
The readability is almost perfect too - you could enter some breaks between continous "if statements" and such.

JASS:
if UnitAddAbility(udg_BB_Missile[udg_BB_Total], 'Amrf') and UnitRemoveAbility(udg_BB_Missile[udg_BB_Total], 'Amrf') then
endif
You could check for autofly - or not, it's just a suggestion.

As suggested, use global group instead - GroupEnum clears the group prior enumeration and you don't need additional overhead.
You could also get rid of SquareRoot - just use multiplied values directly.
 

Deleted member 238589

D

Deleted member 238589

Awesome, keep it up. 4/5 from me
 
Top