1. Updated Resource Submission Rules: All model & skin resource submissions must now include an in-game screenshot. This is to help speed up the moderation process and to show how the model and/or texture looks like from the in-game camera.
    Dismiss Notice
  2. DID YOU KNOW - That you can unlock new rank icons by posting on the forums or winning contests? Click here to customize your rank or read our User Rank Policy to see a list of ranks that you can unlock. Have you won a contest and still haven't received your rank award? Then please contact the administration.
    Dismiss Notice
  3. Join the 6th Melee Mapping Contest for a chance to have your map featured in this year's Hive Cup!
    Dismiss Notice
  4. Shoot to thrill, play to kill. Sate your hunger with the 33rd Modeling Contest!
    Dismiss Notice
  5. Do you hear boss music? It's the 17th Mini Mapping Contest!
    Dismiss Notice
  6. Weave light to take you to your highest hopes - the 6th Special Effect Contest is here!
    Dismiss Notice
  7. Check out the Staff job openings thread.
    Dismiss Notice
Dismiss Notice
60,000 passwords have been reset on July 8, 2019. If you cannot login, read this.

blizzard.j -- patch 1.31.1

  • IcemanBo

    IcemanBo

    Joined:
    Sep 6, 2013
    Messages:
    6,517
    Resources:
    22
    Maps:
    3
    Spells:
    11
    Template:
    1
    Tutorials:
    4
    JASS:
    3
    Resources:
    22
    Code (vJASS):

    //===========================================================================
    // Blizzard.j ( define Jass2 functions that need to be in every map script )
    //===========================================================================


    globals
        //-----------------------------------------------------------------------
        // Constants
        //

        // Misc constants
        constant real      bj_PI                            = 3.14159
        constant real      bj_E                             = 2.71828
        constant real      bj_CELLWIDTH                     = 128.0
        constant real      bj_CLIFFHEIGHT                   = 128.0
        constant real      bj_UNIT_FACING                   = 270.0
        constant real      bj_RADTODEG                      = 180.0/bj_PI
        constant real      bj_DEGTORAD                      = bj_PI/180.0
        constant real      bj_TEXT_DELAY_QUEST              = 20.00
        constant real      bj_TEXT_DELAY_QUESTUPDATE        = 20.00
        constant real      bj_TEXT_DELAY_QUESTDONE          = 20.00
        constant real      bj_TEXT_DELAY_QUESTFAILED        = 20.00
        constant real      bj_TEXT_DELAY_QUESTREQUIREMENT   = 20.00
        constant real      bj_TEXT_DELAY_MISSIONFAILED      = 20.00
        constant real      bj_TEXT_DELAY_ALWAYSHINT         = 12.00
        constant real      bj_TEXT_DELAY_HINT               = 12.00
        constant real      bj_TEXT_DELAY_SECRET             = 10.00
        constant real      bj_TEXT_DELAY_UNITACQUIRED       = 15.00
        constant real      bj_TEXT_DELAY_UNITAVAILABLE      = 10.00
        constant real      bj_TEXT_DELAY_ITEMACQUIRED       = 10.00
        constant real      bj_TEXT_DELAY_WARNING            = 12.00
        constant real      bj_QUEUE_DELAY_QUEST             =  5.00
        constant real      bj_QUEUE_DELAY_HINT              =  5.00
        constant real      bj_QUEUE_DELAY_SECRET            =  3.00
        constant real      bj_HANDICAP_EASY                 = 60.00
        constant real      bj_GAME_STARTED_THRESHOLD        =  0.01
        constant real      bj_WAIT_FOR_COND_MIN_INTERVAL    =  0.10
        constant real      bj_POLLED_WAIT_INTERVAL          =  0.10
        constant real      bj_POLLED_WAIT_SKIP_THRESHOLD    =  2.00

        // Game constants
        constant integer   bj_MAX_INVENTORY                 =  6
        constant integer   bj_MAX_PLAYERS                   =  GetBJMaxPlayers()
        constant integer   bj_PLAYER_NEUTRAL_VICTIM         =  GetBJPlayerNeutralVictim()
        constant integer   bj_PLAYER_NEUTRAL_EXTRA          =  GetBJPlayerNeutralExtra()
        constant integer   bj_MAX_PLAYER_SLOTS              =  GetBJMaxPlayerSlots()
        constant integer   bj_MAX_SKELETONS                 =  25
        constant integer   bj_MAX_STOCK_ITEM_SLOTS          =  11
        constant integer   bj_MAX_STOCK_UNIT_SLOTS          =  11
        constant integer   bj_MAX_ITEM_LEVEL                =  10

        // Ideally these would be looked up from Units/MiscData.txt,
        // but there is currently no script functionality exposed to do that
        constant real      bj_TOD_DAWN                      = 6.00
        constant real      bj_TOD_DUSK                      = 18.00

        // Melee game settings:
        //   - Starting Time of Day (TOD)
        //   - Starting Gold
        //   - Starting Lumber
        //   - Starting Hero Tokens (free heroes)
        //   - Max heroes allowed per player
        //   - Max heroes allowed per hero type
        //   - Distance from start loc to search for nearby mines
        //
        constant real      bj_MELEE_STARTING_TOD            = 8.00
        constant integer   bj_MELEE_STARTING_GOLD_V0        = 750
        constant integer   bj_MELEE_STARTING_GOLD_V1        = 500
        constant integer   bj_MELEE_STARTING_LUMBER_V0      = 200
        constant integer   bj_MELEE_STARTING_LUMBER_V1      = 150
        constant integer   bj_MELEE_STARTING_HERO_TOKENS    = 1
        constant integer   bj_MELEE_HERO_LIMIT              = 3
        constant integer   bj_MELEE_HERO_TYPE_LIMIT         = 1
        constant real      bj_MELEE_MINE_SEARCH_RADIUS      = 2000
        constant real      bj_MELEE_CLEAR_UNITS_RADIUS      = 1500
        constant real      bj_MELEE_CRIPPLE_TIMEOUT         = 120.00
        constant real      bj_MELEE_CRIPPLE_MSG_DURATION    = 20.00
        constant integer   bj_MELEE_MAX_TWINKED_HEROES_V0   = 3
        constant integer   bj_MELEE_MAX_TWINKED_HEROES_V1   = 1

        // Delay between a creep's death and the time it may drop an item.
        constant real      bj_CREEP_ITEM_DELAY              = 0.50

        // Timing settings for Marketplace inventories.
        constant real      bj_STOCK_RESTOCK_INITIAL_DELAY   = 120
        constant real      bj_STOCK_RESTOCK_INTERVAL        = 30
        constant integer   bj_STOCK_MAX_ITERATIONS          = 20

        // Max events registered by a single "dest dies in region" event.
        constant integer   bj_MAX_DEST_IN_REGION_EVENTS     = 64

        // Camera settings
        constant integer   bj_CAMERA_MIN_FARZ               = 100
        constant integer   bj_CAMERA_DEFAULT_DISTANCE       = 1650
        constant integer   bj_CAMERA_DEFAULT_FARZ           = 5000
        constant integer   bj_CAMERA_DEFAULT_AOA            = 304
        constant integer   bj_CAMERA_DEFAULT_FOV            = 70
        constant integer   bj_CAMERA_DEFAULT_ROLL           = 0
        constant integer   bj_CAMERA_DEFAULT_ROTATION       = 90

        // Rescue
        constant real      bj_RESCUE_PING_TIME              = 2.00

        // Transmission behavior settings
        constant real      bj_NOTHING_SOUND_DURATION        = 5.00
        constant real      bj_TRANSMISSION_PING_TIME        = 1.00
        constant integer   bj_TRANSMISSION_IND_RED          = 255
        constant integer   bj_TRANSMISSION_IND_BLUE         = 255
        constant integer   bj_TRANSMISSION_IND_GREEN        = 255
        constant integer   bj_TRANSMISSION_IND_ALPHA        = 255
        constant real      bj_TRANSMISSION_PORT_HANGTIME    = 1.50

        // Cinematic mode settings
        constant real      bj_CINEMODE_INTERFACEFADE        = 0.50
        constant gamespeed bj_CINEMODE_GAMESPEED            = MAP_SPEED_NORMAL

        // Cinematic mode volume levels
        constant real      bj_CINEMODE_VOLUME_UNITMOVEMENT  = 0.40
        constant real      bj_CINEMODE_VOLUME_UNITSOUNDS    = 0.00
        constant real      bj_CINEMODE_VOLUME_COMBAT        = 0.40
        constant real      bj_CINEMODE_VOLUME_SPELLS        = 0.40
        constant real      bj_CINEMODE_VOLUME_UI            = 0.00
        constant real      bj_CINEMODE_VOLUME_MUSIC         = 0.55
        constant real      bj_CINEMODE_VOLUME_AMBIENTSOUNDS = 1.00
        constant real      bj_CINEMODE_VOLUME_FIRE          = 0.60

        // Speech mode volume levels
        constant real      bj_SPEECH_VOLUME_UNITMOVEMENT    = 0.25
        constant real      bj_SPEECH_VOLUME_UNITSOUNDS      = 0.00
        constant real      bj_SPEECH_VOLUME_COMBAT          = 0.25
        constant real      bj_SPEECH_VOLUME_SPELLS          = 0.25
        constant real      bj_SPEECH_VOLUME_UI              = 0.00
        constant real      bj_SPEECH_VOLUME_MUSIC           = 0.55
        constant real      bj_SPEECH_VOLUME_AMBIENTSOUNDS   = 1.00
        constant real      bj_SPEECH_VOLUME_FIRE            = 0.60

        // Smart pan settings
        constant real      bj_SMARTPAN_TRESHOLD_PAN         = 500
        constant real      bj_SMARTPAN_TRESHOLD_SNAP        = 3500

        // QueuedTriggerExecute settings
        constant integer   bj_MAX_QUEUED_TRIGGERS           = 100
        constant real      bj_QUEUED_TRIGGER_TIMEOUT        = 180.00

        // Campaign indexing constants
        constant integer   bj_CAMPAIGN_INDEX_T        = 0
        constant integer   bj_CAMPAIGN_INDEX_H        = 1
        constant integer   bj_CAMPAIGN_INDEX_U        = 2
        constant integer   bj_CAMPAIGN_INDEX_O        = 3
        constant integer   bj_CAMPAIGN_INDEX_N        = 4
        constant integer   bj_CAMPAIGN_INDEX_XN       = 5
        constant integer   bj_CAMPAIGN_INDEX_XH       = 6
        constant integer   bj_CAMPAIGN_INDEX_XU       = 7
        constant integer   bj_CAMPAIGN_INDEX_XO       = 8

        // Campaign offset constants (for mission indexing)
        constant integer   bj_CAMPAIGN_OFFSET_T       = 0
        constant integer   bj_CAMPAIGN_OFFSET_H       = 1
        constant integer   bj_CAMPAIGN_OFFSET_U       = 2
        constant integer   bj_CAMPAIGN_OFFSET_O       = 3
        constant integer   bj_CAMPAIGN_OFFSET_N       = 4
        constant integer   bj_CAMPAIGN_OFFSET_XN      = 0
        constant integer   bj_CAMPAIGN_OFFSET_XH      = 1
        constant integer   bj_CAMPAIGN_OFFSET_XU      = 2
        constant integer   bj_CAMPAIGN_OFFSET_XO      = 3

        // Mission indexing constants
        // Tutorial
        constant integer   bj_MISSION_INDEX_T00       = bj_CAMPAIGN_OFFSET_T * 1000 + 0
        constant integer   bj_MISSION_INDEX_T01       = bj_CAMPAIGN_OFFSET_T * 1000 + 1
        // Human
        constant integer   bj_MISSION_INDEX_H00       = bj_CAMPAIGN_OFFSET_H * 1000 + 0
        constant integer   bj_MISSION_INDEX_H01       = bj_CAMPAIGN_OFFSET_H * 1000 + 1
        constant integer   bj_MISSION_INDEX_H02       = bj_CAMPAIGN_OFFSET_H * 1000 + 2
        constant integer   bj_MISSION_INDEX_H03       = bj_CAMPAIGN_OFFSET_H * 1000 + 3
        constant integer   bj_MISSION_INDEX_H04       = bj_CAMPAIGN_OFFSET_H * 1000 + 4
        constant integer   bj_MISSION_INDEX_H05       = bj_CAMPAIGN_OFFSET_H * 1000 + 5
        constant integer   bj_MISSION_INDEX_H06       = bj_CAMPAIGN_OFFSET_H * 1000 + 6
        constant integer   bj_MISSION_INDEX_H07       = bj_CAMPAIGN_OFFSET_H * 1000 + 7
        constant integer   bj_MISSION_INDEX_H08       = bj_CAMPAIGN_OFFSET_H * 1000 + 8
        constant integer   bj_MISSION_INDEX_H09       = bj_CAMPAIGN_OFFSET_H * 1000 + 9
        constant integer   bj_MISSION_INDEX_H10       = bj_CAMPAIGN_OFFSET_H * 1000 + 10
        constant integer   bj_MISSION_INDEX_H11       = bj_CAMPAIGN_OFFSET_H * 1000 + 11
        // Undead
        constant integer   bj_MISSION_INDEX_U00       = bj_CAMPAIGN_OFFSET_U * 1000 + 0
        constant integer   bj_MISSION_INDEX_U01       = bj_CAMPAIGN_OFFSET_U * 1000 + 1
        constant integer   bj_MISSION_INDEX_U02       = bj_CAMPAIGN_OFFSET_U * 1000 + 2
        constant integer   bj_MISSION_INDEX_U03       = bj_CAMPAIGN_OFFSET_U * 1000 + 3
        constant integer   bj_MISSION_INDEX_U05       = bj_CAMPAIGN_OFFSET_U * 1000 + 4
        constant integer   bj_MISSION_INDEX_U07       = bj_CAMPAIGN_OFFSET_U * 1000 + 5
        constant integer   bj_MISSION_INDEX_U08       = bj_CAMPAIGN_OFFSET_U * 1000 + 6
        constant integer   bj_MISSION_INDEX_U09       = bj_CAMPAIGN_OFFSET_U * 1000 + 7
        constant integer   bj_MISSION_INDEX_U10       = bj_CAMPAIGN_OFFSET_U * 1000 + 8
        constant integer   bj_MISSION_INDEX_U11       = bj_CAMPAIGN_OFFSET_U * 1000 + 9
        // Orc
        constant integer   bj_MISSION_INDEX_O00       = bj_CAMPAIGN_OFFSET_O * 1000 + 0
        constant integer   bj_MISSION_INDEX_O01       = bj_CAMPAIGN_OFFSET_O * 1000 + 1
        constant integer   bj_MISSION_INDEX_O02       = bj_CAMPAIGN_OFFSET_O * 1000 + 2
        constant integer   bj_MISSION_INDEX_O03       = bj_CAMPAIGN_OFFSET_O * 1000 + 3
        constant integer   bj_MISSION_INDEX_O04       = bj_CAMPAIGN_OFFSET_O * 1000 + 4
        constant integer   bj_MISSION_INDEX_O05       = bj_CAMPAIGN_OFFSET_O * 1000 + 5
        constant integer   bj_MISSION_INDEX_O06       = bj_CAMPAIGN_OFFSET_O * 1000 + 6
        constant integer   bj_MISSION_INDEX_O07       = bj_CAMPAIGN_OFFSET_O * 1000 + 7
        constant integer   bj_MISSION_INDEX_O08       = bj_CAMPAIGN_OFFSET_O * 1000 + 8
        constant integer   bj_MISSION_INDEX_O09       = bj_CAMPAIGN_OFFSET_O * 1000 + 9
        constant integer   bj_MISSION_INDEX_O10       = bj_CAMPAIGN_OFFSET_O * 1000 + 10
        // Night Elf
        constant integer   bj_MISSION_INDEX_N00       = bj_CAMPAIGN_OFFSET_N * 1000 + 0
        constant integer   bj_MISSION_INDEX_N01       = bj_CAMPAIGN_OFFSET_N * 1000 + 1
        constant integer   bj_MISSION_INDEX_N02       = bj_CAMPAIGN_OFFSET_N * 1000 + 2
        constant integer   bj_MISSION_INDEX_N03       = bj_CAMPAIGN_OFFSET_N * 1000 + 3
        constant integer   bj_MISSION_INDEX_N04       = bj_CAMPAIGN_OFFSET_N * 1000 + 4
        constant integer   bj_MISSION_INDEX_N05       = bj_CAMPAIGN_OFFSET_N * 1000 + 5
        constant integer   bj_MISSION_INDEX_N06       = bj_CAMPAIGN_OFFSET_N * 1000 + 6
        constant integer   bj_MISSION_INDEX_N07       = bj_CAMPAIGN_OFFSET_N * 1000 + 7
        constant integer   bj_MISSION_INDEX_N08       = bj_CAMPAIGN_OFFSET_N * 1000 + 8
        constant integer   bj_MISSION_INDEX_N09       = bj_CAMPAIGN_OFFSET_N * 1000 + 9
        // Expansion Night Elf
        constant integer   bj_MISSION_INDEX_XN00       = bj_CAMPAIGN_OFFSET_XN * 1000 + 0
        constant integer   bj_MISSION_INDEX_XN01       = bj_CAMPAIGN_OFFSET_XN * 1000 + 1
        constant integer   bj_MISSION_INDEX_XN02       = bj_CAMPAIGN_OFFSET_XN * 1000 + 2
        constant integer   bj_MISSION_INDEX_XN03       = bj_CAMPAIGN_OFFSET_XN * 1000 + 3
        constant integer   bj_MISSION_INDEX_XN04       = bj_CAMPAIGN_OFFSET_XN * 1000 + 4
        constant integer   bj_MISSION_INDEX_XN05       = bj_CAMPAIGN_OFFSET_XN * 1000 + 5
        constant integer   bj_MISSION_INDEX_XN06       = bj_CAMPAIGN_OFFSET_XN * 1000 + 6
        constant integer   bj_MISSION_INDEX_XN07       = bj_CAMPAIGN_OFFSET_XN * 1000 + 7
        constant integer   bj_MISSION_INDEX_XN08       = bj_CAMPAIGN_OFFSET_XN * 1000 + 8
        constant integer   bj_MISSION_INDEX_XN09       = bj_CAMPAIGN_OFFSET_XN * 1000 + 9
        constant integer   bj_MISSION_INDEX_XN10       = bj_CAMPAIGN_OFFSET_XN * 1000 + 10
        // Expansion Human
        constant integer   bj_MISSION_INDEX_XH00       = bj_CAMPAIGN_OFFSET_XH * 1000 + 0
        constant integer   bj_MISSION_INDEX_XH01       = bj_CAMPAIGN_OFFSET_XH * 1000 + 1
        constant integer   bj_MISSION_INDEX_XH02       = bj_CAMPAIGN_OFFSET_XH * 1000 + 2
        constant integer   bj_MISSION_INDEX_XH03       = bj_CAMPAIGN_OFFSET_XH * 1000 + 3
        constant integer   bj_MISSION_INDEX_XH04       = bj_CAMPAIGN_OFFSET_XH * 1000 + 4
        constant integer   bj_MISSION_INDEX_XH05       = bj_CAMPAIGN_OFFSET_XH * 1000 + 5
        constant integer   bj_MISSION_INDEX_XH06       = bj_CAMPAIGN_OFFSET_XH * 1000 + 6
        constant integer   bj_MISSION_INDEX_XH07       = bj_CAMPAIGN_OFFSET_XH * 1000 + 7
        constant integer   bj_MISSION_INDEX_XH08       = bj_CAMPAIGN_OFFSET_XH * 1000 + 8
        constant integer   bj_MISSION_INDEX_XH09       = bj_CAMPAIGN_OFFSET_XH * 1000 + 9
        // Expansion Undead
        constant integer   bj_MISSION_INDEX_XU00       = bj_CAMPAIGN_OFFSET_XU * 1000 + 0
        constant integer   bj_MISSION_INDEX_XU01       = bj_CAMPAIGN_OFFSET_XU * 1000 + 1
        constant integer   bj_MISSION_INDEX_XU02       = bj_CAMPAIGN_OFFSET_XU * 1000 + 2
        constant integer   bj_MISSION_INDEX_XU03       = bj_CAMPAIGN_OFFSET_XU * 1000 + 3
        constant integer   bj_MISSION_INDEX_XU04       = bj_CAMPAIGN_OFFSET_XU * 1000 + 4
        constant integer   bj_MISSION_INDEX_XU05       = bj_CAMPAIGN_OFFSET_XU * 1000 + 5
        constant integer   bj_MISSION_INDEX_XU06       = bj_CAMPAIGN_OFFSET_XU * 1000 + 6
        constant integer   bj_MISSION_INDEX_XU07       = bj_CAMPAIGN_OFFSET_XU * 1000 + 7
        constant integer   bj_MISSION_INDEX_XU08       = bj_CAMPAIGN_OFFSET_XU * 1000 + 8
        constant integer   bj_MISSION_INDEX_XU09       = bj_CAMPAIGN_OFFSET_XU * 1000 + 9
        constant integer   bj_MISSION_INDEX_XU10       = bj_CAMPAIGN_OFFSET_XU * 1000 + 10
        constant integer   bj_MISSION_INDEX_XU11       = bj_CAMPAIGN_OFFSET_XU * 1000 + 11
        constant integer   bj_MISSION_INDEX_XU12       = bj_CAMPAIGN_OFFSET_XU * 1000 + 12
        constant integer   bj_MISSION_INDEX_XU13       = bj_CAMPAIGN_OFFSET_XU * 1000 + 13

        // Expansion Orc
        constant integer   bj_MISSION_INDEX_XO00       = bj_CAMPAIGN_OFFSET_XO * 1000 + 0
        constant integer   bj_MISSION_INDEX_XO01       = bj_CAMPAIGN_OFFSET_XO * 1000 + 1
        constant integer   bj_MISSION_INDEX_XO02       = bj_CAMPAIGN_OFFSET_XO * 1000 + 2
        constant integer   bj_MISSION_INDEX_XO03       = bj_CAMPAIGN_OFFSET_XO * 1000 + 3

        // Cinematic indexing constants
        constant integer   bj_CINEMATICINDEX_TOP      = 0
        constant integer   bj_CINEMATICINDEX_HOP      = 1
        constant integer   bj_CINEMATICINDEX_HED      = 2
        constant integer   bj_CINEMATICINDEX_OOP      = 3
        constant integer   bj_CINEMATICINDEX_OED      = 4
        constant integer   bj_CINEMATICINDEX_UOP      = 5
        constant integer   bj_CINEMATICINDEX_UED      = 6
        constant integer   bj_CINEMATICINDEX_NOP      = 7
        constant integer   bj_CINEMATICINDEX_NED      = 8
        constant integer   bj_CINEMATICINDEX_XOP      = 9
        constant integer   bj_CINEMATICINDEX_XED      = 10

        // Alliance settings
        constant integer   bj_ALLIANCE_UNALLIED        = 0
        constant integer   bj_ALLIANCE_UNALLIED_VISION = 1
        constant integer   bj_ALLIANCE_ALLIED          = 2
        constant integer   bj_ALLIANCE_ALLIED_VISION   = 3
        constant integer   bj_ALLIANCE_ALLIED_UNITS    = 4
        constant integer   bj_ALLIANCE_ALLIED_ADVUNITS = 5
        constant integer   bj_ALLIANCE_NEUTRAL         = 6
        constant integer   bj_ALLIANCE_NEUTRAL_VISION  = 7

        // Keyboard Event Types
        constant integer   bj_KEYEVENTTYPE_DEPRESS     = 0
        constant integer   bj_KEYEVENTTYPE_RELEASE     = 1

        // Keyboard Event Keys
        constant integer   bj_KEYEVENTKEY_LEFT         = 0
        constant integer   bj_KEYEVENTKEY_RIGHT        = 1
        constant integer   bj_KEYEVENTKEY_DOWN         = 2
        constant integer   bj_KEYEVENTKEY_UP           = 3

        // Mouse Event Types
        constant integer   bj_MOUSEEVENTTYPE_DOWN     = 0
        constant integer   bj_MOUSEEVENTTYPE_UP       = 1
        constant integer   bj_MOUSEEVENTTYPE_MOVE     = 2

        // Transmission timing methods
        constant integer   bj_TIMETYPE_ADD             = 0
        constant integer   bj_TIMETYPE_SET             = 1
        constant integer   bj_TIMETYPE_SUB             = 2

        // Camera bounds adjustment methods
        constant integer   bj_CAMERABOUNDS_ADJUST_ADD  = 0
        constant integer   bj_CAMERABOUNDS_ADJUST_SUB  = 1

        // Quest creation states
        constant integer   bj_QUESTTYPE_REQ_DISCOVERED   = 0
        constant integer   bj_QUESTTYPE_REQ_UNDISCOVERED = 1
        constant integer   bj_QUESTTYPE_OPT_DISCOVERED   = 2
        constant integer   bj_QUESTTYPE_OPT_UNDISCOVERED = 3

        // Quest message types
        constant integer   bj_QUESTMESSAGE_DISCOVERED    = 0
        constant integer   bj_QUESTMESSAGE_UPDATED       = 1
        constant integer   bj_QUESTMESSAGE_COMPLETED     = 2
        constant integer   bj_QUESTMESSAGE_FAILED        = 3
        constant integer   bj_QUESTMESSAGE_REQUIREMENT   = 4
        constant integer   bj_QUESTMESSAGE_MISSIONFAILED = 5
        constant integer   bj_QUESTMESSAGE_ALWAYSHINT    = 6
        constant integer   bj_QUESTMESSAGE_HINT          = 7
        constant integer   bj_QUESTMESSAGE_SECRET        = 8
        constant integer   bj_QUESTMESSAGE_UNITACQUIRED  = 9
        constant integer   bj_QUESTMESSAGE_UNITAVAILABLE = 10
        constant integer   bj_QUESTMESSAGE_ITEMACQUIRED  = 11
        constant integer   bj_QUESTMESSAGE_WARNING       = 12

        // Leaderboard sorting methods
        constant integer   bj_SORTTYPE_SORTBYVALUE     = 0
        constant integer   bj_SORTTYPE_SORTBYPLAYER    = 1
        constant integer   bj_SORTTYPE_SORTBYLABEL     = 2

        // Cinematic fade filter methods
        constant integer   bj_CINEFADETYPE_FADEIN      = 0
        constant integer   bj_CINEFADETYPE_FADEOUT     = 1
        constant integer   bj_CINEFADETYPE_FADEOUTIN   = 2

        // Buff removal methods
        constant integer   bj_REMOVEBUFFS_POSITIVE     = 0
        constant integer   bj_REMOVEBUFFS_NEGATIVE     = 1
        constant integer   bj_REMOVEBUFFS_ALL          = 2
        constant integer   bj_REMOVEBUFFS_NONTLIFE     = 3

        // Buff properties - polarity
        constant integer   bj_BUFF_POLARITY_POSITIVE   = 0
        constant integer   bj_BUFF_POLARITY_NEGATIVE   = 1
        constant integer   bj_BUFF_POLARITY_EITHER     = 2

        // Buff properties - resist type
        constant integer   bj_BUFF_RESIST_MAGIC        = 0
        constant integer   bj_BUFF_RESIST_PHYSICAL     = 1
        constant integer   bj_BUFF_RESIST_EITHER       = 2
        constant integer   bj_BUFF_RESIST_BOTH         = 3

        // Hero stats
        constant integer   bj_HEROSTAT_STR             = 0
        constant integer   bj_HEROSTAT_AGI             = 1
        constant integer   bj_HEROSTAT_INT             = 2

        // Hero skill point modification methods
        constant integer   bj_MODIFYMETHOD_ADD    = 0
        constant integer   bj_MODIFYMETHOD_SUB    = 1
        constant integer   bj_MODIFYMETHOD_SET    = 2

        // Unit state adjustment methods (for replaced units)
        constant integer   bj_UNIT_STATE_METHOD_ABSOLUTE = 0
        constant integer   bj_UNIT_STATE_METHOD_RELATIVE = 1
        constant integer   bj_UNIT_STATE_METHOD_DEFAULTS = 2
        constant integer   bj_UNIT_STATE_METHOD_MAXIMUM  = 3

        // Gate operations
        constant integer   bj_GATEOPERATION_CLOSE      = 0
        constant integer   bj_GATEOPERATION_OPEN       = 1
        constant integer   bj_GATEOPERATION_DESTROY    = 2

        // Game cache value types
        constant integer   bj_GAMECACHE_BOOLEAN                 = 0
        constant integer   bj_GAMECACHE_INTEGER                 = 1
        constant integer   bj_GAMECACHE_REAL                    = 2
        constant integer   bj_GAMECACHE_UNIT                    = 3
        constant integer   bj_GAMECACHE_STRING                  = 4
       
        // Hashtable value types
        constant integer   bj_HASHTABLE_BOOLEAN                 = 0
        constant integer   bj_HASHTABLE_INTEGER                 = 1
        constant integer   bj_HASHTABLE_REAL                    = 2
        constant integer   bj_HASHTABLE_STRING                  = 3
        constant integer   bj_HASHTABLE_HANDLE                  = 4

        // Item status types
        constant integer   bj_ITEM_STATUS_HIDDEN       = 0
        constant integer   bj_ITEM_STATUS_OWNED        = 1
        constant integer   bj_ITEM_STATUS_INVULNERABLE = 2
        constant integer   bj_ITEM_STATUS_POWERUP      = 3
        constant integer   bj_ITEM_STATUS_SELLABLE     = 4
        constant integer   bj_ITEM_STATUS_PAWNABLE     = 5

        // Itemcode status types
        constant integer   bj_ITEMCODE_STATUS_POWERUP  = 0
        constant integer   bj_ITEMCODE_STATUS_SELLABLE = 1
        constant integer   bj_ITEMCODE_STATUS_PAWNABLE = 2

        // Minimap ping styles
        constant integer   bj_MINIMAPPINGSTYLE_SIMPLE  = 0
        constant integer   bj_MINIMAPPINGSTYLE_FLASHY  = 1
        constant integer   bj_MINIMAPPINGSTYLE_ATTACK  = 2

        // Corpse creation settings
        constant real      bj_CORPSE_MAX_DEATH_TIME    = 8.00

        // Corpse creation styles
        constant integer   bj_CORPSETYPE_FLESH         = 0
        constant integer   bj_CORPSETYPE_BONE          = 1

        // Elevator pathing-blocker destructable code
        constant integer   bj_ELEVATOR_BLOCKER_CODE    = 'DTep'
        constant integer   bj_ELEVATOR_CODE01          = 'DTrf'
        constant integer   bj_ELEVATOR_CODE02          = 'DTrx'

        // Elevator wall codes
        constant integer   bj_ELEVATOR_WALL_TYPE_ALL        = 0
        constant integer   bj_ELEVATOR_WALL_TYPE_EAST       = 1
        constant integer   bj_ELEVATOR_WALL_TYPE_NORTH      = 2
        constant integer   bj_ELEVATOR_WALL_TYPE_SOUTH      = 3
        constant integer   bj_ELEVATOR_WALL_TYPE_WEST       = 4

        //-----------------------------------------------------------------------
        // Variables
        //

        // Force predefs
        force              bj_FORCE_ALL_PLAYERS        = null
        force array        bj_FORCE_PLAYER

        integer            bj_MELEE_MAX_TWINKED_HEROES = 0

        // Map area rects
        rect               bj_mapInitialPlayableArea   = null
        rect               bj_mapInitialCameraBounds   = null

        // Utility function vars
        integer            bj_forLoopAIndex            = 0
        integer            bj_forLoopBIndex            = 0
        integer            bj_forLoopAIndexEnd         = 0
        integer            bj_forLoopBIndexEnd         = 0

        boolean            bj_slotControlReady         = false
        boolean array      bj_slotControlUsed
        mapcontrol array   bj_slotControl

        // Game started detection vars
        timer              bj_gameStartedTimer         = null
        boolean            bj_gameStarted              = false
        timer              bj_volumeGroupsTimer        = CreateTimer()

        // Singleplayer check
        boolean            bj_isSinglePlayer           = false

        // Day/Night Cycle vars
        trigger            bj_dncSoundsDay             = null
        trigger            bj_dncSoundsNight           = null
        sound              bj_dayAmbientSound          = null
        sound              bj_nightAmbientSound        = null
        trigger            bj_dncSoundsDawn            = null
        trigger            bj_dncSoundsDusk            = null
        sound              bj_dawnSound                = null
        sound              bj_duskSound                = null
        boolean            bj_useDawnDuskSounds        = true
        boolean            bj_dncIsDaytime             = false

        // Triggered sounds
        //sound              bj_pingMinimapSound         = null
        sound              bj_rescueSound              = null
        sound              bj_questDiscoveredSound     = null
        sound              bj_questUpdatedSound        = null
        sound              bj_questCompletedSound      = null
        sound              bj_questFailedSound         = null
        sound              bj_questHintSound           = null
        sound              bj_questSecretSound         = null
        sound              bj_questItemAcquiredSound   = null
        sound              bj_questWarningSound        = null
        sound              bj_victoryDialogSound       = null
        sound              bj_defeatDialogSound        = null

        // Marketplace vars
        trigger            bj_stockItemPurchased       = null
        timer              bj_stockUpdateTimer         = null
        boolean array      bj_stockAllowedPermanent
        boolean array      bj_stockAllowedCharged
        boolean array      bj_stockAllowedArtifact
        integer            bj_stockPickedItemLevel     = 0
        itemtype           bj_stockPickedItemType

        // Melee vars
        trigger            bj_meleeVisibilityTrained   = null
        boolean            bj_meleeVisibilityIsDay     = true
        boolean            bj_meleeGrantHeroItems      = false
        location           bj_meleeNearestMineToLoc    = null
        unit               bj_meleeNearestMine         = null
        real               bj_meleeNearestMineDist     = 0.00
        boolean            bj_meleeGameOver            = false
        boolean array      bj_meleeDefeated
        boolean array      bj_meleeVictoried
        unit array         bj_ghoul
        timer array        bj_crippledTimer
        timerdialog array  bj_crippledTimerWindows
        boolean array      bj_playerIsCrippled
        boolean array      bj_playerIsExposed
        boolean            bj_finishSoonAllExposed     = false
        timerdialog        bj_finishSoonTimerDialog    = null
        integer array      bj_meleeTwinkedHeroes

        // Rescue behavior vars
        trigger            bj_rescueUnitBehavior       = null
        boolean            bj_rescueChangeColorUnit    = true
        boolean            bj_rescueChangeColorBldg    = true

        // Transmission vars
        timer              bj_cineSceneEndingTimer     = null
        sound              bj_cineSceneLastSound       = null
        trigger            bj_cineSceneBeingSkipped    = null

        // Cinematic mode vars
        gamespeed          bj_cineModePriorSpeed       = MAP_SPEED_NORMAL
        boolean            bj_cineModePriorFogSetting  = false
        boolean            bj_cineModePriorMaskSetting = false
        boolean            bj_cineModeAlreadyIn        = false
        boolean            bj_cineModePriorDawnDusk    = false
        integer            bj_cineModeSavedSeed        = 0

        // Cinematic fade vars
        timer              bj_cineFadeFinishTimer      = null
        timer              bj_cineFadeContinueTimer    = null
        real               bj_cineFadeContinueRed      = 0
        real               bj_cineFadeContinueGreen    = 0
        real               bj_cineFadeContinueBlue     = 0
        real               bj_cineFadeContinueTrans    = 0
        real               bj_cineFadeContinueDuration = 0
        string             bj_cineFadeContinueTex      = ""

        // QueuedTriggerExecute vars
        integer            bj_queuedExecTotal          = 0
        trigger array      bj_queuedExecTriggers
        boolean array      bj_queuedExecUseConds
        timer              bj_queuedExecTimeoutTimer   = CreateTimer()
        trigger            bj_queuedExecTimeout        = null

        // Helper vars (for Filter and Enum funcs)
        integer            bj_destInRegionDiesCount    = 0
        trigger            bj_destInRegionDiesTrig     = null
        integer            bj_groupCountUnits          = 0
        integer            bj_forceCountPlayers        = 0
        integer            bj_groupEnumTypeId          = 0
        player             bj_groupEnumOwningPlayer    = null
        group              bj_groupAddGroupDest        = null
        group              bj_groupRemoveGroupDest     = null
        integer            bj_groupRandomConsidered    = 0
        unit               bj_groupRandomCurrentPick   = null
        group              bj_groupLastCreatedDest     = null
        group              bj_randomSubGroupGroup      = null
        integer            bj_randomSubGroupWant       = 0
        integer            bj_randomSubGroupTotal      = 0
        real               bj_randomSubGroupChance     = 0
        integer            bj_destRandomConsidered     = 0
        destructable       bj_destRandomCurrentPick    = null
        destructable       bj_elevatorWallBlocker      = null
        destructable       bj_elevatorNeighbor         = null
        integer            bj_itemRandomConsidered     = 0
        item               bj_itemRandomCurrentPick    = null
        integer            bj_forceRandomConsidered    = 0
        player             bj_forceRandomCurrentPick   = null
        unit               bj_makeUnitRescuableUnit    = null
        boolean            bj_makeUnitRescuableFlag    = true
        boolean            bj_pauseAllUnitsFlag        = true
        location           bj_enumDestructableCenter   = null
        real               bj_enumDestructableRadius   = 0
        playercolor        bj_setPlayerTargetColor     = null
        boolean            bj_isUnitGroupDeadResult    = true
        boolean            bj_isUnitGroupEmptyResult   = true
        boolean            bj_isUnitGroupInRectResult  = true
        rect               bj_isUnitGroupInRectRect    = null
        boolean            bj_changeLevelShowScores    = false
        string             bj_changeLevelMapName       = null
        group              bj_suspendDecayFleshGroup   = CreateGroup()
        group              bj_suspendDecayBoneGroup    = CreateGroup()
        timer              bj_delayedSuspendDecayTimer = CreateTimer()
        trigger            bj_delayedSuspendDecayTrig  = null
        integer            bj_livingPlayerUnitsTypeId  = 0
        widget             bj_lastDyingWidget          = null

        // Random distribution vars
        integer            bj_randDistCount            = 0
        integer array      bj_randDistID
        integer array      bj_randDistChance

        // Last X'd vars
        unit               bj_lastCreatedUnit          = null
        item               bj_lastCreatedItem          = null
        item               bj_lastRemovedItem          = null
        unit               bj_lastHauntedGoldMine      = null
        destructable       bj_lastCreatedDestructable  = null
        group              bj_lastCreatedGroup         = CreateGroup()
        fogmodifier        bj_lastCreatedFogModifier   = null
        effect             bj_lastCreatedEffect        = null
        weathereffect      bj_lastCreatedWeatherEffect = null
        terraindeformation bj_lastCreatedTerrainDeformation = null
        quest              bj_lastCreatedQuest         = null
        questitem          bj_lastCreatedQuestItem     = null
        defeatcondition    bj_lastCreatedDefeatCondition = null
        timer              bj_lastStartedTimer         = CreateTimer()
        timerdialog        bj_lastCreatedTimerDialog   = null
        leaderboard        bj_lastCreatedLeaderboard   = null
        multiboard         bj_lastCreatedMultiboard    = null
        sound              bj_lastPlayedSound          = null
        string             bj_lastPlayedMusic          = ""
        real               bj_lastTransmissionDuration = 0
        gamecache          bj_lastCreatedGameCache     = null
        hashtable          bj_lastCreatedHashtable     = null
        unit               bj_lastLoadedUnit           = null
        button             bj_lastCreatedButton        = null
        unit               bj_lastReplacedUnit         = null
        texttag            bj_lastCreatedTextTag       = null
        lightning          bj_lastCreatedLightning     = null
        image              bj_lastCreatedImage         = null
        ubersplat          bj_lastCreatedUbersplat     = null

        // Filter function vars
        boolexpr           filterIssueHauntOrderAtLocBJ      = null
        boolexpr           filterEnumDestructablesInCircleBJ = null
        boolexpr           filterGetUnitsInRectOfPlayer      = null
        boolexpr           filterGetUnitsOfTypeIdAll         = null
        boolexpr           filterGetUnitsOfPlayerAndTypeId   = null
        boolexpr           filterMeleeTrainedUnitIsHeroBJ    = null
        boolexpr           filterLivingPlayerUnitsOfTypeId   = null

        // Memory cleanup vars
        boolean            bj_wantDestroyGroup         = false

        // Instanced Operation Results
        boolean            bj_lastInstObjFuncSuccessful = true
    endglobals



    //***************************************************************************
    //*
    //*  Debugging Functions
    //*
    //***************************************************************************

    //===========================================================================
    function BJDebugMsg takes string msg returns nothing
        local integer i = 0
        loop
            call DisplayTimedTextToPlayer(Player(i),0,0,60,msg)
            set i = i + 1
            exitwhen i == bj_MAX_PLAYERS
        endloop
    endfunction



    //***************************************************************************
    //*
    //*  Math Utility Functions
    //*
    //***************************************************************************

    //===========================================================================
    function RMinBJ takes real a, real b returns real
        if (a < b) then
            return a
        else
            return b
        endif
    endfunction

    //===========================================================================
    function RMaxBJ takes real a, real b returns real
        if (a < b) then
            return b
        else
            return a
        endif
    endfunction

    //===========================================================================
    function RAbsBJ takes real a returns real
        if (a >= 0) then
            return a
        else
            return -a
        endif
    endfunction

    //===========================================================================
    function RSignBJ takes real a returns real
        if (a >= 0.0) then
            return 1.0
        else
            return -1.0
        endif
    endfunction

    //===========================================================================
    function IMinBJ takes integer a, integer b returns integer
        if (a < b) then
            return a
        else
            return b
        endif
    endfunction

    //===========================================================================
    function IMaxBJ takes integer a, integer b returns integer
        if (a < b) then
            return b
        else
            return a
        endif
    endfunction

    //===========================================================================
    function IAbsBJ takes integer a returns integer
        if (a >= 0) then
            return a
        else
            return -a
        endif
    endfunction

    //===========================================================================
    function ISignBJ takes integer a returns integer
        if (a >= 0) then
            return 1
        else
            return -1
        endif
    endfunction

    //===========================================================================
    function SinBJ takes real degrees returns real
        return Sin(degrees * bj_DEGTORAD)
    endfunction

    //===========================================================================
    function CosBJ takes real degrees returns real
        return Cos(degrees * bj_DEGTORAD)
    endfunction

    //===========================================================================
    function TanBJ takes real degrees returns real
        return Tan(degrees * bj_DEGTORAD)
    endfunction

    //===========================================================================
    function AsinBJ takes real degrees returns real
        return Asin(degrees) * bj_RADTODEG
    endfunction

    //===========================================================================
    function AcosBJ takes real degrees returns real
        return Acos(degrees) * bj_RADTODEG
    endfunction

    //===========================================================================
    function AtanBJ takes real degrees returns real
        return Atan(degrees) * bj_RADTODEG
    endfunction

    //===========================================================================
    function Atan2BJ takes real y, real x returns real
        return Atan2(y, x) * bj_RADTODEG
    endfunction

    //===========================================================================
    function AngleBetweenPoints takes location locA, location locB returns real
        return bj_RADTODEG * Atan2(GetLocationY(locB) - GetLocationY(locA), GetLocationX(locB) - GetLocationX(locA))
    endfunction

    //===========================================================================
    function DistanceBetweenPoints takes location locA, location locB returns real
        local real dx = GetLocationX(locB) - GetLocationX(locA)
        local real dy = GetLocationY(locB) - GetLocationY(locA)
        return SquareRoot(dx * dx + dy * dy)
    endfunction

    //===========================================================================
    function PolarProjectionBJ takes location source, real dist, real angle returns location
        local real x = GetLocationX(source) + dist * Cos(angle * bj_DEGTORAD)
        local real y = GetLocationY(source) + dist * Sin(angle * bj_DEGTORAD)
        return Location(x, y)
    endfunction

    //===========================================================================
    function GetRandomDirectionDeg takes nothing returns real
        return GetRandomReal(0, 360)
    endfunction

    //===========================================================================
    function GetRandomPercentageBJ takes nothing returns real
        return GetRandomReal(0, 100)
    endfunction

    //===========================================================================
    function GetRandomLocInRect takes rect whichRect returns location
        return Location(GetRandomReal(GetRectMinX(whichRect), GetRectMaxX(whichRect)), GetRandomReal(GetRectMinY(whichRect), GetRectMaxY(whichRect)))
    endfunction

    //===========================================================================
    // Calculate the modulus/remainder of (dividend) divided by (divisor).
    // Examples:  18 mod 5 = 3.  15 mod 5 = 0.  -8 mod 5 = 2.
    //
    function ModuloInteger takes integer dividend, integer divisor returns integer
        local integer modulus = dividend - (dividend / divisor) * divisor

        // If the dividend was negative, the above modulus calculation will
        // be negative, but within (-divisor..0).  We can add (divisor) to
        // shift this result into the desired range of (0..divisor).
        if (modulus < 0) then
            set modulus = modulus + divisor
        endif

        return modulus
    endfunction

    //===========================================================================
    // Calculate the modulus/remainder of (dividend) divided by (divisor).
    // Examples:  13.000 mod 2.500 = 0.500.  -6.000 mod 2.500 = 1.500.
    //
    function ModuloReal takes real dividend, real divisor returns real
        local real modulus = dividend - I2R(R2I(dividend / divisor)) * divisor

        // If the dividend was negative, the above modulus calculation will
        // be negative, but within (-divisor..0).  We can add (divisor) to
        // shift this result into the desired range of (0..divisor).
        if (modulus < 0) then
            set modulus = modulus + divisor
        endif

        return modulus
    endfunction

    //===========================================================================
    function OffsetLocation takes location loc, real dx, real dy returns location
        return Location(GetLocationX(loc) + dx, GetLocationY(loc) + dy)
    endfunction

    //===========================================================================
    function OffsetRectBJ takes rect r, real dx, real dy returns rect
        return Rect( GetRectMinX(r) + dx, GetRectMinY(r) + dy, GetRectMaxX(r) + dx, GetRectMaxY(r) + dy )
    endfunction

    //===========================================================================
    function RectFromCenterSizeBJ takes location center, real width, real height returns rect
        local real x = GetLocationX( center )
        local real y = GetLocationY( center )
        return Rect( x - width*0.5, y - height*0.5, x + width*0.5, y + height*0.5 )
    endfunction

    //===========================================================================
    function RectContainsCoords takes rect r, real x, real y returns boolean
        return (GetRectMinX(r) <= x) and (x <= GetRectMaxX(r)) and (GetRectMinY(r) <= y) and (y <= GetRectMaxY(r))
    endfunction

    //===========================================================================
    function RectContainsLoc takes rect r, location loc returns boolean
        return RectContainsCoords(r, GetLocationX(loc), GetLocationY(loc))
    endfunction

    //===========================================================================
    function RectContainsUnit takes rect r, unit whichUnit returns boolean
        return RectContainsCoords(r, GetUnitX(whichUnit), GetUnitY(whichUnit))
    endfunction

    //===========================================================================
    function RectContainsItem takes item whichItem, rect r returns boolean
        if (whichItem == null) then
            return false
        endif

        if (IsItemOwned(whichItem)) then
            return false
        endif

        return RectContainsCoords(r, GetItemX(whichItem), GetItemY(whichItem))
    endfunction



    //***************************************************************************
    //*
    //*  Utility Constructs
    //*
    //***************************************************************************

    //===========================================================================
    // Runs the trigger's actions if the trigger's conditions evaluate to true.
    //
    function ConditionalTriggerExecute takes trigger trig returns nothing
        if TriggerEvaluate(trig) then
            call TriggerExecute(trig)
        endif
    endfunction

    //===========================================================================
    // Runs the trigger's actions if the trigger's conditions evaluate to true.
    //
    function TriggerExecuteBJ takes trigger trig, boolean checkConditions returns boolean
        if checkConditions then
            if not (TriggerEvaluate(trig)) then
                return false
            endif
        endif
        call TriggerExecute(trig)
        return true
    endfunction

    //===========================================================================
    // Arranges for a trigger to fire almost immediately, except that the calling
    // trigger is not interrupted as is the case with a TriggerExecute call.
    // Since the trigger executes normally, its conditions are still evaluated.
    //
    function PostTriggerExecuteBJ takes trigger trig, boolean checkConditions returns boolean
        if checkConditions then
            if not (TriggerEvaluate(trig)) then
                return false
            endif
        endif
        call TriggerRegisterTimerEvent(trig, 0, false)
        return true
    endfunction

    //===========================================================================
    // Debug - Display the contents of the trigger queue (as either null or "x"
    // for each entry).
    function QueuedTriggerCheck takes nothing returns nothing
        local string s = "TrigQueue Check "
        local integer i

        set i = 0
        loop
            exitwhen i >= bj_queuedExecTotal
            set s = s + "q[" + I2S(i) + "]="
            if (bj_queuedExecTriggers[i] == null) then
                set s = s + "null "
            else
                set s = s + "x "
            endif
            set i = i + 1
        endloop
        set s = s + "(" + I2S(bj_queuedExecTotal) + " total)"
        call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,600,s)
    endfunction

    //===========================================================================
    // Searches the queue for a given trigger, returning the index of the
    // trigger within the queue if it is found, or -1 if it is not found.
    //
    function QueuedTriggerGetIndex takes trigger trig returns integer
        // Determine which, if any, of the queued triggers is being removed.
        local integer index     = 0
        loop
            exitwhen index >= bj_queuedExecTotal
            if (bj_queuedExecTriggers[index] == trig) then
                return index
            endif
            set index = index + 1
        endloop
        return -1
    endfunction

    //===========================================================================
    // Removes a trigger from the trigger queue, shifting other triggers down
    // to fill the unused space.  If the currently running trigger is removed
    // in this manner, this function does NOT attempt to run the next trigger.
    //
    function QueuedTriggerRemoveByIndex takes integer trigIndex returns boolean
        local integer index

        // If the to-be-removed index is out of range, fail.
        if (trigIndex >= bj_queuedExecTotal) then
            return false
        endif

        // Shift all queue entries down to fill in the gap.
        set bj_queuedExecTotal = bj_queuedExecTotal - 1
        set index = trigIndex
        loop
            exitwhen index >= bj_queuedExecTotal
            set bj_queuedExecTriggers[index] = bj_queuedExecTriggers[index + 1]
            set bj_queuedExecUseConds[index] = bj_queuedExecUseConds[index + 1]
            set index = index + 1
        endloop
        return true
    endfunction

    //===========================================================================
    // Attempt to execute the first trigger in the queue.  If it fails, remove
    // it and execute the next one.  Continue this cycle until a trigger runs,
    // or until the queue is empty.
    //
    function QueuedTriggerAttemptExec takes nothing returns boolean
        loop
            exitwhen bj_queuedExecTotal == 0

            if TriggerExecuteBJ(bj_queuedExecTriggers[0], bj_queuedExecUseConds[0]) then
                // Timeout the queue if it sits at the front of the queue for too long.
                call TimerStart(bj_queuedExecTimeoutTimer, bj_QUEUED_TRIGGER_TIMEOUT, false, null)
                return true
            endif

            call QueuedTriggerRemoveByIndex(0)
        endloop
        return false
    endfunction

    //===========================================================================
    // Queues a trigger to be executed, assuring that such triggers are not
    // executed at the same time.
    //
    function QueuedTriggerAddBJ takes trigger trig, boolean checkConditions returns boolean
        // Make sure our queue isn't full.  If it is, return failure.
        if (bj_queuedExecTotal >= bj_MAX_QUEUED_TRIGGERS) then
            return false
        endif

        // Add the trigger to an array of to-be-executed triggers.
        set bj_queuedExecTriggers[bj_queuedExecTotal] = trig
        set bj_queuedExecUseConds[bj_queuedExecTotal] = checkConditions
        set bj_queuedExecTotal = bj_queuedExecTotal + 1

        // If this is the only trigger in the queue, run it.
        if (bj_queuedExecTotal == 1) then
            call QueuedTriggerAttemptExec()
        endif
        return true
    endfunction

    //===========================================================================
    // Denotes the end of a queued trigger. Be sure to call this only once per
    // queued trigger, or risk stepping on the toes of other queued triggers.
    //
    function QueuedTriggerRemoveBJ takes trigger trig returns nothing
        local integer index
        local integer trigIndex
        local boolean trigExecuted

        // Find the trigger's index.
        set trigIndex = QueuedTriggerGetIndex(trig)
        if (trigIndex == -1) then
            return
        endif

        // Shuffle the other trigger entries down to fill in the gap.
        call QueuedTriggerRemoveByIndex(trigIndex)

        // If we just axed the currently running trigger, run the next one.
        if (trigIndex == 0) then
            call PauseTimer(bj_queuedExecTimeoutTimer)
            call QueuedTriggerAttemptExec()
        endif
    endfunction

    //===========================================================================
    // Denotes the end of a queued trigger. Be sure to call this only once per
    // queued trigger, lest you step on the toes of other queued triggers.
    //
    function QueuedTriggerDoneBJ takes nothing returns nothing
        local integer index

        // Make sure there's something on the queue to remove.
        if (bj_queuedExecTotal <= 0) then
            return
        endif

        // Remove the currently running trigger from the array.
        call QueuedTriggerRemoveByIndex(0)

        // If other triggers are waiting to run, run one of them.
        call PauseTimer(bj_queuedExecTimeoutTimer)
        call QueuedTriggerAttemptExec()
    endfunction

    //===========================================================================
    // Empty the trigger queue.
    //
    function QueuedTriggerClearBJ takes nothing returns nothing
        call PauseTimer(bj_queuedExecTimeoutTimer)
        set bj_queuedExecTotal = 0
    endfunction

    //===========================================================================
    // Remove all but the currently executing trigger from the trigger queue.
    //
    function QueuedTriggerClearInactiveBJ takes nothing returns nothing
        set bj_queuedExecTotal = IMinBJ(bj_queuedExecTotal, 1)
    endfunction

    //===========================================================================
    function QueuedTriggerCountBJ takes nothing returns integer
        return bj_queuedExecTotal
    endfunction

    //===========================================================================
    function IsTriggerQueueEmptyBJ takes nothing returns boolean
        return bj_queuedExecTotal <= 0
    endfunction

    //===========================================================================
    function IsTriggerQueuedBJ takes trigger trig returns boolean
        return QueuedTriggerGetIndex(trig) != -1
    endfunction

    //===========================================================================
    function GetForLoopIndexA takes nothing returns integer
        return bj_forLoopAIndex
    endfunction

    //===========================================================================
    function SetForLoopIndexA takes integer newIndex returns nothing
        set bj_forLoopAIndex = newIndex
    endfunction

    //===========================================================================
    function GetForLoopIndexB takes nothing returns integer
        return bj_forLoopBIndex
    endfunction

    //===========================================================================
    function SetForLoopIndexB takes integer newIndex returns nothing
        set bj_forLoopBIndex = newIndex
    endfunction

    //===========================================================================
    // We can't do game-time waits, so this simulates one by starting a timer
    // and polling until the timer expires.
    function PolledWait takes real duration returns nothing
        local timer t
        local real  timeRemaining

        if (duration > 0) then
            set t = CreateTimer()
            call TimerStart(t, duration, false, null)
            loop
                set timeRemaining = TimerGetRemaining(t)
                exitwhen timeRemaining <= 0

                // If we have a bit of time left, skip past 10% of the remaining
                // duration instead of checking every interval, to minimize the
                // polling on long waits.
                if (timeRemaining > bj_POLLED_WAIT_SKIP_THRESHOLD) then
                    call TriggerSleepAction(0.1 * timeRemaining)
                else
                    call TriggerSleepAction(bj_POLLED_WAIT_INTERVAL)
                endif
            endloop
            call DestroyTimer(t)
        endif
    endfunction

    //===========================================================================
    function IntegerTertiaryOp takes boolean flag, integer valueA, integer valueB returns integer
        if flag then
            return valueA
        else
            return valueB
        endif
    endfunction


    //***************************************************************************
    //*
    //*  General Utility Functions
    //*  These functions exist purely to make the trigger dialogs cleaner and
    //*  more comprehensible.
    //*
    //***************************************************************************

    //===========================================================================
    function DoNothing takes nothing returns nothing
    endfunction

    //===========================================================================
    // This function does nothing.  WorldEdit should should eventually ignore
    // CommentString triggers during script generation, but until such a time,
    // this function will serve as a stub.
    //
    function CommentString takes string commentString returns nothing
    endfunction

    //===========================================================================
    // This function returns the input string, converting it from the localized text, if necessary
    //
    function StringIdentity takes string theString returns string
        return GetLocalizedString(theString)
    endfunction

    //===========================================================================
    function GetBooleanAnd takes boolean valueA, boolean valueB returns boolean
        return valueA and valueB
    endfunction

    //===========================================================================
    function GetBooleanOr takes boolean valueA, boolean valueB returns boolean
        return valueA or valueB
    endfunction

    //===========================================================================
    // Converts a percentage (real, 0..100) into a scaled integer (0..max),
    // clipping the result to 0..max in case the input is invalid.
    //
    function PercentToInt takes real percentage, integer max returns integer
        local integer result = R2I(percentage * I2R(max) * 0.01)

        if (result < 0) then
            set result = 0
        elseif (result > max) then
            set result = max
        endif

        return result
    endfunction

    //===========================================================================
    function PercentTo255 takes real percentage returns integer
        return PercentToInt(percentage, 255)
    endfunction

    //===========================================================================
    function GetTimeOfDay takes nothing returns real
        return GetFloatGameState(GAME_STATE_TIME_OF_DAY)
    endfunction

    //===========================================================================
    function SetTimeOfDay takes real whatTime returns nothing
        call SetFloatGameState(GAME_STATE_TIME_OF_DAY, whatTime)
    endfunction

    //===========================================================================
    function SetTimeOfDayScalePercentBJ takes real scalePercent returns nothing
        call SetTimeOfDayScale(scalePercent * 0.01)
    endfunction

    //===========================================================================
    function GetTimeOfDayScalePercentBJ takes nothing returns real
        return GetTimeOfDayScale() * 100
    endfunction

    //===========================================================================
    function PlaySound takes string soundName returns nothing
        local sound soundHandle = CreateSound(soundName, false, false, true, 12700, 12700, "")
        call StartSound(soundHandle)
        call KillSoundWhenDone(soundHandle)
    endfunction

    //===========================================================================
    function CompareLocationsBJ takes location A, location B returns boolean
        return GetLocationX(A) == GetLocationX(B) and GetLocationY(A) == GetLocationY(B)
    endfunction

    //===========================================================================
    function CompareRectsBJ takes rect A, rect B returns boolean
        return GetRectMinX(A) == GetRectMinX(B) and GetRectMinY(A) == GetRectMinY(B) and GetRectMaxX(A) == GetRectMaxX(B) and GetRectMaxY(A) == GetRectMaxY(B)
    endfunction

    //===========================================================================
    // Returns a square rect that exactly encompasses the specified circle.
    //
    function GetRectFromCircleBJ takes location center, real radius returns rect
        local real centerX = GetLocationX(center)
        local real centerY = GetLocationY(center)
        return Rect(centerX - radius, centerY - radius, centerX + radius, centerY + radius)
    endfunction



    //***************************************************************************
    //*
    //*  Camera Utility Functions
    //*
    //***************************************************************************

    //===========================================================================
    function GetCurrentCameraSetup takes nothing returns camerasetup
        local camerasetup theCam = CreateCameraSetup()
        local real duration = 0
        call CameraSetupSetField(theCam, CAMERA_FIELD_TARGET_DISTANCE, GetCameraField(CAMERA_FIELD_TARGET_DISTANCE), duration)
        call CameraSetupSetField(theCam, CAMERA_FIELD_FARZ,            GetCameraField(CAMERA_FIELD_FARZ),            duration)
        call CameraSetupSetField(theCam, CAMERA_FIELD_ZOFFSET,         GetCameraField(CAMERA_FIELD_ZOFFSET),         duration)
        call CameraSetupSetField(theCam, CAMERA_FIELD_ANGLE_OF_ATTACK, bj_RADTODEG * GetCameraField(CAMERA_FIELD_ANGLE_OF_ATTACK), duration)
        call CameraSetupSetField(theCam, CAMERA_FIELD_FIELD_OF_VIEW,   bj_RADTODEG * GetCameraField(CAMERA_FIELD_FIELD_OF_VIEW),   duration)
        call CameraSetupSetField(theCam, CAMERA_FIELD_ROLL,            bj_RADTODEG * GetCameraField(CAMERA_FIELD_ROLL),            duration)
        call CameraSetupSetField(theCam, CAMERA_FIELD_ROTATION,        bj_RADTODEG * GetCameraField(CAMERA_FIELD_ROTATION),        duration)
        call CameraSetupSetField(theCam, CAMERA_FIELD_LOCAL_PITCH,     bj_RADTODEG * GetCameraField(CAMERA_FIELD_LOCAL_PITCH),     duration)
        call CameraSetupSetField(theCam, CAMERA_FIELD_LOCAL_YAW,       bj_RADTODEG * GetCameraField(CAMERA_FIELD_LOCAL_YAW),       duration)
        call CameraSetupSetField(theCam, CAMERA_FIELD_LOCAL_ROLL,      bj_RADTODEG * GetCameraField(CAMERA_FIELD_LOCAL_ROLL),      duration)
        call CameraSetupSetDestPosition(theCam, GetCameraTargetPositionX(), GetCameraTargetPositionY(), duration)
        return theCam
    endfunction

    //===========================================================================
    function CameraSetupApplyForPlayer takes boolean doPan, camerasetup whichSetup, player whichPlayer, real duration returns nothing
        if (GetLocalPlayer() == whichPlayer) then
            // Use only local code (no net traffic) within this block to avoid desyncs.
            call CameraSetupApplyForceDuration(whichSetup, doPan, duration)
        endif
    endfunction

    //===========================================================================
    function CameraSetupGetFieldSwap takes camerafield whichField, camerasetup whichSetup returns real
        return CameraSetupGetField(whichSetup, whichField)
    endfunction

    //===========================================================================
    function SetCameraFieldForPlayer takes player whichPlayer, camerafield whichField, real value, real duration returns nothing
        if (GetLocalPlayer() == whichPlayer) then
            // Use only local code (no net traffic) within this block to avoid desyncs.
            call SetCameraField(whichField, value, duration)
        endif
    endfunction

    //===========================================================================
    function SetCameraTargetControllerNoZForPlayer takes player whichPlayer, unit whichUnit, real xoffset, real yoffset, boolean inheritOrientation returns nothing
        if (GetLocalPlayer() == whichPlayer) then
            // Use only local code (no net traffic) within this block to avoid desyncs.
            call SetCameraTargetController(whichUnit, xoffset, yoffset, inheritOrientation)
        endif
    endfunction

    //===========================================================================
    function SetCameraPositionForPlayer takes player whichPlayer, real x, real y returns nothing
        if (GetLocalPlayer() == whichPlayer) then
            // Use only local code (no net traffic) within this block to avoid desyncs.
            call SetCameraPosition(x, y)
        endif
    endfunction

    //===========================================================================
    function SetCameraPositionLocForPlayer takes player whichPlayer, location loc returns nothing
        if (GetLocalPlayer() == whichPlayer) then
            // Use only local code (no net traffic) within this block to avoid desyncs.
            call SetCameraPosition(GetLocationX(loc), GetLocationY(loc))
        endif
    endfunction

    //===========================================================================
    function RotateCameraAroundLocBJ takes real degrees, location loc, player whichPlayer, real duration returns nothing
        if (GetLocalPlayer() == whichPlayer) then
            // Use only local code (no net traffic) within this block to avoid desyncs.
            call SetCameraRotateMode(GetLocationX(loc), GetLocationY(loc), bj_DEGTORAD * degrees, duration)
        endif
    endfunction

    //===========================================================================
    function PanCameraToForPlayer takes player whichPlayer, real x, real y returns nothing
        if (GetLocalPlayer() == whichPlayer) then
            // Use only local code (no net traffic) within this block to avoid desyncs.
            call PanCameraTo(x, y)
        endif
    endfunction

    //===========================================================================
    function PanCameraToLocForPlayer takes player whichPlayer, location loc returns nothing
        if (GetLocalPlayer() == whichPlayer) then
            // Use only local code (no net traffic) within this block to avoid desyncs.
            call PanCameraTo(GetLocationX(loc), GetLocationY(loc))
        endif
    endfunction

    //===========================================================================
    function PanCameraToTimedForPlayer takes player whichPlayer, real x, real y, real duration returns nothing
        if (GetLocalPlayer() == whichPlayer) then
            // Use only local code (no net traffic) within this block to avoid desyncs.
            call PanCameraToTimed(x, y, duration)
        endif
    endfunction

    //===========================================================================
    function PanCameraToTimedLocForPlayer takes player whichPlayer, location loc, real duration returns nothing
        if (GetLocalPlayer() == whichPlayer) then
            // Use only local code (no net traffic) within this block to avoid desyncs.
            call PanCameraToTimed(GetLocationX(loc), GetLocationY(loc), duration)
        endif
    endfunction

    //===========================================================================
    function PanCameraToTimedLocWithZForPlayer takes player whichPlayer, location loc, real zOffset, real duration returns nothing
        if (GetLocalPlayer() == whichPlayer) then
            // Use only local code (no net traffic) within this block to avoid desyncs.
            call PanCameraToTimedWithZ(GetLocationX(loc), GetLocationY(loc), zOffset, duration)
        endif
    endfunction

    //===========================================================================
    function SmartCameraPanBJ takes player whichPlayer, location loc, real duration returns nothing
        local real dist
        local location cameraLoc = GetCameraTargetPositionLoc()
        if (GetLocalPlayer() == whichPlayer) then
            // Use only local code (no net traffic) within this block to avoid desyncs.

            set dist = DistanceBetweenPoints(loc, cameraLoc)
            if (dist >= bj_SMARTPAN_TRESHOLD_SNAP) then
                // If the user is too far away, snap the camera.
                call PanCameraToTimed(GetLocationX(loc), GetLocationY(loc), 0)
            elseif (dist >= bj_SMARTPAN_TRESHOLD_PAN) then
                // If the user is moderately close, pan the camera.
                call PanCameraToTimed(GetLocationX(loc), GetLocationY(loc), duration)
            else
                // User is close enough, so don't touch the camera.
            endif
        endif
        call RemoveLocation(cameraLoc)
    endfunction

    //===========================================================================
    function SetCinematicCameraForPlayer takes player whichPlayer, string cameraModelFile returns nothing
        if (GetLocalPlayer() == whichPlayer) then
            // Use only local code (no net traffic) within this block to avoid desyncs.
            call SetCinematicCamera(cameraModelFile)
        endif
    endfunction

    //===========================================================================
    function ResetToGameCameraForPlayer takes player whichPlayer, real duration returns nothing
        if (GetLocalPlayer() == whichPlayer) then
            // Use only local code (no net traffic) within this block to avoid desyncs.
            call ResetToGameCamera(duration)
        endif
    endfunction

    //===========================================================================
    function CameraSetSourceNoiseForPlayer takes player whichPlayer, real magnitude, real velocity returns nothing
        if (GetLocalPlayer() == whichPlayer) then
            // Use only local code (no net traffic) within this block to avoid desyncs.
            call CameraSetSourceNoise(magnitude, velocity)
        endif
    endfunction

    //===========================================================================
    function CameraSetTargetNoiseForPlayer takes player whichPlayer, real magnitude, real velocity returns nothing
        if (GetLocalPlayer() == whichPlayer) then
            // Use only local code (no net traffic) within this block to avoid desyncs.
            call CameraSetTargetNoise(magnitude, velocity)
        endif
    endfunction

    //===========================================================================
    function CameraSetEQNoiseForPlayer takes player whichPlayer, real magnitude returns nothing
        local real richter = magnitude
        if (richter > 5.0) then
            set richter = 5.0
        endif
        if (richter < 2.0) then
            set richter = 2.0
        endif
        if (GetLocalPlayer() == whichPlayer) then
            // Use only local code (no net traffic) within this block to avoid desyncs.
            call CameraSetTargetNoiseEx(magnitude*2.0, magnitude*Pow(10,richter),true)
            call CameraSetSourceNoiseEx(magnitude*2.0, magnitude*Pow(10,richter),true)
        endif
    endfunction

    //===========================================================================
    function CameraClearNoiseForPlayer takes player whichPlayer returns nothing
        if (GetLocalPlayer() == whichPlayer) then
            // Use only local code (no net traffic) within this block to avoid desyncs.
            call CameraSetSourceNoise(0, 0)
            call CameraSetTargetNoise(0, 0)
        endif
    endfunction

    //===========================================================================
    // Query the current camera bounds.
    //
    function GetCurrentCameraBoundsMapRectBJ takes nothing returns rect
        return Rect(GetCameraBoundMinX(), GetCameraBoundMinY(), GetCameraBoundMaxX(), GetCameraBoundMaxY())
    endfunction

    //===========================================================================
    // Query the initial camera bounds, as defined at map init.
    //
    function GetCameraBoundsMapRect takes nothing returns rect
        return bj_mapInitialCameraBounds
    endfunction

    //===========================================================================
    // Query the playable map area, as defined at map init.
    //
    function GetPlayableMapRect takes nothing returns rect
        return bj_mapInitialPlayableArea
    endfunction

    //===========================================================================
    // Query the entire map area, as defined at map init.
    //
    function GetEntireMapRect takes nothing returns rect
        return GetWorldBounds()
    endfunction

    //===========================================================================
    function SetCameraBoundsToRect takes rect r returns nothing
        local real minX = GetRectMinX(r)
        local real minY = GetRectMinY(r)
        local real maxX = GetRectMaxX(r)
        local real maxY = GetRectMaxY(r)
        call SetCameraBounds(minX, minY, minX, maxY, maxX, maxY, maxX, minY)
    endfunction

    //===========================================================================
    function SetCameraBoundsToRectForPlayerBJ takes player whichPlayer, rect r returns nothing
        if (GetLocalPlayer() == whichPlayer) then
            // Use only local code (no net traffic) within this block to avoid desyncs.
            call SetCameraBoundsToRect(r)
        endif
    endfunction

    //===========================================================================
    function AdjustCameraBoundsBJ takes integer adjustMethod, real dxWest, real dxEast, real dyNorth, real dySouth returns nothing
        local real minX = 0
        local real minY = 0
        local real maxX = 0
        local real maxY = 0
        local real scale = 0

        if (adjustMethod == bj_CAMERABOUNDS_ADJUST_ADD) then
            set scale = 1
        elseif (adjustMethod == bj_CAMERABOUNDS_ADJUST_SUB) then
            set scale = -1
        else
            // Unrecognized adjustment method - ignore the request.
            return
        endif

        // Adjust the actual camera values
        set minX = GetCameraBoundMinX() - scale * dxWest
        set maxX = GetCameraBoundMaxX() + scale * dxEast
        set minY = GetCameraBoundMinY() - scale * dySouth
        set maxY = GetCameraBoundMaxY() + scale * dyNorth

        // Make sure the camera bounds are still valid.
        if (maxX < minX) then
            set minX = (minX + maxX) * 0.5
            set maxX = minX
        endif
        if (maxY < minY) then
            set minY = (minY + maxY) * 0.5
            set maxY = minY
        endif

        // Apply the new camera values.
        call SetCameraBounds(minX, minY, minX, maxY, maxX, maxY, maxX, minY)
    endfunction

    //===========================================================================
    function AdjustCameraBoundsForPlayerBJ takes integer adjustMethod, player whichPlayer, real dxWest, real dxEast, real dyNorth, real dySouth returns nothing
        if (GetLocalPlayer() == whichPlayer) then
            // Use only local code (no net traffic) within this block to avoid desyncs.
            call AdjustCameraBoundsBJ(adjustMethod, dxWest, dxEast, dyNorth, dySouth)
        endif
    endfunction

    //===========================================================================
    function SetCameraQuickPositionForPlayer takes player whichPlayer, real x, real y returns nothing
        if (GetLocalPlayer() == whichPlayer) then
            // Use only local code (no net traffic) within this block to avoid desyncs.
            call SetCameraQuickPosition(x, y)
        endif
    endfunction

    //===========================================================================
    function SetCameraQuickPositionLocForPlayer takes player whichPlayer, location loc returns nothing
        if (GetLocalPlayer() == whichPlayer) then
            // Use only local code (no net traffic) within this block to avoid desyncs.
            call SetCameraQuickPosition(GetLocationX(loc), GetLocationY(loc))
        endif
    endfunction

    //===========================================================================
    function SetCameraQuickPositionLoc takes location loc returns nothing
        call SetCameraQuickPosition(GetLocationX(loc), GetLocationY(loc))
    endfunction

    //===========================================================================
    function StopCameraForPlayerBJ takes player whichPlayer returns nothing
        if (GetLocalPlayer() == whichPlayer) then
            // Use only local code (no net traffic) within this block to avoid desyncs.
            call StopCamera()
        endif
    endfunction

    //===========================================================================
    function SetCameraOrientControllerForPlayerBJ takes player whichPlayer, unit whichUnit, real xoffset, real yoffset returns nothing
        if (GetLocalPlayer() == whichPlayer) then
            // Use only local code (no net traffic) within this block to avoid desyncs.
            call SetCameraOrientController(whichUnit, xoffset, yoffset)
        endif
    endfunction

    //===========================================================================
    function CameraSetSmoothingFactorBJ takes real factor returns nothing
        call CameraSetSmoothingFactor(factor)
    endfunction

    //===========================================================================
    function CameraResetSmoothingFactorBJ takes nothing returns nothing
        call CameraSetSmoothingFactor(0)
    endfunction



    //***************************************************************************
    //*
    //*  Text Utility Functions
    //*
    //***************************************************************************

    //===========================================================================
    function DisplayTextToForce takes force toForce, string message returns nothing
        if (IsPlayerInForce(GetLocalPlayer(), toForce)) then
            // Use only local code (no net traffic) within this block to avoid desyncs.
            call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, message)
        endif
    endfunction

    //===========================================================================
    function DisplayTimedTextToForce takes force toForce, real duration, string message returns nothing
        if (IsPlayerInForce(GetLocalPlayer(), toForce)) then
            // Use only local code (no net traffic) within this block to avoid desyncs.
            call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, duration, message)
        endif
    endfunction

    //===========================================================================
    function ClearTextMessagesBJ takes force toForce returns nothing
        if (IsPlayerInForce(GetLocalPlayer(), toForce)) then
            // Use only local code (no net traffic) within this block to avoid desyncs.
            call ClearTextMessages()
        endif
    endfunction

    //===========================================================================
    // The parameters for the API Substring function are unintuitive, so this
    // merely performs a translation for the starting index.
    //
    function SubStringBJ takes string source, integer start, integer end returns string
        return SubString(source, start-1, end)
    endfunction
     
    function GetHandleIdBJ takes handle h returns integer
        return GetHandleId(h)
    endfunction

    function StringHashBJ takes string s returns integer
        return StringHash(s)
    endfunction



    //***************************************************************************
    //*
    //*  Event Registration Utility Functions
    //*
    //***************************************************************************

    //===========================================================================
    function TriggerRegisterTimerEventPeriodic takes trigger trig, real timeout returns event
        return TriggerRegisterTimerEvent(trig, timeout, true)
    endfunction

    //===========================================================================
    function TriggerRegisterTimerEventSingle takes trigger trig, real timeout returns event
        return TriggerRegisterTimerEvent(trig, timeout, false)
    endfunction

    //===========================================================================
    function TriggerRegisterTimerExpireEventBJ takes trigger trig, timer t returns event
        return TriggerRegisterTimerExpireEvent(trig, t)
    endfunction

    //===========================================================================
    function TriggerRegisterPlayerUnitEventSimple takes trigger trig, player whichPlayer, playerunitevent whichEvent returns event
        return TriggerRegisterPlayerUnitEvent(trig, whichPlayer, whichEvent, null)
    endfunction

    //===========================================================================
    function TriggerRegisterAnyUnitEventBJ takes trigger trig, playerunitevent whichEvent returns nothing
        local integer index

        set index = 0
        loop
            call TriggerRegisterPlayerUnitEvent(trig, Player(index), whichEvent, null)

            set index = index + 1
            exitwhen index == bj_MAX_PLAYER_SLOTS
        endloop
    endfunction

    //===========================================================================
    function TriggerRegisterPlayerSelectionEventBJ takes trigger trig, player whichPlayer, boolean selected returns event
        if selected then
            return TriggerRegisterPlayerUnitEvent(trig, whichPlayer, EVENT_PLAYER_UNIT_SELECTED, null)
        else
            return TriggerRegisterPlayerUnitEvent(trig, whichPlayer, EVENT_PLAYER_UNIT_DESELECTED, null)
        endif
    endfunction

    //===========================================================================
    function TriggerRegisterPlayerKeyEventBJ takes trigger trig, player whichPlayer, integer keType, integer keKey returns event
        if (keType == bj_KEYEVENTTYPE_DEPRESS) then
            // Depress event - find out what key
            if (keKey == bj_KEYEVENTKEY_LEFT) then
                return TriggerRegisterPlayerEvent(trig, whichPlayer, EVENT_PLAYER_ARROW_LEFT_DOWN)
            elseif (keKey == bj_KEYEVENTKEY_RIGHT) then
                return TriggerRegisterPlayerEvent(trig, whichPlayer, EVENT_PLAYER_ARROW_RIGHT_DOWN)
            elseif (keKey == bj_KEYEVENTKEY_DOWN) then
                return TriggerRegisterPlayerEvent(trig, whichPlayer, EVENT_PLAYER_ARROW_DOWN_DOWN)
            elseif (keKey == bj_KEYEVENTKEY_UP) then
                return TriggerRegisterPlayerEvent(trig, whichPlayer, EVENT_PLAYER_ARROW_UP_DOWN)
            else
                // Unrecognized key - ignore the request and return failure.
                return null
            endif
        elseif (keType == bj_KEYEVENTTYPE_RELEASE) then
            // Release event - find out what key
            if (keKey == bj_KEYEVENTKEY_LEFT) then
                return TriggerRegisterPlayerEvent(trig, whichPlayer, EVENT_PLAYER_ARROW_LEFT_UP)
            elseif (keKey == bj_KEYEVENTKEY_RIGHT) then
                return TriggerRegisterPlayerEvent(trig, whichPlayer, EVENT_PLAYER_ARROW_RIGHT_UP)
            elseif (keKey == bj_KEYEVENTKEY_DOWN) then
                return TriggerRegisterPlayerEvent(trig, whichPlayer, EVENT_PLAYER_ARROW_DOWN_UP)
            elseif (keKey == bj_KEYEVENTKEY_UP) then
                return TriggerRegisterPlayerEvent(trig, whichPlayer, EVENT_PLAYER_ARROW_UP_UP)
            else
                // Unrecognized key - ignore the request and return failure.
                return null
            endif
        else
            // Unrecognized type - ignore the request and return failure.
            return null
        endif
    endfunction

    //===========================================================================
    function TriggerRegisterPlayerMouseEventBJ takes trigger trig, player whichPlayer, integer meType returns event
         if (meType == bj_MOUSEEVENTTYPE_DOWN) then
            // Mouse down event
            return TriggerRegisterPlayerEvent(trig, whichPlayer, EVENT_PLAYER_MOUSE_DOWN)
        elseif (meType == bj_MOUSEEVENTTYPE_UP) then
            // Mouse up event
            return TriggerRegisterPlayerEvent(trig, whichPlayer, EVENT_PLAYER_MOUSE_UP)
        elseif (meType == bj_MOUSEEVENTTYPE_MOVE) then
            // Mouse move event
            return TriggerRegisterPlayerEvent(trig, whichPlayer, EVENT_PLAYER_MOUSE_MOVE)
        else
            // Unrecognized type - ignore the request and return failure.
             return null
        endif
    endfunction

    //===========================================================================
    function TriggerRegisterPlayerEventVictory takes trigger trig, player whichPlayer returns event
        return TriggerRegisterPlayerEvent(trig, whichPlayer, EVENT_PLAYER_VICTORY)
    endfunction

    //===========================================================================
    function TriggerRegisterPlayerEventDefeat takes trigger trig, player whichPlayer returns event
        return TriggerRegisterPlayerEvent(trig, whichPlayer, EVENT_PLAYER_DEFEAT)
    endfunction

    //===========================================================================
    function TriggerRegisterPlayerEventLeave takes trigger trig, player whichPlayer returns event
        return TriggerRegisterPlayerEvent(trig, whichPlayer, EVENT_PLAYER_LEAVE)
    endfunction

    //===========================================================================
    function TriggerRegisterPlayerEventAllianceChanged takes trigger trig, player whichPlayer returns event
        return TriggerRegisterPlayerEvent(trig, whichPlayer, EVENT_PLAYER_ALLIANCE_CHANGED)
    endfunction

    //===========================================================================
    function TriggerRegisterPlayerEventEndCinematic takes trigger trig, player whichPlayer returns event
        return TriggerRegisterPlayerEvent(trig, whichPlayer, EVENT_PLAYER_END_CINEMATIC)
    endfunction

    //===========================================================================
    function TriggerRegisterGameStateEventTimeOfDay takes trigger trig, limitop opcode, real limitval returns event
        return TriggerRegisterGameStateEvent(trig, GAME_STATE_TIME_OF_DAY, opcode, limitval)
    endfunction

    //===========================================================================
    function TriggerRegisterEnterRegionSimple takes trigger trig, region whichRegion returns event
        return TriggerRegisterEnterRegion(trig, whichRegion, null)
    endfunction

    //===========================================================================
    function TriggerRegisterLeaveRegionSimple takes trigger trig, region whichRegion returns event
        return TriggerRegisterLeaveRegion(trig, whichRegion, null)
    endfunction

    //===========================================================================
    function TriggerRegisterEnterRectSimple takes trigger trig, rect r returns event
        local region rectRegion = CreateRegion()
        call RegionAddRect(rectRegion, r)
        return TriggerRegisterEnterRegion(trig, rectRegion, null)
    endfunction

    //===========================================================================
    function TriggerRegisterLeaveRectSimple takes trigger trig, rect r returns event
        local region rectRegion = CreateRegion()
        call RegionAddRect(rectRegion, r)
        return TriggerRegisterLeaveRegion(trig, rectRegion, null)
    endfunction

    //===========================================================================
    function TriggerRegisterDistanceBetweenUnits takes trigger trig, unit whichUnit, boolexpr condition, real range returns event
        return TriggerRegisterUnitInRange(trig, whichUnit, range, condition)
    endfunction

    //===========================================================================
    function TriggerRegisterUnitInRangeSimple takes trigger trig, real range, unit whichUnit returns event
        return TriggerRegisterUnitInRange(trig, whichUnit, range, null)
    endfunction

    //===========================================================================
    function TriggerRegisterUnitLifeEvent takes trigger trig, unit whichUnit, limitop opcode, real limitval returns event
        return TriggerRegisterUnitStateEvent(trig, whichUnit, UNIT_STATE_LIFE, opcode, limitval)
    endfunction

    //===========================================================================
    function TriggerRegisterUnitManaEvent takes trigger trig, unit whichUnit, limitop opcode, real limitval returns event
        return TriggerRegisterUnitStateEvent(trig, whichUnit, UNIT_STATE_MANA, opcode, limitval)
    endfunction

    //===========================================================================
    function TriggerRegisterDialogEventBJ takes trigger trig, dialog whichDialog returns event
        return TriggerRegisterDialogEvent(trig, whichDialog)
    endfunction

    //===========================================================================
    function TriggerRegisterShowSkillEventBJ takes trigger trig returns event
        return TriggerRegisterGameEvent(trig, EVENT_GAME_SHOW_SKILL)
    endfunction

    //===========================================================================
    function TriggerRegisterBuildSubmenuEventBJ takes trigger trig returns event
        return TriggerRegisterGameEvent(trig, EVENT_GAME_BUILD_SUBMENU)
    endfunction

    //===========================================================================
    function TriggerRegisterGameLoadedEventBJ takes trigger trig returns event
        return TriggerRegisterGameEvent(trig, EVENT_GAME_LOADED)
    endfunction

    //===========================================================================
    function TriggerRegisterGameSavedEventBJ takes trigger trig returns event
        return TriggerRegisterGameEvent(trig, EVENT_GAME_SAVE)
    endfunction

    //===========================================================================
    function RegisterDestDeathInRegionEnum takes nothing returns nothing
        set bj_destInRegionDiesCount = bj_destInRegionDiesCount + 1
        if (bj_destInRegionDiesCount <= bj_MAX_DEST_IN_REGION_EVENTS) then
            call TriggerRegisterDeathEvent(bj_destInRegionDiesTrig, GetEnumDestructable())
        endif
    endfunction

    //===========================================================================
    function TriggerRegisterDestDeathInRegionEvent takes trigger trig, rect r returns nothing
        set bj_destInRegionDiesTrig = trig
        set bj_destInRegionDiesCount = 0
        call EnumDestructablesInRect(r, null, function RegisterDestDeathInRegionEnum)
    endfunction



    //***************************************************************************
    //*
    //*  Environment Utility Functions
    //*
    //***************************************************************************

    //===========================================================================
    function AddWeatherEffectSaveLast takes rect where, integer effectID returns weathereffect
        set bj_lastCreatedWeatherEffect = AddWeatherEffect(where, effectID)
        return bj_lastCreatedWeatherEffect
    endfunction

    //===========================================================================
    function GetLastCreatedWeatherEffect takes nothing returns weathereffect
        return bj_lastCreatedWeatherEffect
    endfunction

    //===========================================================================
    function RemoveWeatherEffectBJ takes weathereffect whichWeatherEffect returns nothing
        call RemoveWeatherEffect(whichWeatherEffect)
    endfunction

    //===========================================================================
    function TerrainDeformationCraterBJ takes real duration, boolean permanent, location where, real radius, real depth returns terraindeformation
        set bj_lastCreatedTerrainDeformation = TerrainDeformCrater(GetLocationX(where), GetLocationY(where), radius, depth, R2I(duration * 1000), permanent)
        return bj_lastCreatedTerrainDeformation
    endfunction

    //===========================================================================
    function TerrainDeformationRippleBJ takes real duration, boolean limitNeg, location where, real startRadius, real endRadius, real depth, real wavePeriod, real waveWidth returns terraindeformation
        local real spaceWave
        local real timeWave
        local real radiusRatio

        if (endRadius <= 0 or waveWidth <= 0 or wavePeriod <= 0) then
            return null
        endif

        set timeWave = 2.0 * duration / wavePeriod
        set spaceWave = 2.0 * endRadius / waveWidth
        set radiusRatio = startRadius / endRadius

        set bj_lastCreatedTerrainDeformation = TerrainDeformRipple(GetLocationX(where), GetLocationY(where), endRadius, depth, R2I(duration * 1000), 1, spaceWave, timeWave, radiusRatio, limitNeg)
        return bj_lastCreatedTerrainDeformation
    endfunction

    //===========================================================================
    function TerrainDeformationWaveBJ takes real duration, location source, location target, real radius, real depth, real trailDelay returns terraindeformation
        local real distance
        local real dirX
        local real dirY
        local real speed

        set distance = DistanceBetweenPoints(source, target)
        if (distance == 0 or duration <= 0) then
            return null
        endif

        set dirX = (GetLocationX(target) - GetLocationX(source)) / distance
        set dirY = (GetLocationY(target) - GetLocationY(source)) / distance
        set speed = distance / duration

        set bj_lastCreatedTerrainDeformation = TerrainDeformWave(GetLocationX(source), GetLocationY(source), dirX, dirY, distance, speed, radius, depth, R2I(trailDelay * 1000), 1)
        return bj_lastCreatedTerrainDeformation
    endfunction

    //===========================================================================
    function TerrainDeformationRandomBJ takes real duration, location where, real radius, real minDelta, real maxDelta, real updateInterval returns terraindeformation
        set bj_lastCreatedTerrainDeformation = TerrainDeformRandom(GetLocationX(where), GetLocationY(where), radius, minDelta, maxDelta, R2I(duration * 1000), R2I(updateInterval * 1000))
        return bj_lastCreatedTerrainDeformation
    endfunction

    //===========================================================================
    function TerrainDeformationStopBJ takes terraindeformation deformation, real duration returns nothing
        call TerrainDeformStop(deformation, R2I(duration * 1000))
    endfunction

    //===========================================================================
    function GetLastCreatedTerrainDeformation takes nothing returns terraindeformation
        return bj_lastCreatedTerrainDeformation
    endfunction

    //===========================================================================
    function AddLightningLoc takes string codeName, location where1, location where2 returns lightning
        set bj_lastCreatedLightning = AddLightningEx(codeName, true, GetLocationX(where1), GetLocationY(where1), GetLocationZ(where1), GetLocationX(where2), GetLocationY(where2), GetLocationZ(where2))
        return bj_lastCreatedLightning
    endfunction

    //===========================================================================
    function DestroyLightningBJ takes lightning whichBolt returns boolean
        return DestroyLightning(whichBolt)
    endfunction

    //===========================================================================
    function MoveLightningLoc takes lightning whichBolt, location where1, location where2 returns boolean
        return MoveLightningEx(whichBolt, true, GetLocationX(where1), GetLocationY(where1), GetLocationZ(where1), GetLocationX(where2), GetLocationY(where2), GetLocationZ(where2))
    endfunction

    //===========================================================================
    function GetLightningColorABJ takes lightning whichBolt returns real
        return GetLightningColorA(whichBolt)
    endfunction

    //===========================================================================
    function GetLightningColorRBJ takes lightning whichBolt returns real
        return GetLightningColorR(whichBolt)
    endfunction

    //===========================================================================
    function GetLightningColorGBJ takes lightning whichBolt returns real
        return GetLightningColorG(whichBolt)
    endfunction

    //===========================================================================
    function GetLightningColorBBJ takes lightning whichBolt returns real
        return GetLightningColorB(whichBolt)
    endfunction

    //===========================================================================
    function SetLightningColorBJ takes lightning whichBolt, real r, real g, real b, real a returns boolean
        return SetLightningColor(whichBolt, r, g, b, a)
    endfunction

    //===========================================================================
    function GetLastCreatedLightningBJ takes nothing returns lightning
        return bj_lastCreatedLightning
    endfunction

    //===========================================================================
    function GetAbilityEffectBJ takes integer abilcode, effecttype t, integer index returns string
        return GetAbilityEffectById(abilcode, t, index)
    endfunction

    //===========================================================================
    function GetAbilitySoundBJ takes integer abilcode, soundtype t returns string
        return GetAbilitySoundById(abilcode, t)
    endfunction


    //===========================================================================
    function GetTerrainCliffLevelBJ takes location where returns integer
        return GetTerrainCliffLevel(GetLocationX(where), GetLocationY(where))
    endfunction

    //===========================================================================
    function GetTerrainTypeBJ takes location where returns integer
        return GetTerrainType(GetLocationX(where), GetLocationY(where))
    endfunction

    //===========================================================================
    function GetTerrainVarianceBJ takes location where returns integer
        return GetTerrainVariance(GetLocationX(where), GetLocationY(where))
    endfunction

    //===========================================================================
    function SetTerrainTypeBJ takes location where, integer terrainType, integer variation, integer area, integer shape returns nothing
        call SetTerrainType(GetLocationX(where), GetLocationY(where), terrainType, variation, area, shape)
    endfunction

    //===========================================================================
    function IsTerrainPathableBJ takes location where, pathingtype t returns boolean
        return IsTerrainPathable(GetLocationX(where), GetLocationY(where), t)
    endfunction

    //===========================================================================
    function SetTerrainPathableBJ takes location where, pathingtype t, boolean flag returns nothing
        call SetTerrainPathable(GetLocationX(where), GetLocationY(where), t, flag)
    endfunction

    //===========================================================================
    function SetWaterBaseColorBJ takes real red, real green, real blue, real transparency returns nothing
        call SetWaterBaseColor(PercentTo255(red), PercentTo255(green), PercentTo255(blue), PercentTo255(100.0-transparency))
    endfunction

    //===========================================================================
    function CreateFogModifierRectSimple takes player whichPlayer, fogstate whichFogState, rect r, boolean afterUnits returns fogmodifier
        set bj_lastCreatedFogModifier = CreateFogModifierRect(whichPlayer, whichFogState, r, true, afterUnits)
        return bj_lastCreatedFogModifier
    endfunction

    //===========================================================================
    function CreateFogModifierRadiusLocSimple takes player whichPlayer, fogstate whichFogState, location center, real radius, boolean afterUnits returns fogmodifier
        set bj_lastCreatedFogModifier = CreateFogModifierRadiusLoc(whichPlayer, whichFogState, center, radius, true, afterUnits)
        return bj_lastCreatedFogModifier
    endfunction

    //===========================================================================
    // Version of CreateFogModifierRect that assumes use of sharedVision and
    // gives the option of immediately enabling the modifier, so that triggers
    // can default to modifiers that are immediately enabled.
    //
    function CreateFogModifierRectBJ takes boolean enabled, player whichPlayer, fogstate whichFogState, rect r returns fogmodifier
        set bj_lastCreatedFogModifier = CreateFogModifierRect(whichPlayer, whichFogState, r, true, false)
        if enabled then
            call FogModifierStart(bj_lastCreatedFogModifier)
        endif
        return bj_lastCreatedFogModifier
    endfunction

    //===========================================================================
    // Version of CreateFogModifierRadius that assumes use of sharedVision and
    // gives the option of immediately enabling the modifier, so that triggers
    // can default to modifiers that are immediately enabled.
    //
    function CreateFogModifierRadiusLocBJ takes boolean enabled, player whichPlayer, fogstate whichFogState, location center, real radius returns fogmodifier
        set bj_lastCreatedFogModifier = CreateFogModifierRadiusLoc(whichPlayer, whichFogState, center, radius, true, false)
        if enabled then
            call FogModifierStart(bj_lastCreatedFogModifier)
        endif
        return bj_lastCreatedFogModifier
    endfunction

    //===========================================================================
    function GetLastCreatedFogModifier takes nothing returns fogmodifier
        return bj_lastCreatedFogModifier
    endfunction

    //===========================================================================
    function FogEnableOn takes nothing returns nothing
        call FogEnable(true)
    endfunction

    //===========================================================================
    function FogEnableOff takes nothing returns nothing
        call FogEnable(false)
    endfunction

    //===========================================================================
    function FogMaskEnableOn takes nothing returns nothing
        call FogMaskEnable(true)
    endfunction

    //===========================================================================
    function FogMaskEnableOff takes nothing returns nothing
        call FogMaskEnable(false)
    endfunction

    //===========================================================================
    function UseTimeOfDayBJ takes boolean flag returns nothing
        call SuspendTimeOfDay(not flag)
    endfunction

    //===========================================================================
    function SetTerrainFogExBJ takes integer style, real zstart, real zend, real density, real red, real green, real blue returns nothing
        call SetTerrainFogEx(style, zstart, zend, density, red * 0.01, green * 0.01, blue * 0.01)
    endfunction

    //===========================================================================
    function ResetTerrainFogBJ takes nothing returns nothing
        call ResetTerrainFog()
    endfunction

    //===========================================================================
    function SetDoodadAnimationBJ takes string animName, integer doodadID, real radius, location center returns nothing
        call SetDoodadAnimation(GetLocationX(center), GetLocationY(center), radius, doodadID, false, animName, false)
    endfunction

    //===========================================================================
    function SetDoodadAnimationRectBJ takes string animName, integer doodadID, rect r returns nothing
        call SetDoodadAnimationRect(r, doodadID, animName, false)
    endfunction

    //===========================================================================
    function AddUnitAnimationPropertiesBJ takes boolean add, string animProperties, unit whichUnit returns nothing
        call AddUnitAnimationProperties(whichUnit, animProperties, add)
    endfunction


    //============================================================================
    function CreateImageBJ takes string file, real size, location where, real zOffset, integer imageType returns image
        set bj_lastCreatedImage = CreateImage(file, size, size, size, GetLocationX(where), GetLocationY(where), zOffset, 0, 0, 0, imageType)
        return bj_lastCreatedImage
    endfunction

    //============================================================================
    function ShowImageBJ takes boolean flag, image whichImage returns nothing
        call ShowImage(whichImage, flag)
    endfunction

    //============================================================================
    function SetImagePositionBJ takes image whichImage, location where, real zOffset returns nothing
        call SetImagePosition(whichImage, GetLocationX(where), GetLocationY(where), zOffset)
    endfunction

    //============================================================================
    function SetImageColorBJ takes image whichImage, real red, real green, real blue, real alpha returns nothing
        call SetImageColor(whichImage, PercentTo255(red), PercentTo255(green), PercentTo255(blue), PercentTo255(100.0-alpha))
    endfunction

    //============================================================================
    function GetLastCreatedImage takes nothing returns image
        return bj_lastCreatedImage
    endfunction

    //============================================================================
    function CreateUbersplatBJ takes location where, string name, real red, real green, real blue, real alpha, boolean forcePaused, boolean noBirthTime returns ubersplat
        set bj_lastCreatedUbersplat = CreateUbersplat(GetLocationX(where), GetLocationY(where), name, PercentTo255(red), PercentTo255(green), PercentTo255(blue), PercentTo255(100.0-alpha), forcePaused, noBirthTime)
        return bj_lastCreatedUbersplat
    endfunction

    //============================================================================
    function ShowUbersplatBJ takes boolean flag, ubersplat whichSplat returns nothing
        call ShowUbersplat(whichSplat, flag)
    endfunction

    //============================================================================
    function GetLastCreatedUbersplat takes nothing returns ubersplat
        return bj_lastCreatedUbersplat
    endfunction


    //***************************************************************************
    //*
    //*  Sound Utility Functions
    //*
    //***************************************************************************

    //===========================================================================
    function PlaySoundBJ takes sound soundHandle returns nothing
        set bj_lastPlayedSound = soundHandle
        if (soundHandle != null) then
            call StartSound(soundHandle)
        endif
    endfunction

    //===========================================================================
    function StopSoundBJ takes sound soundHandle, boolean fadeOut returns nothing
        call StopSound(soundHandle, false, fadeOut)
    endfunction

    //===========================================================================
    function SetSoundVolumeBJ takes sound soundHandle, real volumePercent returns nothing
        call SetSoundVolume(soundHandle, PercentToInt(volumePercent, 127))
    endfunction

    //===========================================================================
    function SetSoundOffsetBJ takes real newOffset, sound soundHandle returns nothing
        call SetSoundPlayPosition(soundHandle, R2I(newOffset * 1000))
    endfunction

    //===========================================================================
    function SetSoundDistanceCutoffBJ takes sound soundHandle, real cutoff returns nothing
        call SetSoundDistanceCutoff(soundHandle, cutoff)
    endfunction

    //===========================================================================
    function SetSoundPitchBJ takes sound soundHandle, real pitch returns nothing
        call SetSoundPitch(soundHandle, pitch)
    endfunction

    //===========================================================================
    function SetSoundPositionLocBJ takes sound soundHandle, location loc, real z returns nothing
        call SetSoundPosition(soundHandle, GetLocationX(loc), GetLocationY(loc), z)
    endfunction

    //===========================================================================
    function AttachSoundToUnitBJ takes sound soundHandle, unit whichUnit returns nothing
        call AttachSoundToUnit(soundHandle, whichUnit)
    endfunction

    //===========================================================================
    function SetSoundConeAnglesBJ takes sound soundHandle, real inside, real outside, real outsideVolumePercent returns nothing
        call SetSoundConeAngles(soundHandle, inside, outside, PercentToInt(outsideVolumePercent, 127))
    endfunction

    //===========================================================================
    function KillSoundWhenDoneBJ takes sound soundHandle returns nothing
        call KillSoundWhenDone(soundHandle)
    endfunction

    //===========================================================================
    function PlaySoundAtPointBJ takes sound soundHandle, real volumePercent, location loc, real z returns nothing
        call SetSoundPositionLocBJ(soundHandle, loc, z)
        call SetSoundVolumeBJ(soundHandle, volumePercent)
        call PlaySoundBJ(soundHandle)
    endfunction

    //===========================================================================
    function PlaySoundOnUnitBJ takes sound soundHandle, real volumePercent, unit whichUnit returns nothing
        call AttachSoundToUnitBJ(soundHandle, whichUnit)
        call SetSoundVolumeBJ(soundHandle, volumePercent)
        call PlaySoundBJ(soundHandle)
    endfunction

    //===========================================================================
    function PlaySoundFromOffsetBJ takes sound soundHandle, real volumePercent, real startingOffset returns nothing
        call SetSoundVolumeBJ(soundHandle, volumePercent)
        call PlaySoundBJ(soundHandle)
        call SetSoundOffsetBJ(startingOffset, soundHandle)
    endfunction

    //===========================================================================
    function PlayMusicBJ takes string musicFileName returns nothing
        set bj_lastPlayedMusic = musicFileName
        call PlayMusic(musicFileName)
    endfunction

    //===========================================================================
    function PlayMusicExBJ takes string musicFileName, real startingOffset, real fadeInTime returns nothing
        set bj_lastPlayedMusic = musicFileName
        call PlayMusicEx(musicFileName, R2I(startingOffset * 1000), R2I(fadeInTime * 1000))
    endfunction

    //===========================================================================
    function SetMusicOffsetBJ takes real newOffset returns nothing
        call SetMusicPlayPosition(R2I(newOffset * 1000))
    endfunction

    //===========================================================================
    function PlayThematicMusicBJ takes string musicName returns nothing
        call PlayThematicMusic(musicName)
    endfunction

    //===========================================================================
    function PlayThematicMusicExBJ takes string musicName, real startingOffset returns nothing
        call PlayThematicMusicEx(musicName, R2I(startingOffset * 1000))
    endfunction

    //===========================================================================
    function SetThematicMusicOffsetBJ takes real newOffset returns nothing
        call SetThematicMusicPlayPosition(R2I(newOffset * 1000))
    endfunction

    //===========================================================================
    function EndThematicMusicBJ takes nothing returns nothing
        call EndThematicMusic()
    endfunction

    //===========================================================================
    function StopMusicBJ takes boolean fadeOut returns nothing
        call StopMusic(fadeOut)
    endfunction

    //===========================================================================
    function ResumeMusicBJ takes nothing returns nothing
        call ResumeMusic()
    endfunction

    //===========================================================================
    function SetMusicVolumeBJ takes real volumePercent returns nothing
        call SetMusicVolume(PercentToInt(volumePercent, 127))
    endfunction

    //===========================================================================
    function GetSoundDurationBJ takes sound soundHandle returns real
        if (soundHandle == null) then
            return bj_NOTHING_SOUND_DURATION
        else
            return I2R(GetSoundDuration(soundHandle)) * 0.001
        endif
    endfunction

    //===========================================================================
    function GetSoundFileDurationBJ takes string musicFileName returns real
        return I2R(GetSoundFileDuration(musicFileName)) * 0.001
    endfunction

    //===========================================================================
    function GetLastPlayedSound takes nothing returns sound
        return bj_lastPlayedSound
    endfunction

    //===========================================================================
    function GetLastPlayedMusic takes nothing returns string
        return bj_lastPlayedMusic
    endfunction

    //===========================================================================
    function VolumeGroupSetVolumeBJ takes volumegroup vgroup, real percent returns nothing
        call VolumeGroupSetVolume(vgroup, percent * 0.01)
    endfunction

    //===========================================================================
    function SetCineModeVolumeGroupsImmediateBJ takes nothing returns nothing
        call VolumeGroupSetVolume(SOUND_VOLUMEGROUP_UNITMOVEMENT,  bj_CINEMODE_VOLUME_UNITMOVEMENT)
        call VolumeGroupSetVolume(SOUND_VOLUMEGROUP_UNITSOUNDS,    bj_CINEMODE_VOLUME_UNITSOUNDS)
        call VolumeGroupSetVolume(SOUND_VOLUMEGROUP_COMBAT,        bj_CINEMODE_VOLUME_COMBAT)
        call VolumeGroupSetVolume(SOUND_VOLUMEGROUP_SPELLS,        bj_CINEMODE_VOLUME_SPELLS)
        call VolumeGroupSetVolume(SOUND_VOLUMEGROUP_UI,            bj_CINEMODE_VOLUME_UI)
        call VolumeGroupSetVolume(SOUND_VOLUMEGROUP_MUSIC,         bj_CINEMODE_VOLUME_MUSIC)
        call VolumeGroupSetVolume(SOUND_VOLUMEGROUP_AMBIENTSOUNDS, bj_CINEMODE_VOLUME_AMBIENTSOUNDS)
        call VolumeGroupSetVolume(SOUND_VOLUMEGROUP_FIRE,          bj_CINEMODE_VOLUME_FIRE)
    endfunction

    //===========================================================================
    function SetCineModeVolumeGroupsBJ takes nothing returns nothing
        // Delay the request if it occurs at map init.
        if bj_gameStarted then
            call SetCineModeVolumeGroupsImmediateBJ()
        else
            call TimerStart(bj_volumeGroupsTimer, bj_GAME_STARTED_THRESHOLD, false, function SetCineModeVolumeGroupsImmediateBJ)
        endif
    endfunction

    //===========================================================================
    function SetSpeechVolumeGroupsImmediateBJ takes nothing returns nothing
        call VolumeGroupSetVolume(SOUND_VOLUMEGROUP_UNITMOVEMENT,  bj_SPEECH_VOLUME_UNITMOVEMENT)
        call VolumeGroupSetVolume(SOUND_VOLUMEGROUP_UNITSOUNDS,    bj_SPEECH_VOLUME_UNITSOUNDS)
        call VolumeGroupSetVolume(SOUND_VOLUMEGROUP_COMBAT,        bj_SPEECH_VOLUME_COMBAT)
        call VolumeGroupSetVolume(SOUND_VOLUMEGROUP_SPELLS,        bj_SPEECH_VOLUME_SPELLS)
        call VolumeGroupSetVolume(SOUND_VOLUMEGROUP_UI,            bj_SPEECH_VOLUME_UI)
        call VolumeGroupSetVolume(SOUND_VOLUMEGROUP_MUSIC,         bj_SPEECH_VOLUME_MUSIC)
        call VolumeGroupSetVolume(SOUND_VOLUMEGROUP_AMBIENTSOUNDS, bj_SPEECH_VOLUME_AMBIENTSOUNDS)
        call VolumeGroupSetVolume(SOUND_VOLUMEGROUP_FIRE,          bj_SPEECH_VOLUME_FIRE)
    endfunction

    //===========================================================================
    function SetSpeechVolumeGroupsBJ takes nothing returns nothing
        // Delay the request if it occurs at map init.
        if bj_gameStarted then
            call SetSpeechVolumeGroupsImmediateBJ()
        else
            call TimerStart(bj_volumeGroupsTimer, bj_GAME_STARTED_THRESHOLD, false, function SetSpeechVolumeGroupsImmediateBJ)
        endif
    endfunction

    //===========================================================================
    function VolumeGroupResetImmediateBJ takes nothing returns nothing
        call VolumeGroupReset()
    endfunction

    //===========================================================================
    function VolumeGroupResetBJ takes nothing returns nothing
        // Delay the request if it occurs at map init.
        if bj_gameStarted then
            call VolumeGroupResetImmediateBJ()
        else
            call TimerStart(bj_volumeGroupsTimer, bj_GAME_STARTED_THRESHOLD, false, function VolumeGroupResetImmediateBJ)
        endif
    endfunction

    //===========================================================================
    function GetSoundIsPlayingBJ takes sound soundHandle returns boolean
        return GetSoundIsLoading(soundHandle) or GetSoundIsPlaying(soundHandle)
    endfunction

    //===========================================================================
    function WaitForSoundBJ takes sound soundHandle, real offset returns nothing
        call TriggerWaitForSound( soundHandle, offset )
    endfunction

    //===========================================================================
    function SetMapMusicIndexedBJ takes string musicName, integer index returns nothing
        call SetMapMusic(musicName, false, index)
    endfunction

    //===========================================================================
    function SetMapMusicRandomBJ takes string musicName returns nothing
        call SetMapMusic(musicName, true, 0)
    endfunction

    //===========================================================================
    function ClearMapMusicBJ takes nothing returns nothing
        call ClearMapMusic()
    endfunction

    //===========================================================================
    function SetStackedSoundBJ takes boolean add, sound soundHandle, rect r returns nothing
        local real width = GetRectMaxX(r) - GetRectMinX(r)
        local real height = GetRectMaxY(r) - GetRectMinY(r)

        call SetSoundPosition(soundHandle, GetRectCenterX(r), GetRectCenterY(r), 0)
        if add then
            call RegisterStackedSound(soundHandle, true, width, height)
        else
            call UnregisterStackedSound(soundHandle, true, width, height)
        endif
    endfunction

    //===========================================================================
    function StartSoundForPlayerBJ takes player whichPlayer, sound soundHandle returns nothing
        if (whichPlayer == GetLocalPlayer()) then
            call StartSound(soundHandle)
        endif
    endfunction

    //===========================================================================
    function VolumeGroupSetVolumeForPlayerBJ takes player whichPlayer, volumegroup vgroup, real scale returns nothing
        if (GetLocalPlayer() == whichPlayer) then
            call VolumeGroupSetVolume(vgroup, scale)
        endif
    endfunction

    //===========================================================================
    function EnableDawnDusk takes boolean flag returns nothing
        set bj_useDawnDuskSounds = flag
    endfunction

    //===========================================================================
    function IsDawnDuskEnabled takes nothing returns boolean
        return bj_useDawnDuskSounds
    endfunction



    //***************************************************************************
    //*
    //*  Day/Night ambient sounds
    //*
    //***************************************************************************

    //===========================================================================
    function SetAmbientDaySound takes string inLabel returns nothing
        local real ToD

        // Stop old sound, if necessary
        if (bj_dayAmbientSound != null) then
            call StopSound(bj_dayAmbientSound, true, true)
        endif

        // Create new sound
        set bj_dayAmbientSound = CreateMIDISound(inLabel, 20, 20)

        // Start the sound if necessary, based on current time
        set ToD = GetTimeOfDay()
        if (ToD >= bj_TOD_DAWN and ToD < bj_TOD_DUSK) then
            call StartSound(bj_dayAmbientSound)
        endif
    endfunction

    //===========================================================================
    function SetAmbientNightSound takes string inLabel returns nothing
        local real ToD

        // Stop old sound, if necessary
        if (bj_nightAmbientSound != null) then
            call StopSound(bj_nightAmbientSound, true, true)
        endif

        // Create new sound
        set bj_nightAmbientSound = CreateMIDISound(inLabel, 20, 20)

        // Start the sound if necessary, based on current time
        set ToD = GetTimeOfDay()
        if (ToD < bj_TOD_DAWN or ToD >= bj_TOD_DUSK) then
            call StartSound(bj_nightAmbientSound)
        endif
    endfunction



    //***************************************************************************
    //*
    //*  Special Effect Utility Functions
    //*
    //***************************************************************************

    //===========================================================================
    function AddSpecialEffectLocBJ takes location where, string modelName returns effect
        set bj_lastCreatedEffect = AddSpecialEffectLoc(modelName, where)
        return bj_lastCreatedEffect
    endfunction

    //===========================================================================
    function AddSpecialEffectTargetUnitBJ takes string attachPointName, widget targetWidget, string modelName returns effect
        set bj_lastCreatedEffect = AddSpecialEffectTarget(modelName, targetWidget, attachPointName)
        return bj_lastCreatedEffect
    endfunction

    //===========================================================================
    // Two distinct trigger actions can't share the same function name, so this
    // dummy function simply mimics the behavior of an existing call.
    //
    // Commented out - Destructibles have no attachment points.
    //
    //function AddSpecialEffectTargetDestructableBJ takes string attachPointName, widget targetWidget, string modelName returns effect
    //    return AddSpecialEffectTargetUnitBJ(attachPointName, targetWidget, modelName)
    //endfunction

    //===========================================================================
    // Two distinct trigger actions can't share the same function name, so this
    // dummy function simply mimics the behavior of an existing call.
    //
    // Commented out - Items have no attachment points.
    //
    //function AddSpecialEffectTargetItemBJ takes string attachPointName, widget targetWidget, string modelName returns effect
    //    return AddSpecialEffectTargetUnitBJ(attachPointName, targetWidget, modelName)
    //endfunction

    //===========================================================================
    function DestroyEffectBJ takes effect whichEffect returns nothing
        call DestroyEffect(whichEffect)
    endfunction

    //===========================================================================
    function GetLastCreatedEffectBJ takes nothing returns effect
        return bj_lastCreatedEffect
    endfunction



    //***************************************************************************
    //*
    //*  Hero and Item Utility Functions
    //*
    //***************************************************************************

    //===========================================================================
    function GetItemLoc takes item whichItem returns location
        return Location(GetItemX(whichItem), GetItemY(whichItem))
    endfunction

    //===========================================================================
    function GetItemLifeBJ takes widget whichWidget returns real
        return GetWidgetLife(whichWidget)
    endfunction

    //===========================================================================
    function SetItemLifeBJ takes widget whichWidget, real life returns nothing
        call SetWidgetLife(whichWidget, life)
    endfunction

    //===========================================================================
    function AddHeroXPSwapped takes integer xpToAdd, unit whichHero, boolean showEyeCandy returns nothing
        call AddHeroXP(whichHero, xpToAdd, showEyeCandy)
    endfunction

    //===========================================================================
    function SetHeroLevelBJ takes unit whichHero, integer newLevel, boolean showEyeCandy returns nothing
        local integer oldLevel = GetHeroLevel(whichHero)

        if (newLevel > oldLevel) then
            call SetHeroLevel(whichHero, newLevel, showEyeCandy)
        elseif (newLevel < oldLevel) then
            call UnitStripHeroLevel(whichHero, oldLevel - newLevel)
        else
            // No change in level - ignore the request.
        endif
    endfunction

    //===========================================================================
    function DecUnitAbilityLevelSwapped takes integer abilcode, unit whichUnit returns integer
        return DecUnitAbilityLevel(whichUnit, abilcode)
    endfunction

    //===========================================================================
    function IncUnitAbilityLevelSwapped takes integer abilcode, unit whichUnit returns integer
        return IncUnitAbilityLevel(whichUnit, abilcode)
    endfunction

    //===========================================================================
    function SetUnitAbilityLevelSwapped takes integer abilcode, unit whichUnit, integer level returns integer
        return SetUnitAbilityLevel(whichUnit, abilcode, level)
    endfunction

    //===========================================================================
    function GetUnitAbilityLevelSwapped takes integer abilcode, unit whichUnit returns integer
        return GetUnitAbilityLevel(whichUnit, abilcode)
    endfunction

    //===========================================================================
    function UnitHasBuffBJ takes unit whichUnit, integer buffcode returns boolean
        return (GetUnitAbilityLevel(whichUnit, buffcode) > 0)
    endfunction

    //===========================================================================
    function UnitRemoveBuffBJ takes integer buffcode, unit whichUnit returns boolean
        return UnitRemoveAbility(whichUnit, buffcode)
    endfunction

    //===========================================================================
    function UnitAddItemSwapped takes item whichItem, unit whichHero returns boolean
        return UnitAddItem(whichHero, whichItem)
    endfunction

    //===========================================================================
    function UnitAddItemByIdSwapped takes integer itemId, unit whichHero returns item
        // Create the item at the hero's feet first, and then give it to him.
        // This is to ensure that the item will be left at the hero's feet if
        // his inventory is full.
        set bj_lastCreatedItem = CreateItem(itemId, GetUnitX(whichHero), GetUnitY(whichHero))
        call UnitAddItem(whichHero, bj_lastCreatedItem)
        return bj_lastCreatedItem
    endfunction

    //===========================================================================
    function UnitRemoveItemSwapped takes item whichItem, unit whichHero returns nothing
        set bj_lastRemovedItem = whichItem
        call UnitRemoveItem(whichHero, whichItem)
    endfunction

    //===========================================================================
    // Translates 0-based slot indices to 1-based slot indices.
    //
    function UnitRemoveItemFromSlotSwapped takes integer itemSlot, unit whichHero returns item
        set bj_lastRemovedItem = UnitRemoveItemFromSlot(whichHero, itemSlot-1)
        return bj_lastRemovedItem
    endfunction

    //===========================================================================
    function CreateItemLoc takes integer itemId, location loc returns item
        set bj_lastCreatedItem = CreateItem(itemId, GetLocationX(loc), GetLocationY(loc))
        return bj_lastCreatedItem
    endfunction

    //===========================================================================
    function GetLastCreatedItem takes nothing returns item
        return bj_lastCreatedItem
    endfunction

    //===========================================================================
    function GetLastRemovedItem takes nothing returns item
        return bj_lastRemovedItem
    endfunction

    //===========================================================================
    function SetItemPositionLoc takes item whichItem, location loc returns nothing
        call SetItemPosition(whichItem, GetLocationX(loc), GetLocationY(loc))
    endfunction

    //===========================================================================
    function GetLearnedSkillBJ takes nothing returns integer
        return GetLearnedSkill()
    endfunction

    //===========================================================================
    function SuspendHeroXPBJ takes boolean flag, unit whichHero returns nothing
        call SuspendHeroXP(whichHero, not flag)
    endfunction

    //===========================================================================
    function SetPlayerHandicapXPBJ takes player whichPlayer, real handicapPercent returns nothing
        call SetPlayerHandicapXP(whichPlayer, handicapPercent * 0.01)
    endfunction

    //===========================================================================
    function GetPlayerHandicapXPBJ takes player whichPlayer returns real
        return GetPlayerHandicapXP(whichPlayer) * 100
    endfunction

    //===========================================================================
    function SetPlayerHandicapBJ takes player whichPlayer, real handicapPercent returns nothing
        call SetPlayerHandicap(whichPlayer, handicapPercent * 0.01)
    endfunction

    //===========================================================================
    function GetPlayerHandicapBJ takes player whichPlayer returns real
        return GetPlayerHandicap(whichPlayer) * 100
    endfunction

    //===========================================================================
    function GetHeroStatBJ takes integer whichStat, unit whichHero, boolean includeBonuses returns integer
        if (whichStat == bj_HEROSTAT_STR) then
            return GetHeroStr(whichHero, includeBonuses)
        elseif (whichStat == bj_HEROSTAT_AGI) then
            return GetHeroAgi(whichHero, includeBonuses)
        elseif (whichStat == bj_HEROSTAT_INT) then
            return GetHeroInt(whichHero, includeBonuses)
        else
            // Unrecognized hero stat - return 0
            return 0
        endif
    endfunction

    //===========================================================================
    function SetHeroStat takes unit whichHero, integer whichStat, integer value returns nothing
        // Ignore requests for negative hero stats.
        if (value <= 0) then
            return
        endif

        if (whichStat == bj_HEROSTAT_STR) then
            call SetHeroStr(whichHero, value, true)
        elseif (whichStat == bj_HEROSTAT_AGI) then
            call SetHeroAgi(whichHero, value, true)
        elseif (whichStat == bj_HEROSTAT_INT) then
            call SetHeroInt(whichHero, value, true)
        else
            // Unrecognized hero stat - ignore the request.
        endif
    endfunction

    //===========================================================================
    function ModifyHeroStat takes integer whichStat, unit whichHero, integer modifyMethod, integer value returns nothing
        if (modifyMethod == bj_MODIFYMETHOD_ADD) then
            call SetHeroStat(whichHero, whichStat, GetHeroStatBJ(whichStat, whichHero, false) + value)
        elseif (modifyMethod == bj_MODIFYMETHOD_SUB) then
            call SetHeroStat(whichHero, whichStat, GetHeroStatBJ(whichStat, whichHero, false) - value)
        elseif (modifyMethod == bj_MODIFYMETHOD_SET) then
            call SetHeroStat(whichHero, whichStat, value)
        else
            // Unrecognized modification method - ignore the request.
        endif
    endfunction

    //===========================================================================
    function ModifyHeroSkillPoints takes unit whichHero, integer modifyMethod, integer value returns boolean
        if (modifyMethod == bj_MODIFYMETHOD_ADD) then
            return UnitModifySkillPoints(whichHero, value)
        elseif (modifyMethod == bj_MODIFYMETHOD_SUB) then
            return UnitModifySkillPoints(whichHero, -value)
        elseif (modifyMethod == bj_MODIFYMETHOD_SET) then
            return UnitModifySkillPoints(whichHero, value - GetHeroSkillPoints(whichHero))
        else
            // Unrecognized modification method - ignore the request and return failure.
            return false
        endif
    endfunction

    //===========================================================================
    function UnitDropItemPointBJ takes unit whichUnit, item whichItem, real x, real y returns boolean
        return UnitDropItemPoint(whichUnit, whichItem, x, y)
    endfunction

    //===========================================================================
    function UnitDropItemPointLoc takes unit whichUnit, item whichItem, location loc returns boolean
        return UnitDropItemPoint(whichUnit, whichItem, GetLocationX(loc), GetLocationY(loc))
    endfunction

    //===========================================================================
    function UnitDropItemSlotBJ takes unit whichUnit, item whichItem, integer slot returns boolean
        return UnitDropItemSlot(whichUnit, whichItem, slot-1)
    endfunction

    //===========================================================================
    function UnitDropItemTargetBJ takes unit whichUnit, item whichItem, widget target returns boolean
        return UnitDropItemTarget(whichUnit, whichItem, target)
    endfunction

    //===========================================================================
    // Two distinct trigger actions can't share the same function name, so this
    // dummy function simply mimics the behavior of an existing call.
    //
    function UnitUseItemDestructable takes unit whichUnit, item whichItem, widget target returns boolean
        return UnitUseItemTarget(whichUnit, whichItem, target)
    endfunction

    //===========================================================================
    function UnitUseItemPointLoc takes unit whichUnit, item whichItem, location loc returns boolean
        return UnitUseItemPoint(whichUnit, whichItem, GetLocationX(loc), GetLocationY(loc))
    endfunction

    //===========================================================================
    // Translates 0-based slot indices to 1-based slot indices.
    //
    function UnitItemInSlotBJ takes unit whichUnit, integer itemSlot returns item
        return UnitItemInSlot(whichUnit, itemSlot-1)
    endfunction

    //===========================================================================
    // Translates 0-based slot indices to 1-based slot indices.
    //
    function GetInventoryIndexOfItemTypeBJ takes unit whichUnit, integer itemId returns integer
        local integer index
        local item    indexItem

        set index = 0
        loop
            set indexItem = UnitItemInSlot(whichUnit, index)
            if (indexItem != null) and (GetItemTypeId(indexItem) == itemId) then
                return index + 1
            endif

            set index = index + 1
            exitwhen index >= bj_MAX_INVENTORY
        endloop
        return 0
    endfunction

    //===========================================================================
    function GetItemOfTypeFromUnitBJ takes unit whichUnit, integer itemId returns item
        local integer index = GetInventoryIndexOfItemTypeBJ(whichUnit, itemId)

        if (index == 0) then
            return null
        else
            return UnitItemInSlot(whichUnit, index - 1)
        endif
    endfunction

    //===========================================================================
    function UnitHasItemOfTypeBJ takes unit whichUnit, integer itemId returns boolean
        return GetInventoryIndexOfItemTypeBJ(whichUnit, itemId) > 0
    endfunction

    //===========================================================================
    function UnitInventoryCount takes unit whichUnit returns integer
        local integer index = 0
        local integer count = 0

        loop
            if (UnitItemInSlot(whichUnit, index) != null) then
                set count = count + 1
            endif

            set index = index + 1
            exitwhen index >= bj_MAX_INVENTORY
        endloop

        return count
    endfunction

    //===========================================================================
    function UnitInventorySizeBJ takes unit whichUnit returns integer
        return UnitInventorySize(whichUnit)
    endfunction

    //===========================================================================
    function SetItemInvulnerableBJ takes item whichItem, boolean flag returns nothing
        call SetItemInvulnerable(whichItem, flag)
    endfunction

    //===========================================================================
    function SetItemDropOnDeathBJ takes item whichItem, boolean flag returns nothing
        call SetItemDropOnDeath(whichItem, flag)
    endfunction

    //===========================================================================
    function SetItemDroppableBJ takes item whichItem, boolean flag returns nothing
        call SetItemDroppable(whichItem, flag)
    endfunction

    //===========================================================================
    function SetItemPlayerBJ takes item whichItem, player whichPlayer, boolean changeColor returns nothing
        call SetItemPlayer(whichItem, whichPlayer, changeColor)
    endfunction

    //===========================================================================
    function SetItemVisibleBJ takes boolean show, item whichItem returns nothing
        call SetItemVisible(whichItem, show)
    endfunction

    //===========================================================================
    function IsItemHiddenBJ takes item whichItem returns boolean
        return not IsItemVisible(whichItem)
    endfunction

    //===========================================================================
    function ChooseRandomItemBJ takes integer level returns integer
        return ChooseRandomItem(level)
    endfunction

    //===========================================================================
    function ChooseRandomItemExBJ takes integer level, itemtype whichType returns integer
        return ChooseRandomItemEx(whichType, level)
    endfunction

    //===========================================================================
    function ChooseRandomNPBuildingBJ takes nothing returns integer
        return ChooseRandomNPBuilding()
    endfunction

    //===========================================================================
    function ChooseRandomCreepBJ takes integer level returns integer
        return ChooseRandomCreep(level)
    endfunction

    //===========================================================================
    function EnumItemsInRectBJ takes rect r, code actionFunc returns nothing
        call EnumItemsInRect(r, null, actionFunc)
    endfunction

    //===========================================================================
    // See GroupPickRandomUnitEnum for the details of this algorithm.
    //
    function RandomItemInRectBJEnum takes nothing returns nothing
        set bj_itemRandomConsidered = bj_itemRandomConsidered + 1
        if (GetRandomInt(1, bj_itemRandomConsidered) == 1) then
            set bj_itemRandomCurrentPick = GetEnumItem()
        endif
    endfunction

    //===========================================================================
    // Picks a random item from within a rect, matching a condition
    //
    function RandomItemInRectBJ takes rect r, boolexpr filter returns item
        set bj_itemRandomConsidered = 0
        set bj_itemRandomCurrentPick = null
        call EnumItemsInRect(r, filter, function RandomItemInRectBJEnum)
        call DestroyBoolExpr(filter)
        return bj_itemRandomCurrentPick
    endfunction

    //===========================================================================
    // Picks a random item from within a rect
    //
    function RandomItemInRectSimpleBJ takes rect r returns item
        return RandomItemInRectBJ(r, null)
    endfunction

    //===========================================================================
    function CheckItemStatus takes item whichItem, integer status returns boolean
        if (status == bj_ITEM_STATUS_HIDDEN) then
            return not IsItemVisible(whichItem)
        elseif (status == bj_ITEM_STATUS_OWNED) then
            return IsItemOwned(whichItem)
        elseif (status == bj_ITEM_STATUS_INVULNERABLE) then
            return IsItemInvulnerable(whichItem)
        elseif (status == bj_ITEM_STATUS_POWERUP) then
            return IsItemPowerup(whichItem)
        elseif (status == bj_ITEM_STATUS_SELLABLE) then
            return IsItemSellable(whichItem)
        elseif (status == bj_ITEM_STATUS_PAWNABLE) then
            return IsItemPawnable(whichItem)
        else
            // Unrecognized status - return false
            return false
        endif
    endfunction

    //===========================================================================
    function CheckItemcodeStatus takes integer itemId, integer status returns boolean
        if (status == bj_ITEMCODE_STATUS_POWERUP) then
            return IsItemIdPowerup(itemId)
        elseif (status == bj_ITEMCODE_STATUS_SELLABLE) then
            return IsItemIdSellable(itemId)
        elseif (status == bj_ITEMCODE_STATUS_PAWNABLE) then
            return IsItemIdPawnable(itemId)
        else
            // Unrecognized status - return false
            return false
        endif
    endfunction



    //***************************************************************************
    //*
    //*  Unit Utility Functions
    //*
    //***************************************************************************

    //===========================================================================
    function UnitId2OrderIdBJ takes integer unitId returns integer
        return unitId
    endfunction

    //===========================================================================
    function String2UnitIdBJ takes string unitIdString returns integer
        return UnitId(unitIdString)
    endfunction

    //===========================================================================
    function UnitId2StringBJ takes integer unitId returns string
        local string unitString = UnitId2String(unitId)

        if (unitString != null) then
            return unitString
        endif

        // The unitId was not recognized - return an empty string.
        return ""
    endfunction

    //===========================================================================
    function String2OrderIdBJ takes string orderIdString returns integer
        local integer orderId
       
        // Check to see if it's a generic order.
        set orderId = OrderId(orderIdString)
        if (orderId != 0) then
            return orderId
        endif

        // Check to see if it's a (train) unit order.
        set orderId = UnitId(orderIdString)
        if (orderId != 0) then
            return orderId
        endif

        // Unrecognized - return 0
        return 0
    endfunction

    //===========================================================================
    function OrderId2StringBJ takes integer orderId returns string
        local string orderString

        // Check to see if it's a generic order.
        set orderString = OrderId2String(orderId)
        if (orderString != null) then
            return orderString
        endif

        // Check to see if it's a (train) unit order.
        set orderString = UnitId2String(orderId)
        if (orderString != null) then
            return orderString
        endif

        // Unrecognized - return an empty string.
        return ""
    endfunction

    //===========================================================================
    function GetIssuedOrderIdBJ takes nothing returns integer
        return GetIssuedOrderId()
    endfunction

    //===========================================================================
    function GetKillingUnitBJ takes nothing returns unit
        return GetKillingUnit()
    endfunction

    //===========================================================================
    function CreateUnitAtLocSaveLast takes player id, integer unitid, location loc, real face returns unit
        if (unitid == 'ugol') then
            set bj_lastCreatedUnit = CreateBlightedGoldmine(id, GetLocationX(loc), GetLocationY(loc), face)
        else
            set bj_lastCreatedUnit = CreateUnitAtLoc(id, unitid, loc, face)
        endif

        return bj_lastCreatedUnit
    endfunction

    //===========================================================================
    function GetLastCreatedUnit takes nothing returns unit
        return bj_lastCreatedUnit
    endfunction

    //===========================================================================
    function CreateNUnitsAtLoc takes integer count, integer unitId, player whichPlayer, location loc, real face returns group
        call GroupClear(bj_lastCreatedGroup)
        loop
            set count = count - 1
            exitwhen count < 0
            call CreateUnitAtLocSaveLast(whichPlayer, unitId, loc, face)
            call GroupAddUnit(bj_lastCreatedGroup, bj_lastCreatedUnit)
        endloop
        return bj_lastCreatedGroup
    endfunction

    //===========================================================================
    function CreateNUnitsAtLocFacingLocBJ takes integer count, integer unitId, player whichPlayer, location loc, location lookAt returns group
        return CreateNUnitsAtLoc(count, unitId, whichPlayer, loc, AngleBetweenPoints(loc, lookAt))
    endfunction

    //===========================================================================
    function GetLastCreatedGroupEnum takes nothing returns nothing
        call GroupAddUnit(bj_groupLastCreatedDest, GetEnumUnit())
    endfunction

    //===========================================================================
    function GetLastCreatedGroup takes nothing returns group
        set bj_groupLastCreatedDest = CreateGroup()
        call ForGroup(bj_lastCreatedGroup, function GetLastCreatedGroupEnum)
        return bj_groupLastCreatedDest
    endfunction

    //===========================================================================
    function CreateCorpseLocBJ takes integer unitid, player whichPlayer, location loc returns unit
        set bj_lastCreatedUnit = CreateCorpse(whichPlayer, unitid, GetLocationX(loc), GetLocationY(loc), GetRandomReal(0, 360))
        return bj_lastCreatedUnit
    endfunction

    //===========================================================================
    function UnitSuspendDecayBJ takes boolean suspend, unit whichUnit returns nothing
        call UnitSuspendDecay(whichUnit, suspend)
    endfunction

    //===========================================================================
    function DelayedSuspendDecayStopAnimEnum takes nothing returns nothing
        local unit enumUnit = GetEnumUnit()

        if (GetUnitState(enumUnit, UNIT_STATE_LIFE) <= 0) then
            call SetUnitTimeScale(enumUnit, 0.0001)
        endif
    endfunction

    //===========================================================================
    function DelayedSuspendDecayBoneEnum takes nothing returns nothing
        local unit enumUnit = GetEnumUnit()

        if (GetUnitState(enumUnit, UNIT_STATE_LIFE) <= 0) then
            call UnitSuspendDecay(enumUnit, true)
            call SetUnitTimeScale(enumUnit, 0.0001)
        endif
    endfunction

    //===========================================================================
    // Game code explicitly sets the animation back to "decay bone" after the
    // initial corpse fades away, so we reset it now.  It's best not to show
    // off corpses thus created until after this grace period has passed.
    //
    function DelayedSuspendDecayFleshEnum takes nothing returns nothing
        local unit enumUnit = GetEnumUnit()

        if (GetUnitState(enumUnit, UNIT_STATE_LIFE) <= 0) then
            call UnitSuspendDecay(enumUnit, true)
            call SetUnitTimeScale(enumUnit, 10.0)
            call SetUnitAnimation(enumUnit, "decay flesh")
        endif
    endfunction

    //===========================================================================
    // Waits a short period of time to ensure that the corpse is decaying, and
    // then suspend the animation and corpse decay.
    //
    function DelayedSuspendDecay takes nothing returns nothing
        local group boneGroup
        local group fleshGroup

        // Switch the global unit groups over to local variables and recreate
        // the global versions, so that this function can handle overlapping
        // calls.
        set boneGroup = bj_suspendDecayBoneGroup
        set fleshGroup = bj_suspendDecayFleshGroup
        set bj_suspendDecayBoneGroup = CreateGroup()
        set bj_suspendDecayFleshGroup = CreateGroup()

        call ForGroup(fleshGroup, function DelayedSuspendDecayStopAnimEnum)
        call ForGroup(boneGroup, function DelayedSuspendDecayStopAnimEnum)

        call TriggerSleepAction(bj_CORPSE_MAX_DEATH_TIME)
        call ForGroup(fleshGroup, function DelayedSuspendDecayFleshEnum)
        call ForGroup(boneGroup, function DelayedSuspendDecayBoneEnum)

        call TriggerSleepAction(0.05)
        call ForGroup(fleshGroup, function DelayedSuspendDecayStopAnimEnum)

        call DestroyGroup(boneGroup)
        call DestroyGroup(fleshGroup)
    endfunction

    //===========================================================================
    function DelayedSuspendDecayCreate takes nothing returns nothing
        set bj_delayedSuspendDecayTrig = CreateTrigger()
        call TriggerRegisterTimerExpireEvent(bj_delayedSuspendDecayTrig, bj_delayedSuspendDecayTimer)
        call TriggerAddAction(bj_delayedSuspendDecayTrig, function DelayedSuspendDecay)
    endfunction

    //===========================================================================
    function CreatePermanentCorpseLocBJ takes integer style, integer unitid, player whichPlayer, location loc, real facing returns unit
        set bj_lastCreatedUnit = CreateCorpse(whichPlayer, unitid, GetLocationX(loc), GetLocationY(loc), facing)
        call SetUnitBlendTime(bj_lastCreatedUnit, 0)

        if (style == bj_CORPSETYPE_FLESH) then
            call SetUnitAnimation(bj_lastCreatedUnit, "decay flesh")
            call GroupAddUnit(bj_suspendDecayFleshGroup, bj_lastCreatedUnit)
        elseif (style == bj_CORPSETYPE_BONE) then
            call SetUnitAnimation(bj_lastCreatedUnit, "decay bone")
            call GroupAddUnit(bj_suspendDecayBoneGroup, bj_lastCreatedUnit)
        else
            // Unknown decay style - treat as skeletal.
            call SetUnitAnimation(bj_lastCreatedUnit, "decay bone")
            call GroupAddUnit(bj_suspendDecayBoneGroup, bj_lastCreatedUnit)
        endif

        call TimerStart(bj_delayedSuspendDecayTimer, 0.05, false, null)
        return bj_lastCreatedUnit
    endfunction

    //===========================================================================
    function GetUnitStateSwap takes unitstate whichState, unit whichUnit returns real
        return GetUnitState(whichUnit, whichState)
    endfunction

    //===========================================================================
    function GetUnitStatePercent takes unit whichUnit, unitstate whichState, unitstate whichMaxState returns real
        local real value    = GetUnitState(whichUnit, whichState)
        local real maxValue = GetUnitState(whichUnit, whichMaxState)

        // Return 0 for null units.
        if (whichUnit == null) or (maxValue == 0) then
            return 0.0
        endif

        return value / maxValue * 100.0
    endfunction

    //===========================================================================
    function GetUnitLifePercent takes unit whichUnit returns real
        return GetUnitStatePercent(whichUnit, UNIT_STATE_LIFE, UNIT_STATE_MAX_LIFE)
    endfunction

    //===========================================================================
    function GetUnitManaPercent takes unit whichUnit returns real
        return GetUnitStatePercent(whichUnit, UNIT_STATE_MANA, UNIT_STATE_MAX_MANA)
    endfunction

    //===========================================================================
    function SelectUnitSingle takes unit whichUnit returns nothing
        call ClearSelection()
        call SelectUnit(whichUnit, true)
    endfunction

    //===========================================================================
    function SelectGroupBJEnum takes nothing returns nothing
        call SelectUnit( GetEnumUnit(), true )
    endfunction

    //===========================================================================
    function SelectGroupBJ takes group g returns nothing
        call ClearSelection()
        call ForGroup( g, function SelectGroupBJEnum )
    endfunction

    //===========================================================================
    function SelectUnitAdd takes unit whichUnit returns nothing
        call SelectUnit(whichUnit, true)
    endfunction

    //===========================================================================
    function SelectUnitRemove takes unit whichUnit returns nothing
        call SelectUnit(whichUnit, false)
    endfunction

    //===========================================================================
    function ClearSelectionForPlayer takes player whichPlayer returns nothing
        if (GetLocalPlayer() == whichPlayer) then
            // Use only local code (no net traffic) within this block to avoid desyncs.
            call ClearSelection()
        endif
    endfunction

    //===========================================================================
    function SelectUnitForPlayerSingle takes unit whichUnit, player whichPlayer returns nothing
        if (GetLocalPlayer() == whichPlayer) then
            // Use only local code (no net traffic) within this block to avoid desyncs.
            call ClearSelection()
            call SelectUnit(whichUnit, true)
        endif
    endfunction

    //===========================================================================
    function SelectGroupForPlayerBJ takes group g, player whichPlayer returns nothing
        if (GetLocalPlayer() == whichPlayer) then
            // Use only local code (no net traffic) within this block to avoid desyncs.
            call ClearSelection()
            call ForGroup( g, function SelectGroupBJEnum )
        endif
    endfunction

    //===========================================================================
    function SelectUnitAddForPlayer takes unit whichUnit, player whichPlayer returns nothing
        if (GetLocalPlayer() == whichPlayer) then
            // Use only local code (no net traffic) within this block to avoid desyncs.
            call SelectUnit(whichUnit, true)
        endif
    endfunction

    //===========================================================================
    function SelectUnitRemoveForPlayer takes unit whichUnit, player whichPlayer returns nothing
        if (GetLocalPlayer() == whichPlayer) then
            // Use only local code (no net traffic) within this block to avoid desyncs.
            call SelectUnit(whichUnit, false)
        endif
    endfunction

    //===========================================================================
    function SetUnitLifeBJ takes unit whichUnit, real newValue returns nothing
        call SetUnitState(whichUnit, UNIT_STATE_LIFE, RMaxBJ(0,newValue))
    endfunction

    //===========================================================================
    function SetUnitManaBJ takes unit whichUnit, real newValue returns nothing
        call SetUnitState(whichUnit, UNIT_STATE_MANA, RMaxBJ(0,newValue))
    endfunction

    //===========================================================================
    function SetUnitLifePercentBJ takes unit whichUnit, real percent returns nothing
        call SetUnitState(whichUnit, UNIT_STATE_LIFE, GetUnitState(whichUnit, UNIT_STATE_MAX_LIFE) * RMaxBJ(0,percent) * 0.01)
    endfunction

    //===========================================================================
    function SetUnitManaPercentBJ takes unit whichUnit, real percent returns nothing
        call SetUnitState(whichUnit, UNIT_STATE_MANA, GetUnitState(whichUnit, UNIT_STATE_MAX_MANA) * RMaxBJ(0,percent) * 0.01)
    endfunction

    //===========================================================================
    function IsUnitDeadBJ takes unit whichUnit returns boolean
        return GetUnitState(whichUnit, UNIT_STATE_LIFE) <= 0
    endfunction

    //===========================================================================
    function IsUnitAliveBJ takes unit whichUnit returns boolean
        return not IsUnitDeadBJ(whichUnit)
    endfunction

    //===========================================================================
    function IsUnitGroupDeadBJEnum takes nothing returns nothing
        if not IsUnitDeadBJ(GetEnumUnit()) then
            set bj_isUnitGroupDeadResult = false
        endif
    endfunction

    //===========================================================================
    // Returns true if every unit of the group is dead.
    //
    function IsUnitGroupDeadBJ takes group g returns boolean
        // If the user wants the group destroyed, remember that fact and clear
        // the flag, in case it is used again in the callback.
        local boolean wantDestroy = bj_wantDestroyGroup
        set bj_wantDestroyGroup = false

        set bj_isUnitGroupDeadResult = true
        call ForGroup(g, function IsUnitGroupDeadBJEnum)

        // If the user wants the group destroyed, do so now.
        if (wantDestroy) then
            call DestroyGroup(g)
        endif
        return bj_isUnitGroupDeadResult
    endfunction

    //===========================================================================
    function IsUnitGroupEmptyBJEnum takes nothing returns nothing
        set bj_isUnitGroupEmptyResult = false
    endfunction

    //===========================================================================
    // Returns true if the group contains no units.
    //
    function IsUnitGroupEmptyBJ takes group g returns boolean
        // If the user wants the group destroyed, remember that fact and clear
        // the flag, in case it is used again in the callback.
        local boolean wantDestroy = bj_wantDestroyGroup
        set bj_wantDestroyGroup = false

        set bj_isUnitGroupEmptyResult = true
        call ForGroup(g, function IsUnitGroupEmptyBJEnum)

        // If the user wants the group destroyed, do so now.
        if (wantDestroy) then
            call DestroyGroup(g)
        endif
        return bj_isUnitGroupEmptyResult
    endfunction

    //===========================================================================
    function IsUnitGroupInRectBJEnum takes nothing returns nothing
        if not RectContainsUnit(bj_isUnitGroupInRectRect, GetEnumUnit()) then
            set bj_isUnitGroupInRectResult = false
        endif
    endfunction

    //===========================================================================
    // Returns true if every unit of the group is within the given rect.
    //
    function IsUnitGroupInRectBJ takes group g, rect r returns boolean
        set bj_isUnitGroupInRectResult = true
        set bj_isUnitGroupInRectRect = r
        call ForGroup(g, function IsUnitGroupInRectBJEnum)
        return bj_isUnitGroupInRectResult
    endfunction

    //===========================================================================
    function IsUnitHiddenBJ takes unit whichUnit returns boolean
        return IsUnitHidden(whichUnit)
    endfunction

    //===========================================================================
    function ShowUnitHide takes unit whichUnit returns nothing
        call ShowUnit(whichUnit, false)
    endfunction

    //===========================================================================
    function ShowUnitShow takes unit whichUnit returns nothing
        // Prevent dead heroes from being unhidden.
        if (IsUnitType(whichUnit, UNIT_TYPE_HERO) and IsUnitDeadBJ(whichUnit)) then
            return
        endif

        call ShowUnit(whichUnit, true)
    endfunction

    //===========================================================================
    function IssueHauntOrderAtLocBJFilter takes nothing returns boolean
        return GetUnitTypeId(GetFilterUnit()) == 'ngol'
    endfunction

    //===========================================================================
    function IssueHauntOrderAtLocBJ takes unit whichPeon, location loc returns boolean
        local group g = null
        local unit goldMine = null

        // Search for a gold mine within a 1-cell radius of the specified location.
        set g = CreateGroup()
        call GroupEnumUnitsInRangeOfLoc(g, loc, 2*bj_CELLWIDTH, filterIssueHauntOrderAtLocBJ)
        set goldMine = FirstOfGroup(g)
        call DestroyGroup(g)

        // If no mine was found, abort the request.
        if (goldMine == null) then
            return false
        endif

        // Issue the Haunt Gold Mine order.
        return IssueTargetOrderById(whichPeon, 'ugol', goldMine)
    endfunction

    //===========================================================================
    function IssueBuildOrderByIdLocBJ takes unit whichPeon, integer unitId, location loc returns boolean
        if (unitId == 'ugol') then
            return IssueHauntOrderAtLocBJ(whichPeon, loc)
        else
            return IssueBuildOrderById(whichPeon, unitId, GetLocationX(loc), GetLocationY(loc))
        endif
    endfunction

    //===========================================================================
    function IssueTrainOrderByIdBJ takes unit whichUnit, integer unitId returns boolean
        return IssueImmediateOrderById(whichUnit, unitId)
    endfunction

    //===========================================================================
    function GroupTrainOrderByIdBJ takes group g, integer unitId returns boolean
        return GroupImmediateOrderById(g, unitId)
    endfunction

    //===========================================================================
    function IssueUpgradeOrderByIdBJ takes unit whichUnit, integer techId returns boolean
        return IssueImmediateOrderById(whichUnit, techId)
    endfunction

    //===========================================================================
    function GetAttackedUnitBJ takes nothing returns unit
        return GetTriggerUnit()
    endfunction

    //===========================================================================
    function SetUnitFlyHeightBJ takes unit whichUnit, real newHeight, real rate returns nothing
        call SetUnitFlyHeight(whichUnit, newHeight, rate)
    endfunction

    //===========================================================================
    function SetUnitTurnSpeedBJ takes unit whichUnit, real turnSpeed returns nothing
        call SetUnitTurnSpeed(whichUnit, turnSpeed)
    endfunction

    //===========================================================================
    function SetUnitPropWindowBJ takes unit whichUnit, real propWindow returns nothing
        local real angle = propWindow
        if (angle <= 0) then
            set angle = 1
        elseif (angle >= 360) then
            set angle = 359
        endif
        set angle = angle * bj_DEGTORAD

        call SetUnitPropWindow(whichUnit, angle)
    endfunction

    //===========================================================================
    function GetUnitPropWindowBJ takes unit whichUnit returns real
        return GetUnitPropWindow(whichUnit) * bj_RADTODEG
    endfunction

    //===========================================================================
    function GetUnitDefaultPropWindowBJ takes unit whichUnit returns real
        return GetUnitDefaultPropWindow(whichUnit)
    endfunction

    //===========================================================================
    function SetUnitBlendTimeBJ takes unit whichUnit, real blendTime returns nothing
        call SetUnitBlendTime(whichUnit, blendTime)
    endfunction

    //===========================================================================
    function SetUnitAcquireRangeBJ takes unit whichUnit, real acquireRange returns nothing
        call SetUnitAcquireRange(whichUnit, acquireRange)
    endfunction

    //===========================================================================
    function UnitSetCanSleepBJ takes unit whichUnit, boolean canSleep returns nothing
        call UnitAddSleep(whichUnit, canSleep)
    endfunction

    //===========================================================================
    function UnitCanSleepBJ takes unit whichUnit returns boolean
        return UnitCanSleep(whichUnit)
    endfunction

    //===========================================================================
    function UnitWakeUpBJ takes unit whichUnit returns nothing
        call UnitWakeUp(whichUnit)
    endfunction

    //===========================================================================
    function UnitIsSleepingBJ takes unit whichUnit returns boolean
        return UnitIsSleeping(whichUnit)
    endfunction

    //===========================================================================
    function WakePlayerUnitsEnum takes nothing returns nothing
        call UnitWakeUp(GetEnumUnit())
    endfunction

    //===========================================================================
    function WakePlayerUnits takes player whichPlayer returns nothing
        local group g = CreateGroup()
        call GroupEnumUnitsOfPlayer(g, whichPlayer, null)
        call ForGroup(g, function WakePlayerUnitsEnum)
        call DestroyGroup(g)
    endfunction

    //===========================================================================
    function EnableCreepSleepBJ takes boolean enable returns nothing
        call SetPlayerState(Player(PLAYER_NEUTRAL_AGGRESSIVE), PLAYER_STATE_NO_CREEP_SLEEP, IntegerTertiaryOp(enable, 0, 1))

        // If we're disabling, attempt to wake any already-sleeping creeps.
        if (not enable) then
            call WakePlayerUnits(Player(PLAYER_NEUTRAL_AGGRESSIVE))
        endif
    endfunction

    //===========================================================================
    function UnitGenerateAlarms takes unit whichUnit, boolean generate returns boolean
        return UnitIgnoreAlarm(whichUnit, not generate)
    endfunction

    //===========================================================================
    function DoesUnitGenerateAlarms takes unit whichUnit returns boolean
        return not UnitIgnoreAlarmToggled(whichUnit)
    endfunction

    //===========================================================================
    function PauseAllUnitsBJEnum takes nothing returns nothing
        call PauseUnit( GetEnumUnit(), bj_pauseAllUnitsFlag )
    endfunction

    //===========================================================================
    // Pause all units
    function PauseAllUnitsBJ takes boolean pause returns nothing
        local integer index
        local player  indexPlayer
        local group   g

        set bj_pauseAllUnitsFlag = pause
        set g = CreateGroup()
        set index = 0
        loop
            set indexPlayer = Player( index )

            // If this is a computer slot, pause/resume the AI.
            if (GetPlayerController( indexPlayer ) == MAP_CONTROL_COMPUTER) then
                call PauseCompAI( indexPlayer, pause )
            endif

            // Enumerate and unpause every unit owned by the player.
            call GroupEnumUnitsOfPlayer( g, indexPlayer, null )
            call ForGroup( g, function PauseAllUnitsBJEnum )
            call GroupClear( g )

            set index = index + 1
            exitwhen index == bj_MAX_PLAYER_SLOTS
        endloop
        call DestroyGroup(g)
    endfunction

    //===========================================================================
    function PauseUnitBJ takes boolean pause, unit whichUnit returns nothing
        call PauseUnit(whichUnit, pause)
    endfunction

    //===========================================================================
    function IsUnitPausedBJ takes unit whichUnit returns boolean
        return IsUnitPaused(whichUnit)
    endfunction

    //===========================================================================
    function UnitPauseTimedLifeBJ takes boolean flag, unit whichUnit returns nothing
        call UnitPauseTimedLife(whichUnit, flag)
    endfunction

    //===========================================================================
    function UnitApplyTimedLifeBJ takes real duration, integer buffId, unit whichUnit returns nothing
        call UnitApplyTimedLife(whichUnit, buffId, duration)
    endfunction

    //===========================================================================
    function UnitShareVisionBJ takes boolean share, unit whichUnit, player whichPlayer returns nothing
        call UnitShareVision(whichUnit, whichPlayer, share)
    endfunction

    //===========================================================================
    function UnitRemoveBuffsBJ takes integer buffType, unit whichUnit returns nothing
        if (buffType == bj_REMOVEBUFFS_POSITIVE) then
            call UnitRemoveBuffs(whichUnit, true, false)
        elseif (buffType == bj_REMOVEBUFFS_NEGATIVE) then
            call UnitRemoveBuffs(whichUnit, false, true)
        elseif (buffType == bj_REMOVEBUFFS_ALL) then
            call UnitRemoveBuffs(whichUnit, true, true)
        elseif (buffType == bj_REMOVEBUFFS_NONTLIFE) then
            call UnitRemoveBuffsEx(whichUnit, true, true, false, false, false, true, false)
        else
            // Unrecognized dispel type - ignore the request.
        endif
    endfunction

    //===========================================================================
    function UnitRemoveBuffsExBJ takes integer polarity, integer resist, unit whichUnit, boolean bTLife, boolean bAura returns nothing
        local boolean bPos   = (polarity == bj_BUFF_POLARITY_EITHER) or (polarity == bj_BUFF_POLARITY_POSITIVE)
        local boolean bNeg   = (polarity == bj_BUFF_POLARITY_EITHER) or (polarity == bj_BUFF_POLARITY_NEGATIVE)
        local boolean bMagic = (resist == bj_BUFF_RESIST_BOTH) or (resist == bj_BUFF_RESIST_MAGIC)
        local boolean bPhys  = (resist == bj_BUFF_RESIST_BOTH) or (resist == bj_BUFF_RESIST_PHYSICAL)

        call UnitRemoveBuffsEx(whichUnit, bPos, bNeg, bMagic, bPhys, bTLife, bAura, false)
    endfunction

    //===========================================================================
    function UnitCountBuffsExBJ takes integer polarity, integer resist, unit whichUnit, boolean bTLife, boolean bAura returns integer
        local boolean bPos   = (polarity == bj_BUFF_POLARITY_EITHER) or (polarity == bj_BUFF_POLARITY_POSITIVE)
        local boolean bNeg   = (polarity == bj_BUFF_POLARITY_EITHER) or (polarity == bj_BUFF_POLARITY_NEGATIVE)
        local boolean bMagic = (resist == bj_BUFF_RESIST_BOTH) or (resist == bj_BUFF_RESIST_MAGIC)
        local boolean bPhys  = (resist == bj_BUFF_RESIST_BOTH) or (resist == bj_BUFF_RESIST_PHYSICAL)

        return UnitCountBuffsEx(whichUnit, bPos, bNeg, bMagic, bPhys, bTLife, bAura, false)
    endfunction

    //===========================================================================
    function UnitRemoveAbilityBJ takes integer abilityId, unit whichUnit returns boolean
        return UnitRemoveAbility(whichUnit, abilityId)
    endfunction

    //===========================================================================
    function UnitAddAbilityBJ takes integer abilityId, unit whichUnit returns boolean
        return UnitAddAbility(whichUnit, abilityId)
    endfunction

    //===========================================================================
    function UnitRemoveTypeBJ takes unittype whichType, unit whichUnit returns boolean
        return UnitRemoveType(whichUnit, whichType)
    endfunction

    //===========================================================================
    function UnitAddTypeBJ takes unittype whichType, unit whichUnit returns boolean
        return UnitAddType(whichUnit, whichType)
    endfunction

    //===========================================================================
    function UnitMakeAbilityPermanentBJ takes boolean permanent, integer abilityId, unit whichUnit returns boolean
        return UnitMakeAbilityPermanent(whichUnit, permanent, abilityId)
    endfunction

    //===========================================================================
    function SetUnitExplodedBJ takes unit whichUnit, boolean exploded returns nothing
        call SetUnitExploded(whichUnit, exploded)
    endfunction

    //===========================================================================
    function ExplodeUnitBJ takes unit whichUnit returns nothing
        call SetUnitExploded(whichUnit, true)
        call KillUnit(whichUnit)
    endfunction

    //===========================================================================
    function GetTransportUnitBJ takes nothing returns unit
        return GetTransportUnit()
    endfunction

    //===========================================================================
    function GetLoadedUnitBJ takes nothing returns unit
        return GetLoadedUnit()
    endfunction

    //===========================================================================
    function IsUnitInTransportBJ takes unit whichUnit, unit whichTransport returns boolean
        return IsUnitInTransport(whichUnit, whichTransport)
    endfunction

    //===========================================================================
    function IsUnitLoadedBJ takes unit whichUnit returns boolean
        return IsUnitLoaded(whichUnit)
    endfunction

    //===========================================================================
    function IsUnitIllusionBJ takes unit whichUnit returns boolean
        return IsUnitIllusion(whichUnit)
    endfunction

    //===========================================================================
    // This attempts to replace a unit with a new unit type by creating a new
    // unit of the desired type using the old unit's location, facing, etc.
    //
    function ReplaceUnitBJ takes unit whichUnit, integer newUnitId, integer unitStateMethod returns unit
        local unit    oldUnit = whichUnit
        local unit    newUnit
        local boolean wasHidden
        local integer index
        local item    indexItem
        local real    oldRatio

        // If we have bogus data, don't attempt the replace.
        if (oldUnit == null) then
            set bj_lastReplacedUnit = oldUnit
            return oldUnit
        endif

        // Hide the original unit.
        set wasHidden = IsUnitHidden(oldUnit)
        call ShowUnit(oldUnit, false)

        // Create the replacement unit.
        if (newUnitId == 'ugol') then
            set newUnit = CreateBlightedGoldmine(GetOwningPlayer(oldUnit), GetUnitX(oldUnit), GetUnitY(oldUnit), GetUnitFacing(oldUnit))
        else
            set newUnit = CreateUnit(GetOwningPlayer(oldUnit), newUnitId, GetUnitX(oldUnit), GetUnitY(oldUnit), GetUnitFacing(oldUnit))
        endif

        // Set the unit's life and mana according to the requested method.
        if (unitStateMethod == bj_UNIT_STATE_METHOD_RELATIVE) then
            // Set the replacement's current/max life ratio to that of the old unit.
            // If both units have mana, do the same for mana.
            if (GetUnitState(oldUnit, UNIT_STATE_MAX_LIFE) > 0) then
                set oldRatio = GetUnitState(oldUnit, UNIT_STATE_LIFE) / GetUnitState(oldUnit, UNIT_STATE_MAX_LIFE)
                call SetUnitState(newUnit, UNIT_STATE_LIFE, oldRatio * GetUnitState(newUnit, UNIT_STATE_MAX_LIFE))
            endif

            if (GetUnitState(oldUnit, UNIT_STATE_MAX_MANA) > 0) and (GetUnitState(newUnit, UNIT_STATE_MAX_MANA) > 0) then
                set oldRatio = GetUnitState(oldUnit, UNIT_STATE_MANA) / GetUnitState(oldUnit, UNIT_STATE_MAX_MANA)
                call SetUnitState(newUnit, UNIT_STATE_MANA, oldRatio * GetUnitState(newUnit, UNIT_STATE_MAX_MANA))
            endif
        elseif (unitStateMethod == bj_UNIT_STATE_METHOD_ABSOLUTE) then
            // Set the replacement's current life to that of the old unit.
            // If the new unit has mana, do the same for mana.
            call SetUnitState(newUnit, UNIT_STATE_LIFE, GetUnitState(oldUnit, UNIT_STATE_LIFE))
            if (GetUnitState(newUnit, UNIT_STATE_MAX_MANA) > 0) then
                call SetUnitState(newUnit, UNIT_STATE_MANA, GetUnitState(oldUnit, UNIT_STATE_MANA))
            endif
        elseif (unitStateMethod == bj_UNIT_STATE_METHOD_DEFAULTS) then
            // The newly created unit should already have default life and mana.
        elseif (unitStateMethod == bj_UNIT_STATE_METHOD_MAXIMUM) then
            // Use max life and mana.
            call SetUnitState(newUnit, UNIT_STATE_LIFE, GetUnitState(newUnit, UNIT_STATE_MAX_LIFE))
            call SetUnitState(newUnit, UNIT_STATE_MANA, GetUnitState(newUnit, UNIT_STATE_MAX_MANA))
        else
            // Unrecognized unit state method - ignore the request.
        endif

        // Mirror properties of the old unit onto the new unit.
        //call PauseUnit(newUnit, IsUnitPaused(oldUnit))
        call SetResourceAmount(newUnit, GetResourceAmount(oldUnit))

        // If both the old and new units are heroes, handle their hero info.
        if (IsUnitType(oldUnit, UNIT_TYPE_HERO) and IsUnitType(newUnit, UNIT_TYPE_HERO)) then
            call SetHeroXP(newUnit, GetHeroXP(oldUnit), false)

            set index = 0
            loop
                set indexItem = UnitItemInSlot(oldUnit, index)
                if (indexItem != null) then
                    call UnitRemoveItem(oldUnit, indexItem)
                    call UnitAddItem(newUnit, indexItem)
                endif

                set index = index + 1
                exitwhen index >= bj_MAX_INVENTORY
            endloop
        endif

        // Remove or kill the original unit.  It is sometimes unsafe to remove
        // hidden units, so kill the original unit if it was previously hidden.
        if wasHidden then
            call KillUnit(oldUnit)
            call RemoveUnit(oldUnit)
        else
            call RemoveUnit(oldUnit)
        endif

        set bj_lastReplacedUnit = newUnit
        return newUnit
    endfunction

    //===========================================================================
    function GetLastReplacedUnitBJ takes nothing returns unit
        return bj_lastReplacedUnit
    endfunction

    //===========================================================================
    function SetUnitPositionLocFacingBJ takes unit whichUnit, location loc, real facing returns nothing
        call SetUnitPositionLoc(whichUnit, loc)
        call SetUnitFacing(whichUnit, facing)
    endfunction

    //===========================================================================
    function SetUnitPositionLocFacingLocBJ takes unit whichUnit, location loc, location lookAt returns nothing
        call SetUnitPositionLoc(whichUnit, loc)
        call SetUnitFacing(whichUnit, AngleBetweenPoints(loc, lookAt))
    endfunction

    //===========================================================================
    function AddItemToStockBJ takes integer itemId, unit whichUnit, integer currentStock, integer stockMax returns nothing
        call AddItemToStock(whichUnit, itemId, currentStock, stockMax)
    endfunction

    //===========================================================================
    function AddUnitToStockBJ takes integer unitId, unit whichUnit, integer currentStock, integer stockMax returns nothing
        call AddUnitToStock(whichUnit, unitId, currentStock, stockMax)
    endfunction

    //===========================================================================
    function RemoveItemFromStockBJ takes integer itemId, unit whichUnit returns nothing
        call RemoveItemFromStock(whichUnit, itemId)
    endfunction

    //===========================================================================
    function RemoveUnitFromStockBJ takes integer unitId, unit whichUnit returns nothing
        call RemoveUnitFromStock(whichUnit, unitId)
    endfunction

    //===========================================================================
    function SetUnitUseFoodBJ takes boolean enable, unit whichUnit returns nothing
        call SetUnitUseFood(whichUnit, enable)
    endfunction

    //===========================================================================
    function UnitDamagePointLoc takes unit whichUnit, real delay, real radius, location loc, real amount, attacktype whichAttack, damagetype whichDamage returns boolean
        return UnitDamagePoint(whichUnit, delay, radius, GetLocationX(loc), GetLocationY(loc), amount, true, false, whichAttack, whichDamage, WEAPON_TYPE_WHOKNOWS)
    endfunction

    //===========================================================================
    function UnitDamageTargetBJ takes unit whichUnit, unit target, real amount, attacktype whichAttack, damagetype whichDamage returns boolean
        return UnitDamageTarget(whichUnit, target, amount, true, false, whichAttack, whichDamage, WEAPON_TYPE_WHOKNOWS)
    endfunction



    //***************************************************************************
    //*
    //*  Destructable Utility Functions
    //*
    //***************************************************************************

    //===========================================================================
    function CreateDestructableLoc takes integer objectid, location loc, real facing, real scale, integer variation returns destructable
        set bj_lastCreatedDestructable = CreateDestructable(objectid, GetLocationX(loc), GetLocationY(loc), facing, scale, variation)
        return bj_lastCreatedDestructable
    endfunction

    //===========================================================================
    function CreateDeadDestructableLocBJ takes integer objectid, location loc, real facing, real scale, integer variation returns destructable
        set bj_lastCreatedDestructable = CreateDeadDestructable(objectid, GetLocationX(loc), GetLocationY(loc), facing, scale, variation)
        return bj_lastCreatedDestructable
    endfunction

    //===========================================================================
    function GetLastCreatedDestructable takes nothing returns destructable
        return bj_lastCreatedDestructable
    endfunction

    //===========================================================================
    function ShowDestructableBJ takes boolean flag, destructable d returns nothing
        call ShowDestructable(d, flag)
    endfunction

    //===========================================================================
    function SetDestructableInvulnerableBJ takes destructable d, boolean flag returns nothing
        call SetDestructableInvulnerable(d, flag)
    endfunction

    //===========================================================================
    function IsDestructableInvulnerableBJ takes destructable d returns boolean
        return IsDestructableInvulnerable(d)
    endfunction

    //===========================================================================
    function GetDestructableLoc takes destructable whichDestructable returns location
        return Location(GetDestructableX(whichDestructable), GetDestructableY(whichDestructable))
    endfunction

    //===========================================================================
    function EnumDestructablesInRectAll takes rect r, code actionFunc returns nothing
        call EnumDestructablesInRect(r, null, actionFunc)
    endfunction

    //===========================================================================
    function EnumDestructablesInCircleBJFilter takes nothing returns boolean
        local location destLoc = GetDestructableLoc(GetFilterDestructable())
        local boolean result

        set result = DistanceBetweenPoints(destLoc, bj_enumDestructableCenter) <= bj_enumDestructableRadius
        call RemoveLocation(destLoc)
        return result
    endfunction

    //===========================================================================
    function IsDestructableDeadBJ takes destructable d returns boolean
        return GetDestructableLife(d) <= 0
    endfunction

    //===========================================================================
    function IsDestructableAliveBJ takes destructable d returns boolean
        return not IsDestructableDeadBJ(d)
    endfunction

    //===========================================================================
    // See GroupPickRandomUnitEnum for the details of this algorithm.
    //
    function RandomDestructableInRectBJEnum takes nothing returns nothing
        set bj_destRandomConsidered = bj_destRandomConsidered + 1
        if (GetRandomInt(1,bj_destRandomConsidered) == 1) then
            set bj_destRandomCurrentPick = GetEnumDestructable()
        endif
    endfunction

    //===========================================================================
    // Picks a random destructable from within a rect, matching a condition
    //
    function RandomDestructableInRectBJ takes rect r, boolexpr filter returns destructable
        set bj_destRandomConsidered = 0
        set bj_destRandomCurrentPick = null
        call EnumDestructablesInRect(r, filter, function RandomDestructableInRectBJEnum)
        call DestroyBoolExpr(filter)
        return bj_destRandomCurrentPick
    endfunction

    //===========================================================================
    // Picks a random destructable from within a rect
    //
    function RandomDestructableInRectSimpleBJ takes rect r returns destructable
        return RandomDestructableInRectBJ(r, null)
    endfunction

    //===========================================================================
    // Enumerates within a rect, with a filter to narrow the enumeration down
    // objects within a circular area.
    //
    function EnumDestructablesInCircleBJ takes real radius, location loc, code actionFunc returns nothing
        local rect r

        if (radius >= 0) then
            set bj_enumDestructableCenter = loc
            set bj_enumDestructableRadius = radius
            set r = GetRectFromCircleBJ(loc, radius)
            call EnumDestructablesInRect(r, filterEnumDestructablesInCircleBJ, actionFunc)
            call RemoveRect(r)
        endif
    endfunction

    //===========================================================================
    function SetDestructableLifePercentBJ takes destructable d, real percent returns nothing
        call SetDestructableLife(d, GetDestructableMaxLife(d) * percent * 0.01)
    endfunction

    //===========================================================================
    function SetDestructableMaxLifeBJ takes destructable d, real max returns nothing
        call SetDestructableMaxLife(d, max)
    endfunction

    //===========================================================================
    function ModifyGateBJ takes integer gateOperation, destructable d returns nothing
        if (gateOperation == bj_GATEOPERATION_CLOSE) then
            if (GetDestructableLife(d) <= 0) then
                call DestructableRestoreLife(d, GetDestructableMaxLife(d), true)
            endif
            call SetDestructableAnimation(d, "stand")
        elseif (gateOperation == bj_GATEOPERATION_OPEN) then
            if (GetDestructableLife(d) > 0) then
                call KillDestructable(d)
            endif
            call SetDestructableAnimation(d, "death alternate")
        elseif (gateOperation == bj_GATEOPERATION_DESTROY) then
            if (GetDestructableLife(d) > 0) then
                call KillDestructable(d)
            endif
            call SetDestructableAnimation(d, "death")
        else
            // Unrecognized gate state - ignore the request.
        endif
    endfunction

    //===========================================================================
    // Determine the elevator's height from its occlusion height.
    //
    function GetElevatorHeight takes destructable d returns integer
        local integer height

        set height = 1 + R2I(GetDestructableOccluderHeight(d) / bj_CLIFFHEIGHT)
        if (height < 1) or (height > 3) then
            set height = 1
        endif
        return height
    endfunction

    //===========================================================================
    // To properly animate an elevator, we must know not only what height we
    // want to change to, but also what height we are currently at.  This code
    // determines the elevator's current height from its occlusion height.
    // Arbitrarily changing an elevator's occlusion height is thus inadvisable.
    //
    function ChangeElevatorHeight takes destructable d, integer newHeight returns nothing
        local integer oldHeight

        // Cap the new height within the supported range.
        set newHeight = IMaxBJ(1, newHeight)
        set newHeight = IMinBJ(3, newHeight)

        // Find out what height the elevator is already at.
        set oldHeight = GetElevatorHeight(d)

        // Set the elevator's occlusion height.
        call SetDestructableOccluderHeight(d, bj_CLIFFHEIGHT*(newHeight-1))

        if (newHeight == 1) then
            if (oldHeight == 2) then
                call SetDestructableAnimation(d, "birth")
                call QueueDestructableAnimation(d, "stand")
            elseif (oldHeight == 3) then
                call SetDestructableAnimation(d, "birth third")
                call QueueDestructableAnimation(d, "stand")
            else
                // Unrecognized old height - snap to new height.
                call SetDestructableAnimation(d, "stand")
            endif
        elseif (newHeight == 2) then
            if (oldHeight == 1) then
                call SetDestructableAnimation(d, "death")
                call QueueDestructableAnimation(d, "stand second")
            elseif (oldHeight == 3) then
                call SetDestructableAnimation(d, "birth second")
                call QueueDestructableAnimation(d, "stand second")
            else
                // Unrecognized old height - snap to new height.
                call SetDestructableAnimation(d, "stand second")
            endif
        elseif (newHeight == 3) then
            if (oldHeight == 1) then
                call SetDestructableAnimation(d, "death third")
                call QueueDestructableAnimation(d, "stand third")
            elseif (oldHeight == 2) then
                call SetDestructableAnimation(d, "death second")
                call QueueDestructableAnimation(d, "stand third")
            else
                // Unrecognized old height - snap to new height.
                call SetDestructableAnimation(d, "stand third")
            endif
        else
            // Unrecognized new height - ignore the request.
        endif
    endfunction

    //===========================================================================
    // Grab the unit and throw his own coords in his face, forcing him to push
    // and shove until he finds a spot where noone will bother him.
    //
    function NudgeUnitsInRectEnum takes nothing returns nothing
        local unit nudgee = GetEnumUnit()

        call SetUnitPosition(nudgee, GetUnitX(nudgee), GetUnitY(nudgee))
    endfunction

    //===========================================================================
    function NudgeItemsInRectEnum takes nothing returns nothing
        local item nudgee = GetEnumItem()

        call SetItemPosition(nudgee, GetItemX(nudgee), GetItemY(nudgee))
    endfunction

    //===========================================================================
    // Nudge the items and units within a given rect ever so gently, so as to
    // encourage them to find locations where they can peacefully coexist with
    // pathing restrictions and live happy, fruitful lives.
    //
    function NudgeObjectsInRect takes rect nudgeArea returns nothing
        local group        g

        set g = CreateGroup()
        call GroupEnumUnitsInRect(g, nudgeArea, null)
        call ForGroup(g, function NudgeUnitsInRectEnum)
        call DestroyGroup(g)

        call EnumItemsInRect(nudgeArea, null, function NudgeItemsInRectEnum)
    endfunction

    //===========================================================================
    function NearbyElevatorExistsEnum takes nothing returns nothing
        local destructable d     = GetEnumDestructable()
        local integer      dType = GetDestructableTypeId(d)

        if (dType == bj_ELEVATOR_CODE01) or (dType == bj_ELEVATOR_CODE02) then
            set bj_elevatorNeighbor = d
        endif
    endfunction

    //===========================================================================
    function NearbyElevatorExists takes real x, real y returns boolean
        local real findThreshold = 32
        local rect r

        // If another elevator is overlapping this one, ignore the wall.
        set r = Rect(x - findThreshold, y - findThreshold, x + findThreshold, y + findThreshold)
        set bj_elevatorNeighbor = null
        call EnumDestructablesInRect(r, null, function NearbyElevatorExistsEnum)
        call RemoveRect(r)

        return bj_elevatorNeighbor != null
    endfunction

    //===========================================================================
    function FindElevatorWallBlockerEnum takes nothing returns nothing
        set bj_elevatorWallBlocker = GetEnumDestructable()
    endfunction

    //===========================================================================
    // This toggles pathing on or off for one wall of an elevator by killing
    // or reviving a pathing blocker at the appropriate location (and creating
    // the pathing blocker in the first place, if it does not yet exist).
    //
    function ChangeElevatorWallBlocker takes real x, real y, real facing, boolean open returns nothing
        local destructable blocker = null
        local real         findThreshold = 32
        local real         nudgeLength   = 4.25 * bj_CELLWIDTH
        local real         nudgeWidth    = 1.25 * bj_CELLWIDTH
        local rect         r

        // Search for the pathing blocker within the general area.
        set r = Rect(x - findThreshold, y - findThreshold, x + findThreshold, y + findThreshold)
        set bj_elevatorWallBlocker = null
        call EnumDestructablesInRect(r, null, function FindElevatorWallBlockerEnum)
        call RemoveRect(r)
        set blocker = bj_elevatorWallBlocker

        // Ensure that the blocker exists.
        if (blocker == null) then
            set blocker = CreateDeadDestructable(bj_ELEVATOR_BLOCKER_CODE, x, y, facing, 1, 0)
        elseif (GetDestructableTypeId(blocker) != bj_ELEVATOR_BLOCKER_CODE) then
            // If a different destructible exists in the blocker's spot, ignore
            // the request.  (Two destructibles cannot occupy the same location
            // on the map, so we cannot create an elevator blocker here.)
            return
        endif

        if (open) then
            // Ensure that the blocker is dead.
            if (GetDestructableLife(blocker) > 0) then
                call KillDestructable(blocker)
            endif
        else
            // Ensure that the blocker is alive.
            if (GetDestructableLife(blocker) <= 0) then
                call DestructableRestoreLife(blocker, GetDestructableMaxLife(blocker), false)
            endif

            // Nudge any objects standing in the blocker's way.
            if (facing == 0) then
                set r = Rect(x - nudgeWidth/2, y - nudgeLength/2, x + nudgeWidth/2, y + nudgeLength/2)
                call NudgeObjectsInRect(r)
                call RemoveRect(r)
            elseif (facing == 90) then
                set r = Rect(x - nudgeLength/2, y - nudgeWidth/2, x + nudgeLength/2, y + nudgeWidth/2)
                call NudgeObjectsInRect(r)
                call RemoveRect(r)
            else
                // Unrecognized blocker angle - don't nudge anything.
            endif
        endif
    endfunction

    //===========================================================================
    function ChangeElevatorWalls takes boolean open, integer walls, destructable d returns nothing
        local real x = GetDestructableX(d)
        local real y = GetDestructableY(d)
        local real distToBlocker = 192
        local real distToNeighbor = 256

        if (walls == bj_ELEVATOR_WALL_TYPE_ALL) or (walls == bj_ELEVATOR_WALL_TYPE_EAST) then
            if (not NearbyElevatorExists(x + distToNeighbor, y)) then
                call ChangeElevatorWallBlocker(x + distToBlocker, y, 0, open)
            endif
        endif

        if (walls == bj_ELEVATOR_WALL_TYPE_ALL) or (walls == bj_ELEVATOR_WALL_TYPE_NORTH) then
            if (not NearbyElevatorExists(x, y + distToNeighbor)) then
                call ChangeElevatorWallBlocker(x, y + distToBlocker, 90, open)
            endif
        endif

        if (walls == bj_ELEVATOR_WALL_TYPE_ALL) or (walls == bj_ELEVATOR_WALL_TYPE_SOUTH) then
            if (not NearbyElevatorExists(x, y - distToNeighbor)) then
                call ChangeElevatorWallBlocker(x, y - distToBlocker, 90, open)
            endif
        endif

        if (walls == bj_ELEVATOR_WALL_TYPE_ALL) or (walls == bj_ELEVATOR_WALL_TYPE_WEST) then
            if (not NearbyElevatorExists(x - distToNeighbor, y)) then
                call ChangeElevatorWallBlocker(x - distToBlocker, y, 0, open)
            endif
        endif
    endfunction



    //***************************************************************************
    //*
    //*  Neutral Building Utility Functions
    //*
    //***************************************************************************

    //===========================================================================
    function WaygateActivateBJ takes boolean activate, unit waygate returns nothing
        call WaygateActivate(waygate, activate)
    endfunction

    //===========================================================================
    function WaygateIsActiveBJ takes unit waygate returns boolean
        return WaygateIsActive(waygate)
    endfunction

    //===========================================================================
    function WaygateSetDestinationLocBJ takes unit waygate, location loc returns nothing
        call WaygateSetDestination(waygate, GetLocationX(loc), GetLocationY(loc))
    endfunction

    //===========================================================================
    function WaygateGetDestinationLocBJ takes unit waygate returns location
        return Location(WaygateGetDestinationX(waygate), WaygateGetDestinationY(waygate))
    endfunction

    //===========================================================================
    function UnitSetUsesAltIconBJ takes boolean flag, unit whichUnit returns nothing
        call UnitSetUsesAltIcon(whichUnit, flag)
    endfunction



    //***************************************************************************
    //*
    //*  UI Utility Functions
    //*
    //***************************************************************************

    //===========================================================================
    function ForceUIKeyBJ takes player whichPlayer, string key returns nothing
        if (GetLocalPlayer() == whichPlayer) then
            // Use only local code (no net traffic) within this block to avoid desyncs.
            call ForceUIKey(key)
        endif
    endfunction

    //===========================================================================
    function ForceUICancelBJ takes player whichPlayer returns nothing
        if (GetLocalPlayer() == whichPlayer) then
            // Use only local code (no net traffic) within this block to avoid desyncs.
            call ForceUICancel()
        endif
    endfunction



    //***************************************************************************
    //*
    //*  Group and Force Utility Functions
    //*
    //***************************************************************************

    //===========================================================================
    function ForGroupBJ takes group whichGroup, code callback returns nothing
        // If the user wants the group destroyed, remember that fact and clear
        // the flag, in case it is used again in the callback.
        local boolean wantDestroy = bj_wantDestroyGroup
        set bj_wantDestroyGroup = false

        call ForGroup(whichGroup, callback)

        // If the user wants the group destroyed, do so now.
        if (wantDestroy) then
            call DestroyGroup(whichGroup)
        endif
    endfunction

    //===========================================================================
    function GroupAddUnitSimple takes unit whichUnit, group whichGroup returns nothing
        call GroupAddUnit(whichGroup, whichUnit)
    endfunction

    //===========================================================================
    function GroupRemoveUnitSimple takes unit whichUnit, group whichGroup returns nothing
        call GroupRemoveUnit(whichGroup, whichUnit)
    endfunction

    //===========================================================================
    function GroupAddGroupEnum takes nothing returns nothing
        call GroupAddUnit(bj_groupAddGroupDest, GetEnumUnit())
    endfunction

    //===========================================================================
    function GroupAddGroup takes group sourceGroup, group destGroup returns nothing
        // If the user wants the group destroyed, remember that fact and clear
        // the flag, in case it is used again in the callback.
        local boolean wantDestroy = bj_wantDestroyGroup
        set bj_wantDestroyGroup = false

        set bj_groupAddGroupDest = destGroup
        call ForGroup(sourceGroup, function GroupAddGroupEnum)

        // If the user wants the group destroyed, do so now.
        if (wantDestroy) then
            call DestroyGroup(sourceGroup)
        endif
    endfunction

    //===========================================================================
    function GroupRemoveGroupEnum takes nothing returns nothing
        call GroupRemoveUnit(bj_groupRemoveGroupDest, GetEnumUnit())
    endfunction

    //===========================================================================
    function GroupRemoveGroup takes group sourceGroup, group destGroup returns nothing
        // If the user wants the group destroyed, remember that fact and clear
        // the flag, in case it is used again in the callback.
        local boolean wantDestroy = bj_wantDestroyGroup
        set bj_wantDestroyGroup = false

        set bj_groupRemoveGroupDest = destGroup
        call ForGroup(sourceGroup, function GroupRemoveGroupEnum)

        // If the user wants the group destroyed, do so now.
        if (wantDestroy) then
            call DestroyGroup(sourceGroup)
        endif
    endfunction

    //===========================================================================
    function ForceAddPlayerSimple takes player whichPlayer, force whichForce returns nothing
        call ForceAddPlayer(whichForce, whichPlayer)
    endfunction

    //===========================================================================
    function ForceRemovePlayerSimple takes player whichPlayer, force whichForce returns nothing
        call ForceRemovePlayer(whichForce, whichPlayer)
    endfunction

    //===========================================================================
    // Consider each unit, one at a time, keeping a "current pick".   Once all units
    // are considered, this "current pick" will be the resulting random unit.
    //
    // The chance of picking a given unit over the "current pick" is 1/N, where N is
    // the number of units considered thusfar (including the current consideration).
    //
    function GroupPickRandomUnitEnum takes nothing returns nothing
        set bj_groupRandomConsidered = bj_groupRandomConsidered + 1
        if (GetRandomInt(1,bj_groupRandomConsidered) == 1) then
            set bj_groupRandomCurrentPick = GetEnumUnit()
        endif
    endfunction

    //===========================================================================
    // Picks a random unit from a group.
    //
    function GroupPickRandomUnit takes group whichGroup returns unit
        // If the user wants the group destroyed, remember that fact and clear
        // the flag, in case it is used again in the callback.
        local boolean wantDestroy = bj_wantDestroyGroup
        set bj_wantDestroyGroup = false

        set bj_groupRandomConsidered = 0
        set bj_groupRandomCurrentPick = null
        call ForGroup(whichGroup, function GroupPickRandomUnitEnum)

        // If the user wants the group destroyed, do so now.
        if (wantDestroy) then
            call DestroyGroup(whichGroup)
        endif
        return bj_groupRandomCurrentPick
    endfunction

    //===========================================================================
    // See GroupPickRandomUnitEnum for the details of this algorithm.
    //
    function ForcePickRandomPlayerEnum takes nothing returns nothing
        set bj_forceRandomConsidered = bj_forceRandomConsidered + 1
        if (GetRandomInt(1,bj_forceRandomConsidered) == 1) then
            set bj_forceRandomCurrentPick = GetEnumPlayer()
        endif
    endfunction

    //===========================================================================
    // Picks a random player from a force.
    //
    function ForcePickRandomPlayer takes force whichForce returns player
        set bj_forceRandomConsidered = 0
        set bj_forceRandomCurrentPick = null
        call ForForce(whichForce, function ForcePickRandomPlayerEnum)
        return bj_forceRandomCurrentPick
    endfunction

    //===========================================================================
    function EnumUnitsSelected takes player whichPlayer, boolexpr enumFilter, code enumAction returns nothing
        local group g = CreateGroup()
        call SyncSelections()
        call GroupEnumUnitsSelected(g, whichPlayer, enumFilter)
        call DestroyBoolExpr(enumFilter)
        call ForGroup(g, enumAction)
        call DestroyGroup(g)
    endfunction

    //===========================================================================
    function GetUnitsInRectMatching takes rect r, boolexpr filter returns group
        local group g = CreateGroup()
        call GroupEnumUnitsInRect(g, r, filter)
        call DestroyBoolExpr(filter)
        return g
    endfunction

    //===========================================================================
    function GetUnitsInRectAll takes rect r returns group
        return GetUnitsInRectMatching(r, null)
    endfunction

    //===========================================================================
    function GetUnitsInRectOfPlayerFilter takes nothing returns boolean
        return GetOwningPlayer(GetFilterUnit()) == bj_groupEnumOwningPlayer
    endfunction

    //===========================================================================
    function GetUnitsInRectOfPlayer takes rect r, player whichPlayer returns group
        local group g = CreateGroup()
        set bj_groupEnumOwningPlayer = whichPlayer
        call GroupEnumUnitsInRect(g, r, filterGetUnitsInRectOfPlayer)
        return g
    endfunction

    //===========================================================================
    function GetUnitsInRangeOfLocMatching takes real radius, location whichLocation, boolexpr filter returns group
        local group g = CreateGroup()
        call GroupEnumUnitsInRangeOfLoc(g, whichLocation, radius, filter)
        call DestroyBoolExpr(filter)
        return g
    endfunction

    //===========================================================================
    function GetUnitsInRangeOfLocAll takes real radius, location whichLocation returns group
        return GetUnitsInRangeOfLocMatching(radius, whichLocation, null)
    endfunction

    //===========================================================================
    function GetUnitsOfTypeIdAllFilter takes nothing returns boolean
        return GetUnitTypeId(GetFilterUnit()) == bj_groupEnumTypeId
    endfunction

    //===========================================================================
    function GetUnitsOfTypeIdAll takes integer unitid returns group
        local group   result = CreateGroup()
        local group   g      = CreateGroup()
        local integer index

        set index = 0
        loop
            set bj_groupEnumTypeId = unitid
            call GroupClear(g)
            call GroupEnumUnitsOfPlayer(g, Player(index), filterGetUnitsOfTypeIdAll)
            call GroupAddGroup(g, result)

            set index = index + 1
            exitwhen index == bj_MAX_PLAYER_SLOTS
        endloop
        call DestroyGroup(g)

        return result
    endfunction

    //===========================================================================
    function GetUnitsOfPlayerMatching takes player whichPlayer, boolexpr filter returns group
        local group g = CreateGroup()
        call GroupEnumUnitsOfPlayer(g, whichPlayer, filter)
        call DestroyBoolExpr(filter)
        return g
    endfunction

    //===========================================================================
    function GetUnitsOfPlayerAll takes player whichPlayer returns group
        return GetUnitsOfPlayerMatching(whichPlayer, null)
    endfunction

    //===========================================================================
    function GetUnitsOfPlayerAndTypeIdFilter takes nothing returns boolean
        return GetUnitTypeId(GetFilterUnit()) == bj_groupEnumTypeId
    endfunction

    //===========================================================================
    function GetUnitsOfPlayerAndTypeId takes player whichPlayer, integer unitid returns group
        local group g = CreateGroup()
        set bj_groupEnumTypeId = unitid
        call GroupEnumUnitsOfPlayer(g, whichPlayer, filterGetUnitsOfPlayerAndTypeId)
        return g
    endfunction

    //===========================================================================
    function GetUnitsSelectedAll takes player whichPlayer returns group
        local group g = CreateGroup()
        call SyncSelections()
        call GroupEnumUnitsSelected(g, whichPlayer, null)
        return g
    endfunction

    //===========================================================================
    function GetForceOfPlayer takes player whichPlayer returns force
        local force f = CreateForce()
        call ForceAddPlayer(f, whichPlayer)
        return f
    endfunction

    //===========================================================================
    function GetPlayersAll takes nothing returns force
        return bj_FORCE_ALL_PLAYERS
    endfunction

    //===========================================================================
    function GetPlayersByMapControl takes mapcontrol whichControl returns force
        local force f = CreateForce()
        local integer playerIndex
        local player  indexPlayer

        set playerIndex = 0
        loop
            set indexPlayer = Player(playerIndex)
            if GetPlayerController(indexPlayer) == whichControl then
                call ForceAddPlayer(f, indexPlayer)
            endif

            set playerIndex = playerIndex + 1
            exitwhen playerIndex == bj_MAX_PLAYER_SLOTS
        endloop

        return f
    endfunction

    //===========================================================================
    function GetPlayersAllies takes player whichPlayer returns force
        local force f = CreateForce()
        call ForceEnumAllies(f, whichPlayer, null)
        return f
    endfunction

    //===========================================================================
    function GetPlayersEnemies takes player whichPlayer returns force
        local force f = CreateForce()
        call ForceEnumEnemies(f, whichPlayer, null)
        return f
    endfunction

    //===========================================================================
    function GetPlayersMatching takes boolexpr filter returns force
        local force f = CreateForce()
        call ForceEnumPlayers(f, filter)
        call DestroyBoolExpr(filter)
        return f
    endfunction

    //===========================================================================
    function CountUnitsInGroupEnum takes nothing returns nothing
        set bj_groupCountUnits = bj_groupCountUnits + 1
    endfunction

    //===========================================================================
    function CountUnitsInGroup takes group g returns integer
        // If the user wants the group destroyed, remember that fact and clear
        // the flag, in case it is used again in the callback.
        local boolean wantDestroy = bj_wantDestroyGroup
        set bj_wantDestroyGroup = false

        set bj_groupCountUnits = 0
        call ForGroup(g, function CountUnitsInGroupEnum)

        // If the user wants the group destroyed, do so now.
        if (wantDestroy) then
            call DestroyGroup(g)
        endif
        return bj_groupCountUnits
    endfunction

    //===========================================================================
    function CountPlayersInForceEnum takes nothing returns nothing
        set bj_forceCountPlayers = bj_forceCountPlayers + 1
    endfunction

    //===========================================================================
    function CountPlayersInForceBJ takes force f returns integer
        set bj_forceCountPlayers = 0
        call ForForce(f, function CountPlayersInForceEnum)
        return bj_forceCountPlayers
    endfunction

    //===========================================================================
    function GetRandomSubGroupEnum takes nothing returns nothing
        if (bj_randomSubGroupWant > 0) then
            if (bj_randomSubGroupWant >= bj_randomSubGroupTotal) or (GetRandomReal(0,1) < bj_randomSubGroupChance) then
                // We either need every remaining unit, or the unit passed its chance check.
                call GroupAddUnit(bj_randomSubGroupGroup, GetEnumUnit())
                set bj_randomSubGroupWant = bj_randomSubGroupWant - 1
            endif
        endif
        set bj_randomSubGroupTotal = bj_randomSubGroupTotal - 1
    endfunction

    //===========================================================================
    function GetRandomSubGroup takes integer count, group sourceGroup returns group
        local group g = CreateGroup()

        set bj_randomSubGroupGroup = g
        set bj_randomSubGroupWant  = count
        set bj_randomSubGroupTotal = CountUnitsInGroup(sourceGroup)

        if (bj_randomSubGroupWant <= 0 or bj_randomSubGroupTotal <= 0) then
            return g
        endif

        set bj_randomSubGroupChance = I2R(bj_randomSubGroupWant) / I2R(bj_randomSubGroupTotal)
        call ForGroup(sourceGroup, function GetRandomSubGroupEnum)
        return g
    endfunction

    //===========================================================================
    function LivingPlayerUnitsOfTypeIdFilter takes nothing returns boolean
        local unit filterUnit = GetFilterUnit()
        return IsUnitAliveBJ(filterUnit) and GetUnitTypeId(filterUnit) == bj_livingPlayerUnitsTypeId
    endfunction

    //===========================================================================
    function CountLivingPlayerUnitsOfTypeId takes integer unitId, player whichPlayer returns integer
        local group g
        local integer matchedCount

        set g = CreateGroup()
        set bj_livingPlayerUnitsTypeId = unitId
        call GroupEnumUnitsOfPlayer(g, whichPlayer, filterLivingPlayerUnitsOfTypeId)
        set matchedCount = CountUnitsInGroup(g)
        call DestroyGroup(g)

        return matchedCount
    endfunction



    //***************************************************************************
    //*
    //*  Animation Utility Functions
    //*
    //***************************************************************************

    //===========================================================================
    function ResetUnitAnimation takes unit whichUnit returns nothing
        call SetUnitAnimation(whichUnit, "stand")
    endfunction

    //===========================================================================
    function SetUnitTimeScalePercent takes unit whichUnit, real percentScale returns nothing
        call SetUnitTimeScale(whichUnit, percentScale * 0.01)
    endfunction

    //===========================================================================
    function SetUnitScalePercent takes unit whichUnit, real percentScaleX, real percentScaleY, real percentScaleZ returns nothing
        call SetUnitScale(whichUnit, percentScaleX * 0.01, percentScaleY * 0.01, percentScaleZ * 0.01)
    endfunction

    //===========================================================================
    // This version differs from the common.j interface in that the alpha value
    // is reversed so as to be displayed as transparency, and all four parameters
    // are treated as percentages rather than bytes.
    //
    function SetUnitVertexColorBJ takes unit whichUnit, real red, real green, real blue, real transparency returns nothing
        call SetUnitVertexColor(whichUnit, PercentTo255(red), PercentTo255(green), PercentTo255(blue), PercentTo255(100.0-transparency))
    endfunction

    //===========================================================================
    function UnitAddIndicatorBJ takes unit whichUnit, real red, real green, real blue, real transparency returns nothing
        call AddIndicator(whichUnit, PercentTo255(red), PercentTo255(green), PercentTo255(blue), PercentTo255(100.0-transparency))
    endfunction

    //===========================================================================
    function DestructableAddIndicatorBJ takes destructable whichDestructable, real red, real green, real blue, real transparency returns nothing
        call AddIndicator(whichDestructable, PercentTo255(red), PercentTo255(green), PercentTo255(blue), PercentTo255(100.0-transparency))
    endfunction

    //===========================================================================
    function ItemAddIndicatorBJ takes item whichItem, real red, real green, real blue, real transparency returns nothing
        call AddIndicator(whichItem, PercentTo255(red), PercentTo255(green), PercentTo255(blue), PercentTo255(100.0-transparency))
    endfunction

    //===========================================================================
    // Sets a unit's facing to point directly at a location.
    //
    function SetUnitFacingToFaceLocTimed takes unit whichUnit, location target, real duration returns nothing
        local location unitLoc = GetUnitLoc(whichUnit)

        call SetUnitFacingTimed(whichUnit, AngleBetweenPoints(unitLoc, target), duration)
        call RemoveLocation(unitLoc)
    endfunction

    //===========================================================================
    // Sets a unit's facing to point directly at another unit.
    //
    function SetUnitFacingToFaceUnitTimed takes unit whichUnit, unit target, real duration returns nothing
        local location unitLoc = GetUnitLoc(target)

        call SetUnitFacingToFaceLocTimed(whichUnit, unitLoc, duration)
        call RemoveLocation(unitLoc)
    endfunction

    //===========================================================================
    function QueueUnitAnimationBJ takes unit whichUnit, string whichAnimation returns nothing
        call QueueUnitAnimation(whichUnit, whichAnimation)
    endfunction

    //===========================================================================
    function SetDestructableAnimationBJ takes destructable d, string whichAnimation returns nothing
        call SetDestructableAnimation(d, whichAnimation)
    endfunction

    //===========================================================================
    function QueueDestructableAnimationBJ takes destructable d, string whichAnimation returns nothing
        call QueueDestructableAnimation(d, whichAnimation)
    endfunction

    //===========================================================================
    function SetDestAnimationSpeedPercent takes destructable d, real percentScale returns nothing
        call SetDestructableAnimationSpeed(d, percentScale * 0.01)
    endfunction



    //***************************************************************************
    //*
    //*  Dialog Utility Functions
    //*
    //***************************************************************************

    //===========================================================================
    function DialogDisplayBJ takes boolean flag, dialog whichDialog, player whichPlayer returns nothing
        call DialogDisplay(whichPlayer, whichDialog, flag)
    endfunction

    //===========================================================================
    function DialogSetMessageBJ takes dialog whichDialog, string message returns nothing
        call DialogSetMessage(whichDialog, message)
    endfunction

    //===========================================================================
    function DialogAddButtonBJ takes dialog whichDialog, string buttonText returns button
        set bj_lastCreatedButton = DialogAddButton(whichDialog, buttonText,0)
        return bj_lastCreatedButton
    endfunction

    //===========================================================================
    function DialogAddButtonWithHotkeyBJ takes dialog whichDialog, string buttonText, integer hotkey returns button
        set bj_lastCreatedButton = DialogAddButton(whichDialog, buttonText,hotkey)
        return bj_lastCreatedButton
    endfunction

    //===========================================================================
    function DialogClearBJ takes dialog whichDialog returns nothing
        call DialogClear(whichDialog)
    endfunction

    //===========================================================================
    function GetLastCreatedButtonBJ takes nothing returns button
        return bj_lastCreatedButton
    endfunction

    //===========================================================================
    function GetClickedButtonBJ takes nothing returns button
        return GetClickedButton()
    endfunction

    //===========================================================================
    function GetClickedDialogBJ takes nothing returns dialog
        return GetClickedDialog()
    endfunction



    //***************************************************************************
    //*
    //*  Alliance Utility Functions
    //*
    //***************************************************************************

    //===========================================================================
    function SetPlayerAllianceBJ takes player sourcePlayer, alliancetype whichAllianceSetting, boolean value, player otherPlayer returns nothing
        // Prevent players from attempting to ally with themselves.
        if (sourcePlayer == otherPlayer) then
            return
        endif

        call SetPlayerAlliance(sourcePlayer, otherPlayer, whichAllianceSetting, value)
    endfunction

    //===========================================================================
    // Set all flags used by the in-game "Ally" checkbox.
    //
    function SetPlayerAllianceStateAllyBJ takes player sourcePlayer, player otherPlayer, boolean flag returns nothing
        call SetPlayerAlliance(sourcePlayer, otherPlayer, ALLIANCE_PASSIVE,       flag)
        call SetPlayerAlliance(sourcePlayer, otherPlayer, ALLIANCE_HELP_REQUEST,  flag)
        call SetPlayerAlliance(sourcePlayer, otherPlayer, ALLIANCE_HELP_RESPONSE, flag)
        call SetPlayerAlliance(sourcePlayer, otherPlayer, ALLIANCE_SHARED_XP,     flag)
        call SetPlayerAlliance(sourcePlayer, otherPlayer, ALLIANCE_SHARED_SPELLS, flag)
    endfunction

    //===========================================================================
    // Set all flags used by the in-game "Shared Vision" checkbox.
    //
    function SetPlayerAllianceStateVisionBJ takes player sourcePlayer, player otherPlayer, boolean flag returns nothing
        call SetPlayerAlliance(sourcePlayer, otherPlayer, ALLIANCE_SHARED_VISION, flag)
    endfunction

    //===========================================================================
    // Set all flags used by the in-game "Shared Units" checkbox.
    //
    function SetPlayerAllianceStateControlBJ takes player sourcePlayer, player otherPlayer, boolean flag returns nothing
        call SetPlayerAlliance(sourcePlayer, otherPlayer, ALLIANCE_SHARED_CONTROL, flag)
    endfunction

    //===========================================================================
    // Set all flags used by the in-game "Shared Units" checkbox with the Full
    // Shared Unit Control feature enabled.
    //
    function SetPlayerAllianceStateFullControlBJ takes player sourcePlayer, player otherPlayer, boolean flag returns nothing
        call SetPlayerAlliance(sourcePlayer, otherPlayer, ALLIANCE_SHARED_ADVANCED_CONTROL, flag)
    endfunction

    //===========================================================================
    function SetPlayerAllianceStateBJ takes player sourcePlayer, player otherPlayer, integer allianceState returns nothing
        // Prevent players from attempting to ally with themselves.
        if (sourcePlayer == otherPlayer) then
            return
        endif

        if allianceState == bj_ALLIANCE_UNALLIED then
            call SetPlayerAllianceStateAllyBJ(        sourcePlayer, otherPlayer, false )
            call SetPlayerAllianceStateVisionBJ(      sourcePlayer, otherPlayer, false )
            call SetPlayerAllianceStateControlBJ(     sourcePlayer, otherPlayer, false )
            call SetPlayerAllianceStateFullControlBJ( sourcePlayer, otherPlayer, false )
        elseif allianceState == bj_ALLIANCE_UNALLIED_VISION then
            call SetPlayerAllianceStateAllyBJ(        sourcePlayer, otherPlayer, false )
            call SetPlayerAllianceStateVisionBJ(      sourcePlayer, otherPlayer, true  )
            call SetPlayerAllianceStateControlBJ(     sourcePlayer, otherPlayer, false )
            call SetPlayerAllianceStateFullControlBJ( sourcePlayer, otherPlayer, false )
        elseif allianceState == bj_ALLIANCE_ALLIED then
            call SetPlayerAllianceStateAllyBJ(        sourcePlayer, otherPlayer, true  )
            call SetPlayerAllianceStateVisionBJ(      sourcePlayer, otherPlayer, false )
            call SetPlayerAllianceStateControlBJ(     sourcePlayer, otherPlayer, false )
            call SetPlayerAllianceStateFullControlBJ( sourcePlayer, otherPlayer, false )
        elseif allianceState == bj_ALLIANCE_ALLIED_VISION then
            call SetPlayerAllianceStateAllyBJ(        sourcePlayer, otherPlayer, true  )
            call SetPlayerAllianceStateVisionBJ(      sourcePlayer, otherPlayer, true  )
            call SetPlayerAllianceStateControlBJ(     sourcePlayer, otherPlayer, false )
            call SetPlayerAllianceStateFullControlBJ( sourcePlayer, otherPlayer, false )
        elseif allianceState == bj_ALLIANCE_ALLIED_UNITS then
            call SetPlayerAllianceStateAllyBJ(        sourcePlayer, otherPlayer, true  )
            call SetPlayerAllianceStateVisionBJ(      sourcePlayer, otherPlayer, true  )
            call SetPlayerAllianceStateControlBJ(     sourcePlayer, otherPlayer, true  )
            call SetPlayerAllianceStateFullControlBJ( sourcePlayer, otherPlayer, false )
        elseif allianceState == bj_ALLIANCE_ALLIED_ADVUNITS then
            call SetPlayerAllianceStateAllyBJ(        sourcePlayer, otherPlayer, true  )
            call SetPlayerAllianceStateVisionBJ(      sourcePlayer, otherPlayer, true  )
            call SetPlayerAllianceStateControlBJ(     sourcePlayer, otherPlayer, true  )
            call SetPlayerAllianceStateFullControlBJ( sourcePlayer, otherPlayer, true  )
        elseif allianceState == bj_ALLIANCE_NEUTRAL then
            call SetPlayerAllianceStateAllyBJ(        sourcePlayer, otherPlayer, false )
            call SetPlayerAllianceStateVisionBJ(      sourcePlayer, otherPlayer, false )
            call SetPlayerAllianceStateControlBJ(     sourcePlayer, otherPlayer, false )
            call SetPlayerAllianceStateFullControlBJ( sourcePlayer, otherPlayer, false )
            call SetPlayerAlliance( sourcePlayer, otherPlayer, ALLIANCE_PASSIVE, true )
        elseif allianceState == bj_ALLIANCE_NEUTRAL_VISION then
            call SetPlayerAllianceStateAllyBJ(        sourcePlayer, otherPlayer, false )
            call SetPlayerAllianceStateVisionBJ(      sourcePlayer, otherPlayer, true  )
            call SetPlayerAllianceStateControlBJ(     sourcePlayer, otherPlayer, false )
            call SetPlayerAllianceStateFullControlBJ( sourcePlayer, otherPlayer, false )
            call SetPlayerAlliance( sourcePlayer, otherPlayer, ALLIANCE_PASSIVE, true )
        else
            // Unrecognized alliance state - ignore the request.
        endif
    endfunction

    //===========================================================================
    // Set the alliance states for an entire force towards another force.
    //
    function SetForceAllianceStateBJ takes force sourceForce, force targetForce, integer allianceState returns nothing
        local integer sourceIndex
        local integer targetIndex

        set sourceIndex = 0
        loop

            if (sourceForce==bj_FORCE_ALL_PLAYERS or IsPlayerInForce(Player(sourceIndex), sourceForce)) then
                set targetIndex = 0
                loop
                    if (targetForce==bj_FORCE_ALL_PLAYERS or IsPlayerInForce(Player(targetIndex), targetForce)) then
                        call SetPlayerAllianceStateBJ(Player(sourceIndex), Player(targetIndex), allianceState)
                    endif

                    set targetIndex = targetIndex + 1
                    exitwhen targetIndex == bj_MAX_PLAYER_SLOTS
                endloop
            endif

            set sourceIndex = sourceIndex + 1
            exitwhen sourceIndex == bj_MAX_PLAYER_SLOTS
        endloop
    endfunction

    //===========================================================================
    // Test to see if two players are co-allied (allied with each other).
    //
    function PlayersAreCoAllied takes player playerA, player playerB returns boolean
        // Players are considered to be allied with themselves.
        if (playerA == playerB) then
            return true
        endif

        // Co-allies are both allied with each other.
        if GetPlayerAlliance(playerA, playerB, ALLIANCE_PASSIVE) then
            if GetPlayerAlliance(playerB, playerA, ALLIANCE_PASSIVE) then
                return true
            endif
        endif
        return false
    endfunction

    //===========================================================================
    // Force (whichPlayer) AI player to share vision and advanced unit control
    // with all AI players of its allies.
    //
    function ShareEverythingWithTeamAI takes player whichPlayer returns nothing
        local integer playerIndex
        local player  indexPlayer

        set playerIndex = 0
        loop
            set indexPlayer = Player(playerIndex)
            if (PlayersAreCoAllied(whichPlayer, indexPlayer) and whichPlayer != indexPlayer) then
                if (GetPlayerController(indexPlayer) == MAP_CONTROL_COMPUTER) then
                    call SetPlayerAlliance(whichPlayer, indexPlayer, ALLIANCE_SHARED_VISION, true)
                    call SetPlayerAlliance(whichPlayer, indexPlayer, ALLIANCE_SHARED_CONTROL, true)
                    call SetPlayerAlliance(whichPlayer, indexPlayer, ALLIANCE_SHARED_ADVANCED_CONTROL, true)
                endif
            endif

            set playerIndex = playerIndex + 1
            exitwhen playerIndex == bj_MAX_PLAYERS
        endloop
    endfunction

    //===========================================================================
    // Force (whichPlayer) to share vision and advanced unit control with all of his/her allies.
    //
    function ShareEverythingWithTeam takes player whichPlayer returns nothing
        local integer playerIndex
        local player  indexPlayer

        set playerIndex = 0
        loop
            set indexPlayer = Player(playerIndex)
            if (PlayersAreCoAllied(whichPlayer, indexPlayer) and whichPlayer != indexPlayer) then
                call SetPlayerAlliance(whichPlayer, indexPlayer, ALLIANCE_SHARED_VISION, true)
                call SetPlayerAlliance(whichPlayer, indexPlayer, ALLIANCE_SHARED_CONTROL, true)
                call SetPlayerAlliance(indexPlayer, whichPlayer, ALLIANCE_SHARED_CONTROL, true)
                call SetPlayerAlliance(whichPlayer, indexPlayer, ALLIANCE_SHARED_ADVANCED_CONTROL, true)
            endif

            set playerIndex = playerIndex + 1
            exitwhen playerIndex == bj_MAX_PLAYERS
        endloop
    endfunction

    //===========================================================================
    // Creates a 'Neutral Victim' player slot.  This slot is passive towards all
    // other players, but all other players are aggressive towards him/her.
    //
    function ConfigureNeutralVictim takes nothing returns nothing
        local integer index
        local player indexPlayer
        local player neutralVictim = Player(bj_PLAYER_NEUTRAL_VICTIM)

        set index = 0
        loop
            set indexPlayer = Player(index)

            call SetPlayerAlliance(neutralVictim, indexPlayer, ALLIANCE_PASSIVE, true)
            call SetPlayerAlliance(indexPlayer, neutralVictim, ALLIANCE_PASSIVE, false)

            set index = index + 1
            exitwhen index == bj_MAX_PLAYERS
        endloop

        // Neutral Victim and Neutral Aggressive should not fight each other.
        set indexPlayer = Player(PLAYER_NEUTRAL_AGGRESSIVE)
        call SetPlayerAlliance(neutralVictim, indexPlayer, ALLIANCE_PASSIVE, true)
        call SetPlayerAlliance(indexPlayer, neutralVictim, ALLIANCE_PASSIVE, true)

        // Neutral Victim does not give bounties.
        call SetPlayerState(neutralVictim, PLAYER_STATE_GIVES_BOUNTY, 0)
    endfunction

    //===========================================================================
    function MakeUnitsPassiveForPlayerEnum takes nothing returns nothing
        call SetUnitOwner(GetEnumUnit(), Player(bj_PLAYER_NEUTRAL_VICTIM), false)
    endfunction

    //===========================================================================
    // Change ownership for every unit of (whichPlayer)'s team to neutral passive.
    //
    function MakeUnitsPassiveForPlayer takes player whichPlayer returns nothing
        local group   playerUnits = CreateGroup()
        call CachePlayerHeroData(whichPlayer)
        call GroupEnumUnitsOfPlayer(playerUnits, whichPlayer, null)
        call ForGroup(playerUnits, function MakeUnitsPassiveForPlayerEnum)
        call DestroyGroup(playerUnits)
    endfunction

    //===========================================================================
    // Change ownership for every unit of (whichPlayer)'s team to neutral passive.
    //
    function MakeUnitsPassiveForTeam takes player whichPlayer returns nothing
        local integer playerIndex
        local player  indexPlayer

        set playerIndex = 0
        loop
            set indexPlayer = Player(playerIndex)
            if PlayersAreCoAllied(whichPlayer, indexPlayer) then
                call MakeUnitsPassiveForPlayer(indexPlayer)
            endif

            set playerIndex = playerIndex + 1
            exitwhen playerIndex == bj_MAX_PLAYERS
        endloop
    endfunction

    //===========================================================================
    // Determine whether or not victory/defeat is disabled via cheat codes.
    //
    function AllowVictoryDefeat takes playergameresult gameResult returns boolean
        if (gameResult == PLAYER_GAME_RESULT_VICTORY) then
            return not IsNoVictoryCheat()
        endif
        if (gameResult == PLAYER_GAME_RESULT_DEFEAT) then
            return not IsNoDefeatCheat()
        endif
        if (gameResult == PLAYER_GAME_RESULT_NEUTRAL) then
            return (not IsNoVictoryCheat()) and (not IsNoDefeatCheat())
        endif
        return true
    endfunction

    //===========================================================================
    function EndGameBJ takes nothing returns nothing
        call EndGame( true )
    endfunction

    //===========================================================================
    function MeleeVictoryDialogBJ takes player whichPlayer, boolean leftGame returns nothing
        local trigger t = CreateTrigger()
        local dialog  d = DialogCreate()
        local string formatString

        // Display "player was victorious" or "player has left the game" message
        if (leftGame) then
            set formatString = GetLocalizedString( "PLAYER_LEFT_GAME" )
        else
            set formatString = GetLocalizedString( "PLAYER_VICTORIOUS" )
        endif

        call DisplayTimedTextFromPlayer(whichPlayer, 0, 0, 60, formatString)

        call DialogSetMessage( d, GetLocalizedString( "GAMEOVER_VICTORY_MSG" ) )
        call DialogAddButton( d, GetLocalizedString( "GAMEOVER_CONTINUE_GAME" ), GetLocalizedHotkey("GAMEOVER_CONTINUE_GAME") )

        set t = CreateTrigger()
        call TriggerRegisterDialogButtonEvent( t, DialogAddQuitButton( d, true, GetLocalizedString( "GAMEOVER_QUIT_GAME" ), GetLocalizedHotkey("GAMEOVER_QUIT_GAME") ) )

        call DialogDisplay( whichPlayer, d, true )
        call StartSoundForPlayerBJ( whichPlayer, bj_victoryDialogSound )
    endfunction

    //===========================================================================
    function MeleeDefeatDialogBJ takes player whichPlayer, boolean leftGame returns nothing
        local trigger t = CreateTrigger()
        local dialog  d = DialogCreate()
        local string formatString

        // Display "player was defeated" or "player has left the game" message
        if (leftGame) then
            set formatString = GetLocalizedString( "PLAYER_LEFT_GAME" )
        else
            set formatString = GetLocalizedString( "PLAYER_DEFEATED" )
        endif

        call DisplayTimedTextFromPlayer(whichPlayer, 0, 0, 60, formatString)

        call DialogSetMessage( d, GetLocalizedString( "GAMEOVER_DEFEAT_MSG" ) )

        // Only show the continue button if the game is not over and observers on death are allowed
        if (not bj_meleeGameOver and IsMapFlagSet(MAP_OBSERVERS_ON_DEATH)) then
            call DialogAddButton( d, GetLocalizedString( "GAMEOVER_CONTINUE_OBSERVING" ), GetLocalizedHotkey("GAMEOVER_CONTINUE_OBSERVING") )
        endif

        set t = CreateTrigger()
        call TriggerRegisterDialogButtonEvent( t, DialogAddQuitButton( d, true, GetLocalizedString( "GAMEOVER_QUIT_GAME" ), GetLocalizedHotkey("GAMEOVER_QUIT_GAME") ) )

        call DialogDisplay( whichPlayer, d, true )
        call StartSoundForPlayerBJ( whichPlayer, bj_defeatDialogSound )
    endfunction

    //===========================================================================
    function GameOverDialogBJ takes player whichPlayer, boolean leftGame returns nothing
        local trigger t = CreateTrigger()
        local dialog  d = DialogCreate()
        local string  s

        // Display "player left the game" message
        call DisplayTimedTextFromPlayer(whichPlayer, 0, 0, 60, GetLocalizedString( "PLAYER_LEFT_GAME" ))

        if (GetIntegerGameState(GAME_STATE_DISCONNECTED) != 0) then
            set s = GetLocalizedString( "GAMEOVER_DISCONNECTED" )
        else
            set s = GetLocalizedString( "GAMEOVER_GAME_OVER" )
        endif

        call DialogSetMessage( d, s )

        set t = CreateTrigger()
        call TriggerRegisterDialogButtonEvent( t, DialogAddQuitButton( d, true, GetLocalizedString( "GAMEOVER_OK" ), GetLocalizedHotkey("GAMEOVER_OK") ) )

        call DialogDisplay( whichPlayer, d, true )
        call StartSoundForPlayerBJ( whichPlayer, bj_defeatDialogSound )
    endfunction

    //===========================================================================
    function RemovePlayerPreserveUnitsBJ takes player whichPlayer, playergameresult gameResult, boolean leftGame returns nothing
        if AllowVictoryDefeat(gameResult) then

            call RemovePlayer(whichPlayer, gameResult)

            if( gameResult == PLAYER_GAME_RESULT_VICTORY ) then
                call MeleeVictoryDialogBJ( whichPlayer, leftGame )
                return
            elseif( gameResult == PLAYER_GAME_RESULT_DEFEAT ) then
                call MeleeDefeatDialogBJ( whichPlayer, leftGame )
            else
                call GameOverDialogBJ( whichPlayer, leftGame )
            endif

        endif
    endfunction

    //===========================================================================
    function CustomVictoryOkBJ takes nothing returns nothing
        if bj_isSinglePlayer then
            call PauseGame( false )
            // Bump the difficulty back up to the default.
            call SetGameDifficulty(GetDefaultDifficulty())
        endif

        if (bj_changeLevelMapName == null) then
            call EndGame( bj_changeLevelShowScores )
        else
            call ChangeLevel( bj_changeLevelMapName, bj_changeLevelShowScores )
        endif
    endfunction

    //===========================================================================
    function CustomVictoryQuitBJ takes nothing returns nothing
        if bj_isSinglePlayer then
            call PauseGame( false )
            // Bump the difficulty back up to the default.
            call SetGameDifficulty(GetDefaultDifficulty())
        endif

        call EndGame( bj_changeLevelShowScores )
    endfunction

    //===========================================================================
    function CustomVictoryDialogBJ takes player whichPlayer returns nothing
        local trigger t = CreateTrigger()
        local dialog  d = DialogCreate()

        call DialogSetMessage( d, GetLocalizedString( "GAMEOVER_VICTORY_MSG" ) )

        set t = CreateTrigger()
        call TriggerRegisterDialogButtonEvent( t, DialogAddButton( d, GetLocalizedString( "GAMEOVER_CONTINUE" ), GetLocalizedHotkey("GAMEOVER_CONTINUE") ) )
        call TriggerAddAction( t, function CustomVictoryOkBJ )

        set t = CreateTrigger()
        call TriggerRegisterDialogButtonEvent( t, DialogAddButton( d, GetLocalizedString( "GAMEOVER_QUIT_MISSION" ), GetLocalizedHotkey("GAMEOVER_QUIT_MISSION") ) )
        call TriggerAddAction( t, function CustomVictoryQuitBJ )

        if (GetLocalPlayer() == whichPlayer) then
            call EnableUserControl( true )
            if bj_isSinglePlayer then
                call PauseGame( true )
            endif
            call EnableUserUI(false)
        endif

        call DialogDisplay( whichPlayer, d, true )
        call VolumeGroupSetVolumeForPlayerBJ( whichPlayer, SOUND_VOLUMEGROUP_UI, 1.0 )
        call StartSoundForPlayerBJ( whichPlayer, bj_victoryDialogSound )
    endfunction

    //===========================================================================
    function CustomVictorySkipBJ takes player whichPlayer returns nothing
        if (GetLocalPlayer() == whichPlayer) then
            if bj_isSinglePlayer then
                // Bump the difficulty back up to the default.
                call SetGameDifficulty(GetDefaultDifficulty())
            endif

            if (bj_changeLevelMapName == null) then
                call EndGame( bj_changeLevelShowScores )
            else
                call ChangeLevel( bj_changeLevelMapName, bj_changeLevelShowScores )
            endif
        endif
    endfunction

    //===========================================================================
    function CustomVictoryBJ takes player whichPlayer, boolean showDialog, boolean showScores returns nothing
        if AllowVictoryDefeat( PLAYER_GAME_RESULT_VICTORY ) then
            call RemovePlayer( whichPlayer, PLAYER_GAME_RESULT_VICTORY )

            if not bj_isSinglePlayer then
                call DisplayTimedTextFromPlayer(whichPlayer, 0, 0, 60, GetLocalizedString( "PLAYER_VICTORIOUS" )