• 🏆 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!

Shocking Fence v0.01

This bundle is marked as useful / simple. Simplicity is bliss, low effort and/or may contain minor bugs.
Credits in the code.

Creates a fence of pure red lightning. The fence rotates around the targeted unit. If the unit is an ally the fence protect it from some damage. If the target is an enemy the fence will deal some damage each time the unit takes damage ( only 1 time each 2 seconds ).

Should a unit under the effect of fence be the target of a unit target spell the fence will jump to the caster. ( all values refreshed. )

The fence stacks.

Please give credits if you use this one.

JASS:
scope BurningOrbit initializer init

//*************************************************************************************************************//
//                                              Shocking Fence v0.01                                           //
//                                                        by                                                   //
//                                                      cedi                                                   //
//                                                                                                             //
//                                         needs: TimerUtils by Vexorian                                       //
//                                                Bound Sentinel by Vexorian                                   //
//                                                Dummy Model by                                               //
//                                                Vector lib by                                                //
//                                                Heights by cedi                                              //
//*************************************************************************************************************//

//For use, copy the trigger to your map, copy the dummy create a spell and adjust the values below.

private keyword Sub
private keyword Main

globals
    //ID of the spell
    private constant integer        SPELL_ID            = 'A000'
    //ID of your dummy
    private constant integer        DUMMY_ID            = 'h000'
    //Interval of the moves
    private constant real           TIMER_INTERVAL      = 0.035
    //Main
    //Height between each fence
    private constant real           MAIN_HEIGHT         = 20.00
    //MS of the fence when it is in the air ( in wc3 units )
    private constant real           MAIN_SPEED          = 500.00
    //Distance needed of the fence to the unit when the fence flies.
    private constant real           MAIN_COL            = 10.00
    //Angle of the bow when the fence flies. Like oe value.
    private constant real           MAIN_FLY_HEIGHT     = 0.45
    //Time needed between each time damage
    private constant real           MAIN_DAMAGE_INT     = 2.00
    //Percent of the full damage each time the fence deals damage.
    private constant real           MAIN_DMG_PERCENT    = 0.10 //% pro interval
    //SFX when the fence damages a unit.
    private constant string         MAIN_DMG_SFX        = "Abilities\\Spells\\Orc\\FeralSpirit\\feralspiritdone.mdl"
    //Sub
    //Color rgb.
    private constant integer        SUB_COLOR_RED       = 255
    private constant integer        SUB_COLOR_GREEN     = 255
    private constant integer        SUB_COLOR_BLUE      = 255
    private constant integer        SUB_COLOR_ALPHA     = 255
    //Count of the corners
    private constant integer        SUB_MAX             = 6
    //Distance to the unit ( of the corners )
    private constant real           SUB_DISTANCE        = 75.00
    //Amount of grad the corners turn each second.
    private constant real           SUB_TURN_SPEED      = 180.00
    //Size of the corner units
    private constant real           SUB_SIZE            = 1.00
    //Model of the corners
    private constant string         SUB_MODEL           = "Abilities\\Weapons\\VengeanceMissile\\VengeanceMissile.mdl"
    //Lightning
    //Color of the lightning in rgb. ( these things are very strange, looks like they're in percent. )
    private constant real           LIGHT_COLOR_RED     = 1
    private constant real           LIGHT_COLOR_GREEN   = 1
    private constant real           LIGHT_COLOR_BLUE    = 1
    private constant real           LIGHT_COLOR_ALPHA   = 1
    //If of the lightning.
    private constant string         LIGHT_ID            = "AFOD"
    //System
    private          vector         AXIS
    private          hashtable      HASH                = InitHashtable()
    private          trigger        DAMAGE_TRIG         = CreateTrigger()
    private          trigger        SPELL_TRIG          = CreateTrigger()
    private          real           TEMPREAL
    private          unit           TEMPUNIT
    private          Main           TEMPMAIN
endglobals

private function DAMAGE takes integer level returns real
    return 100.00 + 20.00 * level
endfunction

private function SHIELD takes integer level returns real
    return 100.00 + 20.00 * level
endfunction

//*************************************************************************************************************//
//                                                     !SYSTEM!                                                //
//*************************************************************************************************************//

private function AngleBetweenCoordinates takes real x1, real x2, real y1, real y2 returns real
    return bj_RADTODEG * Atan2(y2 - y1, x2 - x1)
endfunction

private function DistanceBetweenCoordinates takes real x1, real x2, real y1, real y2 returns real
    local real dx = x2 - x1
    local real dy = y2 - y1
    return SquareRoot(dx * dx + dy * dy)
endfunction

private function AngleBetweenUnits takes unit u, unit u2 returns real
    return bj_RADTODEG * Atan2(GetUnitY( u2 ) - GetUnitY( u ), GetUnitX( u2 ) - GetUnitX( u ))
endfunction

private function IsAliveAndUnitAndNotMagicImmune takes nothing returns boolean
    return GetWidgetLife( GetFilterUnit() ) > 0.405 and IsUnitType(GetFilterUnit(), UNIT_TYPE_STRUCTURE) == false and IsUnitType(GetFilterUnit(), UNIT_TYPE_MAGIC_IMMUNE) == false
endfunction

private function Heal takes nothing returns nothing
    call DestroyTimer( GetExpiredTimer() )
    call SetWidgetLife( TEMPUNIT, GetWidgetLife( TEMPUNIT ) + TEMPREAL )
endfunction

private function ParabolaZ2 takes real y0, real y1, real h, real d, real x returns real
  local real A = (2*(y0+y1)-4*h)/(d*d)
  local real B = (y1-y0-A*d*d)/d
  return A*x*x + B*x + y0
endfunction

private function OuterControl takes nothing returns nothing
    set TEMPMAIN = GetTimerData( GetExpiredTimer() )
    call TEMPMAIN.control()
endfunction

private struct Sub
    unit u
    lightning l
    Main root
    vector dist
    effect model
    
    method onDestroy takes nothing returns nothing
        call DestroyEffect( .model )
        set .model = null
        call KillUnit( .u )
        set .u = null
        call DestroyLightning( .l )
        set .l = null
        call .dist.destroy()
    endmethod
    
    method turn takes nothing returns nothing
        call .dist.rotate( AXIS, SUB_TURN_SPEED * TIMER_INTERVAL * bj_DEGTORAD )
        call SetUnitX( .u, .root.posi.x + .dist.x )
        call SetUnitY( .u, .root.posi.y + .dist.y )
        if GetUnitZ( .root.target ) != GetUnitZ( .u ) then
            call SetUnitZ( .u, .root.posi.z )
        endif
    endmethod
    
    method setLightning takes integer i returns nothing
        local real x = 0.00
        local real y = 0.00
        local real z = .root.posi.z
        local real tx = .dist.x + .root.posi.x
        local real ty = .dist.y + .root.posi.y
        if i >= SUB_MAX - 1 then
            set x = .root.sub[0].dist.x + .root.posi.x
            set y = .root.sub[0].dist.y + .root.posi.y
        else
            set x = .root.sub[i + 1].dist.x + .root.posi.x
            set y = .root.sub[i + 1].dist.y + .root.posi.y
        endif
        call MoveLightningEx( .l, false, tx, ty, z, x, y, z )
    endmethod
    
    static method create takes Main root, integer count returns thistype
        local thistype this = thistype.allocate()
        local real angle = 360.00 / SUB_MAX * count * bj_DEGTORAD
        set .root = root
        set .dist = vector.create( Cos( angle ) * SUB_DISTANCE, Sin( angle ) * SUB_DISTANCE, 0.00 )
        set .u = CreateUnit( GetOwningPlayer( .root.caster ), DUMMY_ID, .root.posi.x + .dist.x, .root.posi.y + .dist.x, 0.00 )
        set .model = AddSpecialEffectTarget( SUB_MODEL, .u, "origin" )
        call SetUnitVertexColor( .u, SUB_COLOR_RED, SUB_COLOR_GREEN, SUB_COLOR_BLUE, SUB_COLOR_ALPHA )
        call SetUnitScale( .u, SUB_SIZE, SUB_SIZE, SUB_SIZE )
        set .l = AddLightningEx( LIGHT_ID, false, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00 )
        call SetLightningColor( .l, LIGHT_COLOR_RED, LIGHT_COLOR_GREEN, LIGHT_COLOR_BLUE, LIGHT_COLOR_ALPHA )
        return this
    endmethod
    
endstruct

private struct Main
    unit target     = null
    unit caster     = null
    Sub array sub[SUB_MAX]
    vector posi     = 0
    vector start    = 0
    real shield     = 0.00
    real damage     = 0.00
    real interval   = MAIN_DAMAGE_INT
    integer level   = 1
    integer number  = 1
    boolean jumping = false
    boolean friend  = false
    timer t         = null
    
    method onDestroy takes nothing returns nothing
        local integer i = 0
        loop
            exitwhen i >= SUB_MAX
            call .sub[i].destroy()
            set i = i + 1
        endloop
        call SaveInteger( HASH, GetHandleId( .target ), 0, .number - 1 )
        call SaveInteger( HASH, GetHandleId( .target ), .number, 0 )
        set .target = null
        set .caster = null
        call .posi.destroy()
        call .start.destroy()
        call ReleaseTimer( .t )
        set .t = null
    endmethod
    
    method control takes nothing returns nothing
        local integer i = 0
        local real z
        local real x
        local real y
        local real dist
        local real angle
        set .interval = .interval - TIMER_INTERVAL
        if GetWidgetLife( .target ) <= 0.405 then
            call .destroy()
            return
        endif
        //Main move
        if .jumping then
            if DistanceBetweenCoordinates( .posi.x, GetUnitX( .target ), .posi.y, GetUnitY( .target ) ) <= MAIN_COL then
                if not HaveSavedInteger( HASH, GetHandleId( .target ), 0 ) then
                    set .number = 1
                    call SaveInteger( HASH, GetHandleId( .target ), 0, 1 )
                else
                    set .number = LoadInteger( HASH, GetHandleId( .target ), 0 ) + 1
                    call SaveInteger( HASH, GetHandleId( .target ), 0, .number )
                endif
                call SaveInteger( HASH, GetHandleId( .target ), .number, this )
                call .start.destroy()
                set .jumping = false
                set .posi.x = GetUnitX( .target )
                set .posi.y = GetUnitY( .target )
                set .posi.z = GetUnitZ( .target ) + MAIN_HEIGHT * .number
                set .shield = SHIELD( .level )
                set .damage = DAMAGE( .level )
            else
                set angle = AngleBetweenCoordinates( .posi.x, GetUnitX( .target ), .posi.y, GetUnitY( .target ) ) * bj_DEGTORAD
                set dist = DistanceBetweenCoordinates( .start.x, GetUnitX( .target ), .start.y, GetUnitY( .target ) )
                set x = .posi.x + Cos( angle ) * MAIN_SPEED * TIMER_INTERVAL
                set y = .posi.y + Sin( angle ) * MAIN_SPEED * TIMER_INTERVAL
                set z = ParabolaZ2( .start.z, GetUnitZ( .target ), dist * MAIN_FLY_HEIGHT, dist, DistanceBetweenCoordinates( .start.x, x, .start.y, y ) )
                set .posi.x = x
                set .posi.y = y
                set .posi.z = z
            endif
        else
            set .posi.x = GetUnitX( .target )
            set .posi.y = GetUnitY( .target )
            set .posi.z = GetUnitZ( .target ) + MAIN_HEIGHT * .number
        endif
        //Turn
        loop
            exitwhen i >= SUB_MAX
            call .sub[i].turn()
            set i = i + 1
        endloop
        set i = 0
        loop
            exitwhen i >= SUB_MAX
            call .sub[i].setLightning( i )
            set i = i + 1
        endloop
    endmethod
    
    method TookDamage takes unit damager, real damage returns nothing
        local real r
        local timer t
        if .friend and not .jumping then
            if damage >= .shield then
                set damage = damage - .shield
                call .destroy()
            else
                set .shield = .shield - damage
            endif
            set r = GetUnitState( .target, UNIT_STATE_MAX_LIFE ) - GetWidgetLife( .target )
            if damage > r then
                call SetWidgetLife( .target, GetWidgetLife( .target ) + r )
                set TEMPREAL = damage - r
                set TEMPUNIT = .target
                set t = CreateTimer()
                call TimerStart( t, 0.00, false, function Heal )
                set t = null
            else
                call SetWidgetLife( .target, GetWidgetLife( .target ) + damage )
            endif
        else
            if .interval <= 0.00 then
                set .interval = MAIN_DAMAGE_INT
                set .damage = .damage - DAMAGE( .level ) * MAIN_DMG_PERCENT
                call DisableTrigger( DAMAGE_TRIG )
                call UnitDamageTarget( .caster, .target, DAMAGE( .level ) * MAIN_DMG_PERCENT, true, false, ATTACK_TYPE_MAGIC, DAMAGE_TYPE_NORMAL, null )
                call EnableTrigger( DAMAGE_TRIG )
                call DestroyEffect( AddSpecialEffectTarget( MAIN_DMG_SFX, .target, "chest" ) )
                if .damage <= 0.00 then
                    call .destroy()
                endif
            endif
        endif
    endmethod
    
    method NewTarget takes unit target returns nothing
        call SaveInteger( HASH, GetHandleId( .target ), 0, .number - 1 )
        call SaveInteger( HASH, GetHandleId( .target ), .number, 0 )
        set .target = target
        set .start = vector.create( .posi.x, .posi.y, .posi.z )
        set .jumping = true
        if IsUnitEnemy( target, GetOwningPlayer( .caster ) ) then
            set .friend = false
        else
            set .friend = true
        endif
    endmethod
    
    static method create takes unit caster, unit target returns thistype
        local thistype this = thistype.allocate()
        local integer i = 0
        set .caster = caster
        set .target = target
        if not HaveSavedInteger( HASH, GetHandleId( target ), 0 ) then
            set .number = 1
            call SaveInteger( HASH, GetHandleId( target ), 0, 1 )
        else
            set .number = LoadInteger( HASH, GetHandleId( target ), 0 ) + 1
            call SaveInteger( HASH, GetHandleId( target ), 0, .number )
        endif
        call SaveInteger( HASH, GetHandleId( target ), .number, this )
        set .posi = vector.create( GetUnitX( target ), GetUnitY( target ), GetUnitZ( target ) + MAIN_HEIGHT * .number )
        set .level = GetUnitAbilityLevel( caster, SPELL_ID )
        set .shield = SHIELD( .level )
        set .damage = DAMAGE( .level )
        loop
            exitwhen i >= SUB_MAX
            set .sub[i] = Sub.create( this, i )
            set i = i + 1
        endloop
        set i = 0
        loop
            exitwhen i >= SUB_MAX
            call .sub[i].setLightning( i )
            set i = i + 1
        endloop
        if IsUnitEnemy( target, GetOwningPlayer( caster ) ) then
            set .friend = false
        else
            set .friend = true
        endif
        set .t = NewTimer()
        call SetTimerData( .t, this )
        call TimerStart( .t, TIMER_INTERVAL, true, function OuterControl )
        return this
    endmethod
    
endstruct

private function UnderFence takes nothing returns boolean
    if HaveSavedInteger( HASH, GetHandleId( GetSpellTargetUnit() ), 0 ) then
        set TEMPMAIN = LoadInteger( HASH, GetHandleId( GetSpellTargetUnit() ), LoadInteger( HASH, GetHandleId( GetSpellTargetUnit() ), 0 ) )
        call TEMPMAIN.NewTarget( GetTriggerUnit() )
    endif
    return false
endfunction

private function TakesDamage takes nothing returns boolean
    if HaveSavedInteger( HASH, GetHandleId( GetTriggerUnit() ), 0 ) then
        set TEMPMAIN = LoadInteger( HASH, GetHandleId( GetTriggerUnit() ), LoadInteger( HASH, GetHandleId( GetTriggerUnit() ), 0 ) )
        call TEMPMAIN.TookDamage( GetEventDamageSource(), GetEventDamage() )
    endif
    return false
endfunction

private function IsSpell takes nothing returns nothing
    if GetSpellAbilityId() == SPELL_ID then
        call DisableTrigger( SPELL_TRIG )
        call Main.create( GetTriggerUnit(), GetSpellTargetUnit() )
        call PolledWait( 0.4 )
        call EnableTrigger( SPELL_TRIG )
    endif
endfunction

private function AddToTakesDamage takes nothing returns nothing
    call TriggerRegisterUnitEvent( DAMAGE_TRIG, GetTriggerUnit(), EVENT_UNIT_DAMAGED )
endfunction

private function init takes nothing returns nothing
    local trigger t = CreateTrigger()
    local trigger t3 = CreateTrigger()
    local group g = CreateGroup()
    local unit u
    call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddAction( t, function IsSpell )
    
    call TriggerRegisterAnyUnitEventBJ( SPELL_TRIG, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( SPELL_TRIG, Condition( function UnderFence ) )
    
    call TriggerRegisterEnterRectSimple( t3, GetWorldBounds() )
    call TriggerAddAction( t3, function AddToTakesDamage )
    
    call TriggerAddCondition( DAMAGE_TRIG, Condition( function TakesDamage ) )
    
    call GroupEnumUnitsInRange( g, 0.00, 0.00, 9999999.00, null )
    loop
        set u = FirstOfGroup( g )
        exitwhen u == null
        call TriggerRegisterUnitEvent( DAMAGE_TRIG, u, EVENT_UNIT_DAMAGED )
        call GroupRemoveUnit( g, u )
        set u = null
    endloop
    call DestroyGroup( g )
    set g = null
    
    set AXIS = vector.create( 0.00, 0.00, 1.00 )
    
    set t = null
endfunction

endscope

Keywords:
fence, shocking, shock, lightning, red, corner, ball, energy, shield,
Contents

Shocking Fence v0.01 (Map)

Reviews
12th Dec 2015 IcemanBo: Too long time as NeedsFix. Rejected. 13:43, 13th Feb 2010 TriggerHappy: IsAliveAndUnitAndNotMagicImmune could use UnitAlive. You really shouldn't be destroying timers, and what bugs me the most is... YOU HAVE...

Moderator

M

Moderator

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

13:43, 13th Feb 2010
TriggerHappy:

  • IsAliveAndUnitAndNotMagicImmune could use UnitAlive.
  • You really shouldn't be destroying timers, and what bugs me the most is... YOU HAVE TIMERUTILS which comes with timer recycling..
  • Why are you using PolledWait..?
  • scope BurningOrbit should be scope ShockingFence.
  • Your group enum in your init trigger should be enum units in a rect (playable map area) instead of in range.
  • Inlining your own damage detection system is a horrible idea, just use one of the already existent ones.
 
Level 10
Joined
May 19, 2008
Messages
176
* IsAliveAndUnitAndNotMagicImmune could use UnitAlive. ?
* You really shouldn't be destroying timers, and what bugs me the most is... YOU HAVE TIMERUTILS which comes with timer recycling.. The 0 timer is only sed to go past the damage event. Bt I can se a globals for it and pause it only.
* Why are you using PolledWait..? ( because of a tricky bug. When I won't set a wait between deactivate and activate the fence jumps everytime you cast it to the caster. But it wasn't a proper solution, I can change it. )
* scope BurningOrbit should be scope ShockingFence. // yayay
* Your group enum in your init trigger should be enum units in a rect (playable map area) instead of in range. Does this matter?
* Inlining your own damage detection system is a horrible idea, just use one of the already existent ones. //Nahh I don't know, I don't want to force tzhe user to add a specific damage sys... But I can add a how to remove mine and add the needed things to your own.

cedi
 
Top