1. Are you planning to upload your awesome map to Hive? Please review the rules here.
    Dismiss Notice
  2. 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
  3. 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 havn't received your rank award? Then please contact the administration.
    Dismiss Notice
  4. We have recently started the 16th edition of the Mini Mapping Contest. The theme is mini RPG. Do check it out and have fun.
    Dismiss Notice
  5. Dismiss Notice
  6. Choose your ride to damnation in the 5th Special Effect Contest Poll.
    Dismiss Notice
  7. The winners of the 13th Techtree Contest have been announced!
    Dismiss Notice
  8. 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.

Trigger Viewer

(1)Jetpack_100b.w3x
Variables
Documentation
About
Credits
Changelog
Initialization
Map Initialization
System Initialization
Quest
Quest
Menu
Menu
LevelBlocker
Scoreboard
Scoreboard
ScoreboardTable
EndScoreboard
ResetLevelScores
Imported Systems
TimerUtils
Multibars
Systems
Data
StackModule
KeySystem
AngleCalculations
Music
Music
Camera
Camera
AbilityDummy
AbilityDummy
Abilities
RepeatTile
LoopTileRow
LoopTileCol
TestMap
TestReturn
SaveLevel
LoadLevel
ClearMap
Zoom
MainMenu
RestartLevel
-==[ Level ]==-
LevelMain
LevelMain
Cells
Exit
CacheSaveLoad
SaveLevelToCache
LoadLevelFromCache
SaveLoadHighScore
Levels
Levels
LevelDialog
LevelBuilder
Level1
Level2
Level3
Level4
Level5
Level6
Custom
Objects
ObjectStruct
CreateObjecttypes
-==[ Entities ]==-
Entities
EntityMain
EntityTypes
EntityLogic
-==[ Gameplay ]==-
GameMain
GameMain
Player
Death
Timer
VisionLimit
Movement
Collision
Movement
Jump
Gamestats
Gamestats
ObjectFunctions
Teleporter
Lever
FireTrap
ActionKey
ActionKeyPress
-==[ Level Editor ]==-
EditorMain
EditorMain
CellEditFrame
StartLocationFlag
SaveSlots
SaveSlots
SaveSlotsDialog
Navigation
CellMouseTracked
CellMouseHit
CellBrowser
TileMenu
TileMenu
TileSpecial
-== Special Tiles ==-
TileStartLoc
TileExitLoc
-== Entity Tiles ==-
TileClearEntity
TileSawBlade
TileRocket
TileBat
TileTurret
EntityTable
EntityTable
EntityTableButtons
-==[]==-
Debug
startDebug
startEdDebug
entityDebug
zoomDebug
lightDebug
invertDebug
//TESH.scrollpos=0
//TESH.alwaysfold=0
Name Type Is Array Initial Value
//TESH.scrollpos=0
//TESH.alwaysfold=0

JETPACK is a 2D platform game created by Sourc[e]x for WarCraft III.
The object of the levels is to collect all of the keys scattered around the level while avoiding obstacles and enemies.
Once accomplished, a door opens which the player must go through in order to complete the level.

The map also features a in-game level editor. It`s kinda slow and not very user-friendly, but it works properly.
//TESH.scrollpos=0
//TESH.alwaysfold=0
Models:
        Sourc[e]x
   
Textures:
        Sourc[e]x
   
Scripting:
        Sourc[e]x
   
Additional systems:
        Vexorian (TimerUtils)
        Ammorth  (MultiBars)
       
Music:
        Main menu: darthduba @ Newgrounds
        Gameplay: coolguy @ Newgrounds
        Editor: Marcus-Werhaldus @ Newgrounds
       
Special thanks to Vexorian for creating JassHelper.
//TESH.scrollpos=3
//TESH.alwaysfold=0
Official JETPACK Changelog

0.9.5
 - Public release.
 
0.9.6
 - Added a computer player slot.
 - Fixed a bug with the Exit Location not loading correctly from a previously saved level.
 
0.9.7
 - New editor tiles and entities.
 - Fixed a level tile bug.
 - Added some new sound effects.
 - Added a zoom function.
 
0.9.7b
 - Fixed Death Wheel`s facing direction.
 
0.9.8
 - Added more editor tiles and two new entities.
 - Fixed a bug with entities pushing buttons in the editor.
 - Fixed zoom camera position.
 - Some more sound effects added.
 
0.9.9
 - Minor bug fixes and code maintenance.
 
1.0.0
 - Logo added to the main menu.
 - Created a scoreboard to keep track of highscores.
 - Jetpack is now open source.
 
1.0.0b
 - Decreased map bounds size to get rid of a "out of bounds" bug on empty bound cells.
//TESH.scrollpos=0
//TESH.alwaysfold=0
library MapInitialization

function MapInitializer takes nothing returns nothing
    call FogEnable         ( false )
    call FogMaskEnable     ( false )
    call SetTimeOfDayScale ( 0.00 )
    call SetFloatGameState ( GAME_STATE_TIME_OF_DAY, 12.00 )
    call EnableDragSelect  ( false, false )
    call EndThematicMusic  (  )
    call StopMusic         ( false )
    call SetMusicVolume    ( 0 )
    call EnableWorldFogBoundary ( false )
    call EnableMinimapFilterButtons ( false, false )
    call SetTerrainFogEx   ( 0, 10000, 10000, 0, 0, 0, 0 )
endfunction

endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library SystemInitialization initializer init

private function init takes nothing returns nothing
    // Initialize map settings
    call MapInitializer.execute (  )

    // Initialize camera system
    call CameraSystemInitializer.execute (  )
   
    // Initialize key system
    call KeySystemInitializer.execute (  )
   
    // Initialize object types
    call InitializeObjectTypes.execute (  )
   
    // Initialize entity types
    call InitializeEntityTypes.execute (  )
   
    // Initialize level layout
    call InitializeLevel.execute (  )
   
    // Initialize status board
    call InitializeStatusBoard.execute (  )
   
    // Initialize standard levels
    call InitializeLevels.execute (  )
   
    // Initialize menu dialog
    call InitializeMenu.execute (  )
   
    // Initialize all save/load slots
    call InitializeSaveSlots.execute (  )
endfunction

endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library Quest initializer init

private function init takes nothing returns nothing
    local quest q = CreateQuest (  )
    call QuestSetDescription ( q, "JETPACK is a 2D platform game created by Sourc[e]x for WarCraft III.\nThe object of the levels is to collect all of the keys scattered around the level while avoiding obstacles and enemies.\nOnce accomplished, a door opens which the player must go through in order to complete the level." )
    call QuestSetEnabled     ( q, true )
    call QuestSetIconPath    ( q, "ReplaceableTextures\\CommandButtons\\BTNSpy.blp" )
    call QuestSetRequired    ( q, true )
    call QuestSetTitle       ( q, "About" )
    call QuestSetDiscovered  ( q, true )
   
    set q = CreateQuest (  )
    call QuestSetDescription ( q, "Models:
        Sourc[e]x
   
Textures:
        Sourc[e]x
   
Scripting:
        Sourc[e]x
   
Additional systems:
        Vexorian (TimerUtils)
        Ammorth  (MultiBars)
       
Music:
        Main menu: darthduba
        Gameplay: coolguy
        Editor: Marcus-Werhaldus
       
Special thanks to Vexorian for creating JassHelper."
)
    call QuestSetEnabled     ( q, true )
    call QuestSetIconPath    ( q, "ReplaceableTextures\\CommandButtons\\BTNSpy.blp" )
    call QuestSetRequired    ( q, false )
    call QuestSetTitle       ( q, "Credits" )
    call QuestSetDiscovered  ( q, true )
endfunction

endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library Menu requires LevelDialog, Scoreboard

globals
    boolean MENU = false

    private dialog Menu
    private button BTNStartGame
    private button BTNScoreBoard
    private button BTNStartEditor
    private button BTNQuitGame
   
    private destructable Logo = null
endglobals

function StartGameClick takes nothing returns nothing
    call OpenLevelSelection (  )
    call ShowDestructable ( Logo, false )
endfunction

function ScoreBoardClick takes nothing returns nothing
    call OpenScoreBoard (  )
    call ShowDestructable ( Logo, false )
endfunction

function StartEditorClick takes nothing returns nothing
    call ShowDestructable ( Blocker, false )
    call ShowDestructable ( Logo, false )
    call StartEditor.execute (  )
    set MENU = false
endfunction

function QuitGameClick takes nothing returns nothing
    call EndGame ( false )
endfunction

function OpenMenu takes nothing returns nothing
    if EDITOR then
        call EndEditor.execute (  )
    endif
    if GAMEPLAY then
        call EndGameplay.execute (  )
    endif
    set MENU = true
    call StartMusic ( gg_snd_MenuTrack )
   
    call ShowDestructable ( Blocker, true )
    call ShowDestructable ( Logo, true )
    call DialogDisplay ( Player ( 0 ), Menu, true )
endfunction

private function InitializeMenuDelay takes nothing returns nothing
    local trigger t
    set Menu = DialogCreate (  )
    set BTNStartGame   = DialogAddButton ( Menu, "|cffffcc00Start Game|r", 0 )
    set BTNScoreBoard  = DialogAddButton ( Menu, "Scoreboard", 0 )
    set BTNStartEditor = DialogAddButton ( Menu, "Launch Editor", 0 )
    set BTNQuitGame    = DialogAddButton ( Menu, "Quit Game", 0 )
   
    set t = CreateTrigger (  )
    call TriggerRegisterDialogButtonEvent ( t, BTNStartGame )
    call TriggerAddAction ( t, function StartGameClick )
   
    set t = CreateTrigger (  )
    call TriggerRegisterDialogButtonEvent ( t, BTNScoreBoard )
    call TriggerAddAction ( t, function ScoreBoardClick )
     
    set t = CreateTrigger (  )
    call TriggerRegisterDialogButtonEvent ( t, BTNStartEditor )
    call TriggerAddAction ( t, function StartEditorClick )
   
    set t = CreateTrigger (  )
    call TriggerRegisterDialogButtonEvent ( t, BTNQuitGame )
    call TriggerAddAction ( t, function QuitGameClick )
   
    set Logo = CreateDestructableZ ( 'B016', MID_X, MID_Y, 9300, 270, 0.5, 0 )
    call ShowDestructable ( Logo, false )
   
    call OpenMenu (  )
endfunction

function InitializeMenu takes nothing returns nothing
    call TimerStart ( CreateTimer (  ), 0, false, function InitializeMenuDelay )
endfunction

endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library LevelBlocker initializer init

globals
    destructable Blocker
endglobals

private function init takes nothing returns nothing
    set Blocker = CreateDestructableZ ( 'B00C', MID_X, MID_Y, 9000, 270, 6000.0, 0 )
endfunction

endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library Scoreboard requires ScoreboardTable

globals
    boolean SCOREBOARD = false
endglobals

function OpenScoreBoard takes nothing returns nothing
    set SCOREBOARD = true
    call ShowScoreTable (  )
endfunction

function CloseScoreBoard takes nothing returns nothing
    set SCOREBOARD = false
    call HideScoreTable (  )
    call OpenMenu.execute (  )
endfunction

endlibrary
//TESH.scrollpos=9
//TESH.alwaysfold=0
library ScoreboardTable initializer init

globals
    public  multiboard Table
    private multiboarditem array HighScoreI     [ 2 ][ 30 ]
    private multiboarditem array SessionScoreI1 [ 2 ][ 30 ]
    private multiboarditem array SessionScoreI2 [ 2 ][ 30 ]
    private multiboarditem array SessionScoreI3 [ 2 ][ 30 ]
            integer        array SessionScore1  [ 2 ][ 30 ]
            integer        array SessionScore2  [ 2 ][ 30 ]
            integer        array SessionScore3  [ 2 ][ 30 ]
            integer        array HighScore      [ 2 ][ 30 ]
    private constant real MAX_WIDTH = 0.75
endglobals

function UpdateScoreTable takes nothing returns nothing
    local integer i = 0
    loop
        set i = i + 1
        exitwhen i > MAX_LEVELS
        call MultiboardSetItemValue ( HighScoreI     [ 0 ][ i ], I2S ( HighScore     [ 0 ][ i ] ) )
        call MultiboardSetItemValue ( SessionScoreI1 [ 0 ][ i ], I2S ( SessionScore1 [ 0 ][ i ] ) )
        call MultiboardSetItemValue ( SessionScoreI2 [ 0 ][ i ], I2S ( SessionScore2 [ 0 ][ i ] ) )
        call MultiboardSetItemValue ( SessionScoreI3 [ 0 ][ i ], I2S ( SessionScore3 [ 0 ][ i ] ) )
    endloop
    set i = 0
    loop
        set i = i + 1
        exitwhen i > MAX_SAVE_SLOTS
        call MultiboardSetItemValue ( HighScoreI     [ 1 ][ i ], I2S ( HighScore     [ 1 ][ i ] ) )
        call MultiboardSetItemValue ( SessionScoreI1 [ 1 ][ i ], I2S ( SessionScore1 [ 1 ][ i ] ) )
        call MultiboardSetItemValue ( SessionScoreI2 [ 1 ][ i ], I2S ( SessionScore2 [ 1 ][ i ] ) )
        call MultiboardSetItemValue ( SessionScoreI3 [ 1 ][ i ], I2S ( SessionScore3 [ 1 ][ i ] ) )
    endloop
endfunction

function SetScore takes boolean customLevel, integer levelId, integer score returns nothing
    local integer i = 0
    if customLevel then
        set i = 1
    endif
   
    if score > HighScore [ i ][ levelId ] then
        call SaveHighScore.execute ( customLevel, levelId, score )
    endif
    set SessionScore3 [ i ][ levelId ] = SessionScore2 [ i ][ levelId ]
    set SessionScore2 [ i ][ levelId ] = SessionScore1 [ i ][ levelId ]
    set SessionScore1 [ i ][ levelId ] = score
endfunction

function ShowScoreTable takes nothing returns nothing
    call UpdateScoreTable (  )
    call MultiboardDisplay  ( Table, true )
    call MultiboardMinimize ( Table, false )
endfunction

function HideScoreTable takes nothing returns nothing
    call MultiboardDisplay  ( Table, false )
endfunction

private function CreateTable takes nothing returns nothing
    local integer i  = 0
    local integer i2
    local multiboarditem m
    local integer collumns = MAX_LEVELS + MAX_SAVE_SLOTS + 2
    set Table = CreateMultiboard  (  )
    call MultiboardSetColumnCount ( Table, collumns )
    call MultiboardSetRowCount    ( Table, 10 )
    call MultiboardSetTitleText   ( Table, "|cff00ff00Preset levels                                                                                                          |cffff0000Custom levels|r" )
    loop
        set i2 = 0
        loop
            set m = MultiboardGetItem   ( Table, i2, i )
            call MultiboardSetItemStyle ( m, true, false )
            call MultiboardSetItemWidth ( m, MAX_WIDTH / collumns )
            call MultiboardReleaseItem  ( m )
            set i2 = i2 + 1
            exitwhen i2 > 10
        endloop
        set i = i + 1
        exitwhen i > MAX_LEVELS + MAX_SAVE_SLOTS + 2
    endloop
   
    set i = 0
    loop
        set i = i + 1
        exitwhen i > MAX_LEVELS
        set m = MultiboardGetItem        ( Table, 0, i )
        call MultiboardSetItemValue      ( m, "Level " + I2S ( i ) )
        call MultiboardSetItemValueColor ( m, 0, 255, 0, 255 )
        call MultiboardReleaseItem       ( m )
       
        set HighScoreI     [ 0 ][ i ] = MultiboardGetItem ( Table, 2, i )
        call MultiboardSetItemValueColor ( HighScoreI     [ 0 ][ i ], 255, 204, 0, 255 )
        set SessionScoreI1 [ 0 ][ i ] = MultiboardGetItem ( Table, 4, i )
        call MultiboardSetItemValueColor ( SessionScoreI1 [ 0 ][ i ], 255, 204, 0, 255 )
        set SessionScoreI2 [ 0 ][ i ] = MultiboardGetItem ( Table, 5, i )
        call MultiboardSetItemValueColor ( SessionScoreI2 [ 0 ][ i ], 255, 204, 0, 255 )
        set SessionScoreI3 [ 0 ][ i ] = MultiboardGetItem ( Table, 6, i )
        call MultiboardSetItemValueColor ( SessionScoreI3 [ 0 ][ i ], 255, 204, 0, 255 )
    endloop
   
    loop
        set i = i + 1
        exitwhen i > MAX_LEVELS + MAX_SAVE_SLOTS + 1
        set m = MultiboardGetItem   ( Table, 0, i )
        call MultiboardSetItemValue ( m, "Level #" + I2S ( i - ( MAX_LEVELS + 1 ) ) )
        call MultiboardSetItemValueColor ( m, 255, 0, 0, 255 )
        call MultiboardReleaseItem  ( m )
       
        set HighScoreI     [ 1 ][ i - ( MAX_LEVELS + 1 ) ] = MultiboardGetItem ( Table, 2, i )
        call MultiboardSetItemValueColor ( HighScoreI     [ 1 ][ i - ( MAX_LEVELS + 1 ) ], 255, 204, 0, 255 )
        set SessionScoreI1 [ 1 ][ i - ( MAX_LEVELS + 1 ) ] = MultiboardGetItem ( Table, 4, i )
        call MultiboardSetItemValueColor ( SessionScoreI1 [ 1 ][ i - ( MAX_LEVELS + 1 ) ], 255, 204, 0, 255 )
        set SessionScoreI2 [ 1 ][ i - ( MAX_LEVELS + 1 ) ] = MultiboardGetItem ( Table, 5, i )
        call MultiboardSetItemValueColor ( SessionScoreI2 [ 1 ][ i - ( MAX_LEVELS + 1 ) ], 255, 204, 0, 255 )
        set SessionScoreI3 [ 1 ][ i - ( MAX_LEVELS + 1 ) ] = MultiboardGetItem ( Table, 6, i )
        call MultiboardSetItemValueColor ( SessionScoreI3 [ 1 ][ i - ( MAX_LEVELS + 1 ) ], 255, 204, 0, 255 )
    endloop
   
    set m = MultiboardGetItem   ( Table, 2, 0 )
    call MultiboardSetItemValue ( m, "|cffffcc00Highscore:|r" )
    call MultiboardReleaseItem  ( m )
   
    set m = MultiboardGetItem   ( Table, 4, 0 )
    call MultiboardSetItemValue ( m, "|cffffcc00Latest:|r" )
    call MultiboardReleaseItem  ( m )
   
    //set m = MultiboardGetItem   ( Table, 5, 0 )
    //call MultiboardSetItemValue ( m, "|cffffcc00session|r" )
    //call MultiboardReleaseItem  ( m )
   
    //set m = MultiboardGetItem   ( Table, 6, 0 )
    //call MultiboardSetItemValue ( m, "|cffffcc00scores:|r" )
    //call MultiboardReleaseItem  ( m )
   
    set m = MultiboardGetItem   ( Table, 9, 1 )
    call MultiboardSetItemValue ( m, "|cffff0000Press ESC to return to main menu|r" )
    call MultiboardSetItemWidth ( m, MAX_WIDTH / 2 )
    call MultiboardReleaseItem  ( m )
   
    call MultiboardDisplay ( Table, false )
endfunction

private function init takes nothing returns nothing
    call TimerStart ( CreateTimer (  ), 0.1, false, function CreateTable )
endfunction

endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope EndScoreBoard initializer init

private function OnScoreBoardEnd takes nothing returns boolean
    if SCOREBOARD then
        call CloseScoreBoard (  )
    endif
    return false
endfunction

private function init takes nothing returns nothing
    local trigger t = CreateTrigger (  )
    call TriggerRegisterPlayerEvent ( t, Player ( 0 ), EVENT_PLAYER_END_CINEMATIC )
    call TriggerAddCondition ( t, Condition ( function OnScoreBoardEnd ) )
endfunction

endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope ResetLevelScores initializer init

function ResetScores takes nothing returns nothing
    local integer i = MAX_LEVELS
    loop
        set i = i - 1
        exitwhen i < 0
        call SaveHighScore ( false, i, 0 )
    endloop
   
    set i = MAX_SAVE_SLOTS
    loop
        set i = i - 1
        exitwhen i < 0
        call SaveHighScore ( true, i, 0 )
    endloop
   
    if SCOREBOARD then
        call UpdateScoreTable (  )
    endif
    call ClearTextMessages (  )
endfunction

private function init takes nothing returns nothing
    local trigger t = CreateTrigger (  )
    call TriggerRegisterPlayerChatEvent ( t, Player ( 0 ), "-ResetScores", true )
    call TriggerAddAction ( t, function ResetScores )
endfunction

endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
library_once TimerUtils initializer init
//*********************************************************************
//* TimerUtils (Blue flavor for 1.23b or later)
//* ----------
//*
//*  To implement it , create a custom text trigger called TimerUtils
//* and paste the contents of this script there.
//*
//*  To copy from a map to another, copy the trigger holding this
//* library to your map.
//*
//* (requires vJass)   More scripts: htt://www.wc3campaigns.net
//*
//* For your timer needs:
//*  * Attaching
//*  * Recycling (with double-free protection)
//*
//* set t=NewTimer()      : Get a timer (alternative to CreateTimer)
//* ReleaseTimer(t)       : Relese a timer (alt to DestroyTimer)
//* SetTimerData(t,2)     : Attach value 2 to timer
//* GetTimerData(t)       : Get the timer's value.
//*                         You can assume a timer's value is 0
//*                         after NewTimer.
//*
//* Blue Flavor: Slower than the red flavor, it got a 408000 handle id
//*             limit, which means that if more than 408000 handle ids
//*             are used in your map, TimerUtils might fail, this
//*             value is quite big and it is much bigger than the
//*             timer limit in Red flavor.
//*
//********************************************************************

    //==================================================================================================
    globals
        private hashtable hasht //I <3 blizz
    endglobals

    //It is dependent on jasshelper's recent inlining optimization in order to perform correctly.
    function SetTimerData takes timer t, integer value returns nothing
        call SaveInteger(hasht,0, GetHandleId(t), value)
    endfunction

    function GetTimerData takes timer t returns integer
        return LoadInteger(hasht, 0, GetHandleId(t))
    endfunction

    //==========================================================================================
    globals
        private timer array tT
        private integer tN = 0
        private constant integer HELD=0x28829022
        //use a totally random number here, the more improbable someone uses it, the better.
    endglobals

    //==========================================================================================
    function NewTimer takes nothing returns timer
        if (tN==0) then
            set tT[0]=CreateTimer()
        else
            set tN=tN-1
        endif
        call SetTimerData(tT[tN],0)
     return tT[tN]
    endfunction

    //==========================================================================================
    function ReleaseTimer takes timer t returns nothing
        if(t==null) then
            debug call BJDebugMsg("Warning: attempt to release a null timer")
            return
        endif
        if (tN==8191) then
            debug call BJDebugMsg("Warning: Timer stack is full, destroying timer!!")

            //stack is full, the map already has much more troubles than the chance of bug
            call DestroyTimer(t)
        else
            call PauseTimer(t)
            if(GetTimerData(t)==HELD) then
                debug call BJDebugMsg("Warning: ReleaseTimer: Double free!")
                return
            endif
            call SetTimerData(t,HELD)
            set tT[tN]=t
            set tN=tN+1
        endif    
    endfunction

    private function init takes nothing returns nothing
        set hasht = InitHashtable()
    endfunction

endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library Multibars initializer InitMultibars

globals
    // ====================
    // PUBLIC CONSTANTS
    // ====================    
   
    // These constants should not be modified
    constant integer MULTIBAR_TYPE_RADIO = 1
   
   
    // ====================
    // END PUBLIC CONSTANTS
    // ====================
   
   
    //Private Variables    
   
    private integer Gcurrent = 0
    private string array Gbartype
    private string array Gfiletype
    private string array Gdir
    private integer array Gdivs    
     
endglobals

struct Multibar
    integer row
    integer colstart
    integer numcols
    integer numdivs
    real max
    real current
    string bartype
    string filetype
    string dir
    multiboard whichboard
    integer array curfill[16]

    static method create takes multiboard whichboard, integer colstart, integer row, integer size, real maxval, real currentval, integer bartype returns Multibar
        local Multibar b = Multibar.allocate()
        local integer i = 0
        set b.whichboard = whichboard
        debug if row > 32 then
            debug call BJDebugMsg("Multibars Error: Multiboards only have 32 rows!")
        debug elseif row < 0 then
            debug call BJDebugMsg("Multibars Error: Multiboards do not have negative rows!")
        debug endif
        set b.row = row
        set b. colstart = colstart
        debug if colstart > 16 then
            debug call BJDebugMsg("Multibars Error: Multiboards only have 16 columns")
        debug elseif colstart < 0 then
            debug call BJDebugMsg("Multibars Error: Multiboards do not have negative columns!")
        debug endif
        debug if size < 2 then
            debug call BJDebugMsg("Multibars Error: Bar size must be greater than 1!")
        debug elseif size > 16 then
            debug call BJDebugMsg("Multibars Error: Bar size must be less than 17!")
        debug endif
        set b.numcols = size
        set b.max = maxval
        if maxval < 0 then
            debug call BJDebugMsg("Multibars Error: Multibars does not support negative values!")
            set b.max = 1
        endif
        set b.current = currentval
        if currentval < 0 then
            debug call BJDebugMsg("Multibars Error: Multibars does not support negative values!")
            set b.current = 0
        elseif currentval > b.max then
            debug call BJDebugMsg("Multibars Error: Value cannot exceed max value!")
            set b.current = b.max
        endif
        debug if bartype > Gcurrent then
            debug call BJDebugMsg("Multibars Error: Invalid bartype!")
        debug endif
        set b.numdivs = Gdivs[bartype]
        set b.bartype = Gbartype[bartype]
        set b.filetype = Gfiletype[bartype]
        set b.dir = Gdir[bartype]
        loop
            exitwhen i > 15
            set b.curfill[i] = -1
            set i = i + 1
        endloop
        call b.SetMultiboardBar()
        call b.UpdateBoard()
        return b
    endmethod
   
    method onDestroy takes nothing returns nothing
        set .whichboard = null
    endmethod
   
    private method UpdateBoard takes nothing returns nothing
        local real step = .max/.numcols
        local real step2 = step/.numdivs
        local real val = .current
        local multiboarditem mbi
        local integer col = .colstart
        local integer num
        loop
            exitwhen val <= 0.
            if val >= step then
                if .curfill[col] != .numdivs then // check if it requires an update
                    set mbi = MultiboardGetItem(.whichboard, .row, col)
                    if col == .colstart then
                        call MultiboardSetItemIcon(mbi, .dir+.bartype+"L"+I2S(.numdivs)+.filetype) // Left
                    elseif col == .colstart+.numcols-1 then
                        call MultiboardSetItemIcon(mbi, .dir+.bartype+"R"+I2S(.numdivs)+.filetype) // Right
                    else
                        call MultiboardSetItemIcon(mbi, .dir+.bartype+"M"+I2S(.numdivs)+.filetype) // Middle
                    endif
                    call MultiboardReleaseItem(mbi)
                    set .curfill[col] = .numdivs
                endif
                set val = val - step
            else
                set num = R2I((val/step2)+0.5)
                if .curfill[col] != num then // check if it requires an update
                    set mbi = MultiboardGetItem(.whichboard, .row, col)
                    if col == .colstart then
                        call MultiboardSetItemIcon(mbi, .dir+.bartype+"L"+I2S(num)+.filetype) // Left
                    elseif col == .colstart+.numcols-1 then
                        call MultiboardSetItemIcon(mbi, .dir+.bartype+"R"+I2S(num)+.filetype) // Right
                    else
                        call MultiboardSetItemIcon(mbi, .dir+.bartype+"M"+I2S(num)+.filetype) // Middle
                    endif
                    call MultiboardReleaseItem(mbi)
                    set .curfill[col] = num
                endif
                set val = 0.
            endif
            set col = col + 1
        endloop
        loop
            exitwhen col >= .colstart + .numcols
            if .curfill[col] != 0 then // check if it requires an update
                set mbi = MultiboardGetItem(.whichboard, .row, col)
                if col == .colstart then
                    call MultiboardSetItemIcon(mbi, .dir+.bartype+"L0"+.filetype) // Left
                elseif col == .colstart+.numcols-1 then
                    call MultiboardSetItemIcon(mbi, .dir+.bartype+"R0"+.filetype) // Right
                else
                    call MultiboardSetItemIcon(mbi, .dir+.bartype+"M0"+.filetype) // Middle
                endif
                call MultiboardReleaseItem(mbi)
                set .curfill[col] = 0
            endif
            set col = col + 1
        endloop
        set mbi = null
    endmethod
   
    method UpdateValue takes real newvalue, boolean update returns nothing
        if newvalue > .max then
            set .current = .max
        elseif newvalue < 0. then
            set .current = 0.
        else
            set .current = newvalue
        endif
        if update then
            call .UpdateBoard()
        endif
    endmethod
   
    method UpdateMaxValue takes real newvalue, boolean update returns nothing
        if newvalue < 1 then
            set .max = 1
        else
            set .max = newvalue
        endif
        if .current > .max then
                set .current = .max
            endif
        if update then
            call .UpdateBoard()
        endif
    endmethod
   
    private method SetMultiboardBar takes nothing returns nothing
        local integer col = .colstart
        local integer end = col + .numcols
        local multiboarditem mbi
        loop
            exitwhen col >= end
            set mbi = MultiboardGetItem(.whichboard, .row, col)
            call MultiboardSetItemWidth(mbi, 0.01)
            call MultiboardSetItemStyle(mbi, false, true)
            call MultiboardReleaseItem(mbi)
            set col = col + 1
        endloop
        set mbi = null
    endmethod
endstruct

public function CreateBartype takes string name, string filetype, string directory, integer divisions returns integer
    set Gcurrent = Gcurrent + 1
    set Gbartype[Gcurrent] = name
    set Gfiletype[Gcurrent] = filetype
    set Gdir[Gcurrent] = directory
    set Gdivs[Gcurrent] = divisions
    return Gcurrent
endfunction

private function CreateNativeBartype takes integer bartype, string name, string filetype, string directory, integer divisions returns nothing
    if bartype > Gcurrent then
        set Gcurrent = bartype
    endif
    set Gbartype[bartype] = name
    set Gfiletype[bartype] = filetype
    set Gdir[bartype] = directory
    set Gdivs[bartype] = divisions
endfunction

private function InitMultibars takes nothing returns nothing    
    // Set-up Native Bartypes
    call CreateNativeBartype(MULTIBAR_TYPE_RADIO, "Radio", ".tga", "war3mapImported\\", 8)
endfunction

endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library Data
globals
    private hashtable TABLE = InitHashtable (  )
endglobals

// =================================================================================================

function SetHandleData takes handle h, integer key, integer data returns nothing
    call SaveInteger ( TABLE, GetHandleId ( h ), key, data )
endfunction

function GetHandleData takes handle h, integer key returns integer
    return LoadInteger ( TABLE, GetHandleId ( h ), key )
endfunction

function FlushHandleData takes handle h, integer key returns nothing
    call RemoveSavedInteger ( TABLE, GetHandleId ( h ), key )
endfunction

function FlushAllHandleData takes handle h returns nothing
    call FlushChildHashtable ( TABLE, GetHandleId ( h ) )
endfunction

function FlushAllData takes nothing returns nothing
    call FlushParentHashtable ( TABLE )
endfunction

// =================================================================================================

endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
module StackModule
    integer stackid
    static  thistype array stack
    static  integer        count = 0
    method addStack takes nothing returns nothing
        set thistype.stack [ thistype.count ] = this
        set this.stackid = thistype.count
        set thistype.count = thistype.count + 1
    endmethod
   
    method removeStack takes nothing returns nothing
        set thistype.count = thistype.count - 1
        set thistype.stack [ this.stackid ] = thistype.stack [ thistype.count ]
        set thistype.stack [ this.stackid ].stackid = this.stackid
    endmethod
endmodule
//TESH.scrollpos=0
//TESH.alwaysfold=0
library KeySystem

globals
    boolean LEFT  = false
    boolean RIGHT = false
    boolean UP    = false
    boolean DOWN  = false
   
    boolean Invert = false
endglobals

function NormalizeKeys takes nothing returns nothing
    if Invert then
        call InvertKeys.execute (  )
    endif
endfunction

function InvertKeys takes nothing returns nothing
    local boolean up    = UP
    local boolean down  = DOWN
    local boolean left  = LEFT
    local boolean right = RIGHT
    set Invert = not Invert
    set UP     = down
    set DOWN   = up
    set LEFT   = right
    set RIGHT  = left
endfunction

function KeyTriggerEvent takes nothing returns boolean
    local eventid id = GetTriggerEventId (  )
    if id == EVENT_PLAYER_ARROW_DOWN_DOWN then
        if Invert then
            set UP = true
        else
            set DOWN = true
        endif
    elseif id == EVENT_PLAYER_ARROW_DOWN_UP then
        if Invert then
            set UP = false
        else
            set DOWN = false
        endif
    elseif id == EVENT_PLAYER_ARROW_UP_DOWN then
        if Invert then
            set DOWN = true
        else
            set UP = true
        endif
    elseif id == EVENT_PLAYER_ARROW_UP_UP then
        if Invert then
            set DOWN = false
        else
            set UP = false
        endif
    elseif id == EVENT_PLAYER_ARROW_LEFT_DOWN then
        if Invert then
            set RIGHT = true
        else
            set LEFT = true
        endif
    elseif id == EVENT_PLAYER_ARROW_LEFT_UP then
        if Invert then
            set RIGHT = false
        else
            set LEFT = false
        endif
    elseif id == EVENT_PLAYER_ARROW_RIGHT_DOWN then
        if Invert then
            set LEFT = true
        else
            set RIGHT = true
        endif
    elseif id == EVENT_PLAYER_ARROW_RIGHT_UP then
        if Invert then
            set LEFT = false
        else
            set RIGHT = false
        endif
    endif
    return false
endfunction

function KeySystemInitializer takes nothing returns nothing
    local trigger t = CreateTrigger (  )
    call TriggerRegisterPlayerEvent ( t, Player ( 0 ), EVENT_PLAYER_ARROW_DOWN_DOWN )
    call TriggerRegisterPlayerEvent ( t, Player ( 0 ), EVENT_PLAYER_ARROW_DOWN_UP )
    call TriggerRegisterPlayerEvent ( t, Player ( 0 ), EVENT_PLAYER_ARROW_UP_DOWN )
    call TriggerRegisterPlayerEvent ( t, Player ( 0 ), EVENT_PLAYER_ARROW_UP_UP )
    call TriggerRegisterPlayerEvent ( t, Player ( 0 ), EVENT_PLAYER_ARROW_LEFT_DOWN )
    call TriggerRegisterPlayerEvent ( t, Player ( 0 ), EVENT_PLAYER_ARROW_LEFT_UP )
    call TriggerRegisterPlayerEvent ( t, Player ( 0 ), EVENT_PLAYER_ARROW_RIGHT_DOWN )
    call TriggerRegisterPlayerEvent ( t, Player ( 0 ), EVENT_PLAYER_ARROW_RIGHT_UP )
    call TriggerAddCondition ( t, Condition ( function KeyTriggerEvent ) )
endfunction

endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library AngleCalculations

function IsAngleBetweenAngles takes real angle, real angle1, real angle2 returns boolean
    local real x
    set angle  = ModuloReal ( angle,  360 )
    set angle1 = ModuloReal ( angle1, 360 )
    set angle2 = ModuloReal ( angle2, 360 )
    if angle1 > angle2 then
        set x      = angle1
        set angle1 = angle2
        set angle2 = x
    endif
    if angle2 - angle1 >  angle1 - ( angle2 - 360 ) then
        set angle2 = angle2 - 360
        if angle > 180 then
            set angle = angle - 360
        endif
        return angle >= angle2 and angle <= angle1
    endif
    return angle >= angle1 and angle <= angle2
endfunction

function OffsetAngleTowardsAngle takes real a1, real a2, real offset returns real
    local real result
    set a1 = ModuloReal ( a1, 360 )
    set a2 = ModuloReal ( a2, 360 )
    if a1 > a2 then
        set result = a1 - 360
        if a1 - a2 > a2 - result then
            set a1 = result
        endif
    else
        set result = a2 - 360
        if a2 - a1 > a1 - result then
            set a2 = result
        endif
    endif
    if a1 > a2 then
        set result = a1 - offset
        if result <= a2 then
            return a2
        endif
        return result
    endif
    set result = a1 + offset
    if result >= a2 then
        return a2
    endif
    return result
endfunction

endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library MusicSystem

globals
    private sound NowPlaying = null
endglobals

function StartMusic takes sound s returns nothing
    if NowPlaying != s then
        call StopSound  ( NowPlaying, false, true )
        call StopSound  ( s, false, false )
        call StartSound ( s )
        set NowPlaying = s
    endif
endfunction

endlibrary
//TESH.scrollpos=15
//TESH.alwaysfold=0
library CameraSystem

globals
    private constant real UPDATE_FREQUENCE = 0.01
    camera Camera = 0
endglobals

//! textmacro CameraConfig
    set Camera.distance    = 9500
    set Camera.farZ        = 10000
    set Camera.angle       = 270
    set Camera.fieldOfView = 33
    set Camera.roll        = 0
    set Camera.rotation    = 90
    set Camera.zOffset     = 0
    set Camera.duration    = 0
//! endtextmacro

struct camera
    real x = 0
    real y = -256
    real distance
    real farZ
    real angle
    real fieldOfView
    real roll
    real rotation
    real zOffset
    real duration
    boolean follow = false
    method followPlayer takes boolean flag returns nothing
        set this.follow = flag
        set this.x = MID_X
        set this.y = MID_Y
        if flag then
            set this.fieldOfView = 20
        else
            set this.fieldOfView = 33
        endif
    endmethod
   
    static method onLoop takes nothing returns nothing
        local camera this = Camera
        if this.follow then
            set this.x = X + 256
            set this.y = Y
        endif
        call SetCameraField   ( CAMERA_FIELD_TARGET_DISTANCE, this.distance,    this.duration )
        call SetCameraField   ( CAMERA_FIELD_FARZ,            this.farZ,        this.duration )
        call SetCameraField   ( CAMERA_FIELD_ANGLE_OF_ATTACK, this.angle,       this.duration )
        call SetCameraField   ( CAMERA_FIELD_FIELD_OF_VIEW,   this.fieldOfView, this.duration )
        call SetCameraField   ( CAMERA_FIELD_ROLL,            this.roll,        this.duration )
        call SetCameraField   ( CAMERA_FIELD_ROTATION,        this.rotation,    this.duration )
        call SetCameraField   ( CAMERA_FIELD_ZOFFSET,         this.zOffset,     this.duration )
        call SetCameraBounds  ( this.x, this.y, this.x, this.y, this.x, this.y, this.x, this.y )
    endmethod
endstruct
   
function CameraSystemInitializer takes nothing returns nothing
    set Camera = camera.create (  )
    //! runtextmacro CameraConfig (  )
    call TimerStart ( CreateTimer (  ), UPDATE_FREQUENCE, true, function camera.onLoop )
    call camera.onLoop (  )
endfunction
   
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library AbilityDummy initializer init

globals
    unit Dummy
endglobals

function DummyAddAbility takes integer abilityId returns nothing
    call UnitAddAbility ( Dummy, abilityId )
endfunction

function DummyRemoveAbility takes integer abilityId returns nothing
    call UnitRemoveAbility ( Dummy, abilityId )
endfunction

function SelectDummy takes nothing returns nothing
    call SelectUnit ( Dummy, true )
endfunction

private function init takes nothing returns nothing
    set Dummy = CreateUnit ( Player ( 0 ), 'h001', 0, 0, 270 )
    call TimerStart ( CreateTimer (  ), 0.10, true, function SelectDummy )
endfunction

endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope RepeatTile initializer init

globals
    private constant integer ABILITY_ID = 'A000'
endglobals

private function OnAbilityActivate takes nothing returns nothing
    if GetSpellAbilityId (  ) == ABILITY_ID then
        call RepeatLastTile (  )
    endif
endfunction

private function init takes nothing returns nothing
    local trigger t = CreateTrigger (  )
    call TriggerRegisterAnyUnitEventBJ ( t, EVENT_PLAYER_UNIT_SPELL_CHANNEL )
    call TriggerAddAction ( t, function OnAbilityActivate )
endfunction

endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope LoopTileRow initializer init

globals
    private constant integer ABILITY_ID = 'A005'
endglobals

private function OnAbilityActivate takes nothing returns nothing
    local integer x
    local integer y
    local objecttype t
    if GetSpellAbilityId (  ) == ABILITY_ID then
        set x = 0
        set y = CellEdit.yId
        set t = CellEdit.object.type
        loop
            //if CELLS [ x ][ y ].object.type == OBJECT_NONE then
                call CELLS [ x ][ y ].setObject ( t )
            //endif
            set x = x + 1
            exitwhen x > WIDTH
        endloop
    endif
endfunction

private function init takes nothing returns nothing
    local trigger t = CreateTrigger (  )
    call TriggerRegisterAnyUnitEventBJ ( t, EVENT_PLAYER_UNIT_SPELL_CHANNEL )
    call TriggerAddAction ( t, function OnAbilityActivate )
endfunction

endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope LoopTileCol initializer init

globals
    private constant integer ABILITY_ID = 'A006'
endglobals

private function OnAbilityActivate takes nothing returns nothing
    local integer x
    local integer y
    local objecttype t
    if GetSpellAbilityId (  ) == ABILITY_ID then
        set x = CellEdit.xId
        set y = 0
        set t = CellEdit.object.type
        loop
            //if CELLS [ x ][ y ].object.type == OBJECT_NONE then
                call CELLS [ x ][ y ].setObject ( t )
            //endif
            set y = y + 1
            exitwhen y > HEIGHT
        endloop
    endif
endfunction

private function init takes nothing returns nothing
    local trigger t = CreateTrigger (  )
    call TriggerRegisterAnyUnitEventBJ ( t, EVENT_PLAYER_UNIT_SPELL_CHANNEL )
    call TriggerAddAction ( t, function OnAbilityActivate )
endfunction

endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope TestMap initializer init

globals
    private constant integer ABILITY_ID = 'A001'
endglobals

private function OnAbilityActivate takes nothing returns nothing
    if GetSpellAbilityId (  ) == ABILITY_ID then
        set CellSaveX = CellEdit.xId
        set CellSaveY = CellEdit.yId
        set TEST = true
        call StartGameplay.execute (  )
    endif
endfunction

private function init takes nothing returns nothing
    local trigger t = CreateTrigger (  )
    call TriggerRegisterAnyUnitEventBJ ( t, EVENT_PLAYER_UNIT_SPELL_CHANNEL )
    call TriggerAddAction ( t, function OnAbilityActivate )
endfunction

endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope TestReturn initializer init

globals
    private constant integer ABILITY_ID = 'A002'
endglobals

private function OnAbilityActivate takes nothing returns nothing
    if GetSpellAbilityId (  ) == ABILITY_ID then
        call StartEditor.execute (  )
    endif
endfunction

private function init takes nothing returns nothing
    local trigger t = CreateTrigger (  )
    call TriggerRegisterAnyUnitEventBJ ( t, EVENT_PLAYER_UNIT_SPELL_CHANNEL )
    call TriggerAddAction ( t, function OnAbilityActivate )
endfunction

endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope SaveLevel initializer init

globals
    private constant integer ABILITY_ID = 'A008'
endglobals

private function OnAbilityActivate takes nothing returns nothing
    if GetSpellAbilityId (  ) == ABILITY_ID then
        call UpdateSlotDialog ( true, false )
    endif
endfunction

private function init takes nothing returns nothing
    local trigger t = CreateTrigger (  )
    call TriggerRegisterAnyUnitEventBJ ( t, EVENT_PLAYER_UNIT_SPELL_CHANNEL )
    call TriggerAddAction ( t, function OnAbilityActivate )
endfunction

endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope LoadLevel initializer init

globals
    private constant integer ABILITY_ID = 'A009'
endglobals

private function OnAbilityActivate takes nothing returns nothing
    if GetSpellAbilityId (  ) == ABILITY_ID then
        call UpdateSlotDialog ( false, true )
    endif
endfunction

private function init takes nothing returns nothing
    local trigger t = CreateTrigger (  )
    call TriggerRegisterAnyUnitEventBJ ( t, EVENT_PLAYER_UNIT_SPELL_CHANNEL )
    call TriggerAddAction ( t, function OnAbilityActivate )
endfunction

endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope ClearMap initializer init

globals
    private constant integer ABILITY_ID = 'A004'
    private dialog Dialog = null
    private button Button = null
    private trigger Trig = CreateTrigger (  )
endglobals

private function OnDialogYes takes nothing returns nothing
    call EraseCells.execute (  )
    call MoveCellEdit ( CellEdit.xId, CellEdit.yId )
endfunction

private function OnAbilityActivate takes nothing returns nothing
    if GetSpellAbilityId (  ) == ABILITY_ID then
        if Dialog == null then
            set Dialog = DialogCreate (  )
            set Button = DialogAddButton ( Dialog, "|cffffcc00Yes|r", 0 )
            call DialogAddButton ( Dialog, "No", 0 )
            call TriggerRegisterDialogButtonEvent ( Trig, Button )
            call TriggerAddAction ( Trig, function OnDialogYes )
        endif
        call DialogSetMessage ( Dialog, "This will erase the entire level.\n\nContinue?" )
        call DialogDisplay ( Player ( 0 ), Dialog, true )
    endif
endfunction

private function init takes nothing returns nothing
    local trigger t = CreateTrigger (  )
    call TriggerRegisterAnyUnitEventBJ ( t, EVENT_PLAYER_UNIT_SPELL_CHANNEL )
    call TriggerAddAction ( t, function OnAbilityActivate )
endfunction

endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope Zoom initializer init

globals
    private constant integer ABILITY_ID1 = 'A00B'
    private constant integer ABILITY_ID2 = 'A00C'
endglobals

private function OnAbilityActivate takes nothing returns nothing
    if GetSpellAbilityId (  ) == ABILITY_ID1 then
        call Camera.followPlayer ( true )
        call UnitRemoveAbility ( Dummy, ABILITY_ID1 )
        call UnitAddAbility    ( Dummy, ABILITY_ID2 )
        set Zoom = true
       
    elseif GetSpellAbilityId (  ) == ABILITY_ID2 then
        call Camera.followPlayer ( false )
        call UnitRemoveAbility ( Dummy, ABILITY_ID2 )
        call UnitAddAbility    ( Dummy, ABILITY_ID1 )
        set Zoom = false
       
    endif
endfunction

private function init takes nothing returns nothing
    local trigger t = CreateTrigger (  )
    call TriggerRegisterAnyUnitEventBJ ( t, EVENT_PLAYER_UNIT_SPELL_CHANNEL )
    call TriggerAddAction ( t, function OnAbilityActivate )
endfunction

endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope MainMenu initializer init

globals
    private constant integer ABILITY_ID1 = 'A003'
    private constant integer ABILITY_ID2 = 'A00A'
endglobals

private function OnAbilityActivate takes nothing returns nothing
    if GetSpellAbilityId (  ) == ABILITY_ID1 or GetSpellAbilityId (  ) == ABILITY_ID2 then
        call OpenMenu.execute (  )
    endif
endfunction

private function init takes nothing returns nothing
    local trigger t = CreateTrigger (  )
    call TriggerRegisterAnyUnitEventBJ ( t, EVENT_PLAYER_UNIT_SPELL_CHANNEL )
    call TriggerAddAction ( t, function OnAbilityActivate )
endfunction

endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope RestartLevel initializer init

globals
    private constant integer ABILITY_ID = 'A007'
endglobals

private function OnAbilityActivate takes nothing returns nothing
    if GetSpellAbilityId (  ) == ABILITY_ID then
        call StartGameplay.execute (  )
    endif
endfunction

private function init takes nothing returns nothing
    local trigger t = CreateTrigger (  )
    call TriggerRegisterAnyUnitEventBJ ( t, EVENT_PLAYER_UNIT_SPELL_CHANNEL )
    call TriggerAddAction ( t, function OnAbilityActivate )
endfunction

endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
globals
             level   LEVEL  =  0      // Current level
             integer ID     =  0      // Current level ID
             boolean CUSTOM =  false  // Current level is custom
    constant integer WIDTH  =  18     // Max cells width
    constant integer HEIGHT =  18     // Max cells height
    constant real    MID_X  =  0.00   // Level middle X
    constant real    MID_Y  = -256.00 // Level middle Y
    constant real    SIZE   =  128.00 // Level cell size
    constant real    MAX_X  = MID_X + ( ( ( WIDTH  * SIZE ) / 2 ) )  // Level maximum X
    constant real    MIN_X  = MID_X - ( ( ( WIDTH  * SIZE ) / 2 ) )  // Level minimum X
    constant real    MAX_Y  = MID_Y + ( ( ( HEIGHT * SIZE ) / 2 ) )  // Level maximum Y
    constant real    MIN_Y  = MID_Y - ( ( ( HEIGHT * SIZE ) / 2 ) )  // Level minimum Y
endglobals

struct level
    cell startLoc
    cell exit
    method setStartLoc takes cell new returns nothing
        set this.startLoc = new
        if EDITOR then
            call MoveStartLocFlag ( new.x, new.y )
        endif
    endmethod
    method setExitLoc takes cell new returns nothing
        call SetExitLoc.execute ( new )
    endmethod
    static method create takes nothing returns level
        local level this = level.allocate (  )
        local integer x
        local integer y = 0
        loop
            set x = 0
            loop
                call SaveInteger ( this.content, x, y, integer ( OBJECT_NONE ) )
                call SaveInteger ( this.entities, x, y, 0 )
                set x = x + 1
                exitwhen x > WIDTH
            endloop
            set y = y + 1
            exitwhen y > HEIGHT
        endloop
        set this.saveStart = CELLS [ 0 ][ 0 ]
        set this.saveExit  = CELLS [ WIDTH ][ HEIGHT ]  
        return this
    endmethod
   
    hashtable content = InitHashtable (  )
    cell saveStart
    cell saveExit
    hashtable entities  = InitHashtable (  )
    hashtable entityDir = InitHashtable (  )
    hashtable entityVel = InitHashtable (  )
    method save takes nothing returns nothing
        local integer x
        local integer y = 0
        loop
            set x = 0
            loop
                call SaveInteger ( this.content, x, y, integer ( CELLS [ x ][ y ].object.type ) )
                call SaveInteger ( this.entities, x, y, integer ( CELLS [ x ][ y ].entity.type ) )
                if CELLS [ x ][ y ].entity.type != 0 then
                    call SaveReal ( this.entityDir, x, y, CELLS [ x ][ y ].entityDir )
                    call SaveReal ( this.entityVel, x, y, CELLS [ x ][ y ].entityVel )
                endif
                set x = x + 1
                exitwhen x > WIDTH
            endloop
            set y = y + 1
            exitwhen y > HEIGHT
        endloop
        set this.saveStart = LEVEL.startLoc
        set this.saveExit  = LEVEL.exit
    endmethod
   
    method load takes nothing returns nothing
        local integer x
        local integer y = 0
        local entitytype t
        set LEVEL = this
        loop
            set x = 0
            loop
                call CELLS [ x ][ y ].setObject ( objecttype ( LoadInteger ( this.content, x, y ) ) )
                call CELLS [ x ][ y ].setEntity ( entitytype ( LoadInteger ( this.entities, x, y ) ), LoadReal ( this.entityDir, x, y ), LoadReal ( this.entityVel, x, y )  )
                set x = x + 1
                exitwhen x > WIDTH
            endloop
            set y = y + 1
            exitwhen y > HEIGHT
        endloop
        call this.setStartLoc ( this.saveStart )
        call this.setExitLoc  ( this.saveExit )
    endmethod
   
    method saveTypeCell takes integer x, integer y, objecttype whichType returns nothing
        call SaveInteger ( this.content, x, y, integer(whichType) )
    endmethod
   
    method saveEntityCell takes integer x, integer y, entitytype whichType, real direction, real velocity returns nothing
        call SaveInteger ( this.entities, x, y, integer(whichType) )
        call SaveReal   ( this.entityDir, x, y, direction )
        call SaveReal   ( this.entityVel, x, y, velocity )
    endmethod
   
    method getCellType takes integer x, integer y returns objecttype
        return objecttype ( LoadInteger ( this.content, x, y ) )
    endmethod
endstruct

function InitializeLevel takes nothing returns nothing
    local integer x
    local integer y = 0
    local real posx
    local real posy = MIN_Y
   
    set LEVEL = level.create (  )
   
    loop
        set x    = 0
        set posx = MIN_X
        loop
            set  CELLS [ x ][ y ] = cell.create ( posx, posy, x, y )
            call CELLS [ x ][ y ].setObject ( OBJECT_NONE )
            set x = x + 1
            set posx = posx + SIZE
            exitwhen x > WIDTH
        endloop
        set y = y + 1
        set posy = posy + SIZE
        exitwhen y > HEIGHT
    endloop
    call LEVEL.setStartLoc ( CELLS [ 0 ][ 0 ] )
    call LEVEL.setExitLoc  ( CELLS [ WIDTH ] [ HEIGHT ] )
endfunction
//TESH.scrollpos=0
//TESH.alwaysfold=0
globals
    cell array CELLS [ 80 ] [ 80 ] // All level cells ( WIDTH * HEIGHT )
    cell       CURRENT_CELL = 0    // Current player cell
    key        CellKey
endglobals

function ResetCells takes nothing returns nothing
    local integer x
    local integer y = 0
    loop
        set x = 0
        loop
            set  CELLS [ x ][ y ].object.executed = false
            call CELLS [ x ][ y ].resetEntity (  )
            call SetDestructableAnimation ( CELLS [ x ][ y ].object.d, "stand" )
            set CELLS [ x ][ y ].object.collision = true
            set CELLS [ x ][ y ].object.used      = false
            set CELLS [ x ][ y ].object.lethal    = CELLS [ x ][ y ].object.type.lethal
            set x = x + 1
            exitwhen x > WIDTH
        endloop
        set y = y + 1
        exitwhen y > HEIGHT
    endloop
endfunction

function EraseCells takes nothing returns nothing
    local integer x
    local integer y = 0
    loop
        set x = 0
        loop
            call CELLS [ x ][ y ].setObject ( OBJECT_NONE )
            call CELLS [ x ][ y ].clearEntity (  )
            set x = x + 1
            exitwhen x > WIDTH
        endloop
        set y = y + 1
        exitwhen y > HEIGHT
    endloop
    call LEVEL.setStartLoc ( CELLS [ 0 ][ 0 ] )
    call LEVEL.setExitLoc  ( CELLS [ WIDTH ][ HEIGHT ] )
endfunction

struct cell
    real x
    real y
    integer xId
    integer yId
    object  object = 0
    entity  entity = 0
    real    entityDir = 0
    real    entityVel = 0
    region reg = CreateRegion (  )
    rect   collision
    trackable track
    private static rect r = Rect ( 0, 0, 0, 0 )
    private static trigger t = CreateTrigger (  )
    private static method onEnter takes nothing returns nothing
        local cell this = cell ( GetHandleData ( GetTriggeringRegion (  ), CellKey ) )
        local entity e
        // Player enter cell
        if GetTriggerUnit (  ) == Unit then
            set CURRENT_CELL = this
            set AddXVel = CURRENT_CELL.object.type.addXVel
            set AddYVel = CURRENT_CELL.object.type.addYVel
            if not CURRENT_CELL.object.executed then
                set CURRENT_CELL.object.executed = true
               
                // =============================================
                // Specific execution effects
                if CURRENT_CELL.object.type.score > 0 then
                    set Score = Score + CURRENT_CELL.object.type.score
                    call SetDestructableAnimation ( CURRENT_CELL.object.d, "death" )
                    call StartSound ( gg_snd_PickUpItem )
                endif
               
                if CURRENT_CELL.object.type.isKey then
                    set Keys = Keys + 1
                    call SetDestructableAnimation ( CURRENT_CELL.object.d, "death" )
                    call StartSound ( gg_snd_PickUpItem )
                endif
               
                if CURRENT_CELL.object.type.fuel > 0 then
                    set Fuel = Fuel + CURRENT_CELL.object.type.fuel
                    if Fuel > 100 then
                        set Fuel = 100
                    endif
                    call SetDestructableAnimation ( CURRENT_CELL.object.d, "death" )
                    call StartSound ( gg_snd_PickUpItem )
                endif
               
                if CURRENT_CELL.object.lethal then
                    call KillPlayer.execute ( CURRENT_CELL.object.type.deathMethod )
                endif
               
                if CURRENT_CELL.object.type == OBJECT_LIGHT_SWITCH then
                    if not LimitedVision then
                        call LimitVision (  )
                    else
                        call ResetVision (  )
                    endif
                    call StartSound ( gg_snd_Lever1 )
                endif
               
                if CURRENT_CELL.object.type == OBJECT_INVERT_FIELD then
                    call InvertKeys (  )
                    call SetDestructableAnimation ( CURRENT_CELL.object.d, "death" )
                    call StartSound ( gg_snd_KeyInvert )
                endif
               
                if CURRENT_CELL.object.type == OBJECT_ENTITY_FREEZER then
                    call FreezeEntities ( 10.00 )
                    call SetDestructableAnimation ( CURRENT_CELL.object.d, "death" )
                    call StartSound ( gg_snd_Freeze )
                endif
                // End specific execution effects
                // =============================================
               
            endif
            if CURRENT_CELL == LEVEL.exit and Keys == MaxKeys then
           
                // =============================================
                // Level exit
                call StopTimer (  )
                call StartSound ( gg_snd_LevelCompleted )
                call SetPlayerState ( Player ( 0 ), PLAYER_STATE_RESOURCE_LUMBER, Score )
                call SetPlayerState ( Player ( 0 ), PLAYER_STATE_RESOURCE_GOLD, GetPlayerState ( Player ( 0 ), PLAYER_STATE_RESOURCE_GOLD ) + Score )
                if TEST then
                    call StartEditor.execute (  )
                else
                    call SetScore.execute ( CUSTOM, ID, Score )
                    set PAUSE = true
                    call SetUnitAnimationByIndex ( Unit, STAND_RIGHT )
                    call SetUnitTimeScale        ( Unit, 1.00 )
                    call SetUnitVertexColor      ( Unit, 0, 0, 0, 0 )
                    call PauseUnit               ( Dummy, true )
                    call TriggerSleepAction      ( 2.00 )
                    call OpenLevelSelection      (  )
                    call SetUnitVertexColor      ( Unit, 255, 255, 255, 255 )
                    call PauseUnit               ( Dummy, false )
                endif
                // =============================================
               
            endif
           
            if CURRENT_CELL.object.type.isSwitch then
                call ActivateLever ( CURRENT_CELL )
            endif
           
        // Entity enter cell
        elseif entity.isUnitEntity ( GetTriggerUnit (  ) ) and GAMEPLAY and not PAUSE then
            set e = entity.getFromUnit ( GetTriggerUnit (  ) )
            if this.object.type.isTeleporter and e.type.canTeleport then
                set e.canTeleport = not e.canTeleport
                if e.canTeleport then
                    call TeleportEntity.execute ( e, this )
                endif
            endif
            if this.object.type.isSwitch then
                call ActivateLever ( this )
            endif
        endif
    endmethod
   
    static method create takes real x, real y, integer xId, integer yId returns cell
        local cell this = cell.allocate (  )
        local real size = ( SIZE / 2 ) - 1
        set this.x   = x
        set this.y   = y
        set this.xId = xId
        set this.yId = yId
        call SetRect ( cell.r, x - size, y - size, x + size, y + size )
        call RegionAddRect ( this.reg, cell.r )
        call SetHandleData ( this.reg, CellKey, integer(this) )
        call TriggerRegisterEnterRegion ( cell.t, this.reg, null )
        set size = ( SIZE / 1.5 )
        set this.collision = Rect ( x - size, y - size, x + size, y + size )
        set this.track = CreateTrackable ( "CellTrackable.mdl", x, y, 270 * bj_DEGTORAD )
        call TriggerRegisterTrackableTrackEvent ( CellMouseTracked, this.track )
        call TriggerRegisterTrackableHitEvent   ( CellMouseHit, this.track )
        call SetHandleData ( this.track, CellKey, integer(this) )
        return this
    endmethod
   
    method operator maxX takes nothing returns real
        return this.x + ( SIZE / 2 )
    endmethod
    method operator minX takes nothing returns real
        return this.x - ( SIZE / 2 )
    endmethod
    method operator maxY takes nothing returns real
        return this.y + ( SIZE / 2 )
    endmethod
    method operator minY takes nothing returns real
        return this.y - ( SIZE / 2 )
    endmethod
   
    method setObject takes objecttype whichObject returns nothing
        if this.object != 0 then
            call this.object.destroy (  )
        endif
        set this.object = object.create ( this, whichObject )
    endmethod
   
    method setEntity takes entitytype whichEntity, real facing, real velocity returns nothing
        if whichEntity == 0 then
            call this.clearEntity (  )
            return
        endif
        if this.entity != 0 then
            call this.entity.destroy (  )
        endif
        set this.entityDir = facing
        set this.entityVel = velocity
        set this.entity = entity.create ( whichEntity, this, facing, velocity )
    endmethod
   
    method resetEntity takes nothing returns nothing
        local entitytype t
        if this.entity != 0 then
            set t = this.entity.type
            call this.entity.destroy (  )
            set this.entity = entity.create ( t, this, this.entityDir, this.entityVel )
        endif
    endmethod
   
    method clearEntity takes nothing returns nothing
        if this.entity != 0 then
            call this.entity.destroy (  )
            set this.entity = 0
            set this.entityDir = 0
            set this.entityVel = 0
        endif
    endmethod
   
    method containsPoint takes real x, real y returns boolean
        return IsPointInRegion ( this.reg, x, y )
    endmethod
   
    static method getCurrent takes nothing returns cell
        return CURRENT_CELL
    endmethod
   
    private static method onInit takes nothing returns nothing
        call TriggerAddAction ( cell.t, function cell.onEnter )
    endmethod
endstruct
//TESH.scrollpos=0
//TESH.alwaysfold=0
library Exit initializer init

globals
    destructable ExitDest = null
    private boolean open = false
endglobals

function SetExitLoc takes cell new returns nothing
    call RemoveDestructable ( ExitDest )
    set ExitDest = CreateDestructable ( 'B009', new.x, new.y, 270, 1.00, 0 )
    set LEVEL.exit = new
endfunction

function ResetExitAnimation takes nothing returns nothing
    set open = false
    call SetDestructableAnimation ( ExitDest, "stand" )
endfunction

private function ExitSentinel takes nothing returns nothing
    if GAMEPLAY then
        if Keys == MaxKeys and not open then
            call SetDestructableAnimation ( ExitDest, "death" )
            set open = true
        elseif Keys < MaxKeys and open then
            call SetDestructableAnimation ( ExitDest, "stand" )
            set open = false
        endif
    else
        call SetDestructableAnimation ( ExitDest, "stand" )
    endif
endfunction

private function init takes nothing returns nothing
    call TimerStart ( CreateTimer (  ), 0.15, true, function ExitSentinel )
endfunction

endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library SaveLevelToCache
globals
    gamecache Cache = InitGameCache ( "Jetpack" )
    constant string TILES   = "tile"
    constant string ENTITY  = "entity"
    constant string ENT_DIR = "dir"
    constant string ENT_VEL = "vel"
    constant string START   = "start"
    constant string EXIT    = "exit"
endglobals

function IsLevelIdSaved takes integer id returns boolean
    return HaveStoredInteger ( Cache, I2S ( id ), START )
endfunction

function SaveLevelToCache takes integer id returns nothing
    local integer x
    local integer y = 0
    loop
        set x = 0
        loop
            call StoreInteger ( Cache, I2S ( id ) + TILES   + I2S ( x ), I2S ( id ) + TILES   + I2S ( y ), integer (CELLS [ x ][ y ].object.type) )
            call StoreInteger ( Cache, I2S ( id ) + ENTITY  + I2S ( x ), I2S ( id ) + ENTITY  + I2S ( y ), integer (CELLS [ x ][ y ].entity.type) )
            call StoreReal    ( Cache, I2S ( id ) + ENT_DIR + I2S ( x ), I2S ( id ) + ENT_DIR + I2S ( y ), CELLS [ x ][ y ].entityDir )
            call StoreReal    ( Cache, I2S ( id ) + ENT_VEL + I2S ( x ), I2S ( id ) + ENT_VEL + I2S ( y ), CELLS [ x ][ y ].entityVel )
            set x = x + 1
            exitwhen x > WIDTH
        endloop
        set y = y + 1
        exitwhen y > HEIGHT
    endloop
    call StoreInteger ( Cache, I2S ( id ), START, integer (LEVEL.startLoc) )
    call StoreInteger ( Cache, I2S ( id ), EXIT,  integer (LEVEL.exit) )
    call SaveGameCache ( Cache )
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library LoadLevelFromCache
function LoadLevelFromCache takes integer id returns nothing
    local integer  x
    local integer  y = 0
    local real     dir
    local real     vel
    local level    this = SaveSlots [ id ]
    call ReloadGameCachesFromDisk (  )
    set LEVEL = this
    loop
        set x = 0
        loop
            call CELLS [ x ][ y ].setObject ( objecttype( GetStoredInteger ( Cache, I2S ( id ) + TILES + I2S ( x ), I2S ( id ) + TILES + I2S ( y ) ) ) )
            set dir = GetStoredReal ( Cache, I2S ( id ) + ENT_DIR + I2S ( x ), I2S ( id ) + ENT_DIR + I2S ( y ) )
            set vel = GetStoredReal ( Cache, I2S ( id ) + ENT_VEL + I2S ( x ), I2S ( id ) + ENT_VEL + I2S ( y ) )
            call CELLS [ x ][ y ].setEntity ( entitytype( GetStoredInteger ( Cache, I2S ( id ) + ENTITY + I2S ( x ), I2S ( id ) + ENTITY + I2S ( y ) ) ), dir, vel )
            set x = x + 1
            exitwhen x > WIDTH
        endloop
        set y = y + 1
        exitwhen y > HEIGHT
    endloop
    call this.setStartLoc ( cell ( GetStoredInteger ( Cache, I2S ( id ), START ) ) )
    call this.setExitLoc  ( cell ( GetStoredInteger ( Cache, I2S ( id ), EXIT ) ) )
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library SaveLoadHighScore initializer init

globals
    private constant string Prefix = "Highscore"
endglobals

function SaveHighScore takes boolean customLevel, integer levelId, integer score returns nothing
    local integer i = 0
    if customLevel then
        set i = 1
    endif
    call DisplayTextToPlayer ( GetLocalPlayer (  ), 0, 0, "|cff00ff00New level highscore!|r" )
    call StoreInteger ( Cache, Prefix + I2S ( i ), I2S ( levelId ), score )
    set HighScore [ i ][ levelId ] = score
    call SaveGameCache ( Cache )
endfunction

function LoadHighScores takes nothing returns nothing
    local integer i = 0
    call ReloadGameCachesFromDisk (  )
    loop
        set i = i + 1
        exitwhen i > MAX_LEVELS
        set HighScore [ 0 ][ i ] = GetStoredInteger ( Cache, Prefix + I2S ( 0 ), I2S ( i ) )
    endloop
   
    set i = 0
    loop
        set i = i + 1
        exitwhen i > MAX_SAVE_SLOTS
        set HighScore [ 1 ][ i ] = GetStoredInteger ( Cache, Prefix + I2S ( 1 ), I2S ( i ) )
    endloop
endfunction

private function init takes nothing returns nothing
    call TimerStart ( CreateTimer (  ), 0.11, false, function LoadHighScores )
endfunction

endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library Levels requires LevelBuilderLib

globals
    level array Levels
    integer     MAX_LEVELS
endglobals

function InitializeLevels takes nothing returns nothing
    // Initialize all preset levels
    local level this
   
    //! runtextmacro Level1 (  )
    //! runtextmacro Level2 (  )
    //! runtextmacro Level3 (  )
    //! runtextmacro Level4 (  )
    //! runtextmacro Level5 (  )
    //! runtextmacro Level6 (  )
    set MAX_LEVELS = 6
   
endfunction

endlibrary
//TESH.scrollpos=18
//TESH.alwaysfold=0
library LevelDialog initializer init requires Data

globals
    private dialog Dialog
    private key Key
endglobals

function OpenLevelSelection takes nothing returns nothing
    // Displays the preset level selection dialog
    if EDITOR then
        call EndEditor.execute (  )
    endif
    if GAMEPLAY then
        call EndGameplay.execute (  )
    endif
    set MENU = true
   
    call StartMusic ( gg_snd_MenuTrack )
   
    call ShowDestructable ( Blocker, true )
    call DialogSetMessage ( Dialog, "L E V E L   S E L E C T I O N" )
    call DialogDisplay ( Player ( 0 ), Dialog, true )
endfunction

private function LevelSelectionClick takes nothing returns nothing
    local integer i = GetHandleData ( GetClickedButton (  ), Key )
    if i == 0 then
        call ShowCustomLevelDialog.execute (  )
    elseif i == -1 then
        call OpenMenu.execute (  )
    else
        set MENU = false
        call ShowDestructable ( Blocker, false )
        set  ID = i
        set  CUSTOM = false
        call Levels [ i ].load.execute (  )
        call StartGameplay.execute (  )
    endif
endfunction

private function InitializeDialog takes nothing returns nothing
    local trigger t
    local button  b
    local integer i = 1
    set Dialog = DialogCreate (  )
   
    loop
        exitwhen Levels [ i ] == null
        set b = DialogAddButton ( Dialog, "|cffffcc00Level " + I2S ( i ) + "|r", 0 )
        call SetHandleData ( b, Key, i )
        set i = i + 1
    endloop
   
    set b = DialogAddButton ( Dialog, "Load custom level", 0 )
    call SetHandleData ( b, Key, 0 )
   
    set b = DialogAddButton ( Dialog, "Exit", 0 )
    call SetHandleData ( b, Key, -1 )
   
    set t = CreateTrigger (  )
    call TriggerRegisterDialogEvent ( t, Dialog )
    call TriggerAddAction ( t, function LevelSelectionClick )
endfunction

private function init takes nothing returns nothing
    call TimerStart ( CreateTimer (  ), 0, false, function InitializeDialog )
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library LevelBuilderLib
// ======================================================================================
// This library contains functions for creating preset levels
// ======================================================================================
/*
    FillCell              ( level this, integer col, integer row, objecttype whichType )
    FillCellsRow          ( level this, integer row, objecttype whichType )
    FillCellsCollumn      ( level this, integer col, objecttype whichType )
    FillEmptyCellsRow     ( level this, integer row, objecttype whichType )
    FillEmptyCellsCollumn ( level this, integer col, objecttype whichType )
    FillCellsBlock        ( level this, integer startRow, integer startCol, integer endRow, integer endCol, objecttype whichType )
    FillEmptyCellsBlock   ( level this, integer startRow, integer startCol, integer endRow, integer endCol, objecttype whichType )
    SetCellEntity         ( level this, integer col, integer row, entitytype whichType, real direction, real velocity )
*/

// ======================================================================================

function FillCell takes level this, integer col, integer row, objecttype whichType returns nothing
    call this.saveTypeCell ( col, row, whichType )
endfunction

function FillCellsRow takes level this, integer row, objecttype whichType returns nothing
    local integer x = 0
    loop
        call this.saveTypeCell ( x, row, whichType )
        set x = x + 1
        exitwhen x > WIDTH
    endloop
endfunction

function FillCellsCollumn takes level this, integer col, objecttype whichType returns nothing
    local integer y = 0
    loop
        call this.saveTypeCell ( col, y, whichType )
        set y = y + 1
        exitwhen y > HEIGHT
    endloop
endfunction

function FillEmptyCellsRow takes level this, integer row, objecttype whichType returns nothing
    local integer x = 0
    loop
        if this.getCellType ( x, row ) == OBJECT_NONE then
            call this.saveTypeCell ( x, row, whichType )
        endif
        set x = x + 1
        exitwhen x > WIDTH
    endloop
endfunction

function FillEmptyCellsCollumn takes level this, integer col, objecttype whichType returns nothing
    local integer y = 0
    loop
        if this.getCellType ( col, y ) == OBJECT_NONE then
            call this.saveTypeCell ( col, y, whichType )
        endif
        set y = y + 1
        exitwhen y > HEIGHT
    endloop
endfunction

function FillCellsBlock takes level this, integer startCol, integer startRow, integer endCol, integer endRow, objecttype whichType returns nothing
    local integer addx = 1
    local integer addy = 1
    local integer x
    local integer y = startRow
    if endCol < startCol then
        set addx = -1
    elseif endCol == startCol then
        set addx = 0
    endif
    if endRow < startRow then
        set addy = -1
    elseif endRow == startRow then
        set addy = 0
    endif
    loop
        set x = startCol
        loop
            call this.saveTypeCell ( x, y, whichType )
            exitwhen x == endCol
            set x = x + addx
        endloop
        exitwhen y == endRow
        set y = y + addy
    endloop
endfunction

function FillEmptyCellsBlock takes level this, integer startCol, integer startRow, integer endCol, integer endRow, objecttype whichType returns nothing
    local integer addx = 1
    local integer addy = 1
    local integer x
    local integer y = startRow
    if endCol < startCol then
        set addx = -1
    elseif endCol == startCol then
        set addx = 0
    endif
    if endRow < startRow then
        set addy = -1
    elseif endRow == startRow then
        set addy = 0
    endif
    loop
        set x = startCol
        loop
            if this.getCellType ( x, y ) == OBJECT_NONE then
                call this.saveTypeCell ( x, y, whichType )
            endif
            exitwhen x == endCol
            set x = x + addx
        endloop
        exitwhen y == endRow
        set y = y + addy
    endloop
endfunction

function SetCellEntity takes level this, integer col, integer row, entitytype whichType, real direction, real velocity returns nothing
    call this.saveEntityCell ( col, row, whichType, direction, velocity )
endfunction

endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0

//! textmacro Level1
   
    set this = level.create (  )
    set Levels [ 1 ]   = this
    set this.saveStart = CELLS [ 1 ][ 1 ]
    set this.saveExit  = CELLS [ 9 ][ 6 ]
   
    call FillCellsRow     ( this, 0, OBJECT_BRICKS )
   
    call FillCellsRow     ( this, 18, OBJECT_BRICKS )
   
    call FillCellsCollumn ( this, 0, OBJECT_BRICKS )
   
    call FillCellsCollumn ( this, 18, OBJECT_BRICKS )
   
    call FillCell ( this, 16, 1, OBJECT_BRICKS )
    call FillCell ( this, 17, 1, OBJECT_BRICKS )
    call FillCell ( this, 17, 2, OBJECT_BRICKS )
   
    call FillCell ( this, 3, 5, OBJECT_BRICKS )
    call FillCell ( this, 4, 5, OBJECT_BRICKS )
    call FillCell ( this, 5, 5, OBJECT_BRICKS )
   
    call FillCell ( this, 8, 5, OBJECT_BRICKS )
    call FillCell ( this, 9, 5, OBJECT_BRICKS )
    call FillCell ( this, 10, 5, OBJECT_BRICKS )
    call FillCell ( this, 10, 4, OBJECT_BRICKS )
    call FillCell ( this, 11, 4, OBJECT_BRICKS )
    call FillCell ( this, 11, 3, OBJECT_BRICKS )
    call FillCell ( this, 12, 3, OBJECT_BRICKS )
    call FillCell ( this, 12, 2, OBJECT_BRICKS )
    call FillCell ( this, 13, 2, OBJECT_BRICKS )
   
    call FillCell ( this, 6, 0, OBJECT_SPIKE_UP )
    call FillCell ( this, 7, 0, OBJECT_SPIKE_UP )
   
    call FillCell ( this, 3, 1, OBJECT_KEY )
    call FillCell ( this, 5, 1, OBJECT_KEY )
    call FillCell ( this, 8, 1, OBJECT_KEY )
    call FillCell ( this, 10, 1, OBJECT_KEY )
   
    call FillCell ( this, 3, 6, OBJECT_KEY )
    call FillCell ( this, 5, 6, OBJECT_KEY )
    call FillCell ( this, 8, 6, OBJECT_KEY )
    call FillCell ( this, 10, 6, OBJECT_KEY )
   
    call FillCell ( this, 13, 6, OBJECT_BRICKS )
    call FillCell ( this, 13, 7, OBJECT_FUEL1 )
   
    call FillCellsBlock ( this, 1, 17, 17, 17, OBJECT_SPIKE_DOWN )
    call FillCellsBlock ( this, 5, 14, 9, 14, OBJECT_BRICKS )
    call FillCellsBlock ( this, 5, 13, 9, 13, OBJECT_SPIKE_DOWN )
    call FillCellsBlock ( this, 5, 15, 9, 15, OBJECT_GOLD_WASE )
   
    call FillCellsBlock ( this, 4, 4, 4, 1, OBJECT_COLLUMN )
    call FillCellsBlock ( this, 9, 4, 9, 1, OBJECT_COLLUMN )
    call FillCellsBlock ( this, 13, 5, 13, 3, OBJECT_COLLUMN )
    call FillCell ( this, 13, 1, OBJECT_COLLUMN )
   
//! endtextmacro
 
//TESH.scrollpos=0
//TESH.alwaysfold=0

//! textmacro Level2
   
    set this = level.create (  )
    set Levels [ 2 ]   = this
    set this.saveStart = CELLS [ 17 ][ 10 ]
    set this.saveExit  = CELLS [ 1 ][ 10 ]
   
    call FillCellsBlock ( this, 0, 13, 18, 6, OBJECT_BRICKS )
    call FillCellsBlock ( this, 2, 5, 8, 2, OBJECT_BRICKS )
   
    call FillCellsBlock ( this, 1, 11, 17, 10, OBJECT_NONE )
    call FillCellsBlock ( this, 3, 9, 15, 8, OBJECT_NONE )
    call FillCellsBlock ( this, 5, 7, 5, 5, OBJECT_NONE )
   
    call FillCell ( this, 16, 10, OBJECT_FUEL1 )
   
    call FillCellsBlock ( this, 3, 8, 4, 8, OBJECT_SPIKE_UP )
    call FillCellsBlock ( this, 6, 8, 15, 8, OBJECT_SPIKE_UP )
   
    call FillCellsBlock ( this, 4, 11, 17, 11, OBJECT_SPIKE_DOWN )
    call FillCell ( this, 4, 5, OBJECT_SPIKE_DOWN )
    call FillCell ( this, 6, 5, OBJECT_SPIKE_DOWN )
   
    call FillCell ( this, 9, 8, OBJECT_KEY )
   
    call FillCellsBlock ( this, 4, 4, 6, 4, OBJECT_GOLD_WASE )

    call FillCell ( this, 4, 10, OBJECT_GOLD_COIN )
    call FillCell ( this, 6, 10, OBJECT_GOLD_COIN )
    call FillCell ( this, 8, 10, OBJECT_GOLD_COIN )
    call FillCell ( this, 10, 10, OBJECT_GOLD_COIN )
    call FillCell ( this, 12, 10, OBJECT_GOLD_COIN )
    call FillCell ( this, 14, 10, OBJECT_GOLD_COIN )
   
    call FillCell ( this, 5, 9, OBJECT_GOLD_COIN )
    call FillCell ( this, 7, 9, OBJECT_GOLD_COIN )
    call FillCell ( this, 9, 9, OBJECT_GOLD_COIN )
    call FillCell ( this, 11, 9, OBJECT_GOLD_COIN )
    call FillCell ( this, 13, 9, OBJECT_GOLD_COIN )
   
//! endtextmacro
 
//TESH.scrollpos=0
//TESH.alwaysfold=0

//! textmacro Level3
   
    set this = level.create (  )
    set Levels [ 3 ]   = this
    set this.saveStart = CELLS [ 1 ][ 1 ]
    set this.saveExit  = CELLS [ 1 ][ 3 ]
   
    call FillCellsRow     ( this, 0, OBJECT_BRICKS_DARK )
    call FillCellsRow     ( this, 18, OBJECT_BRICKS_DARK )
    call FillCellsCollumn ( this, 0, OBJECT_BRICKS_DARK )
    call FillCellsCollumn ( this, 18, OBJECT_BRICKS_DARK )
    call FillCellsRow     ( this, 2, OBJECT_BRICKS_DARK )
    call FillCellsRow     ( this, 9, OBJECT_BRICKS_DARK )
    call FillCellsBlock ( this, 14, 8, 17, 4, OBJECT_BRICKS_DARK )
    call FillCellsBlock ( this, 12, 8, 13, 7, OBJECT_BRICKS_DARK )
    call FillCellsBlock ( this, 12, 5, 12, 3, OBJECT_BRICKS_DARK )
    call FillCell ( this, 1, 5, OBJECT_BRICKS_DARK )
    call FillCell ( this, 3, 11, OBJECT_BRICKS_DARK )
    call FillCell ( this, 3, 10, OBJECT_BRICKS_DARK )
    call FillCell ( this, 5, 11, OBJECT_BRICKS_DARK )
    call FillCell ( this, 5, 10, OBJECT_BRICKS_DARK )
    call FillCell ( this, 15, 14, OBJECT_BRICKS_DARK )
    call FillCell ( this, 14, 12, OBJECT_BRICKS_DARK )
    call FillCell ( this, 15, 12, OBJECT_BRICKS_DARK )
   
    call FillCellsBlock ( this, 17, 3, 17, 1, OBJECT_LADDER )
    call FillCellsBlock ( this, 13, 6, 13, 3, OBJECT_LADDER )
    call FillCellsBlock ( this, 4, 12, 4, 9, OBJECT_LADDER )
   
    call FillCell ( this, 2, 1, OBJECT_COLLUMN )
    call FillCell ( this, 4, 1, OBJECT_COLLUMN )
    call FillCell ( this, 6, 1, OBJECT_COLLUMN )
    call FillCell ( this, 8, 1, OBJECT_COLLUMN )
    call FillCell ( this, 10, 1, OBJECT_COLLUMN )
    call FillCell ( this, 12, 1, OBJECT_COLLUMN )
    call FillCell ( this, 14, 1, OBJECT_COLLUMN )
    call FillCell ( this, 16, 1, OBJECT_COLLUMN )
    call FillCell ( this, 15, 13, OBJECT_COLLUMN )
   
    call FillCell ( this, 12, 6, OBJECT_FUEL2 )
   
    call FillCell ( this, 3, 1, OBJECT_KEY )
    call FillCell ( this, 5, 1, OBJECT_KEY )
    call FillCell ( this, 7, 1, OBJECT_KEY )
    call FillCell ( this, 9, 1, OBJECT_KEY )
    call FillCell ( this, 11, 1, OBJECT_KEY )
    call FillCell ( this, 13, 1, OBJECT_KEY )
    call FillCell ( this, 15, 1, OBJECT_KEY )
    call FillCell ( this, 15, 3, OBJECT_KEY )
    call FillCell ( this, 1, 6, OBJECT_KEY )
    call FillCell ( this, 14, 13, OBJECT_KEY )
   
    call FillCellsBlock ( this, 3, 3, 11, 3, OBJECT_SPIKE_UP )
    call FillCellsBlock ( this, 6, 10, 17, 10, OBJECT_SPIKE_UP )
    call FillCellsBlock ( this, 1, 10, 2, 10, OBJECT_SPIKE_UP )
   
    call FillCellsBlock ( this, 1, 17, 17, 17, OBJECT_SPIKE_DOWN )
    call FillCellsBlock ( this, 1, 8, 3, 8, OBJECT_SPIKE_DOWN )
    call FillCellsBlock ( this, 5, 8, 11, 8, OBJECT_SPIKE_DOWN )
   
    call FillCellsBlock ( this, 1, 16, 1, 11, OBJECT_SPIKE_RIGHT )
    call FillCellsBlock ( this, 17, 16, 17, 11, OBJECT_SPIKE_LEFT )
   
    call FillCell ( this, 14, 3, OBJECT_GOLD_WASE )
    call FillCell ( this, 16, 3, OBJECT_GOLD_WASE )
    call FillCell ( this, 15, 15, OBJECT_GOLD_WASE )
    call FillCell ( this, 9, 10, OBJECT_GOLD_WASE )
    call FillCell ( this, 12, 10, OBJECT_GOLD_WASE )
   
    call FillCell ( this, 2, 7, OBJECT_GOLD_COIN )
    call FillCell ( this, 4, 7, OBJECT_GOLD_COIN )
    call FillCell ( this, 6, 7, OBJECT_GOLD_COIN )
    call FillCell ( this, 8, 7, OBJECT_GOLD_COIN )
    call FillCell ( this, 10, 7, OBJECT_GOLD_COIN )
   
    call FillCell ( this, 3, 6, OBJECT_GOLD_COIN )
    call FillCell ( this, 5, 6, OBJECT_GOLD_COIN )
    call FillCell ( this, 7, 6, OBJECT_GOLD_COIN )
    call FillCell ( this, 9, 6, OBJECT_GOLD_COIN )
   
    call FillCell ( this, 2, 5, OBJECT_GOLD_COIN )
    call FillCell ( this, 4, 5, OBJECT_GOLD_COIN )
    call FillCell ( this, 6, 5, OBJECT_GOLD_COIN )
    call FillCell ( this, 8, 5, OBJECT_GOLD_COIN )
    call FillCell ( this, 10, 5, OBJECT_GOLD_COIN )
   
    call FillCell ( this, 7, 15, OBJECT_GOLD_COIN )
    call FillCell ( this, 9, 15, OBJECT_GOLD_COIN )
    call FillCell ( this, 11, 15, OBJECT_GOLD_COIN )
   
    call FillCell ( this, 6, 14, OBJECT_GOLD_COIN )
    call FillCell ( this, 8, 14, OBJECT_GOLD_COIN )
    call FillCell ( this, 10, 14, OBJECT_GOLD_COIN )
    call FillCell ( this, 12, 14, OBJECT_GOLD_COIN )
   
    call FillCell ( this, 7, 13, OBJECT_GOLD_COIN )
    call FillCell ( this, 9, 13, OBJECT_GOLD_COIN )
    call FillCell ( this, 11, 13, OBJECT_GOLD_COIN )
   
    call SetCellEntity ( this, 7, 14, ENTITY_DEATH_WHEEL, 90, 512 )
   
//! endtextmacro
 
//TESH.scrollpos=0
//TESH.alwaysfold=0

//! textmacro Level4
   
    set this = level.create (  )
    set Levels [ 4 ]   = this
    set this.saveStart = CELLS [ 17 ][ 17 ]
    set this.saveExit  = CELLS [ 9 ][ 9 ]
   
    call FillCellsRow     ( this, 0, OBJECT_SPIKE_UP )
    call FillCellsRow     ( this, 18, OBJECT_BRICKS_DARK )
    call FillCellsCollumn ( this, 0, OBJECT_BRICKS_DARK )
    call FillCellsCollumn ( this, 18, OBJECT_BRICKS_DARK )
    call FillCellsCollumn ( this, 12, OBJECT_BRICKS_DARK )
   
    call FillCellsBlock ( this, 10, 8, 10, 0, OBJECT_BRICKS_DARK )
    call FillCellsBlock ( this, 1, 3, 3, 3, OBJECT_BRICKS_DARK )
    call FillCellsBlock ( this, 1, 12, 5, 12, OBJECT_BRICKS_DARK )
    call FillCellsBlock ( this, 5, 13, 5, 16, OBJECT_BRICKS_DARK )
    call FillCellsBlock ( this, 3, 17, 3, 14, OBJECT_BRICKS_DARK )
    call FillCellsBlock ( this, 8, 16, 11, 16, OBJECT_BRICKS_DARK )
    call FillCellsBlock ( this, 8, 14, 11, 14, OBJECT_BRICKS_DARK )
    call FillCellsBlock ( this, 8, 12, 11, 12, OBJECT_BRICKS_DARK )
    call FillCellsBlock ( this, 8, 10, 11, 10, OBJECT_BRICKS_DARK )
    call FillCellsBlock ( this, 17, 16, 16, 16, OBJECT_BRICKS_DARK )
    call FillCellsBlock ( this, 15, 11, 17, 11, OBJECT_BRICKS_DARK )
    call FillCellsBlock ( this, 8, 8, 9, 8, OBJECT_BRICKS_DARK )
   
    call FillCell ( this, 1, 16, OBJECT_BRICKS_DARK )
    call FillCell ( this, 2, 14, OBJECT_BRICKS_DARK )
    call FillCell ( this, 1, 1, OBJECT_BRICKS_DARK )
    call FillCell ( this, 11, 0, OBJECT_BRICKS_DARK )
    call FillCell ( this, 13, 11, OBJECT_BRICKS_DARK )
    call FillCell ( this, 8, 9, OBJECT_BRICKS_DARK )
   
    call FillCell ( this, 8, 17, OBJECT_COLLUMN )
    call FillCell ( this, 8, 15, OBJECT_COLLUMN )
    call FillCell ( this, 8, 13, OBJECT_COLLUMN )
    call FillCell ( this, 8, 11, OBJECT_COLLUMN )
    call FillCell ( this, 10, 1, OBJECT_COLLUMN )
    call FillCell ( this, 12, 1, OBJECT_COLLUMN )
   
    call FillCell ( this, 14, 11, OBJECT_FUEL2 )
   
    call FillCellsBlock ( this, 1, 4, 2, 4, OBJECT_SPIKE_UP )
    call FillCellsBlock ( this, 15, 12, 17, 12, OBJECT_SPIKE_UP )
    call FillCell ( this, 13, 12, OBJECT_SPIKE_UP )
   
    call FillCellsBlock ( this, 15, 10, 17, 10, OBJECT_SPIKE_DOWN )
    call FillCell ( this, 13, 10, OBJECT_SPIKE_DOWN )
    call FillCell ( this, 4, 11, OBJECT_SPIKE_DOWN )
   
    call FillCell ( this, 1, 10, OBJECT_SPIKE_RIGHT )
    call FillCell ( this, 1, 8, OBJECT_SPIKE_RIGHT )
    call FillCell ( this, 1, 6, OBJECT_SPIKE_RIGHT )
   
    call FillCell ( this, 7, 9, OBJECT_SPIKE_LEFT )
    call FillCell ( this, 9, 6, OBJECT_SPIKE_LEFT )
    call FillCell ( this, 9, 4, OBJECT_SPIKE_LEFT )
   
    call FillCell ( this, 1, 17, OBJECT_KEY )
    call FillCell ( this, 11, 17, OBJECT_KEY )
    call FillCell ( this, 11, 15, OBJECT_KEY )
    call FillCell ( this, 11, 13, OBJECT_KEY )
    call FillCell ( this, 11, 11, OBJECT_KEY )
    call FillCell ( this, 3, 4, OBJECT_KEY )

    call FillCell ( this, 2, 15, OBJECT_GOLD_WASE )
    call FillCell ( this, 1, 2, OBJECT_GOLD_WASE )
    call FillCell ( this, 9, 17, OBJECT_GOLD_WASE )
    call FillCell ( this, 9, 15, OBJECT_GOLD_WASE )
    call FillCell ( this, 9, 13, OBJECT_GOLD_WASE )
    call FillCell ( this, 9, 11, OBJECT_GOLD_WASE )
   
    call FillCell ( this, 1, 13, OBJECT_GOLD_COIN )
    call FillCell ( this, 4, 13, OBJECT_GOLD_COIN )
    call FillCell ( this, 3, 9, OBJECT_GOLD_COIN )
    call FillCell ( this, 3, 7, OBJECT_GOLD_COIN )
    call FillCell ( this, 5, 9, OBJECT_GOLD_COIN )
    call FillCell ( this, 5, 7, OBJECT_GOLD_COIN )
    call FillCell ( this, 7, 7, OBJECT_GOLD_COIN )
    call FillCell ( this, 5, 5, OBJECT_GOLD_COIN )
    call FillCell ( this, 5, 3, OBJECT_GOLD_COIN )
    call FillCell ( this, 7, 5, OBJECT_GOLD_COIN )
    call FillCell ( this, 7, 3, OBJECT_GOLD_COIN )
    call FillCell ( this, 11, 3, OBJECT_GOLD_COIN )
    call FillCell ( this, 11, 5, OBJECT_GOLD_COIN )
    call FillCell ( this, 11, 7, OBJECT_GOLD_COIN )
    call FillCell ( this, 14, 7, OBJECT_GOLD_COIN )
    call FillCell ( this, 14, 5, OBJECT_GOLD_COIN )
    call FillCell ( this, 14, 3, OBJECT_GOLD_COIN )
    call FillCell ( this, 16, 7, OBJECT_GOLD_COIN )
    call FillCell ( this, 16, 5, OBJECT_GOLD_COIN )
    call FillCell ( this, 16, 3, OBJECT_GOLD_COIN )
   
    call SetCellEntity ( this, 9, 3, ENTITY_DEATH_WHEEL, 180, 512 )
    call SetCellEntity ( this, 17, 3, ENTITY_ROCKET, 90, 512 )
   
//! endtextmacro
 
//TESH.scrollpos=0
//TESH.alwaysfold=0

//! textmacro Level5
   
    set this = level.create (  )
    set Levels [ 5 ]   = this
    set this.saveStart = CELLS [ 6 ][ 14 ]
    set this.saveExit  = CELLS [ 15 ][ 3 ]
   
    call FillCellsBlock ( this, 1, 17, 17, 1, OBJECT_IRON_FENCE )
   
    call FillCellsRow     ( this, 0, OBJECT_BRICKS_DARK )
    call FillCellsRow     ( this, 18, OBJECT_BRICKS_DARK )
    call FillCellsRow     ( this, 9, OBJECT_BRICKS_DARK )
    call FillCellsCollumn ( this, 0, OBJECT_BRICKS_DARK )
    call FillCellsCollumn ( this, 18, OBJECT_BRICKS_DARK )
    call FillCellsCollumn ( this, 9, OBJECT_BRICKS_DARK )
   
    call FillCellsBlock ( this, 2, 16, 7, 11, OBJECT_BRICKS_DARK )
    call FillCellsBlock ( this, 11, 16, 16, 11, OBJECT_BRICKS_DARK )
    call FillCellsBlock ( this, 2, 7, 7, 2, OBJECT_BRICKS_DARK )
    call FillCellsBlock ( this, 11, 7, 16, 2, OBJECT_BRICKS_DARK )
   
    call FillCellsBlock ( this, 3, 15, 15, 15, OBJECT_NONE )
    call FillCellsBlock ( this, 3, 3, 15, 3, OBJECT_NONE )
    call FillCellsBlock ( this, 3, 14, 3, 4, OBJECT_NONE )
    call FillCellsBlock ( this, 15, 14, 15, 4, OBJECT_NONE )
    call FillCellsBlock ( this, 4, 14, 5, 12, OBJECT_NONE )
    call FillCellsBlock ( this, 13, 14, 14, 12, OBJECT_NONE )
    call FillCellsBlock ( this, 4, 6, 5, 4, OBJECT_NONE )
    call FillCellsBlock ( this, 13, 6, 14, 4, OBJECT_NONE )
   
    call FillCell ( this, 6, 14, OBJECT_FUEL1 )
   
    call FillCell ( this, 6, 12, OBJECT_KEY )
    call FillCell ( this, 12, 12, OBJECT_KEY )
    call FillCell ( this, 6, 6, OBJECT_KEY )
    call FillCell ( this, 12, 6, OBJECT_KEY )

    call FillCell ( this, 7, 15, OBJECT_GOLD_WASE )
    call FillCell ( this, 5, 13, OBJECT_GOLD_WASE )
    call FillCell ( this, 4, 12, OBJECT_GOLD_WASE )
    call FillCell ( this, 3, 11, OBJECT_GOLD_WASE )
   
    call FillCell ( this, 3, 7, OBJECT_GOLD_WASE )
    call FillCell ( this, 4, 6, OBJECT_GOLD_WASE )
    call FillCell ( this, 5, 5, OBJECT_GOLD_WASE )
    call FillCell ( this, 6, 4, OBJECT_GOLD_WASE )
    call FillCell ( this, 7, 3, OBJECT_GOLD_WASE )
   
    call FillCell ( this, 11, 3, OBJECT_GOLD_WASE )
    call FillCell ( this, 12, 4, OBJECT_GOLD_WASE )
    call FillCell ( this, 13, 5, OBJECT_GOLD_WASE )
    call FillCell ( this, 14, 6, OBJECT_GOLD_WASE )
    call FillCell ( this, 15, 7, OBJECT_GOLD_WASE )
   
    call FillCell ( this, 15, 11, OBJECT_GOLD_WASE )
    call FillCell ( this, 14, 12, OBJECT_GOLD_WASE )
    call FillCell ( this, 13, 13, OBJECT_GOLD_WASE )
    call FillCell ( this, 12, 14, OBJECT_GOLD_WASE )
    call FillCell ( this, 11, 15, OBJECT_GOLD_WASE )
   
    call SetCellEntity ( this, 8, 12, ENTITY_ROCKET, 90, 1024 )
    call SetCellEntity ( this, 8, 3, ENTITY_ROCKET, 90, 1024 )
    call SetCellEntity ( this, 17, 3, ENTITY_ROCKET, 90, 1024 )
    call SetCellEntity ( this, 17, 12, ENTITY_ROCKET, 90, 1024 )
    call SetCellEntity ( this, 15, 9, ENTITY_ROCKET, 90, 1024 )
   
//! endtextmacro
 
//TESH.scrollpos=0
//TESH.alwaysfold=0

//! textmacro Level6
   
    set this = level.create (  )
    set Levels [ 6 ]   = this
    set this.saveStart = CELLS [ 10 ][ 17 ]
    set this.saveExit  = CELLS [ 16 ][ 1 ]
   
    call FillCellsRow     ( this, 0, OBJECT_SPIKE_UP )
    call FillCellsRow     ( this, 18, OBJECT_BRICKS_DARK )
    call FillCellsRow     ( this, 16, OBJECT_BRICKS_DARK )
    call FillCellsCollumn ( this, 0, OBJECT_BRICKS_DARK )
    call FillCellsCollumn ( this, 18, OBJECT_BRICKS_DARK )
   
    call FillCellsBlock ( this, 1, 11, 4, 11, OBJECT_BRICKS_DARK )
    call FillCellsBlock ( this, 4, 15, 4, 12, OBJECT_BRICKS_DARK )
    call FillCellsBlock ( this, 5, 14, 10, 14, OBJECT_BRICKS_DARK )
    call FillCellsBlock ( this, 1, 9, 9, 9, OBJECT_BRICKS_DARK )
    call FillCellsBlock ( this, 2, 8, 10, 5, OBJECT_BRICKS_DARK )
    call FillCellsBlock ( this, 2, 4, 14, 4, OBJECT_BRICKS_DARK )
    call FillCellsBlock ( this, 14, 5, 17, 5, OBJECT_BRICKS_DARK )
    call FillCellsBlock ( this, 15, 0, 17, 0, OBJECT_BRICKS_DARK )
   
    call FillCell ( this, 17, 3, OBJECT_BRICKS_DARK )
    call FillCell ( this, 5, 0, OBJECT_BRICKS_DARK )
    call FillCell ( this, 11, 0, OBJECT_BRICKS_DARK )
    call FillCell ( this, 12, 10, OBJECT_BRICKS_DARK )
    call FillCell ( this, 15, 11, OBJECT_BRICKS_DARK )
   
    call FillCellsBlock ( this, 3, 17, 15, 17, OBJECT_IRON_FENCE )
    call FillCellsBlock ( this, 6, 15, 10, 15, OBJECT_IRON_FENCE )
    call FillCellsBlock ( this, 3, 10, 4, 10, OBJECT_IRON_FENCE )
    call FillCellsBlock ( this, 8, 13, 9, 10, OBJECT_IRON_FENCE )
    call FillCellsBlock ( this, 6, 9, 7, 9, OBJECT_IRON_FENCE )
    call FillCellsBlock ( this, 6, 8, 10, 5, OBJECT_IRON_FENCE )
    call FillCellsBlock ( this, 7, 4, 10, 4, OBJECT_IRON_FENCE )
    call FillCellsBlock ( this, 1, 6, 1, 4, OBJECT_IRON_FENCE )
   
    call FillCell ( this, 2, 16, OBJECT_IRON_FENCE )
    call FillCell ( this, 2, 11, OBJECT_IRON_FENCE )
    call FillCell ( this, 10, 9, OBJECT_IRON_FENCE )
    call FillCell ( this, 5, 5, OBJECT_IRON_FENCE )
   
    call FillCellsBlock ( this, 17, 17, 17, 12, OBJECT_LADDER )
    call FillCellsBlock ( this, 4, 7, 4, 5, OBJECT_LADDER )
   
    call FillCellsBlock ( this, 3, 15, 3, 12, OBJECT_SPIKE_LEFT )
    call FillCellsBlock ( this, 1, 15, 1, 12, OBJECT_SPIKE_RIGHT )
    call FillCellsBlock ( this, 5, 13, 7, 13, OBJECT_SPIKE_DOWN )
    call FillCellsBlock ( this, 1, 8, 4, 8, OBJECT_SPIKE_DOWN )
    call FillCellsBlock ( this, 11, 5, 12, 5, OBJECT_SPIKE_UP )
    call FillCellsBlock ( this, 14, 6, 17, 6, OBJECT_SPIKE_UP )
   
    call FillCell ( this, 10, 13, OBJECT_SPIKE_DOWN )
    call FillCell ( this, 12, 9, OBJECT_SPIKE_DOWN )
    call FillCell ( this, 15, 10, OBJECT_SPIKE_DOWN )
   
    call FillCell ( this, 1, 17, OBJECT_KEY )
    call FillCell ( this, 5, 15, OBJECT_KEY )
    call FillCell ( this, 1, 10, OBJECT_KEY )
    call FillCell ( this, 12, 11, OBJECT_KEY )
    call FillCell ( this, 15, 12, OBJECT_KEY )
    call FillCell ( this, 5, 1, OBJECT_KEY )
    call FillCell ( this, 11, 1, OBJECT_KEY )
   
    call FillCell ( this, 16, 17, OBJECT_FUEL1 )
    call FillCell ( this, 15, 8, OBJECT_FUEL1 )
   
    call FillCell ( this, 3, 5, OBJECT_GOLD_WASE )
    call FillCell ( this, 2, 7, OBJECT_GOLD_WASE )
    call FillCell ( this, 3, 7, OBJECT_GOLD_WASE )
    call FillCell ( this, 13, 5, OBJECT_GOLD_WASE )
    call FillCell ( this, 17, 4, OBJECT_GOLD_WASE )
    call FillCell ( this, 15, 1, OBJECT_GOLD_WASE )
    call FillCell ( this, 17, 1, OBJECT_GOLD_WASE )
   
    call FillCell ( this, 16, 8, OBJECT_GOLD_COIN )
   
    call FillCellsBlock ( this, 2, 15, 2, 12, OBJECT_GOLD_COIN )
    call FillCellsBlock ( this, 6, 11, 7, 11, OBJECT_GOLD_COIN )
    call FillCellsBlock ( this, 10, 11, 11, 11, OBJECT_GOLD_COIN )
    call FillCellsBlock ( this, 13, 12, 14, 12, OBJECT_GOLD_COIN )
    call FillCellsBlock ( this, 17, 11, 17, 9, OBJECT_GOLD_COIN )
    call FillCellsBlock ( this, 2, 2, 3, 2, OBJECT_GOLD_COIN )
    call FillCellsBlock ( this, 7, 2, 9, 2, OBJECT_GOLD_COIN )
    call FillCellsBlock ( this, 12, 2, 14, 2, OBJECT_GOLD_COIN )
   
    call SetCellEntity ( this, 4, 3, ENTITY_ROCKET, 180, 256 )

    call SetCellEntity ( this, 17, 17, ENTITY_DEATH_WHEEL, 180, 256 )
    call SetCellEntity ( this, 13, 3, ENTITY_DEATH_WHEEL, 270, 256 )
    call SetCellEntity ( this, 12, 14, ENTITY_DEATH_WHEEL, 270, 256 )
   
    call SetCellEntity ( this, 8, 13, ENTITY_DEATH_WHEEL, 0, 0 )
    call SetCellEntity ( this, 9, 13, ENTITY_DEATH_WHEEL, 0, 0 )
    call SetCellEntity ( this, 6, 8, ENTITY_DEATH_WHEEL, 0, 0 )
    call SetCellEntity ( this, 7, 8, ENTITY_DEATH_WHEEL, 0, 0 )
    call SetCellEntity ( this, 8, 8, ENTITY_DEATH_WHEEL, 0, 0 )
    call SetCellEntity ( this, 9, 8, ENTITY_DEATH_WHEEL, 0, 0 )
    call SetCellEntity ( this, 10, 8, ENTITY_DEATH_WHEEL, 0, 0 )
    call SetCellEntity ( this, 11, 9, ENTITY_DEATH_WHEEL, 0, 0 )
    call SetCellEntity ( this, 13, 9, ENTITY_DEATH_WHEEL, 0, 0 )
    call SetCellEntity ( this, 14, 10, ENTITY_DEATH_WHEEL, 0, 0 )
    call SetCellEntity ( this, 7, 4, ENTITY_DEATH_WHEEL, 0, 0 )
    call SetCellEntity ( this, 8, 4, ENTITY_DEATH_WHEEL, 0, 0 )
    call SetCellEntity ( this, 9, 4, ENTITY_DEATH_WHEEL, 0, 0 )
    call SetCellEntity ( this, 10, 4, ENTITY_DEATH_WHEEL, 0, 0 )
   
//! endtextmacro
 
//TESH.scrollpos=0
//TESH.alwaysfold=0
library CustomLevel initializer init requires Data, SaveSlots
globals
    private dialog Dialog = null
    private button array Buttons
    private trigger Trig = CreateTrigger (  )
endglobals

function ShowCustomLevelDialog takes nothing returns nothing
    // Displays the custom level selection dialog
    local integer i = 0
    if Dialog == null then
        set Dialog = DialogCreate (  )
        call TriggerRegisterDialogEvent ( Trig, Dialog )
    else
        call DialogClear ( Dialog )
    endif
    loop
        if not IsSlotEmpty [ i ] or IsLevelIdSaved ( i ) then
            // Saved level detected, create load button
            set Buttons [ i ] = DialogAddButton ( Dialog, "Level #" + I2S ( i + 1 ), 0 )
        endif
        set i = i + 1
        exitwhen i >= MAX_SAVE_SLOTS
    endloop
    call DialogAddButton ( Dialog, "Exit", 0 )
    call DialogSetMessage ( Dialog, "Load custom level" )
    call DialogDisplay ( Player ( 0 ), Dialog, true )
endfunction

private function OnCustomLevelSelection takes nothing returns nothing
    // A dialog button is pressed
    local button b  = GetClickedButton (  )
    local integer i = MAX_SAVE_SLOTS
    loop
        // Find the pressed button
        set i = i - 1
        exitwhen i < 0 or b == Buttons [ i ]
    endloop
    if i >= 0 then
        // A level load button was pressed, load and start level
        set MENU = false
        call ShowDestructable ( Blocker, false )
        set  ID = i + 1
        set  CUSTOM = true
        call LoadFromSlot ( i )
        call StartGameplay.execute (  )
    else
        // The return button was pressed, returns to preset level selection
        call OpenLevelSelection.execute (  )
    endif
    set b = null
endfunction

private function init takes nothing returns nothing
    call TriggerAddAction ( Trig, function OnCustomLevelSelection )
endfunction

endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
struct objecttype
    implement StackModule
    integer destId       = 0
    real    facing       = 270
    real    scale        = 1.00
    //
    boolean walkable     = true
    boolean ladder       = false
    boolean destructable = false
    boolean lethal       = false
    string  deathMethod  = DEATH_METHOD_SPLATTER
    boolean isKey        = false
    boolean isTeleporter = false
    boolean isLever      = false
    boolean isSwitch     = false
    objecttype openType  = 0
    //
    real    health       = 0
    real    fuel         = 0
    real    addXVel      = 0
    real    addYVel      = 0
    integer score        = 0
    string  name
    static method create takes integer destId, real facing, string name returns objecttype
        local objecttype this = objecttype.allocate (  )
        set  this.facing = facing
        call this.addStack (  )
        set  this.destId = destId
        set  this.name   = name
        call tile.create ( this )
        return this
    endmethod
    method onDestroy takes nothing returns nothing
        call this.removeStack (  )
    endmethod
endstruct

struct object
    implement StackModule
    real x
    real y
    destructable d
    objecttype type
    boolean executed  = false
    boolean collision = true
    boolean used      = false
    boolean lethal    = false
    cell cell
    static method create takes cell whichCell, objecttype whichType returns object
        local object this = object.allocate (  )
        set  this.x = whichCell.x
        set  this.y = whichCell.y
        call this.addStack (  )
        set  this.type = whichType
        set  this.cell = whichCell
        set  this.lethal = whichType.lethal
        if whichType.isKey then
            set MaxKeys = MaxKeys + 1
        endif
        set  this.d = CreateDestructable ( whichType.destId, this.x, this.y, whichType.facing, whichType.scale, 0 )
        return this
    endmethod
    method onDestroy takes nothing returns nothing
        call RemoveDestructable ( this.d )
        if this.type.isKey then
            set MaxKeys = MaxKeys - 1
        endif
        call this.removeStack (  )
    endmethod
endstruct
//TESH.scrollpos=182
//TESH.alwaysfold=0
globals
    // Object type list
    objecttype OBJECT_NONE
    objecttype OBJECT_KEY
    objecttype OBJECT_FUEL1
    objecttype OBJECT_FUEL2
    objecttype OBJECT_LADDER
    objecttype OBJECT_BRICKS
    objecttype OBJECT_SPIKE_UP
    objecttype OBJECT_SPIKE_DOWN
    objecttype OBJECT_SPIKE_LEFT
    objecttype OBJECT_SPIKE_RIGHT
    objecttype OBJECT_BRICKS_DARK
    objecttype OBJECT_WOOD_FENCE
    objecttype OBJECT_GOLD_COIN
    objecttype OBJECT_GOLD_WASE
    objecttype OBJECT_COLLUMN
    objecttype OBJECT_CRATE
    objecttype OBJECT_IRON_FENCE
    objecttype OBJECT_TELEPORTER_RED
    objecttype OBJECT_TELEPORTER_GREEN
    objecttype OBJECT_TELEPORTER_YELLOW
    objecttype OBJECT_BARRIER_RED
    objecttype OBJECT_LEVER_RED
    objecttype OBJECT_SWITCH_RED
    objecttype OBJECT_BARRIER_BLUE
    objecttype OBJECT_LEVER_BLUE
    objecttype OBJECT_SWITCH_BLUE
    objecttype OBJECT_BARRIER_TEAL
    objecttype OBJECT_LEVER_TEAL
    objecttype OBJECT_SWITCH_TEAL
    objecttype OBJECT_FIRE_TRAP
    objecttype OBJECT_LIGHT_SWITCH
    objecttype OBJECT_INVERT_FIELD
    objecttype OBJECT_VINES
    objecttype OBJECT_ENTITY_FREEZER
endglobals
function InitializeObjectTypes takes nothing returns nothing
    local objecttype this
   
    // Empty object
    set this = objecttype.create ( 0, 0, "Clear tile" )
    set OBJECT_NONE = this
   
    // Key
    set this = objecttype.create ( 'B00A', 270, "Key" )
    set this.isKey = true
    set this.score = 50
    set OBJECT_KEY = this
   
    // Fuel (50%)
    set this = objecttype.create ( 'B00D', 270, "Fuel tank (50%)" )
    set this.fuel  = 50
    set this.score = 50
    set OBJECT_FUEL1 = this
   
    // Fuel (100%)
    set this = objecttype.create ( 'B00E', 270, "Fuel tank (100%)" )
    set this.fuel  = 100
    set this.score = 100
    set OBJECT_FUEL2 = this
   
    // Ladder
    set this = objecttype.create ( 'B007', 270, "Ladder" )
    set this.ladder = true
    set OBJECT_LADDER = this
   
    // Brick Wall
    set this = objecttype.create ( 'B002', 270, "Red Bricks" )
    set this.walkable = false
    set OBJECT_BRICKS = this
   
    // Spikes (up)
    set this = objecttype.create ( 'B00B', 270, "Spikes (north)" )
    set this.lethal = true
    set OBJECT_SPIKE_UP = this
   
    // Spikes (down)
    set this = objecttype.create ( 'B00B', 90, "Spikes (south)" )
    set this.lethal = true
    set OBJECT_SPIKE_DOWN = this
   
    // Spikes (left)
    set this = objecttype.create ( 'B00B', 0, "Spikes (west)" )
    set this.lethal = true
    set OBJECT_SPIKE_LEFT = this
   
    // Spikes (right)
    set this = objecttype.create ( 'B00B', 180, "Spikes (east)" )
    set this.lethal = true
    set OBJECT_SPIKE_RIGHT = this
   
    // Dark Brick Wall
    set this = objecttype.create ( 'B00I', 270, "Dark Bricks" )
    set this.walkable = false
    set OBJECT_BRICKS_DARK = this
   
    // Wooden Fence
    set this = objecttype.create ( 'B006', 270, "Wooden fence" )
    set OBJECT_WOOD_FENCE = this
   
    // Gold Coin
    set this = objecttype.create ( 'B00H', 270, "Gold coin" )
    set this.score = 150
    set OBJECT_GOLD_COIN = this
   
    // Gold Wase
    set this = objecttype.create ( 'B00F', 270, "Gold wase" )
    set this.score = 300
    set OBJECT_GOLD_WASE = this
   
    // Collumn
    set this = objecttype.create ( 'B00G', 270, "Collumn" )
    set OBJECT_COLLUMN = this
   
    // Crate
    set this = objecttype.create ( 'B00P', 270, "Crate" )
    set this.walkable = false
    set OBJECT_CRATE = this
   
    // Iron Fence
    set this = objecttype.create ( 'B00L', 270, "Iron fence" )
    set OBJECT_IRON_FENCE = this
   
    // Teleporter (Red)
    set this = objecttype.create ( 'B00M', 270, "Teleporter (Red)" )
    set this.isTeleporter = true
    set OBJECT_TELEPORTER_RED = this
   
    // Teleporter (Green)
    set this = objecttype.create ( 'B00N', 270, "Teleporter (Green)" )
    set this.isTeleporter = true
    set OBJECT_TELEPORTER_GREEN = this
   
    // Teleporter (Yellow)
    set this = objecttype.create ( 'B00O', 270, "Teleporter (Yellow)" )
    set this.isTeleporter = true
    set OBJECT_TELEPORTER_YELLOW = this
   
    // Barrier (Red)
    set this = objecttype.create ( 'B00T', 270, "Barrier (Red)" )
    set this.walkable = false
    set OBJECT_BARRIER_RED = this
   
    // Lever (Red)
    set this = objecttype.create ( 'B00Q', 270, "Lever (Red)" )
    set this.isLever = true
    set this.openType = OBJECT_BARRIER_RED
    set OBJECT_LEVER_RED = this
   
    // Switch (Red)
    set this = objecttype.create ( 'B00W', 270, "Switch (Red)" )
    set this.isSwitch = true
    set this.openType = OBJECT_BARRIER_RED
    set OBJECT_SWITCH_RED = this
   
    // Fire Trap
    set this = objecttype.create ( 'B00Z', 270, "Fire Trap" )
    set this.lethal = true
    set this.deathMethod = DEATH_METHOD_SMOKE
    set OBJECT_FIRE_TRAP = this
   
    // Light Switch
    set this = objecttype.create ( 'B012', 270, "Light Switch" )
    set OBJECT_LIGHT_SWITCH = this
   
    // Barrier (Blue)
    set this = objecttype.create ( 'B00U', 270, "Barrier (Blue)" )
    set this.walkable = false
    set OBJECT_BARRIER_BLUE = this
   
    // Lever (Blue)
    set this = objecttype.create ( 'B00R', 270, "Lever (Blue)" )
    set this.isLever = true
    set this.openType = OBJECT_BARRIER_BLUE
    set OBJECT_LEVER_BLUE = this
   
    // Switch (Blue)
    set this = objecttype.create ( 'B00X', 270, "Switch (Blue)" )
    set this.isSwitch = true
    set this.openType = OBJECT_BARRIER_BLUE
    set OBJECT_SWITCH_BLUE = this
   
    // Control Invert Field
    set this = objecttype.create ( 'B013', 270, "Control Invert Field" )
    set OBJECT_INVERT_FIELD = this
   
    // Entity Freezer
    set this = objecttype.create ( 'B015', 270, "Entity Freezer" )
    set OBJECT_ENTITY_FREEZER = this
   
    // Barrier (Teal)
    set this = objecttype.create ( 'B00V', 270, "Barrier (Teal)" )
    set this.walkable = false
    set OBJECT_BARRIER_TEAL = this
   
    // Lever (Teal)
    set this = objecttype.create ( 'B00S', 270, "Lever (Teal)" )
    set this.isLever = true
    set this.openType = OBJECT_BARRIER_TEAL
    set OBJECT_LEVER_TEAL = this
   
    // Switch (Teal)
    set this = objecttype.create ( 'B00Y', 270, "Switch (Teal)" )
    set this.isSwitch = true
    set this.openType = OBJECT_BARRIER_TEAL
    set OBJECT_SWITCH_TEAL = this
   
    // Vines
    set this = objecttype.create ( 'B014', 270, "Vines" )
    set OBJECT_VINES = this

endfunction
//TESH.scrollpos=0
//TESH.alwaysfold=0
library Entities requires Collision, Data

function ClearAllEntities takes nothing returns nothing
    // Removes all active entities from the level
    call entity.clearAll (  )
endfunction

function ClearAllEntitiesOfType takes entitytype whichType returns nothing
    // Removes all entities of a certain type from the level
    call entity.clearAllOfType ( whichType )
endfunction

function PauseAllEntities takes boolean flag returns nothing
    // Pauses/unpauses all entities
    set ENTITY_PAUSE = flag
endfunction

function UnfreezeEntities takes nothing returns nothing
    // Unfreezes all entities
    globals
        private timer FreezeTimer = CreateTimer (  )
    endglobals
    call PauseTimer ( FreezeTimer )
    set ENTITY_PAUSE = false
endfunction

function FreezeEntities takes real duration returns nothing
    // Freezes all entities for a specified duration
    call TimerStart ( FreezeTimer, duration, false, function UnfreezeEntities )
    set ENTITY_PAUSE = true
endfunction

globals
    public   constant   real   UPDATE_FREQUENCE     = 0.030  // Entity loop frequence
             constant   real   ENTITY_MAX_VELOCITY  = 2048   // Max entity velocity (Editor)
    private key Key
    boolean ENTITY_PAUSE = false
endglobals

function interface logic takes entity this returns nothing

struct entity
    implement StackModule
    cell spawn
    real x
    real y
    real vel  = 0
    real dir  = 0
    real xVel = 0
    real yVel = 0
    unit u
    real coll = 64
    boolean followFacing = false
    boolean canTeleport  = false
    entitytype type
    logic onXCollision = 0
    logic onYCollision = 0
    logic onPlayerHit  = 0
    logic onLoopLogic  = 0
    static group group = CreateGroup (  )
   
    // Turret addons
    timer cooldownTimer
   
    static method isUnitEntity takes unit u returns boolean
        // Returns true if the given unit is in fact a entity
        return IsUnitInGroup ( u, entity.group )
    endmethod
   
    static method getFromUnit takes unit u returns entity
        // Takes a entity unit and returns its entity id
        return entity ( GetHandleData ( u, Key ) )
    endmethod
   
    static method onLoop takes nothing returns nothing
        // Main entity movement loop function
        local integer i = entity.count
        local entity this
        local real x
        local real y
        if GAMEPLAY and not PAUSE and not ENTITY_PAUSE then
            loop
                set i = i - 1
                exitwhen i < 0
                set this = entity.stack [ i ]
                if this.onLoopLogic != 0 then
                    call this.onLoopLogic.execute ( this )
                endif
                set x = this.x + ( this.xVel * UPDATE_FREQUENCE )
                set y = this.y + ( this.yVel * UPDATE_FREQUENCE )
                if not IsPointPathable ( x, this.y ) or x > MAX_X + (SIZE/2) or x < MIN_X - (SIZE/2) then
                    if this.onXCollision != 0 then
                        call this.onXCollision.execute ( this )
                    else
                        set this.xVel = -this.xVel
                    endif
                else
                    set this.x = x
                endif
           
                if not IsPointPathable ( this.x, y ) or y > MAX_Y + (SIZE/2) or y < MIN_Y - (SIZE/2) then
                    if this.onYCollision != 0 then
                        call this.onYCollision.execute ( this )
                    else
                        set this.yVel = -this.yVel
                    endif
                else
                    set this.y = y
                endif
               
                //call SetUnitX ( this.u, this.x )
                //call SetUnitY ( this.u, this.y )
                call SetUnitPosition ( this.u, this.x, this.y ) // Perhaps slower, but far less buggy
               
                if this.followFacing then
                    call SetUnitFacing ( this.u, Atan2 ( this.yVel, this.xVel ) * bj_RADTODEG )
                endif
               
                if X < this.x + this.coll and X > this.x - this.coll and Y < this.y + this.coll and Y > this.y - this.coll then
                    if this.onPlayerHit != 0 then
                        call this.onPlayerHit.execute ( this )
                    else
                        call KillPlayer.execute ( DEATH_METHOD_SPLATTER )
                    endif
                endif
            endloop
        endif
    endmethod
   
    static method create takes entitytype whichType, cell spawn, real direction, real velocity returns entity
        local entity this = entity.allocate (  )
        set this.x = spawn.x
        set this.y = spawn.y
        set this.spawn = spawn
        if not whichType.startRotation then
            set this.u = CreateUnit ( Player ( 0 ), whichType.unitId, spawn.x, spawn.y, 270 )
        else
            set this.u = CreateUnit ( Player ( 0 ), whichType.unitId, spawn.x, spawn.y, direction )
        endif
        call SetHandleData ( this.u, Key, integer(this) )
        call GroupAddUnit ( entity.group, this.u )
        set this.type = whichType
        set this.vel  = velocity
        set this.dir  = direction
        set this.xVel = velocity * Cos ( direction * bj_DEGTORAD )
        set this.yVel = velocity * Sin ( direction * bj_DEGTORAD )
        set this.onXCollision  = whichType.onXCollision
        set this.onYCollision  = whichType.onYCollision
        set this.onPlayerHit   = whichType.onPlayerHit
        set this.onLoopLogic   = whichType.onPreLoop
        set this.followFacing  = whichType.followRotation
        set this.cooldownTimer = NewTimer (  )
        call this.addStack (  )
        return this
    endmethod
   
    method onDestroy takes nothing returns nothing
        call FlushHandleData ( this.u, Key )
        call GroupRemoveUnit ( entity.group, this.u )
        call RemoveUnit ( this.u )
        call TimerStart ( this.cooldownTimer, 0, false, null )
        call ReleaseTimer ( this.cooldownTimer )
        call this.removeStack (  )
    endmethod
   
    static method onInit takes nothing returns nothing
        call TimerStart ( CreateTimer (  ), UPDATE_FREQUENCE, true, function entity.onLoop )
    endmethod
   
    static method clearAll takes nothing returns nothing
        local integer i = entity.count
        loop
            set i = i - 1
            exitwhen i < 0
            call entity.stack [ i ].destroy (  )
        endloop
    endmethod
   
    static method clearAllOfType takes entitytype whichType returns nothing
        local integer i = entity.count
        loop
            set i = i - 1
            exitwhen i < 0
            if entity.stack [ i ].type == whichType then
                call entity.stack [ i ].destroy (  )
            endif
        endloop
    endmethod
endstruct

endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library EntityTypes
globals
        // Entity type list
             entitytype ENTITY_NONE = 0
             entitytype ENTITY_DEATH_WHEEL
             entitytype ENTITY_ROCKET
             entitytype ENTITY_BAT
             entitytype ENTITY_TURRET
             entitytype ENTITY_PROJECTILE
        // ================
endglobals

struct entitytype
    integer unitId        // Unit type id of this entity type
    logic onXCollision = 0    // Executed on row collision
    logic onYCollision = 0    // Executed on collumn collision
    logic onPlayerHit  = 0    // Executed when hitting the player
    logic onPreLoop    = 0    // Executed before anything else in the loop
    boolean followRotation = false  // Facing rotation follows entity velocity direction
    boolean startRotation  = false  // Applies starting facing direction, but does not follow velocity direction
    boolean canTeleport    = false  // Can teleport using teleporters
    static method create takes integer unitId returns entitytype
        local entitytype this = entitytype.allocate (  )
        set this.unitId = unitId
        return this
    endmethod
endstruct

function InitializeEntityTypes takes nothing returns nothing
    local entitytype this
   
    // Create all entity types
   
    // ======================================================
    // D E A T H   W H E E L
    set this = entitytype.create ( 'h002' )
    set this.canTeleport = true
    set ENTITY_DEATH_WHEEL = this
    // ======================================================
   
    // ======================================================
    // R O C K E T
    set this = entitytype.create ( 'h005' )
    set this.onXCollision   = RocketCollisionLogic
    set this.onYCollision   = RocketCollisionLogic
    set this.followRotation = true
    set this.startRotation  = true
    set ENTITY_ROCKET = this
    // ======================================================
   
    // ======================================================
    // B A T
    set this = entitytype.create ( 'h006' )
    set this.onPreLoop = BatLoopLogic
    set ENTITY_BAT = this
    // ======================================================
   
    // ======================================================
    // T U R R E T
    set this = entitytype.create ( 'h007' )
    set this.startRotation = true
    set this.onPreLoop = TurretFacingLogic
    set ENTITY_TURRET = this
    // ======================================================
   
    // ======================================================
    // P R O J E C T I L E
    set this = entitytype.create ( 'h008' )
    set this.onXCollision  = ProjectileCollisionLogic
    set this.onYCollision  = ProjectileCollisionLogic
    set this.onPlayerHit   = ProjectilePlayerHitLogic
    set this.startRotation = true
    set ENTITY_PROJECTILE  = this
    // ======================================================
   
endfunction

endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
// ===========================
// Entities AI logic functions
// ===========================

function RocketCollisionLogic takes entity this returns nothing
    // On collision, a rocket always turns to the left
    local real dir = Atan2 ( this.yVel, this.xVel ) + ( 90 * bj_DEGTORAD )
    set this.xVel = this.vel * Cos ( dir )
    set this.yVel = this.vel * Sin ( dir )
endfunction

function BatLoopLogic takes entity this returns nothing
    // Bats should always face the player
    local real dir = Atan2 ( this.y - Y, this.x - X )
    set this.xVel = -( this.vel * Cos ( dir ) )
    set this.yVel = -( this.vel * Sin ( dir ) )
endfunction

function ProjectileCollisionLogic takes entity this returns nothing
    // Turret projectile collision logic
    // Kill it and display a special effect
    call DestroyEffect ( AddSpecialEffect ( "MortarMissile.mdl", this.x, this.y ) )
    call this.destroy (  )
endfunction

function ProjectilePlayerHitLogic takes entity this returns nothing
    // Turret projectile player hit logic
    // Kill both the projectile and the player and display a special effect
    call DestroyEffect ( AddSpecialEffect ( "MortarMissile.mdl", this.x, this.y ) )
    call this.destroy (  )
    call KillPlayer.execute ( DEATH_METHOD_SPLATTER )
endfunction

function TurretFacingLogic takes entity this returns nothing
    // Turrets will always be tracking the player's current position
    // and fires a projectile when the angle is within range
    // Turrets also have a cooldown of 1 second between each shot
    // The angle calculation functions used are rather resource-heavy,
    // so too many turrets would most likely cause some lag
    local real angle = Atan2 ( Y - this.y, X - this.x ) * bj_RADTODEG
    set this.dir = OffsetAngleTowardsAngle ( this.dir, angle, 30.00 * Entities_UPDATE_FREQUENCE )
    set this.xVel = 0
    set this.yVel = 0
    call SetUnitFacing ( this.u, this.dir )
    if IsAngleBetweenAngles ( this.dir, angle - 5, angle + 5 ) and TimerGetRemaining ( this.cooldownTimer ) <= 0.01 then
        call TimerStart ( this.cooldownTimer, 1.00, false, null )
        call entity.create ( ENTITY_PROJECTILE, this.spawn, this.dir, 512.00 )
        call StartSound ( gg_snd_TurretAttack )
    endif
endfunction
//TESH.scrollpos=30
//TESH.alwaysfold=0
globals
    boolean GAMEPLAY = false
    boolean PAUSE    = false
endglobals

function StartGameplay takes nothing returns nothing
    if EDITOR then
        call EndEditor.execute (  )
    endif
    if GAMEPLAY then
        call ResetCells (  )
    endif
    set GAMEPLAY = true
    set PAUSE    = false
    call CreatePlayer (  )
    set Score = 0
    set Fuel  = 0
    set Keys  = 0
    if TEST then
        call DummyAddAbility ( 'A002' )
    else
        call DummyAddAbility ( 'A007' )
        call DummyAddAbility ( 'A00A' )
    endif
    if Zoom then
        call DummyAddAbility ( 'A00C' )
    else
        call DummyAddAbility ( 'A00B' )
    endif
   
    call StartMusic ( gg_snd_GameplayTrack )
    call BuildCollision (  )
    call ResetExitAnimation (  )
   
    call ResetTimer (  )
    call StartTimer (  )
    call Camera.followPlayer ( Zoom )
endfunction

function EndGameplay takes nothing returns nothing
    set GAMEPLAY = false
    call ClearPlayer (  )
    call ResetCells  (  )
    call UnfreezeEntities (  )
    call ClearAllEntitiesOfType ( ENTITY_PROJECTILE )
    if TEST then
        call DummyRemoveAbility ( 'A002' )
        set TEST = false
    endif
    call DummyRemoveAbility ( 'A00A' )
    call DummyRemoveAbility ( 'A007' )
    call DummyRemoveAbility ( 'A00B' )
    call DummyRemoveAbility ( 'A00C' )
    call MultiboardDisplay ( Gamestats.main, false )
    call StopTimer (  )
    call Camera.followPlayer ( false )
    if LimitedVision then
        call ResetVision (  )
    endif
    call NormalizeKeys (  )
endfunction
//TESH.scrollpos=50
//TESH.alwaysfold=0
library Player requires Collision, Death

globals
    constant integer PLAYER_UNIT_MODEL = 'h000'
    constant integer STAND_LEFT  = 0
    constant integer STAND_RIGHT = 1
    constant integer WALK_LEFT   = 2
    constant integer WALK_RIGHT  = 3
    constant integer JUMP_LEFT   = 4
    constant integer JUMP_RIGHT  = 5
    constant integer JET_LEFT    = 6
    constant integer JET_RIGHT   = 7
    constant integer LADDER_ANIM = 8
   
    real X = 0
    real Y = 0
    real XVel = 0
    real YVel = 0
    real AddXVel = 0
    real AddYVel = 0
    unit Unit = null
    real Fuel = 0.00
    real FuelDrop = 5.00
    integer Keys    = 0
    integer MaxKeys = 0
    integer Score   = 0
   
    real XAcceleration = 128.00
    real XMaxVel       = 256.00
   
    real FallVel       = 5.00
   
    real JetpackVel    = 10.00
    real JetpackMaxVel = 512.00
   
    boolean Walking    = false
    boolean Jetpack    = false
    boolean Jumping    = false
    boolean Ladder     = false
    real JumpVel       = 375.00
    real LadderSpeed   = 256.00
   
    boolean Zoom       = false
   
    real array CollisionX
    real array CollisionY
endglobals

function CreatePlayer takes nothing returns nothing
    if Unit != null then
        call ClearPlayer.execute (  )
    endif
    set X = LEVEL.startLoc.x
    set Y = LEVEL.startLoc.y
    set Unit = CreateUnit ( Player ( 0 ), PLAYER_UNIT_MODEL, X, Y, 180 )
endfunction

function ClearPlayer takes nothing returns nothing
    call RemoveUnit ( Unit )
    set Unit = null
    set XVel = 0
    set YVel = 0
endfunction

function NudgePlayer takes nothing returns nothing
    if not IsPointPathable ( X, Y ) then
        if IsPointPathable ( X + 32, Y ) then
            set X = X + 32
        elseif IsPointPathable ( X - 32, Y ) then
            set X = X - 32
        elseif IsPointPathable ( X, Y + 32 ) then
            set Y = Y - 32
        elseif IsPointPathable ( X, Y - 32 ) then
            set Y = Y - 32
        else
            call KillPlayer ( DEATH_METHOD_SPLATTER )
        endif
    endif
endfunction

endlibrary
//TESH.scrollpos=3
//TESH.alwaysfold=0
library Death requires Timer

globals
    constant string DEATH_METHOD_SPLATTER = "DeathExplode.mdl"
    constant string DEATH_METHOD_SMOKE    = "Abilities\\Spells\\Orc\\MirrorImage\\MirrorImageDeathCaster.mdl"
endglobals

function KillPlayer takes string DeathMethod returns nothing
    // Always execute this function or the thread may crash
    local boolean b = TEST
    set PAUSE = true
    if DeathMethod == DEATH_METHOD_SPLATTER then
        call StartSound ( gg_snd_PlayerDeath )
    elseif DeathMethod == DEATH_METHOD_SMOKE then
        call StartSound ( gg_snd_PlayerDeath2 )
    endif
    call PauseUnit ( Dummy, true )
    call SetUnitVertexColor ( Unit, 0, 0, 0, 0 )
    call DestroyEffect ( AddSpecialEffect ( DeathMethod, X, Y ) )
    call StopTimer (  )
    // ---
    // No need to be very accurate here
    call TriggerSleepAction ( 1.50 )
    // ---
    call PauseUnit ( Dummy, false )
    call SetUnitVertexColor ( Unit, 255, 255, 255, 255 )
    if b then
        call StartEditor.execute (  )
    else
        call StartGameplay.execute (  )
    endif
endfunction

endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library Timer

globals
    private constant integer SCORE_LOSS_PER_SECOND = 10

    private integer S = 0
    private integer M = 0
    private integer H = 0
    private timer GameTimer = CreateTimer (  )
endglobals

private function Clock2String takes nothing returns string
    local string result = I2S ( H ) + ":"
    if M < 10 then
        set result = result + "0" + I2S ( M ) + ":"
    else
        set result = result + I2S ( M ) + ":"
    endif
    if S < 10 then
        return result + "0" + I2S ( S )
    endif
    return result + I2S ( S )
endfunction

private function TimerCallback takes nothing returns nothing
    set S = S + 1
    if S > 59 then
        set S = 0
        set M = M + 1
        if M > 59 then
            set M = 0
            set H = H + 1
        endif
    endif
    set Score = Score - SCORE_LOSS_PER_SECOND
    if Score < 0 then
        set Score = 0
    endif
    call MultiboardSetItemValue ( Gamestats.time, Clock2String (  ) )
endfunction

function ResetTimer takes nothing returns nothing
    set S = 0
    set M = 0
    set H = 0
    call MultiboardSetItemValue ( Gamestats.time, "0:00:00" )
endfunction

function StartTimer takes nothing returns nothing
    call TimerStart ( GameTimer, 1.00, true, function TimerCallback )
endfunction

function StopTimer takes nothing returns nothing
    call PauseTimer ( GameTimer )
endfunction

endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library VisionLimit
   
globals
    private destructable Filter = null
    private timer        Timer  = CreateTimer (  )
    boolean LimitedVision       = false
endglobals

private function VisionFilterLoop takes nothing returns nothing
    call RemoveDestructable ( Filter )
    set Filter = CreateDestructableZ ( 'B011', X, Y, 512, 270, 2.0, 0 )
endfunction

function LimitVision takes nothing returns nothing
    call TimerStart ( Timer, 0.01, true, function VisionFilterLoop )
    call VisionFilterLoop (  )
    set LimitedVision = true
endfunction

function ResetVision takes nothing returns nothing
    call RemoveDestructable ( Filter )
    call PauseTimer ( Timer )
    set LimitedVision = false
endfunction
   
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library Collision initializer init

globals
    region Collision
endglobals

function BuildCollision takes nothing returns nothing
    local integer x
    local integer y = HEIGHT + 1
    call ClearCollision.evaluate (  )
    loop
        set x = WIDTH + 1
        loop
            set x = x - 1
            exitwhen x < 0
            if not CELLS [ x ][ y ].object.type.walkable and CELLS [ x ][ y ].object.collision then
                call RegionAddRect ( Collision, CELLS [ x ][ y ].collision )
            endif
        endloop
        set y = y - 1
        exitwhen y < 0
    endloop
endfunction

function ClearCollision takes nothing returns nothing
    call RegionClearRect ( Collision, bj_mapInitialPlayableArea )
endfunction

function IsPointPathable takes real x, real y returns boolean
    return not IsPointInRegion ( Collision, x, y )
endfunction

private function init takes nothing returns nothing
    set Collision = CreateRegion (  )
endfunction
endlibrary
//TESH.scrollpos=54
//TESH.alwaysfold=0
library Movement initializer init requires Collision
globals
    private constant real UPDATE_FREQUENCE = 0.01
    boolean Right = true
endglobals

function MovementLoop takes nothing returns nothing
    local real x
    local real y
    call SetSoundVolume ( gg_snd_Jet, 0 )
    if GAMEPLAY and not PAUSE then
        set Jetpack = false
        set Walking = false
        set Ladder  = false
   
        if LEFT and AddXVel == 0 then
            set Right = false
            set XVel = XVel - XAcceleration
            if XVel < -XMaxVel then
                set XVel = -XMaxVel
            endif
        elseif RIGHT and AddXVel == 0 then
            set Right = true
            set XVel = XVel + XAcceleration
            if XVel > XMaxVel then
                set XVel = XMaxVel
            endif
        else
            if XVel > 0 then
                set XVel = XVel - XAcceleration
            elseif XVel < 0 then
                set XVel = XVel + XAcceleration
            endif
        endif
       
        if UP and Fuel > 0 and not CURRENT_CELL.object.type.ladder then
            set Jetpack = true
            set Jumping = false
            set YVel = YVel + JetpackVel
            set Fuel = Fuel - ( FuelDrop * UPDATE_FREQUENCE )
            if YVel > JetpackMaxVel then
                set YVel = JetpackMaxVel
            endif
            if Fuel < 0 then
                set Fuel = 0
            endif
            call SetSoundVolume ( gg_snd_Jet, 127 )
           
        elseif UP and CURRENT_CELL.object.type.ladder then
            set Y = Y + ( LadderSpeed * UPDATE_FREQUENCE )
            if not IsPointPathable ( X, Y ) then
                set Y = Y - ( LadderSpeed * UPDATE_FREQUENCE )
            endif
           
        endif
       
        if DOWN and CURRENT_CELL.object.type.ladder then
            set Y = Y - ( LadderSpeed * UPDATE_FREQUENCE )
            if not IsPointPathable ( X, Y ) then
                set Y = Y + ( LadderSpeed * UPDATE_FREQUENCE )
            endif
        endif
       
        if Y > MIN_Y - (SIZE/4) then
            set Walking = false
            set YVel = YVel - FallVel
        endif
       
        if CURRENT_CELL.object.type.ladder then
            set YVel = 0
            set Ladder = true
        endif
       
        set x = X + ( XVel * UPDATE_FREQUENCE ) + ( AddXVel * UPDATE_FREQUENCE )
        set y = Y + ( YVel * UPDATE_FREQUENCE ) + ( AddYVel * UPDATE_FREQUENCE )
       
        // Check x/y level object pathing
        if ( XVel > 0 or AddXVel > 0 ) and not IsPointPathable ( x, Y ) then
            set XVel = 0
        elseif ( XVel < 0 or AddXVel < 0 ) and not IsPointPathable ( x, Y ) then
            set XVel = 0
        else
            set X = x
        endif
       
        if ( YVel > 0 or AddYVel > 0 ) and not IsPointPathable ( X, y ) then
            set YVel = 0
        elseif ( YVel < 0 or AddYVel < 0 ) and not IsPointPathable ( X, y ) then
            set YVel = 0
            set Walking = true
            set Jumping = false
        else
            set Y = y
        endif
       
        if X > MAX_X + (SIZE/4) then
            set X = MAX_X + (SIZE/4)
            set XVel = 0
        elseif X < MIN_X - (SIZE/4) then
            set X = MIN_X - (SIZE/4)
            set XVel = 0
        endif
       
        if Y > MAX_Y + (SIZE/4) then
            set Y = MAX_Y + (SIZE/4)
            set YVel = 0
        elseif Y <= MIN_Y - (SIZE/4) then
            set Y = MIN_Y - (SIZE/4)
            set YVel = 0
            set Walking = true
            set Jumping = false
        endif
       
        //call SetUnitX ( Unit, X )
        //call SetUnitY ( Unit, Y )
        call SetUnitPosition ( Unit, X, Y ) // Perhaps slower, but far less buggy
       
        // Animations
        if Ladder then
            call SetUnitAnimationByIndex ( Unit, LADDER_ANIM )
            if UP or DOWN or LEFT or RIGHT then
                call SetUnitTimeScale ( Unit, 1.00 )
            else
                call SetUnitTimeScale ( Unit, 0.00 )
            endif
        elseif Walking then
            if Right then
                call SetUnitAnimationByIndex ( Unit, WALK_RIGHT )
            else
                call SetUnitAnimationByIndex ( Unit, WALK_LEFT )
            endif
            if XVel < 0 then
                call SetUnitTimeScale ( Unit, -XVel / XMaxVel )
            else
                call SetUnitTimeScale ( Unit, XVel / XMaxVel )
            endif
            if XVel == 0 then
                if Right then
                    call SetUnitAnimationByIndex ( Unit, STAND_RIGHT )
                else
                    call SetUnitAnimationByIndex ( Unit, STAND_LEFT )
                endif
                call SetUnitTimeScale ( Unit, 1.00 )
            endif
        elseif Jetpack then
            if Right then
                call SetUnitAnimationByIndex ( Unit, JET_RIGHT )
            else
                call SetUnitAnimationByIndex ( Unit, JET_LEFT )
            endif
            call SetUnitTimeScale ( Unit, 1.00 )
        elseif Jumping then
            if Right then
                call SetUnitAnimationByIndex ( Unit, JUMP_RIGHT )
            else
                call SetUnitAnimationByIndex ( Unit, JUMP_LEFT )
            endif
            call SetUnitTimeScale ( Unit, 1.00 )
        else
            if Right then
                call SetUnitAnimationByIndex ( Unit, STAND_RIGHT )
            else
                call SetUnitAnimationByIndex ( Unit, STAND_LEFT )
            endif
            call SetUnitTimeScale ( Unit, 1.00 )
        endif
    endif
endfunction

private function init takes nothing returns nothing
    call TimerStart ( CreateTimer (  ), 0.01, true, function MovementLoop )
    call StartSound ( gg_snd_Jet )
    call SetSoundVolume ( gg_snd_Jet, 0 )
endfunction

endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library Jump initializer init

private function OnJump takes nothing returns boolean
    local eventid id = GetTriggerEventId (  )
    if Walking and Fuel <= 0 and GAMEPLAY and not PAUSE and ( ( id == EVENT_PLAYER_ARROW_UP_DOWN and not Invert ) or ( id == EVENT_PLAYER_ARROW_DOWN_DOWN and Invert ) ) then
        set Walking = false
        set Jumping = true
        set YVel = JumpVel
        if Right then
            call SetUnitAnimationByIndex ( Unit, JUMP_RIGHT )
        else
            call SetUnitAnimationByIndex ( Unit, JUMP_LEFT )
        endif
        call SetUnitTimeScale ( Unit, 1.00 )
    endif
    return false
endfunction

private function init takes nothing returns nothing
    local trigger t = CreateTrigger (  )
    call TriggerRegisterPlayerEvent ( t, Player ( 0 ), EVENT_PLAYER_ARROW_UP_DOWN )
    call TriggerRegisterPlayerEvent ( t, Player ( 0 ), EVENT_PLAYER_ARROW_DOWN_DOWN )
    call TriggerAddCondition ( t, Condition ( function OnJump ) )
endfunction

endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library Gamestats

globals
    board Gamestats
    constant integer COLLUMNS = 12
    constant integer ROWS     = 29
endglobals

struct board
    multiboard main
    Multibar   fuel
    multiboarditem score
    multiboarditem keys
    multiboarditem time
    method SetTitle takes integer collumn, integer row, string text, real width returns nothing
        local multiboarditem i = MultiboardGetItem ( this.main, row, collumn )
        call MultiboardSetItemStyle ( i, true, false )
        call MultiboardSetItemWidth ( i, width )
        call MultiboardSetItemValue ( i, text )
        call MultiboardReleaseItem  ( i )
        set i = null
    endmethod
    static method create takes nothing returns board
        local board this = board.allocate (  )
        local integer col = 0
        local integer row = 0
        local multiboarditem i
        set  this.main = CreateMultiboard (  )
        call MultiboardSetTitleText   ( this.main, "Statusbar" )
        call MultiboardSetColumnCount ( this.main, COLLUMNS )
        call MultiboardSetRowCount    ( this.main, ROWS )
        loop
            set col = 0
            loop
                set i = MultiboardGetItem   ( this.main, row, col )
                call MultiboardSetItemStyle ( i, false, false )
                call MultiboardSetItemWidth ( i, 0.0 )
                call MultiboardReleaseItem  ( i )
                set col = col + 1
                exitwhen col >= COLLUMNS
            endloop
            set row = row + 1
            exitwhen row >= ROWS
        endloop
        call this.SetTitle ( 0, 0, "|cffffcc00FUEL|r", 0.13 )
       
        set this.fuel = Multibar.create ( this.main, 0, 1, COLLUMNS, 100.00, 0.00, MULTIBAR_TYPE_RADIO )
       
        call this.SetTitle ( 0, 3, "|cffffcc00SCORE|r", 0.13 )
       
        set this.score = MultiboardGetItem ( this.main, 4, 0 )
        call MultiboardSetItemStyle ( this.score, true, false )
        call MultiboardSetItemWidth ( this.score, 0.13 )
       
        call this.SetTitle ( 0, 6, "|cffffcc00KEYS|r", 0.13 )
       
        set this.keys = MultiboardGetItem ( this.main, 7, 0 )
        call MultiboardSetItemStyle ( this.keys, true, false )
        call MultiboardSetItemWidth ( this.keys, 0.13 )
       
        call this.SetTitle ( 0, 9, "|cffffcc00TIME|r", 0.13 )
       
        set this.time = MultiboardGetItem ( this.main, 10, 0 )
        call MultiboardSetItemStyle ( this.time, true, false )
        call MultiboardSetItemWidth ( this.time, 0.13 )
        call MultiboardSetItemValueColor ( this.time, 255, 204, 0, 255 )
       
        return this
    endmethod
endstruct

private function OnLoop takes nothing returns nothing
    if not SCOREBOARD then
        call MultiboardDisplay ( Gamestats.main, GAMEPLAY )
    else
        call MultiboardDisplay  ( ScoreboardTable_Table, true  )
        call MultiboardMinimize ( ScoreboardTable_Table, false )
    endif
    if GAMEPLAY then
        call MultiboardMinimize ( Gamestats.main, false )
        call Gamestats.fuel.UpdateValue ( Fuel, true )
        call MultiboardSetItemValue ( Gamestats.score, "|cffffcc00" + I2S ( Score ) + "|r" )
        call MultiboardSetItemValue ( Gamestats.keys, "|cffffcc00" + I2S ( Keys ) + "/" + I2S ( MaxKeys ) + "|r" )
    endif
endfunction

private function DelayCreate takes nothing returns nothing
    set Gamestats = board.create (  )
    call TimerStart ( GetExpiredTimer (  ), 0.25, true, function OnLoop )
endfunction

function InitializeStatusBoard takes nothing returns nothing
    call TimerStart ( CreateTimer (  ), 0.0, true, function DelayCreate )
endfunction

endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library Teleporter

function FindNextObjectType takes object this returns object
    local objecttype Type = this.type
    local integer id      = this.stackid
    loop
        set id = id + 1
        if id >= object.count then
            set id = 0
        endif
        exitwhen ( object.stack [ id ].type == Type ) or id == this.stackid
    endloop
    return object.stack [ id ]
endfunction

function TeleportEffect takes real x, real y returns nothing
    call DestroyEffect ( AddSpecialEffect ( "Abilities\\Spells\\NightElf\\Blink\\BlinkTarget.mdl", x, y ) )
endfunction

function TeleportPlayer takes cell fromCell returns nothing
    local object to = FindNextObjectType ( fromCell.object )
    call TeleportEffect ( X, Y )
    set X = to.x
    set Y = to.y
    call TeleportEffect ( X, Y )
endfunction

function TeleportEntity takes entity e, cell fromCell returns nothing
    local object to = FindNextObjectType ( fromCell.object )
    call TeleportEffect ( e.x, e.y )
    set e.x = to.x
    set e.y = to.y
    call TeleportEffect ( e.x, e.y )
endfunction

endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library Lever requires Player

function ActivateLever takes cell leverCell returns nothing
    local object this
    local objecttype Type = leverCell.object.type.openType
    local integer i = object.count
    loop
        set i = i - 1
        exitwhen i < 0
        set this = object.stack [ i ]
        if this.type == Type then
            set this.collision = not this.collision
            if this.collision then
                call SetDestructableAnimation ( this.d, "stand" )
            else
                call SetDestructableAnimation ( this.d, "death" )
            endif
        endif
    endloop
    call BuildCollision.execute (  )
    call NudgePlayer (  )
    call StartSound ( gg_snd_Lever1 )
endfunction

endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope FireTrapFunction initializer init

private function OnLoop takes nothing returns nothing
    local object this
    local integer i = object.count
    local boolean snd
    if GAMEPLAY and not PAUSE then
        loop
            set i = i - 1
            exitwhen i < 0
            set this = object.stack [ i ]
            if this.type == OBJECT_FIRE_TRAP then
                set this.lethal = not this.lethal
                set snd = this.lethal
                if this.lethal then
                    call SetDestructableAnimation ( this.d, "stand" )
                    if CURRENT_CELL == this.cell then
                        call KillPlayer.execute ( DEATH_METHOD_SMOKE )
                    endif
                else
                    call SetDestructableAnimation ( this.d, "death" )
                endif
            endif
        endloop
        if snd then
            call StartSound ( gg_snd_FireTrap )
        endif
    endif
endfunction

private function init takes nothing returns nothing
    call TimerStart ( CreateTimer (  ), 1.50, true, function OnLoop )
endfunction

endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope ActionKeyMain initializer init

function ActionKeyPressed takes nothing returns nothing
    local cell this = CURRENT_CELL
    if GAMEPLAY and not PAUSE then
   
        if this.object.type.isTeleporter then
            call TeleportPlayer ( this )
        endif
       
        if this.object.type.isLever then
            if this.object.used then
                call SetDestructableAnimation ( this.object.d, "stand" )
            else
                call SetDestructableAnimation ( this.object.d, "death" )
            endif
            set this.object.used = not this.object.used
            call ActivateLever ( this )
        endif
    endif
endfunction

private function init takes nothing returns nothing
    local trigger t = CreateTrigger (  )
    call TriggerRegisterPlayerEvent ( t, Player ( 0 ), EVENT_PLAYER_ARROW_DOWN_DOWN )
    call TriggerAddAction ( t, function ActionKeyPressed )
endfunction

endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
globals
    boolean EDITOR = false
    boolean TEST   = false
endglobals

function StartEditor takes nothing returns nothing
    if GAMEPLAY then
        call EndGameplay.execute (  )
    endif
    if EDITOR then
        call ResetCells (  )
    endif
    call StartMusic ( gg_snd_EditorTrack )
    set EDITOR = true
    set TEST   = false
    call ShowTileMenu ( true )
    call ResetExitAnimation (  )
    call MoveCellEdit ( CellSaveX, CellSaveY )
    call MoveStartLocFlag ( LEVEL.startLoc.x, LEVEL.startLoc.y )
    call DummyAddAbility ( 'A000' )
    call DummyAddAbility ( 'A001' )
    call DummyAddAbility ( 'A003' )
    call DummyAddAbility ( 'A004' )
    call DummyAddAbility ( 'A005' )
    call DummyAddAbility ( 'A006' )
    call DummyAddAbility ( 'A008' )
    call DummyAddAbility ( 'A009' )
endfunction

function EndEditor takes nothing returns nothing
    set EDITOR = false
    call ClearCellEdit (  )
    call ClearTileType (  )
    call RemoveStartLocFlag (  )
    call ResetCells (  )
    call ClearTextMessages (  )
    call HideEntityTable.execute (  )
    call HideDescription.execute (  )
    call ShowTileMenu ( false )
    call DummyRemoveAbility ( 'A000' )
    call DummyRemoveAbility ( 'A001' )
    call DummyRemoveAbility ( 'A003' )
    call DummyRemoveAbility ( 'A004' )
    call DummyRemoveAbility ( 'A005' )
    call DummyRemoveAbility ( 'A006' )
    call DummyRemoveAbility ( 'A008' )
    call DummyRemoveAbility ( 'A009' )
endfunction
//TESH.scrollpos=24
//TESH.alwaysfold=0
library CellEdit
globals
    cell         CellEdit      = 0    // Currently editing cell
    destructable CellEditFrame = null
    integer      CellEditX     = 0
    integer      CellEditY     = 0
    destructable CellHighlight = null
    integer      CellSaveX     = 0
    integer      CellSaveY     = 0
endglobals

function MoveCellEdit takes integer x, integer y returns nothing
    if CellEditFrame != null then
        call RemoveDestructable ( CellEditFrame )
    endif
   
    set CellEdit = CELLS [ x ][ y ]
    set CellEditFrame = CreateDestructable ( 'B004', CellEdit.x, CellEdit.y, 270, 1.25, 0 )
    set CellEditX = x
    set CellEditY = y
    call ClearCellHighlight.execute (  )
   
    call ClearTextMessages (  )
    call DisplayTimedTextToPlayer ( Player ( 0 ), 0, 0, 3600, "|cff44ff44[ " + I2S(CellEdit.xId) + ", " + I2S(CellEdit.yId) + " ]|r" )
   
    if CellEdit.entity != 0 then
        call DisplayEntityTable.execute ( CellEdit.entity )
    else
        call HideEntityTable.execute (  )
    endif
endfunction

function ClearCellEdit takes nothing returns nothing
    call RemoveDestructable ( CellEditFrame )
    set CellEdit  = 0
    set CellEditX = 0
    set CellEditY = 0
    call ClearCellHighlight.execute (  )
endfunction

function MoveCellHighlight takes integer x, integer y returns nothing
    if CellHighlight != null then
        call RemoveDestructable ( CellHighlight )
    endif
    set CellHighlight = CreateDestructable ( 'B005', CELLS [ x ][ y ].x, CELLS [ x ][ y ].y, 270, 1.25, 0 )
    call ClearTextMessages (  )
    call DisplayTimedTextToPlayer ( Player ( 0 ), 0, 0, 3600, "[ " + I2S(x) + ", " + I2S(y) + " ]" )
    call DisplayTimedTextToPlayer ( Player ( 0 ), 0, 0, 3600, "|cff44ff44[ " + I2S(CellEdit.xId) + ", " + I2S(CellEdit.yId) + " ]|r" )
endfunction

function ClearCellHighlight takes nothing returns nothing
    call RemoveDestructable ( CellHighlight )
endfunction

endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library StartLocationFlag

globals
    private destructable StartLocFlag = null
endglobals

function MoveStartLocFlag takes real x, real y returns nothing
    if StartLocFlag != null then
        call RemoveDestructable ( StartLocFlag )
    endif
    set StartLocFlag = CreateDestructable ( 'B008', x, y, 270, 1.00, 0 )
endfunction

function RemoveStartLocFlag takes nothing returns nothing
    call RemoveDestructable ( StartLocFlag )
endfunction

endlibrary
//TESH.scrollpos=2
//TESH.alwaysfold=0
library SaveSlots requires SaveLevelToCache

globals
    constant integer MAX_SAVE_SLOTS = 8
    level array SaveSlots
    boolean array IsSlotEmpty
endglobals

function SaveToSlot takes integer slot returns nothing
    call SaveSlots [ slot ].save.execute (  )
    call SaveLevelToCache.execute ( slot )
    set IsSlotEmpty [ slot ] = false
endfunction

function LoadFromSlot takes integer slot returns nothing
    if IsLevelIdSaved ( slot ) then
        call LoadLevelFromCache.execute ( slot )
    else
        call SaveSlots [ slot ].load.execute (  )
    endif
endfunction

function InitializeSaveSlots takes nothing returns nothing
    local integer i = MAX_SAVE_SLOTS
    loop
        set i = i - 1
        exitwhen i < 0
        set SaveSlots [ i ] = level.create (  )
        set IsSlotEmpty [ i ] = true
    endloop
endfunction

endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library SaveSlotsDialog initializer init requires SaveSlots, LoadLevelFromCache
globals
    private dialog SlotDialog = null
    private button array SlotOption
    private trigger SlotEvent = CreateTrigger (  )
    private boolean Saving
endglobals

function UpdateSlotDialog takes boolean saving, boolean displayWarning returns nothing
    local integer i = 0
    local string msg
    if SlotDialog == null then
        set SlotDialog = DialogCreate (  )
        call TriggerRegisterDialogEvent ( SlotEvent, SlotDialog )
    else
        call DialogClear ( SlotDialog )
    endif
    loop
        if IsSlotEmpty [ i ] and not IsLevelIdSaved ( i ) then
            set SlotOption [ i ] = DialogAddButton ( SlotDialog, "<Empty Slot>", 0 )
        else
            set SlotOption [ i ] = DialogAddButton ( SlotDialog, "Level #" + I2S ( i + 1 ), 0 )
        endif
        set i = i + 1
        exitwhen i >= MAX_SAVE_SLOTS
    endloop
    call DialogAddButton ( SlotDialog, "Exit", 0 )
    set Saving = saving
    if saving then
        set msg = "SAVE"
    else
        set msg = "LOAD"
        if displayWarning then
            set msg = msg + "\n|cffff0000This will delete all unsaved data.|r"
        endif
    endif
    call DialogSetMessage ( SlotDialog, msg )
    call DialogDisplay ( Player ( 0 ), SlotDialog, true )
endfunction

private function OnSlotDialogClick takes nothing returns nothing
    local integer i = 0
    local button  b = GetClickedButton (  )
    loop
        exitwhen b == SlotOption [ i ]
        set i = i + 1
        exitwhen i > MAX_SAVE_SLOTS
    endloop
    if i < MAX_SAVE_SLOTS then
        if Saving then
            call SaveToSlot ( i )
            call SaveHighScore.execute ( true, i + 1, 0 )
        else
            call LoadFromSlot ( i )
        endif
    endif
    set b = null
endfunction

private function init takes nothing returns nothing
    call TriggerAddAction ( SlotEvent, function OnSlotDialogClick )
endfunction

endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library CellMouseTracked initializer init requires Data

globals
    trigger CellMouseTracked = CreateTrigger (  )
endglobals

function OnCellMouseTrack takes nothing returns nothing
    local cell this = cell ( GetHandleData ( GetTriggeringTrackable (  ), CellKey ) )
    if EDITOR then
        call MoveCellHighlight ( this.xId, this.yId )
    endif
endfunction

private function init takes nothing returns nothing
    call TriggerAddAction ( CellMouseTracked, function OnCellMouseTrack )
endfunction

endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library CellMouseHit initializer init requires Data

globals
    trigger CellMouseHit = CreateTrigger (  )
endglobals

function OnCellMouseHit takes nothing returns nothing
    local cell this = cell ( GetHandleData ( GetTriggeringTrackable (  ), CellKey ) )
    if EDITOR then
        call MoveCellEdit ( this.xId, this.yId )
        call StartSound ( gg_snd_Click )
    endif
endfunction

private function init takes nothing returns nothing
    call TriggerAddAction ( CellMouseHit, function OnCellMouseHit )
endfunction

endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library CellBrowser initializer init

private function Action takes nothing returns boolean
    local eventid id = GetTriggerEventId (  )
    if EDITOR then
        if id == EVENT_PLAYER_ARROW_DOWN_DOWN then
            set CellEditY = CellEditY - 1
            if CellEditY < 0 then
                set CellEditY = HEIGHT
            endif
           
        elseif id == EVENT_PLAYER_ARROW_UP_DOWN then
            set CellEditY = CellEditY + 1
            if CellEditY > HEIGHT then
                set CellEditY = 0
            endif
           
        elseif id == EVENT_PLAYER_ARROW_LEFT_DOWN then
            set CellEditX = CellEditX - 1
            if CellEditX < 0 then
                set CellEditX = WIDTH
            endif
           
        elseif id == EVENT_PLAYER_ARROW_RIGHT_DOWN then
            set CellEditX = CellEditX + 1
            if CellEditX > WIDTH then
                set CellEditX = 0
            endif
       
        endif
        call MoveCellEdit.evaluate ( CellEditX, CellEditY )
    endif
    return false
endfunction

private function init takes nothing returns nothing
    local trigger t = CreateTrigger (  )
    call TriggerRegisterPlayerEvent ( t, Player ( 0 ), EVENT_PLAYER_ARROW_DOWN_DOWN )
    call TriggerRegisterPlayerEvent ( t, Player ( 0 ), EVENT_PLAYER_ARROW_UP_DOWN )
    call TriggerRegisterPlayerEvent ( t, Player ( 0 ), EVENT_PLAYER_ARROW_LEFT_DOWN )
    call TriggerRegisterPlayerEvent ( t, Player ( 0 ), EVENT_PLAYER_ARROW_RIGHT_DOWN )
    call TriggerAddCondition ( t, Condition ( function Action ) )
endfunction

endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library TileMenu initializer init requires Data

globals
    tile TILE         = 0        // Last used tile type
    real TILE_START_X = 1536.00  // Tile menu start x
    real TILE_START_Y = 896.00   // Tile menu start y
    real TILE_SIZE    = 145      // Tile spacing
    integer TILE_COL  = 5        // Number of tiles on a single row
   
    real TileX        = TILE_START_X
    real TileY        = TILE_START_Y
    real TileColl     = 0
    destructable TileFrame = null
    texttag TileName
    key  TileKey
    private destructable array Filter
    private integer FilterCount = 0
    private trigger TileHit   = CreateTrigger (  )
    private trigger TileTrack = CreateTrigger (  )
endglobals

function ShowTileMenu takes boolean flag returns nothing
    local integer i = FilterCount
    loop
        set i = i - 1
        exitwhen i < 0
        call ShowDestructable ( Filter [ i ], not flag )
    endloop
endfunction

function CreateTileType takes tile whichTile returns nothing
    set TILE = whichTile
    call CellEdit.setObject ( whichTile.type )
endfunction

function RepeatLastTile takes nothing returns nothing
    if TILE != 0 then
        call CellEdit.setObject ( TILE.type )
    endif
endfunction

function ClearTileType takes nothing returns nothing
    call RemoveDestructable ( TileFrame )
endfunction

function SetDescription takes string text returns nothing
    call SetTextTagText ( TileName, text, 0.02 )
    call SetTextTagVisibility ( TileName, true )
endfunction
function HideDescription takes nothing returns nothing
    call SetTextTagVisibility ( TileName, false )
endfunction

private function OnTileHit takes nothing returns nothing
    local tile this = tile ( GetHandleData ( GetTriggeringTrackable (  ), TileKey ) )
    if EDITOR then
        call CreateTileType ( this )
        call StartSound ( gg_snd_Click )
    endif
endfunction

private function OnTileTrack takes nothing returns nothing
    local tile this = tile ( GetHandleData ( GetTriggeringTrackable (  ), TileKey ) )
    if EDITOR then
        if TileFrame != null then
            call RemoveDestructable ( TileFrame )
        endif
        set TileFrame = CreateDestructable ( 'B004', this.x, this.y, 270, 1.25, 0 )
        call SetDescription ( this.type.name )
    endif
endfunction

struct tile
    real x
    real y
    objecttype type
    trackable  track
    static method create takes objecttype whichType returns tile
        local tile this = tile.allocate (  )
        set this.type = whichType
        set this.x = TileX
        set this.y = TileY
        if whichType.destId != 0 then
            call CreateDestructable ( whichType.destId, TileX, TileY, whichType.facing, whichType.scale, 0 )
        else
            call SetDestructableAnimationSpeed ( CreateDestructable ( 'B005', TileX, TileY, whichType.facing, whichType.scale, 0 ), 0 )
        endif
        set this.track = CreateTrackable ( "CellTrackable.mdl", TileX, TileY, 270 * bj_DEGTORAD )
        call TriggerRegisterTrackableHitEvent ( TileHit, this.track )
        call TriggerRegisterTrackableTrackEvent ( TileTrack, this.track )
        call SetHandleData ( this.track, TileKey, integer(this) )
        set TileX    = TileX + TILE_SIZE
        set TileColl = TileColl + 1
        if TileColl >= TILE_COL then
            set TileColl = 0
            set TileX = TILE_START_X
            set TileY = TileY - TILE_SIZE
        endif
        return this
    endmethod
endstruct

private function init takes nothing returns nothing
    local real x
    local real y
    call TriggerAddAction ( TileHit, function OnTileHit )
    call TriggerAddAction ( TileTrack, function OnTileTrack )
   
    set TileName = CreateTextTag (  )
    call SetTextTagColor      ( TileName, 255, 255, 255, 255 )
    call SetTextTagPermanent  ( TileName, true )
    call SetTextTagPos        ( TileName, 1472, -768, 0 )
   
    set y = TILE_START_Y + 64
    loop
        set x = TILE_START_X - 64
        loop
            set Filter [ FilterCount ] = CreateDestructableZ ( 'B010', x, y, 130, 270, 1.00, 0 )
            set FilterCount = FilterCount + 1
            set x = x + 128
            exitwhen x > TILE_START_X + 1024
        endloop
        set y = y - 128
        exitwhen y < TILE_START_Y - 2688
    endloop
endfunction

endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library TileSpecial initializer init requires TileMenu

globals
    private trigger TileHit   = CreateTrigger (  )
    private trigger TileTrack = CreateTrigger (  )
endglobals

function interface uniqueExecute takes nothing returns nothing

private function OnTileHit takes nothing returns nothing
    local tileUnique this = tileUnique ( GetHandleData ( GetTriggeringTrackable (  ), TileKey ) )
    if EDITOR then
        call this.onClick.execute (  )
        call StartSound ( gg_snd_Click )
    endif
endfunction

private function OnTileTrack takes nothing returns nothing
    local tileUnique this = tileUnique ( GetHandleData ( GetTriggeringTrackable (  ), TileKey ) )
    if EDITOR then
        if TileFrame != null then
            call RemoveDestructable ( TileFrame )
        endif
        set TileFrame = CreateDestructable ( 'B004', this.x, this.y, 270, 1.25, 0 )
        call SetDescription ( this.name )
    endif
endfunction

struct tileUnique
    real x
    real y
    trackable  track
    uniqueExecute onClick
    destructable d
    string name
    static method create takes string name, real x, real y, integer destId, uniqueExecute onClick returns tileUnique
        local tileUnique this = tileUnique.allocate (  )
        set this.x  = x
        set this.y  = y
        set this.name = name
        set this.onClick = onClick
        set this.d  = CreateDestructable ( destId, x, y, 270, 1.00, 0 )
        set this.track = CreateTrackable ( "CellTrackable.mdl", x, y, 270 * bj_DEGTORAD )
        call TriggerRegisterTrackableHitEvent ( TileHit, this.track )
        call TriggerRegisterTrackableTrackEvent ( TileTrack, this.track )
        call SetHandleData ( this.track, TileKey, integer(this) )
        return this
    endmethod
   
    static method createUnit takes string name, real x, real y, integer unitId, uniqueExecute onClick returns tileUnique
        local tileUnique this = tileUnique.allocate (  )
        local unit u = CreateUnit ( Player ( 0 ), unitId, x, y, 270 )
        set this.x  = x
        set this.y  = y
        set this.name = name
        set this.onClick = onClick
        call SetUnitX ( u, x )
        call SetUnitY ( u, y )
        set this.track = CreateTrackable ( "CellTrackable.mdl", x, y, 270 * bj_DEGTORAD )
        call TriggerRegisterTrackableHitEvent ( TileHit, this.track )
        call TriggerRegisterTrackableTrackEvent ( TileTrack, this.track )
        call SetHandleData ( this.track, TileKey, integer(this) )
        set u = null
        return this
    endmethod
endstruct

private function init takes nothing returns nothing
    call TriggerAddAction ( TileHit, function OnTileHit )
    call TriggerAddAction ( TileTrack, function OnTileTrack )
endfunction

endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library TileStartLoc initializer init requires TileSpecial

globals
    private constant string  Name   = "Start location"
    private constant real    PosX   =  1600.00
    private constant real    PosY   = -1280.00
    private constant integer DestId = 'B008'
endglobals

private function OnTileClick takes nothing returns nothing
    call LEVEL.setStartLoc ( CellEdit )
endfunction

private function init takes nothing returns nothing
    call tileUnique.create ( Name, PosX, PosY, DestId, OnTileClick )
endfunction
   
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library TileExitLoc initializer init requires TileSpecial

globals
    private constant string  Name   = "Exit location"
    private constant real    PosX   =  1856.00
    private constant real    PosY   = -1280.00
    private constant integer DestId = 'B009'
endglobals

private function OnTileClick takes nothing returns nothing
    call LEVEL.setExitLoc ( CellEdit )
endfunction

private function init takes nothing returns nothing
    call tileUnique.create ( Name, PosX, PosY, DestId, OnTileClick )
endfunction
   
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library TileClearEntity initializer init requires TileSpecial

globals
    private constant string  Name   = "Clear entity"
    private constant real    PosX   =  1600.00
    private constant real    PosY   = -896.00
    private constant integer destId = 'B005'
endglobals

private function OnTileClick takes nothing returns nothing
    call CellEdit.clearEntity (  )
    call MoveCellEdit ( CellEdit.xId, CellEdit.yId )
endfunction

private function init takes nothing returns nothing
    local tileUnique t = tileUnique.create ( Name, PosX, PosY, destId, OnTileClick )
    call SetDestructableAnimationSpeed ( t.d, 0 )
endfunction
   
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library TileSawBlade initializer init requires TileSpecial

globals
    private constant string  Name   = "Sawblade"
    private constant real    PosX   =  1728.00
    private constant real    PosY   = -896.00
    private constant integer unitId = 'h002'
endglobals

private function OnTileClick takes nothing returns nothing
    call CellEdit.setEntity ( ENTITY_DEATH_WHEEL, LastDirection, LastVelocity )
    call MoveCellEdit ( CellEdit.xId, CellEdit.yId )
endfunction

private function init takes nothing returns nothing
    call tileUnique.createUnit ( Name, PosX, PosY, unitId, OnTileClick )
endfunction
   
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library TileRocket initializer init requires TileSpecial

globals
    private constant string  Name   = "Rocket"
    private constant real    PosX   =  1856.00
    private constant real    PosY   = -896.00
    private constant integer unitId = 'h005'
endglobals

private function OnTileClick takes nothing returns nothing
    call CellEdit.setEntity ( ENTITY_ROCKET, LastDirection, LastVelocity )
    call MoveCellEdit ( CellEdit.xId, CellEdit.yId )
endfunction

private function init takes nothing returns nothing
    call tileUnique.createUnit ( Name, PosX, PosY, unitId, OnTileClick )
endfunction
   
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library TileBat initializer init requires TileSpecial

globals
    private constant string  Name   = "Bat"
    private constant real    PosX   =  1984.00
    private constant real    PosY   = -896.00
    private constant integer unitId = 'h006'
endglobals

private function OnTileClick takes nothing returns nothing
    call CellEdit.setEntity ( ENTITY_BAT, LastDirection, LastVelocity )
    call MoveCellEdit ( CellEdit.xId, CellEdit.yId )
endfunction

private function init takes nothing returns nothing
    call tileUnique.createUnit ( Name, PosX, PosY, unitId, OnTileClick )
endfunction
   
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library TileTurret initializer init requires TileSpecial

globals
    private constant string  Name   = "Turret"
    private constant real    PosX   =  2112.00
    private constant real    PosY   = -896.00
    private constant integer unitId = 'h007'
endglobals

private function OnTileClick takes nothing returns nothing
    call CellEdit.setEntity ( ENTITY_TURRET, LastDirection, LastVelocity )
    call MoveCellEdit ( CellEdit.xId, CellEdit.yId )
endfunction

private function init takes nothing returns nothing
    call tileUnique.createUnit ( Name, PosX, PosY, unitId, OnTileClick )
endfunction
   
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library EntityTable initializer init requires Data

globals
    private texttag Direction
    private texttag Velocity
    private unit array Filter
    private texttag DirTitle
    private texttag VelTitle
    private unit    Helper
    private constant real TextSize     = 0.03
    private constant real EntityTableX = -1920
    private constant real EntityTableY = -1280
   
    Button DirAdd
    Button DirSub
    Button VelAdd
    Button VelSub
endglobals

function DisplayEntityTable takes entity whichEntity returns nothing
    call SetTextTagVisibility ( Direction, true )
    call SetTextTagVisibility ( Velocity, true )
    call SetTextTagVisibility ( DirTitle, true )
    call SetTextTagVisibility ( VelTitle, true )
    call ShowUnit             ( Filter[0], false )
    call ShowUnit             ( Filter[1], false )
    call ShowUnit             ( Filter[2], false )
    call ShowUnit             ( Filter[3], false )
    call ShowUnit             ( Filter[4], false )
    call ShowUnit             ( Filter[5], false )
    call ShowUnit             ( Filter[6], false )
    call ShowUnit             ( Filter[7], false )
    call SetUnitX             ( Helper, CellEdit.x )
    call SetUnitY             ( Helper, CellEdit.y )
    call ShowUnit             ( Helper, true  )
    call UpdateEntityTable.execute (  )
endfunction

function HideEntityTable takes nothing returns nothing
    call SetTextTagVisibility ( Direction, false )
    call SetTextTagVisibility ( Velocity, false )
    call SetTextTagVisibility ( DirTitle, false )
    call SetTextTagVisibility ( VelTitle, false )
    call ShowUnit             ( Filter[0], true )
    call ShowUnit             ( Filter[1], true )
    call ShowUnit             ( Filter[