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

Region Elevation System v1.00 Beta1

This is a simple system that allows units to "levitate" above ground in desired regions. It can be used for many purposes, stairs or platforms to name a few.

Full documentation provided in the Map. Please have a glimpse at the documentation before trying the system out to prevent unwelcome surprises and otherwise.

Features:
Primary functionality
- Changes units' heights when they enter an elevation region.
- Controls the rising/falling speed for elevator effect.
- Basic dynamic creation of regions and modification of height/speed parameters.

Jass version
- Primary functionality only (no region overlap).
- GUI Interface.

vJass version
- Primary functionality.
- Advanced functionality (composite regions, overlap support, trigger recycling).

Limitations:
- Difficult to manage dynamic operations such as adding/removing/setting properties for regions. I designed basic support for those but it's not designed for complex stuff (i.e overlapping composite region with rects removed/added with units on it at run time will probably glitch).
- Counter intuitive for Move Clicks, because its not a real terrain or platform, but virtual. If you have a tower like in my example map, then clicking on the top of the tower will move the unit behind the tower, rather than on it. To move on the tower, click on the base of the tower (because that's where the real terrain and region is). Hope that was clear enough to explain this problem.
- Non flat terrain region will produce weird results, because of that it's not very useful in a lot of situations.


WAIT A MINUTE, YOUR SYSTEM IS OBSOLETE!
Why not just use destructable platforms with IsWalkable turned on?
(its much more hot too!!!)

Any desctructable will gobble FPS (in small amounts) it is best to avoid them. Someone actually had 2000+ destructables in his map and had it unplayable.

Walkable destructables are a special case, because it only takes a few to screw up the wc3 engine. I had a map with only a few walkable destructables that covered about 5% of the map, and the game was unplayable with 30 units on screen in some areas. I suspect each walkable destructable creates a region that checks if ALL the units in the map are in it or not, every 0.1 seconds or so, and adjust their height accordingly if they are.

The formula for the FPS reduction is something like that:
alot of units + some walkables + units moving = LAGZ0R!

Conclusion: Only use destructables when (1) you want a 'doodad' to be interfaced with at run-time (i.e added/removed) (2) when you want a walkable bridge/platform (i.e wc3 bridges) and there is no other option but to use walkable destructables (3) when you don't care about performance, or you have a map with very few units.


Anything else I need to know?

I tried to put everything I relevant I know about this system in the documentation, and this post. Look in the other sections or the documentation.

If you are only going to read one thing, read the "Caveats and Bugs" section.


The test map won't save :(

You probably tried to save under the classic wc3 editor. Erase the test map and re-download it, then run it without saving. If you have JNGP then use the advanced editor to save the map.

Alternatively, just delete the vJass triggers if you don't need them and save.


Will you make a multi floor system out of this?

No.

Actually maybe, but it will have more worms in it than your nearest fishing bait store.


Why?

This a very complicated question, the answer to that probably states the meaning of life, as well as some of the secrets of the cosmos.

I cannot answer this question, ask your local God.

You may have tried to ask a more specific question, please try again with "Why + the rest of a question + ?"

I'm such an ass -.-


Is there anything else you would like to say?



Please take a moment of silence for my sacrifice of 30 minutes here, and yet another proof that without type safety, we would probably be all dead. No joke.





CAVEATS:
- Only usable in 1.24 and above.
- Uses 1 Global Hashtable (Might want to merge it with an existing one in the target map).
- The terrain in the levitation region should be a flat plane, or the units will move in a funky manner.
-
JASS:
 Overlapping regions will glitch. [vJass] Will glitch if the overlap feature of those regions is turned off.
- Removing the rect variables of the elevation regions before destroying the levitation region will cause leaks and/or glitches.
- [code=jass] Destroying the LSGroup() UnitGroup will glitch the system.
- System might behave unpredictably in conjunction with other functions that modify unit heights unless those functions are designed well enough to accommodate this system.

- Those bj variables are used by the system in certain functions (Jass only)
bj_isUnitGroupInRectRect 
bj_enumDestructableRadius

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

BUGS:
- Flying units will have their selection circle shifted down for some reason, If anyone knows how to solve this please let me know.
- Small leak on Jass version when destroying regions (due to DestroyTrigger())
- Overlap feature and dynamic setting of region properties (such as height, activation/deactivation) do not work. The system only supports the basic, non-overlap approach on those cases.

[/HIDDEN]

[HIDDEN="Example Screenshots "]
[URL=http://img16.imageshack.us/i/asdasdasdadasdlllllllll.jpg/][IMG]http://img16.imageshack.us/img16/8365/asdasdasdadasdlllllllll.jpg[/IMG][/URL]


[URL=http://img27.imageshack.us/i/wc3scrnshot033010211629.jpg/][IMG]http://img27.imageshack.us/img27/8265/wc3scrnshot033010211629.jpg[/IMG][/URL]


[URL=http://img413.imageshack.us/i/stresstest.jpg/][IMG]http://img413.imageshack.us/img413/9162/stresstest.jpg[/IMG][/URL]
(40 fps is my regular fps with that many units on screen)
[/HIDDEN]

[HIDDEN="Algorithms Used"]
Primary Functionality:
Jass
-The System takes a region, and a desired levitation height and entry/leave levitation speed.
-The System binds those values together and creates 2 triggers.
-The triggers will handle the enter/leave events for the region in question.
-When a unit enters the region, then the system will add/remove dummy flying ability and set the unit flying height to the specified height.
-When a unit leaves the region, then the system will reset the unit flying height to normal.

vJass
- The system creates a ElevationRegion Object from a target rect variable.
- The ElevationRegion Object manages the levitation height and entry/leave levitation speed.
- The ElevationRegion Object also manages 2 triggers responsible for entry/leave, but since there's no way to have a "GetTriggeringObject()" function, the object is bound to an outside hashtable which is accessed by "GetTriggeringRegion()".
- When a unit enters/leaves the LevitationRegion Object bounds, the Object will apply the height transformations by calling library functions.

Overlap (vJass Only):
- The overlap variable is a boolean field that exist in every ElevationRegion Object, and is turned on by default.
- The system will keep track of a list of regions a unit has entered (keep adding as entering, and removing as leaving). The list is then put into a hashtable and is accessed by unit handles. This is done per unit and because of that, I advise to turn overlap off if the ElevationRegion in question does not overlap other ElevationRegions.
- When a unit leaves a ElevationRegion, the system will find the next highest ElevationRegion that the unit is in, and apply it's height.
- Higher ElevationRegions have priority over lower ones.
- When a unit leaves a ElevationRegion and the unit's list is empty, then It's hashtable node is purged and freed, and height is set back to default.
[/HIDDEN]

[HIDDEN="Changelog & TODOs"]
1.00 beta 1 - restructured the vJass code, vJass supports composite regions, added trigger recycling (vJass only), fixed a potential bug when setting/resetting/destroying regions. Added extra documentation.
0.99b - [vJass] Fixed a possible major leak when destroying lists. Both Jass and vJass versions are now running at the same time. Updated some documentation.
0.99 - vJass version supports universal region overlap.
0.98c - Major bug fix with 2 of the vJass functions.
0.98b - Added a vJass version.
0.98 - Added more interface functions, improved documentation, added new feature - control the levitation rising/falling speed.
0.97c - Fixed a minor leak by recycling a unit group.
0.97b - Fixed a minor leak with a region when destroying a Levitation Region, did a refraction on some of  the function names for clarity.
0.97 - AntiBJ and some other minor fixes.
0.96b - Added another example, a screen-shot, and description of how it works.
0.96 - Fixed a lot of outdated text and typos in the documentation. Map description, map name & version names changed to better describe the system. Mentioned a bug I forgot about.
0.95 - Initial Release.

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

TODO:
[vJass] add non-flat terrain elevation support option (will be CPU intensive).
[/HIDDEN]


Release Notes:
The core functionality of the system is long finished, the beta1 stands for the advanced features which I felt like adding. It's very hard to have a comprehensive test for those features alone, so I put the beta tag on it. If there are no issues identified from mods/users during some period then i will deem the final version.

I might add one more feature for vJass:
Add non-flat terrain elevation support option (will be CPU intensive).

[b]Keywords:[/b]
Levitation, Levitate, Elevation, Elevator, Elevate, Fly, Flying, Hover, Terrain, Platform, Bridge, Walkable, IsWalkable
Contents

Region Levitation System v1.00 beta1 (Map)

Reviews
11:53, 31st Mar 2010 The_Reborn_Devil: The code looks pretty good and this is really useful. Status: Approved Rating: Recommended

Moderator

M

Moderator

11:53, 31st Mar 2010
The_Reborn_Devil:
The code looks pretty good and this is really useful.

Status: Approved
Rating: Recommended
 
Level 6
Joined
Jan 17, 2010
Messages
149
The code


Put under trigger named "Levitation System JassGui"
JASS:
//===============================================
// System Configuration
//===============================================

//-----------------------------------------------
// Dummy Fly Ability for ground units
//
// This ability is needed to make ground units appear
// flying.
//-----------------------------------------------

constant function LS_DUMMY_FLY_ABILITY takes nothing returns integer
    return 'A000'
endfunction

//-----------------------------------------------
// Pointer to the system hashtable
//
// Example: hashtable "udg_LevitationVars".
// Replace with desired global hashtable here.
//-----------------------------------------------

function LS_DATABASE takes nothing returns hashtable
    return udg_LevitationVars
endfunction

function CreateLeviSystemTable takes nothing returns nothing  
    set udg_LevitationVars = InitHashtable()
endfunction

//-----------------------------------------------
// Static keys
//
// Must be UNIQUE integers and not overlap
// with other table PARENT keys.
//-----------------------------------------------

// parent keys
constant function LS_TRIGGER_KEY takes nothing returns integer
    return 1
endfunction

constant function LS_REGION_KEY takes nothing returns integer
    return 2
endfunction

constant function LS_REGION_E_TRIGGER_KEY takes nothing returns integer
    return 3
endfunction

constant function LS_REGION_L_TRIGGER_KEY takes nothing returns integer
    return 4
endfunction

constant function LS_REGION_RECT_KEY takes nothing returns integer
    return 5
endfunction

constant function LS_UNIT_KEY takes nothing returns integer
    return 6
endfunction

constant function LS_ELEVATION_SPEED_KEY takes nothing returns integer
    return 7
endfunction

constant function LS_UTIL_VARIABLES_KEY takes nothing returns integer
    return 8
endfunction

// child keys
constant function LS_GROUP_VAR_KEY takes nothing returns integer
    return 1
endfunction

constant function LS_LOCATION_VAR_KEY takes nothing returns integer
    return 2
endfunction

// constant dummy
constant function LS_TRUE takes nothing returns boolean
   return true
endfunction

//===============================================
// System Core components
//===============================================

//-----------------------------------------------
// DB interface
//
// Do not modify or use without full understanding of the
// system's operations.
//-----------------------------------------------

// -- binds a region with 2 triggers
function LSSetRegion takes rect r,trigger t, trigger t2 returns nothing 
     call SaveRectHandle(LS_DATABASE(),GetHandleId(t),LS_REGION_KEY(),r)
     call SaveTriggerHandle(LS_DATABASE(),GetHandleId(r),LS_REGION_E_TRIGGER_KEY(),t)
     call SaveTriggerHandle(LS_DATABASE(),GetHandleId(r),LS_REGION_L_TRIGGER_KEY(),t2)  
endfunction

// -- returns an elevation region controlled by the trigger
function LSGetRegion takes trigger t returns rect
    return LoadRectHandle(LS_DATABASE(),GetHandleId(t),LS_REGION_KEY())
endfunction

// -- getters for elevation triggers attached to region
function LSGetEnterTriggerOfRegion takes rect r returns trigger
    return LoadTriggerHandle(LS_DATABASE(),GetHandleId(r),LS_REGION_E_TRIGGER_KEY())
endfunction

function LSGetLeaveTriggerOfRegion takes rect r returns trigger
    return LoadTriggerHandle(LS_DATABASE(),GetHandleId(r),LS_REGION_L_TRIGGER_KEY())
endfunction


// -- region elevation height accessors
function LSSetRegionHeightByTrigger takes trigger t, real h returns nothing
    call SaveReal(LS_DATABASE(),GetHandleId(t),LS_TRIGGER_KEY(),h)
endfunction

function LSGetRegionHeightByTrigger takes trigger t returns real
    return LoadReal(LS_DATABASE(),GetHandleId(t),LS_TRIGGER_KEY())
endfunction

function LSSetRegionHeight takes rect r, real h returns nothing
     call LSSetRegionHeightByTrigger(LSGetEnterTriggerOfRegion(r),h)
endfunction

function LSGetRegionHeight takes rect r returns real
     return LSGetRegionHeightByTrigger(LSGetEnterTriggerOfRegion(r))
endfunction

// -- region elevation speed accessors
function LSGetRegionElevationSpeedByTrigger takes trigger t returns real
    return LoadReal(LS_DATABASE(),GetHandleId(t),LS_ELEVATION_SPEED_KEY())
endfunction

function LSSetRegionElevationSpeedByTrigger takes trigger t, real s returns nothing
    call SaveReal(LS_DATABASE(),GetHandleId(t),LS_ELEVATION_SPEED_KEY(),s)
endfunction

function LSGetRegionRisingElevationSpeed takes rect r returns real
    return LSGetRegionElevationSpeedByTrigger(LSGetEnterTriggerOfRegion(r))
endfunction

function LSSetRegionRisingElevationSpeed takes rect r, real s returns nothing
    call LSSetRegionElevationSpeedByTrigger(LSGetEnterTriggerOfRegion(r),s)
endfunction

function LSGetRegionFallingElevationSpeed takes rect r returns real
    return LSGetRegionElevationSpeedByTrigger(LSGetLeaveTriggerOfRegion(r))
endfunction

function LSSetRegionFallingElevationSpeed takes rect r, real s returns nothing
    call LSSetRegionElevationSpeedByTrigger(LSGetLeaveTriggerOfRegion(r),s)
endfunction

// -- dummy region creation
function LSCreateRegionFromRect takes rect r,region r1 returns nothing 
     call RegionAddRect(r1,r)
     call SaveRegionHandle(LS_DATABASE(),GetHandleId(r),LS_REGION_RECT_KEY(),r1)
endfunction

function LSGetRegionFromRect takes rect r returns region
    return LoadRegionHandle(LS_DATABASE(),GetHandleId(r),LS_REGION_RECT_KEY())
endfunction

// -- cleanups
function LSFlushTriggerData takes trigger t returns nothing
    call SaveReal(LS_DATABASE(),GetHandleId(t),LS_TRIGGER_KEY(),0)
    call SaveRectHandle(LS_DATABASE(),GetHandleId(t),LS_REGION_KEY(),null)
    call FlushChildHashtable(LS_DATABASE(),GetHandleId(t))
endfunction

function LSFlushRegionData takes rect r returns nothing
    call SaveTriggerHandle(LS_DATABASE(),GetHandleId(r),LS_REGION_E_TRIGGER_KEY(),null)
    call SaveTriggerHandle(LS_DATABASE(),GetHandleId(r),LS_REGION_L_TRIGGER_KEY(),null)
    call FlushChildHashtable(LS_DATABASE(),GetHandleId(r))
endfunction

// group recycle
function LSGroup takes nothing returns group
     return LoadGroupHandle(LS_DATABASE(),LS_UTIL_VARIABLES_KEY(),LS_GROUP_VAR_KEY())
endfunction



//-----------------------------------------------
// Setting unit heights
//
// Those methods are used by the system, do not
// modify without full knowledge of the system's
// operations.
//
// Can probably apply those methods outside the
// system as well.
//-----------------------------------------------

//-----------------------------------------
// Height modification for single units
//-----------------------------------------

function LSResetUnitHeight takes unit u returns nothing
    if not IsUnitType(u, UNIT_TYPE_FLYING) then
        call UnitAddAbility(u, LS_DUMMY_FLY_ABILITY())
        call UnitRemoveAbility(u, LS_DUMMY_FLY_ABILITY())
    endif
    call SetUnitFlyHeight(u,GetUnitDefaultFlyHeight(u),0)
endfunction

function LSSetUnitHeight takes unit u, real h returns nothing
    if not IsUnitType(u, UNIT_TYPE_FLYING) then
        call UnitAddAbility(u, LS_DUMMY_FLY_ABILITY())
        call UnitRemoveAbility(u, LS_DUMMY_FLY_ABILITY())
    endif
    call SetUnitFlyHeight(u,GetUnitDefaultFlyHeight(u)+h,0)
endfunction

function LSResetUnitHeightTimed takes unit u, real speed returns nothing
    if not IsUnitType(u, UNIT_TYPE_FLYING) then
        call UnitAddAbility(u, LS_DUMMY_FLY_ABILITY())
        call UnitRemoveAbility(u, LS_DUMMY_FLY_ABILITY())
    endif
    call SetUnitFlyHeight(u,GetUnitDefaultFlyHeight(u),speed)
endfunction

function LSSetUnitHeightTimed takes unit u, real h, real speed returns nothing
    if not IsUnitType(u, UNIT_TYPE_FLYING) then
        call UnitAddAbility(u, LS_DUMMY_FLY_ABILITY())
        call UnitRemoveAbility(u, LS_DUMMY_FLY_ABILITY())
    endif
    call SetUnitFlyHeight(u,GetUnitDefaultFlyHeight(u)+h,speed)
endfunction


//-----------------------------------------
// Height modification for all units in rect
//-----------------------------------------

function LS_RESETHEIGHTENUM takes nothing returns nothing
    call LSResetUnitHeight(GetEnumUnit())
endfunction
function LSResetAllUnitsHeight takes rect r returns nothing
    local group g = LSGroup()
    local conditionfunc LSAlwaysTrue = Condition(function LS_TRUE)
    call GroupEnumUnitsInRect(g,r,LSAlwaysTrue )
    call ForGroup( g, function LS_RESETHEIGHTENUM )
    call DestroyBoolExpr(LSAlwaysTrue)
    set LSAlwaysTrue = null 
    set g = null
endfunction

function LS_SETHEIGHTENUM takes nothing returns nothing
    call LSSetUnitHeight(GetEnumUnit(),bj_enumDestructableRadius)
endfunction
function LSSetAllUnitsHeight takes rect r, real h returns nothing
    local group g = LSGroup()
    local conditionfunc LSAlwaysTrue = Condition(function LS_TRUE)
    set bj_enumDestructableRadius = h
    call GroupEnumUnitsInRect(g,r, LSAlwaysTrue )
    call ForGroup( g, function LS_SETHEIGHTENUM )
    call DestroyBoolExpr(LSAlwaysTrue)
    set LSAlwaysTrue = null
    set g = null 
endfunction


//-----------------------------------------------
// TriggerActions for region entry/leave
//
// Used by the triggers responsible for elevation.
//-----------------------------------------------

function Unit_Enteres_LS_Region_Actions takes nothing returns nothing
    call LSSetUnitHeightTimed(GetTriggerUnit(),LSGetRegionHeightByTrigger(GetTriggeringTrigger()),LSGetRegionElevationSpeedByTrigger(GetTriggeringTrigger()))
endfunction

function Unit_Leaves_LS_Region_Actions takes nothing returns nothing
    call LSResetUnitHeightTimed(GetTriggerUnit(),LSGetRegionElevationSpeedByTrigger(GetTriggeringTrigger())) 
endfunction


//===============================================
// System Front End Interface
//===============================================
//
// create/add/remove/destroy/enable/disable
// levitation regions
//
// Warning: must call LSAddRegion() or LSCreateRegion()
// before doing any other operations.
//
// Parameters:
//
// Region r - the region that is to be used for levitation.
// Real h - the levitation height of a region.
//
// Real x1,y1,x2,y2 - used for creating regions.
//
//-----------------------------------------------

// Toggle unit enters levitation region event on or off
function LSToggleRegionEnterEvent takes rect r, boolean b returns nothing
    local trigger t = LSGetEnterTriggerOfRegion(r)
    if (b) then    
        call EnableTrigger(t)
        call LSSetAllUnitsHeight(r,LSGetRegionHeightByTrigger(t))
    else
        call DisableTrigger(t)
    endif 
    set t = null
endfunction

// Toggle unit leaves levitation region event on or off
function LSToggleRegionLeaveEvent takes rect r, boolean b returns nothing
    local trigger t = LSGetLeaveTriggerOfRegion(r)
    if (b) then
        call EnableTrigger(t)
    else
        call DisableTrigger(t)
    endif 
    set t = null
endfunction

// Turn levitation region on
function LSEnableRegion takes rect r returns nothing
     call EnableTrigger(LSGetEnterTriggerOfRegion(r))
     call EnableTrigger(LSGetLeaveTriggerOfRegion(r))
     call LSSetAllUnitsHeight(r,LSGetRegionHeight(r))
endfunction

// Turn levitation region off
function LSDisableRegion takes rect r returns nothing
     call DisableTrigger(LSGetEnterTriggerOfRegion(r))
     call DisableTrigger(LSGetLeaveTriggerOfRegion(r))
     call LSResetAllUnitsHeight(r) 
endfunction

function LSRegionIsOn takes rect r returns boolean
     return IsTriggerEnabled(LSGetEnterTriggerOfRegion(r)) or IsTriggerEnabled(LSGetLeaveTriggerOfRegion(r))
endfunction

function LSRegionEnterTriggerIsOn takes rect r returns boolean
     return IsTriggerEnabled(LSGetEnterTriggerOfRegion(r))
endfunction

function LSRegionLeaveTriggerIsOn takes rect r returns boolean
     return IsTriggerEnabled(LSGetLeaveTriggerOfRegion(r))
endfunction


//--------------------------------------


// Set levitation region levitation height
function LSSetHeight takes rect r, real h returns nothing
     call LSDisableRegion(r)
     call LSSetRegionHeightByTrigger(LSGetEnterTriggerOfRegion(r),h)
     call LSSetRegionHeightByTrigger(LSGetLeaveTriggerOfRegion(r),h)
     call LSEnableRegion(r)
endfunction

function LSGetHeight takes rect r returns real
     return LSGetRegionHeight(r)
endfunction

function LSSetRisingSpeed takes rect r, real s returns nothing
     call LSSetRegionRisingElevationSpeed(r,s)
endfunction

function LSSetFallingSpeed takes rect r, real s returns nothing
    call LSSetRegionFallingElevationSpeed(r,s)
endfunction

function LSGetRisingSpeed takes rect r returns real
     return LSGetRegionRisingElevationSpeed(r)
endfunction

function LSGetFallingSpeed takes rect r returns real
     return LSGetRegionFallingElevationSpeed(r)
endfunction


// destroys a levitation region (does not actually destroy the
// region itself)
function LSDestroyRegion takes rect r returns nothing
     local trigger t = LSGetEnterTriggerOfRegion(r) 
     local trigger t2 = LSGetLeaveTriggerOfRegion(r)
     local region r1 = LSGetRegionFromRect(r)

     call LSFlushTriggerData(t)
     call LSFlushTriggerData(t2)
     call LSFlushRegionData(r)

     call DisableTrigger(t)
     call DisableTrigger(t2)

     call DestroyTrigger(t)
     call DestroyTrigger(t2)

     call RemoveRegion(r1)

     call LSResetAllUnitsHeight(r)

     set r1 = null
     set t = null
     set t2 = null
endfunction

// adds and enables region for levitation
function LSAddRegion takes rect r, real h returns nothing
     local trigger t1 = CreateTrigger()
     local trigger t2 = CreateTrigger()
     local region r1 = CreateRegion()
     
      // events here
     
     call LSCreateRegionFromRect(r,r1) // also defines r1 from r
     call TriggerRegisterEnterRegion(t1,r1,null)
     call TriggerRegisterLeaveRegion(t2,r1,null)
  
     // conditions here
    
     // trigger actions here
     call TriggerAddAction( t1, function Unit_Enteres_LS_Region_Actions )
     call TriggerAddAction( t2, function Unit_Leaves_LS_Region_Actions )

     call LSSetRegion(r,t1,t2) 
     call LSSetRegionHeightByTrigger(t1,h)
     call LSSetRegionHeightByTrigger(t2,h)
     call LSSetRegionElevationSpeedByTrigger(t1,0)
     call LSSetRegionElevationSpeedByTrigger(t2,0)

     call LSEnableRegion(r)
     
     set r1 = null
     set t1 = null
     set t2 = null
endfunction


//creates a region for levitation
function LSCreateRegion takes real x1, real y1,real x2, real y2, real h returns rect
     set bj_isUnitGroupInRectRect = Rect(x1, y1, x2, y2)
     call LSAddRegion(bj_isUnitGroupInRectRect,h)
     return bj_isUnitGroupInRectRect
endfunction



//===========================================================================
function InitTrig_Levitation_System_JassGui takes nothing returns nothing
    local group g = CreateGroup() 
    call CreateLeviSystemTable()
    call SaveGroupHandle(LS_DATABASE(),LS_UTIL_VARIABLES_KEY(),LS_GROUP_VAR_KEY(),g)
    set g = null
endfunction



JASS:
scope ElevationSystem initializer ElevationSystemInit
    function ElevationSystemInit takes nothing returns nothing
        call ElevationRegionMemoryManager.initStack()
    endfunction

    function db takes string s returns nothing
        call BJDebugMsg(s)
    endfunction
    
    // -----------------------------------------------
    // Constants for designer/mapper configuration
    // -----------------------------------------------
    struct ElevationSystemConstants
        // dummy ability for flying
        static integer FlyAbility = 'Amrf'
         
        // hashtable used by the LS memory manager.
        // merging with existing table: 
        // hashtable LSHashtable = your_hashtable_variable
        static hashtable Hashtable = InitHashtable()
         
        // group used to save some memory by recycling.
        // merging with existing recycle group: 
        // group LSDummyGroup = your_group_variable
        // Do not destroy this value.
        static group DummyGroup = CreateGroup()
         
        // this is the a value for determening 
        // if a unit is inside a rect ( partial
        // containing does not count) so this value 
        // extends the rect in the 'insiderect'
        // calculation. if this value is too small, then 
        // a glitch  is possible when resetting/setting a region's
        // height. (a unit will have it's height shifted
        // but it won't be considered completly inside the 
        // region for setting/resetting heights )
         
        // I recommend a value of 32.
        static real MaxUnitCollision = 32
         
        static real HeightEnum = 0
         
        static method ALWAYS_TRUE takes nothing returns boolean
            return true
        endmethod
    endstruct
        
        // -----------------------------------------------
    // The ElevationRegion Class 
    // -----------------------------------------------
    struct ElevationRegion

         public boolean overlap
         
         private real Height
         private real EntrySpeed
         private real LeaveSpeed
         
         private region ElevationArea
         private ElevationRegionRectList RectList
         
         private trigger EntryTrigger
         private trigger LeaveTrigger
         private boolean locked
         
         // -----------------------------------------------------------------
         // Constructors/Destructors
         // -----------------------------------------------------------------
         // constructor requires a base region
         static method create takes rect targetRect, real h returns ElevationRegion
            local ElevationRegion e = ElevationRegion.new()
            call e.addRect(targetRect)
            call e.setHeight(h)
            return e
         endmethod
         
         // constructor without a base region
         static method new takes nothing returns ElevationRegion
            local ElevationRegion e = ElevationRegion.allocate()
            call ElevationRegionMemoryManager.allocTriggers(e) // assigns trigger values
            call e.lock()
            call e.createRectList()
            call e.setHeight(0)
            call e.setEntrySpeed(0)
            call e.setLeaveSpeed(0)    
            call e.setOverlap(true)    
            return e
         endmethod
         
         // destructors
         private method DestroyElevationRegion takes nothing returns nothing
                local integer i = 0
                local rect r
                
                loop
                    exitwhen i > .RectList.size
                        set r =  .RectList.get(i)
                        if(r!=null) then
                            call RegionClearRect(.ElevationArea,r)
                        endif
                    set  i = i + 1
                endloop
                
                call ElevationRegionActionManager.updateElevationRegion(this)        
                call ElevationRegionMemoryManager.freeTriggers(this) // frees up the triggers
                
                call .setHeight(0)
                call .RectList.destroy()
                set .ElevationArea = null
                set .EntryTrigger = null
                set .LeaveTrigger = null
                set r = null  
         endmethod
         method onDestroy takes nothing returns nothing
            call .DestroyElevationRegion()
         endmethod
         
         // ----------------------------------------------------------
         // accessors (could prolly have a nice macro here)
         // ----------------------------------------------------------
         method setEntrySpeed takes real s returns nothing
            set .EntrySpeed = s
         endmethod
         method getEntrySpeed takes nothing returns real
           return .EntrySpeed
         endmethod  
         method setLeaveSpeed takes real s returns nothing
            set .LeaveSpeed =  s
         endmethod
         method getLeaveSpeed takes nothing returns real
           return .LeaveSpeed
         endmethod   
         method setHeight takes real h returns nothing 
               set .Height = h
               call ElevationRegionActionManager.updateElevationRegion(this)
         endmethod   
         method getHeight takes nothing returns real
           return .Height
         endmethod 
         
         method lock takes nothing returns nothing
            set .locked = true
         endmethod
         method setRegion takes region r returns nothing
            set .ElevationArea =  r
         endmethod
         method getRegion takes nothing returns region
           return .ElevationArea
         endmethod  
         method setEntryTrigger takes trigger t returns nothing
            if(.locked == false) then
                set .EntryTrigger =  t
            endif
         endmethod
         method getEntryTrigger takes nothing returns trigger
           return .EntryTrigger
         endmethod  
         method setLeaveTrigger takes trigger t returns nothing
            if(.locked == false) then
                set .LeaveTrigger =  t
            endif
         endmethod
         method getLeaveTrigger takes nothing returns trigger
           return .LeaveTrigger
         endmethod  
         
         // -----------------------------------------------------------------
         // overlap boolean
         // -----------------------------------------------------------------          
         method setOverlap takes boolean b returns nothing
            set .overlap = b
         endmethod
         
         // -----------------------------------------------------------------
         // composite region list
         // -----------------------------------------------------------------  
         method createRectList takes nothing returns nothing
            set .RectList = ElevationRegionRectList.create()
         endmethod
         method getRectList takes nothing returns ElevationRegionRectList
            return .RectList
         endmethod
         method addRect takes rect r returns nothing
            if(r!=null) then
                call .RectList.add(r)
                call RegionAddRect(.ElevationArea,r)
                call ElevationRegionActionManager.updateElevationRegion(this)
            endif
         endmethod
         method removeRect takes rect r returns nothing
            if(r!=null) then
                call .RectList.remove(r)
                call RegionClearRect(.ElevationArea,r)
                call ElevationRegionActionManager.ResetAllUnitsHeight(r)
                call ElevationRegionActionManager.updateElevationRegion(this)
            endif
         endmethod
         
         // -----------------------------------------------------
         // Controlling the triggers
         // ------------------------------------------------------
         // enable/disable entry/leave events for a ElevationRegion
         method setEntryEvent takes boolean b returns nothing
            if(b) then
                call EnableTrigger(.EntryTrigger)
            else
                call DisableTrigger(.EntryTrigger)
            endif
         endmethod
         method setLeaveEvent takes boolean b returns nothing
            if(b) then
                call EnableTrigger(.LeaveTrigger)
            else
                call DisableTrigger(.LeaveTrigger)
            endif
         endmethod
         
         // enable/disable entire elevation region
         method enable takes nothing returns nothing
           call .setEntryEvent(true)
           call .setLeaveEvent(true)
           call ElevationRegionActionManager.updateElevationRegion(this)
         endmethod
         method disable takes nothing returns nothing
            call .setEntryEvent(false)
            call .setLeaveEvent(false)
            call ElevationRegionActionManager.updateElevationRegion(this)
         endmethod

         // get boolean values if the region is active or not
         method isEntryOn takes nothing returns boolean
            return IsTriggerEnabled(.EntryTrigger)
         endmethod
         method isLeaveOn takes nothing returns boolean
            return IsTriggerEnabled(.LeaveTrigger)
         endmethod
         method isRegionActive takes nothing returns boolean
            return .isLeaveOn() or .isEntryOn()
         endmethod

         // ----------------------------------------------------
         // --- String representation ---
         method toString takes nothing returns string
            return "H:"+ R2S(.Height) + " SE:" + R2S(.EntrySpeed) + " SL:" + R2S(.LeaveSpeed)
         endmethod
    endstruct
    // ---- End of ElevationRegion Class ----
    
    
    // ------------------------------------------------------------
    //  This static object is responsible for all the actions that can
    //  be performed by the ElevationRegion objects
    // ------------------------------------------------------------
    struct ElevationRegionActionManager
        static method updateElevationRegion takes ElevationRegion er returns nothing  
            local integer i = 0
            local rect r
            local boolean enabled = er.isEntryOn()
            local ElevationRegionRectList rl = er.getRectList()  
            loop 
                exitwhen i > rl.size
                    set r = rl.get(i)
                    if(r!= null) then
                        if(enabled) then
                            call ElevationRegionActionManager.SetAllUnitsHeight(r,er.getHeight())
                        else
                            call ElevationRegionActionManager.ResetAllUnitsHeight(r)
                        endif
                    endif
                set i = i + 1
            endloop
            set r = null
        endmethod
        
        static method EnterTriggerAction takes nothing returns nothing
            local ElevationRegion e = ElevationRegionMemoryManager.LoadER(GetTriggeringRegion())
            local ElevationRegionList el
            local unit u = GetTriggerUnit()
        
            if(e.overlap) then
                set el = ElevationRegionMemoryManager.LoadERList(u)
                if( el != 0 ) then // there is a list of regions for the triggering unit
                    call el.add(e)
                    if(e.getHeight() >  ElevationRegionMemoryManager.LoadCurrentUnitHeight(u)) then 
                       // go up 
                        call ElevationRegionActionManager.SetUnitHeight(u,e.getHeight(),e.getEntrySpeed())
                        call ElevationRegionMemoryManager.SaveCurrentUnitHeight(u,e.getHeight())
                    endif
                else // there is no list, create it
                    set el = ElevationRegionList.create()
                    call el.add(e)
                    call ElevationRegionMemoryManager.SaveERList(u,el)
                    call ElevationRegionActionManager.SetUnitHeight(u,e.getHeight(),e.getEntrySpeed())
                    call ElevationRegionMemoryManager.SaveCurrentUnitHeight(u,e.getHeight())
                endif
            else
                call ElevationRegionActionManager.SetUnitHeight(u,e.getHeight(),e.getEntrySpeed())
            endif
            set u = null
        endmethod
        static method LeaveTriggerAction takes nothing returns nothing
            local ElevationRegion e = ElevationRegionMemoryManager.LoadER(GetTriggeringRegion())
            local ElevationRegionList el
            local ElevationRegion e0
            local unit u = GetTriggerUnit()

            if(e.overlap) then
                set el = ElevationRegionMemoryManager.LoadERList(u)
                if( el != 0 and el.isEmpty() == false) then
                    call el.remove(e)   
                    if(e.getHeight()== ElevationRegionMemoryManager.LoadCurrentUnitHeight(u)) then 
                        // go down the highest last region
                         set e0 = el.getmax()
                         call ElevationRegionActionManager.SetUnitHeight(u,e0.getHeight(),e.getLeaveSpeed())
                         call ElevationRegionMemoryManager.SaveCurrentUnitHeight(u,e0.getHeight())
                    endif
                else
                    if(el!=0) then
                         call el.destroy()
                    endif
                    call ElevationRegionMemoryManager.SaveERList(u,0)
                    call ElevationRegionMemoryManager.SaveCurrentUnitHeight(u,0)
                    call ElevationRegionMemoryManager.FlushVars(u)            
                    call ElevationRegionActionManager.ResetUnitHeight(u,e.getLeaveSpeed())
                endif
                
            else
               call ElevationRegionActionManager.ResetUnitHeight(u,e.getLeaveSpeed())
            endif
            set u = null
        endmethod
        
        // interaction with outside objects - units
        static method SetUnitHeight takes unit u, real h, real s returns nothing
            if (not IsUnitType(u, UNIT_TYPE_FLYING)) then
                call UnitAddAbility(u, ElevationSystemConstants.FlyAbility)
                call UnitRemoveAbility(u, ElevationSystemConstants.FlyAbility)  
            endif
            call SetUnitFlyHeight(u,GetUnitDefaultFlyHeight(u)+h,s)
        endmethod
        static method ResetUnitHeight takes unit u, real s returns nothing
            if (not IsUnitType(u, UNIT_TYPE_FLYING)) then
                call UnitAddAbility(u, ElevationSystemConstants.FlyAbility)
                call UnitRemoveAbility(u, ElevationSystemConstants.FlyAbility)  
            endif
            call SetUnitFlyHeight(u,GetUnitDefaultFlyHeight(u),s)    
        endmethod  
        
        static method SetHeightEnum takes nothing returns nothing
            call ElevationRegionActionManager.SetUnitHeight(GetEnumUnit(),ElevationSystemConstants.HeightEnum,0)     
        endmethod
        static method SetAllUnitsHeight takes rect r, real h returns nothing
            local conditionfunc alwaystrue = Condition(function ElevationSystemConstants.ALWAYS_TRUE)
            local rect copyr = Rect(GetRectMinX(r)-ElevationSystemConstants.MaxUnitCollision,GetRectMinY(r)-ElevationSystemConstants.MaxUnitCollision,GetRectMaxX(r)+ElevationSystemConstants.MaxUnitCollision,GetRectMaxY(r)+ElevationSystemConstants.MaxUnitCollision)     
            set ElevationSystemConstants.HeightEnum  = h
            call GroupEnumUnitsInRect(ElevationSystemConstants.DummyGroup,copyr, alwaystrue )
            call ForGroup( ElevationSystemConstants.DummyGroup, function ElevationRegionActionManager.SetHeightEnum )
            call DestroyBoolExpr(alwaystrue)
            call RemoveRect(copyr)
            set copyr = null
            set alwaystrue = null
        endmethod
        static method ResetAllUnitsHeight takes rect r returns nothing
            call ElevationRegionActionManager.SetAllUnitsHeight(r,0)
        endmethod
        
    endstruct
    // ---- End of ElevationRegionActionManager Class ----
    
    // --------------------------------------------------------------------
    // This static object is responsible for managing custom memory allocations
    // for the many ElevationRegion Objects.
    // ---------------------------------------------------------------------
    struct ElevationRegionMemoryManager 
        // =================================================================
        // This object binds ElevationRegion objects to outside data storage
        // entities so that they can interface with units via trigger actions.
        // This entire class basically creates a "GetTriggeringElevationRegion()"
        // function for the trigger actions to use.
        // =================================================================
        
        //------------------------------------------------------------------
        // level 1 - global level (accessors for the hastable and other memory instances)
        // -----------------------------------------------------------------
        static integer ElevationRegionSlot = 1
        static integer ElevationRegionListSlot = 2
        static integer ElevationRegionUnitSlot = 3
        static TriggerStack triggerstack 
        static RegionStack regionstack

        static method LoadERList takes handle h returns ElevationRegionList
            return LoadInteger(ElevationSystemConstants.Hashtable,GetHandleId(h),ElevationRegionMemoryManager.ElevationRegionListSlot)
        endmethod
        static method SaveERList takes handle h, ElevationRegionList erlist returns nothing
            call SaveInteger(ElevationSystemConstants.Hashtable,GetHandleId(h),ElevationRegionMemoryManager.ElevationRegionListSlot,erlist)
        endmethod
        static method LoadCurrentUnitHeight takes handle h returns real
            return LoadReal(ElevationSystemConstants.Hashtable,GetHandleId(h),ElevationRegionMemoryManager.ElevationRegionUnitSlot)
        endmethod
        static method SaveCurrentUnitHeight takes handle h, real val returns nothing
            call SaveReal(ElevationSystemConstants.Hashtable,GetHandleId(h),ElevationRegionMemoryManager.ElevationRegionUnitSlot,val)
        endmethod
        static method LoadER takes handle h returns ElevationRegion
            return LoadInteger(ElevationSystemConstants.Hashtable,GetHandleId(h),ElevationRegionMemoryManager.ElevationRegionSlot)
        endmethod
        static method SaveER takes handle h, ElevationRegion e returns nothing
            call SaveInteger(ElevationSystemConstants.Hashtable,GetHandleId(h),ElevationRegionMemoryManager.ElevationRegionSlot,e)
        endmethod
        static method FlushVars takes handle h returns nothing
            call FlushChildHashtable(ElevationSystemConstants.Hashtable,GetHandleId(h))
        endmethod
        static method initStack takes nothing returns nothing
            set ElevationRegionMemoryManager.triggerstack = TriggerStack.create()
            set ElevationRegionMemoryManager.regionstack = RegionStack.create()
         endmethod
         static method getTriggerStackSize takes nothing returns integer
        
            return ElevationRegionMemoryManager.triggerstack.size
         endmethod
         
         static method allocateTrigger takes nothing returns trigger
            if(ElevationRegionMemoryManager.triggerstack.size >0) then
                return ElevationRegionMemoryManager.triggerstack.pop()
            else
                return CreateTrigger()
            endif
         endmethod
         static method allocateRegion takes nothing returns region
            if(ElevationRegionMemoryManager.regionstack.size >0) then
                return ElevationRegionMemoryManager.regionstack.pop()
            else
                return CreateRegion()
            endif
         endmethod
         static method freeTrigger takes trigger t returns nothing
            call ElevationRegionMemoryManager.triggerstack.push(t)
         endmethod
         static method freeRegion takes region r returns nothing
            call ElevationRegionMemoryManager.regionstack.push(r)
         endmethod
         
        // -----------------------------------------------------------------
        // level 2 - class level  (keys are the e.elevationRegion handle Ids)
        // -----------------------------------------------------------------
         static method BindElevationRegion takes ElevationRegion e returns nothing 
            call ElevationRegionMemoryManager.SaveER(e.getRegion(),e)
         endmethod 
         static method UnbindElevationRegion takes ElevationRegion e returns nothing
            call ElevationRegionMemoryManager.SaveER(e.getRegion(),0)  
            call ElevationRegionMemoryManager.FlushVars(e.getRegion())
         endmethod 
         
         // -----------------------------------------------------------------
         // level 3 - object level (trigger actions in the object)
         // -----------------------------------------------------------------
         static method allocTriggers takes ElevationRegion e returns nothing
               local boolean newTriggers = ElevationRegionMemoryManager.getTriggerStackSize() <= 0
               call e.setEntryTrigger(ElevationRegionMemoryManager.allocateTrigger())
               call e.setLeaveTrigger(ElevationRegionMemoryManager.allocateTrigger())
               call e.setRegion (ElevationRegionMemoryManager.allocateRegion())
               
               // old triggers already have a region event, a region, and the actions needed.
               if(newTriggers) then
                    call TriggerRegisterEnterRegion(e.getEntryTrigger(),e.getRegion(),null)
                    call TriggerRegisterLeaveRegion(e.getLeaveTrigger(),e.getRegion(),null)
                    call TriggerAddAction( e.getEntryTrigger(), function ElevationRegionActionManager.EnterTriggerAction )
                    call TriggerAddAction( e.getLeaveTrigger(), function ElevationRegionActionManager.LeaveTriggerAction ) 
               endif 
               call ElevationRegionMemoryManager.BindElevationRegion(e)
         endmethod
         
         static method freeTriggers takes ElevationRegion e returns nothing
                call ElevationRegionMemoryManager.freeRegion(e.getRegion())
                call ElevationRegionMemoryManager.freeTrigger(e.getLeaveTrigger())
                call ElevationRegionMemoryManager.freeTrigger(e.getEntryTrigger())  
                call ElevationRegionMemoryManager.UnbindElevationRegion(e)
         endmethod
         
    endstruct
    // ---- End of ElevationRegionMemoryManager Class ----
     
     
    // -----------------------------------------------
    // Utilities Needed by the ElevationRegion memory
    // Manager.
    // -----------------------------------------------
    struct TriggerStackNode
        public trigger value = null
        public TriggerStackNode next
        public TriggerStackNode prev   
        method onDestroy takes nothing returns nothing
            set .value = null
        endmethod
    endstruct
    struct TriggerStack
        public TriggerStackNode first
        public TriggerStackNode current
        public integer size
        
        method push takes trigger t returns nothing
            local TriggerStackNode next = TriggerStackNode.create()    
            set next.value = t
            set next.prev = .current    
            set .current.next = next
            set .current = next
            set .size = .size + 1
        endmethod
        
        method pop takes nothing returns trigger
            local trigger t = null
            if(.size>0 ) then
                set t = .current.value
                set .current = .current.prev
                call .current.next.destroy()   
                set .size = .size - 1
            endif
            return t
        endmethod
        
        static method create takes nothing returns TriggerStack
            local TriggerStack stack = TriggerStack.allocate()
            set stack.first = TriggerStackNode.create()
            set stack.size = 0 
            set stack.current = stack.first
            return stack
       endmethod
    endstruct

    struct RegionStackNode
        public region value = null
        public RegionStackNode next
        public RegionStackNode prev   
        method onDestroy takes nothing returns nothing
            set .value = null
        endmethod
    endstruct
    struct RegionStack
        public RegionStackNode first
        public RegionStackNode current
        public integer size
        
        method push takes region r returns nothing
            local RegionStackNode next = RegionStackNode.create()    
            set next.value = r
            set next.prev = .current    
            set .current.next = next
            set .current = next
            set .size = .size + 1
        endmethod
        
        method pop takes nothing returns region
            local region r = null
            if(.size>0 ) then
                set r = .current.value
                set .current = .current.prev
                call .current.next.destroy()   
                set .size = .size - 1
            endif
            return r
        endmethod
        
        static method create takes nothing returns RegionStack
            local RegionStack stack = RegionStack.allocate()
            set stack.first = RegionStackNode.create()
            set stack.size = 0 
            set stack.current = stack.first
            return stack
       endmethod
    endstruct

    // -----------------------------------------------
    // Utilities Needed by the ElevationRegion Class.
    // -----------------------------------------------

    struct ElevationRegionListNode
        // could probably use a nice interface here
        // and generalize the linked list class so it 
        // can be used for other stuff.
        // but meh, didnt feel like writing a complete
        // linked list implementation for vJass 
        //(im aware somebody might of done it already, 
        // but im too lazy/stubborn).
        // Instead, I wrote 2 custom made ones.
        public ElevationRegion value
        public ElevationRegionListNode next
    endstruct
    struct ElevationRegionList
       
       public ElevationRegionListNode current
       public ElevationRegionListNode first
       public integer size   
       
       method add takes ElevationRegion e returns nothing
            set .current.next = ElevationRegionListNode.create()
            set .current.next.value = e
            set .current = .current.next
            set .size = .size + 1
       endmethod
       
       method remove takes ElevationRegion e returns nothing
            local integer i = 0
            local ElevationRegionListNode curr = .first
            local ElevationRegionListNode prev 
            
            loop
                exitwhen i> .size
                    set prev = curr
                    set curr = curr.next
      
                    if( curr.value == e) then   
                        if(curr == .current) then
                            set .current = prev
                        endif
                        
                        set prev.next = curr.next
                        set curr.value = 0
                        
                        call curr.destroy()
                        
                        set .size = .size - 1
                        return 
                    endif   
                set i = i + 1
            endloop
       endmethod
       
       method getmax takes nothing returns ElevationRegion
            local integer i = 1
            
            local ElevationRegionListNode node
            local ElevationRegion max
            
            if(.size <=0) then
                return 0
            endif
            
            set node = .first.next
            set max = .first.next.value
            
            loop
                exitwhen i> .size
                    if(max.getHeight() < node.value.getHeight()) then
                        set max = node.value
                    endif
                    set node = node.next
                set i = i + 1
            endloop   
            return max
       endmethod
       
       method isEmpty takes nothing returns boolean
            return .size <=0
       endmethod
       
       static method create takes nothing returns ElevationRegionList
       
            local ElevationRegionList elist = ElevationRegionList.allocate()
            
            set elist.first = ElevationRegionListNode.create()
            set elist.current = elist.first
            
            set elist.first.value = 0
            set elist.first.next = 0
             
            set elist.size = 0
            return elist
       endmethod
       
       method onDestroy takes nothing returns nothing
            // deallocate all the nodes
            local ElevationRegionListNode node = .first
            local ElevationRegionListNode next  
            local integer i = 0
            loop
                exitwhen i < .size
                set next = node.next 
                call node.destroy()
                set node = next
                set i = i + 1
            endloop
       endmethod
    endstruct

    struct ElevationRegionRectListNode
        public rect value = null
        public ElevationRegionRectListNode next
        method onDestroy takes nothing returns nothing
            set .value = null
        endmethod
    endstruct
    struct ElevationRegionRectList
       
       public ElevationRegionRectListNode current
       public ElevationRegionRectListNode first
       public integer size     // the current size of valid elements

       method add takes rect e returns nothing
            set .current.next = ElevationRegionRectListNode.create()
            set .current.next.value = e
            set .current = .current.next
            set .size = .size + 1
       endmethod
       
       method remove takes rect e returns nothing
            local integer i = 0
            local ElevationRegionRectListNode curr = .first
            local ElevationRegionRectListNode prev 
            loop
                exitwhen i> .size
                    set prev = curr
                    set curr = curr.next
      
                    if( curr.value == e) then         
                        if(curr == .current) then
                            set .current = prev
                        endif              
                        set prev.next = curr.next
                        call curr.destroy()       
                        set .size = .size - 1
                        return 
                    endif
                    
                set i = i + 1
            endloop
       endmethod

       method get takes integer i returns rect
            local integer j = 0
            local ElevationRegionRectListNode curr = .first
            loop 
                exitwhen j > i
                set curr = curr.next
                set j = j + 1
            endloop
            return curr.value
       endmethod
       
       method isEmpty takes nothing returns boolean
            return .size <=0
       endmethod
       
       static method create takes nothing returns ElevationRegionRectList
       
            local ElevationRegionRectList elist = ElevationRegionRectList.allocate()
            
            set elist.first = ElevationRegionRectListNode.create()
            set elist.current = elist.first
            
            set elist.first.next = 0
             
            set elist.size = 0
            return elist
       endmethod
       
       method onDestroy takes nothing returns nothing
            // deallocate all the nodes
            local ElevationRegionRectListNode node = .first
            local ElevationRegionRectListNode next  
            local integer i = 0
            loop
                exitwhen i < .size
                set next = node.next 
                call node.destroy()
                set node = next
                set i = i + 1
            endloop
       endmethod
    endstruct

endscope


  • LS Regions GUI
    • Events
      • Map Initialization
    • Conditions
    • Actions
      • Custom script: call LSAddRegion(gg_rct_RTower,390)
      • Custom script: call LSSetRisingSpeed(gg_rct_RTower,300)
      • Custom script: call LSSetFallingSpeed(gg_rct_RTower,0)



Put under trigger named "LS Regions Jass"

JASS:
function InitTrig_LS_Regions_Jass takes nothing returns nothing
   local rect r 

        // static examples, regions defined by region palatte
        // define height on 2nd parameter
       call LSAddRegion(gg_rct_R1,40)
       call LSAddRegion(gg_rct_R2,70) 
       call LSAddRegion(gg_rct_R3,90) 
       call LSAddRegion(gg_rct_R4,120)
       call LSAddRegion(gg_rct_R5,135) 
       call LSAddRegion(gg_rct_R6,120)

       call LSAddRegion(gg_rct_RTower,390) 

       call LSSetRisingSpeed(gg_rct_RTower,300) 
       call LSSetFallingSpeed(gg_rct_RTower,0)

       // disable those events for optimization
       // and potential bug fixing with region 
       // overlap, only have the leave event  
       // handler where unit leaves the levitation area
       // and steps on a regular terrain
       // WARNING: if there are air units, then doing this
       // will not reset their flying height if they leave
       // a "middle" region

       //call LSToggleRegionLeaveEvent(gg_rct_R2,false) 
       //call LSToggleRegionLeaveEvent(gg_rct_R3,false) 
       //call LSToggleRegionLeaveEvent(gg_rct_R4,false) 
       //call LSToggleRegionLeaveEvent(gg_rct_R5,false)
       // call LSToggleRegionLeaveEvent(gg_rct_R6,false)

       //dynamic example, regions defined by triggers
       //set r = LSCreateRegion(0,0,300,300,400) 
       set r = null
endfunction




Put under trigger named "LS Regions vJass"

JASS:
//===========================================================================
function InitTrig_LS_Regions_vJass takes nothing returns nothing
    local ElevationRegion e
    local ElevationRegion e1
    local integer i = 0
    
    // basic test, same as the jass version
    //set e = ElevationRegion.create(gg_rct_R1,40)
    //set e = ElevationRegion.create(gg_rct_R2,70)
   // set e = ElevationRegion.create(gg_rct_R3,90)
   // set e = ElevationRegion.create(gg_rct_R4,120)
   // set e = ElevationRegion.create(gg_rct_R5,135)
   // set e = ElevationRegion.create(gg_rct_R6,120)

   // set e = ElevationRegion.create(gg_rct_RTower,390)
   // call e.setElevationEntrySpeed(300)
   // call e.setElevationLeaveSpeed(0)  // 0 = instant
    

    // e.destroy() will not touch the original rect used to create the region
   // call e.setLeaveEvent(true)
   // call e.setEntryEvent(true)


    // overlap test
    set e = ElevationRegion.create(gg_rct_VR1,200)
    set e = ElevationRegion.create(gg_rct_VR2,400)
    set e = ElevationRegion.create(gg_rct_VR3,300)
    set e = ElevationRegion.create(gg_rct_VR4,700)
    
    // composite region test
    set e = ElevationRegion.create(gg_rct_RR1,300)
    call e.addRect(gg_rct_RR2)
    call e.removeRect(gg_rct_RR2)
    call e.addRect(gg_rct_RR2)
    call e.addRect(gg_rct_RR4)
    call e.addRect(gg_rct_RR3)
    call e.removeRect(gg_rct_RR3)
    
    // trigger recycling test
    loop 
        exitwhen i > 20
            set e = ElevationRegion.new()
            set e1 = ElevationRegion.new()
            call e.addRect(gg_rct_RR5)
            call e.destroy()
            call e1.destroy()
            set i = i + 1
    endloop
    
    // new() test
    set e = ElevationRegion.new()
    call e.addRect(gg_rct_RR5)
    call e.setHeight(300)
    set e1 = ElevationRegion.new()
    call e1.addRect(gg_rct_RR6)
    call e1.setHeight(300)
    
endfunction
 
Last edited:
Can't you use 'Amrf' instead of the custom Fly ability? I tested it and it worked, but I haven't tested it in all cases.

Anyway, nice system, but it needs more documentation. You should make a list of the functions that the GUI users can use along with the parameters, ex:
JASS:
//    function LSAddRegion takes rect r returns nothing

Or something like that. Anyway, I noticed that your code had a lot of BJ's in it. BJ's are slightly slower than calling natives, so it is better to avoid them. I went ahead and fixed some of the errors I could find in your code, and I've added comments to help explain a bit:
JASS:
//===============================================
// System Configuration
//===============================================

//-----------------------------------------------
// Dummy Fly Ability for ground units
//
// This ability is needed to make ground units appear
// flying.
//-----------------------------------------------

constant function LS_DUMMY_FLY_ABILITY takes nothing returns integer
    return 'A000'
endfunction

//-----------------------------------------------
// Pointer to the system hashtable
//
// Example: hashtable "udg_LevitationVars".
// Replace with desired global hashtable here.
//-----------------------------------------------

function LS_DATABASE takes nothing returns hashtable
    return udg_LevitationVars
endfunction

function CreateLeviSystemTable takes nothing returns nothing    
    //call InitHashtableBJ(  )
    //set udg_LevitationVars =GetLastCreatedHashtableBJ()
    //** Purge: You can initially set this to InitHashtable() since it returns a hashtable
    set udg_LevitationVars = InitHashtable()
endfunction

//-----------------------------------------------
// Static keys
//
// Must be UNIQUE integers and not overlap
// with other table PARENT keys.
//-----------------------------------------------

constant function LS_TRIGGER_KEY takes nothing returns integer
    return 1
endfunction

constant function LS_REGION_KEY takes nothing returns integer
    return 2
endfunction

constant function LS_REGIONT_KEY takes nothing returns integer
    return 3
endfunction

constant function LS_REGIONT2_KEY takes nothing returns integer
    return 4
endfunction


//===============================================
// System Core components
//===============================================

//-----------------------------------------------
// DB interface
//
// Do not modify without full understanding of the 
// system's operations.
//-----------------------------------------------

function LSSR takes rect r,trigger t, trigger t2 returns nothing 
     //call SaveRectHandleBJ(r,LS_REGION_KEY(),GetHandleId(t),LS_DATABASE())
     //call SaveTriggerHandleBJ(t,LS_REGIONT_KEY(),GetHandleId(r),LS_DATABASE())
     //call SaveTriggerHandleBJ(t2,LS_REGIONT2_KEY(),GetHandleId(r),LS_DATABASE())
     call SaveRectHandle(LS_DATABASE(),GetHandleId(t),LS_REGION_KEY(),r)
     call SaveTriggerHandle(LS_DATABASE(),GetHandleId(r),LS_REGIONT_KEY(),t)
     call SaveTriggerHandle(LS_DATABASE(),GetHandleId(r),LS_REGIONT2_KEY(),t2)
     //** Purge: Try to refrain from using BJ's since they are slightly slower than natives.
     //You can switch to the native for these functions easily by just swapping the arguments.
endfunction

function LSGR takes trigger t returns rect
    return LoadRectHandle(LS_DATABASE(),GetHandleId(t),LS_REGION_KEY())
    //** Old bj: return LoadRectHandleBJ(LS_REGION_KEY(),GetHandleId(t),LS_DATABASE())
endfunction

function LSGT takes rect r returns trigger
    return LoadTriggerHandle(LS_DATABASE(),GetHandleId(r),LS_REGIONT_KEY())
    //** Old bj: return LoadTriggerHandleBJ(LS_REGIONT_KEY(),GetHandleId(r),LS_DATABASE())
endfunction

function LSGT2 takes rect r returns trigger
    return LoadTriggerHandle(LS_DATABASE(),GetHandleId(r),LS_REGIONT2_KEY())
    //* Old bj: return LoadTriggerHandleBJ(LS_REGIONT2_KEY(),GetHandleId(r),LS_DATABASE())
endfunction

function LSSH takes trigger t, real h returns nothing
    call SaveReal(LS_DATABASE(),GetHandleId(t),LS_TRIGGER_KEY(),h)
    //* Old bj: call SaveRealBJ(h,LS_TRIGGER_KEY(),GetHandleId(t),LS_DATABASE())
endfunction

function LSGH takes trigger t returns real
    return LoadReal(LS_DATABASE(),GetHandleId(t),LS_TRIGGER_KEY())
    //* Old bj: return LoadRealBJ(LS_TRIGGER_KEY(),GetHandleId(t),LS_DATABASE())
endfunction

function FlushTriggerData takes trigger t returns nothing
    call SaveReal(LS_DATABASE(),GetHandleId(t),LS_TRIGGER_KEY(),0)
    call SaveRectHandle(LS_DATABASE(),GetHandleId(t),LS_REGION_KEY(),null)
    //** Purge: You can always use FlushChildHashtable(LS_DATABASE(),GetHandleId(t)) instead
    //** Old bjs:
    //** call SaveRealBJ(0,LS_TRIGGER_KEY(),GetHandleId(t),LS_DATABASE())
    //** call SaveRectHandleBJ(null,LS_REGION_KEY(),GetHandleId(t),LS_DATABASE())
endfunction

function FlushRegionData takes rect r returns nothing
    call SaveTriggerHandle(LS_DATABASE(),GetHandleId(r),LS_REGIONT_KEY(),null)
    call SaveTriggerHandle(LS_DATABASE(),GetHandleId(r),LS_REGIONT2_KEY(),null)
    //** Purge: Same thing, try FlushChildHashtable(LS_DATABASE(),GetHandleId(r))
    //** Old bjs:
    //** call SaveTriggerHandleBJ(null,LS_REGIONT_KEY(),GetHandleId(r),LS_DATABASE())
    //** call SaveTriggerHandleBJ(null,LS_REGIONT2_KEY(),GetHandleId(r),LS_DATABASE())
endfunction


//-----------------------------------------------
// Setting unit heights
//
// Those methods are used by the system, do not 
// modify without full knowledge of the system's
// operations.
// 
// Can probably apply those methods outside the
// system as well.
//-----------------------------------------------

function LSResetUnitHeight takes unit u returns nothing
    if not IsUnitType(u, UNIT_TYPE_FLYING) then
        call UnitAddAbility(u, LS_DUMMY_FLY_ABILITY())
        call UnitRemoveAbility(u, LS_DUMMY_FLY_ABILITY())
        //** Purge: Switched with natives
    endif
    call SetUnitFlyHeight(u,GetUnitDefaultFlyHeight(u),0)
    //** Purge: You can use 0 for instant setting. Switched to native.
endfunction

function LSSetUnitHeight takes unit u, real h returns nothing
    if not IsUnitType(u, UNIT_TYPE_FLYING) then
        call UnitAddAbility(u, LS_DUMMY_FLY_ABILITY())
        call UnitRemoveAbility(u, LS_DUMMY_FLY_ABILITY())
        //** Purge: Switched with natives
    endif
    call SetUnitFlyHeight(u,GetUnitDefaultFlyHeight(u)+h,0)
endfunction

function LS_RESETENUM takes nothing returns boolean
    call LSResetUnitHeight(GetEnumUnit())
    return false
endfunction
function LSResetAllUnitsHeight takes rect r returns nothing
    //local group g = GetUnitsInRectAll(r)
    //BJ, a little inefficient
    local group g = CreateGroup()
    call GroupEnumUnitsInRect(g,r,Condition(function LS_RESETENUM))
    //** Purge: FirstOfGroup() loops are inefficient in comparison to filter enumerations
    call DestroyGroup(g)
    //** Purge: Should technically be a global group for group recycling but we can fix that later
    set g = null
endfunction
function LS_SETHEIGHTENUM takes nothing returns boolean
    call LSSetUnitHeight(GetEnumUnit(),bj_enumDestructableRadius)
    return false
endfunction
function LSSetAllUnitsHeight takes rect r, real h returns nothing
    local group g = CreateGroup()
    set bj_enumDestructableRadius = h
    //This is just for quick global variable usage for our enumeration =D
    call GroupEnumUnitsInRect(g,r,Condition(function LS_SETHEIGHTENUM))
    //filter enumeration, faster
    call DestroyGroup(g)
    set g = null
endfunction


//-----------------------------------------------
// TriggerActions for region entry/leave
//
// Used by the triggers responsible for elevation.
//-----------------------------------------------

function Unit_Enteres_LS_Region_Actions takes nothing returns nothing
    //local unit u =  GetTriggerUnit()
    //local trigger t = GetTriggeringTrigger()
    //local real h = LSGH(t)  
    call LSSetUnitHeight(GetTriggerUnit(),LSGH(GetTriggeringTrigger()))
    //** Purge: Quick optimization, basically inline the variables since it is used once
    //set u = null
    //set t = null
endfunction

function Unit_Leaves_LS_Region_Actions takes nothing returns nothing
    //local unit u =  GetTriggerUnit()
    call LSResetUnitHeight(GetTriggerUnit()) 
    //** Purge: Just use GetTriggerUnit() directly since it is used only once
    //set u = null
endfunction



//===============================================
// System Front End Interface
//===============================================
//
// create/add/remove/destroy/enable/disable 
// levitation regions
//
// Warning: must call LSAddRegion() or LSCreateRegion()
// before doing any other operations.
//
// Parameters:
//
// Region r - the region that is to be used for levitation.
// Real h - the levitation height of a region.
// 
// Real x1,y1,x2,y2 - used for creating regions.
//
//-----------------------------------------------

// Toggle unit enters levitation region event on or off
function LSToggleRegionEnterEvent takes rect r, boolean b returns nothing
    local trigger t = LSGT(r)
    if (b) then    
        call EnableTrigger(t)
        call LSSetAllUnitsHeight(r,LSGH(t))
    else
        call DisableTrigger(t)
    endif 
    set t = null
endfunction

// Toggle unit leaves levitation region event on or off
function LSToggleRegionLeaveEvent takes rect r, boolean b returns nothing
    local trigger t = LSGT2(r)
    if (b) then
        call EnableTrigger(t)
    else
        call DisableTrigger(t)
    endif 
    set t = null
endfunction

// Turn levitation region on
function LSEnableRegion takes rect r returns nothing
     local trigger t1 = LSGT(r)
     //local trigger t2 = LSGT2(r)
     //local real h = LSGH(t1)
     call EnableTrigger(t1)
     call EnableTrigger(LSGT2(r))
     call LSSetAllUnitsHeight(r,LSGH(t1))
     //** Purge: Just some quick optimization
     //set t2 = null
     set t1 = null
endfunction

// Turn levitation region off
function LSDisableRegion takes rect r returns nothing
     //local trigger t1 = LSGT(r)
     //local trigger t2 = LSGT2(r)

     call DisableTrigger(LSGT(r))
     call DisableTrigger(LSGT2(r))

     call LSResetAllUnitsHeight(r)
     //** Purge: More quick optimization
     //set t2 = null
     //set t1 = null
endfunction

// Set levitation region levitation height
function LSSetHeight takes rect r, real h returns nothing
     //local trigger t = LSGT(r)
     call LSDisableRegion(r)
     call LSSH(LSGT(r),h)
     call LSEnableRegion(r)
     //** Purge: More quick optimization
     //set t = null
endfunction


// destroys a levitation region
function LSDestroyRegion takes rect r returns nothing
     local trigger t = LSGT(r) 
     local trigger t2 = LSGT2(r)

     call FlushTriggerData(t)
     call FlushRegionData(r)

     call DisableTrigger(t)
     call DisableTrigger(t2)

     call DestroyTrigger(t)
     call DestroyTrigger(t2)

     call LSResetAllUnitsHeight(r)
     call RemoveRect(r)

     //set r = null
     //** Purge: No need to null arguments
     set t = null
     set t2 = null
endfunction


// adds and enables region for levitation
function LSAddRegion takes rect r, real h returns nothing
     local trigger t1 = CreateTrigger()
     local trigger t2 = CreateTrigger()
     local region r1 = CreateRegion()
     
     // events here
     call RegionAddRect(r1,r)
     call TriggerRegisterEnterRegion(t1,r1,null)
     call TriggerRegisterLeaveRegion(t2,r1,null)
     //** Purge: Inlined the events
     //call TriggerRegisterEnterRectSimple( t1, r )
     //call TriggerRegisterLeaveRectSimple( t2, r )

     // conditions here
    
     // trigger actions here
     call TriggerAddAction( t1, function Unit_Enteres_LS_Region_Actions )
     call TriggerAddAction( t2, function Unit_Leaves_LS_Region_Actions )

     call LSSR(r,t1,t2) 
     call LSSH(t1,h)

     call LSEnableRegion(r)
     
     set r1 = null
     set t1 = null
     set t2 = null
endfunction


//creates a region for levitation
function LSCreateRegion takes real x1, real y1,real x2, real y2, real h returns rect
     set bj_isUnitGroupInRectRect = Rect(x1, y1, x2, y2)
     //** Purge: The annoying thing about creating local handles which you'll return
     //is that you need to null to prevent the small ref leak. I replaced it with a global
     //so that you don't need to worry about nulling it.
     call LSAddRegion(bj_isUnitGroupInRectRect,h)
     return bj_isUnitGroupInRectRect
endfunction



//===========================================================================
function InitTrig_Levitation_System takes nothing returns nothing
    call CreateLeviSystemTable()
endfunction

Overall, nice job on the system. It is very creative. =)

It is most certainly useful without a doubt. This will also help many GUI users who pray for systems like this but can't make them on their own/require vJASS/require more confusing methods.

The commented lines can go away.
 
Level 19
Joined
Feb 4, 2009
Messages
1,313
you either use that or import a custom pathing texture..... :grin:

some tutorial for drawing ur own pathing maps:
http://www.wc3c.net/showthread.php?t=91193

I'll test it now if the fps difference will be noticeable or if I'm just talking trash

edit:
it works!!!!
....but not perfectly
if you change the height using ctrl + up the units won't be lifted into the air while walking on the new pathing map
but if you create them with "CreateDestructableZ" it works perfectly
to be able to place them in the editor and still have them work one would need some tool to decrypt the data from the "war3map.doo" file and paste it to the trigger data instead
else it would be as time-consuming as your code method

but the frame loss with this method is equal to zero

and I found another method
just redraw your pathing map with this
http://www.hiveworkshop.com/forums/tools-560/zepirs-war3-map-editor-1-0-a-62889/
it's pretty much beta though and I don't think it's developed any further
alternatively you can just export the pathingmap of your map, redraw it, and import it back

however bigger pathing maps should solve all problems anyway

suggestions:
I really like your idea for multi-level support
problems regarding that:
-units on different floors block the unit above and below
-they might be able to attack each other
-different entrances will fuck up the pathing system (waygates/portals might come in handy to fix this)
It will be hard to accomplish workarounds for these things though

edit2:
multi level support is not equal to 2 level support but it might be too hard
and it's not the pathing that causes the lag but the usage of a lot of destructables
 
Last edited:
Level 6
Joined
Jan 17, 2010
Messages
149
Thanks for the advice,

There is a separate documentation category with all the function signatures in it. I do not think it needs any more documentation than that.

--
@fly ability
I guess its good to give the option to change it to anything rather than hard coding it.


--

I avoid leaky BJs (because they leak) and global BJenums (because changing those might affect other parts of code if the silly programmers are not careful)

Otherwise, The change from BJ to non BJ doesn't do much performance wise (unless its a loop of 100 iterations or more) but sometimes improves readability. Furthermore, Vex's MapOpt antiBJ does a good job anyway (and removes the over 9000kb of comments too -.-).

Gah, I was hoping I would not need a trigger recycle, gonna have to remove events from triggers and put the triggers them to a stack.
Edit: Impractical to recycle triggers because they retain old events.


FlushChildTable() does not reset the value itself, it just frees up that table space. I've had some weird bug because of that (kind of the opposite of a dangling pointer error in C).

Enum conditions leak, that's why I used a loop because I'm too lazy to look how to destroy them =|
Edit: Removed the leak.
--

@custom pathing

Doesn't that only affect pathing? I tried it a while ago, but resolved using doodad pathing blockers instead as they are more versatile.

The wc3c example uses stairs (platform destructables) with a pathing map. This is what this system attempts to avoid as walkable destructables chop up FPS.


Any desctructable will gobble FPS (in small amounts) it is best to avoid them. Somone actually had 2000+ destructables in his map and had it unplayable. I had a map with only a few walkable destructables that covered about 5% of the map, and the game was unplayable with 30 units on screen in some areas.

Conclusion: Only use destructables when (1) you want a 'doodad' to be interfaced with at run-time (i.e added/removed) (2) when you want a walkable bridge/platform (i.e wc3 bridges) (3) when you don't care about performance, or you have a map with very few units.


Edit:
@multilevel support
I could easily do this, without units attacking each other. But it involves some tricks with air/ground path blockers and collision size and would treat top level units as air units. Probably will not need more than 2 levels in most maps. This is outside the scope of the system.

I think ToadCop Had more than 2 levels going for his quake3 map, but it was for arrow key movement type.
 
Last edited:
@The end of the main post: You can only have 256 active hashtables on the map. (Before the hashtables created afterward appear null) However, the amount of nodes to a child:
http://www.wc3c.net/showpost.php?p=1118269&postcount=8

Anyway, we must assume that the mapper doesn't use the Optimizer when submitting though, or at least for it to be approved. Despite the fact that most of the optimizations hardly make a visible difference, we have those optimizations done anyway when submitting a resource.

Anyway, for group recycling, it is pretty simple.
Create your global group named whatever you want, then just use the group normally, ex:
JASS:
//for example, if your global group is named "udg_ESGroup"
function LS_SETHEIGHTENUM takes nothing returns boolean
    call LSSetUnitHeight(GetEnumUnit(),bj_enumDestructableRadius)
    return false
endfunction
function LSSetAllUnitsHeight takes rect r, real h returns nothing
    set bj_enumDestructableRadius = h
    call GroupEnumUnitsInRect(udg_ESGroup,r,Condition(function LS_SETHEIGHTENUM))
    call GroupClear(udg_ESGroup)
//the group clear is actually not really necessary though since Enumerations auto-clear groups
endfunction

That is essentially it, a simple group recycle. You just use a global group instead of creating one each time and destroying it. It is not necessarily the enum itself that causes the leak, it is just if you destroy a group that has had an enumeration it leaks a bit of RAM. This is just preferred over FirstOfGroup since it is slightly less efficient/deprecated, and in most cases you'd end up enumerating anyway.

Good luck with the multi-level support. =)
 
Level 6
Joined
Jan 17, 2010
Messages
149
[...]
it is just if you destroy a group that has had an enumeration it leaks a bit of RAM.
[...]

May I have a link to an article/discussion that states that?

I'm really curious on that matter, because frankly, I don't know why bother recycling anything (timers,triggers,groups, etc). It seems like a waste of effort.

All the tutorials I've been through just say "WE RECYCLE AND THAT'S HOW WE DO IT" without stating the why.

P.S
Your method does not work for me for some reason, I had to change it to something like this:

JASS:
constant function LS_TRUE takes nothing returns boolean
    return true
    // best function ever because its made of pure awesomeness
endfunction
function LS_SETHEIGHTENUM takes nothing returns nothing
    call LSSetUnitHeight(GetEnumUnit(),bj_enumDestructableRadius)
endfunction
function LSSetAllUnitsHeight takes rect r, real h returns nothing
    local group g = CreateGroup()
    local conditionfunc LSset_height = Condition(function LS_TRUE)
    set bj_enumDestructableRadius = h
    call GroupEnumUnitsInRect(g,r, LSset_height )
    call ForGroup( g, function LS_SETHEIGHTENUM )
    call DestroyGroup(g)
    call DestroyBoolExpr(LSset_height)
    set LSset_height = null
    set g = null 
endfunction

Pretty horrible but that gets around the condition leak (if there is one).
 
http://www.thehelper.net/forums/showthread.php?t=138374

I just took his word for it since I know he knows what he does. =P Recycling is just more efficient since you don't have to create and destroy a handle each time the code is run. It is situational, but is usually more efficient in most cases. In a game it doesn't make the largest difference, especially in your case so it isn't entirely needed. Not group recycling doesn't really make it more sucky or prone to "rare dangers" though so it is your choice when it comes down to it.

About conditions.. Blizzard has kind of an "auto-recycling" method when it comes to conditions. Basically, it will create the condition once, and then each time the code is ran afterward it will use the condition. In your case, the condition is permanent. It does "leak" as in it creates a handle and never removes it, but there is no need to destroy it since you use that lone condition throughout the entire game. It will return the same thing each time without creating a new boolexpr. You can kind of think of this as the point of other recycling methods as well.

But anyway, my bad. I made a mistake:
JASS:
//for example, if your global group is named "udg_ESGroup"
function LS_SETHEIGHTENUM takes nothing returns boolean
    call LSSetUnitHeight(GetFilterUnit(),bj_enumDestructableRadius)
//I had put GetEnumUnit(), sorry =(
    return false
endfunction
function LSSetAllUnitsHeight takes rect r, real h returns nothing
    set bj_enumDestructableRadius = h
    call GroupEnumUnitsInRect(udg_ESGroup,r,Condition(function LS_SETHEIGHTENUM))
    call GroupClear(udg_ESGroup)
//the group clear is actually not really necessary though since Enumerations auto-clear groups
endfunction

That way it should properly work. But it is your choice, since you know the system better than I do. Based on the number of times you'd predict the functions pertaining to this will be executed, you can choose whether to recycle or just use the normal method. =D
 
Level 6
Joined
Jan 17, 2010
Messages
149
I tested it and it works fine. I also must say that this is a pretty neat idea.
(But the implementation is kinda ugly :/)

It takes peanuts to do the same thing with vJass, and it will probably look neater too but I was aiming for the entire mapping audience, not just the 25% or so who use vJass.

Whats so ugly about the implementation btw?
 
Level 6
Joined
Jan 17, 2010
Messages
149
I could find no such implementation, otherwise I wouldn't be posting this.

It simple, yes, I just said how simple it is a post ago. But for the average mapper, copying a ready system that deals with this is much easier than making one from scratch and then calibrating it.

@vjass

CnP vjass script without into an editor without a vJass compiler and try to compile to see what happens.

A lot of people do not have a vJass compiler, and they probably won't ever have one.


--

If you are making the courtesy of taking your time to post and making a negative comment, then at-least give your target a chance and some pointers to what he/she did wrong.
 
Level 6
Joined
Jan 17, 2010
Messages
149
I don't understand what you are saying ._.

- Are you disappointed this system is not GUI and would of used it if it was?

- Are you disappointed this system is designed to accommodate GUI, and will not use it because of that?

- Are you disappointed this system is not vJass, but for Jass&GUI and because of that, the implementation is a bit different than it would look in vJass, and you will not use it because of that?


There is a stereotype that all GUI codes are clumsy, glitchy, and hard to use. But it's just a stereotype, some of the GUI codes I came across actually amazed me in their neatness.


ANYWAY

Wrote a vJass version, but I have not had the chance to completely test it for leaks. It seems to be working fine.
 
Last edited:
Level 6
Joined
Jan 17, 2010
Messages
149
Done.

I am going to make this final soon. I do not think I have anything else to add (unless someone comes with a suggestion that fits, or finds a can full of bugs ).

Zig zag terrain involves a timer checking LocationZ per unit every 0.1 seconds or so. This is probably the same as or worse than using a destructable platform, so I am not adding that.
 
Level 6
Joined
Jan 17, 2010
Messages
149
Hey folks, this will seem kinda retarded as this response is after 2 years.

After careful thought (no that's not the reason of my absence :p) I concluded that using destructable platforms is better, as using masses of region+events present its own performance issues. I haven't tested it extensively but I'm pretty sure that's the case. Finally this kind of sucks in conjunction with stuff that relies on SetUnitZ() (tossing units and dummy projectiles) and will require adjustments to the SetUnitZ function to check if the X,Y is inside the rect.. bummer

Overall I'm quite disappointed it can't solve the problem fully. I resorted to using destructable platforms and terrain tricks where possible.

One small benefit I found is for minor elevations that cannot be accomplished by hidden terrain, in places where platforms would be overkillishly stupid. An example would be the need to elevate some units just barely enough so they don't "sink" in a decoration doodad or terrain

I give permission to anyone who feels like improving it(the system) and posting the code as his/her own.
 
Top