1. Are you planning to upload your awesome spell or system to Hive? Please review the rules here.
    Dismiss Notice
  2. Join other hivers in a friendly concept-art contest. The contestants have to create a genie coming out of its container. We wish you the best of luck!
    Dismiss Notice
  3. The Melee Mapping Contest #4: 2v2 - Results are out! Step by to congratulate the winners!
    Dismiss Notice
  4. We're hosting the 15th Mini-Mapping Contest with YouTuber Abelhawk! The contestants are to create a custom map that uses the hidden content within Warcraft 3 or is inspired by any of the many secrets within the game.
    Dismiss Notice
  5. The 20th iteration of the Terraining Contest is upon us! Join and create exquisite Water Structures for it.
    Dismiss Notice
  6. Check out the Staff job openings thread.
    Dismiss Notice

Wall of Death v1.12

Submitted by Flux
This bundle is marked as approved. It works and satisfies the submission rules.
Code (vJASS):

scope WallOfDeath /*

                                Wall of Death v1.12
                                      by Flux
             
        Raises a wall of warping light that damages units who crosses it.
     
 
    OPTIONALLY REQUIRES:
        Table - if not found, this spell will resort to creating a hashtable.
                Hashtables are limited to 255 per map.
             
        RegisterPlayerUnitEvent/RegisterEventPack - If not found, an extra trigger is created.
     
        SpellEffectEvent - If not found, an extra trigger is created and GetSpellAbilityId() check will occur.
 
    CREDITS:
        Bannar        - RegisterEvent Pack
        Bribe         - Table, SpellEffectEvent
        Magtheridon96 - RegisterPlayerUnitEvent
 
    */

    globals
        //Rawcode of the Spell
        private constant integer SPELL_ID = 'AWoD'
     
        //How high is the Wall Visual Line.
        private constant real WALL_HEIGHT = 500.0
     
        //When a unit gets a certain distance to the wall, it gets monitored whether it crosses the wall or not
        private constant real MONITOR_RANGE = 50.0
     
        //Periodic Timeout
        private constant real TIMEOUT = 0.03125
     
        //Determines whether the owner of the wall changes when the owner of the caster changes
        private constant boolean CHANGE_WITH_OWNER = false
     
        //Wall Visual Line appearance
        private constant string LIGHTNING_ID = "DRAL"
     
        //Wall Effecs
        private constant string WALL_SFX = "Abilities\\Spells\\Undead\\Unsummon\\UnsummonTarget.mdl"
     
        //Wall Effect distance to each other
        private constant real WALL_SFX_INTERVAL = 150.0
     
        //Note that you have to choose a scalable effect in WALL_SFX
        private constant real WALL_SFX_SCALE = 0.75
     
        //Height of Visual Effects
        private constant real SFX_HEIGHT = 0
     
        //When DummyRecycler is not found, this configuration will be used
        private constant player NEUTRAL_PLAYER = Player(14)
        private constant integer DUMMY_ID = 'dumi'
    endglobals
 
    native UnitAlive takes unit u returns boolean
 
    private function WallLength takes integer level returns real
        return 100.0*level + 700.0
    endfunction
 
    private function Duration takes integer level returns real
        return 5.0*level + 10.0
    endfunction
 
    private function TargetFilter takes unit target, player owner returns boolean
        return UnitAlive(target) and IsUnitEnemy(target, owner) and not IsUnitType(target, UNIT_TYPE_STRUCTURE) and not IsUnitType(target, UNIT_TYPE_MAGIC_IMMUNE)
    endfunction
 
    //What happens when a unit crosses the wall
    private function WallAction takes unit caster, unit target, integer level returns nothing
        call UnitDamageTarget(caster, target, 100.0*level, false, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_MAGIC, null)
        //You can also put all kinds of other stuffs here like stun/silence/root/create illusion of the target
    endfunction
 
    private struct WallSfx
        private unit u
        private effect sfx
     
        private thistype next
        private thistype prev
     
        private method remove takes nothing returns nothing
            set this.prev.next = this.next
            set this.next.prev = this.prev
            static if LIBRARY_DummyRecycler then
                call DummyAddRecycleTimer(this.u, 3.0)
            else
                call UnitApplyTimedLife(this.u, 'BTLF', 3.0)
            endif
            call DestroyEffect(this.sfx)
            set this.sfx = null
            call this.deallocate()
        endmethod
     
        method destroy takes nothing returns nothing
            local thistype node = this.next
            loop
                exitwhen node == this
                call node.remove()
                set node = node.next
            endloop
            call this.remove()
        endmethod
     
     
        private method add takes thistype head, real x, real y returns nothing
            set this.next = head
            set this.prev = head.prev
            set this.next.prev = this
            set this.prev.next = this
            static if LIBRARY_DummyRecycler then
                set this.u = GetRecycledDummyAnyAngle(x, y, SFX_HEIGHT)
            else
                set this.u = CreateUnit(NEUTRAL_PLAYER, DUMMY_ID, x, y, 0)
                call SetUnitFlyHeight(this.u, SFX_HEIGHT, 0)
            endif
            call SetUnitScale(this.u, WALL_SFX_SCALE, 0, 0)
            set this.sfx = AddSpecialEffectTarget(WALL_SFX, this.u, "origin")
        endmethod
     
        static method ceil takes real r returns integer
            local integer i = R2I(r)
            if I2R(i) == r then
                return i
            endif
            return i + 1
        endmethod
     
        static method create takes real x1, real y1, real length, real angle returns thistype
            local thistype head = thistype.allocate()
            local thistype node
            local real x = x1
            local real y = y1
            local integer i = thistype.ceil(length/WALL_SFX_INTERVAL)
            local real dist = length/i
            set head.next = head
            set head.prev = head
            static if LIBRARY_DummyRecycler then
                set head.u = GetRecycledDummyAnyAngle(x, y, SFX_HEIGHT)
            else
                set head.u = CreateUnit(NEUTRAL_PLAYER, DUMMY_ID, x, y, 0)
                call SetUnitFlyHeight(head.u, SFX_HEIGHT, 0)
            endif
            call SetUnitScale(head.u, WALL_SFX_SCALE, 0, 0)
            set head.sfx = AddSpecialEffectTarget(WALL_SFX, head.u, "origin")
            loop
                exitwhen i == 0
                set x = x + dist*Cos(angle)
                set y = y + dist*Sin(angle)
                set node = thistype.allocate()
                call node.add(head, x, y)
                set i = i - 1
            endloop
            return head
        endmethod
     
    endstruct
 
    private struct Wall
 
        //Spell Data
        private unit caster
        private group g
        private integer lvl
        private real radius
        private real duration
        private real x
        private real y
        static if not CHANGE_WITH_OWNER then
            private player owner
        endif
     
     
        //Visuals
        private lightning topL
        private lightning botL
        private lightning leftL
        private lightning rightL
        private WallSfx sfx
     
        //Wall Line Data
        private real m  //slope
        private real b //slope-intercept
     
        //List
        private thistype next
        private thistype prev
     
        //Unit Data
        static if LIBRARY_Table then
            private Table pos
        else
            private static hashtable hash = InitHashtable()
        endif
     
        private static group search = CreateGroup()
        private static timer t = CreateTimer()
        private static thistype global
     
        private method destroy takes nothing returns nothing
            set this.prev.next = this.next
            set this.next.prev = this.prev
            if thistype(0).next == 0 then
                call PauseTimer(thistype.t)
            endif
            //Remove saved positions
            static if LIBRARY_Table then
                call this.pos.destroy()
            else
                call FlushChildHashtable(thistype.hash, this)
            endif
            static if not CHANGE_WITH_OWNER then
                set this.owner = null
            endif
            //Destroy Handles
            call this.sfx.destroy()
            call DestroyLightning(this.botL)
            call DestroyLightning(this.topL)
            call DestroyLightning(this.leftL)
            call DestroyLightning(this.rightL)
            call DestroyGroup(this.g)
            set this.caster = null
            set this.botL = null
            set this.topL = null
            set this.leftL = null
            set this.rightL = null
            set this.g = null
            call this.deallocate()
        endmethod
     
        private method isAbove takes real x, real y returns boolean
            return y > this.m*x + this.b
        endmethod
     
        private method distance takes real x, real y returns real
            return (this.m*x - y + b)*(this.m*x - y + b)/(this.m*this.m + 1)
        endmethod
     
        private static method scanGroup takes nothing returns nothing
            local thistype this = thistype.global
            local unit u = GetEnumUnit()
            local integer id = GetHandleId(u)
            if this.distance(GetUnitX(u), GetUnitY(u)) > MONITOR_RANGE*MONITOR_RANGE or not IsUnitInRangeXY(u, this.x, this.y, this.radius)then
                call GroupRemoveUnit(this.g, u)
                if IsUnitInGroup(u, thistype.search) then
                    call GroupRemoveUnit(thistype.search, u)
                endif
                static if LIBRARY_Table then
                    if this.pos.boolean.has(id) then
                        call this.pos.boolean.remove(id)
                    endif
                elsea
                    if HaveSavedBoolean(thistype.hash, this, id) then
                        call RemoveSavedBoolean(thistype.hash, this, id)
                    endif
                endif
            endif
            set u = null
        endmethod
     
        private static method onPeriod takes nothing returns nothing
            local thistype this = thistype(0).next
            local unit u
            local real x
            local real y
            local integer id
            local boolean newPos
            static if CHANGE_WITH_OWNER then
                local player p = GetOwningPlayer(this.caster)
            else
                local player p = this.owner
            endif
            loop
                exitwhen this == 0
                set this.duration = this.duration - TIMEOUT
                if this.duration > 0 then
                    call GroupEnumUnitsInRange(thistype.search, this.x, this.y, this.radius, null)
                    set thistype.global = this
                    call ForGroup(this.g, function thistype.scanGroup)
                    loop
                        set u = FirstOfGroup(thistype.search)
                        exitwhen u == null
                        call GroupRemoveUnit(thistype.search, u)
                        if TargetFilter(u, p) then
                            set id = GetHandleId(u)
                            set x = GetUnitX(u)
                            set y = GetUnitY(u)
                            if this.distance(x, y) <= MONITOR_RANGE*MONITOR_RANGE then
                                static if LIBRARY_Table then
                                    if this.pos.boolean.has(id) then
                                        set newPos = this.isAbove(x, y)
                                        if this.pos.boolean[id] != newPos then
                                            call WallAction(this.caster, u, this.lvl)
                                        endif
                                        set this.pos.boolean[id] = newPos
                                    else
                                        set this.pos.boolean[id] = this.isAbove(x, y)
                                        call GroupAddUnit(this.g, u)
                                    endif
                                else
                                    if HaveSavedBoolean(thistype.hash, this, id) then
                                        set newPos = this.isAbove(x, y)
                                        if LoadBoolean(thistype.hash, this, id) != newPos then
                                            call WallAction(this.caster, u, this.lvl)
                                        endif
                                        call SaveBoolean(thistype.hash, this, id, newPos)
                                    else
                                        call SaveBoolean(thistype.hash, this, id, this.isAbove(x, y))
                                        call GroupAddUnit(this.g, u)
                                    endif
                                endif
                            endif
                        endif
                    endloop
                else
                    call this.destroy()
                endif
                set this = this.next
            endloop
            set p = null
        endmethod
     
        private static method onCast takes nothing returns boolean
            local thistype this = thistype.allocate()
            local real angle
            local real ex1
            local real ex2
            local real ey1
            local real ey2
            //Get Spell Data
            set this.caster = GetTriggerUnit()
            set this.g = CreateGroup()
            set this.lvl = GetUnitAbilityLevel(this.caster, SPELL_ID)
            set this.x = GetSpellTargetX()
            set this.y = GetSpellTargetY()
            set this.radius = 0.5*WallLength(this.lvl)
            set this.duration = Duration(this.lvl)
            static if LIBRARY_Table then
                set this.pos = Table.create()
            endif
            static if not CHANGE_WITH_OWNER then
                set this.owner = GetTriggerPlayer()
            endif
            //Get Line Data
            set angle = GetUnitFacing(this.caster)*bj_DEGTORAD - 0.5*bj_PI
            set ex1 = this.x + this.radius*Cos(angle)
            set ey1 = this.y + this.radius*Sin(angle)
            set angle = angle + bj_PI
            set ex2 = this.x + this.radius*Cos(angle)
            set ey2 = this.y + this.radius*Sin(angle)
            set this.m = (ey2 - ey1)/(ex2 - ex1)
            set this.b = ey1 - this.m*ex1
            //Create Lightning effects
            set this.botL = AddLightningEx(LIGHTNING_ID, true, ex1, ey1, 0, ex2, ey2, 0)
            set this.topL = AddLightningEx(LIGHTNING_ID, true, ex1, ey1, WALL_HEIGHT, ex2, ey2, WALL_HEIGHT)
            set this.leftL = AddLightningEx(LIGHTNING_ID, true, ex2, ey2, 0, ex2, ey2, WALL_HEIGHT)
            set this.rightL = AddLightningEx(LIGHTNING_ID, true, ex1, ey1, 0, ex1, ey1, WALL_HEIGHT)
            //Create Sfx
            set this.sfx = WallSfx.create(ex1, ey1, 2*this.radius, angle)
            //List insertion
            set this.next = thistype(0)
            set this.prev = thistype(0).prev
            set this.next.prev = this
            set this.prev.next = this
            if this.prev == 0 then
                call TimerStart(thistype.t, TIMEOUT, true, function thistype.onPeriod)
            endif
            return false
        endmethod
     
        static if not LIBRARY_SpellEffectEvent then
            private static method cond takes nothing returns boolean
                return GetSpellAbilityId() == SPELL_ID and thistype.onCast()
            endmethod
        endif
     
        private static method onInit takes nothing returns nothing
            static if LIBRARY_SpellEffectEvent then
                call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
            elseif RPUE_VERSION_NEW then
                call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_SPELL_EFFECT, function thistype.cond)
            elseif LIBRARY_RegisterPlayerUnitEvent then
                call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_SPELL_EFFECT, function thistype.cond)
            else
                local trigger t = CreateTrigger()
                call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
                call TriggerAddCondition(t, Condition(function thistype.cond))
            endif
        endmethod
     
    endstruct
 
endscope
 


Changelog

v1.00 - [25 November 2016]
- Initial Release

v1.10 - [27 November 2016]
- Fixed bug where units gets damaged even when not passing through the wall.
- Optimized distance check and core algorithm.
- Removed unused local variable.

v1.11 - [14 December 2016]
- Fixed possible bug for units instantly moving in and out of the search radius.

v1.12 - [21 January 2017]
- Added WallSfx scaling feature.
- Added CHANGE_WITH_OWNER feature.
- Fixed in-game tooltip.
Contents

Wall of Death v1.12 (Map)

Reviews
KILLCIDE
Simple spell in terms of concept, but execution is definitely challenging. I am unable to verify the math involved in calculating whether or not a unit is in the segment, but I trust the community will eventually point it out. The WallFunction()...
  1. Reventhous

    Reventhous

    Joined:
    May 2, 2015
    Messages:
    104
    Resources:
    1
    Spells:
    1
    Resources:
    1
    I think the spell looks a bit plain. My suggestion is add some more effect,
    like when the units cross the wall and when the wall expires. :)
     
  2. Flux

    Flux

    Joined:
    Feb 6, 2014
    Messages:
    2,334
    Resources:
    28
    Maps:
    1
    Spells:
    19
    Tutorials:
    2
    JASS:
    6
    Resources:
    28
    Visual effect or spell-mechanics effects? Nevertheless, a user can do that on his own that's why I put this:
    Code (vJASS):

        //What happens when a unit crosses the wall
        private function WallAction takes unit caster, unit target, integer level returns nothing
            call UnitDamageTarget(caster, target, 100.0*level, false, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_MAGIC, null)
            //You can also put all kinds of other stuffs here like stun/silence/root/create illusion of the target
        endfunction
     


    EDIT:
    However, upon reading the code again, I saw some other issues that needs fixing:
    - The distance check could be optimized by avoiding the SquareRoot.
    - Units outside the monitor range should be removed from the instance group (this.g).
    - local unit u in method destroy is unnecessary.
     
  3. zv27

    zv27

    Joined:
    Aug 21, 2010
    Messages:
    296
    Resources:
    0
    Resources:
    0
    This is the distance between the unit and the wall, right?
    Code (vJASS):

    private method distance takes real x, real y returns real
                return RAbsBJ(this.m*x - y + b)/SquareRoot(this.m*this.m + 1)
            endmethod
     

    If so, this func is stupid.It is very easy to avoid RAbsBJ and SquareRoot.
     
  4. AGD

    AGD

    Joined:
    Mar 29, 2016
    Messages:
    396
    Resources:
    13
    Spells:
    7
    Tutorials:
    1
    JASS:
    5
    Resources:
    13
    Code (vJASS):
                static if LIBRARY_RegisterPlayerUnitEvent then
                    static if LIBRARY_SpellEffectEvent then
                        call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
                    else
                        call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_SPELL_EFFECT, function thistype.cond)
                    endif
                else
                    local trigger t = CreateTrigger()
                    call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
                    call TriggerAddCondition(t, Condition(function thistype.cond))
                endif

    =>
    Code (vJASS):

                static if LIBRARY_SpellEffectEvent then
                    call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
                elseif RPUE_VERSION_NEW then // =)
                    call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_SPELL_EFFECT, function thistype.cond)
                elseif LIBRARY_RegisterPlayerUnitEvent then
                    call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_SPELL_EFFECT, function thistype.cond)
                else
                    local trigger t = CreateTrigger()
                    call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
                    call TriggerAddCondition(t, Condition(function thistype.cond))
                endif
     
  5. Flux

    Flux

    Joined:
    Feb 6, 2014
    Messages:
    2,334
    Resources:
    28
    Maps:
    1
    Spells:
    19
    Tutorials:
    2
    JASS:
    6
    Resources:
    28
    Last edited: Aug 25, 2017
  6. Aniki

    Aniki

    Joined:
    Nov 7, 2014
    Messages:
    517
    Resources:
    4
    Spells:
    1
    JASS:
    3
    Resources:
    4
    I think walls tend to have thickness, so I gues this is more like the "Line of Death" =).

    edit:

    The group check seems unnecessary:
    Code (vJASS):

    //if IsUnitInGroup(u, this.g) then
        set id = GetHandleId(u)
        set newPos = this.isAbove(x, y)
        //Compare unit position data
        static if LIBRARY_Table then
            if this.pos.boolean[id] != newPos then
                call WallAction(this.caster, u, this.lvl)
            endif
            set this.pos.boolean[id] = newPos
        else
            if LoadBoolean(thistype.hash, this, id) != newPos then
                call WallAction(this.caster, u, this.lvl)
            endif
            call SaveBoolean(thistype.hash, this, id, newPos)
        endif
    /*
    else
        call GroupAddUnit(this.g, u)
        //Set unit position data
        static if LIBRARY_Table then
            set this.pos.boolean[GetHandleId(u)] = this.isAbove(x, y)
        else
            call SaveBoolean(thistype.hash, this, GetHandleId(u), this.isAbove(x, y))
        endif
    endif
    */

     


    and the monitor check
    if this.distance(x, y) < MONITOR_RANGE then
    seems like an optimization?
     
    Last edited: Nov 26, 2016
  7. zv27

    zv27

    Joined:
    Aug 21, 2010
    Messages:
    296
    Resources:
    0
    Resources:
    0

    Since you're a bit lazy hahaha try something like this.

    Code (vJASS):

        //One solution from my "Wall System"
        //There are a whole bunch
        //But now I'm a little lazy to adding them all
        //This is the simplest but works perfectly.
        function IfTheUnitTouchesTheWall takes real x,real y,real x1,real y1,real x2,real y2,real x3,real y3,real x4,real y4 returns boolean
            local real c1=(y-y1)*(x2-x1)-(x-x1)*(y2-y1)
            local real c2=(y-y3)*(x1-x3)-(x-x3)*(y1-y3)
            local real c3=(y-y2)*(x3-x2)-(x-x2)*(y3-y2)
            local real c4=(y-y1)*(x4-x1)-(x-x1)*(y4-y1)
            local real c5=(y-y4)*(x3-x4)-(x-x4)*(y3-y4)
            return(c1*c2>0 and c3*c2>0)or(c4*c2>0 and c5*c2>0)
        endfunction
        //One example of how to use:
        //This(IfTheUnitTouchesTheWall) is not the original name, just an example.Call whatever you want.
        //coord. of the unit and the wall <->x0,y0,x1,y1,x2,y2,x3,y3,x4,y4
        //set this somewhere...blah...usually in a periodic func.
     
        set x0=GetUnitX(unit)//the unit is usually a fucking victim
        set y0=GetUnitY(unit)
        //when you define your wall usually have this:annoying coordinates
        set x1=WALL_X[1]
        set y1=WALL_Y[1]
        set x2=WALL_X[2]
        set y2=WALL_Y[2]
        set x3=WALL_X[3]
        set y3=WALL_Y[3]
        set x4=WALL_X[4]
        set y4=WALL_Y[4]
        set x5=WALL_X[5]
        set y5=WALL_Y[5]
        set x6=WALL_X[6]
        set y6=WALL_Y[6]
        if GetWidgetLife(unit)>.405 and IsUnitEnemy(unit,player)and IfTheUnitTouchesTheWall(x0,y0,x1,y1,x2,y2,x3,y3,x4,y4)then
            //some action here
        endif
     

     

    Maybe not a bad idea to share this system with the public ,would be a much better insight into how it works.Thus this is only a small segment
     
    Last edited: Nov 26, 2016
  8. Flux

    Flux

    Joined:
    Feb 6, 2014
    Messages:
    2,334
    Resources:
    28
    Maps:
    1
    Spells:
    19
    Tutorials:
    2
    JASS:
    6
    Resources:
    28
    Actually the idea came from here.

    It is because empty hash boolean values are false by default If I recall correctly. Therefore I must only compare if unit had previous data. I could still avoid the group using a check of HaveSavedBoolean before comparing so I guess I'll do that it the next update.

    What I had in mind was
    if this.distance(x, y) < MONITOR_RANGE then
    and
    return RAbsBJ(this.m*x - y + b)/SquareRoot(this.m*this.m + 1)

    ->
    if this.distance(x, y) < MONITOR_RANGE*MONITOR_RANGE then
    and
    return (this.m*x - y + b)*(this.m*x - y + b)/(this.m*this.m + 1)

    I think this is simpler than your implementation.
     
  9. zv27

    zv27

    Joined:
    Aug 21, 2010
    Messages:
    296
    Resources:
    0
    Resources:
    0
    You're probably right.Your version is definitely easier but it is very limited(limited to this spell).
    My version can be used anywhere, for anything.The point is:"If the unit touches something" (In your case, the wall) or anything else.
     
  10. Aniki

    Aniki

    Joined:
    Nov 7, 2014
    Messages:
    517
    Resources:
    4
    Spells:
    1
    JASS:
    3
    Resources:
    4
    Have you tried:

    Code (vJASS):

    private function WallLength takes integer level returns real
        return 256.0 //*level + 700.0
    endfunction

    private function Duration takes integer level returns real
        return 60.0 //*level + 10.0
    endfunction
     


    then walking a footman near a wall and then walk around the wall (not through) on to the other side of it? The result is that the footman gets "WallAction"ed even though he didn't cross/go through the wall.
     
  11. zv27

    zv27

    Joined:
    Aug 21, 2010
    Messages:
    296
    Resources:
    0
    Resources:
    0
    This is because the "distance" func totally incorrect.
    The problem is the following: Is not precisely defined border between the wall and unit.This is the main reason why I am saying that "dist" func is stupid.
    He uses GroupEnumUnit...which means that it uses a certain radius,which would also mean that within that radius something(actions) happens.That's where the problem arises.
     
    Last edited: Nov 27, 2016
  12. Flux

    Flux

    Joined:
    Feb 6, 2014
    Messages:
    2,334
    Resources:
    28
    Maps:
    1
    Spells:
    19
    Tutorials:
    2
    JASS:
    6
    Resources:
    28
    Nope that's not it, don't make claims that is not true. The reason that happens is I forgot to do this:
    Or if I'm not going to use a group, the position boolean data should be removed.
     
  13. zv27

    zv27

    Joined:
    Aug 21, 2010
    Messages:
    296
    Resources:
    0
    Resources:
    0
    I do not agree with this.
     
  14. Flux

    Flux

    Joined:
    Feb 6, 2014
    Messages:
    2,334
    Resources:
    28
    Maps:
    1
    Spells:
    19
    Tutorials:
    2
    JASS:
    6
    Resources:
    28
    My working update do not agree with this.

    EDIT:
    v1.11 - [14 December 2016]
    - Fixed possible bug for units instantly moving in and out of the search radius.
     
    Last edited: Dec 13, 2016
  15. KILLCIDE

    KILLCIDE

    Administrator

    Joined:
    Jul 22, 2015
    Messages:
    3,489
    Resources:
    20
    Models:
    2
    Icons:
    10
    Spells:
    7
    Tutorials:
    1
    Resources:
    20
    Simple spell in terms of concept, but execution is definitely challenging. I am unable to verify the math involved in calculating whether or not a unit is in the segment, but I trust the community will eventually point it out. The
    WallFunction()
    function gives users the freedom to decide what happens to units when they cross the wall without diving deep into the code, which is a huge plus from me.

    Needs Fixed

    • Add a proper ingame tooltip. I apologize for being picky about this, but I'm trying to keep some consistency with our database

    Suggestions

    • If you really wanted to, I don't see anything wrong with storing
      Cos(angle)
      and
      Sin(angle)
      into locals onCast to avoid calculating an extra time
    • In the TargetFilter function, you constantly call
      GetOwningPlayer()
      . Why not just store it into a variable before the
      FirstOfGroup()
      loop, and then pass it as a parameter? Alternatively, you can store the owner onCast and turn the function into a method. Either way works, the preference is just how you would want a "mind control" mechanic to work.
    • Instead of creating the effect on the point, I would attach it to a dummy unit. I know this adds extra clutter, but some effects don't look great on just the ground. It also gives users the freedom to scale the size of the effect
    • I normally don't have a problem with
      static ifs
      , but given you always make libraries optional with your spells, they really stack up and make the code a lot harder to read

    Status


    Approved
     
  16. Flux

    Flux

    Joined:
    Feb 6, 2014
    Messages:
    2,334
    Resources:
    28
    Maps:
    1
    Spells:
    19
    Tutorials:
    2
    JASS:
    6
    Resources:
    28
    It uses distance of a point to a line formula and stores whether the picked unit is above or below the line in a hashtable (or Table) as a boolean (true if above, false if below). If that changes while at the same time maintaining the short distance within the line, then WallFunction() is called.

    I actually support this, I just forgot to do it. Will do this when I have the time.

    I knew someone will eventually point this out. In my opinion it does not matter (it only runs one per cast) but if I would ever do it, I would store this.radius*Cos(angle) and this.radius*Sin(angle) to also avoid repeating multiplication.

    Exactly, this guy gets it. Since I'll update this, maybe I'll add a
    private constant boolean CHANGE_WITH_OWNER = true/false
    feature so that users can easily configured whether the Wall will also change owner when the caster changes owner.

    Good suggestion.

    I only make libraries optional if its a public resource. But if it's for a map, I uses dependencies to its full capacity. Yeah I know it make it harder to read, but once you get the hang of it, it's not that hard.
     
  17. KILLCIDE

    KILLCIDE

    Administrator

    Joined:
    Jul 22, 2015
    Messages:
    3,489
    Resources:
    20
    Models:
    2
    Icons:
    10
    Spells:
    7
    Tutorials:
    1
    Resources:
    20
    You just went the extra mile :D

    Even better! I'll need to do this in the future as well.
     
  18. Flux

    Flux

    Joined:
    Feb 6, 2014
    Messages:
    2,334
    Resources:
    28
    Maps:
    1
    Spells:
    19
    Tutorials:
    2
    JASS:
    6
    Resources:
    28
    Updated!
    v1.12 - [21 January 2017]
    - Added WallSfx scaling feature.
    - Added CHANGE_WITH_OWNER feature.
    - Fixed in-game tooltip.

    Turns out this was wrong because there is
    set angle = angle + bj_PI
    in between them. The other Cos(angle) and Sin(angle) is a completely different value.
     
    Last edited: Jan 21, 2017