wc3campaigns
WC3C Homepage - www.wc3c.netUser Control Panel (Requires Log-In)Engage in discussions with other users and join contests in the WC3C forums!Read one of our many tutorials, ranging in difficulty from beginner to advanced!Show off your artistic talents in the WC3C Gallery!Download quality models, textures, spells (vJASS/JASS), systems, and scripts!Download maps that have passed through our rigorous approval process!

Go Back   Wc3C.net > Resources > - Submit a resource -
User Name
Password
Register Rules Get Hosted! Chat Pastebin FAQ and Rules Members List Calendar



Reply
 
Thread Tools Search this Thread
Old 09-05-2015, 11:57 AM   #1
iNfraNe
PhD
 
iNfraNe's Avatar


Cinematics Moderator
 
Join Date: Dec 2003
Posts: 2,283

Submissions (7)

iNfraNe is just really nice (266)iNfraNe is just really nice (266)iNfraNe is just really nice (266)iNfraNe is just really nice (266)iNfraNe is just really nice (266)

Approved Map: Mortar Ball

Send a message via MSN to iNfraNe
Default IRect

Rects and regions are mostly used to create "Enter" or "Leave" events for units. The correct implementation of these events is, however, quite annoying. For this reason I wrote a simple script that to automate all the bothersome things one needs to do when working with rects.

For example, rects and regions don't share the same x&y coordinates (regions are on a 32 grid, rects are not) and the maxX and maxY of regions are rounded up to the nearest 32, which is very counter-intuitive. (for a good study on the subject, see http://www.wc3c.net/showthread.php?p=1134281).

This script addresses these issues and adds some useful functionality such as hysteresis between enter/leave events, counting occupants, a never-failing .containsUnit function and a very easy method to filter units by the Enter response function. It also comes with a debug method ".display()", which shows the boundaries of the rect.

The library requires Table

Collapse JASS:
library IRect initializer Init requires Table
   /*****************************************************************************************
    *
    *    rects are mostly used to do "on enter" and "on leave" events
    *    the IRect provides a straightforward way to use these events.
    *    
    *    besides having an easy syntax, it automatically fixes some bugs
    *    associated with rects/regions, such as the up-rounding of the 
    *    maxX / maxY, and regions being set to multiple of 32, while
    *    rects keep the original input leading to bugs when calling 
    *    RectContainsUnit. Another function is provided to check if a 
    *    unit is in the IRect.
    *    
    *    the last added benefit is the addition of hysteresis. A dynamic
    *    "outer" rect can be added, so that the leave event fires at a
    *    different position than the enter event. 
    *    
    *    syntax:
    *
    *******************************User variables/functions:*********************************
    *    IRect eventIRect
    *    
    *    this is the IRect from which an event was fired
    *****************************************************************************************
    *    unit eventUnit
    *    
    *    this is the entering/leaving/filter unit.
    *****************************************************************************************
    *    integer numberOfOccupants
    *    
    *    this is the number of units in the IRect
    *****************************************************************************************      
    *    method containsUnit(unit u) returns boolean
    *    
    *    returns if a unit is currently occupying the IRect
    *    it inlines to a simple native call
    *    
    ************************************Setup functions:*************************************
    *
    *    IRect.create(real minX, real minY, real maxX, real maxY) returns IRect
    *    
    *    this function creates an IRect. The IRect automatically creates 
    *    the rect, region and triggers required for the events
    *****************************************************************************************
    *    method setOnEnter(eventCondition) returns IRect
    *
    *    pass a function to this method that takes nothing and returns a boolean
    *    this function will be called when a unit enters the IRect
    *    if the function returns true, then the unit is added to the occupants
    *    if the function returns false, then the unit is ignored by the system
    *    As the entering/leaving unit, use IRect.eventUnit (a global variable)
    *    if you want to use the IRect variable itself, use the global
    *    IRect.eventIRect  
    *    
    *    this method also adds all units that are currently occupying the rect
    *    and calls their enter function.
    *****************************************************************************************
    *    method setOnLeave(eventCallBack) returns IRect
    *    
    *    pass a function to this method that takes and returns nothing
    *    this function will be called when a unit leaves the IRect
    *    for event unit/iRect, use the same as in setOnEnter
    *****************************************************************************************
    *    method setHysteresis(real left, real bottom, real right, real top) returns IRect
    *    
    *    sets the margins for hysteresis at the sides of the IRect.
    *    For example, for an IRect(0,0,64,64)
    *    with a left hysteresis of 32, an entering unit will fire an event
    *    coming from the left when it crosses x = 0. When exiting the
    *    IRect, a leave event is fired at -32.
    *****************************************************************************************
    *    method setHysteresisSimple(real margin) returns IRect
    *    
    *    sets the hysteresis margins to the same value for all
    *****************************************************************************************
    *    method display() returns IRect
    *    
    *    displays the rects boundaries (including hysteresis if set)
    *    useful for debugging, but nothing else
    *****************************************************************************************
    */
    
    globals
        HandleTable rTbl
        location l
        group g
    endglobals
    
    private function interface eventCallback takes nothing returns nothing
    private function interface eventCondition takes nothing returns boolean
    struct IRect
        readonly static unit eventUnit
        readonly static IRect eventIRect
        readonly integer numberOfOccupants = 0
        private rect mainRect
        private rect outerRect
        private region mainRegion
        private region outerRegion
        private group occupants
        private trigger enterTrig
        private trigger leaveTrig
        
        private eventCondition enterFunc = 0
        private eventCallback leaveFunc = 0
        method containsUnit takes unit u returns boolean
            return IsUnitInGroup(u, .occupants)
        endmethod
        // -------------------------------------------------------
        // setup functions (return the type to allow 1-line coding)
        // -------------------------------------------------------
        // set the function that is called upon entering, also adds all units that the function returns true for to the group
        method setOnEnter takes eventCallback enterFunction returns thistype
            set .enterFunc = enterFunction
            call GroupEnumUnitsInRect(g, .mainRect, null)
            loop
                set .eventUnit = FirstOfGroup(g)
                exitwhen .eventUnit == null
                if not IsUnitInGroup(.eventUnit, .occupants) then
                    if .enterFunc.evaluate() then
                        call GroupAddUnit(.occupants, .eventUnit)
                        set .numberOfOccupants = .numberOfOccupants + 1                    
                    endif
                endif
                call GroupRemoveUnit(g,.eventUnit)
            endloop
            return this
        endmethod
        // set the function that is called upon leaving
        method setOnLeave takes eventCallback leaveFunction returns thistype
            set .leaveFunc = leaveFunction
            return this
        endmethod
        // sets the hysteresis margins (different for all)
        method setHysteresis takes real left, real bottom, real right, real top returns thistype
            set left = RAbsBJ(left)
            set top = RAbsBJ(top)
            set right = RAbsBJ(right)
            set bottom = RAbsBJ(bottom)
            if .outerRect != null then
                call RegionClearRect(.outerRegion, .outerRect)
                call RemoveRect(.outerRect)
            else    
                call RegionClearRect(.outerRegion, .mainRect)
            endif
            set .outerRect = Rect(GetRectMinX(.mainRect)-left, GetRectMinY(.mainRect)-bottom, GetRectMaxX(.mainRect)+right, GetRectMaxY(.mainRect)+top)
            call RegionAddRect(.outerRegion, .outerRect)
            return this            
        endmethod
        // sets the hysteresis margins (same for all sides)
        method setHysteresisSimple takes real margin returns thistype
            set margin = RAbsBJ(margin)
            if .outerRect != null then
                call RegionClearRect(.outerRegion, .outerRect)
                call RemoveRect(.outerRect)
            else    
                call RegionClearRect(.outerRegion, .mainRect)
            endif
            set .outerRect = Rect(GetRectMinX(.mainRect)-margin, GetRectMinY(.mainRect)-margin, GetRectMaxX(.mainRect)+margin, GetRectMaxY(.mainRect)+margin)
            call RegionAddRect(.outerRegion, .outerRect)
            return this
        endmethod        
        // for debugging purposes
        method display takes nothing returns thistype
            local real x1 = GetRectMinX(.mainRect)
            local real x2 = GetRectMaxX(.mainRect)
            local real y1 = GetRectMinY(.mainRect)
            local real y2 = GetRectMaxY(.mainRect)            
            local real z1
            local real z2
            local real z3
            local real z4
            call MoveLocation(l, x1, y1)
            set z1 = GetLocationZ(l)
            call MoveLocation(l, x2, y1)
            set z2 = GetLocationZ(l)
            call MoveLocation(l, x2, y2)
            set z3 = GetLocationZ(l)            
            call MoveLocation(l, x1, y2)
            set z4 = GetLocationZ(l)                        
            call AddLightningEx("DRAL",true, x1, y1, z1, x2, y1, z2) // left, top to bottom
            call AddLightningEx("DRAL",true, x2, y1, z2, x2, y2, z3) // bottom, left to right
            call AddLightningEx("DRAL",true, x2, y2, z3, x1, y2, z4) // right, bottom to top
            call AddLightningEx("DRAL",true, x1, y2, z4, x1, y1, z1) // top, right to left
            if .outerRect != null then
                set x1 = GetRectMinX(.outerRect)
                set x2 = GetRectMaxX(.outerRect)
                set y1 = GetRectMinY(.outerRect)
                set y2 = GetRectMaxY(.outerRect) 
                call MoveLocation(l, x1, y1)
                set z1 = GetLocationZ(l)
                call MoveLocation(l, x2, y1)
                set z2 = GetLocationZ(l)
                call MoveLocation(l, x2, y2)
                set z3 = GetLocationZ(l)            
                call MoveLocation(l, x1, y2)
                set z4 = GetLocationZ(l)                        
                call AddLightningEx("DRAM",true, x1, y1, z1, x2, y1, z2) // left, top to bottom
                call AddLightningEx("DRAM",true, x2, y1, z2, x2, y2, z3) // bottom, left to right
                call AddLightningEx("DRAM",true, x2, y2, z3, x1, y2, z4) // right, bottom to top
                call AddLightningEx("DRAM",true, x1, y2, z4, x1, y1, z1) // top, right to left            
            endif
            return this
        endmethod
        // this method is called everytime a unit enters the rect
        private static method eventEnter takes nothing returns nothing
            local thistype this = rTbl[GetTriggeringTrigger()]
            set .eventIRect = this
            set .eventUnit = GetEnteringUnit()
            
            if not .containsUnit(.eventUnit) then //enter event, the unit enters for the first time
                // check if an enter event is configured, if so, evaluate it to see if the unit should be added to the occupants
                if .enterFunc == 0 or .enterFunc.evaluate() then
                    call GroupAddUnit(.occupants, .eventUnit)
                    set .numberOfOccupants = .numberOfOccupants + 1                
                endif
            endif            
        endmethod
        // this method is called everytime a unit leaves the rect
        private static method eventLeave takes nothing returns nothing
            local thistype this = rTbl[GetTriggeringTrigger()]
            set .eventIRect = this
            set .eventUnit = GetLeavingUnit()
            if .containsUnit(eventUnit) then //leave event should only fire when the unit is in the occupants group
                call GroupRemoveUnit(.occupants, .eventUnit)
                set .numberOfOccupants = .numberOfOccupants - 1
                // check if a leave event is configured, if so, fire it
                if .leaveFunc != 0 then
                    call .leaveFunc.execute()
                endif
            endif
        endmethod     
        // create method has identical parameters as Rect()
        static method create takes real minX, real minY, real maxX, real maxY returns thistype
            local thistype this = thistype.allocate()
            // setup the main rect
            // The rect must be put on a grid, because blizzard only allows multiples of 32 for region, but still stores a value that can be anything on the rect, leading to bugs.
            // Also, the maxX and maxY are bugged, they are rounded to +32. Therefore, a correction is in order. The Rect is 0.01 smaller than the region.
            set minX = I2R(R2I((minX)/32))*32
            set minY = I2R(R2I((minY)/32))*32
            set maxX = I2R(R2I((maxX)/32))*32-0.01
            set maxY = I2R(R2I((maxY)/32))*32-0.01
            set .mainRect = Rect(minX, minY, maxX, maxY) // correction for stupid blizzard bug
            set .outerRect = null
            set .mainRegion = CreateRegion()
            call RegionAddRect(.mainRegion, .mainRect)
            set .outerRegion = CreateRegion()
            call RegionAddRect(.outerRegion, .mainRect)
            // create the group of occupants
            set .occupants = CreateGroup()
            // setup the triggers
            set .enterTrig = CreateTrigger()
            set .leaveTrig = CreateTrigger()
            set rTbl[.enterTrig] = this
            set rTbl[.leaveTrig] = this
            call TriggerRegisterEnterRegion(.enterTrig, .mainRegion, null)
            call TriggerRegisterLeaveRegion(.leaveTrig, .outerRegion, null)
            call TriggerAddAction(.enterTrig, function thistype.eventEnter)
            call TriggerAddAction(.leaveTrig, function thistype.eventLeave)
            return this
        endmethod
    endstruct
    private function Init takes nothing returns nothing
        set rTbl = HandleTable.create()
        set l = Location(0,0)
        set g = CreateGroup()
    endfunction
endlibrary
__________________
Ever so slightly active.
Table:
Past Projects:
The Spirit of Vengeance (Cinematic, Blizzard contest winner)
Elimination Tournament
Mortar Ball
iNfraNe is offline   Reply With Quote
Sponsored Links - Login to hide this ad!
Reply


Thread Tools Search this Thread
Search this Thread:

Advanced Search

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off


All times are GMT. The time now is 05:40 PM.


Affiliates
The Hubb The JASS Vault Clan WEnW Campaign Creations Clan CBS GamesModding Flixreel Videos

Powered by vBulletin (Copyright ©2000 - 2019, Jelsoft Enterprises Ltd).
Hosted by www.OICcam.com
IT Support and Services provided by Executive IT Services