Blade Barrier v1.04a

This bundle is marked as useful / simple. Simplicity is bliss, low effort and/or may contain minor bugs.
  • Like
Reactions: Jaakko
Blade Barrier by BlackRose (aka Siraraz)
v1.04a - 04/07/2010

Description:
Creates a barrier of blades around the caster, dealing damage to enemy units nearby.​

  • MUI
  • Coded in vJass

Requires:

Ability code:
JASS:
library BladeBarrier initializer onInit requires GT, KT, AIDS, SimError, Table
//+----------------------------------------------------------------------------------+
//|                                     v1.04a - 30/06/2010 Saturday
//|     Blade Barrier by BlackRose
//|
//+----------------------------------------------------------------------------------+
//|
//| Description: Creates a barrier of blades around the caster unit, damaging enemy 
//|              units that get in the way. 
//|
//| Requires: - JassHelper [latest - [url]http://www.wc3c.net/showthread.php?t=88142][/url]
//|           - GTrigger    by Jesus4Lyf
//|           - KeyTimer2   by Jesus4Lyf
//|           - AIDS        by Jesus4Lyf
//|           - SimError    by Vexorian
//|           - Table       by Vexorian
//| 
//| Usage: call BladeBarrier_Create( unit source, unit target, integer level )
//|
//| How to import:
//| -------------
//| 1) Copy this trigger, and all requirements listed above into your map. 
//|    (You can copy the folder)
//| 2) Go to AIDS system, and follow it's implementation steps.
//| 3) Copy any object with Blade Barrier in it.
//| 4) Import the custom pitchModel into your map.
//| 5) Update all rawcodes.
//|
//+----------------------------------------------------------------------------------+
//|
//| - All credit to Tossrock, creator or Pudge Wars Advanced for the spell concept.
//|
//+----------------------------------------------------------------------------------+

//+-----------------------------------------------------------------------------------------------------------------------------------+\\
//+                                                     CONFIGURABLES SECTION
//+-----------------------------------------------------------------------------------------------------------------------------------+\\
    globals
        private constant integer ABILITY_ID = 'A004'   // "Blade Barrier" ability ID. This causes the response.
        private constant integer DUMMY_ID   = 'h001'   // Rawcode of the required dummy unit. Must used the PitchDummy.MDX model.
                
        private constant real    TIMER_PERIOD  = 0.03  // Movement of Blades done every TIMER_PERIOD.
        private constant real    DAMAGE_PERIOD = 0.10  // Units are damaged every DAMAGE_PERIOD.
        
        private constant boolean ALLOW_MULTI_INSTANCE = false // If enabled, more than one Blade Barrier's can be made per unit.
        private constant string  ERROR_MSG            = "You can only have one Blade Barrier at once."
        
        //=====================================================
        
        private constant attacktype ATTACK_TYPE      = ATTACK_TYPE_CHAOS
        private constant damagetype DAMAGE_TYPE      = DAMAGE_TYPE_NORMAL
        private constant weapontype WEAPON_TYPE      = null
        private constant real       COLLISION_SIZE   = 32    // Collision size of units... well Pudge. 
        
        private constant real       BLADE_SPINSPEED  = 8.00
        private constant real       BLADE_ANGLESPEED = 0.12
        
        //=====================================================

        // Where damage effects are made.
        private constant boolean DO_DMG_SFX = true
        private constant string  DMG_SFX    = "Abilities\\Spells\\Other\\Stampede\\StampedeMissileDeath.mdl"
        private constant string  DMG_LOC    = "chest"
        
        // When a unit is killed by Blade Barrier, create these effects.
        private constant boolean DO_DEATH_SFX = false
        private constant string  DEATH_SFX    = ""
        private constant string  DEATH_LOC    = ""
        
        //=====================================================
        // Sounds:
        // --------
        // They are played every x ticks of the TIMER_PERIOD, which is 0.075 seconds. One of the sounds below is chosen to play.
        // Note: Some sounds cannot be played rapidly.
        
        private constant real    SOUND_EVERY  = 0.09  // It has to be a multiple of TIMER_PERIOD.
        private constant integer SOUND_VOLUME = 50    // The volume of the created sounds. Max is 127.
        
        private constant string  SOUND_ONE   = "Sound\\Units\\Combat\\MetalLightSliceMetal1.wav"
        private constant string  SOUND_TWO   = "Sound\\Units\\Combat\\MetalLightSliceMetal2.wav"
        private constant string  SOUND_THREE = "Sound\\Units\\Combat\\MetalLightSliceMetal3.wav"
        
        private constant string  SOUND_PARAM = "MetalLightSliceMetal"
        private constant string  SOUND_EAX   = "CombatSoundsEAX"
        
        private constant integer SOUND_DUR_1 = 376
        private constant integer SOUND_DUR_2 = 480
        private constant integer SOUND_DUR_3 = 461
        
        //=====================================================
        // Models:
        // -------
        // What shall your Blade Barrier look like?
        private constant boolean FADE       = true // Or else its death animation will be played.
        private constant real RANGE_OFFSET  = 20   // Blade will be created at RADIUS (function) + GetRandomReal( -20, 20 )
        
        // The unit is not created with this colour for effiency.
        // You must make sure these match the Object Editor's values unless you want an odd result.
        private constant integer RED        = 255
        private constant integer GREEN      = 255
        private constant integer BLUE       = 255
        
        private constant real    HEIGHT     = 60.00
        
        private constant string MODEL_ONE   = "Abilities\\Weapons\\BloodElfSpellThiefMISSILE\\BloodElfSpellThiefMISSILE.mdl"
        private constant string MODEL_TWO   = "Abilities\\Weapons\\BloodElfSpellThiefMISSILE\\BloodElfSpellThiefMISSILE.mdl"
        private constant string MODEL_THREE = "Abilities\\Weapons\\BloodElfSpellThiefMISSILE\\BloodElfSpellThiefMISSILE.mdl"
        
        // Should the death animation be that of the original Pudge Wars?
        private constant boolean ORIGINAL_DEATH_ANIM = false 
    endglobals
 
    //+------------------------+
    //|   Don't touch below.   |
    //+------------------------+
    private keyword BladeBarrier
    
    globals
        private constant integer INLINED_VOLUME = R2I(SOUND_VOLUME * 127.00 * 0.01)
        private BladeBarrier tempData = 0
    endglobals

    //===========================================================\\
    // Unit filter:
    // - Organic
    // - Living
    // - Enemy

    private function IsTargetValid takes unit whichUnit returns boolean
        return  IsUnitEnemy( whichUnit, tempData.owner )                    and /* Enemy
        */      IsUnitType( whichUnit, UNIT_TYPE_STRUCTURE ) == false       and /* Structure
        */      IsUnitType( whichUnit, UNIT_TYPE_DEAD ) == false            and /* Alive
                Don't touch this one.                                           
        */      IsUnitInGroup( whichUnit, tempData.damageGroup ) == false
    endfunction

    //===========================================================\\
    // Damage per second.

    private function DPS takes integer level returns real
        return level * 250.
    endfunction

    //===========================================================\\
    // Blade Barrier duration.

    private function DURATION takes integer level returns real
        return 20.00
    endfunction

    //===========================================================\\
    // Area of the spell. 
    private function RADIUS takes integer level returns real
        return 200.00
    endfunction
    
    //===========================================================\\
    // How many Blade effects are created?
    private function BLADES takes integer level returns integer
        return level * 20
    endfunction
    
//+-----------------------------------------------------------------------------------------------------------------------------------+\\
//+                                                        END CONFIGURABLES
//+                                                    BLADE BARRIER STARTS HERE.
//+-----------------------------------------------------------------------------------------------------------------------------------+\\
            
    native UnitAlive takes unit id returns boolean
            
    static if not ALLOW_MULTI_INSTANCE then             // Do not create the group if its true. Waste of variable.
        globals
            private group ACTIVE_GROUP = CreateGroup()  // Required group if you only want one barrier per unit.
        endglobals
    endif
    
    //----------------------------------------------------
    // BladeUnitData: Handles the blade units.
    //----------------------------------------------------
    private struct BladeUnitData extends array
        //! runtextmacro AIDS()
        unit    blade
        effect  fxpath
        integer pitch
        
        real pitchDir
        real rotationDir
        real angle
        real angleDir
        real radius
        
        static method AIDS_filter takes unit u returns boolean
            return GetUnitTypeId( u ) == DUMMY_ID
        endmethod
        
        method cleanup takes nothing returns nothing
            call DestroyEffect( this.fxpath )
            static if FADE then
                call RemoveUnit( this.blade )
            else
                call KillUnit( this.blade )
            endif
        endmethod
    endstruct
    
//------------------------------------------------------------------------------------------
//                                      DAMAGE SECTION
//------------------------------------------------------------------------------------------

    private struct DamageData
        BladeBarrier     originData // Used to keep track of the parent data.
        unit target     = null      // Assigned target.
        real dps        = 0.00      // Damage per Second.
        
        real radius_45  = 0.00
        real radius_80  = 0.00
        
        private static method periodic takes nothing returns boolean
            local thistype this = KT_GetData()
            local real tx    = GetUnitX( this.target )
            local real ty    = GetUnitY( this.target )
            local real tempX = this.originData.cx - tx
            local real tempY = this.originData.cy - ty
            
            if  this.originData.sec >= this.originData.dur                  or   /* Expired
            */  ( tempX*tempX+tempY*tempY ) > this.radius_45                or   /* Exit barrier radius
            */  not UnitAlive( this.originData.caster )                     or   /* Dead caster
            */  not UnitAlive( this.target )                                then // dead target 
            
                call GroupRemoveUnit( this.originData.damageGroup, this.target )
                call this.destroy()
                return true
            endif
            
            if ( tempX*tempX+tempY*tempY ) > this.radius_80 then
                static if DO_DMG_SFX then
                    call DestroyEffect( AddSpecialEffectTarget( DMG_SFX, this.target, DMG_LOC ) )
                endif
                call UnitDamageTarget( this.originData.source, this.target, this.dps, false, false, ATTACK_TYPE, DAMAGE_TYPE, WEAPON_TYPE )

                if not UnitAlive( this.target ) then
                    static if DO_DEATH_SFX then
                        call call DestroyEffect( AddSpecialEffectTarget( DEATH_SFX, this.target, DEATH_SFX_LOC ) )
                    endif
                    call this.destroy()
                    return true
                endif
            endif
            
            return false
        endmethod
        
        static method create takes unit u, BladeBarrier i returns thistype
            local thistype this = thistype.allocate()
            
            set this.originData = i
            set this.target     = u
            set this.dps        = DPS( i.level ) * DAMAGE_PERIOD // Level is stored, incase you leveled up the skill while the blade is active.
            
            set this.radius_45  = (i.radius+45)*(i.radius+45)
            set this.radius_80  = (i.radius-80)*(i.radius-80)
            
            call GroupAddUnit( i.damageGroup, u )
            call KT_Add( function thistype.periodic, this, DAMAGE_PERIOD )
            return this
        endmethod
    endstruct
                
    private struct BladeBarrier
        boolean early  = false  // Was the spell cancelled early?
    
        unit   source  = null
        player owner   = null
        unit   caster  = null

        integer level  = 0

        real   cx      = 0.00
        real   cy      = 0.00
        real   cz      = 0.00
        real   radius  = 0.00
        
        real ticks    = 0.00
        integer alpha = 255
        real sec      = 0.00
        real dur      = 0.00
        
        group bladeGroup  = null
        group damageGroup = null

        trigger t = null

        static HandleTable data  = 0

        // Creates yet another blade to add into the barrier.
        private method AddBlade takes nothing returns nothing
            local unit blade = CreateUnit( this.owner, DUMMY_ID, this.cx, this.cy, 0 )
            local BladeUnitData bdata = BladeUnitData[blade]
            local integer n = GetRandomInt( 0, 2 )
            
            // Add a bit of variety into the blades, aye?
            set bdata.rotationDir = GetRandomReal( 0.40, 1.60 )
            set bdata.pitchDir    = GetRandomReal( 0.40, 1.60 )
            set bdata.angleDir    = GetRandomReal( 0.40, 1.60 )
            
            if GetRandomInt( 0, 1 ) == 0 then
                set bdata.rotationDir = bdata.rotationDir * -1
                set bdata.angleDir    = bdata.angleDir    * -1
            else
                set bdata.pitchDir    = bdata.pitchDir    * -1
            endif

            set bdata.blade  = blade
            set bdata.pitch  = GetRandomInt( 0 ,180 )
            set bdata.radius = this.radius + GetRandomReal( -RANGE_OFFSET, RANGE_OFFSET )
            set bdata.angle  = GetRandomReal( -bj_PI, bj_PI )

            if n == 0 then
                set bdata.fxpath = AddSpecialEffectTarget( MODEL_ONE, blade, "origin" )
            elseif n == 1 then
                set bdata.fxpath = AddSpecialEffectTarget( MODEL_TWO, blade, "origin" )
            elseif n == 2 then
                set bdata.fxpath = AddSpecialEffectTarget( MODEL_THREE, blade, "origin" )
            endif
            call GroupAddUnit( this.bladeGroup, blade )
            
            set blade = null
        endmethod
        
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
                    
        private static method rotateBlade takes nothing returns nothing
            local unit bladeUnit  = GetEnumUnit()
            local BladeUnitData bdata = BladeUnitData[bladeUnit]
            local integer newPitch    = ModuloInteger( bdata.pitch + R2I( bdata.pitchDir * BLADE_SPINSPEED ), 180 )
            local real newFacing      = GetUnitFacing( bladeUnit ) + bdata.rotationDir   * BLADE_SPINSPEED
            local real x = tempData.cx + bdata.radius * Cos( bdata.angle )
            local real y = tempData.cy + bdata.radius * Sin( bdata.angle )

            call SetUnitAnimationByIndex( bladeUnit, newPitch )
            call SetUnitFacing( bladeUnit, newFacing )
            call SetUnitX( bladeUnit, x )
            call SetUnitY( bladeUnit, y )
            set bdata.angle = bdata.angle + bdata.angleDir * BLADE_ANGLESPEED
            
            set bladeUnit = null
        endmethod
        
        private method createBladeSound takes nothing returns nothing
            local integer r = GetRandomInt( 0, 2 )
            local sound   s = null

            if r == 0 then
                set s = CreateSound( SOUND_ONE, false, true, true, 10, 10, SOUND_EAX )
                call SetSoundParamsFromLabel( s, SOUND_PARAM )
                call SetSoundDuration( s, SOUND_DUR_1 )
            elseif r == 1 then
                set s = CreateSound( SOUND_TWO, false, true, true, 10, 10, SOUND_EAX )
                call SetSoundParamsFromLabel( s, SOUND_PARAM )
                call SetSoundDuration( s, SOUND_DUR_2 )
            elseif r == 2 then
                set s = CreateSound( SOUND_THREE, false, true, true, 10, 10, SOUND_EAX )
                call SetSoundParamsFromLabel( s, SOUND_PARAM )
                call SetSoundDuration( s, SOUND_DUR_3 )
            endif

            call SetSoundVolume( s, INLINED_VOLUME )
            call SetSoundPosition( s, this.cx, this.cy, GetUnitFlyHeight( this.caster ) )
            call StartSound( s )
            call KillSoundWhenDone( s )
            
            set this.ticks = 0.00
            set s = null
        endmethod
        
        private static method adjustHeight takes nothing returns nothing
            call SetUnitFlyHeight( GetEnumUnit(), tempData.cz + HEIGHT, 0.00 )
        endmethod

        private static method periodic takes nothing returns boolean
            local thistype this = KT_GetData()
            local real     z    = GetUnitFlyHeight( this.caster )
            
            set this.cx = GetUnitX( this.caster )
            set this.cy = GetUnitY( this.caster )
            
            set tempData = this
            call ForGroup( this.bladeGroup, function thistype.rotateBlade )
            
            if z != this.cz then
                set this.cz = z
                call ForGroup( this.bladeGroup, function thistype.adjustHeight )
            endif
            
            set this.ticks = this.ticks + TIMER_PERIOD
            if ModuloReal( this.ticks, SOUND_EVERY ) == 0.00 then
                call this.createBladeSound()
            endif
            
            set this.sec = this.sec + TIMER_PERIOD
            if this.sec >= this.dur or not UnitAlive( this.caster ) then
                static if not ALLOW_MULTI_INSTANCE then 
                    call GroupRemoveUnit( ACTIVE_GROUP, this.caster )
                endif
                call thistype.data.flush( this.t )
                call DestroyTrigger( this.t )
                
                static if ORIGINAL_DEATH_ANIM then
                    set this.sec  = 15.00
                endif
                set this.ticks    = 0

                static if FADE then
                    call KT_Add( function thistype.release, this, TIMER_PERIOD )
                else
                    call this.destroy()
                endif
                
                return true
            endif
            return false
        endmethod
        
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        
        private static method GroupDestroy takes nothing returns nothing
            call BladeUnitData[GetEnumUnit()].cleanup()
        endmethod
                
        private static method deathBlade takes nothing returns nothing
            local unit u = GetEnumUnit()
            local BladeUnitData bdata = BladeUnitData[u]
            
            call SetUnitAnimationByIndex( u, ModuloInteger( bdata.pitch + R2I( bdata.pitchDir * BLADE_SPINSPEED ), 180 ) )
            call SetUnitFacing( u, GetUnitFacing( u ) + bdata.rotationDir * BLADE_SPINSPEED )
            call SetUnitX( u, tempData.cx + bdata.radius * Cos( bdata.angle ) )
            call SetUnitY( u, tempData.cy + bdata.radius * Sin( bdata.angle ) )
            
            set bdata.radius = bdata.radius - 18 / ( tempData.sec - 13 )
            call SetUnitVertexColor( u, RED, GREEN, BLUE, tempData.alpha ) 
        endmethod
        
        private static method release takes nothing returns boolean
            local thistype this = KT_GetData()

            set tempData = this
            call ForGroup( this.bladeGroup, function thistype.deathBlade )
            
            set this.ticks = this.ticks + 1
            set this.sec   = this.sec   + TIMER_PERIOD
            set this.alpha = R2I( 255 - this.ticks * 3 )
            
            if this.alpha <= 0.00 then
                call this.destroy()
                return true
            endif
        
            return false
        endmethod

        private static method damageCond takes nothing returns boolean
            local unit u = GetTriggerUnit()
            if IsTargetValid( u ) then 
                call DamageData.create( u, thistype( thistype.data[GetTriggeringTrigger()] ) )
            endif
            set u = null
            return false
        endmethod
        
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        
        private method onDestroy takes nothing returns nothing
            call ForGroup( this.bladeGroup, function thistype.GroupDestroy )
        endmethod
        
        static method create takes unit sourceUnit, unit whichUnit, integer level returns thistype
            local thistype this = thistype.allocate()
            local integer  i    = 0
            
            // sourceUnit  --> Who cast the spell. They will be responsible for damage.
            // whichUnit   --> Who is the centre of the barrier.
            // whichPlayer --> Who owns the Blade Barrier.
            
            static if not ALLOW_MULTI_INSTANCE then
                if IsUnitInGroup( whichUnit, ACTIVE_GROUP ) then
                    call SimError( GetOwningPlayer( sourceUnit ), ERROR_MSG )
                    set this.early = true
                    call this.deallocate()
                    return 0
                endif
            endif
            
            if this.bladeGroup == null then
                set this.bladeGroup = CreateGroup()
            else
                call GroupClear( this.bladeGroup )
            endif
            
            if this.damageGroup == null then
                set this.damageGroup = CreateGroup()
            else
                call GroupClear( this.damageGroup )
            endif

            set this.source = sourceUnit
            set this.caster = whichUnit
            set this.cx     = GetUnitX( whichUnit )
            set this.cy     = GetUnitY( whichUnit )
            set this.cz     = GetUnitFlyHeight( whichUnit )
            set this.owner  = GetOwningPlayer( sourceUnit )
            set this.level  = level
            set this.dur    = DURATION( this.level ) 
            set this.radius = RADIUS  ( this.level )
            
            loop
                exitwhen i == BLADES( this.level )
                call this.AddBlade()
                set i = i + 1
            endloop
                        
            set tempData = this
            set this.t = CreateTrigger()
            set thistype.data[this.t] = this
            call TriggerRegisterUnitInRange( this.t, this.caster, this.radius + ( 45 - COLLISION_SIZE ), null )
            call TriggerAddCondition( this.t, function thistype.damageCond )
            
            static if not ALLOW_MULTI_INSTANCE then
                call GroupAddUnit( ACTIVE_GROUP, this.caster )
            endif   

            call KT_Add( function thistype.periodic, this, TIMER_PERIOD )
            return this
        endmethod
    endstruct
    
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    // Event responses:
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    private function onCast takes nothing returns boolean
        local unit u = GetTriggerUnit()
        if IsUnitInGroup( u, ACTIVE_GROUP ) then
            call PauseUnit( u, true )
            call IssueImmediateOrder( u, "stop" )
            call PauseUnit( u, false )
            call SimError( GetTriggerPlayer(), ERROR_MSG )
        endif
        set u = null
        return false
    endfunction
    
    // onEffect: response to the no target ability, it directly utilizes the struct.
    private function onEffect takes nothing returns boolean
        local unit u = GetTriggerUnit()
        call BladeBarrier.create( u, u, GetUnitAbilityLevel( u, ABILITY_ID ) )
        set u = null
        return false
    endfunction
    
    // BladeBarrier_Create: uses a boolean for determining whether a instance was 
    // successful or not, eg: powerups.
    public function Create takes unit source, unit target, integer level returns boolean
        local BladeBarrier barrier = BladeBarrier.create( source, target, level )

        if barrier == 0 then
            return false
        endif

        return true
    endfunction
    
    private function onInit takes nothing returns nothing
        call GT_AddStartsEffectAction( function onEffect, ABILITY_ID )
        static if not ALLOW_MULTI_INSTANCE then
            call GT_AddBeginsChanellingAction( function onCast, ABILITY_ID )
        endif
        set BladeBarrier.data = Table.create()
    endfunction

endlibrary

CHANGELOG:

v1.04 - 01/07/2010
  • Removed dependency on Damage. Added configurable attacktype, damagetype, and weapontype.
  • Made one barrier per instance blocked in the create method as well.
  • Added a possible death animation if a unit is killed by this ability.
  • SOUND_EVERY is now in seconds.
  • Blade Barrier will now track it's target's height.
  • The number of blades created can now be adjusted by a configurable function which takes a level.
  • Changed IsUnitType( unit, UNIT_TYPE_DEAD ) calls to UnitAlive.
  • DamageData now ends if the damage dealt kills the target rather than on the next timer iteration.
  • BladeUnitData now longer checks for a null effect or nulls the unit.
  • Cleaned up the BladeBarrier struct a bit.
  • Added colouring global configs. for the Blade Barrier units.
  • Create method no longer takes a player, but takes a integer level now.
  • Inlined the INLINED_VOLUME.
  • Added a new function: public function Create takes unit source, unit target, integer level returns boolean
  • No longer using GroupUtils for recylcing.

v1.03 - 26/06/2010
  • It actually was not MUI. Fixed this.
  • Privatalized a shitload of members. I don't know why.
  • When a unit in the damage group left the radius, they couldn't get back in, fixed this.

v1.02 - 03/06/2010
  • Code optimisation
  • Added a configurable to have the original death animation
  • Fixed the configurables
  • Create method takes a player, and a unit - sourceUnit and whichPlayer
  • Fixed the target Blade Barrier - still no support for one instance per target.
  • Removed the target from the 'damage whirl' when they die.

v1.01 - 16/05/2010
  • Code improvement
  • Allowed option for instant removal of blades or fade out
  • Allowed option for one barrier per unit
  • Changed creation method of struct to take a unit, to allow in making a 'target' Blade Barrier (Dinowc)

v1.00 - 13/05/2010
  • Initial release

Keywords:
Blade Barrier, Blade, Pitch, Tossrock, Pudge Wars Advanced, Pudge Wars, Circle, Metal, sharp, vJass, MRFAN1231, Siraraz, Pudge Wars, BlackRose, DPS
Contents

Blade Barrier v1.04 (Map)

Reviews
12th Dec 2015 IcemanBo: Too long as NeedsFix. Rejected. 09:28, 27th May 2010 TriggerHappy: There were a few BJs which could have been avoided, but other than that it looks nice. edit Rejected until updated by author's request.

Moderator

M

Moderator

12th Dec 2015
IcemanBo: Too long as NeedsFix. Rejected.

09:28, 27th May 2010
TriggerHappy:

There were a few BJs which could have been avoided, but other than that it looks nice.

edit
Rejected until updated by author's request.
 
Level 38
Joined
Sep 26, 2009
Messages
8,464
JASS:
//| Requires:
//|           - GTrigger    by Jesus4Lyf
//|           - KeyTimer2   by Jesus4Lyf
//|           - GroupUtils  by Rising_Dusk
//|           - AIDS        by Jesus4Lyf
//|           - Damage      by Jesus4Lyf <Geez4Lyf :D>

Ouch O_O

I should see this spell if it requires so many libraries.

Edit: OK, tested.

1. My computer only handles 2-3 simultaneous before significant framerate drop. Using fewer blades couldn't hurt in case multiple users have selected that hero.

2. I know it's customizable, but the blade model could be a lot better, such Glaive Missile.

3. Blood and sound effects are good. I don't have much experience with sounds so I have no further imput on that.

4. Why are you nulling struct members?
 
Level 15
Joined
Jul 6, 2009
Messages
888
JASS:
//| Requires:
//|           - GTrigger    by Jesus4Lyf
//|           - KeyTimer2   by Jesus4Lyf
//|           - GroupUtils  by Rising_Dusk
//|           - AIDS        by Jesus4Lyf
//|           - Damage      by Jesus4Lyf <Geez4Lyf :D>
Ouch O_O

I should see this spell if it requires so many libraries.

Nothing wrong with so many requirements, they are all commonly used systems.

Actually, I could remove Damage from the list, and replace it with the Blizzard native, but I like Damage (lol) :D
I could also let go of GTrigger and use the native register.
But nah.

1. My computer only handles 2-3 simultaneous before significant framerate drop. Using fewer blades couldn't hurt in case multiple users have selected that hero.

Hm. I actually intended to have a '1 INSTANCE PER UNIT' option like my Whilrpool spell, I'll get around to that. This spell is made of 60 units per instance, all moving around... I wander if how I am applying the actions to the chainUnits good or bad, I know nothing of Linked Lists ;o


2. I know it's customizable, but the blade model could be a lot better, such Glaive Missile.

This is meant to recreate Pudge War's Blade Barrier, it was only made of Spell Breaker missiles. So yeah, you try it and tell me how it looks.

Heck:
Compare.jpg

Here it is, left = Glaive, right = Trail missiels.

3. Blood and sound effects are good. I don't have much experience with sounds so I have no further imput on that.

All compliments to Tossrock on the blade sounds.

4. Why are you nulling struct members?
Good practice to, even if they are globals and will be used again, there is no harm in doing so. Infact, you'll see a lot of people null members in structs.
 
Level 38
Joined
Sep 26, 2009
Messages
8,464
I understand good practice, but with 60 missiles 300 needless function calls when the spell wraps up is excessive. (Setting to null takes the same speed as calling an empty function).

Spell is good, the whirl effect looks really sweet and I like to see what people are currently making with vJass capabilities.

You have to lock up millions of handles before there is any problem, so nulling arrays which already have a limit of 8000 is more likely a good practice to avoid. Similar to how nulling triggers and players is something better off ignored. I think most oldschool JASS coders are just in the habit of nulling every little thing, but since I never had to develop that habit I can see past the overkill.
 
Level 15
Joined
Jul 6, 2009
Messages
888
I understand good practice, but with 60 missiles 300 needless function calls when the spell wraps up is excessive. (Setting to null takes the same speed as calling an empty function).

Really? o.o, wow.

Spell is good, the whirl effect looks really sweet and I like to see what people are currently making with vJass capabilities.

This spell is nothing compared to SANDMAN. :)

You have to lock up millions of handles before there is any problem, so nulling arrays which already have a limit of 8000 is more likely a good practice to avoid. Similar to how nulling triggers and players is something better off ignored. I think most oldschool JASS coders are just in the habit of nulling every little thing, but since I never had to develop that habit I can see past the overkill.

Ok. I'll just leave every global unnulled o_O
 
Level 38
Joined
Sep 26, 2009
Messages
8,464
I really like it :infl_thumbs_up: The effect in the end that makes the blades fade away is well made.
I forgot to mention that :p

I really like the fade effect ! There's only one thing I ask MRFAN to change about it: please make them dispell right from the circle instead of moving back to the caseter. I think the effect would be more realistic as right now the fast-pace of the spell has a comparatively long mull before releasing the blades...
 
I understand good practice, but with 60 missiles 300 needless function calls when the spell wraps up is excessive. (Setting to null takes the same speed as calling an empty function).

Since when is nulling the same speed as calling an empty function? =P Nulling is nearly twice as fast.

------------------------------------

Anyway, some comments on the code.

Any particular reason for these 3 variables?
JASS:
            local real ticks = 0
            local real tempDur = SPELL_DUR
            local real radius = BLADE_AOE + 20

They don't seem to be used.

For this set this.owner = GetOwningPlayer( this.caster ), you can use GetTriggerPlayer() instead since benchmarks show it is faster. =)

JASS:
            call TriggerRegisterUnitInRange( this.t, this.caster, BLADE_AOE + ( 45 - COLLISION_SIZE ), BOOLEXPR_TRUE )

You can use "null" without worry now as of patch 1.24. GroupUtils isn't really needed if you just recycle them.

JASS:
            call TriggerAddCondition( this.t, function thistype.damageCond )

Shouldn't that be Condition(function thistype.damageCond)?

You can also use this directly as so:
JASS:
 static method GroupDestroy takes nothing returns nothing
            call BladeUnitData[GetEnumUnit()].cleanup()
        endmethod

Rather than the local declaration and then the call x.cleanup().

JASS:
static method periodic takes nothing returns boolean
            local thistype this = KT_GetData()
            local integer  r
            local sound    s = null
            set tempData = this

In that method you don't use r nor s, so you can simply remove them. =)

In static method rotateBlade takes nothing returns nothing, you need to null the bladeUnit variable.

For this line:
JASS:
        method AddBlade takes nothing returns nothing
            local unit blade = CreateUnit( this.owner, DUMMY_ID ,GetUnitX(.caster),GetUnitY(.caster), 0 )

You should save the X/Y of the caster into global variables/members of the struct temporarily so that you don't need to calculate it 59 extra times.

JASS:
  IsUnitType( this.originData.caster, UNIT_TYPE_DEAD ) then

You might want to use native UnitAlive takes unit id returns boolean instead. (Declare the native wherever you want in the scope and then use it [it is a native from common.ai])

---------

Other than that, awesome job on the spell. Really nice eyecandy, and cool idea. =D
 
Level 15
Joined
Jul 6, 2009
Messages
888
Have you people never played Pudge Wars Advanced lol? It's the same from that. But anyways, I'll need to do the 'on list' things and what Purge mentioned.

Shouldn't that be Condition(function thistype.damageCond) ?
Doesn't JassHelper do that for you?

You can use "null" without worry now as of patch 1.24. GroupUtils isn't really needed if you just recycle them.
Done to null, but I like GropuUtils as it's just two lines. NewGroup() and ReleaseGroup(), and people already use such systems :)

Ok. I'm going to try do stuff on the do list now.

--

Bribe: MY NAME HAS NUMBERS .__.
 
Have you people never played Pudge Wars Advanced lol? It's the same from that. But anyways, I'll need to do the 'on list' things and what Purge mentioned.

Yeah, but that doesn't take away from the fact of it being an awesome spell. =P

Doesn't JassHelper do that for you?

Oh, I didn't know that. I wasn't sure, you can just leave it as it is then. =)

Done to null, but I like GropuUtils as it's just two lines. NewGroup() and ReleaseGroup()

Well, I guess it is up to you. But just remember that group recycling (more like "reusing" rather than recycling, it is never dumped at all) is effectively done without "using" any lines (sort of, or at least not any extra lines). =D If you are interested in it, you can read this real quick (it is pretty short [if you haven't already read it]): How to use Groups without leaking
 
Top