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

Wall of Death v1.12

JASS:
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


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()...
Level 22
Joined
Feb 6, 2014
Messages
2,466
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. :)
Visual effect or spell-mechanics effects? Nevertheless, a user can do that on his own that's why I put this:
JASS:
    //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.
 
Level 10
Joined
Aug 21, 2010
Messages
316
This is the distance between the unit and the wall, right?
JASS:
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.
 

AGD

AGD

Level 16
Joined
Mar 29, 2016
Messages
688
JASS:
            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
=>
JASS:
            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
 
Level 13
Joined
Nov 7, 2014
Messages
571
I think walls tend to have thickness, so I gues this is more like the "Line of Death" =).

edit:

The group check seems unnecessary:
JASS:
//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:
Level 10
Joined
Aug 21, 2010
Messages
316
If it looks stupid but it works it ain't stupid. Besides, I didn't have enough time to check the code. I thought it was pretty obvious that I usually submit spells without checking it thoroughly (unless it's for a Contest). Checking for errors and room for optimization is one of the boring part of making spells next to fixing the Object Editor stuffs in my opinion. I usually just submit these quick spells for the fun and challenge in making the core algorithm of the spell (like in this example, the math part of comparing whether a unit crosses the wall).!


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

JASS:
    //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:
Level 22
Joined
Feb 6, 2014
Messages
2,466
I think walls tend to have thickness, so I gues this is more like the "Line of Death" =).
Actually the idea came from here.

The group check seems unnecessary:
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.

Since you're a bit lazy hahaha try something like this.
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.
 
Level 10
Joined
Aug 21, 2010
Messages
316
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.

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.
 
Level 13
Joined
Nov 7, 2014
Messages
571
Have you tried:

JASS:
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.
 
Level 10
Joined
Aug 21, 2010
Messages
316
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.
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:
Level 22
Joined
Feb 6, 2014
Messages
2,466
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.
Nope that's not it, don't make claims that is not true. The reason that happens is I forgot to do this:
Units outside the monitor range should be removed from the instance group (this.g).
Or if I'm not going to use a group, the position boolean data should be removed.
 
Level 37
Joined
Jul 22, 2015
Messages
3,485
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
 
Level 22
Joined
Feb 6, 2014
Messages
2,466
I am unable to verify the math involved in calculating whether or not a unit is in the segment
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.

Add a proper ingame tooltip. I apologize for being picky about this, but I'm trying to keep some consistency with our database
I actually support this, I just forgot to do it. Will do this when I have the time.

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
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.

the preference is just how you would want a "mind control" mechanic to work.
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.

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
Good suggestion.

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
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.
 
Level 37
Joined
Jul 22, 2015
Messages
3,485
store this.radius*Cos(angle) and this.radius*Sin(angle) to also avoid repeating multiplication.
You just went the extra mile :D

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.
Even better! I'll need to do this in the future as well.
 
Level 22
Joined
Feb 6, 2014
Messages
2,466
Updated!
v1.12 - [21 January 2017]
- Added WallSfx scaling feature.
- Added CHANGE_WITH_OWNER feature.
- Fixed in-game tooltip.

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
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:
Top