diff --git a/2d.png b/2d.png
new file mode 100644
index 00000000..efdbfc2f
Binary files /dev/null and b/2d.png differ
diff --git a/3d.png b/3d.png
new file mode 100644
index 00000000..83ce2a25
Binary files /dev/null and b/3d.png differ
diff --git a/PushEd.gmx/Configs/Default.config.gmx b/PushEd.gmx/Configs/Default.config.gmx
new file mode 100644
index 00000000..0180771a
--- /dev/null
+++ b/PushEd.gmx/Configs/Default.config.gmx
@@ -0,0 +1,696 @@
+
+
+
+ False
+ 0
+ 0
+ false
+
+ MoPub
+ 0
+ 0
+ 0
+ True
+ True
+ True
+ false
+ false
+ True
+ 23.0.1
+ 0
+ 0
+ 23
+ PushEd
+
+
+ 0
+ 0
+ 0
+ false
+ 0
+ 0
+ 0
+
+
+ -1
+ Configs\Default\android\icons\icon_hdpi.png
+ Configs\Default\android\icons\icon_ldpi.png
+ Configs\Default\android\icons\icon_mdpi.png
+ Configs\Default\android\icons\icon_xhdpi.png
+ Configs\Default\android\icons\icon_xxhdpi.png
+ Configs\Default\android\icons\icon_xxxhdpi.png
+ auto
+ -1
+ 0
+
+ -1
+ 1
+ 9
+ 0
+ false
+ 0
+ 0
+ True
+ True
+ True
+ True
+ 2
+ Configs\Default\android\icons\ouyaIcon.png
+ companyname
+ com
+ PushEd
+ 8
+ Configs\Default\android\portrait_splash.png
+ 0
+
+ -1
+
+ 4
+ Configs\Default\android\splash.png
+ 0
+ 23.1.1
+ 23
+ 1024
+ false
+ 0
+ false
+ false
+
+ MoPub
+ 0
+ true
+ true
+ false
+ false
+ false
+ True
+ True
+ True
+ false
+ false
+ True
+ Configs\Default\amazonfire\icons\background.png
+ Configs\Default\amazonfire\icons\banner.png
+ 23.0.1
+ 0
+ 0
+ 23
+ PushEd
+
+
+ false
+ false
+ 0
+ 0
+
+ false
+ Configs\Default\amazonfire\icons\icon_hdpi.png
+ Configs\Default\amazonfire\icons\icon_ldpi.png
+ Configs\Default\amazonfire\icons\icon_mdpi.png
+ Configs\Default\amazonfire\icons\icon_xhdpi.png
+ Configs\Default\amazonfire\icons\icon_xxhdpi.png
+ Configs\Default\amazonfire\icons\icon_xxxhdpi.png
+ auto
+ true
+ true
+ Configs\Default\amazonfire\icons\large_icon.png
+ false
+
+ 0
+ 1
+ 9
+ 0
+ 0
+ True
+ True
+ True
+ True
+ companyname
+ com
+ PushEd
+ 8
+ Configs\Default\amazonfire\portrait_splash.png
+ 0
+
+ -1
+
+ Configs\Default\amazonfire\icons\small_icon
+ Configs\Default\amazonfire\splash.png
+ 0
+ 23.1.1
+ 23
+ 1024
+ false
+ 0
+ false
+ false
+
+ MoPub
+ 0
+ true
+ true
+ false
+ false
+ 0
+ True
+ True
+ True
+ false
+ false
+ True
+ Configs\Default\androidtv\icons\background.png
+ Configs\Default\androidtv\icons\banner.png
+ 23.0.1
+ 0
+ 0
+ 23
+ PushEd
+
+
+ 0
+ false
+ 0
+ 0
+
+ false
+ Configs\Default\androidtv\icons\icon_hdpi.png
+ Configs\Default\androidtv\icons\icon_ldpi.png
+ Configs\Default\androidtv\icons\icon_mdpi.png
+ Configs\Default\androidtv\icons\icon_xhdpi.png
+ Configs\Default\androidtv\icons\icon_xxhdpi.png
+ Configs\Default\androidtv\icons\icon_xxxhdpi.png
+ auto
+ true
+ -1
+ Configs\Default\androidtv\icons\large_icon.png
+ 0
+
+ -1
+ 1
+ 0
+ 0
+ companyname
+ com
+ PushEd
+ 8
+ 0
+
+ -1
+
+ Configs\Default\androidtv\icons\small_icon
+ Configs\Default\androidtv\splash.png
+ 23.1.1
+ 1024
+ false
+ True
+ 1
+
+ nil
+ False
+ false
+ false
+ true
+ 0
+ GameMaker: Studio
+ True
+ 0
+
+
+ false
+ False
+ false
+ 0
+ nil
+ False
+ {99CF62D0-86C6-43F8-B882-4D2E07F732B1}
+ 200864383
+ false
+ true
+ true
+ 2
+ 0
+
+ Supersonic Ads
+ True
+ False
+ Created with GameMaker: Studio
+ False
+
+
+ 0
+ 0
+
+ html5game
+ 0
+ 0
+
+ Configs\Default\html5\fav.ico
+ -1
+ -1
+
+ -1
+ True
+ True
+ index.html
+ -1
+ True
+ Configs\Default\html5\splash.png
+ 2048
+ 0
+ True
+ False
+ nil
+ 0
+
+ True
+ false
+ false
+
+ iAds
+
+ 0
+ < Press refresh to fetch certificate list from Mac>
+ 3
+ PushEd
+
+
+ 0
+ false
+
+ false
+
+
+ 0
+ Configs\Default\ios\icons\app\ipad_152.png
+ Configs\Default\ios\icons\app\ipad_76.png
+ Configs\Default\ios\icons\notification\ipad_20.png
+ Configs\Default\ios\icons\notification\ipad_40.png
+ Configs\Default\ios\icons\app\ipad_pro_167.png
+ Configs\Default\ios\icons\settings\ipad_29.png
+ Configs\Default\ios\icons\settings\ipad_58.png
+ Configs\Default\ios\icons\spotlight\ipad_40.png
+ Configs\Default\ios\icons\spotlight\ipad_80.png
+ Configs\Default\ios\icons\app\iphone_120.png
+ Configs\Default\ios\icons\app\iphone_180.png
+ Configs\Default\ios\icons\notification\iphone_40.png
+ Configs\Default\ios\icons\notification\iphone_60.png
+ Configs\Default\ios\icons\settings\iphone_58.png
+ Configs\Default\ios\icons\settings\iphone_87.png
+ Configs\Default\ios\icons\spotlight\iphone_120.png
+ Configs\Default\ios\icons\spotlight\iphone_80.png
+ Configs\Default\ios\icons\itunes\itunes_1024.png
+ -1
+ Configs\Default\ios\iPadRetinasplash.png
+ Configs\Default\ios\iPadRetinasplashPortrait.png
+ Configs\Default\ios\iPadsplash.png
+ Configs\Default\ios\iPadsplashPortrait.png
+ Configs\Default\ios\iPhone5splash.png
+ Configs\Default\ios\iPhone5splashPortrait.png
+ Configs\Default\ios\iPhone6splash.png
+ Configs\Default\ios\iPhone6splashPortrait.png
+ Configs\Default\ios\iPhone6Plussplash.png
+ Configs\Default\ios\iPhone6PlussplashPortrait.png
+ Configs\Default\ios\iPhone7splash.png
+ Configs\Default\ios\iPhone7splashPortrait.png
+ Configs\Default\ios\iPhone7Plussplash.png
+ Configs\Default\ios\iPhone7PlussplashPortrait.png
+ Configs\Default\ios\iPhone8splash.png
+ Configs\Default\ios\iPhone8splashPortrait.png
+ Configs\Default\ios\iPhone8Plussplash.png
+ Configs\Default\ios\iPhone8PlussplashPortrait.png
+ Configs\Default\ios\IphoneRetinasplash.png
+ Configs\Default\ios\IphoneRetinasplashPortrait.png
+ Configs\Default\ios\iPhonesplash.png
+ Configs\Default\ios\iPhonesplashPortrait.png
+ Configs\Default\ios\iPhonesesplash.png
+ Configs\Default\ios\iPhonesesplashPortrait.png
+ Configs\Default\ios\iPhonexsplash.png
+ Configs\Default\ios\iPhonexsplashPortrait.png
+ 1
+ 0
+ -1
+ -1
+ -1
+ -1
+
+ 0
+ -1
+
+ Configs\Default\ios\splash.png
+ 0
+
+ 1024
+ false
+
+ PushEd
+ 0
+ 0
+ http://www.PushEd.com
+ Configs\Default\linux\icon64.png
+ -1
+ PushEd
+
+ -1
+ -1
+ PushEd
+ 0
+ Configs\Default\linux\splash.png
+ 0
+ 0
+ 2048
+ 0
+ 1
+ 0
+ 255
+ nil
+ false
+
+
+ 0
+ 0
+ 0
+ (c)2016 CompanyName Ltd...
+ 0
+ PushEd
+ 0
+ 0
+ 0
+ Configs\Default\mac\icon512.png
+ -1
+ 1
+ 0
+ 0
+ ~/GameMaker-Studio/PushEd
+ -1
+ -1
+ -1
+ Developer ID Application:
+ 0
+ Configs\Default\mac\splash.png
+ 0
+ 0
+
+ 2048
+ false
+ false
+ true
+ 0
+ 0
+ -1
+
+ Memo1
+ Memo1
+
+
+ 0
+ 2048
+ 1.00
+ 0
+ 0
+ -1
+ 1
+ 0
+
+ Memo1
+
+
+ 0
+ 0
+ 2048
+ 0
+ 0
+ -1
+ 1
+ 0
+
+ Memo1
+ Memo1
+
+
+ 0
+ 0
+ 0
+ 2048
+ true
+ 0
+ true
+ -1
+ true
+ 0
+
+ True
+ true
+ True
+ True
+ 1
+ False
+ false
+ 0
+ 2
+ 0
+ <none>
+ 0
+ 9223372036854775807
+ 1
+ Default
+ http://yourdomain/PushEd
+ 0
+ PushEd
+ 0
+
+ 0
+
+
+ Configs\Default\tizen\icon117.png
+ -1
+ 1
+ 0
+
+ 0
+ 0
+ 0
+ Configs\Default\tizen\splash.png
+ 1024
+ False
+
+ 0
+ < Press refresh to fetch certificate list from Mac>
+ PushEd
+ Configs\Default\tvos\icons\icon1280.png
+ Configs\Default\tvos\icons\icon400.png
+ -1
+ 1
+ 0
+ -1
+ Configs\Default\tvos\splash.png
+ 0
+ 2048
+ False
+ True
+ False
+ 100
+ 0
+
+
+
+ 1
+ 0
+
+ 0
+
+ 0
+
+ 0
+
+ 0
+
+ 0
+ Windows8_TemporaryKey.pfx
+ PushEd
+ 0
+ -1
+ -1
+ -1
+ Configs\Default\Windows8\logos\logo150.png
+ #000000
+ light
+ 1
+ 0
+ 0
+ Win8NativeRunner_TemporaryKey.pfx
+ x86
+ YourPackageDisplayName
+ YourPackageName
+ -1
+ -1
+ YourPublisherName
+ CN=YoyoGames
+ 0
+ false
+ 0
+ Configs\Default\Windows8\logos\logo30.png
+ #000000
+ Configs\Default\Windows8\splashscreen.png
+ Configs\Default\Windows8\logos\logo50.png
+ 1024
+ Configs\Default\Windows8\logos\logo310.png
+ 0
+ 0
+ WinUWPRunner_TemporaryKey.pfx
+ PushEd
+ 0
+ 0
+ -1
+ -1
+ -1
+ Configs\Default\WindowsUAP\logos\LargeLogo.scale-100.png
+ Configs\Default\WindowsUAP\logos\Logo.scale-100.png
+ #000000
+ light
+ 1
+ 0
+ 0
+ x86
+ YourPackageDisplayName
+ YourPackageName
+ -1
+ -1
+ YourPublisherName
+ CN=Sandbox
+ 0
+ false
+ 0
+ Configs\Default\WindowsUAP\logos\SmallLogo.scale-100.png
+ Configs\Default\WindowsUAP\logos\SmallishLogo.scale-100.png
+ #000000
+ Configs\Default\WindowsUAP\SplashScreen.scale-100.png
+ Configs\Default\WindowsUAP\logos\StoreLogo.scale-100.png
+ 1024
+ Configs\Default\WindowsUAP\logos\WideLogo.scale-100.png
+ clBlack
+ False
+ 0
+
+
+ False
+
+ 0
+ Configs\Default\windows\runner_icon.ico
+ Configs\Default\windows\License.txt
+ 0
+ 1
+ Configs\Default\windows\RunnerInstaller.nsi
+
+ 0
+ Configs\Default\windows\Runner_finish.bmp
+ Configs\Default\windows\Runner_header.bmp
+ 0
+ 1
+ #000000
+ Configs\Default\windows\splash.png
+ 0
+ 2048
+ -1
+ 1
+ Configs\Default\WinPhone\SplashScreenImage480.jpg
+ Configs\Default\WinPhone\SplashScreenImage720.jpg
+ Configs\Default\WinPhone\SplashScreenImage.jpg
+
+ 0
+
+ 0
+
+ You
+ 0
+ Configs\Default\WinPhone\CycleSmall.png
+ Configs\Default\WinPhone\CycleWide1.png
+ Configs\Default\WinPhone\CycleWide2.png
+ Configs\Default\WinPhone\CycleWide3.png
+ Configs\Default\WinPhone\CycleWide4.png
+ Configs\Default\WinPhone\CycleWide5.png
+ Configs\Default\WinPhone\CycleWide6.png
+ Configs\Default\WinPhone\CycleWide7.png
+ Configs\Default\WinPhone\CycleWide8.png
+ Configs\Default\WinPhone\CycleWide9.png
+ YourDescription
+ PushEd
+
+
+ Configs\Default\WinPhone\FlipMedBack.png
+ Configs\Default\WinPhone\FlipMedFront.png
+ FlipSmallBack.png
+ Configs\Default\WinPhone\FlipSmallFront.png
+ Configs\Default\WinPhone\FlipWideBack.png
+ Configs\Default\WinPhone\FlipWideFront.png
+ 0
+
+ 1
+ 0
+
+ Configs\Default\WinPhone\ApplicationIcon.png
+ #000000
+
+ Configs\Default\WinPhone\IconicSmall.png
+
+
+
+ Configs\Default\WinPhone\IconicWide.png
+ -1
+ -1
+ -1
+ 1
+ 0
+ -1
+ YourPublisherName
+ f47823f0-220d-459a-bf68-b6ea984b24a8
+ -1
+ 0
+ -1
+ -1
+ -1
+ -1
+ 1024
+ 0
+ False
+ #FFFFFF
+ 0
+
+
+ -1
+ light
+ -1
+ "<!-- Insert the languages your title supports here -->","<!-- os_get_language will only return languages that are listed -->","<!-- ""en"" is supported by default -->","<!-- Resource Language=""en-us""/ -->"
+ 0
+ 0
+
+
+
+
+ "<mx:Ratings Category=""game"">","<!-- Insert your title ratings here -->","<!-- See ""Title Ratings for Xbox One"" white paper -->","<!-- from developer.xboxlive.com for details -->",</mx:Ratings>
+ False
+ False
+ True
+ 0
+ -1
+
+ #FFFFFF
+ 1024
+
+
+
+
+ x
+ y
+ z
+ rotX
+ rotY
+ image_angle
+ image_xscale
+ image_yscale
+ scaleZ
+ image_blend
+ image_alpha
+ true
+
+
+
diff --git a/PushEd.gmx/Configs/Default/Android/icons/icon_hdpi.png b/PushEd.gmx/Configs/Default/Android/icons/icon_hdpi.png
new file mode 100644
index 00000000..ded177e1
Binary files /dev/null and b/PushEd.gmx/Configs/Default/Android/icons/icon_hdpi.png differ
diff --git a/PushEd.gmx/Configs/Default/Android/icons/icon_ldpi.png b/PushEd.gmx/Configs/Default/Android/icons/icon_ldpi.png
new file mode 100644
index 00000000..edf85a0d
Binary files /dev/null and b/PushEd.gmx/Configs/Default/Android/icons/icon_ldpi.png differ
diff --git a/PushEd.gmx/Configs/Default/Android/icons/icon_mdpi.png b/PushEd.gmx/Configs/Default/Android/icons/icon_mdpi.png
new file mode 100644
index 00000000..a6f63dbd
Binary files /dev/null and b/PushEd.gmx/Configs/Default/Android/icons/icon_mdpi.png differ
diff --git a/PushEd.gmx/Configs/Default/Android/icons/icon_xhdpi.png b/PushEd.gmx/Configs/Default/Android/icons/icon_xhdpi.png
new file mode 100644
index 00000000..19049f52
Binary files /dev/null and b/PushEd.gmx/Configs/Default/Android/icons/icon_xhdpi.png differ
diff --git a/PushEd.gmx/Configs/Default/Android/icons/icon_xxhdpi.png b/PushEd.gmx/Configs/Default/Android/icons/icon_xxhdpi.png
new file mode 100644
index 00000000..efc628d3
Binary files /dev/null and b/PushEd.gmx/Configs/Default/Android/icons/icon_xxhdpi.png differ
diff --git a/PushEd.gmx/Configs/Default/Android/icons/icon_xxxhdpi.png b/PushEd.gmx/Configs/Default/Android/icons/icon_xxxhdpi.png
new file mode 100644
index 00000000..72d7d3c1
Binary files /dev/null and b/PushEd.gmx/Configs/Default/Android/icons/icon_xxxhdpi.png differ
diff --git a/PushEd.gmx/Configs/Default/Android/icons/ouyaIcon.png b/PushEd.gmx/Configs/Default/Android/icons/ouyaIcon.png
new file mode 100644
index 00000000..6ef7d2d6
Binary files /dev/null and b/PushEd.gmx/Configs/Default/Android/icons/ouyaIcon.png differ
diff --git a/PushEd.gmx/Configs/Default/Android/portrait_splash.png b/PushEd.gmx/Configs/Default/Android/portrait_splash.png
new file mode 100644
index 00000000..aefc3c58
Binary files /dev/null and b/PushEd.gmx/Configs/Default/Android/portrait_splash.png differ
diff --git a/PushEd.gmx/Configs/Default/Android/splash.png b/PushEd.gmx/Configs/Default/Android/splash.png
new file mode 100644
index 00000000..75cc0f78
Binary files /dev/null and b/PushEd.gmx/Configs/Default/Android/splash.png differ
diff --git a/PushEd.gmx/Configs/Default/HTML5/fav.ico b/PushEd.gmx/Configs/Default/HTML5/fav.ico
new file mode 100644
index 00000000..1dd04b42
Binary files /dev/null and b/PushEd.gmx/Configs/Default/HTML5/fav.ico differ
diff --git a/PushEd.gmx/Configs/Default/HTML5/splash.png b/PushEd.gmx/Configs/Default/HTML5/splash.png
new file mode 100644
index 00000000..4248cbb7
Binary files /dev/null and b/PushEd.gmx/Configs/Default/HTML5/splash.png differ
diff --git a/PushEd.gmx/Configs/Default/Linux/icon64.png b/PushEd.gmx/Configs/Default/Linux/icon64.png
new file mode 100644
index 00000000..58d096ef
Binary files /dev/null and b/PushEd.gmx/Configs/Default/Linux/icon64.png differ
diff --git a/PushEd.gmx/Configs/Default/Linux/splash.png b/PushEd.gmx/Configs/Default/Linux/splash.png
new file mode 100644
index 00000000..f56c9f88
Binary files /dev/null and b/PushEd.gmx/Configs/Default/Linux/splash.png differ
diff --git a/PushEd.gmx/Configs/Default/Mac/icon512.png b/PushEd.gmx/Configs/Default/Mac/icon512.png
new file mode 100644
index 00000000..e5286259
Binary files /dev/null and b/PushEd.gmx/Configs/Default/Mac/icon512.png differ
diff --git a/PushEd.gmx/Configs/Default/Mac/splash.png b/PushEd.gmx/Configs/Default/Mac/splash.png
new file mode 100644
index 00000000..8ebb8ca2
Binary files /dev/null and b/PushEd.gmx/Configs/Default/Mac/splash.png differ
diff --git a/PushEd.gmx/Configs/Default/Windows8/Win8NativeRunner_TemporaryKey.pfx b/PushEd.gmx/Configs/Default/Windows8/Win8NativeRunner_TemporaryKey.pfx
new file mode 100644
index 00000000..86273350
Binary files /dev/null and b/PushEd.gmx/Configs/Default/Windows8/Win8NativeRunner_TemporaryKey.pfx differ
diff --git a/PushEd.gmx/Configs/Default/Windows8/Windows8_TemporaryKey.pfx b/PushEd.gmx/Configs/Default/Windows8/Windows8_TemporaryKey.pfx
new file mode 100644
index 00000000..86273350
Binary files /dev/null and b/PushEd.gmx/Configs/Default/Windows8/Windows8_TemporaryKey.pfx differ
diff --git a/PushEd.gmx/Configs/Default/Windows8/logos/logo150.png b/PushEd.gmx/Configs/Default/Windows8/logos/logo150.png
new file mode 100644
index 00000000..dde18df7
Binary files /dev/null and b/PushEd.gmx/Configs/Default/Windows8/logos/logo150.png differ
diff --git a/PushEd.gmx/Configs/Default/Windows8/logos/logo30.png b/PushEd.gmx/Configs/Default/Windows8/logos/logo30.png
new file mode 100644
index 00000000..9e363f99
Binary files /dev/null and b/PushEd.gmx/Configs/Default/Windows8/logos/logo30.png differ
diff --git a/PushEd.gmx/Configs/Default/Windows8/logos/logo310.png b/PushEd.gmx/Configs/Default/Windows8/logos/logo310.png
new file mode 100644
index 00000000..80d6a033
Binary files /dev/null and b/PushEd.gmx/Configs/Default/Windows8/logos/logo310.png differ
diff --git a/PushEd.gmx/Configs/Default/Windows8/logos/logo50.png b/PushEd.gmx/Configs/Default/Windows8/logos/logo50.png
new file mode 100644
index 00000000..2cd17b13
Binary files /dev/null and b/PushEd.gmx/Configs/Default/Windows8/logos/logo50.png differ
diff --git a/PushEd.gmx/Configs/Default/Windows8/splashscreen.png b/PushEd.gmx/Configs/Default/Windows8/splashscreen.png
new file mode 100644
index 00000000..e1aca274
Binary files /dev/null and b/PushEd.gmx/Configs/Default/Windows8/splashscreen.png differ
diff --git a/PushEd.gmx/Configs/Default/WindowsUAP/SplashScreen.scale-100.png b/PushEd.gmx/Configs/Default/WindowsUAP/SplashScreen.scale-100.png
new file mode 100644
index 00000000..e1aca274
Binary files /dev/null and b/PushEd.gmx/Configs/Default/WindowsUAP/SplashScreen.scale-100.png differ
diff --git a/PushEd.gmx/Configs/Default/WindowsUAP/WinUWPRunner_TemporaryKey.pfx b/PushEd.gmx/Configs/Default/WindowsUAP/WinUWPRunner_TemporaryKey.pfx
new file mode 100644
index 00000000..86273350
Binary files /dev/null and b/PushEd.gmx/Configs/Default/WindowsUAP/WinUWPRunner_TemporaryKey.pfx differ
diff --git a/PushEd.gmx/Configs/Default/WindowsUAP/logos/LargeLogo.scale-100.png b/PushEd.gmx/Configs/Default/WindowsUAP/logos/LargeLogo.scale-100.png
new file mode 100644
index 00000000..24f419d1
Binary files /dev/null and b/PushEd.gmx/Configs/Default/WindowsUAP/logos/LargeLogo.scale-100.png differ
diff --git a/PushEd.gmx/Configs/Default/WindowsUAP/logos/Logo.scale-100.png b/PushEd.gmx/Configs/Default/WindowsUAP/logos/Logo.scale-100.png
new file mode 100644
index 00000000..dde18df7
Binary files /dev/null and b/PushEd.gmx/Configs/Default/WindowsUAP/logos/Logo.scale-100.png differ
diff --git a/PushEd.gmx/Configs/Default/WindowsUAP/logos/SmallLogo.scale-100.png b/PushEd.gmx/Configs/Default/WindowsUAP/logos/SmallLogo.scale-100.png
new file mode 100644
index 00000000..db087da8
Binary files /dev/null and b/PushEd.gmx/Configs/Default/WindowsUAP/logos/SmallLogo.scale-100.png differ
diff --git a/PushEd.gmx/Configs/Default/WindowsUAP/logos/SmallishLogo.scale-100.png b/PushEd.gmx/Configs/Default/WindowsUAP/logos/SmallishLogo.scale-100.png
new file mode 100644
index 00000000..c8aed5f5
Binary files /dev/null and b/PushEd.gmx/Configs/Default/WindowsUAP/logos/SmallishLogo.scale-100.png differ
diff --git a/PushEd.gmx/Configs/Default/WindowsUAP/logos/StoreLogo.scale-100.png b/PushEd.gmx/Configs/Default/WindowsUAP/logos/StoreLogo.scale-100.png
new file mode 100644
index 00000000..2cd17b13
Binary files /dev/null and b/PushEd.gmx/Configs/Default/WindowsUAP/logos/StoreLogo.scale-100.png differ
diff --git a/PushEd.gmx/Configs/Default/WindowsUAP/logos/WideLogo.scale-100.png b/PushEd.gmx/Configs/Default/WindowsUAP/logos/WideLogo.scale-100.png
new file mode 100644
index 00000000..80d6a033
Binary files /dev/null and b/PushEd.gmx/Configs/Default/WindowsUAP/logos/WideLogo.scale-100.png differ
diff --git a/PushEd.gmx/Configs/Default/iOS/iPadRetinaSplash.png b/PushEd.gmx/Configs/Default/iOS/iPadRetinaSplash.png
new file mode 100644
index 00000000..7dad383e
Binary files /dev/null and b/PushEd.gmx/Configs/Default/iOS/iPadRetinaSplash.png differ
diff --git a/PushEd.gmx/Configs/Default/iOS/iPadRetinaSplashPortrait.png b/PushEd.gmx/Configs/Default/iOS/iPadRetinaSplashPortrait.png
new file mode 100644
index 00000000..c4261468
Binary files /dev/null and b/PushEd.gmx/Configs/Default/iOS/iPadRetinaSplashPortrait.png differ
diff --git a/PushEd.gmx/Configs/Default/iOS/iPadsplash.png b/PushEd.gmx/Configs/Default/iOS/iPadsplash.png
new file mode 100644
index 00000000..3dbaca7a
Binary files /dev/null and b/PushEd.gmx/Configs/Default/iOS/iPadsplash.png differ
diff --git a/PushEd.gmx/Configs/Default/iOS/iPadsplashPortrait.png b/PushEd.gmx/Configs/Default/iOS/iPadsplashPortrait.png
new file mode 100644
index 00000000..aefc3c58
Binary files /dev/null and b/PushEd.gmx/Configs/Default/iOS/iPadsplashPortrait.png differ
diff --git a/PushEd.gmx/Configs/Default/iOS/iPhone5splash.png b/PushEd.gmx/Configs/Default/iOS/iPhone5splash.png
new file mode 100644
index 00000000..7f605ccd
Binary files /dev/null and b/PushEd.gmx/Configs/Default/iOS/iPhone5splash.png differ
diff --git a/PushEd.gmx/Configs/Default/iOS/iPhone5splashPortrait.png b/PushEd.gmx/Configs/Default/iOS/iPhone5splashPortrait.png
new file mode 100644
index 00000000..1bebb182
Binary files /dev/null and b/PushEd.gmx/Configs/Default/iOS/iPhone5splashPortrait.png differ
diff --git a/PushEd.gmx/Configs/Default/iOS/iPhone6PlusSplash.png b/PushEd.gmx/Configs/Default/iOS/iPhone6PlusSplash.png
new file mode 100644
index 00000000..0e997c6c
Binary files /dev/null and b/PushEd.gmx/Configs/Default/iOS/iPhone6PlusSplash.png differ
diff --git a/PushEd.gmx/Configs/Default/iOS/iPhone6PlusSplashPortrait.png b/PushEd.gmx/Configs/Default/iOS/iPhone6PlusSplashPortrait.png
new file mode 100644
index 00000000..c2000720
Binary files /dev/null and b/PushEd.gmx/Configs/Default/iOS/iPhone6PlusSplashPortrait.png differ
diff --git a/PushEd.gmx/Configs/Default/iOS/iPhone6Splash.png b/PushEd.gmx/Configs/Default/iOS/iPhone6Splash.png
new file mode 100644
index 00000000..34e3e797
Binary files /dev/null and b/PushEd.gmx/Configs/Default/iOS/iPhone6Splash.png differ
diff --git a/PushEd.gmx/Configs/Default/iOS/iPhone6SplashPortrait.png b/PushEd.gmx/Configs/Default/iOS/iPhone6SplashPortrait.png
new file mode 100644
index 00000000..0bc68ae2
Binary files /dev/null and b/PushEd.gmx/Configs/Default/iOS/iPhone6SplashPortrait.png differ
diff --git a/PushEd.gmx/Configs/Default/iOS/iPhone7PlusSplash.png b/PushEd.gmx/Configs/Default/iOS/iPhone7PlusSplash.png
new file mode 100644
index 00000000..0e997c6c
Binary files /dev/null and b/PushEd.gmx/Configs/Default/iOS/iPhone7PlusSplash.png differ
diff --git a/PushEd.gmx/Configs/Default/iOS/iPhone7PlusSplashPortrait.png b/PushEd.gmx/Configs/Default/iOS/iPhone7PlusSplashPortrait.png
new file mode 100644
index 00000000..c2000720
Binary files /dev/null and b/PushEd.gmx/Configs/Default/iOS/iPhone7PlusSplashPortrait.png differ
diff --git a/PushEd.gmx/Configs/Default/iOS/iPhone7Splash.png b/PushEd.gmx/Configs/Default/iOS/iPhone7Splash.png
new file mode 100644
index 00000000..34e3e797
Binary files /dev/null and b/PushEd.gmx/Configs/Default/iOS/iPhone7Splash.png differ
diff --git a/PushEd.gmx/Configs/Default/iOS/iPhone7SplashPortrait.png b/PushEd.gmx/Configs/Default/iOS/iPhone7SplashPortrait.png
new file mode 100644
index 00000000..0bc68ae2
Binary files /dev/null and b/PushEd.gmx/Configs/Default/iOS/iPhone7SplashPortrait.png differ
diff --git a/PushEd.gmx/Configs/Default/iOS/iPhone8PlusSplash.png b/PushEd.gmx/Configs/Default/iOS/iPhone8PlusSplash.png
new file mode 100644
index 00000000..0e997c6c
Binary files /dev/null and b/PushEd.gmx/Configs/Default/iOS/iPhone8PlusSplash.png differ
diff --git a/PushEd.gmx/Configs/Default/iOS/iPhone8PlusSplashPortrait.png b/PushEd.gmx/Configs/Default/iOS/iPhone8PlusSplashPortrait.png
new file mode 100644
index 00000000..c2000720
Binary files /dev/null and b/PushEd.gmx/Configs/Default/iOS/iPhone8PlusSplashPortrait.png differ
diff --git a/PushEd.gmx/Configs/Default/iOS/iPhone8Splash.png b/PushEd.gmx/Configs/Default/iOS/iPhone8Splash.png
new file mode 100644
index 00000000..34e3e797
Binary files /dev/null and b/PushEd.gmx/Configs/Default/iOS/iPhone8Splash.png differ
diff --git a/PushEd.gmx/Configs/Default/iOS/iPhone8SplashPortrait.png b/PushEd.gmx/Configs/Default/iOS/iPhone8SplashPortrait.png
new file mode 100644
index 00000000..0bc68ae2
Binary files /dev/null and b/PushEd.gmx/Configs/Default/iOS/iPhone8SplashPortrait.png differ
diff --git a/PushEd.gmx/Configs/Default/iOS/iPhoneRetinasplash.png b/PushEd.gmx/Configs/Default/iOS/iPhoneRetinasplash.png
new file mode 100644
index 00000000..cc606c49
Binary files /dev/null and b/PushEd.gmx/Configs/Default/iOS/iPhoneRetinasplash.png differ
diff --git a/PushEd.gmx/Configs/Default/iOS/iPhoneRetinasplashPortrait.png b/PushEd.gmx/Configs/Default/iOS/iPhoneRetinasplashPortrait.png
new file mode 100644
index 00000000..bc2332b7
Binary files /dev/null and b/PushEd.gmx/Configs/Default/iOS/iPhoneRetinasplashPortrait.png differ
diff --git a/PushEd.gmx/Configs/Default/iOS/iPhoneSEsplash.png b/PushEd.gmx/Configs/Default/iOS/iPhoneSEsplash.png
new file mode 100644
index 00000000..7f605ccd
Binary files /dev/null and b/PushEd.gmx/Configs/Default/iOS/iPhoneSEsplash.png differ
diff --git a/PushEd.gmx/Configs/Default/iOS/iPhoneSEsplashPortrait.png b/PushEd.gmx/Configs/Default/iOS/iPhoneSEsplashPortrait.png
new file mode 100644
index 00000000..1bebb182
Binary files /dev/null and b/PushEd.gmx/Configs/Default/iOS/iPhoneSEsplashPortrait.png differ
diff --git a/PushEd.gmx/Configs/Default/iOS/iPhoneXSplash.png b/PushEd.gmx/Configs/Default/iOS/iPhoneXSplash.png
new file mode 100644
index 00000000..d57845b7
Binary files /dev/null and b/PushEd.gmx/Configs/Default/iOS/iPhoneXSplash.png differ
diff --git a/PushEd.gmx/Configs/Default/iOS/iPhoneXSplashPortrait.png b/PushEd.gmx/Configs/Default/iOS/iPhoneXSplashPortrait.png
new file mode 100644
index 00000000..24400e8f
Binary files /dev/null and b/PushEd.gmx/Configs/Default/iOS/iPhoneXSplashPortrait.png differ
diff --git a/PushEd.gmx/Configs/Default/iOS/iPhonesplash.png b/PushEd.gmx/Configs/Default/iOS/iPhonesplash.png
new file mode 100644
index 00000000..1425f785
Binary files /dev/null and b/PushEd.gmx/Configs/Default/iOS/iPhonesplash.png differ
diff --git a/PushEd.gmx/Configs/Default/iOS/iPhonesplashPortrait.png b/PushEd.gmx/Configs/Default/iOS/iPhonesplashPortrait.png
new file mode 100644
index 00000000..62d072e7
Binary files /dev/null and b/PushEd.gmx/Configs/Default/iOS/iPhonesplashPortrait.png differ
diff --git a/PushEd.gmx/Configs/Default/iOS/icons/app/ipad_152.png b/PushEd.gmx/Configs/Default/iOS/icons/app/ipad_152.png
new file mode 100644
index 00000000..08dd6122
Binary files /dev/null and b/PushEd.gmx/Configs/Default/iOS/icons/app/ipad_152.png differ
diff --git a/PushEd.gmx/Configs/Default/iOS/icons/app/ipad_76.png b/PushEd.gmx/Configs/Default/iOS/icons/app/ipad_76.png
new file mode 100644
index 00000000..b41922e7
Binary files /dev/null and b/PushEd.gmx/Configs/Default/iOS/icons/app/ipad_76.png differ
diff --git a/PushEd.gmx/Configs/Default/iOS/icons/app/ipad_pro_167.png b/PushEd.gmx/Configs/Default/iOS/icons/app/ipad_pro_167.png
new file mode 100644
index 00000000..6ee8e4bf
Binary files /dev/null and b/PushEd.gmx/Configs/Default/iOS/icons/app/ipad_pro_167.png differ
diff --git a/PushEd.gmx/Configs/Default/iOS/icons/app/iphone_120.png b/PushEd.gmx/Configs/Default/iOS/icons/app/iphone_120.png
new file mode 100644
index 00000000..3f3337d0
Binary files /dev/null and b/PushEd.gmx/Configs/Default/iOS/icons/app/iphone_120.png differ
diff --git a/PushEd.gmx/Configs/Default/iOS/icons/app/iphone_180.png b/PushEd.gmx/Configs/Default/iOS/icons/app/iphone_180.png
new file mode 100644
index 00000000..27fbd05f
Binary files /dev/null and b/PushEd.gmx/Configs/Default/iOS/icons/app/iphone_180.png differ
diff --git a/PushEd.gmx/Configs/Default/iOS/icons/itunes/itunes_1024.png b/PushEd.gmx/Configs/Default/iOS/icons/itunes/itunes_1024.png
new file mode 100644
index 00000000..6979be82
Binary files /dev/null and b/PushEd.gmx/Configs/Default/iOS/icons/itunes/itunes_1024.png differ
diff --git a/PushEd.gmx/Configs/Default/iOS/icons/notification/ipad_20.png b/PushEd.gmx/Configs/Default/iOS/icons/notification/ipad_20.png
new file mode 100644
index 00000000..ca89de59
Binary files /dev/null and b/PushEd.gmx/Configs/Default/iOS/icons/notification/ipad_20.png differ
diff --git a/PushEd.gmx/Configs/Default/iOS/icons/notification/ipad_40.png b/PushEd.gmx/Configs/Default/iOS/icons/notification/ipad_40.png
new file mode 100644
index 00000000..cf142cbc
Binary files /dev/null and b/PushEd.gmx/Configs/Default/iOS/icons/notification/ipad_40.png differ
diff --git a/PushEd.gmx/Configs/Default/iOS/icons/notification/iphone_40.png b/PushEd.gmx/Configs/Default/iOS/icons/notification/iphone_40.png
new file mode 100644
index 00000000..ea54faf5
Binary files /dev/null and b/PushEd.gmx/Configs/Default/iOS/icons/notification/iphone_40.png differ
diff --git a/PushEd.gmx/Configs/Default/iOS/icons/notification/iphone_60.png b/PushEd.gmx/Configs/Default/iOS/icons/notification/iphone_60.png
new file mode 100644
index 00000000..b1cc847e
Binary files /dev/null and b/PushEd.gmx/Configs/Default/iOS/icons/notification/iphone_60.png differ
diff --git a/PushEd.gmx/Configs/Default/iOS/icons/settings/ipad_29.png b/PushEd.gmx/Configs/Default/iOS/icons/settings/ipad_29.png
new file mode 100644
index 00000000..15cc6334
Binary files /dev/null and b/PushEd.gmx/Configs/Default/iOS/icons/settings/ipad_29.png differ
diff --git a/PushEd.gmx/Configs/Default/iOS/icons/settings/ipad_58.png b/PushEd.gmx/Configs/Default/iOS/icons/settings/ipad_58.png
new file mode 100644
index 00000000..ecd94e19
Binary files /dev/null and b/PushEd.gmx/Configs/Default/iOS/icons/settings/ipad_58.png differ
diff --git a/PushEd.gmx/Configs/Default/iOS/icons/settings/iphone_58.png b/PushEd.gmx/Configs/Default/iOS/icons/settings/iphone_58.png
new file mode 100644
index 00000000..fceb4ea2
Binary files /dev/null and b/PushEd.gmx/Configs/Default/iOS/icons/settings/iphone_58.png differ
diff --git a/PushEd.gmx/Configs/Default/iOS/icons/settings/iphone_87.png b/PushEd.gmx/Configs/Default/iOS/icons/settings/iphone_87.png
new file mode 100644
index 00000000..aeb6236b
Binary files /dev/null and b/PushEd.gmx/Configs/Default/iOS/icons/settings/iphone_87.png differ
diff --git a/PushEd.gmx/Configs/Default/iOS/icons/spotlight/ipad_40.png b/PushEd.gmx/Configs/Default/iOS/icons/spotlight/ipad_40.png
new file mode 100644
index 00000000..7c9bee8b
Binary files /dev/null and b/PushEd.gmx/Configs/Default/iOS/icons/spotlight/ipad_40.png differ
diff --git a/PushEd.gmx/Configs/Default/iOS/icons/spotlight/ipad_80.png b/PushEd.gmx/Configs/Default/iOS/icons/spotlight/ipad_80.png
new file mode 100644
index 00000000..6ec9d9fb
Binary files /dev/null and b/PushEd.gmx/Configs/Default/iOS/icons/spotlight/ipad_80.png differ
diff --git a/PushEd.gmx/Configs/Default/iOS/icons/spotlight/iphone_120.png b/PushEd.gmx/Configs/Default/iOS/icons/spotlight/iphone_120.png
new file mode 100644
index 00000000..8ae41095
Binary files /dev/null and b/PushEd.gmx/Configs/Default/iOS/icons/spotlight/iphone_120.png differ
diff --git a/PushEd.gmx/Configs/Default/iOS/icons/spotlight/iphone_80.png b/PushEd.gmx/Configs/Default/iOS/icons/spotlight/iphone_80.png
new file mode 100644
index 00000000..079fe6d8
Binary files /dev/null and b/PushEd.gmx/Configs/Default/iOS/icons/spotlight/iphone_80.png differ
diff --git a/PushEd.gmx/Configs/Default/iOS/splash.png b/PushEd.gmx/Configs/Default/iOS/splash.png
new file mode 100644
index 00000000..0b9f0263
Binary files /dev/null and b/PushEd.gmx/Configs/Default/iOS/splash.png differ
diff --git a/PushEd.gmx/Configs/Default/iOS/splashPortrait.png b/PushEd.gmx/Configs/Default/iOS/splashPortrait.png
new file mode 100644
index 00000000..b9fd91ba
Binary files /dev/null and b/PushEd.gmx/Configs/Default/iOS/splashPortrait.png differ
diff --git a/PushEd.gmx/Configs/Default/windows/License.txt b/PushEd.gmx/Configs/Default/windows/License.txt
new file mode 100644
index 00000000..1842321f
--- /dev/null
+++ b/PushEd.gmx/Configs/Default/windows/License.txt
@@ -0,0 +1,42 @@
+This Application was made using GameMaker. GameMaker is the intellectual property of YoYo Games Ltd, a company of the United Kingdom.
+
+THIS END USER LICENSE AGREEMENT APPLIES TO ALL USE OF THIS APPLICATION.
+
+Scope
+-----
+You may:
+
+Install and use this Application for personal, or commercial use [provided you have paid any license fee, if this applies];
+
+You must not:
+
+use, copy, transfer, distribute modify, adapt, merge, translate, decompile, disassemble, or reverse engineer the Application or part of it except as expressly permitted by this Licence;
+remove, modify, or tamper with any copyright, trademark or other notice;
+use the Application for any illegal, unlawful or immoral purposes.
+
+
+Term
+----
+This License will end automatically if you breach any of the terms.
+When this License ends you must stop all use of the Application and destroy and erase any copies you have.
+
+
+Liability
+---------
+
+YoYo Games Limited (“YoYo Games) is not involved in the creation of this Application except only that it was made using GameMaker. YoYo Games warrants only that GameMaker will function satisfactorily if used properly and on the correct operating system.
+YoYo Games does not warrant that the operation of the Appication will be uninterrupted or error free or that errors can be corrected. You install and use the Application at your own risk and in no event will YoYo Games be liable to you for any loss or damage of any kind (except personal injury or death arising from YoYo Games’ negligence) including lost profits or other consequential loss arising from the use of or inability to use the Application or from errors or deficiencies in it whether caused by negligence or otherwise.
+To the maximum extent permitted by law, YoYo Games Limited excludes liability for all warranties, conditions and other terms which but for this notice would have effect.
+
+
+General
+-------
+
+All copyright and other intellectual property rights in GameMaker and parts of it included in the Application belong to and vest in YoYo Games. All rights of YoYo Games are hereby asserted and reserved.
+All trade marks are the property of their respective owners and YoYo Games makes no warranty or representation in respect of and has no responsibility and excludes all liability for any trade mark or third party content.
+The rights granted in this license are non-exclusive.
+This license does not affect your statutory rights as a consumer.
+This License is governed by English Law and the parties submit to the exclusive jurisdiction of the English Courts.
+If you have a problem with this Application please contact the person who made it, not YoYo Games.
+
+Click “ACCEPT” and accept these terms to install and use this Application. If you do not accept them terms you may not continue with the installation and you must not install, use or run this software on this or any other computer.
diff --git a/PushEd.gmx/Configs/Default/windows/RunnerInstaller.nsi b/PushEd.gmx/Configs/Default/windows/RunnerInstaller.nsi
new file mode 100644
index 00000000..cd77b582
--- /dev/null
+++ b/PushEd.gmx/Configs/Default/windows/RunnerInstaller.nsi
@@ -0,0 +1,213 @@
+; RunnerInstaller.nsi
+;
+; This script is based on example1.nsi, but it remember the directory,
+; has uninstall support and (optionally) installs start menu shortcuts.
+;
+; It will install example2.nsi into a directory that the user selects,
+
+;--------------------------------
+!include MUI2.nsh
+
+!ifndef FULL_VERSION
+!define FULL_VERSION "1.0.0.0"
+!endif
+!ifndef SOURCE_DIR
+!define SOURCE_DIR "C:\source\temp\InstallerTest\runner"
+!endif
+!ifndef INSTALLER_FILENAME
+!define INSTALLER_FILENAME "C:\source\temp\InstallerTest\RunnerInstaller.exe"
+!endif
+
+!ifndef MAKENSIS
+!define MAKENSIS "%appdata%\GameMaker-Studio\makensis"
+!endif
+
+!ifndef COMPANY_NAME
+!define COMPANY_NAME ""
+!endif
+
+!ifndef COPYRIGHT_TXT
+!define COPYRIGHT_TXT "(c)Copyright 2013"
+!endif
+
+!ifndef FILE_DESC
+!define FILE_DESC "Created with GameMaker:Studio"
+!endif
+
+!ifndef LICENSE_NAME
+!define LICENSE_NAME "License.txt"
+!endif
+
+!ifndef ICON_FILE
+!define ICON_FILE "icon.ico"
+!endif
+
+!ifndef IMAGE_FINISHED
+!define IMAGE_FINISHED "Runner_finish.bmp"
+!endif
+
+!ifndef IMAGE_HEADER
+!define IMAGE_HEADER "Runner_header.bmp"
+!endif
+
+!ifndef PRODUCT_NAME
+!define PRODUCT_NAME "Runner"
+!endif
+
+!define APP_NAME "${PRODUCT_NAME}"
+!define SHORT_NAME "${PRODUCT_NAME}"
+
+!ifndef EXE_NAME
+!define EXE_NAME "${PRODUCT_NAME}"
+!endif
+
+
+;;USAGE:
+!define MIN_FRA_MAJOR "2"
+!define MIN_FRA_MINOR "0"
+!define MIN_FRA_BUILD "*"
+
+!addplugindir "."
+
+;--------------------------------
+
+; The name of the installer
+Name "${APP_NAME}"
+Caption "${APP_NAME}"
+BrandingText "${APP_NAME}"
+
+; The file to write
+OutFile "${INSTALLER_FILENAME}"
+
+; The default installation directory
+InstallDir "$PROFILE\${APP_NAME}"
+
+; Registry key to check for directory (so if you install again, it will
+; overwrite the old one automatically)
+InstallDirRegKey HKCU "Software\Runner" "Install_Dir"
+
+; Request application privileges for Windows Vista
+RequestExecutionLevel admin
+
+
+VIProductVersion "${FULL_VERSION}"
+VIAddVersionKey /LANG=1033 "FileVersion" "${FULL_VERSION}"
+VIAddVersionKey /LANG=1033 "ProductVersion" "${FULL_VERSION}"
+VIAddVersionKey /LANG=1033 "ProductName" "${PRODUCT_NAME}"
+VIAddVersionKey /LANG=1033 "CompanyName" "${PRODUCT_PUBLISHER}"
+VIAddVersionKey /LANG=1033 "LegalCopyright" "${COPYRIGHT_TXT}"
+VIAddVersionKey /LANG=1033 "FileDescription" "${FILE_DESC}"
+
+
+
+!define MUI_HEADERIMAGE
+!define MUI_HEADERIMAGE_BITMAP_NOSTRETCH
+!define MUI_ICON "${ICON_FILE}"
+!define MUI_WELCOMEFINISHPAGE_BITMAP "${IMAGE_FINISHED}"
+!define MUI_HEADERIMAGE_BITMAP "${IMAGE_HEADER}"
+!define MUI_WELCOMEFINISHPAGE_BITMAP_NOSTRETCH
+
+
+;--------------------------------
+
+; Pages
+!insertmacro MUI_PAGE_LICENSE "${LICENSE_NAME}"
+!insertmacro MUI_PAGE_COMPONENTS
+!insertmacro MUI_PAGE_DIRECTORY
+!insertmacro MUI_PAGE_INSTFILES
+ # These indented statements modify settings for MUI_PAGE_FINISH
+ !define MUI_FINISHPAGE_NOAUTOCLOSE
+ !define MUI_FINISHPAGE_RUN_TEXT "Start ${PRODUCT_NAME}"
+ !define MUI_FINISHPAGE_RUN "$INSTDIR\${EXE_NAME}.exe"
+!insertmacro MUI_PAGE_FINISH
+
+Var DirectXSetupError
+
+UninstPage uninstConfirm
+UninstPage instfiles
+
+!insertmacro MUI_LANGUAGE "English"
+;--------------------------------
+
+; The stuff to install
+Section `${APP_NAME}`
+ SectionIn RO
+
+ ; Set output path to the installation directory.
+ SetOutPath $INSTDIR
+
+ ; Put file there
+ File "${LICENSE_NAME}"
+ File /r "${SOURCE_DIR}\*.*"
+
+ ; Write the uninstall keys for Windows
+ WriteRegStr SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\${SHORT_NAME}" "DisplayName" "${APP_NAME}"
+ WriteRegStr SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\${SHORT_NAME}" "UninstallString" '"$INSTDIR\uninstall.exe"'
+ WriteRegDWORD SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\${SHORT_NAME}" "NoModify" 1
+ WriteRegDWORD SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\${SHORT_NAME}" "NoRepair" 1
+ WriteUninstaller "uninstall.exe"
+
+SectionEnd
+
+; Optional section (can be disabled by the user)
+Section "Start Menu Shortcuts"
+
+ CreateDirectory "$SMPROGRAMS\${APP_NAME}"
+ CreateShortCut "$SMPROGRAMS\${APP_NAME}\Uninstall.lnk" "$INSTDIR\uninstall.exe" "" "$INSTDIR\uninstall.exe" 0
+ CreateShortCut "$SMPROGRAMS\${APP_NAME}\${APP_NAME}.lnk" "$INSTDIR\${EXE_NAME}.exe" "" "$INSTDIR\${EXE_NAME}.exe"
+ CreateShortCut "$SMPROGRAMS\${APP_NAME}\${APP_NAME} License.lnk" "notepad.exe" "$INSTDIR\License.txt"
+
+SectionEnd
+
+
+; Optional section (can be enabled by the user)
+Section /o "Desktop shortcut"
+
+ CreateShortCut "$DESKTOP\${APP_NAME}.lnk" "$INSTDIR\${EXE_NAME}.exe" ""
+
+SectionEnd
+
+
+;--------------------------------
+
+; Uninstaller
+
+Section "Uninstall"
+ ; Remove registry keys
+ DeleteRegKey SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\${SHORT_NAME}"
+
+ ; Remove files and uninstaller (everything)
+ RMDir /r "$INSTDIR"
+
+ ; Remove desktop icon
+ Delete "$DESKTOP\${APP_NAME}.lnk"
+
+ ; Remove shortcuts, if any
+ Delete "$SMPROGRAMS\${APP_NAME}\*.*"
+
+ ; Remove directories used
+ RMDir "$SMPROGRAMS\${APP_NAME}"
+ RMDir "$INSTDIR"
+
+SectionEnd
+
+
+;--------------------------------
+;
+; This should be the LAST section available....
+;
+Section "DirectX Install" SEC_DIRECTX
+
+ SectionIn RO
+
+ SetOutPath "$TEMP"
+ File "${MAKENSIS}\dxwebsetup.exe"
+ DetailPrint "Running DirectX Setup..."
+ ExecWait '"$TEMP\dxwebsetup.exe" /Q' $DirectXSetupError
+ DetailPrint "Finished DirectX Setup"
+
+ Delete "$TEMP\dxwebsetup.exe"
+
+ SetOutPath "$INSTDIR"
+
+SectionEnd
diff --git a/PushEd.gmx/Configs/Default/windows/Runner_Icon_256.ico b/PushEd.gmx/Configs/Default/windows/Runner_Icon_256.ico
new file mode 100644
index 00000000..655f1c1f
Binary files /dev/null and b/PushEd.gmx/Configs/Default/windows/Runner_Icon_256.ico differ
diff --git a/PushEd.gmx/Configs/Default/windows/Runner_finish.bmp b/PushEd.gmx/Configs/Default/windows/Runner_finish.bmp
new file mode 100644
index 00000000..01b4a2c5
Binary files /dev/null and b/PushEd.gmx/Configs/Default/windows/Runner_finish.bmp differ
diff --git a/PushEd.gmx/Configs/Default/windows/Runner_header.bmp b/PushEd.gmx/Configs/Default/windows/Runner_header.bmp
new file mode 100644
index 00000000..db9ea12c
Binary files /dev/null and b/PushEd.gmx/Configs/Default/windows/Runner_header.bmp differ
diff --git a/PushEd.gmx/Configs/Default/windows/runner_icon.ico b/PushEd.gmx/Configs/Default/windows/runner_icon.ico
new file mode 100644
index 00000000..f6c1b444
Binary files /dev/null and b/PushEd.gmx/Configs/Default/windows/runner_icon.ico differ
diff --git a/PushEd.gmx/Configs/Default/windows/splash.png b/PushEd.gmx/Configs/Default/windows/splash.png
new file mode 100644
index 00000000..d89882f9
Binary files /dev/null and b/PushEd.gmx/Configs/Default/windows/splash.png differ
diff --git a/PushEd.gmx/PushEd.project.gmx b/PushEd.gmx/PushEd.project.gmx
new file mode 100644
index 00000000..c5f91422
--- /dev/null
+++ b/PushEd.gmx/PushEd.project.gmx
@@ -0,0 +1,747 @@
+
+
+
+ Configs\Default
+
+
+
+
+
+
+ sprites\PEd_guiSprCheckbox
+ sprites\PEd_guiSprIcons
+ sprites\PEd_guiSprInput
+ sprites\PEd_guiSprMisc
+ sprites\PEd_guiSprPanel
+ sprites\PEd_guiSprRectangle
+ sprites\PEd_guiSprRoll
+ sprites\PEd_guiSprScrollbarHor
+ sprites\PEd_guiSprScrollbarVer
+ sprites\PEd_guiSprShadow
+ sprites\PEd_guiSprWarning
+ sprites\PEd_guiSprWindowCross
+
+ sprites\PEd_sprTest
+ sprites\PEd_sprDummy2D
+
+
+
+
+ background\PEd_bgrDef
+ background\PEd_bgrTiles
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ shaders\PEd_shInstSelect.shader
+ shaders\PEd_shInstHighlight.shader
+ shaders\PEd_shOutline.shader
+
+
+
+
+ fonts\PEd_fntBold
+ fonts\PEd_fntNormal
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ rooms\PEd_rmEditor
+ rooms\PEd_rmPlay
+
+
+
+ help.rtf
+
+
+ 0
+
+ 0
+
+
diff --git a/PushEd.gmx/background/PEd_bgrDef.background.gmx b/PushEd.gmx/background/PEd_bgrDef.background.gmx
new file mode 100644
index 00000000..f23a204a
--- /dev/null
+++ b/PushEd.gmx/background/PEd_bgrDef.background.gmx
@@ -0,0 +1,19 @@
+
+
+ 0
+ 16
+ 16
+ 0
+ 0
+ 0
+ 0
+ -1
+ -1
+
+ 0
+
+ -1
+ 128
+ 128
+ images\PEd_bgrDef.png
+
diff --git a/PushEd.gmx/background/PEd_bgrTiles.background.gmx b/PushEd.gmx/background/PEd_bgrTiles.background.gmx
new file mode 100644
index 00000000..82128b24
--- /dev/null
+++ b/PushEd.gmx/background/PEd_bgrTiles.background.gmx
@@ -0,0 +1,19 @@
+
+
+ -1
+ 64
+ 64
+ 0
+ 0
+ 0
+ 0
+ -1
+ -1
+
+ 0
+
+ 0
+ 256
+ 256
+ images\PEd_bgrTiles.png
+
diff --git a/PushEd.gmx/background/images/PEd_bgrDef.png b/PushEd.gmx/background/images/PEd_bgrDef.png
new file mode 100644
index 00000000..5e2fbc93
Binary files /dev/null and b/PushEd.gmx/background/images/PEd_bgrDef.png differ
diff --git a/PushEd.gmx/background/images/PEd_bgrTiles.png b/PushEd.gmx/background/images/PEd_bgrTiles.png
new file mode 100644
index 00000000..c4b4441f
Binary files /dev/null and b/PushEd.gmx/background/images/PEd_bgrTiles.png differ
diff --git a/PushEd.gmx/extensions/yymanifest.xml b/PushEd.gmx/extensions/yymanifest.xml
new file mode 100644
index 00000000..7151a40e
--- /dev/null
+++ b/PushEd.gmx/extensions/yymanifest.xml
@@ -0,0 +1,611 @@
+
+
+
+ PushEd.extension.gmx
+ PushEd\Documentation.zip
+ PushEd\Licence.txt
+ PushEd\PushEd.gml
+ PushEd\Assets\Backgrounds\PEd_bgrDef.background.gmx
+ PushEd\Assets\Backgrounds\PEd_bgrTiles.background.gmx
+ PushEd\Assets\Backgrounds\images\PEd_bgrDef.png
+ PushEd\Assets\Backgrounds\images\PEd_bgrTiles.png
+ PushEd\Assets\Fonts\PEd_fntBold.font.gmx
+ PushEd\Assets\Fonts\PEd_fntBold.png
+ PushEd\Assets\Fonts\PEd_fntNormal.font.gmx
+ PushEd\Assets\Fonts\PEd_fntNormal.png
+ PushEd\Assets\Objects\oBlank.object.gmx
+ PushEd\Assets\Objects\oBlock.object.gmx
+ PushEd\Assets\Objects\oCone.object.gmx
+ PushEd\Assets\Objects\oCylinder.object.gmx
+ PushEd\Assets\Objects\oEllipsoid.object.gmx
+ PushEd\Assets\Objects\oSprite.object.gmx
+ PushEd\Assets\Objects\PEd_oEditor.object.gmx
+ PushEd\Assets\Objects\PEd_oObject.object.gmx
+ PushEd\Assets\Objects\PEd_oObject2D.object.gmx
+ PushEd\Assets\Objects\PEd_oObject3D.object.gmx
+ PushEd\Assets\Objects\PEd_oPivot.object.gmx
+ PushEd\Assets\Objects\PEd_oPlay.object.gmx
+ PushEd\Assets\Rooms\PEd_rmEditor.room.gmx
+ PushEd\Assets\Rooms\PEd_rmPlay.room.gmx
+ PushEd\Assets\Scripts\PEd_actClearRoom.gml
+ PushEd\Assets\Scripts\PEd_actClearSelection.gml
+ PushEd\Assets\Scripts\PEd_actCloseWindow.gml
+ PushEd\Assets\Scripts\PEd_actCopySelectedObjects.gml
+ PushEd\Assets\Scripts\PEd_actCreateEmptyRoom.gml
+ PushEd\Assets\Scripts\PEd_actDestroySelectedObjects.gml
+ PushEd\Assets\Scripts\PEd_actEmpty.gml
+ PushEd\Assets\Scripts\PEd_actExit.gml
+ PushEd\Assets\Scripts\PEd_actExportRoom.gml
+ PushEd\Assets\Scripts\PEd_actHideSelectedObjects.gml
+ PushEd\Assets\Scripts\PEd_actImportRoom.gml
+ PushEd\Assets\Scripts\PEd_actInstallMap.gml
+ PushEd\Assets\Scripts\PEd_actMovePivotHere.gml
+ PushEd\Assets\Scripts\PEd_actMoveTool.gml
+ PushEd\Assets\Scripts\PEd_actOpenRoom.gml
+ PushEd\Assets\Scripts\PEd_actPlayRoom.gml
+ PushEd\Assets\Scripts\PEd_actRotateTool.gml
+ PushEd\Assets\Scripts\PEd_actSaveRoom.gml
+ PushEd\Assets\Scripts\PEd_actSaveRoomAs.gml
+ PushEd\Assets\Scripts\PEd_actScaleTool.gml
+ PushEd\Assets\Scripts\PEd_actShowInfo.gml
+ PushEd\Assets\Scripts\PEd_actSwitchDebug.gml
+ PushEd\Assets\Scripts\PEd_actSwitchEditMode.gml
+ PushEd\Assets\Scripts\PEd_actSwitchFloor.gml
+ PushEd\Assets\Scripts\PEd_actSwitchGrid.gml
+ PushEd\Assets\Scripts\PEd_actSwitchTo2D.gml
+ PushEd\Assets\Scripts\PEd_actSwitchTo3D.gml
+ PushEd\Assets\Scripts\PEd_actSwitchToObjectMode.gml
+ PushEd\Assets\Scripts\PEd_actSwitchTool.gml
+ PushEd\Assets\Scripts\PEd_actSwitchToTileMode.gml
+ PushEd\Assets\Scripts\PEd_addCustomData.gml
+ PushEd\Assets\Scripts\PEd_argbToAlpha.gml
+ PushEd\Assets\Scripts\PEd_argbToColour.gml
+ PushEd\Assets\Scripts\PEd_assignVariable.gml
+ PushEd\Assets\Scripts\PEd_backgroundGetImage.gml
+ PushEd\Assets\Scripts\PEd_backgroundGetSpeedHor.gml
+ PushEd\Assets\Scripts\PEd_backgroundGetSpeedVer.gml
+ PushEd\Assets\Scripts\PEd_backgroundGetStretch.gml
+ PushEd\Assets\Scripts\PEd_backgroundGetTiledHor.gml
+ PushEd\Assets\Scripts\PEd_backgroundGetTiledVer.gml
+ PushEd\Assets\Scripts\PEd_backgroundGetVisible.gml
+ PushEd\Assets\Scripts\PEd_backgroundGetX.gml
+ PushEd\Assets\Scripts\PEd_backgroundGetY.gml
+ PushEd\Assets\Scripts\PEd_backgroundIsForeground.gml
+ PushEd\Assets\Scripts\PEd_backgroundSetAsForeground.gml
+ PushEd\Assets\Scripts\PEd_backgroundSetImage.gml
+ PushEd\Assets\Scripts\PEd_backgroundSetSpeedHor.gml
+ PushEd\Assets\Scripts\PEd_backgroundSetSpeedVer.gml
+ PushEd\Assets\Scripts\PEd_backgroundSetStretch.gml
+ PushEd\Assets\Scripts\PEd_backgroundSetTiledHor.gml
+ PushEd\Assets\Scripts\PEd_backgroundSetTiledVer.gml
+ PushEd\Assets\Scripts\PEd_backgroundSetVisible.gml
+ PushEd\Assets\Scripts\PEd_backgroundSetX.gml
+ PushEd\Assets\Scripts\PEd_backgroundSetY.gml
+ PushEd\Assets\Scripts\PEd_cameraControl.gml
+ PushEd\Assets\Scripts\PEd_cameraGetDirVec3.gml
+ PushEd\Assets\Scripts\PEd_cameraGetPosVec3.gml
+ PushEd\Assets\Scripts\PEd_cameraGetRightVec3.gml
+ PushEd\Assets\Scripts\PEd_cameraGetTanFovY.gml
+ PushEd\Assets\Scripts\PEd_cameraGetUpVec3.gml
+ PushEd\Assets\Scripts\PEd_cameraScreenToPlaneXY.gml
+ PushEd\Assets\Scripts\PEd_cameraSetProjection.gml
+ PushEd\Assets\Scripts\PEd_cameraUnprojectVec2.gml
+ PushEd\Assets\Scripts\PEd_checkSurface.gml
+ PushEd\Assets\Scripts\PEd_codeProcess.gml
+ PushEd\Assets\Scripts\PEd_colourAlphaToArgb.gml
+ PushEd\Assets\Scripts\PEd_createBackground.gml
+ PushEd\Assets\Scripts\PEd_createCustomDataContainer.gml
+ PushEd\Assets\Scripts\PEd_createPhysicsWorld.gml
+ PushEd\Assets\Scripts\PEd_createRoom.gml
+ PushEd\Assets\Scripts\PEd_createSaveInstance.gml
+ PushEd\Assets\Scripts\PEd_createSaveTile.gml
+ PushEd\Assets\Scripts\PEd_createTile.gml
+ PushEd\Assets\Scripts\PEd_createViewport.gml
+ PushEd\Assets\Scripts\PEd_deselectObject.gml
+ PushEd\Assets\Scripts\PEd_dragAndDrop.gml
+ PushEd\Assets\Scripts\PEd_dsListAddList.gml
+ PushEd\Assets\Scripts\PEd_dsListAddMap.gml
+ PushEd\Assets\Scripts\PEd_dsListAddUnique.gml
+ PushEd\Assets\Scripts\PEd_dsListInsertList.gml
+ PushEd\Assets\Scripts\PEd_dsListInsertMap.gml
+ PushEd\Assets\Scripts\PEd_dsListInsertUnique.gml
+ PushEd\Assets\Scripts\PEd_end3D.gml
+ PushEd\Assets\Scripts\PEd_getCurrentRoom.gml
+ PushEd\Assets\Scripts\PEd_getCustomData.gml
+ PushEd\Assets\Scripts\PEd_getFloat.gml
+ PushEd\Assets\Scripts\PEd_getMouseWorldPosition.gml
+ PushEd\Assets\Scripts\PEd_getRoomCustomData.gml
+ PushEd\Assets\Scripts\PEd_getSelectedObject.gml
+ PushEd\Assets\Scripts\PEd_getTileCustomData.gml
+ PushEd\Assets\Scripts\PEd_getVarFromStr.gml
+ PushEd\Assets\Scripts\PEd_guiAddItem.gml
+ PushEd\Assets\Scripts\PEd_guiAddKeyboardShortcut.gml
+ PushEd\Assets\Scripts\PEd_guiAlphaMixGetHeight.gml
+ PushEd\Assets\Scripts\PEd_guiBeginFill.gml
+ PushEd\Assets\Scripts\PEd_guiCanShowContextMenu.gml
+ PushEd\Assets\Scripts\PEd_guiCanvasCleanUp.gml
+ PushEd\Assets\Scripts\PEd_guiCanvasDraw.gml
+ PushEd\Assets\Scripts\PEd_guiCanvasGetBackground.gml
+ PushEd\Assets\Scripts\PEd_guiCanvasGetSurface.gml
+ PushEd\Assets\Scripts\PEd_guiCanvasSetBackground.gml
+ PushEd\Assets\Scripts\PEd_guiCanvasSetSurface.gml
+ PushEd\Assets\Scripts\PEd_guiCleanUp.gml
+ PushEd\Assets\Scripts\PEd_guiColourMixGetHeight.gml
+ PushEd\Assets\Scripts\PEd_guiCompoundShapeCleanUp.gml
+ PushEd\Assets\Scripts\PEd_guiCompoundShapeGetItems.gml
+ PushEd\Assets\Scripts\PEd_guiCompoundShapeUpdate.gml
+ PushEd\Assets\Scripts\PEd_guiContainerDraw.gml
+ PushEd\Assets\Scripts\PEd_guiContainerGetContent.gml
+ PushEd\Assets\Scripts\PEd_guiContainerGetContentHeight.gml
+ PushEd\Assets\Scripts\PEd_guiContainerGetContentWidth.gml
+ PushEd\Assets\Scripts\PEd_guiContainerSetContent.gml
+ PushEd\Assets\Scripts\PEd_guiContainerSetContentHeight.gml
+ PushEd\Assets\Scripts\PEd_guiContainerSetContentWidth.gml
+ PushEd\Assets\Scripts\PEd_guiContainerUpdate.gml
+ PushEd\Assets\Scripts\PEd_guiContentColourPicker.gml
+ PushEd\Assets\Scripts\PEd_guiContentContentBrowser.gml
+ PushEd\Assets\Scripts\PEd_guiContentDetails.gml
+ PushEd\Assets\Scripts\PEd_guiContentInfo.gml
+ PushEd\Assets\Scripts\PEd_guiContentSceneOutline.gml
+ PushEd\Assets\Scripts\PEd_guiContentStatusBar.gml
+ PushEd\Assets\Scripts\PEd_guiContentTitleBar.gml
+ PushEd\Assets\Scripts\PEd_guiContentTitleBarContentBrowser.gml
+ PushEd\Assets\Scripts\PEd_guiContentTitleBarSceneOutline.gml
+ PushEd\Assets\Scripts\PEd_guiContentTitleBarWindow.gml
+ PushEd\Assets\Scripts\PEd_guiContentToolbar.gml
+ PushEd\Assets\Scripts\PEd_guiContentTools.gml
+ PushEd\Assets\Scripts\PEd_guiContextMenuDraw.gml
+ PushEd\Assets\Scripts\PEd_guiContextMenuItemDraw.gml
+ PushEd\Assets\Scripts\PEd_guiContextMenuItemUpdate.gml
+ PushEd\Assets\Scripts\PEd_guiContextMenuUpdate.gml
+ PushEd\Assets\Scripts\PEd_guiCreateCanvas.gml
+ PushEd\Assets\Scripts\PEd_guiCreateCompoundShape.gml
+ PushEd\Assets\Scripts\PEd_guiCreateContainer.gml
+ PushEd\Assets\Scripts\PEd_guiCreateContextMenu.gml
+ PushEd\Assets\Scripts\PEd_guiCreateContextMenuItem.gml
+ PushEd\Assets\Scripts\PEd_guiCreateDock.gml
+ PushEd\Assets\Scripts\PEd_guiCreateKeyboardShortcut.gml
+ PushEd\Assets\Scripts\PEd_guiCreateMenuBar.gml
+ PushEd\Assets\Scripts\PEd_guiCreateMenuBarItem.gml
+ PushEd\Assets\Scripts\PEd_guiCreatePanel.gml
+ PushEd\Assets\Scripts\PEd_guiCreateScrollbar.gml
+ PushEd\Assets\Scripts\PEd_guiCreateScrollbarHor.gml
+ PushEd\Assets\Scripts\PEd_guiCreateScrollbarVer.gml
+ PushEd\Assets\Scripts\PEd_guiCreateShape.gml
+ PushEd\Assets\Scripts\PEd_guiCreateToolbarButton.gml
+ PushEd\Assets\Scripts\PEd_guiCreateViewport.gml
+ PushEd\Assets\Scripts\PEd_guiCreateWindow.gml
+ PushEd\Assets\Scripts\PEd_guiDecodeID.gml
+ PushEd\Assets\Scripts\PEd_guiDestroy.gml
+ PushEd\Assets\Scripts\PEd_guiDestroyShape.gml
+ PushEd\Assets\Scripts\PEd_guiDockDraw.gml
+ PushEd\Assets\Scripts\PEd_guiDockUpdate.gml
+ PushEd\Assets\Scripts\PEd_guiDraw.gml
+ PushEd\Assets\Scripts\PEd_guiDrawAlphaMix.gml
+ PushEd\Assets\Scripts\PEd_guiDrawBackgroundInput.gml
+ PushEd\Assets\Scripts\PEd_guiDrawCheckbox.gml
+ PushEd\Assets\Scripts\PEd_guiDrawColourInput.gml
+ PushEd\Assets\Scripts\PEd_guiDrawColourMix.gml
+ PushEd\Assets\Scripts\PEd_guiDrawInput.gml
+ PushEd\Assets\Scripts\PEd_guiDrawItem.gml
+ PushEd\Assets\Scripts\PEd_guiDrawListItem.gml
+ PushEd\Assets\Scripts\PEd_guiDrawListItemEye.gml
+ PushEd\Assets\Scripts\PEd_guiDrawObjectInput.gml
+ PushEd\Assets\Scripts\PEd_guiDrawPopupMessage.gml
+ PushEd\Assets\Scripts\PEd_guiDrawRectangle.gml
+ PushEd\Assets\Scripts\PEd_guiDrawSection.gml
+ PushEd\Assets\Scripts\PEd_guiDrawShadow.gml
+ PushEd\Assets\Scripts\PEd_guiDrawSpriteClickable.gml
+ PushEd\Assets\Scripts\PEd_guiDrawTextBold.gml
+ PushEd\Assets\Scripts\PEd_guiDrawTextPart.gml
+ PushEd\Assets\Scripts\PEd_guiDrawTextShadow.gml
+ PushEd\Assets\Scripts\PEd_guiDrawTooltip.gml
+ PushEd\Assets\Scripts\PEd_guiEncodeID.gml
+ PushEd\Assets\Scripts\PEd_guiEndFill.gml
+ PushEd\Assets\Scripts\PEd_guiFindShape.gml
+ PushEd\Assets\Scripts\PEd_guiGetActiveShape.gml
+ PushEd\Assets\Scripts\PEd_guiGetHoveredShape.gml
+ PushEd\Assets\Scripts\PEd_guiGetSelectedShape.gml
+ PushEd\Assets\Scripts\PEd_guiInit.gml
+ PushEd\Assets\Scripts\PEd_guiInputCopy.gml
+ PushEd\Assets\Scripts\PEd_guiInputCut.gml
+ PushEd\Assets\Scripts\PEd_guiInputDelete.gml
+ PushEd\Assets\Scripts\PEd_guiInputDeleteSelectedPart.gml
+ PushEd\Assets\Scripts\PEd_guiInputPaste.gml
+ PushEd\Assets\Scripts\PEd_guiInputSelectAll.gml
+ PushEd\Assets\Scripts\PEd_guiKeyboardShorcutAddKey.gml
+ PushEd\Assets\Scripts\PEd_guiKeyboardShortcutToString.gml
+ PushEd\Assets\Scripts\PEd_guiKeyboardShortcutUpdate.gml
+ PushEd\Assets\Scripts\PEd_guiLogKey.gml
+ PushEd\Assets\Scripts\PEd_guiMatrixPush.gml
+ PushEd\Assets\Scripts\PEd_guiMatrixRestore.gml
+ PushEd\Assets\Scripts\PEd_guiMatrixSet.gml
+ PushEd\Assets\Scripts\PEd_guiMenuBarAddItem.gml
+ PushEd\Assets\Scripts\PEd_guiMenuBarDraw.gml
+ PushEd\Assets\Scripts\PEd_guiMenuBarItemDraw.gml
+ PushEd\Assets\Scripts\PEd_guiMenuBarItemUpdate.gml
+ PushEd\Assets\Scripts\PEd_guiMenuBarUpdate.gml
+ PushEd\Assets\Scripts\PEd_guiMenuFile.gml
+ PushEd\Assets\Scripts\PEd_guiMenuHelp.gml
+ PushEd\Assets\Scripts\PEd_guiMenuInput.gml
+ PushEd\Assets\Scripts\PEd_guiMenuObjectEdit.gml
+ PushEd\Assets\Scripts\PEd_guiMenuPlay.gml
+ PushEd\Assets\Scripts\PEd_guiMoveItemToTop.gml
+ PushEd\Assets\Scripts\PEd_guiPanelDraw.gml
+ PushEd\Assets\Scripts\PEd_guiPanelGetContainer.gml
+ PushEd\Assets\Scripts\PEd_guiPanelGetTitleBar.gml
+ PushEd\Assets\Scripts\PEd_guiPanelSetContent.gml
+ PushEd\Assets\Scripts\PEd_guiPanelSetTitleBar.gml
+ PushEd\Assets\Scripts\PEd_guiPushMouseCoordinates.gml
+ PushEd\Assets\Scripts\PEd_guiRedrawWindow.gml
+ PushEd\Assets\Scripts\PEd_guiRequestRedraw.gml
+ PushEd\Assets\Scripts\PEd_guiRequestRedrawAll.gml
+ PushEd\Assets\Scripts\PEd_guiScrollbarCalcJumpAndThumbSize.gml
+ PushEd\Assets\Scripts\PEd_guiScrollbarGetScroll.gml
+ PushEd\Assets\Scripts\PEd_guiScrollbarHorDraw.gml
+ PushEd\Assets\Scripts\PEd_guiScrollbarHorUpdate.gml
+ PushEd\Assets\Scripts\PEd_guiScrollbarIsVisible.gml
+ PushEd\Assets\Scripts\PEd_guiScrollbarSetScroll.gml
+ PushEd\Assets\Scripts\PEd_guiScrollbarUpdate.gml
+ PushEd\Assets\Scripts\PEd_guiScrollbarVerDraw.gml
+ PushEd\Assets\Scripts\PEd_guiScrollbarVerUpdate.gml
+ PushEd\Assets\Scripts\PEd_guiShapeCleanUp.gml
+ PushEd\Assets\Scripts\PEd_guiShapeDelegatesRecursive.gml
+ PushEd\Assets\Scripts\PEd_guiShapeExists.gml
+ PushEd\Assets\Scripts\PEd_guiShapeGetDelegate.gml
+ PushEd\Assets\Scripts\PEd_guiShapeGetDepth.gml
+ PushEd\Assets\Scripts\PEd_guiShapeGetHeight.gml
+ PushEd\Assets\Scripts\PEd_guiShapeGetRedraw.gml
+ PushEd\Assets\Scripts\PEd_guiShapeGetTooltip.gml
+ PushEd\Assets\Scripts\PEd_guiShapeGetType.gml
+ PushEd\Assets\Scripts\PEd_guiShapeGetWidth.gml
+ PushEd\Assets\Scripts\PEd_guiShapeGetX.gml
+ PushEd\Assets\Scripts\PEd_guiShapeGetY.gml
+ PushEd\Assets\Scripts\PEd_guiShapeIsActive.gml
+ PushEd\Assets\Scripts\PEd_guiShapeIsHovered.gml
+ PushEd\Assets\Scripts\PEd_guiShapeIsSelected.gml
+ PushEd\Assets\Scripts\PEd_guiShapeSetDelegate.gml
+ PushEd\Assets\Scripts\PEd_guiShapeSetDepth.gml
+ PushEd\Assets\Scripts\PEd_guiShapeSetHeight.gml
+ PushEd\Assets\Scripts\PEd_guiShapeSetPosition.gml
+ PushEd\Assets\Scripts\PEd_guiShapeSetRectangle.gml
+ PushEd\Assets\Scripts\PEd_guiShapeSetRedraw.gml
+ PushEd\Assets\Scripts\PEd_guiShapeSetSize.gml
+ PushEd\Assets\Scripts\PEd_guiShapeSetTooltip.gml
+ PushEd\Assets\Scripts\PEd_guiShapeSetType.gml
+ PushEd\Assets\Scripts\PEd_guiShapeSetWidth.gml
+ PushEd\Assets\Scripts\PEd_guiShapeSetX.gml
+ PushEd\Assets\Scripts\PEd_guiShapeSetY.gml
+ PushEd\Assets\Scripts\PEd_guiShapeUpdate.gml
+ PushEd\Assets\Scripts\PEd_guiShowContextMenu.gml
+ PushEd\Assets\Scripts\PEd_guiShowPopupMessage.gml
+ PushEd\Assets\Scripts\PEd_guiToolbarButtonDraw.gml
+ PushEd\Assets\Scripts\PEd_guiToolbarButtonUpdate.gml
+ PushEd\Assets\Scripts\PEd_guiUpdate.gml
+ PushEd\Assets\Scripts\PEd_guiViewportDraw.gml
+ PushEd\Assets\Scripts\PEd_guiViewportGetTitleBar.gml
+ PushEd\Assets\Scripts\PEd_guiViewportUpdate.gml
+ PushEd\Assets\Scripts\PEd_guiWindowDraw.gml
+ PushEd\Assets\Scripts\PEd_guiWindowGetContainer.gml
+ PushEd\Assets\Scripts\PEd_guiWindowGetContent.gml
+ PushEd\Assets\Scripts\PEd_guiWindowGetTitleBar.gml
+ PushEd\Assets\Scripts\PEd_guiWindowSetContent.gml
+ PushEd\Assets\Scripts\PEd_guiWindowUpdate.gml
+ PushEd\Assets\Scripts\PEd_instanceAddRotX.gml
+ PushEd\Assets\Scripts\PEd_instanceAddRotY.gml
+ PushEd\Assets\Scripts\PEd_instanceAddRotZ.gml
+ PushEd\Assets\Scripts\PEd_instanceAddScaleX.gml
+ PushEd\Assets\Scripts\PEd_instanceAddScaleY.gml
+ PushEd\Assets\Scripts\PEd_instanceAddScaleZ.gml
+ PushEd\Assets\Scripts\PEd_instanceAutocompleteCode.gml
+ PushEd\Assets\Scripts\PEd_instanceCopy.gml
+ PushEd\Assets\Scripts\PEd_instanceCreate.gml
+ PushEd\Assets\Scripts\PEd_instanceDestroy.gml
+ PushEd\Assets\Scripts\PEd_instanceEdit2D.gml
+ PushEd\Assets\Scripts\PEd_instanceEdit3D.gml
+ PushEd\Assets\Scripts\PEd_instanceGenerateName.gml
+ PushEd\Assets\Scripts\PEd_instanceGetAlpha.gml
+ PushEd\Assets\Scripts\PEd_instanceGetCode.gml
+ PushEd\Assets\Scripts\PEd_instanceGetColour.gml
+ PushEd\Assets\Scripts\PEd_instanceGetColourARGB.gml
+ PushEd\Assets\Scripts\PEd_instanceGetMatrix.gml
+ PushEd\Assets\Scripts\PEd_instanceGetName.gml
+ PushEd\Assets\Scripts\PEd_instanceGetObjectName.gml
+ PushEd\Assets\Scripts\PEd_instanceGetPosVec2.gml
+ PushEd\Assets\Scripts\PEd_instanceGetPosVec3.gml
+ PushEd\Assets\Scripts\PEd_instanceGetPosX.gml
+ PushEd\Assets\Scripts\PEd_instanceGetPosY.gml
+ PushEd\Assets\Scripts\PEd_instanceGetPosZ.gml
+ PushEd\Assets\Scripts\PEd_instanceGetRotX.gml
+ PushEd\Assets\Scripts\PEd_instanceGetRotY.gml
+ PushEd\Assets\Scripts\PEd_instanceGetRotZ.gml
+ PushEd\Assets\Scripts\PEd_instanceGetScaleVec2.gml
+ PushEd\Assets\Scripts\PEd_instanceGetScaleVec3.gml
+ PushEd\Assets\Scripts\PEd_instanceGetScaleX.gml
+ PushEd\Assets\Scripts\PEd_instanceGetScaleY.gml
+ PushEd\Assets\Scripts\PEd_instanceGetScaleZ.gml
+ PushEd\Assets\Scripts\PEd_instanceSelecting.gml
+ PushEd\Assets\Scripts\PEd_instanceSetAlpha.gml
+ PushEd\Assets\Scripts\PEd_instanceSetCode.gml
+ PushEd\Assets\Scripts\PEd_instanceSetColour.gml
+ PushEd\Assets\Scripts\PEd_instanceSetColourARGB.gml
+ PushEd\Assets\Scripts\PEd_instanceSetName.gml
+ PushEd\Assets\Scripts\PEd_instanceSetObjectName.gml
+ PushEd\Assets\Scripts\PEd_instanceSetPosVec2.gml
+ PushEd\Assets\Scripts\PEd_instanceSetPosVec3.gml
+ PushEd\Assets\Scripts\PEd_instanceSetPosX.gml
+ PushEd\Assets\Scripts\PEd_instanceSetPosY.gml
+ PushEd\Assets\Scripts\PEd_instanceSetPosZ.gml
+ PushEd\Assets\Scripts\PEd_instanceSetRotX.gml
+ PushEd\Assets\Scripts\PEd_instanceSetRotY.gml
+ PushEd\Assets\Scripts\PEd_instanceSetRotZ.gml
+ PushEd\Assets\Scripts\PEd_instanceSetScaleVec2.gml
+ PushEd\Assets\Scripts\PEd_instanceSetScaleVec3.gml
+ PushEd\Assets\Scripts\PEd_instanceSetScaleX.gml
+ PushEd\Assets\Scripts\PEd_instanceSetScaleY.gml
+ PushEd\Assets\Scripts\PEd_instanceSetScaleZ.gml
+ PushEd\Assets\Scripts\PEd_keyToString.gml
+ PushEd\Assets\Scripts\PEd_loadRoomFromBBMAP.gml
+ PushEd\Assets\Scripts\PEd_loadRoomFromBBMAP2.gml
+ PushEd\Assets\Scripts\PEd_loadRoomFromBBMAPAuto.gml
+ PushEd\Assets\Scripts\PEd_loadRoomFromGMX.gml
+ PushEd\Assets\Scripts\PEd_loadRoomFromPEd.gml
+ PushEd\Assets\Scripts\PEd_matrix.gml
+ PushEd\Assets\Scripts\PEd_matrixDeterminant.gml
+ PushEd\Assets\Scripts\PEd_matrixIdentity.gml
+ PushEd\Assets\Scripts\PEd_matrixInverse.gml
+ PushEd\Assets\Scripts\PEd_matrixLookAt.gml
+ PushEd\Assets\Scripts\PEd_matrixPerspectiveFov.gml
+ PushEd\Assets\Scripts\PEd_matrixPrint.gml
+ PushEd\Assets\Scripts\PEd_matrixRotToEuler.gml
+ PushEd\Assets\Scripts\PEd_matrixScale.gml
+ PushEd\Assets\Scripts\PEd_matrixTranspose.gml
+ PushEd\Assets\Scripts\PEd_meshCreate.gml
+ PushEd\Assets\Scripts\PEd_meshDestroy.gml
+ PushEd\Assets\Scripts\PEd_meshInit.gml
+ PushEd\Assets\Scripts\PEd_meshLoadFromObj.gml
+ PushEd\Assets\Scripts\PEd_meshToModelD3D.gml
+ PushEd\Assets\Scripts\PEd_meshToVBuffer.gml
+ PushEd\Assets\Scripts\PEd_mouseRay3D.gml
+ PushEd\Assets\Scripts\PEd_objectGetIndex.gml
+ PushEd\Assets\Scripts\PEd_objectListGetItem.gml
+ PushEd\Assets\Scripts\PEd_objectListGetSize.gml
+ PushEd\Assets\Scripts\PEd_physicsWorldGetEnabled.gml
+ PushEd\Assets\Scripts\PEd_physicsWorldGetGravityX.gml
+ PushEd\Assets\Scripts\PEd_physicsWorldGetGravityY.gml
+ PushEd\Assets\Scripts\PEd_physicsWorldGetPxToM.gml
+ PushEd\Assets\Scripts\PEd_physicsWorldSetEnabled.gml
+ PushEd\Assets\Scripts\PEd_physicsWorldSetGravityX.gml
+ PushEd\Assets\Scripts\PEd_physicsWorldSetGravityY.gml
+ PushEd\Assets\Scripts\PEd_physicsWorldSetPxToM.gml
+ PushEd\Assets\Scripts\PEd_pointDistPlane.gml
+ PushEd\Assets\Scripts\PEd_rayPlaneIntersect.gml
+ PushEd\Assets\Scripts\PEd_registerCustomDataContainer.gml
+ PushEd\Assets\Scripts\PEd_removeFromStr.gml
+ PushEd\Assets\Scripts\PEd_rgbToBgr.gml
+ PushEd\Assets\Scripts\PEd_roomApplySettings.gml
+ PushEd\Assets\Scripts\PEd_roomCopy.gml
+ PushEd\Assets\Scripts\PEd_roomDestroy.gml
+ PushEd\Assets\Scripts\PEd_roomGetBackground.gml
+ PushEd\Assets\Scripts\PEd_roomGetCaption.gml
+ PushEd\Assets\Scripts\PEd_roomGetCode.gml
+ PushEd\Assets\Scripts\PEd_roomGetColour.gml
+ PushEd\Assets\Scripts\PEd_roomGetColourARGB.gml
+ PushEd\Assets\Scripts\PEd_roomGetFromId.gml
+ PushEd\Assets\Scripts\PEd_roomGetGrid.gml
+ PushEd\Assets\Scripts\PEd_roomGetHeight.gml
+ PushEd\Assets\Scripts\PEd_roomGetId.gml
+ PushEd\Assets\Scripts\PEd_roomGetInstances.gml
+ PushEd\Assets\Scripts\PEd_roomGetName.gml
+ PushEd\Assets\Scripts\PEd_roomGetPersistent.gml
+ PushEd\Assets\Scripts\PEd_roomGetPhysicsWorld.gml
+ PushEd\Assets\Scripts\PEd_roomGetShowColour.gml
+ PushEd\Assets\Scripts\PEd_roomGetSnapD.gml
+ PushEd\Assets\Scripts\PEd_roomGetSnapH.gml
+ PushEd\Assets\Scripts\PEd_roomGetSnapV.gml
+ PushEd\Assets\Scripts\PEd_roomGetSpeed.gml
+ PushEd\Assets\Scripts\PEd_roomGetTiles.gml
+ PushEd\Assets\Scripts\PEd_roomGetViewport.gml
+ PushEd\Assets\Scripts\PEd_roomGetWidth.gml
+ PushEd\Assets\Scripts\PEd_roomSaveToBBMAP2.gml
+ PushEd\Assets\Scripts\PEd_roomSaveToGMX.gml
+ PushEd\Assets\Scripts\PEd_roomSaveToPEd.gml
+ PushEd\Assets\Scripts\PEd_roomSetCaption.gml
+ PushEd\Assets\Scripts\PEd_roomSetCode.gml
+ PushEd\Assets\Scripts\PEd_roomSetColour.gml
+ PushEd\Assets\Scripts\PEd_roomSetColourARGB.gml
+ PushEd\Assets\Scripts\PEd_roomSetGrid.gml
+ PushEd\Assets\Scripts\PEd_roomSetHeight.gml
+ PushEd\Assets\Scripts\PEd_roomSetName.gml
+ PushEd\Assets\Scripts\PEd_roomSetPersistent.gml
+ PushEd\Assets\Scripts\PEd_roomSetShowColour.gml
+ PushEd\Assets\Scripts\PEd_roomSetSnapD.gml
+ PushEd\Assets\Scripts\PEd_roomSetSnapH.gml
+ PushEd\Assets\Scripts\PEd_roomSetSnapV.gml
+ PushEd\Assets\Scripts\PEd_roomSetSpeed.gml
+ PushEd\Assets\Scripts\PEd_roomSetWidth.gml
+ PushEd\Assets\Scripts\PEd_savePivotOffsets.gml
+ PushEd\Assets\Scripts\PEd_selectObject.gml
+ PushEd\Assets\Scripts\PEd_start3D.gml
+ PushEd\Assets\Scripts\PEd_stringSplitOnFirst.gml
+ PushEd\Assets\Scripts\PEd_switchMode.gml
+ PushEd\Assets\Scripts\PEd_tileCopy.gml
+ PushEd\Assets\Scripts\PEd_tileDelete.gml
+ PushEd\Assets\Scripts\PEd_tileEdit.gml
+ PushEd\Assets\Scripts\PEd_tileGetAlpha.gml
+ PushEd\Assets\Scripts\PEd_tileGetBlend.gml
+ PushEd\Assets\Scripts\PEd_tileGetColourARGB.gml
+ PushEd\Assets\Scripts\PEd_tileGetDepth.gml
+ PushEd\Assets\Scripts\PEd_tileGetPosVec2.gml
+ PushEd\Assets\Scripts\PEd_tileGetPosX.gml
+ PushEd\Assets\Scripts\PEd_tileGetPosY.gml
+ PushEd\Assets\Scripts\PEd_tileGetScaleVec2.gml
+ PushEd\Assets\Scripts\PEd_tileGetScaleX.gml
+ PushEd\Assets\Scripts\PEd_tileGetScaleY.gml
+ PushEd\Assets\Scripts\PEd_tileSetAlpha.gml
+ PushEd\Assets\Scripts\PEd_tileSetBlend.gml
+ PushEd\Assets\Scripts\PEd_tileSetColourARGB.gml
+ PushEd\Assets\Scripts\PEd_tileSetDepth.gml
+ PushEd\Assets\Scripts\PEd_tileSetPosVec2.gml
+ PushEd\Assets\Scripts\PEd_tileSetPosX.gml
+ PushEd\Assets\Scripts\PEd_tileSetPosY.gml
+ PushEd\Assets\Scripts\PEd_tileSetScaleVec2.gml
+ PushEd\Assets\Scripts\PEd_tileSetScaleX.gml
+ PushEd\Assets\Scripts\PEd_tileSetScaleY.gml
+ PushEd\Assets\Scripts\PEd_vec2.gml
+ PushEd\Assets\Scripts\PEd_vec2Abs.gml
+ PushEd\Assets\Scripts\PEd_vec2Add.gml
+ PushEd\Assets\Scripts\PEd_vec2Clone.gml
+ PushEd\Assets\Scripts\PEd_vec2Dot.gml
+ PushEd\Assets\Scripts\PEd_vec2Equals.gml
+ PushEd\Assets\Scripts\PEd_vec2Length.gml
+ PushEd\Assets\Scripts\PEd_vec2LengthSq.gml
+ PushEd\Assets\Scripts\PEd_vec2Lerp.gml
+ PushEd\Assets\Scripts\PEd_vec2Normalize.gml
+ PushEd\Assets\Scripts\PEd_vec2Reflect.gml
+ PushEd\Assets\Scripts\PEd_vec2Scale.gml
+ PushEd\Assets\Scripts\PEd_vec2Subtract.gml
+ PushEd\Assets\Scripts\PEd_vec3.gml
+ PushEd\Assets\Scripts\PEd_vec3Abs.gml
+ PushEd\Assets\Scripts\PEd_vec3Add.gml
+ PushEd\Assets\Scripts\PEd_vec3Clone.gml
+ PushEd\Assets\Scripts\PEd_vec3Cross.gml
+ PushEd\Assets\Scripts\PEd_vec3Dot.gml
+ PushEd\Assets\Scripts\PEd_vec3Equals.gml
+ PushEd\Assets\Scripts\PEd_vec3Length.gml
+ PushEd\Assets\Scripts\PEd_vec3LengthSq.gml
+ PushEd\Assets\Scripts\PEd_vec3Lerp.gml
+ PushEd\Assets\Scripts\PEd_vec3Normalize.gml
+ PushEd\Assets\Scripts\PEd_vec3Project.gml
+ PushEd\Assets\Scripts\PEd_vec3Reflect.gml
+ PushEd\Assets\Scripts\PEd_vec3Scale.gml
+ PushEd\Assets\Scripts\PEd_vec3Subtract.gml
+ PushEd\Assets\Scripts\PEd_vec4.gml
+ PushEd\Assets\Scripts\PEd_vec4Abs.gml
+ PushEd\Assets\Scripts\PEd_vec4Add.gml
+ PushEd\Assets\Scripts\PEd_vec4Clone.gml
+ PushEd\Assets\Scripts\PEd_vec4Dot.gml
+ PushEd\Assets\Scripts\PEd_vec4Equals.gml
+ PushEd\Assets\Scripts\PEd_vec4Length.gml
+ PushEd\Assets\Scripts\PEd_vec4LengthSq.gml
+ PushEd\Assets\Scripts\PEd_vec4Lerp.gml
+ PushEd\Assets\Scripts\PEd_vec4Normalize.gml
+ PushEd\Assets\Scripts\PEd_vec4Scale.gml
+ PushEd\Assets\Scripts\PEd_vec4Subtract.gml
+ PushEd\Assets\Scripts\PEd_vec4Transform.gml
+ PushEd\Assets\Scripts\PEd_viewportGetBorderHor.gml
+ PushEd\Assets\Scripts\PEd_viewportGetBorderVer.gml
+ PushEd\Assets\Scripts\PEd_viewportGetHeight.gml
+ PushEd\Assets\Scripts\PEd_viewportGetObject.gml
+ PushEd\Assets\Scripts\PEd_viewportGetPortHeight.gml
+ PushEd\Assets\Scripts\PEd_viewportGetPortWidth.gml
+ PushEd\Assets\Scripts\PEd_viewportGetPortX.gml
+ PushEd\Assets\Scripts\PEd_viewportGetPortY.gml
+ PushEd\Assets\Scripts\PEd_viewportGetSpeedHor.gml
+ PushEd\Assets\Scripts\PEd_viewportGetSpeedVer.gml
+ PushEd\Assets\Scripts\PEd_viewportGetVisible.gml
+ PushEd\Assets\Scripts\PEd_viewportGetWidth.gml
+ PushEd\Assets\Scripts\PEd_viewportGetX.gml
+ PushEd\Assets\Scripts\PEd_viewportGetY.gml
+ PushEd\Assets\Scripts\PEd_viewportSetBorderHor.gml
+ PushEd\Assets\Scripts\PEd_viewportSetBorderVer.gml
+ PushEd\Assets\Scripts\PEd_viewportSetHeight.gml
+ PushEd\Assets\Scripts\PEd_viewportSetObject.gml
+ PushEd\Assets\Scripts\PEd_viewportSetPortHeight.gml
+ PushEd\Assets\Scripts\PEd_viewportSetPortWidth.gml
+ PushEd\Assets\Scripts\PEd_viewportSetPortX.gml
+ PushEd\Assets\Scripts\PEd_viewportSetPortY.gml
+ PushEd\Assets\Scripts\PEd_viewportSetSpeedHor.gml
+ PushEd\Assets\Scripts\PEd_viewportSetSpeedVer.gml
+ PushEd\Assets\Scripts\PEd_viewportSetVisible.gml
+ PushEd\Assets\Scripts\PEd_viewportSetWidth.gml
+ PushEd\Assets\Scripts\PEd_viewportSetX.gml
+ PushEd\Assets\Scripts\PEd_viewportSetY.gml
+ PushEd\Assets\Scripts\PEd_xmlAddChildElement.gml
+ PushEd\Assets\Scripts\PEd_xmlCreateElement.gml
+ PushEd\Assets\Scripts\PEd_xmlDestroyElement.gml
+ PushEd\Assets\Scripts\PEd_xmlElementHasAttribute.gml
+ PushEd\Assets\Scripts\PEd_xmlElementHasValue.gml
+ PushEd\Assets\Scripts\PEd_xmlFindAllElements.gml
+ PushEd\Assets\Scripts\PEd_xmlFindElement.gml
+ PushEd\Assets\Scripts\PEd_xmlFindFirstElementAttribute.gml
+ PushEd\Assets\Scripts\PEd_xmlFindNextElementAttribute.gml
+ PushEd\Assets\Scripts\PEd_xmlGetChildElement.gml
+ PushEd\Assets\Scripts\PEd_xmlGetElementAttribute.gml
+ PushEd\Assets\Scripts\PEd_xmlGetElementName.gml
+ PushEd\Assets\Scripts\PEd_xmlGetElementParent.gml
+ PushEd\Assets\Scripts\PEd_xmlGetElementValue.gml
+ PushEd\Assets\Scripts\PEd_xmlGetNumberOfChildElements.gml
+ PushEd\Assets\Scripts\PEd_xmlGetNumberOfElementAttributes.gml
+ PushEd\Assets\Scripts\PEd_xmlInit.gml
+ PushEd\Assets\Scripts\PEd_xmlParse.gml
+ PushEd\Assets\Scripts\PEd_xmlRead.gml
+ PushEd\Assets\Scripts\PEd_xmlRemoveElementAttribute.gml
+ PushEd\Assets\Scripts\PEd_xmlSetElementAttribute.gml
+ PushEd\Assets\Scripts\PEd_xmlSetElementName.gml
+ PushEd\Assets\Scripts\PEd_xmlSetElementValue.gml
+ PushEd\Assets\Scripts\PEd_xmlString.gml
+ PushEd\Assets\Scripts\PEd_xmlWrite.gml
+ PushEd\Assets\Shaders\PEd_shInstHighlight.shader
+ PushEd\Assets\Shaders\PEd_shInstSelect.shader
+ PushEd\Assets\Shaders\PEd_shOutline.shader
+ PushEd\Assets\Sprites\PEd_guiSprCheckbox.sprite.gmx
+ PushEd\Assets\Sprites\PEd_guiSprIcons.sprite.gmx
+ PushEd\Assets\Sprites\PEd_guiSprInput.sprite.gmx
+ PushEd\Assets\Sprites\PEd_guiSprMisc.sprite.gmx
+ PushEd\Assets\Sprites\PEd_guiSprPanel.sprite.gmx
+ PushEd\Assets\Sprites\PEd_guiSprRectangle.sprite.gmx
+ PushEd\Assets\Sprites\PEd_guiSprRoll.sprite.gmx
+ PushEd\Assets\Sprites\PEd_guiSprScrollbarHor.sprite.gmx
+ PushEd\Assets\Sprites\PEd_guiSprScrollbarVer.sprite.gmx
+ PushEd\Assets\Sprites\PEd_guiSprShadow.sprite.gmx
+ PushEd\Assets\Sprites\PEd_guiSprWarning.sprite.gmx
+ PushEd\Assets\Sprites\PEd_guiSprWindowCross.sprite.gmx
+ PushEd\Assets\Sprites\PEd_sprDummy2D.sprite.gmx
+ PushEd\Assets\Sprites\PEd_sprTest.sprite.gmx
+ PushEd\Assets\Sprites\images\PEd_guiSprCheckbox_0.png
+ PushEd\Assets\Sprites\images\PEd_guiSprCheckbox_1.png
+ PushEd\Assets\Sprites\images\PEd_guiSprIcons_0.png
+ PushEd\Assets\Sprites\images\PEd_guiSprIcons_1.png
+ PushEd\Assets\Sprites\images\PEd_guiSprIcons_10.png
+ PushEd\Assets\Sprites\images\PEd_guiSprIcons_11.png
+ PushEd\Assets\Sprites\images\PEd_guiSprIcons_12.png
+ PushEd\Assets\Sprites\images\PEd_guiSprIcons_13.png
+ PushEd\Assets\Sprites\images\PEd_guiSprIcons_14.png
+ PushEd\Assets\Sprites\images\PEd_guiSprIcons_15.png
+ PushEd\Assets\Sprites\images\PEd_guiSprIcons_2.png
+ PushEd\Assets\Sprites\images\PEd_guiSprIcons_3.png
+ PushEd\Assets\Sprites\images\PEd_guiSprIcons_4.png
+ PushEd\Assets\Sprites\images\PEd_guiSprIcons_5.png
+ PushEd\Assets\Sprites\images\PEd_guiSprIcons_6.png
+ PushEd\Assets\Sprites\images\PEd_guiSprIcons_7.png
+ PushEd\Assets\Sprites\images\PEd_guiSprIcons_8.png
+ PushEd\Assets\Sprites\images\PEd_guiSprIcons_9.png
+ PushEd\Assets\Sprites\images\PEd_guiSprInput_0.png
+ PushEd\Assets\Sprites\images\PEd_guiSprInput_1.png
+ PushEd\Assets\Sprites\images\PEd_guiSprInput_2.png
+ PushEd\Assets\Sprites\images\PEd_guiSprMisc_0.png
+ PushEd\Assets\Sprites\images\PEd_guiSprMisc_1.png
+ PushEd\Assets\Sprites\images\PEd_guiSprMisc_2.png
+ PushEd\Assets\Sprites\images\PEd_guiSprMisc_3.png
+ PushEd\Assets\Sprites\images\PEd_guiSprMisc_4.png
+ PushEd\Assets\Sprites\images\PEd_guiSprPanel_0.png
+ PushEd\Assets\Sprites\images\PEd_guiSprRectangle_0.png
+ PushEd\Assets\Sprites\images\PEd_guiSprRoll_0.png
+ PushEd\Assets\Sprites\images\PEd_guiSprRoll_1.png
+ PushEd\Assets\Sprites\images\PEd_guiSprScrollbarHor_0.png
+ PushEd\Assets\Sprites\images\PEd_guiSprScrollbarHor_1.png
+ PushEd\Assets\Sprites\images\PEd_guiSprScrollbarHor_2.png
+ PushEd\Assets\Sprites\images\PEd_guiSprScrollbarVer_0.png
+ PushEd\Assets\Sprites\images\PEd_guiSprScrollbarVer_1.png
+ PushEd\Assets\Sprites\images\PEd_guiSprScrollbarVer_2.png
+ PushEd\Assets\Sprites\images\PEd_guiSprShadow_0.png
+ PushEd\Assets\Sprites\images\PEd_guiSprShadow_1.png
+ PushEd\Assets\Sprites\images\PEd_guiSprShadow_2.png
+ PushEd\Assets\Sprites\images\PEd_guiSprWarning_0.png
+ PushEd\Assets\Sprites\images\PEd_guiSprWindowCross_0.png
+ PushEd\Assets\Sprites\images\PEd_sprDummy2D_0.png
+ PushEd\Assets\Sprites\images\PEd_sprTest_0.png
+ NpGnXPHmhOdLSY81dc/k3VLavEoZ71Q2S7iYAfqTfaKmfYeY9Ae2jt6EORMK3rTdjTMNNNVVR1aheJYb+l8kHvwjEV62VTLv3W8BIOJUXuG7yTaNkf4dlrPk0+O6ecJ3qXxFc9hzbWmI39LstSbHXS6QthxhY9ru7qV4bg4JdZus0cOU5KGv8ABZlk3TR3sVNFlZBok+DJPO9JnUu1aRB0GBYae6hR2rnEBf7KOkXfD19vvRR2eQjAk1qzFGwapCWY1Wtx65csTl8ZrbYzKdZF/WEBBHp+HMMJ3nsEkpWHd57QRfWAQkcEwu+f71R5irQ3vUABVdR7ymrdFyMBRjig==
+ 2D2D2D2D2D424547494E205055424C4943204B45592D2D2D2D2D0A4D494942496A414E42676B71686B6947397730424151454641414F43415138414D49494243674B4341514541776D43313443613367722F51426D7667304171640A505A387971473544314235652B67326C6371446F327746714F744C684E596362454E6C4E535649486C527A332F367234583063776F6F764F646C33695A745A470A6C4B6C724B514A764E4950612F4741484F2F785045494579794C36735955636133694172394D416F4A7258786456554D594644536859716F526472446B4969750A586D36307871714458566C5052616F67356A7350484C69417157665A38395159476F336F475A3447546D2B4F2B4C676568554C7A6B6E6257656B6437363963500A4C74674D326F69445234507735565A646951375A3630567A677465506441326A615A6F672B42303935377A5438556C7852665551526B4D56784F553050456B760A767A75364C6E594F304B7854344F692B68702F46665737645951304C777131615750317A6B706841556649433274424E68385446563374436D4F3543454B34350A57514944415141420A2D2D2D2D2D454E44205055424C4943204B45592D2D2D2D2D0A
+
diff --git a/PushEd.gmx/fonts/PEd_fntBold.font.gmx b/PushEd.gmx/fonts/PEd_fntBold.font.gmx
new file mode 100644
index 00000000..52e50b59
--- /dev/null
+++ b/PushEd.gmx/fonts/PEd_fntBold.font.gmx
@@ -0,0 +1,246 @@
+
+
+ Consolas
+ 10
+ -1
+ -1
+ 0
+ 1
+ 3
+ 0
+
+
+ 0
+
+
+ 32,255
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ PEd_fntBold.png
+
diff --git a/PushEd.gmx/fonts/PEd_fntBold.png b/PushEd.gmx/fonts/PEd_fntBold.png
new file mode 100644
index 00000000..5c9900d3
Binary files /dev/null and b/PushEd.gmx/fonts/PEd_fntBold.png differ
diff --git a/PushEd.gmx/fonts/PEd_fntNormal.font.gmx b/PushEd.gmx/fonts/PEd_fntNormal.font.gmx
new file mode 100644
index 00000000..63d27efa
--- /dev/null
+++ b/PushEd.gmx/fonts/PEd_fntNormal.font.gmx
@@ -0,0 +1,246 @@
+
+
+ Consolas
+ 10
+ 0
+ -1
+ 0
+ 1
+ 3
+ 0
+
+
+ 0
+
+
+ 32,255
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ PEd_fntNormal.png
+
diff --git a/PushEd.gmx/fonts/PEd_fntNormal.png b/PushEd.gmx/fonts/PEd_fntNormal.png
new file mode 100644
index 00000000..5c948d11
Binary files /dev/null and b/PushEd.gmx/fonts/PEd_fntNormal.png differ
diff --git a/PushEd.gmx/help.rtf b/PushEd.gmx/help.rtf
new file mode 100644
index 00000000..059d9297
Binary files /dev/null and b/PushEd.gmx/help.rtf differ
diff --git a/PushEd.gmx/objects/PEd_oEditor.object.gmx b/PushEd.gmx/objects/PEd_oEditor.object.gmx
new file mode 100644
index 00000000..03b52151
--- /dev/null
+++ b/PushEd.gmx/objects/PEd_oEditor.object.gmx
@@ -0,0 +1,1436 @@
+
+
diff --git a/PushEd.gmx/objects/PEd_oObject.object.gmx b/PushEd.gmx/objects/PEd_oObject.object.gmx
new file mode 100644
index 00000000..f2c1c204
--- /dev/null
+++ b/PushEd.gmx/objects/PEd_oObject.object.gmx
@@ -0,0 +1,91 @@
+
+
diff --git a/PushEd.gmx/objects/PEd_oObject2D.object.gmx b/PushEd.gmx/objects/PEd_oObject2D.object.gmx
new file mode 100644
index 00000000..cbad4aed
--- /dev/null
+++ b/PushEd.gmx/objects/PEd_oObject2D.object.gmx
@@ -0,0 +1,114 @@
+
+
diff --git a/PushEd.gmx/objects/PEd_oObject3D.object.gmx b/PushEd.gmx/objects/PEd_oObject3D.object.gmx
new file mode 100644
index 00000000..77af1b24
--- /dev/null
+++ b/PushEd.gmx/objects/PEd_oObject3D.object.gmx
@@ -0,0 +1,118 @@
+
+
diff --git a/PushEd.gmx/objects/PEd_oPivot.object.gmx b/PushEd.gmx/objects/PEd_oPivot.object.gmx
new file mode 100644
index 00000000..f0d23fa3
--- /dev/null
+++ b/PushEd.gmx/objects/PEd_oPivot.object.gmx
@@ -0,0 +1,347 @@
+
+
diff --git a/PushEd.gmx/objects/PEd_oPlay.object.gmx b/PushEd.gmx/objects/PEd_oPlay.object.gmx
new file mode 100644
index 00000000..1835a7c8
--- /dev/null
+++ b/PushEd.gmx/objects/PEd_oPlay.object.gmx
@@ -0,0 +1,143 @@
+
+
diff --git a/PushEd.gmx/objects/oBlank.object.gmx b/PushEd.gmx/objects/oBlank.object.gmx
new file mode 100644
index 00000000..5ceec829
--- /dev/null
+++ b/PushEd.gmx/objects/oBlank.object.gmx
@@ -0,0 +1,23 @@
+
+
diff --git a/PushEd.gmx/objects/oBlock.object.gmx b/PushEd.gmx/objects/oBlock.object.gmx
new file mode 100644
index 00000000..fb2ec928
--- /dev/null
+++ b/PushEd.gmx/objects/oBlock.object.gmx
@@ -0,0 +1,90 @@
+
+
diff --git a/PushEd.gmx/objects/oCone.object.gmx b/PushEd.gmx/objects/oCone.object.gmx
new file mode 100644
index 00000000..b46d13ac
--- /dev/null
+++ b/PushEd.gmx/objects/oCone.object.gmx
@@ -0,0 +1,90 @@
+
+
diff --git a/PushEd.gmx/objects/oCylinder.object.gmx b/PushEd.gmx/objects/oCylinder.object.gmx
new file mode 100644
index 00000000..c485c6f5
--- /dev/null
+++ b/PushEd.gmx/objects/oCylinder.object.gmx
@@ -0,0 +1,90 @@
+
+
diff --git a/PushEd.gmx/objects/oEllipsoid.object.gmx b/PushEd.gmx/objects/oEllipsoid.object.gmx
new file mode 100644
index 00000000..7fd2975e
--- /dev/null
+++ b/PushEd.gmx/objects/oEllipsoid.object.gmx
@@ -0,0 +1,90 @@
+
+
diff --git a/PushEd.gmx/objects/oSprite.object.gmx b/PushEd.gmx/objects/oSprite.object.gmx
new file mode 100644
index 00000000..c9a957fa
--- /dev/null
+++ b/PushEd.gmx/objects/oSprite.object.gmx
@@ -0,0 +1,52 @@
+
+
diff --git a/PushEd.gmx/rooms/PEd_rmEditor.room.gmx b/PushEd.gmx/rooms/PEd_rmEditor.room.gmx
new file mode 100644
index 00000000..4e3ecc45
--- /dev/null
+++ b/PushEd.gmx/rooms/PEd_rmEditor.room.gmx
@@ -0,0 +1,65 @@
+
+
+
+ 512
+ 512
+ 32
+ 32
+ 0
+ 60
+ -1
+ 0
+ -1
+
+ -1
+ -1
+ -1
+
+ -1
+ 1024
+ 600
+ -1
+ -1
+ -1
+ -1
+ -1
+ 0
+ 0
+ -1
+ 0
+ 0
+ 0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0
+ 0
+ 0
+ 640
+ 480
+ 0
+ 10
+ 0.100000001490116
+
diff --git a/PushEd.gmx/rooms/PEd_rmPlay.room.gmx b/PushEd.gmx/rooms/PEd_rmPlay.room.gmx
new file mode 100644
index 00000000..3ebe8e09
--- /dev/null
+++ b/PushEd.gmx/rooms/PEd_rmPlay.room.gmx
@@ -0,0 +1,65 @@
+
+
+
+ 512
+ 512
+ 32
+ 32
+ 0
+ 60
+ 0
+ 12632256
+ -1
+
+ -1
+ -1
+ -1
+
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0
+ 0
+ 0
+ 1024
+ 768
+ 0
+ 10
+ 0.100000001490116
+
diff --git a/PushEd.gmx/scripts/PEd_actClearRoom.gml b/PushEd.gmx/scripts/PEd_actClearRoom.gml
new file mode 100644
index 00000000..ea0cbbe1
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_actClearRoom.gml
@@ -0,0 +1,49 @@
+/// PEd_actClearRoom()
+/**
+ * @brief Creates a new room.
+ */
+view_xview[0] = 0;
+view_yview[0] = 0;
+x = -16;
+y = 4;
+z = 4;
+direction = 0;
+camPitch = 0;
+viewZoom = 1;
+viewLastX = 0;
+viewLastY = 0;
+editMode = PEdEditModes.Object;
+editTool = PEdTools.Move;
+editNow = PEdTools.None;
+editFloor = true;
+PEd_oPivot.x = 0;
+PEd_oPivot.y = 0;
+PEd_oPivot.z = 0;
+
+// Reset tile editor
+ds_list_clear(tileLayers);
+ds_list_clear(tileVisible);
+tileLayerSelected = 0;
+ds_list_add(tileLayers, tileDepth);
+ds_list_add(tileVisible, true);
+
+// Deselect instances
+ds_list_clear(selectedObjects);
+ds_list_clear(selectedObjectsData);
+
+// Destroy all rooms
+for (var i = ds_list_size(pedRoomList) - 1; i >= 0; i--)
+{
+ PEd_roomDestroy(pedRoomList[| i]);
+ ds_list_delete(pedRoomList, i);
+}
+
+// Add new empty room
+pedRoomCurrent = 0;
+ds_list_add(pedRoomList, PEd_createRoom("room", 512, 512));
+
+// Redraw all
+PEd_guiRequestRedrawAll(guiRoot)
+
+// Message
+PEd_guiShowPopupMessage("Created a new empty room.");
diff --git a/PushEd.gmx/scripts/PEd_actClearSelection.gml b/PushEd.gmx/scripts/PEd_actClearSelection.gml
new file mode 100644
index 00000000..e6ebee83
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_actClearSelection.gml
@@ -0,0 +1,7 @@
+/// PEd_actClearSelection()
+/**
+ * @brief Selects no object.
+ */
+ds_list_clear(selectedObjects);
+ds_list_clear(selectedObjectsData);
+PEd_guiRequestRedrawAll(guiRoot)
diff --git a/PushEd.gmx/scripts/PEd_actCloseWindow.gml b/PushEd.gmx/scripts/PEd_actCloseWindow.gml
new file mode 100644
index 00000000..dfa02861
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_actCloseWindow.gml
@@ -0,0 +1,13 @@
+/// PEd_actCloseWindow()
+/**
+ * @brief Closes the topmost window.
+ */
+var _items = PEd_guiCompoundShapeGetItems(guiRoot);
+var _item = _items[| ds_list_size(_items) - 1];
+if (!is_undefined(_item))
+{
+ if (PEd_guiShapeGetType(_item) == PEdGUIShape.Window)
+ {
+ PEd_guiDestroyShape(_item);
+ }
+}
diff --git a/PushEd.gmx/scripts/PEd_actCopySelectedObjects.gml b/PushEd.gmx/scripts/PEd_actCopySelectedObjects.gml
new file mode 100644
index 00000000..900569e8
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_actCopySelectedObjects.gml
@@ -0,0 +1,30 @@
+/// PEd_actCopySelectedObjects()
+/**
+ * @brief Creates a copy of all selected objects.
+ */
+var _size = ds_list_size(selectedObjects);
+if (_size > 0)
+{
+ if (editMode == PEdEditModes.Object)
+ {
+ var _instances = PEd_roomGetInstances(PEd_getCurrentRoom());
+ for (var i = 0; i < _size; i++)
+ {
+ var _copy = PEd_instanceCopy(selectedObjects[| i]);
+ ds_list_add(_instances, _copy);
+ ds_list_replace(selectedObjects, i, _copy);
+ }
+ }
+ else if (editMode == PEdEditModes.Tile)
+ {
+ var _room = PEd_getCurrentRoom();
+ var _tiles = PEd_roomGetTiles(_room);
+ for (var i = 0; i < _size; i ++)
+ {
+ var _tile = PEd_tileCopy(_room, selectedObjects[| i]);
+ ds_list_add(_tiles, _tile);
+ ds_list_replace(selectedObjects, i, _tile);
+ }
+ }
+ PEd_guiRequestRedrawAll(guiRoot)
+}
diff --git a/PushEd.gmx/scripts/PEd_actCreateEmptyRoom.gml b/PushEd.gmx/scripts/PEd_actCreateEmptyRoom.gml
new file mode 100644
index 00000000..1913ddb8
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_actCreateEmptyRoom.gml
@@ -0,0 +1,9 @@
+/// PEd_actCreateEmptyRoom()
+/**
+ * @brief Creates a new empty room.
+ */
+if (show_question("Do you really want to create new room?"))
+{
+ instance_activate_all();
+ alarm[3] = 1;
+}
diff --git a/PushEd.gmx/scripts/PEd_actDestroySelectedObjects.gml b/PushEd.gmx/scripts/PEd_actDestroySelectedObjects.gml
new file mode 100644
index 00000000..5a8821cd
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_actDestroySelectedObjects.gml
@@ -0,0 +1,20 @@
+/// PEd_actDestroySelectedObjects()
+/**
+ * @brief Destroys all selected objects.
+ */
+if (PEd_getSelectedObject() <= 0)
+ exit;
+
+switch (editMode)
+{
+ case PEdEditModes.Object:
+ for (var i = ds_list_size(selectedObjects) - 1; i >= 0; i--)
+ PEd_instanceDestroy(selectedObjects[| i]);
+ break;
+
+ case PEdEditModes.Tile:
+ for (var i = ds_list_size(selectedObjects) - 1; i >= 0; i--)
+ PEd_tileDelete(selectedObjects[| i]);
+ break;
+}
+PEd_actClearSelection();
diff --git a/PushEd.gmx/scripts/PEd_actEmpty.gml b/PushEd.gmx/scripts/PEd_actEmpty.gml
new file mode 100644
index 00000000..9f7af564
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_actEmpty.gml
@@ -0,0 +1,6 @@
+/// PEd_actEmpty()
+/**
+ * @brief An empty action script. Does nothing (duh... -_-).
+ * @note Or it maybe actually does something?
+ */
+show_debug_message("It works!");
diff --git a/PushEd.gmx/scripts/PEd_actExit.gml b/PushEd.gmx/scripts/PEd_actExit.gml
new file mode 100644
index 00000000..74e8c23a
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_actExit.gml
@@ -0,0 +1,10 @@
+/// PEd_actExit()
+/**
+ * @brief Quits the editor.
+ */
+// Activate all instances
+instance_activate_all();
+
+// Instance activate all works only after end of the script so we have to put
+// the rest into alarm :-/
+alarm[5] = 1;
diff --git a/PushEd.gmx/scripts/PEd_actExportRoom.gml b/PushEd.gmx/scripts/PEd_actExportRoom.gml
new file mode 100644
index 00000000..0aa30540
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_actExportRoom.gml
@@ -0,0 +1,7 @@
+/// PEd_actExportRoom()
+// Activate all instances
+instance_activate_all();
+
+// Instance activate all works only after end of the script so we have to put
+// the rest into alarm :-/
+alarm[1] = 1;
diff --git a/PushEd.gmx/scripts/PEd_actHideSelectedObjects.gml b/PushEd.gmx/scripts/PEd_actHideSelectedObjects.gml
new file mode 100644
index 00000000..4402f01c
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_actHideSelectedObjects.gml
@@ -0,0 +1,21 @@
+/// PEd_actHideSelectedObjects()
+/**
+ * @brief Hides selected objects and clears selection.
+ */
+switch (editMode)
+{
+ case PEdEditModes.Object:
+ for (var i = ds_list_size(selectedObjects) - 1; i >= 0; i--)
+ {
+ selectedObjects[| i].visible = false;
+ }
+ break;
+
+ case PEdEditModes.Tile:
+ for (var i = ds_list_size(selectedObjects) - 1; i >= 0; i--)
+ {
+ tile_set_visible(selectedObjects[| i], false);
+ }
+ break;
+}
+PEd_actClearSelection();
diff --git a/PushEd.gmx/scripts/PEd_actImportRoom.gml b/PushEd.gmx/scripts/PEd_actImportRoom.gml
new file mode 100644
index 00000000..591dd7df
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_actImportRoom.gml
@@ -0,0 +1,7 @@
+/// PEd_actImportRoom()
+// Activate all instances
+instance_activate_all();
+
+// Instance activate all works only after end of the script so we have to put
+// the rest into alarm :-/
+alarm[2] = 1;
diff --git a/PushEd.gmx/scripts/PEd_actInstallMap.gml b/PushEd.gmx/scripts/PEd_actInstallMap.gml
new file mode 100644
index 00000000..d50952b2
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_actInstallMap.gml
@@ -0,0 +1,7 @@
+/// PEd_actInstallMap(file)
+/**
+ * @brief Copies given map file to platform specific saving/loading directory.
+ * @param {string} file The name of the file to install.
+ */
+gml_pragma("forceinline");
+file_copy(argument0, string_replace(argument0, filename_dir(argument0) + "\", ""));
diff --git a/PushEd.gmx/scripts/PEd_actMovePivotHere.gml b/PushEd.gmx/scripts/PEd_actMovePivotHere.gml
new file mode 100644
index 00000000..734995c3
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_actMovePivotHere.gml
@@ -0,0 +1,15 @@
+/// PEd_actMovePivotHere()
+/**
+ * @brief Moves pivot at the mouse position.
+ */
+if (global.pedUsing3D)
+{
+ var _space = PEd_cameraScreenToPlaneXY(guiMousePressX, guiMousePressY, PEd_oPivot.z);
+ PEd_oPivot.x = _space[0];
+ PEd_oPivot.y = _space[1];
+}
+else
+{
+ PEd_oPivot.x = (guiMousePressX - viewportX) * viewZoom + view_xview[0];
+ PEd_oPivot.y = (guiMousePressY - viewportY) * viewZoom + view_yview[0];
+}
diff --git a/PushEd.gmx/scripts/PEd_actMoveTool.gml b/PushEd.gmx/scripts/PEd_actMoveTool.gml
new file mode 100644
index 00000000..6fee19de
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_actMoveTool.gml
@@ -0,0 +1,5 @@
+/// PEd_actMoveTool()
+/**
+ * @brief Selects the move tool.
+ */
+editTool = PEdTools.Move;
diff --git a/PushEd.gmx/scripts/PEd_actOpenRoom.gml b/PushEd.gmx/scripts/PEd_actOpenRoom.gml
new file mode 100644
index 00000000..c196df9d
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_actOpenRoom.gml
@@ -0,0 +1,7 @@
+/// PEd_actOpenRoom()
+// Activate all instances
+instance_activate_all();
+
+// Instance activate all works only after end of the script so we have to put
+// the rest into alarm :-/
+alarm[4] = 1;
diff --git a/PushEd.gmx/scripts/PEd_actPlayRoom.gml b/PushEd.gmx/scripts/PEd_actPlayRoom.gml
new file mode 100644
index 00000000..f39a6fed
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_actPlayRoom.gml
@@ -0,0 +1,11 @@
+/// PEd_actPlayRoom()
+/**
+ * @brief Saves the room into a "play.bbmap" file and goes to PEd_rmPlay.
+ */
+var _room = PEd_getCurrentRoom();
+var _name = PEd_roomGetName(_room);
+if (PEd_roomSaveToBBMAP2(_room, "play.bbmap"))
+{
+ PEd_roomSetName(_room, _name);
+ room_goto(PEd_rmPlay);
+}
diff --git a/PushEd.gmx/scripts/PEd_actRotateTool.gml b/PushEd.gmx/scripts/PEd_actRotateTool.gml
new file mode 100644
index 00000000..b0187620
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_actRotateTool.gml
@@ -0,0 +1,5 @@
+/// PEd_actRotateTool()
+/**
+ * @brief Selects the rotate tool.
+ */
+editTool = PEdTools.Rotate;
diff --git a/PushEd.gmx/scripts/PEd_actSaveRoom.gml b/PushEd.gmx/scripts/PEd_actSaveRoom.gml
new file mode 100644
index 00000000..2b129b31
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_actSaveRoom.gml
@@ -0,0 +1,11 @@
+/// PEd_actSaveRoom()
+/**
+ * @brief Saves the room.
+ */
+// Make sure that all instances that you want to save are activated,
+// deactivated instances can not be saved!
+instance_activate_all();
+
+// Instance activate all works only after end of the script so we have to put
+// the rest into alarm :-/
+alarm[0] = 1;
diff --git a/PushEd.gmx/scripts/PEd_actSaveRoomAs.gml b/PushEd.gmx/scripts/PEd_actSaveRoomAs.gml
new file mode 100644
index 00000000..d4765b87
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_actSaveRoomAs.gml
@@ -0,0 +1,6 @@
+/// PEd_actSaveRoomAs()
+/**
+ * @brief Saves the room into a new file.
+ */
+pedFileSave = "";
+PEd_actSaveRoom();
diff --git a/PushEd.gmx/scripts/PEd_actScaleTool.gml b/PushEd.gmx/scripts/PEd_actScaleTool.gml
new file mode 100644
index 00000000..169e0f69
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_actScaleTool.gml
@@ -0,0 +1,5 @@
+/// PEd_actScaleTool()
+/**
+ * @brief Selects the scale tool.
+ */
+editTool = PEdTools.Scale;
diff --git a/PushEd.gmx/scripts/PEd_actShowInfo.gml b/PushEd.gmx/scripts/PEd_actShowInfo.gml
new file mode 100644
index 00000000..247ba187
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_actShowInfo.gml
@@ -0,0 +1,18 @@
+/// PEd_actShowInfo()
+/**
+ * @brief Shows message containing info about PushEd.
+ */
+if (!PEd_guiShapeExists(guiWindowInfo))
+{
+ var _info = PEd_guiCreateWindow("Info");
+ var _width = 450;
+ var _height = 150;
+ PEd_guiShapeSetPosition(_info,
+ round((windowWidth - _width) * 0.5),
+ round((windowHeight - _height) * 0.5));
+ PEd_guiShapeSetSize(_info, _width, _height);
+ var _infoContainer = _info[? "container"];
+ PEd_guiWindowSetContent(_info, PEd_guiContentInfo);
+ PEd_guiAddItem(guiRoot, _info);
+ guiWindowInfo = _info;
+}
diff --git a/PushEd.gmx/scripts/PEd_actSwitchDebug.gml b/PushEd.gmx/scripts/PEd_actSwitchDebug.gml
new file mode 100644
index 00000000..6081a2f9
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_actSwitchDebug.gml
@@ -0,0 +1,5 @@
+/// PEd_actSwitchDebug()
+/**
+ * @brief Switches debug on/off.
+ */
+debugShow = !debugShow;
diff --git a/PushEd.gmx/scripts/PEd_actSwitchEditMode.gml b/PushEd.gmx/scripts/PEd_actSwitchEditMode.gml
new file mode 100644
index 00000000..1a2ad12b
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_actSwitchEditMode.gml
@@ -0,0 +1,12 @@
+/// PEd_actSwitchEditMode()
+/**
+ * @brief If not in 3D then switches between object and tile edit mode.
+ */
+if (!global.pedUsing3D)
+{
+ if ((++editMode) > 1)
+ {
+ editMode = 0;
+ }
+ PEd_actClearSelection();
+}
diff --git a/PushEd.gmx/scripts/PEd_actSwitchFloor.gml b/PushEd.gmx/scripts/PEd_actSwitchFloor.gml
new file mode 100644
index 00000000..001477b4
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_actSwitchFloor.gml
@@ -0,0 +1,13 @@
+/// PEd_actSwitchFloor()
+/**
+ * @brief Switches drawing of the floor on/off.
+ */
+editFloor = !editFloor;
+if (editFloor)
+{
+ PEd_guiShowPopupMessage("Show floor.");
+}
+else
+{
+ PEd_guiShowPopupMessage("Hide floor.");
+}
diff --git a/PushEd.gmx/scripts/PEd_actSwitchGrid.gml b/PushEd.gmx/scripts/PEd_actSwitchGrid.gml
new file mode 100644
index 00000000..65dbfbe5
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_actSwitchGrid.gml
@@ -0,0 +1,14 @@
+/// PEd_actSwitchGrid()
+/**
+ * @brief Switches aligning to the grid on/off.
+ */
+var _room = PEd_getCurrentRoom();
+PEd_roomSetGrid(_room, !PEd_roomGetGrid(_room));
+if (PEd_roomGetGrid(_room))
+{
+ PEd_guiShowPopupMessage("Enabled snapping to grid.");
+}
+else
+{
+ PEd_guiShowPopupMessage("Disabled snapping to grid.");
+}
diff --git a/PushEd.gmx/scripts/PEd_actSwitchTo2D.gml b/PushEd.gmx/scripts/PEd_actSwitchTo2D.gml
new file mode 100644
index 00000000..e2cdf1de
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_actSwitchTo2D.gml
@@ -0,0 +1,16 @@
+/// PEd_actSwitchTo2D()
+/**
+ * @brief Switches to the 2D mode.
+ */
+if (global.pedUsing3D)
+{
+ var _q = "Do you really want to switch to 2D mode?"
+ + " In this mode variables used only for"
+ + " 3D will not be saved!";
+
+ if (show_question(_q))
+ {
+ modeSwitch = true;
+ PEd_guiShowPopupMessage("2D mode.");
+ }
+}
diff --git a/PushEd.gmx/scripts/PEd_actSwitchTo3D.gml b/PushEd.gmx/scripts/PEd_actSwitchTo3D.gml
new file mode 100644
index 00000000..7eef6c65
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_actSwitchTo3D.gml
@@ -0,0 +1,17 @@
+/// PEd_actSwitchTo3D()
+/**
+ * @brief Switches to the 3D mode.
+ */
+if (!global.pedUsing3D)
+{
+ var _q = "Do you really want to switch to 3D mode?"
+ + " In this mode every instance that you"
+ + " want to edit has to containt variables"
+ + " for 3D transformations!";
+
+ if (show_question(_q))
+ {
+ modeSwitch = true;
+ PEd_guiShowPopupMessage("3D mode.");
+ }
+}
diff --git a/PushEd.gmx/scripts/PEd_actSwitchToObjectMode.gml b/PushEd.gmx/scripts/PEd_actSwitchToObjectMode.gml
new file mode 100644
index 00000000..30267c60
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_actSwitchToObjectMode.gml
@@ -0,0 +1,11 @@
+/// PEd_actSwitchToObjectMode()
+/**
+ * @brief Switches to object edit mode.
+ */
+if (editMode != PEdEditModes.Object)
+{
+ editMode = PEdEditModes.Object;
+ PEd_actClearSelection();
+ PEd_guiRequestRedrawAll(guiRoot)
+ PEd_guiShowPopupMessage("Object editing mode.");
+}
diff --git a/PushEd.gmx/scripts/PEd_actSwitchToTileMode.gml b/PushEd.gmx/scripts/PEd_actSwitchToTileMode.gml
new file mode 100644
index 00000000..f6dcad15
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_actSwitchToTileMode.gml
@@ -0,0 +1,18 @@
+/// PEd_actSwitchToTileMode()
+/**
+ * @brief Switches to tile edit mode.
+ */
+if (!global.pedUsing3D)
+{
+ if (editMode != PEdEditModes.Tile)
+ {
+ editMode = PEdEditModes.Tile;
+ PEd_actClearSelection();
+ PEd_guiRequestRedrawAll(guiRoot)
+ PEd_guiShowPopupMessage("Tile editing mode.");
+ }
+}
+else
+{
+ PEd_guiShowPopupMessage("Cannot change editing mode in 3D.");
+}
diff --git a/PushEd.gmx/scripts/PEd_actSwitchTool.gml b/PushEd.gmx/scripts/PEd_actSwitchTool.gml
new file mode 100644
index 00000000..9c5ad965
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_actSwitchTool.gml
@@ -0,0 +1,10 @@
+/// PEd_actSwitchTool()
+/**
+ * @brief Loops between tools.
+ */
+editTool += 1;
+if (editTool > 2)
+{
+ editTool = 0;
+}
+PEd_guiRequestRedrawAll(guiRoot);
diff --git a/PushEd.gmx/scripts/PEd_addCustomData.gml b/PushEd.gmx/scripts/PEd_addCustomData.gml
new file mode 100644
index 00000000..dc817edb
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_addCustomData.gml
@@ -0,0 +1,11 @@
+/// PEd_addCustomData(container, type, name, getter, setter)
+/**
+ * @brief Adds custom data to the data container.
+ * @param {real} container The id of the container.
+ * @param {real} type The type of the custom data. See PEdDataTypes enumerator.
+ * @param {string} name The name of the custom data.
+ * @param {real} getter The id of the script that gets the custom data. Use noone for no getter.
+ * @param {real} setter The id of the script that sets the custom data. Use noone for no setter.
+ */
+gml_pragma("forceinline");
+ds_list_add(argument0, argument1, argument2, argument3, argument4);
diff --git a/PushEd.gmx/scripts/PEd_argbToAlpha.gml b/PushEd.gmx/scripts/PEd_argbToAlpha.gml
new file mode 100644
index 00000000..445424f2
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_argbToAlpha.gml
@@ -0,0 +1,8 @@
+/// PEd_argbToAlpha(argb)
+/**
+ * @brief Converts ARGB colour to alpha.
+ * @param {real} argb The ARGB colour.
+ * @return {real} The alpha.
+ */
+gml_pragma("forceinline");
+return (((argument0 & $FF000000) >> 24) / 255);
diff --git a/PushEd.gmx/scripts/PEd_argbToColour.gml b/PushEd.gmx/scripts/PEd_argbToColour.gml
new file mode 100644
index 00000000..b37ca9a2
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_argbToColour.gml
@@ -0,0 +1,8 @@
+/// PEd_argbToColour(argb)
+/**
+ * @brief Converts ARGB colour to BGR colour.
+ * @param {real} argb The ARGB colour.
+ * @return {real} The BGR colour.
+ */
+gml_pragma("forceinline");
+return PEd_rgbToBgr(argument0 & $FFFFFF);
diff --git a/PushEd.gmx/scripts/PEd_assignVariable.gml b/PushEd.gmx/scripts/PEd_assignVariable.gml
new file mode 100644
index 00000000..6434311e
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_assignVariable.gml
@@ -0,0 +1,96 @@
+/// PEd_assignVariable(name, value)
+/**
+ * @brief Sets variable of given name.
+ * @param {string} name The name of the variable.
+ * @param {real} value The value to be assigned.
+ */
+var _name = string(argument0);
+var _value = string(argument1);
+
+switch (_name)
+{
+ // TODO: Assign values to user defined variables
+ case "z": z = real(_value); break;
+ case "rotX": rotX = real(_value); break;
+ case "rotY": rotY = real(_value); break;
+ case "scaleZ": scaleZ = real(_value); break;
+
+ // PushEd variables
+ case "PEd_ALPHA": PEd_ALPHA = real(_value); break;
+ case "PEd_COLOUR": PEd_COLOUR = real(_value); break;
+ case "PEd_POS_X": PEd_POS_X = real(_value); break;
+ case "PEd_POS_Y": PEd_POS_Y = real(_value); break;
+ case "PEd_POS_Z": PEd_POS_Z = real(_value); break;
+ case "PEd_ROT_X": PEd_ROT_X = real(_value); break;
+ case "PEd_ROT_Y": PEd_ROT_Y = real(_value); break;
+ case "PEd_ROT_Z": PEd_ROT_Z = real(_value); break;
+ case "PEd_SCALE_X": PEd_SCALE_X = real(_value); break;
+ case "PEd_SCALE_Y": PEd_SCALE_Y = real(_value); break;
+ case "PEd_SCALE_Z": PEd_SCALE_Z = real(_value); break;
+
+ // Built-in variables
+ case "alarm[0]": alarm[0] = real(_value); break;
+ case "alarm[1]": alarm[1] = real(_value); break;
+ case "alarm[2]": alarm[2] = real(_value); break;
+ case "alarm[3]": alarm[3] = real(_value); break;
+ case "alarm[4]": alarm[4] = real(_value); break;
+ case "alarm[5]": alarm[5] = real(_value); break;
+ case "alarm[6]": alarm[6] = real(_value); break;
+ case "alarm[7]": alarm[7] = real(_value); break;
+ case "alarm[8]": alarm[8] = real(_value); break;
+ case "alarm[9]": alarm[9] = real(_value); break;
+ case "alarm[10]": alarm[10] = real(_value); break;
+ case "alarm[11]": alarm[11] = real(_value); break;
+ case "depth": depth = real(_value); break;
+ case "direction": direction = real(_value); break;
+ case "friction": friction = real(_value); break;
+ case "gravity": gravity = real(_value); break;
+ case "gravity_direction": gravity_direction = real(_value); break;
+ case "hspeed": hspeed = real(_value); break;
+ case "image_alpha": image_alpha = real(_value); break;
+ case "image_angle": image_angle = real(_value); break;
+ case "image_blend": image_blend = real(_value); break;
+ case "image_index": image_index = real(_value); break;
+ case "image_speed": image_speed = real(_value); break;
+ case "image_xscale": image_xscale = real(_value); break;
+ case "image_yscale": image_yscale = real(_value); break;
+ case "mask_index": mask_index = real(_value); break;
+ case "path_endaction": path_endaction = real(_value); break;
+ case "path_orientation": path_orientation = real(_value); break;
+ case "path_position": path_position = real(_value); break;
+ case "path_positionprevious": path_positionprevious = real(_value); break;
+ case "path_scale": path_scale = real(_value); break;
+ case "path_speed": path_speed = real(_value); break;
+ case "persistent": persistent = real(_value); break;
+ case "phy_active": phy_active = real(_value); break;
+ case "phy_angular_damping": phy_angular_damping = real(_value); break;
+ case "phy_angular_velocity": phy_angular_velocity = real(_value); break;
+ case "phy_bullet": phy_bullet = real(_value); break;
+ case "phy_fixed_rotation": phy_fixed_rotation = real(_value); break;
+ case "phy_linear_damping": phy_linear_damping = real(_value); break;
+ case "phy_linear_velocity_x": phy_linear_velocity_x = real(_value); break;
+ case "phy_linear_velocity_y": phy_linear_velocity_y = real(_value); break;
+ case "phy_position_x": phy_position_x = real(_value); break;
+ case "phy_position_xprevious": phy_position_xprevious = real(_value); break;
+ case "phy_position_y": phy_position_y = real(_value); break;
+ case "phy_position_yprevious": phy_position_yprevious = real(_value); break;
+ case "phy_rotation": phy_rotation = real(_value); break;
+ case "phy_speed_x": phy_speed_x = real(_value); break;
+ case "phy_speed_y": phy_speed_y = real(_value); break;
+ case "solid": solid = real(_value); break;
+ case "speed": speed = real(_value); break;
+ case "sprite_index": sprite_index = real(_value); break;
+ case "timeline_index": timeline_index = real(_value); break;
+ case "timeline_loop": timeline_loop = real(_value); break;
+ case "timeline_position": timeline_position = real(_value); break;
+ case "timeline_running": timeline_running = real(_value); break;
+ case "timeline_speed": timeline_speed = real(_value); break;
+ case "vspeed": vspeed = real(_value); break;
+ case "x": x = real(_value); break;
+ case "xprevious": xprevious = real(_value); break;
+ case "xstart": xstart = real(_value); break;
+ case "y": y = real(_value); break;
+ case "yprevious": yprevious = real(_value); break;
+ case "ystart": ystart = real(_value); break;
+ case "visible": visible = real(_value); break;
+}
diff --git a/PushEd.gmx/scripts/PEd_backgroundGetImage.gml b/PushEd.gmx/scripts/PEd_backgroundGetImage.gml
new file mode 100644
index 00000000..4833e93c
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_backgroundGetImage.gml
@@ -0,0 +1,8 @@
+/// PEd_backgroundGetImage(background)
+/**
+ * @brief Gets the image of the background.
+ * @param {real} background The id of the background.
+ * @return {real} The image of the background.
+ */
+gml_pragma("forceinline");
+return argument0[? "index"];
diff --git a/PushEd.gmx/scripts/PEd_backgroundGetSpeedHor.gml b/PushEd.gmx/scripts/PEd_backgroundGetSpeedHor.gml
new file mode 100644
index 00000000..43094592
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_backgroundGetSpeedHor.gml
@@ -0,0 +1,8 @@
+/// PEd_backgroundGetSpeedHor(background)
+/**
+ * @brief Gets the horizontal speed of the background.
+ * @param {real} background The id of the background.
+ * @return {real} The horizontal speed of the background.
+ */
+gml_pragma("forceinline");
+return argument0[? "hspeed"];
diff --git a/PushEd.gmx/scripts/PEd_backgroundGetSpeedVer.gml b/PushEd.gmx/scripts/PEd_backgroundGetSpeedVer.gml
new file mode 100644
index 00000000..c0dee8c3
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_backgroundGetSpeedVer.gml
@@ -0,0 +1,8 @@
+/// PEd_backgroundGetSpeedVer(background)
+/**
+ * @brief Gets the vertical speed of the background.
+ * @param {real} background The id of the background.
+ * @return {real} The vertical speed of the background.
+ */
+gml_pragma("forceinline");
+return argument0[? "vspeed"];
diff --git a/PushEd.gmx/scripts/PEd_backgroundGetStretch.gml b/PushEd.gmx/scripts/PEd_backgroundGetStretch.gml
new file mode 100644
index 00000000..9b28fa3f
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_backgroundGetStretch.gml
@@ -0,0 +1,8 @@
+/// PEd_backgroundGetStretch(background)
+/**
+ * @brief Gets the stretch setting of the background.
+ * @param {real} background The id of the background.
+ * @return {bool} True if stretching is enabled.
+ */
+gml_pragma("forceinline");
+return argument0[? "stretch"];
diff --git a/PushEd.gmx/scripts/PEd_backgroundGetTiledHor.gml b/PushEd.gmx/scripts/PEd_backgroundGetTiledHor.gml
new file mode 100644
index 00000000..b0204fa5
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_backgroundGetTiledHor.gml
@@ -0,0 +1,8 @@
+/// PEd_backgroundGetTiledHor(background)
+/**
+ * @brief Gets wgether the background is horizontally tiled.
+ * @param {real} background The id of the background.
+ * @return {bool} True if the background is horizontally tiled.
+ */
+gml_pragma("forceinline");
+return argument0[? "htiled"];
diff --git a/PushEd.gmx/scripts/PEd_backgroundGetTiledVer.gml b/PushEd.gmx/scripts/PEd_backgroundGetTiledVer.gml
new file mode 100644
index 00000000..2736414a
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_backgroundGetTiledVer.gml
@@ -0,0 +1,8 @@
+/// PEd_backgroundGetTiledVer(background)
+/**
+ * @brief Gets whether the background is vertically tiled.
+ * @param {real} background The id of the background.
+ * @return {bool} True if the background is vertically tiled.
+ */
+gml_pragma("forceinline");
+return argument0[? "vtiled"];
diff --git a/PushEd.gmx/scripts/PEd_backgroundGetVisible.gml b/PushEd.gmx/scripts/PEd_backgroundGetVisible.gml
new file mode 100644
index 00000000..3168d494
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_backgroundGetVisible.gml
@@ -0,0 +1,8 @@
+/// PEd_backgroundGetVisible(background)
+/**
+ * @brief Gets whether the background is visible.
+ * @param {real} background The id of the background.
+ * @return {bool} True if the background is visible.
+ */
+gml_pragma("forceinline");
+return argument0[? "visible"];
diff --git a/PushEd.gmx/scripts/PEd_backgroundGetX.gml b/PushEd.gmx/scripts/PEd_backgroundGetX.gml
new file mode 100644
index 00000000..2d1e3cb9
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_backgroundGetX.gml
@@ -0,0 +1,8 @@
+/// PEd_backgroundGetX(background)
+/**
+ * @brief Gets the x position of the background.
+ * @param {real} background The id of the background.
+ * @return {real} The x position of the background.
+ */
+gml_pragma("forceinline");
+return argument0[? "x"];
diff --git a/PushEd.gmx/scripts/PEd_backgroundGetY.gml b/PushEd.gmx/scripts/PEd_backgroundGetY.gml
new file mode 100644
index 00000000..3ab56468
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_backgroundGetY.gml
@@ -0,0 +1,8 @@
+/// PEd_backgroundGetY(background)
+/**
+ * @brief Gets the y position of the background.
+ * @param {real} background The id of the background.
+ * @return {real} The y position of the background.
+ */
+gml_pragma("forceinline");
+return argument0[? "y"];
diff --git a/PushEd.gmx/scripts/PEd_backgroundIsForeground.gml b/PushEd.gmx/scripts/PEd_backgroundIsForeground.gml
new file mode 100644
index 00000000..6708146a
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_backgroundIsForeground.gml
@@ -0,0 +1,8 @@
+/// PEd_backgroundIsForeground(background)
+/**
+ * @brief Gets whether the background is marked as foreground.
+ * @param {real} background The id of the background.
+ * @return {bool} True if the background is marked as foreground.
+ */
+gml_pragma("forceinline");
+return argument0[? "foreground"];
diff --git a/PushEd.gmx/scripts/PEd_backgroundSetAsForeground.gml b/PushEd.gmx/scripts/PEd_backgroundSetAsForeground.gml
new file mode 100644
index 00000000..08ae774f
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_backgroundSetAsForeground.gml
@@ -0,0 +1,7 @@
+/// PEd_backgroundSetAsForeground(background, isForeground)
+/**
+ * @brief Marks/unmarks background as foreground.
+ * @param {real} background The id of the background.
+ * @param {bool} isForeground True to mark as foreground.
+ */
+argument0[? "foreground"] = argument1;
diff --git a/PushEd.gmx/scripts/PEd_backgroundSetImage.gml b/PushEd.gmx/scripts/PEd_backgroundSetImage.gml
new file mode 100644
index 00000000..6fce592f
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_backgroundSetImage.gml
@@ -0,0 +1,7 @@
+/// PEd_backgroundSetImage(background, image)
+/**
+ * @brief Sets background image.
+ * @param {real} background The id of the background.
+ * @param {real} image The background image. Use noone for none.
+ */
+argument0[? "index"] = max(argument1, -1);
diff --git a/PushEd.gmx/scripts/PEd_backgroundSetSpeedHor.gml b/PushEd.gmx/scripts/PEd_backgroundSetSpeedHor.gml
new file mode 100644
index 00000000..eab12d8e
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_backgroundSetSpeedHor.gml
@@ -0,0 +1,7 @@
+/// PEd_backgroundSetSpeedHor(background, speed)
+/**
+ * @brief Sets the horizontal speed of the background.
+ * @param {real} background The id of the background.
+ * @param {real} speed The horizontal speed.
+ */
+argument0[? "hspeed"] = argument1;
diff --git a/PushEd.gmx/scripts/PEd_backgroundSetSpeedVer.gml b/PushEd.gmx/scripts/PEd_backgroundSetSpeedVer.gml
new file mode 100644
index 00000000..57255f25
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_backgroundSetSpeedVer.gml
@@ -0,0 +1,7 @@
+/// PEd_backgroundSetSpeedVer(background, speed)
+/**
+ * @brief Sets the vertical speed of the background.
+ * @param {real} background The id of the background.
+ * @param {real} speed The vertical speed.
+ */
+argument0[? "vspeed"] = argument1;
diff --git a/PushEd.gmx/scripts/PEd_backgroundSetStretch.gml b/PushEd.gmx/scripts/PEd_backgroundSetStretch.gml
new file mode 100644
index 00000000..0de5ee0c
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_backgroundSetStretch.gml
@@ -0,0 +1,7 @@
+/// PEd_backgroundSetStretch(background, enabled)
+/**
+ * @brief Enables/disables stretching of the background.
+ * @param {real} background The id of the background.
+ * @param {bool} enabled True to enable stretching.
+ */
+argument0[? "stretch"] = argument1;
diff --git a/PushEd.gmx/scripts/PEd_backgroundSetTiledHor.gml b/PushEd.gmx/scripts/PEd_backgroundSetTiledHor.gml
new file mode 100644
index 00000000..1f8262d1
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_backgroundSetTiledHor.gml
@@ -0,0 +1,7 @@
+/// PEd_backgroundSetTiledHor(background, tiled)
+/**
+ * @brief Enables/disables horizontal tiling of the background.
+ * @param {real} background The id of the background.
+ * @param {bool} tiled True to enable horizontal tiling.
+ */
+argument0[? "htiled"] = argument1;
diff --git a/PushEd.gmx/scripts/PEd_backgroundSetTiledVer.gml b/PushEd.gmx/scripts/PEd_backgroundSetTiledVer.gml
new file mode 100644
index 00000000..655f686d
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_backgroundSetTiledVer.gml
@@ -0,0 +1,7 @@
+/// PEd_backgroundSetTiledVer(background, tiled)
+/**
+ * @brief Enables/disables vertical tiling of the background.
+ * @param {real} background The id of the background.
+ * @param {bool} tiled True to enable vertical tiling.
+ */
+argument0[? "vtiled"] = argument1;
diff --git a/PushEd.gmx/scripts/PEd_backgroundSetVisible.gml b/PushEd.gmx/scripts/PEd_backgroundSetVisible.gml
new file mode 100644
index 00000000..39276fc4
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_backgroundSetVisible.gml
@@ -0,0 +1,7 @@
+/// PEd_backgroundSetVisible(background, visible)
+/**
+ * @brief Enables/disables drawing of the background.
+ * @param {real} background The id of the background.
+ * @param {bool} visible True to enable drawing.
+ */
+argument0[? "visible"] = argument1;
diff --git a/PushEd.gmx/scripts/PEd_backgroundSetX.gml b/PushEd.gmx/scripts/PEd_backgroundSetX.gml
new file mode 100644
index 00000000..f4572207
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_backgroundSetX.gml
@@ -0,0 +1,7 @@
+/// PEd_backgroundSetX(background, val)
+/**
+ * @brief Sets the x position of the background.
+ * @param {real} background The id of the background.
+ * @param {real} val The new x position of the background.
+ */
+argument0[? "x"] = argument1;
diff --git a/PushEd.gmx/scripts/PEd_backgroundSetY.gml b/PushEd.gmx/scripts/PEd_backgroundSetY.gml
new file mode 100644
index 00000000..9524254b
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_backgroundSetY.gml
@@ -0,0 +1,7 @@
+/// PEd_backgroundSetY(background, val)
+/**
+ * @brief Sets the y position of the background.
+ * @param {real} background The id of the background.
+ * @param {real} val The new y position of the background.
+ */
+argument0[? "y"] = argument1;
diff --git a/PushEd.gmx/scripts/PEd_cameraControl.gml b/PushEd.gmx/scripts/PEd_cameraControl.gml
new file mode 100644
index 00000000..88593f98
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_cameraControl.gml
@@ -0,0 +1,217 @@
+/// PEd_cameraControl()
+/**
+ * @brief Handles camera controll.
+ */
+if (guiInputActive != noone
+ || keyboard_check(vk_control)
+ || keyboard_check(vk_alt))
+{
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Camera speed
+//
+if (keyboard_check_pressed(vk_numpad0))
+{
+ camSpeed = 0.5;
+}
+else if (keyboard_check_pressed(vk_numpad1))
+{
+ camSpeed = 1;
+}
+else if (keyboard_check_pressed(vk_numpad2))
+{
+ camSpeed = 4;
+}
+else if (keyboard_check_pressed(vk_numpad3))
+{
+ camSpeed = 6;
+}
+else if (keyboard_check_pressed(vk_numpad4))
+{
+ camSpeed = 8;
+}
+else if (keyboard_check_pressed(vk_numpad5))
+{
+ camSpeed = 10;
+}
+else if (keyboard_check_pressed(vk_numpad6))
+{
+ camSpeed = 12;
+}
+else if (keyboard_check_pressed(vk_numpad7))
+{
+ camSpeed = 14;
+}
+else if (keyboard_check_pressed(vk_numpad8))
+{
+ camSpeed = 16;
+}
+else if (keyboard_check_pressed(vk_numpad9))
+{
+ camSpeed = 18;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Mouselook
+//
+if (mouseInViewport)
+{
+ if (mouse_check_button(mb_right))
+ {
+ if (global.pedUsing3D)
+ {
+ // Mouselook (3D)
+ direction += (mouseLastX - windowMouseX) * mouseSens;
+ camPitch += (mouseLastY - windowMouseY) * mouseSens;
+ camPitch = max(-89, min(camPitch, 89));
+ }
+ else
+ {
+ // Move camera (2D)
+ view_xview[0] += (mouseLastX - windowMouseX) * viewZoom;
+ view_yview[0] += (mouseLastY - windowMouseY) * viewZoom;
+ }
+ }
+ else if (!global.pedUsing3D)
+ {
+ // Zoom
+ var _wheel = mouse_wheel_up() - mouse_wheel_down();
+
+ if (_wheel == 0)
+ {
+ _wheel = (keyboard_check(vk_pageup) - keyboard_check(vk_pagedown)) * 0.25;
+ }
+
+ if (_wheel != 0)
+ {
+ viewZoom -= _wheel * 0.25 * max(1, keyboard_check(vk_shift) * 4);
+ viewZoom = max(0.25, viewZoom);
+ view_wview[0] = view_wport[0] * viewZoom;
+ view_hview[0] = view_hport[0] * viewZoom;
+ }
+ }
+}
+
+if (global.pedUsing3D)
+{
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Look around with arrows
+ //
+ if (keyboard_check(vk_left))
+ {
+ direction += 3;
+ }
+ else if (keyboard_check(vk_right))
+ {
+ direction -= 3;
+ }
+ if (keyboard_check(vk_up))
+ {
+ camPitch = clamp(camPitch + 3, -89, 89);
+ }
+ else if (keyboard_check(vk_down))
+ {
+ camPitch = clamp(camPitch - 3, -89, 89);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Movement
+ //
+
+ //
+ // Keyboard
+ //
+ var _move = 0;
+ var _run = 1;
+
+ // Run
+ if (keyboard_check(vk_shift))
+ {
+ _run = 4;
+ }
+
+ var _speed = camSpeed * _run;
+
+ // Move forward/backward
+ if (keyboard_check(ord("W")))
+ {
+ x += lengthdir_x(_speed, direction);
+ y += lengthdir_y(_speed, direction);
+ _move = 1;
+ }
+ else if (keyboard_check(ord ("S")))
+ {
+ x -= lengthdir_x(_speed, direction);
+ y -= lengthdir_y(_speed, direction);
+ _move = -1;
+ }
+
+ // Strafe left/right
+ if (keyboard_check(ord("A")))
+ {
+ x += lengthdir_x(_speed, direction + 90);
+ y += lengthdir_y(_speed, direction + 90);
+ }
+ else if (keyboard_check(ord("D")))
+ {
+ x += lengthdir_x(_speed, direction - 90);
+ y += lengthdir_y(_speed, direction - 90);
+ }
+
+ // Move down/up
+ z += _speed * (keyboard_check(ord("E")) - keyboard_check(ord("Q")));
+
+ //
+ // Mousewheel
+ //
+ if (mouseInViewport)
+ {
+ var _l = camSpeed * 10;
+
+ if (keyboard_check(vk_shift))
+ {
+ _l *= 2;
+ }
+
+ x += lengthdir_x(_l, direction) * (mouse_wheel_up() - mouse_wheel_down());
+ y += lengthdir_y(_l, direction) * (mouse_wheel_up() - mouse_wheel_down());
+ z += sin(degtorad(camPitch)) * _l * (mouse_wheel_up() - mouse_wheel_down());
+ }
+}
+else
+{
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Move around with arrows
+ //
+ var _speed = camSpeed * 5;
+
+ if (keyboard_check(vk_shift))
+ {
+ _speed *= 4;
+ }
+
+ if (keyboard_check(vk_left))
+ {
+ view_xview[0] -= _speed;
+ }
+ else if (keyboard_check(vk_right))
+ {
+ view_xview[0] += _speed;
+ }
+
+ if (keyboard_check(vk_up))
+ {
+ view_yview[0] -= _speed;
+ }
+ else if (keyboard_check(vk_down))
+ {
+ view_yview[0] += _speed;
+ }
+}
diff --git a/PushEd.gmx/scripts/PEd_cameraGetDirVec3.gml b/PushEd.gmx/scripts/PEd_cameraGetDirVec3.gml
new file mode 100644
index 00000000..57a189a2
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_cameraGetDirVec3.gml
@@ -0,0 +1,11 @@
+/// PEd_cameraGetDirVec3()
+/**
+ * @brief Gets the direction vector of the editor camera.
+ * @return {vec3} The direction vector of the editor camera.
+ */
+var _camDir = PEd_oEditor.direction
+var _camPitch = PEd_oEditor.camPitch;
+var _cosPitch = dcos(_camPitch);
+return PEd_vec3(+dcos(_camDir) * _cosPitch,
+ -dsin(_camDir) * _cosPitch,
+ +dsin(_camPitch));
diff --git a/PushEd.gmx/scripts/PEd_cameraGetPosVec3.gml b/PushEd.gmx/scripts/PEd_cameraGetPosVec3.gml
new file mode 100644
index 00000000..8be28cac
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_cameraGetPosVec3.gml
@@ -0,0 +1,7 @@
+/// PEd_cameraGetPosVec3()
+/**
+ * @brief Gets the position of the editor camera.
+ * @return {vec3} The position of the editor camera.
+ */
+gml_pragma("forceinline");
+return PEd_vec3(PEd_oEditor.x, PEd_oEditor.y, PEd_oEditor.z);
diff --git a/PushEd.gmx/scripts/PEd_cameraGetRightVec3.gml b/PushEd.gmx/scripts/PEd_cameraGetRightVec3.gml
new file mode 100644
index 00000000..b58c2b4f
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_cameraGetRightVec3.gml
@@ -0,0 +1,9 @@
+/// PEd_cameraGetRightVec3()
+/**
+ * @brief Gets the right vector of the editor camera.
+ * @return {vec3} The right vector of the editor camera.
+ */
+gml_pragma("forceinline");
+return PEd_vec3(+dcos(PEd_oEditor.direction - 90),
+ -dsin(PEd_oEditor.direction - 90),
+ 0);
diff --git a/PushEd.gmx/scripts/PEd_cameraGetTanFovY.gml b/PushEd.gmx/scripts/PEd_cameraGetTanFovY.gml
new file mode 100644
index 00000000..a4b6c256
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_cameraGetTanFovY.gml
@@ -0,0 +1,7 @@
+/// PEd_cameraGetTanFovY()
+/**
+ * @brief Gets the tangent of the editor camera FovY value.
+ * @return {real} The tangent of the editor camera FovY value.
+ */
+gml_pragma("forceinline");
+return dtan(PEd_oEditor.camFOV * 0.5);
diff --git a/PushEd.gmx/scripts/PEd_cameraGetUpVec3.gml b/PushEd.gmx/scripts/PEd_cameraGetUpVec3.gml
new file mode 100644
index 00000000..c3dc26f0
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_cameraGetUpVec3.gml
@@ -0,0 +1,8 @@
+/// PEd_cameraGetUpVec3()
+/**
+ * @brief Gets the up vector of the editor camera.
+ * @return {vec3} The up vector of the editor camera.
+ */
+var _dir = PEd_cameraGetDirVec3();
+var _right = PEd_cameraGetRightVec3();
+return PEd_vec3Normalize(PEd_vec3Cross(_dir, _right));
diff --git a/PushEd.gmx/scripts/PEd_cameraScreenToPlaneXY.gml b/PushEd.gmx/scripts/PEd_cameraScreenToPlaneXY.gml
new file mode 100644
index 00000000..94938d36
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_cameraScreenToPlaneXY.gml
@@ -0,0 +1,12 @@
+/// PEd_cameraScreenToPlaneXY(x, y, z)
+/**
+ * @brief Converts the given screen coordinates to the space coordinates lying on the xy-plane at given height.
+ * @param {real} x The x position on the screen.
+ * @param {real} y The y position on the screen.
+ * @param {real} z The target z position in the space.
+ */
+gml_pragma("forceinline");
+return PEd_rayPlaneIntersect(PEd_cameraGetPosVec3(),
+ PEd_cameraUnprojectVec2(PEd_vec2(argument0, argument1)),
+ PEd_vec3(0, 0, argument2),
+ PEd_vec3(0, 0, 1));
diff --git a/PushEd.gmx/scripts/PEd_cameraSetProjection.gml b/PushEd.gmx/scripts/PEd_cameraSetProjection.gml
new file mode 100644
index 00000000..1ec74a7f
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_cameraSetProjection.gml
@@ -0,0 +1,36 @@
+/// PEd_cameraSetProjection([disable2D])
+/**
+ * @brief Sets projection to the editor's camera and stores WVP matrices.
+ * @param {bool} [disable2D] Set to true to disable using orthographic projection when in 2D mode.
+ */
+var _disable2D = false;
+if (argument_count > 0)
+{
+ _disable2D = argument[0];
+}
+with (PEd_oEditor)
+{
+ if (global.pedUsing3D)
+ {
+ var _dir = PEd_cameraGetDirVec3();
+ var _up = PEd_cameraGetUpVec3();
+ d3d_set_projection_ext(x, y, z,
+ x + _dir[0],
+ y + _dir[1],
+ z + _dir[2],
+ _up[0], _up[1], _up[2],
+ camFOV, viewportWidth / viewportHeight,
+ camClipNear, camClipFar);
+ }
+ else if (!_disable2D)
+ {
+ d3d_set_projection_ortho(view_xview[0], view_yview[0], viewportWidth * viewZoom, viewportHeight * viewZoom, 0);
+ }
+
+ matWorld = matrix_get(matrix_world);
+ matView = matrix_get(matrix_view);
+ matProjection = matrix_get(matrix_projection);
+
+ return true;
+}
+show_error("PEd_oEditor does not exist!", true);
diff --git a/PushEd.gmx/scripts/PEd_cameraUnprojectVec2.gml b/PushEd.gmx/scripts/PEd_cameraUnprojectVec2.gml
new file mode 100644
index 00000000..1a5ddaab
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_cameraUnprojectVec2.gml
@@ -0,0 +1,20 @@
+/// PEd_cameraUnprojectVec2(vector)
+/**
+ * @brief Unprojects the point on screen from screen space to a direction in world space.
+ * @return {vec3} A vector pointing from the camera in point direction.
+ */
+var _vec = argument0;
+var _d = PEd_cameraGetDirVec3();
+var _u = PEd_cameraGetUpVec3();
+var _v = PEd_cameraGetRightVec3();
+var _tFov = PEd_cameraGetTanFovY();
+_u = PEd_vec3Scale(_u, _tFov);
+var _width = PEd_oEditor.viewportWidth;
+var _height = PEd_oEditor.viewportHeight;
+var _aspect = _width / _height;
+_v = PEd_vec3Scale(_v, _tFov * _aspect);
+var _screenX = _vec[0] - PEd_oEditor.viewportX;
+var _screenY = _vec[1] - PEd_oEditor.viewportY;
+var _ray = PEd_vec3Add(_d, PEd_vec3Add(PEd_vec3Scale(_u, 1 - 2 * _screenY / _height),
+ PEd_vec3Scale(_v, 2 * _screenX / _width - 1)));
+return PEd_vec3Normalize(_ray);
diff --git a/PushEd.gmx/scripts/PEd_checkSurface.gml b/PushEd.gmx/scripts/PEd_checkSurface.gml
new file mode 100644
index 00000000..d818a775
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_checkSurface.gml
@@ -0,0 +1,25 @@
+/// PEd_checkSurface(surface, width, height)
+/**
+ * @brief Checks whether the surface exists and if it has correct size.
+ * Broken surfaces are recreated. Surfaces of wrong size are resized.
+ * @param {real} surface The id of the surface.
+ * @param {real} width The desired width of the surface.
+ * @param {real} height The desired height of the surface.
+ * @return {real} The surface id.
+ */
+var _surface = argument0;
+var _width = max(argument1, 1);
+var _height = max(argument2, 1);
+if (surface_exists(_surface))
+{
+ if (surface_get_width(_surface) != _width
+ || surface_get_height(_surface) != _height)
+ {
+ surface_resize(_surface, _width, _height);
+ }
+}
+else
+{
+ _surface = surface_create(_width, _height);
+}
+return _surface;
diff --git a/PushEd.gmx/scripts/PEd_codeProcess.gml b/PushEd.gmx/scripts/PEd_codeProcess.gml
new file mode 100644
index 00000000..2d938fa4
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_codeProcess.gml
@@ -0,0 +1,97 @@
+/// PEd_codeProcess(code)
+/**
+ * @brief Process given code.
+ * @param {string} code The code that should be processed.
+ * @note This can only handle code like "x = 20; y = 10;"
+ * and is not supposed to serve as GML code interpreter!
+ */
+var i = 0;
+var _code = string(argument0);
+var _char = "";
+var _lastChar = " ";
+var _word = "";
+var _isString = "";
+var _isComment = "";
+var _stack = ds_stack_create();
+
+do
+{
+ _char = string_char_at(_code, ++i);
+
+ if (_char == "/")
+ {
+ // Check for comments
+ if (string_char_at(_code, i + 1) == "*")
+ {
+ // Block comment start
+ if (_isComment == "")
+ {
+ _isComment = "*";
+ }
+ }
+ else if (string_char_at(_code, i - 1) == "*")
+ {
+ // Block comment end
+ if (_isComment == "*")
+ {
+ _isComment = "";
+ }
+ }
+ }
+ else if (((_char == " " && _lastChar != _char)
+ || _char == ""
+ || _char == ";"
+ || _char == "=")
+ && _isComment == "")
+ {
+ if (string_replace_all(_word, " ", "") != "")
+ {
+ ds_stack_push(_stack, _word);
+ }
+
+ if (_char == "=")
+ {
+ // Operator '='
+ ds_stack_push(_stack, _char);
+ }
+ else if (_char == ";"
+ || _char == "")
+ {
+ // End of statement
+ var _variable, _operator, _value;
+
+ if (!ds_stack_empty(_stack))
+ {
+ _value = ds_stack_pop(_stack);
+
+ if (!ds_stack_empty(_stack))
+ {
+ _operator = ds_stack_pop(_stack);
+
+ if (!ds_stack_empty(_stack))
+ {
+ _variable = ds_stack_pop(_stack);
+
+ // Assign variable
+ if (_operator == "=")
+ {
+ PEd_assignVariable(_variable, _value);
+ }
+ }
+ }
+ }
+ }
+
+ _word = "";
+ }
+ else if (_isComment == "")
+ {
+ // Concatenate word
+ _word += _char;
+ }
+
+ _lastChar = _char;
+}
+until (_char == "");
+
+ds_stack_destroy(_stack);
diff --git a/PushEd.gmx/scripts/PEd_colourAlphaToArgb.gml b/PushEd.gmx/scripts/PEd_colourAlphaToArgb.gml
new file mode 100644
index 00000000..04e4b925
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_colourAlphaToArgb.gml
@@ -0,0 +1,9 @@
+/// PEd_colourAlphaToArgb(colour, alpha)
+/**
+ * @brief Converts the colour and aplha into a ARGB colour.
+ * @param {real} colour The colour.
+ * @param {real} alpha The alpha.
+ * @return {real} The ARGB colour.
+ */
+gml_pragma("forceinline");
+return PEd_rgbToBgr(argument0) | ((argument1 * 255) << 24);
diff --git a/PushEd.gmx/scripts/PEd_createBackground.gml b/PushEd.gmx/scripts/PEd_createBackground.gml
new file mode 100644
index 00000000..fd18435f
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_createBackground.gml
@@ -0,0 +1,17 @@
+/// PEd_createBackground()
+/**
+ * @brief Creates a background.
+ * @return {real} The id of the background.
+ */
+var _background = ds_map_create();
+_background[? "x"] = 0;
+_background[? "y"] = 0;
+_background[? "foreground"] = false;
+_background[? "stretch"] = 0;
+_background[? "index"] = noone;
+_background[? "htiled"] = false;
+_background[? "vtiled"] = false;
+_background[? "hspeed"] = 0;
+_background[? "vspeed"] = 0;
+_background[? "visible"] = false;
+return _background;
diff --git a/PushEd.gmx/scripts/PEd_createCustomDataContainer.gml b/PushEd.gmx/scripts/PEd_createCustomDataContainer.gml
new file mode 100644
index 00000000..b45f4a4a
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_createCustomDataContainer.gml
@@ -0,0 +1,9 @@
+/// PEd_createCustomDataContainer(name)
+/**
+ * @brief Creates a container for a custom data.
+ * @param {string} name The name of the container.
+ * @return {real} The id of the container.
+ */
+var _container = ds_list_create();
+ds_list_add(_container, argument0, true); // name, expanded
+return _container;
diff --git a/PushEd.gmx/scripts/PEd_createPhysicsWorld.gml b/PushEd.gmx/scripts/PEd_createPhysicsWorld.gml
new file mode 100644
index 00000000..cf0b6ff2
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_createPhysicsWorld.gml
@@ -0,0 +1,15 @@
+/// PEd_createPhysicsWorld(enabled, gravityX, gravityY, pxToM)
+/**
+ * @brief Creates a physics world.
+ * @param {bool} enabled True to enable physics.
+ * @param {real} gravityX The gravity on the x axis.
+ * @param {real} gravityY The gravity on the y axis.
+ * @param {real} pxToM Pixel to meter ratio.
+ * @return {real} The id of the physics world.
+ */
+var _physicsWorld = ds_map_create();
+_physicsWorld[? "enabled"] = argument[0];
+_physicsWorld[? "gravityX"] = argument[1];
+_physicsWorld[? "gravityY"] = argument[2];
+_physicsWorld[? "pxToM"] = argument[3];
+return _physicsWorld;
diff --git a/PushEd.gmx/scripts/PEd_createRoom.gml b/PushEd.gmx/scripts/PEd_createRoom.gml
new file mode 100644
index 00000000..0c16d718
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_createRoom.gml
@@ -0,0 +1,44 @@
+/// PEd_createRoom(name, width, height)
+/**
+ * @brief Creates a new room.
+ * @param {string} name The name of the room.
+ * @param {real} width The width of the room.
+ * @param {real} height The height of the room.
+ * @return {real} room The id of the room.
+ */
+var _room = ds_map_create();
+_room[? "name"] = argument[0];
+_room[? "caption"] = "";
+_room[? "width"] = argument[1];
+_room[? "height"] = argument[2];
+_room[? "hsnap"] = 1;
+_room[? "vsnap"] = 1;
+_room[? "dsnap"] = 1;
+_room[? "speed"] = 60;
+_room[? "persistent"] = false;
+_room[? "colour"] = 0;
+_room[? "code"] = "";
+_room[? "grid"] = true;
+_room[? "showColour"] = true;
+
+var _views = ds_list_create();
+var _backgrounds = ds_list_create();
+for (var i = 0; i < 8; i++)
+{
+ var _view = PEd_createViewport((i == 0), 0, 0, 640, 480, 32, 32, -1, -1, noone);
+ var _background = PEd_createBackground();
+ PEd_dsListAddMap(_views, _view);
+ PEd_dsListAddMap(_backgrounds, _background);
+}
+ds_map_add_list(_room, "viewports", _views);
+ds_map_add_list(_room, "backgrounds", _backgrounds);
+var _physicsWorld = PEd_createPhysicsWorld(false, 0, 0, argument[1], argument[2], 0, 10, 0.1);
+ds_map_add_map(_room, "physicsWorld", _physicsWorld);
+
+var _instances = ds_list_create();
+ds_map_add_list(_room, "instances", _instances);
+
+var _tiles = ds_list_create();
+ds_map_add_list(_room, "tiles", _tiles);
+
+return _room;
diff --git a/PushEd.gmx/scripts/PEd_createSaveInstance.gml b/PushEd.gmx/scripts/PEd_createSaveInstance.gml
new file mode 100644
index 00000000..f44b78ed
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_createSaveInstance.gml
@@ -0,0 +1,20 @@
+/// PEd_createSaveInstance(instance)
+/**
+ * @brief Stores the instance data into a ds_map.
+ * @param {real} instance The id of the instance.
+ * @return {real} The ds_map.
+ */
+PEd_instanceAutocompleteCode(argument0);
+var _saveInstance = ds_map_create();
+_saveInstance[? "object"] = PEd_instanceGetObjectName(argument0);
+_saveInstance[? "name"] = PEd_instanceGetName(argument0);
+_saveInstance[? "x"] = PEd_instanceGetPosX(argument0);
+_saveInstance[? "y"] = PEd_instanceGetPosY(argument0);
+_saveInstance[? "scaleX"] = PEd_instanceGetScaleX(argument0);
+_saveInstance[? "scaleY"] = PEd_instanceGetScaleY(argument0);
+_saveInstance[? "rotation"] = PEd_instanceGetRotZ(argument0);
+_saveInstance[? "colour"] = PEd_instanceGetColour(argument0);
+_saveInstance[? "alpha"] = PEd_instanceGetAlpha(argument0);
+_saveInstance[? "code"] = PEd_instanceGetCode(argument0);
+_saveInstance[? "visible"] = argument0.visible;
+return _saveInstance;
diff --git a/PushEd.gmx/scripts/PEd_createSaveTile.gml b/PushEd.gmx/scripts/PEd_createSaveTile.gml
new file mode 100644
index 00000000..1d7ac028
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_createSaveTile.gml
@@ -0,0 +1,21 @@
+/// PEd_createSaveTile(tile)
+/**
+ * @brief Stores the tile data into a ds_map.
+ * @param {real} tile The id of the tile.
+ * @return {real} The ds_map.
+ */
+var _saveTile = ds_map_create();
+_saveTile[? "background" ] = background_get_name(tile_get_background(argument0));
+_saveTile[? "left"] = tile_get_left(argument0);
+_saveTile[? "top"] = tile_get_top(argument0);
+_saveTile[? "width"] = tile_get_width(argument0);
+_saveTile[? "height"] = tile_get_height(argument0);
+_saveTile[? "x"] = tile_get_x(argument0);
+_saveTile[? "y"] = tile_get_y(argument0);
+_saveTile[? "depth"] = tile_get_depth(argument0);
+_saveTile[? "scaleX"] = tile_get_xscale(argument0);
+_saveTile[? "scaleY"] = tile_get_yscale(argument0);
+_saveTile[? "colour"] = tile_get_blend(argument0);
+_saveTile[? "alpha"] = tile_get_alpha(argument0);
+_saveTile[? "visible"] = tile_get_visible(argument0);
+return _saveTile;
diff --git a/PushEd.gmx/scripts/PEd_createTile.gml b/PushEd.gmx/scripts/PEd_createTile.gml
new file mode 100644
index 00000000..fcf52eb7
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_createTile.gml
@@ -0,0 +1,30 @@
+/// PEd_createTile(room, background, left, top, width, height, x, y, depth)
+/**
+ * @brief Creates a tile and adds it to the room.
+ * @param {real} room The id of the room.
+ * @param {real} background The background asset from which the new tile will be extracted.
+ * @param {real} left The x coordinate of the left of the new tile, relative to the background asset's top left corner.
+ * @param {real} top The y coordinate of the top of the new tile, relative to the background assets top left corner.
+ * @param {real} width The width of the tile.
+ * @param {real} height The height of the tile.
+ * @param {real} x The x position in the room to place the tile.
+ * @param {real} y The y position in the room to place the tile.
+ * @param {real} depth The depth at which to place the tile.
+ * @return {real} The tile id.
+ */
+var _room = argument0;
+var _id = tile_add(argument1, argument2, argument3, argument4, argument5, argument6, argument7, argument8);
+var _tiles = PEd_roomGetTiles(_room);
+ds_list_add(_tiles, _id);
+
+// Register tile layer
+with (PEd_oEditor)
+{
+ if (ds_list_find_index(tileLayers, argument8) == -1)
+ {
+ ds_list_add(tileLayers, argument8);
+ ds_list_add(tileVisible, true);
+ }
+}
+
+return _id;
diff --git a/PushEd.gmx/scripts/PEd_createViewport.gml b/PushEd.gmx/scripts/PEd_createViewport.gml
new file mode 100644
index 00000000..cb348b12
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_createViewport.gml
@@ -0,0 +1,31 @@
+/// PEd_createViewport(visible, x, y, width, height, borderH, borderV, speedH, speedV, object)
+/**
+ * @brief Creates a new viewport.
+ * @param {bool} visible True to make the viewport visible.
+ * @param {real} x The x position of the viewport.
+ * @param {real} y The y position of the viewport.
+ * @param {real} width The width of the viewport.
+ * @param {real} height The height of the viewport.
+ * @param {real} borderH The size of the horizontal border of the viewport.
+ * @param {real} borderV The size of the vertical border of the viewport.
+ * @param {real} speedH The horizontal speed of the viewport.
+ * @param {real} speedV The vertical speed of the viewport.
+ * @param {real} object The object that the viewport will follow. Use noone for none.
+ * @return {real} The id of the viewport.
+ */
+var _viewport = ds_map_create();
+_viewport[? "visible"] = argument0;
+_viewport[? "x"] = argument1;
+_viewport[? "y"] = argument2;
+_viewport[? "width"] = argument3;
+_viewport[? "height"] = argument4;
+_viewport[? "portX"] = argument1;
+_viewport[? "portY"] = argument2;
+_viewport[? "portWidth"] = argument3;
+_viewport[? "portHeight"] = argument4;
+_viewport[? "borderHor"] = argument5;
+_viewport[? "borderVer"] = argument6;
+_viewport[? "speedHor"] = argument7;
+_viewport[? "speedVer"] = argument8;
+_viewport[? "object"] = argument9;
+return _viewport;
diff --git a/PushEd.gmx/scripts/PEd_deselectObject.gml b/PushEd.gmx/scripts/PEd_deselectObject.gml
new file mode 100644
index 00000000..42c70029
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_deselectObject.gml
@@ -0,0 +1,15 @@
+/// PEd_deselectObject(id)
+/**
+ * @brief Removes object from selection.
+ * @param {real} id The id of the instance or tile to remove from selection.
+ * @param {bool} True if the object was removed from selection.
+ */
+var _pos = ds_list_find_index(PEd_oEditor.selectedObjects, argument0);
+if (_pos >= 0)
+{
+ ds_list_delete(PEd_oEditor.selectedObjects, _pos);
+ ds_list_delete(PEd_oEditor.selectedObjectsData, _pos);
+ PEd_savePivotOffsets();
+ return true;
+}
+return false;
diff --git a/PushEd.gmx/scripts/PEd_dragAndDrop.gml b/PushEd.gmx/scripts/PEd_dragAndDrop.gml
new file mode 100644
index 00000000..bc9cebed
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_dragAndDrop.gml
@@ -0,0 +1,41 @@
+/// PEd_dragAndDrop()
+/**
+ * @brief Handles instance drag and drop.
+ */
+if (editMode == PEdEditModes.Object
+ && mouseInViewport)
+{
+ var _room = PEd_getCurrentRoom();
+ var _space = PEd_getMouseWorldPosition(_room);
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Drop instance
+ if (guiDnDObject != -1
+ && mouse_check_button(mb_left))
+ {
+ var i = PEd_instanceCreate(_room, _space[0], _space[1], guiDnDObject);
+ PEd_instanceSetPosZ(i, _space[2]);
+
+ with (i)
+ {
+ pivotOffX = 0;
+ pivotOffY = 0;
+ pivotOffZ = 0;
+ }
+
+ // Save instance id
+ pedDnDInstance = i;
+ contentBrowserIndexObject = -1;
+ guiDnDObject = -1;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Move dropped instance
+ if (pedDnDInstance != noone
+ && mouse_check_button(mb_left))
+ {
+ PEd_instanceSetPosX(pedDnDInstance, _space[0]);
+ PEd_instanceSetPosY(pedDnDInstance, _space[1]);
+ PEd_instanceSetPosZ(pedDnDInstance, _space[2]);
+ }
+}
diff --git a/PushEd.gmx/scripts/PEd_dsListAddList.gml b/PushEd.gmx/scripts/PEd_dsListAddList.gml
new file mode 100644
index 00000000..21de9336
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_dsListAddList.gml
@@ -0,0 +1,8 @@
+/// PEd_dsListAddList(l1, l2)
+/**
+ * @brief Adds the list l2 into the list l1.
+ * @param {real} l1 The list to add into.
+ * @param {real} l2 The list to be added.
+ */
+ds_list_add(argument0, argument1);
+ds_list_mark_as_list(argument0, ds_list_size(argument0) - 1);
diff --git a/PushEd.gmx/scripts/PEd_dsListAddMap.gml b/PushEd.gmx/scripts/PEd_dsListAddMap.gml
new file mode 100644
index 00000000..1480ab03
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_dsListAddMap.gml
@@ -0,0 +1,8 @@
+/// PEd_dsListAddMap(list, map)
+/**
+ * @brief Adds the map into the list.
+ * @param {real} list The list to add into.
+ * @param {real} map The map to be added.
+ */
+ds_list_add(argument0, argument1);
+ds_list_mark_as_map(argument0, ds_list_size(argument0) - 1);
diff --git a/PushEd.gmx/scripts/PEd_dsListAddUnique.gml b/PushEd.gmx/scripts/PEd_dsListAddUnique.gml
new file mode 100644
index 00000000..85e7f079
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_dsListAddUnique.gml
@@ -0,0 +1,13 @@
+/// PEd_dsListAddUnique(list, value)
+/**
+ * @brief If the value is not in the list, it is added to it.
+ * @param {real} list The id of the list.
+ * @param {any} value The value to be added.
+ * @return {real} The index on which has been the value found or -1.
+ */
+var _pos = ds_list_find_index(argument0, argument1);
+if (_pos == -1)
+{
+ ds_list_add(argument0, argument1);
+}
+return _pos;
diff --git a/PushEd.gmx/scripts/PEd_dsListInsertList.gml b/PushEd.gmx/scripts/PEd_dsListInsertList.gml
new file mode 100644
index 00000000..3589b4e1
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_dsListInsertList.gml
@@ -0,0 +1,10 @@
+/// PEd_dsListInsertList(l1, pos, l2)
+/**
+ * @brief Inserts the list l2 into the list t1 at the given position.
+ * @param {real} l1 The list to inserted into.
+ * @param {real} pos The index to insert the list at.
+ * @param {real} l2 The list to be inserted.
+ */
+gml_pragma("forceinline");
+ds_list_insert(argument0, argument1, argument2);
+ds_list_mark_as_list(argument0, argument1);
diff --git a/PushEd.gmx/scripts/PEd_dsListInsertMap.gml b/PushEd.gmx/scripts/PEd_dsListInsertMap.gml
new file mode 100644
index 00000000..61e623ae
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_dsListInsertMap.gml
@@ -0,0 +1,9 @@
+/// PEd_dsListInsertMap(list, pos, map)
+/**
+ * @brief Inserts the map into the list at the given position.
+ * @param {real} list The list to inserted into.
+ * @param {real} pos The index to insert the list at.
+ * @param {real} map The map to be inserted.
+ */
+ds_list_insert(argument0, argument1, argument2);
+ds_list_mark_as_map(argument0, argument1);
diff --git a/PushEd.gmx/scripts/PEd_dsListInsertUnique.gml b/PushEd.gmx/scripts/PEd_dsListInsertUnique.gml
new file mode 100644
index 00000000..c4aeda0d
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_dsListInsertUnique.gml
@@ -0,0 +1,14 @@
+/// PEd_dsListInsertUnique(list, value, position)
+/**
+ * @brief If the value is not in the list, it is inserted to it at given position.
+ * @param {real} list The id of the list.
+ * @param {any} value The value to be added.
+ * @param {real} position The index to insert the value at.
+ * @return {real} The index on which has been the value found or -1.
+ */
+var _pos = ds_list_find_index(argument0, argument1);
+if (_pos == -1)
+{
+ ds_list_insert(argument0, argument2, argument1);
+}
+return _pos;
diff --git a/PushEd.gmx/scripts/PEd_end3D.gml b/PushEd.gmx/scripts/PEd_end3D.gml
new file mode 100644
index 00000000..94e08e46
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_end3D.gml
@@ -0,0 +1,9 @@
+/// PEd_end3D()
+/**
+ * @brief Ends 3D mode.
+ */
+global.pedUsing3D = false;
+d3d_set_culling(false);
+d3d_end();
+view_xview[0] = viewLastX;
+view_yview[0] = viewLastY;
diff --git a/PushEd.gmx/scripts/PEd_getCurrentRoom.gml b/PushEd.gmx/scripts/PEd_getCurrentRoom.gml
new file mode 100644
index 00000000..ae0f50ff
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_getCurrentRoom.gml
@@ -0,0 +1,7 @@
+/// PEd_getCurrentRoom()
+/**
+ * @brief Gets the id of the currently selected room.
+ * @return {real} The id of the currently selected room.
+ */
+gml_pragma("forceinline");
+return PEd_oEditor.pedRoomList[| PEd_oEditor.pedRoomCurrent];
diff --git a/PushEd.gmx/scripts/PEd_getCustomData.gml b/PushEd.gmx/scripts/PEd_getCustomData.gml
new file mode 100644
index 00000000..17c78b92
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_getCustomData.gml
@@ -0,0 +1,24 @@
+/// PEd_getCustomData(instance)
+/**
+ * @brief Gets custom data of the instance.
+ * @param {real} instance The id of the instance.
+ */
+with (PEd_oEditor)
+{
+ if (!(object_is_ancestor(argument0.object_index, PEd_oObject2D)
+ || argument0.object_index == PEd_oObject2D)
+ && !(object_is_ancestor(argument0.object_index, PEd_oObject3D)
+ || argument0.object_index == PEd_oObject3D))
+ {
+ if (global.pedUsing3D)
+ {
+ event_perform_object(PEd_oObject3D, ev_other, ev_user15);
+ }
+ else
+ {
+ event_perform_object(PEd_oObject2D, ev_other, ev_user15);
+ }
+ }
+
+ event_perform_object(argument0.object_index, ev_other, ev_user15);
+}
diff --git a/PushEd.gmx/scripts/PEd_getFloat.gml b/PushEd.gmx/scripts/PEd_getFloat.gml
new file mode 100644
index 00000000..ef83a937
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_getFloat.gml
@@ -0,0 +1,9 @@
+/// PEd_getFloat(str, def)
+/**
+ * @brief Displays a popup message for the user to input a floating point number.
+ * @param {string} str The string to show in the popup message.
+ * @param {real} def The default number in the popup message.
+ * @return {real} The input number, or the default value if nothing has been entered.
+ */
+gml_pragma("forceinline");
+return real(get_string(argument0, string(argument1)));
diff --git a/PushEd.gmx/scripts/PEd_getMouseWorldPosition.gml b/PushEd.gmx/scripts/PEd_getMouseWorldPosition.gml
new file mode 100644
index 00000000..55b52309
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_getMouseWorldPosition.gml
@@ -0,0 +1,38 @@
+/// PEd_getMouseWorldPosition([room])
+/**
+ * @brief Gets the world position of the mouse cursor.
+ * @param {real} [room] The id of the room. If specified then
+ * the positon is snapped to it's grid (if
+ * snapping is enabled).
+ * @return {vec3} The world position of the mouse cursor.
+ */
+var _room = noone;
+if (argument_count > 0)
+{
+ _room = argument[0];
+}
+with (PEd_oEditor)
+{
+ var _space;
+ if (global.pedUsing3D)
+ {
+ _space = PEd_cameraScreenToPlaneXY(windowMouseX, windowMouseY, PEd_oPivot.z);
+ }
+ else
+ {
+ _space = PEd_vec3((windowMouseX - viewportX) * viewZoom + view_xview[0],
+ (windowMouseY - viewportY) * viewZoom + view_yview[0],
+ 0);
+ }
+
+ if (_room != noone
+ && PEd_roomGetGrid(_room))
+ {
+ _space[0] = round(_space[0] / PEd_roomGetSnapH(_room)) * PEd_roomGetSnapH(_room);
+ _space[1] = round(_space[1] / PEd_roomGetSnapV(_room)) * PEd_roomGetSnapV(_room);
+ _space[2] = round(_space[2] / PEd_roomGetSnapD(_room)) * PEd_roomGetSnapD(_room);
+ }
+
+ return _space;
+}
+show_error("PEd_oEditor does not exist!", true);
diff --git a/PushEd.gmx/scripts/PEd_getRoomCustomData.gml b/PushEd.gmx/scripts/PEd_getRoomCustomData.gml
new file mode 100644
index 00000000..4d97ebf5
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_getRoomCustomData.gml
@@ -0,0 +1,31 @@
+/// PEd_getRoomCustomData()
+/**
+ * @brief Gets custom data common for all rooms.
+ */
+with (PEd_oEditor)
+{
+ ds_list_clear(customData);
+
+ var _c;
+
+ _c = PEd_createCustomDataContainer("Room");
+ PEd_addCustomData(_c, PEdDataTypes.String, "Name", PEd_roomGetName, PEd_roomSetName);
+ PEd_addCustomData(_c, PEdDataTypes.String, "Caption", PEd_roomGetCaption, PEd_roomGetCaption);
+ PEd_addCustomData(_c, PEdDataTypes.String, "Creation Code", PEd_roomGetCode, PEd_roomSetCode);
+ PEd_addCustomData(_c, PEdDataTypes.Real, "Speed", PEd_roomGetSpeed, PEd_roomSetSpeed);
+ PEd_addCustomData(_c, PEdDataTypes.Bool, "Persistent", PEd_roomGetPersistent, PEd_roomSetPersistent);
+
+ PEd_addCustomData(_c, PEdDataTypes.Vector2, "Size", PEd_roomGetWidth, PEd_roomSetWidth);
+ PEd_addCustomData(_c, PEdDataTypes.Vector2, "", PEd_roomGetHeight, PEd_roomSetHeight);
+
+ PEd_addCustomData(_c, PEdDataTypes.Vector3, "Grid", PEd_roomGetSnapH, PEd_roomSetSnapH);
+ PEd_addCustomData(_c, PEdDataTypes.Vector3, "", PEd_roomGetSnapV, PEd_roomSetSnapV);
+ PEd_addCustomData(_c, PEdDataTypes.Vector3, "", PEd_roomGetSnapD, PEd_roomSetSnapD);
+ PEd_registerCustomDataContainer(_c);
+
+ _c = PEd_createCustomDataContainer("Background");
+ PEd_addCustomData(_c, PEdDataTypes.Colour, "Colour", PEd_roomGetColourARGB, PEd_roomSetColourARGB);
+ PEd_addCustomData(_c, PEdDataTypes.Bool, "Show Colour", PEd_roomGetShowColour, PEd_roomSetShowColour);
+ PEd_addCustomData(_c, PEdDataTypes.Background, "Image", PEd_roomGetBackground, noone);
+ PEd_registerCustomDataContainer(_c);
+}
diff --git a/PushEd.gmx/scripts/PEd_getSelectedObject.gml b/PushEd.gmx/scripts/PEd_getSelectedObject.gml
new file mode 100644
index 00000000..1685f8ad
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_getSelectedObject.gml
@@ -0,0 +1,10 @@
+/// PEd_getSelectedObject()
+/**
+ * @brief Gets the id of currently selected object.
+ * @return {real} The id of currently selected object or 0.
+ * If the id is less than 0, then a room is selected.
+ */
+var _selectedObj = PEd_oEditor.selectedObjects[| 0];
+if (is_undefined(_selectedObj))
+ _selectedObj = 0;
+return _selectedObj;
diff --git a/PushEd.gmx/scripts/PEd_getTileCustomData.gml b/PushEd.gmx/scripts/PEd_getTileCustomData.gml
new file mode 100644
index 00000000..3791a48f
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_getTileCustomData.gml
@@ -0,0 +1,26 @@
+/// PEd_getTileCustomData()
+/**
+ * @brief Gets custom data common for all tiles.
+ */
+with (PEd_oEditor)
+{
+ ds_list_clear(customData);
+
+ var _c;
+
+ _c = PEd_createCustomDataContainer("Tile");
+ PEd_addCustomData(_c, PEdDataTypes.Real, "Depth", PEd_tileGetDepth, PEd_tileSetDepth);
+ PEd_registerCustomDataContainer(_c);
+
+ _c = PEd_createCustomDataContainer("Transform");
+ PEd_addCustomData(_c, PEdDataTypes.Vector2, "Location", PEd_tileGetPosX, PEd_tileSetPosX);
+ PEd_addCustomData(_c, PEdDataTypes.Vector2, "", PEd_tileGetPosY, PEd_tileSetPosY);
+
+ PEd_addCustomData(_c, PEdDataTypes.Vector2, "Scale", PEd_tileGetScaleX, PEd_tileSetScaleX);
+ PEd_addCustomData(_c, PEdDataTypes.Vector2, "", PEd_tileGetScaleY, PEd_tileSetScaleY);
+ PEd_registerCustomDataContainer(_c);
+
+ _c = PEd_createCustomDataContainer("Blend");
+ PEd_addCustomData(_c, PEdDataTypes.Colour, "Colour", PEd_tileGetColourARGB, PEd_tileSetColourARGB);
+ PEd_registerCustomDataContainer(_c);
+}
diff --git a/PushEd.gmx/scripts/PEd_getVarFromStr.gml b/PushEd.gmx/scripts/PEd_getVarFromStr.gml
new file mode 100644
index 00000000..4cc3b60f
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_getVarFromStr.gml
@@ -0,0 +1,33 @@
+/// PEd_getVarFromStr(string, var)
+/**
+ * @brief Reads value of the variable from the tring.
+ * @param {string} string The string to read from.
+ * @param {string} var The name of the variable.
+ * @return {string} A string containing the variable value or an empty string.
+ */
+var _str = string(argument0) + "=";
+var _pos = string_pos(argument1, _str);
+
+if (_pos != 0)
+{
+ _pos += string_length(argument1);
+
+ var _char;
+ var _val = "";
+ var _len = string_length(_str);
+
+ for (_pos = _pos; _pos <= _len; _pos++)
+ {
+ _char = string_char_at(_str, _pos);
+
+ if (_char == ";")
+ {
+ break;
+ }
+
+ _val += _char;
+ }
+ return _val;
+}
+
+return "";
diff --git a/PushEd.gmx/scripts/PEd_guiAddItem.gml b/PushEd.gmx/scripts/PEd_guiAddItem.gml
new file mode 100644
index 00000000..e5300e75
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiAddItem.gml
@@ -0,0 +1,23 @@
+/// PEd_guiAddItem(compoundShape, item)
+/**
+ * @brief Adds item to the compound shape while preserving depth order.
+ * @param {real} compoundShape The id of the compound shape.
+ * @param {real} item The id of the item to be added.
+ * @return {real} The index where the item has been placed at.
+ */
+var _items = PEd_guiCompoundShapeGetItems(argument0);
+var _index = ds_list_size(_items);
+while (_index > 0)
+{
+ if (PEd_guiShapeGetDepth(_items[| _index - 1]) > PEd_guiShapeGetDepth(argument1))
+ {
+ _index--;
+ }
+ else
+ {
+ break;
+ }
+}
+PEd_dsListInsertMap(_items, _index, argument1);
+PEd_guiShapeSetDelegate(argument1, argument0);
+return _index;
diff --git a/PushEd.gmx/scripts/PEd_guiAddKeyboardShortcut.gml b/PushEd.gmx/scripts/PEd_guiAddKeyboardShortcut.gml
new file mode 100644
index 00000000..72899018
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiAddKeyboardShortcut.gml
@@ -0,0 +1,12 @@
+/// PEd_guiAddKeyboardShortcut(shape, keyboardShortcut)
+/**
+ * @brief Adds keyboard shortcut to the shape.
+ * @pram {real} shape The id of the shape to add the keyboard shortcut to.
+ * @param {real} keyboardShortcut The id of the keyboard shortcut.
+ */
+var _shape = argument0;
+if (is_undefined(_shape[? "keyboardShortcuts"]))
+{
+ ds_map_add_list(_shape, "keyboardShortcuts", ds_list_create());
+}
+PEd_dsListAddMap(_shape[? "keyboardShortcuts"], argument1);
diff --git a/PushEd.gmx/scripts/PEd_guiAlphaMixGetHeight.gml b/PushEd.gmx/scripts/PEd_guiAlphaMixGetHeight.gml
new file mode 100644
index 00000000..5be800e7
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiAlphaMixGetHeight.gml
@@ -0,0 +1,7 @@
+/// PEd_guiAlphaMixGetHeight()
+/**
+ * @brief Gets the height of an alpha mixer.
+ * @return {real} The height of an alpha mixer.
+ */
+gml_pragma("forceinline");
+return (guiLineHeight * 2 + 4);
diff --git a/PushEd.gmx/scripts/PEd_guiBeginFill.gml b/PushEd.gmx/scripts/PEd_guiBeginFill.gml
new file mode 100644
index 00000000..c6908f73
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiBeginFill.gml
@@ -0,0 +1,58 @@
+/// PEd_guiBeginFill(canvas)
+/**
+ * @brief Sets the canvas surface as the render target.
+ * @param {real} canvas The id of the canvas.
+ * @return {bool} True if the surface has been set as the render target.
+ */
+var _canvas = argument0;
+
+////////////////////////////////////////////////////////////////////////////////
+// Check surface
+var _surface = PEd_guiCanvasGetSurface(_canvas);
+var _width = max(PEd_guiShapeGetWidth(_canvas), 1);
+var _height = max(PEd_guiShapeGetHeight(_canvas), 1);
+
+if (surface_exists(_surface))
+{
+ if (surface_get_width(_surface) != _width
+ || surface_get_height(_surface) != _height)
+ {
+ surface_resize(_surface, _width, _height);
+ PEd_guiRequestRedraw(_canvas);
+ }
+}
+else
+{
+ _surface = surface_create(_width, _height);
+ PEd_guiRequestRedraw(_canvas);
+}
+PEd_guiCanvasSetSurface(_canvas, _surface);
+
+////////////////////////////////////////////////////////////////////////////////
+// Start filling
+if (PEd_guiShapeGetRedraw(_canvas)
+ && !guiShapeFilling)
+{
+ PEd_guiShapeSetRedraw(_canvas, false);
+ var _scrollX = 0;
+ var _scrollY = 0;
+ var _scrollbarHor = _canvas[? "scrollbarHor"]
+ var _scrollbarVer = _canvas[? "scrollbarVer"];
+ if (!is_undefined(_scrollbarHor))
+ {
+ _scrollX = PEd_guiScrollbarGetScroll(_scrollbarHor)
+ * PEd_guiScrollbarIsVisible(_scrollbarHor);
+ }
+ if (!is_undefined(_scrollbarVer))
+ {
+ _scrollY = PEd_guiScrollbarGetScroll(_scrollbarVer)
+ * PEd_guiScrollbarIsVisible(_scrollbarVer);
+ }
+ surface_set_target(_surface);
+ draw_clear(PEd_guiCanvasGetBackground(_canvas));
+ PEd_guiMatrixSet(-_scrollX, -_scrollY);
+ guiShapeFilling = _canvas;
+ guiShapeId = 0;
+ return true;
+}
+return false;
diff --git a/PushEd.gmx/scripts/PEd_guiCanShowContextMenu.gml b/PushEd.gmx/scripts/PEd_guiCanShowContextMenu.gml
new file mode 100644
index 00000000..30f9bd6d
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiCanShowContextMenu.gml
@@ -0,0 +1,8 @@
+/// PEd_guiCanShowContextMenu()
+/**
+ * @brief Gets whether the context menu can be opened.
+ * @return {real} True if the context menu can be opened.
+ */
+return (windowMouseX == guiMousePressX
+ && windowMouseY == guiMousePressY
+ && !keyboard_check(vk_alt));
diff --git a/PushEd.gmx/scripts/PEd_guiCanvasCleanUp.gml b/PushEd.gmx/scripts/PEd_guiCanvasCleanUp.gml
new file mode 100644
index 00000000..9d45a586
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiCanvasCleanUp.gml
@@ -0,0 +1,11 @@
+/// PEd_guiCanvasCleanUp(canvas)
+/**
+ * @brief Frees canvas resources from memory.
+ * @param {real} canvas The id of the canvas.
+ */
+var _surface = argument0[? "surface"];
+if (surface_exists(_surface))
+{
+ surface_free(_surface);
+}
+PEd_guiCompoundShapeCleanUp(argument0);
diff --git a/PushEd.gmx/scripts/PEd_guiCanvasDraw.gml b/PushEd.gmx/scripts/PEd_guiCanvasDraw.gml
new file mode 100644
index 00000000..967843b4
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiCanvasDraw.gml
@@ -0,0 +1,12 @@
+/// PEd_guiCanvasDraw(canvas)
+/**
+ * @brief Draws the canvas.
+ * @param {canvas} The id of the canvas.
+ */
+var _surface = PEd_guiCanvasGetSurface(argument0);
+if (surface_exists(_surface))
+{
+ draw_surface(_surface,
+ PEd_guiShapeGetX(argument0),
+ PEd_guiShapeGetY(argument0));
+}
diff --git a/PushEd.gmx/scripts/PEd_guiCanvasGetBackground.gml b/PushEd.gmx/scripts/PEd_guiCanvasGetBackground.gml
new file mode 100644
index 00000000..4a478ceb
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiCanvasGetBackground.gml
@@ -0,0 +1,8 @@
+/// PEd_guiCanvasGetBackground(canvas)
+/**
+ * @brief Gets the background colour of the canvas.
+ * @param {real} canvas The id of the canvas.
+ * @return {real} The background colour of the canvas.
+ */
+gml_pragma("forceinline");
+return argument0[? "background"];
diff --git a/PushEd.gmx/scripts/PEd_guiCanvasGetSurface.gml b/PushEd.gmx/scripts/PEd_guiCanvasGetSurface.gml
new file mode 100644
index 00000000..8944e9f1
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiCanvasGetSurface.gml
@@ -0,0 +1,8 @@
+/// PEd_guiCanvasGetSurface(canvas)
+/**
+ * @brief Gets the surface of the canvas.
+ * @param {real} canvas The id of the canvas.
+ * @return {real} The surface of the canvas.
+ */
+gml_pragma("forceinline");
+return argument0[? "surface"];
diff --git a/PushEd.gmx/scripts/PEd_guiCanvasSetBackground.gml b/PushEd.gmx/scripts/PEd_guiCanvasSetBackground.gml
new file mode 100644
index 00000000..5d12a15a
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiCanvasSetBackground.gml
@@ -0,0 +1,7 @@
+/// PEd_guiCanvasSetBackground(canvas, background)
+/**
+ * @brief Sets the background colour of the canvas.
+ * @param {real} canvas The id of the canvas.
+ * @param {real} background The new background colour.
+ */
+argument0[? "background"] = argument1;
diff --git a/PushEd.gmx/scripts/PEd_guiCanvasSetSurface.gml b/PushEd.gmx/scripts/PEd_guiCanvasSetSurface.gml
new file mode 100644
index 00000000..2551c3f8
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiCanvasSetSurface.gml
@@ -0,0 +1,7 @@
+/// PEd_guiCanvasSetSurface(canvas, surface)
+/**
+ * @brief Sets the surface of the canvas.
+ * @param {real} canvas The id of the canvas.
+ * @param {real} surface The id of the new surface.
+ */
+argument0[? "surface"] = argument1;
diff --git a/PushEd.gmx/scripts/PEd_guiCleanUp.gml b/PushEd.gmx/scripts/PEd_guiCleanUp.gml
new file mode 100644
index 00000000..3c26ff57
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiCleanUp.gml
@@ -0,0 +1,8 @@
+/// PEd_guiCleanUp()
+/**
+ * @brief Frees resources used by GUI from memory.
+ */
+PEd_guiShapeCleanUp(guiRoot);
+ds_stack_destroy(guiMatrixStack);
+ds_stack_destroy(guiDestroyStack);
+ds_list_destroy(guiKeyLog);
diff --git a/PushEd.gmx/scripts/PEd_guiColourMixGetHeight.gml b/PushEd.gmx/scripts/PEd_guiColourMixGetHeight.gml
new file mode 100644
index 00000000..a166b2d1
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiColourMixGetHeight.gml
@@ -0,0 +1,7 @@
+/// PEd_guiColourMixGetHeight()
+/**
+ * @brief Gets the height of a colour mixer.
+ * @return {real} The height of a colour mixer.
+ */
+gml_pragma("forceinline");
+return (guiLineHeight * 4 + 12);
diff --git a/PushEd.gmx/scripts/PEd_guiCompoundShapeCleanUp.gml b/PushEd.gmx/scripts/PEd_guiCompoundShapeCleanUp.gml
new file mode 100644
index 00000000..bf603d35
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiCompoundShapeCleanUp.gml
@@ -0,0 +1,16 @@
+/// PEd_guiCompoundShapeCleanUp(compoundShape)
+/**
+ * @brief Frees resources used by the compound shape from memory.
+ * @param {real} compoundShape The id of the compound shape.
+ */
+var _items = PEd_guiCompoundShapeGetItems(argument0);
+while (ds_list_size(_items) > 0)
+{
+ var _item = _items[| 0];
+ var _scrCleanUp = _item[? "scrCleanUp"];
+ if (_scrCleanUp != noone)
+ {
+ script_execute(_scrCleanUp, _item);
+ }
+}
+PEd_guiShapeCleanUp(argument0);
diff --git a/PushEd.gmx/scripts/PEd_guiCompoundShapeGetItems.gml b/PushEd.gmx/scripts/PEd_guiCompoundShapeGetItems.gml
new file mode 100644
index 00000000..dffb6eb1
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiCompoundShapeGetItems.gml
@@ -0,0 +1,9 @@
+/// PEd_guiCompoundShapeGetItems(compoundShape)
+/**
+ * @brief Gets the list of items of the compound shape.
+ * @param {real} compoundShape The id of the compound shape.
+ * @return {real/undefiend} The list of items of the compound shape or
+ * undefined, if the shape is not compound.
+ */
+gml_pragma("forceinline");
+return argument0[? "items"];
diff --git a/PushEd.gmx/scripts/PEd_guiCompoundShapeUpdate.gml b/PushEd.gmx/scripts/PEd_guiCompoundShapeUpdate.gml
new file mode 100644
index 00000000..37a1fce0
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiCompoundShapeUpdate.gml
@@ -0,0 +1,18 @@
+/// PEd_guiCompoundShapeUpdate(compoundShape)
+/**
+ * @brief Updates the compound shape.
+ * @param {real} compoundShape The id of the compound shape.
+ */
+PEd_guiShapeUpdate(argument0);
+
+////////////////////////////////////////////////////////////////////////////////
+// Update items
+var _items = PEd_guiCompoundShapeGetItems(argument0);
+for (var i = ds_list_size(_items) - 1; i >= 0; i--)
+{
+ var _item = _items[| i];
+ if (!PEd_guiShapeExists(_item))
+ {
+ ds_list_delete(_items, i);
+ }
+}
diff --git a/PushEd.gmx/scripts/PEd_guiContainerDraw.gml b/PushEd.gmx/scripts/PEd_guiContainerDraw.gml
new file mode 100644
index 00000000..dea2ace5
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiContainerDraw.gml
@@ -0,0 +1,22 @@
+/// PEd_guiContainerDraw(container)
+/**
+ * @brief Draws the container.
+ * @param {real} container The id of the container.
+ */
+// Draw items
+if (PEd_guiBeginFill(argument0))
+{
+ var _size = PEd_vec2(0.1, 0.1);
+ var _content = argument0[? "content"];
+ if (_content != noone)
+ {
+ _size = script_execute(_content, argument0);
+ }
+
+ PEd_guiContainerSetContentWidth(argument0, _size[0]);
+ PEd_guiContainerSetContentHeight(argument0, _size[1]);
+ PEd_guiEndFill(argument0);
+}
+
+// Draw container
+PEd_guiCanvasDraw(argument0);
diff --git a/PushEd.gmx/scripts/PEd_guiContainerGetContent.gml b/PushEd.gmx/scripts/PEd_guiContainerGetContent.gml
new file mode 100644
index 00000000..6dc12f9f
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiContainerGetContent.gml
@@ -0,0 +1,8 @@
+/// PEd_guiContainerGetContent(container, content)
+/**
+ * @brief Gets the content script of the container.
+ * @param {real} container The id of the container.
+ * @param {real} The content script of the container or noone.
+ */
+gml_pragma("forceinline");
+return argument0[? "content"];
diff --git a/PushEd.gmx/scripts/PEd_guiContainerGetContentHeight.gml b/PushEd.gmx/scripts/PEd_guiContainerGetContentHeight.gml
new file mode 100644
index 00000000..410325f9
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiContainerGetContentHeight.gml
@@ -0,0 +1,8 @@
+/// PEd_guiContainerGetContentHeight(container)
+/**
+ * @brief Gets the height of the container's content.
+ * @param {real} container The id of the container.
+ * @return {real} The height of the container's content.
+ */
+gml_pragma("forceinline");
+return ds_map_find_value(argument0[? "scrollbarVer"], "contentSize");
diff --git a/PushEd.gmx/scripts/PEd_guiContainerGetContentWidth.gml b/PushEd.gmx/scripts/PEd_guiContainerGetContentWidth.gml
new file mode 100644
index 00000000..bc9a7440
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiContainerGetContentWidth.gml
@@ -0,0 +1,8 @@
+/// PEd_guiContainerGetContentWidth(container)
+/**
+ * @brief Gets the width of the container's content.
+ * @param {real} container The id of the container.
+ * @return {real} The width of the container's content.
+ */
+gml_pragma("forceinline");
+return ds_map_find_value(argument0[? "scrollbarHor"], "contentSize");
diff --git a/PushEd.gmx/scripts/PEd_guiContainerSetContent.gml b/PushEd.gmx/scripts/PEd_guiContainerSetContent.gml
new file mode 100644
index 00000000..ff405ab3
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiContainerSetContent.gml
@@ -0,0 +1,7 @@
+/// PEd_guiContainerSetContent(container, content)
+/**
+ * @brief Sets the content script of the container.
+ * @param {real} container The id of the container.
+ * @param {real} content The new content script or noone.
+ */
+argument0[? "content"] = argument1;
diff --git a/PushEd.gmx/scripts/PEd_guiContainerSetContentHeight.gml b/PushEd.gmx/scripts/PEd_guiContainerSetContentHeight.gml
new file mode 100644
index 00000000..8c4154b1
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiContainerSetContentHeight.gml
@@ -0,0 +1,9 @@
+/// PEd_guiContainerSetContentHeight(container, contentHeight)
+/**
+ * @brief Sets height of the content of the given
+ * container to the given value.
+ * @param {real} container The id of the container.
+ * @param {real} contentHeight The new height of the container's content.
+ */
+gml_pragma("forceinline");
+ds_map_set(argument0[? "scrollbarVer"], "contentSize", max(1, argument1));
diff --git a/PushEd.gmx/scripts/PEd_guiContainerSetContentWidth.gml b/PushEd.gmx/scripts/PEd_guiContainerSetContentWidth.gml
new file mode 100644
index 00000000..ca774a32
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiContainerSetContentWidth.gml
@@ -0,0 +1,9 @@
+/// PEd_guiContainerSetContentWidth(container, contentWidth)
+/**
+ * @brief Sets width of the content of the given
+ * container to the given value.
+ * @param {real} container The id of the container.
+ * @param {real} contentWidth The new width of the container's content.
+ */
+gml_pragma("forceinline");
+ds_map_set(argument0[? "scrollbarHor"], "contentSize", max(1, argument1));
diff --git a/PushEd.gmx/scripts/PEd_guiContainerUpdate.gml b/PushEd.gmx/scripts/PEd_guiContainerUpdate.gml
new file mode 100644
index 00000000..a04bb201
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiContainerUpdate.gml
@@ -0,0 +1,39 @@
+/// PEd_guiContainerUpdate(container)
+/**
+ * @brief Updates the container.
+ * @param {real} container The id of the container.
+ */
+var _container = argument0;
+var _scrollbarHor = _container[? "scrollbarHor"];
+var _scrollbarVer = _container[? "scrollbarVer"];
+PEd_guiCompoundShapeUpdate(_container);
+
+// Click scroll
+if (mouse_check_button_pressed(mb_middle)
+ && PEd_guiShapeIsHovered(_container)
+ && !PEd_guiShapeExists(guiShapeActive))
+{
+ _container[? "clickScroll"] = true;
+ _container[? "clickScrollMouseX"] = windowMouseX;
+ _container[? "clickScrollMouseY"] = windowMouseY;
+ guiShapeActive = _container;
+}
+
+if (guiShapeActive == _container
+ && _container[? "clickScroll"])
+{
+ _scrollbarHor[? "scroll"] += (windowMouseX - _container[? "clickScrollMouseX"])
+ / _scrollbarHor[? "scrollJump"] * 0.1;
+ _scrollbarVer[? "scroll"] += (windowMouseY - _container[? "clickScrollMouseY"])
+ / _scrollbarVer[? "scrollJump"] * 0.1;
+
+ _scrollbarHor[? "scroll"] = clamp(_scrollbarHor[? "scroll"], 0, _scrollbarHor[? "size"] - _scrollbarHor[? "thumbSize"]);
+ _scrollbarVer[? "scroll"] = clamp(_scrollbarVer[? "scroll"], 0, _scrollbarVer[? "size"] - _scrollbarVer[? "thumbSize"]);
+
+ if (!mouse_check_button(mb_middle))
+ {
+ _container[? "clickScroll"] = false;
+ guiShapeActive = noone;
+ }
+ guiCursor = cr_drag;
+}
diff --git a/PushEd.gmx/scripts/PEd_guiContentColourPicker.gml b/PushEd.gmx/scripts/PEd_guiContentColourPicker.gml
new file mode 100644
index 00000000..7fd36f86
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiContentColourPicker.gml
@@ -0,0 +1,18 @@
+/// PEd_guiContentColourPicker(container)
+/**
+ * @brief Draws the content of a Colour Picker to the container.
+ * @param {real} container The id of the container.
+ * @return {vec2} The content size.
+ */
+var _container = argument0;
+var _containerWidth = PEd_guiShapeGetWidth(_container);
+var _contentX = 8;
+var _contentY = 4;
+
+_container[? "colour"] = PEd_guiDrawColourMix(_contentX, _contentY, _container[? "colour"]);
+_contentY += PEd_guiColourMixGetHeight() + 4;
+
+_container[? "alpha"] = PEd_guiDrawAlphaMix(_contentX, _contentY, _container[? "alpha"]);
+_contentY += PEd_guiAlphaMixGetHeight() + 4;
+
+return PEd_vec2(_containerWidth, _contentY);
diff --git a/PushEd.gmx/scripts/PEd_guiContentContentBrowser.gml b/PushEd.gmx/scripts/PEd_guiContentContentBrowser.gml
new file mode 100644
index 00000000..6f379224
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiContentContentBrowser.gml
@@ -0,0 +1,101 @@
+/// PEd_guiContentContentBrowser(container)
+/**
+ * @brief Draws the content of a Content Browser to the container.
+ * @param {real} container The id of the container.
+ * @return {vec2} The content size.
+ */
+var _container = argument0;
+var _containerWidth = PEd_guiShapeGetWidth(_container);
+var _contentX = round(guiLineHeight * 0.75);
+var _contentY = 0;
+var _room = PEd_getCurrentRoom();
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Objects
+//
+
+// Draw section
+if (PEd_guiDrawSection("Objects", _contentX, _contentY, contentBrowserShowObjects))
+{
+ contentBrowserShowObjects = !contentBrowserShowObjects;
+}
+_contentY += guiLineHeight;
+
+// Object list
+if (contentBrowserShowObjects)
+{
+ for (var i = 0; i < PEd_objectListGetSize(); i++)
+ {
+ var _name = PEd_objectListGetItem(i);
+ if (_name != ""
+ && (contentBrowserFilter == ""
+ || string_pos(string_lower(contentBrowserFilter), string_lower(_name)) != 0))
+ {
+ if (PEd_guiDrawListItem(_name, _contentX, _contentY, contentBrowserIndexObject == i, editMode != 0))
+ {
+ // Create object
+ if (contentBrowserIndexObject == i)
+ {
+ var _id = PEd_instanceCreate(_room, PEd_oPivot.x, PEd_oPivot.y, i);
+ PEd_instanceSetPosX(_id, PEd_oPivot.x);
+ PEd_instanceSetPosY(_id, PEd_oPivot.y);
+ PEd_instanceSetPosZ(_id, PEd_oPivot.z);
+ PEd_selectObject(_id, true);
+ contentBrowserIndexObject = -1; // Select none (to avoid accidental instance creation)
+ }
+ else
+ {
+ contentBrowserIndexObject = i;
+ guiDnDObject = asset_get_index(_name);
+ }
+ }
+ _contentY += guiLineHeight;
+ }
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Backgrounds
+//
+
+// Draw section
+if (PEd_guiDrawSection("Backgrounds", _contentX, _contentY, contentBrowserShowBackgrounds))
+{
+ contentBrowserShowBackgrounds = !contentBrowserShowBackgrounds;
+}
+_contentY += guiLineHeight;
+
+// Backgound list
+if (contentBrowserShowBackgrounds)
+{
+ bkgShow = -1;
+ var i = 0;
+ do
+ {
+ var _name = background_get_name(i);
+ if (_name != ""
+ && (contentBrowserFilter == ""
+ || string_pos(string_lower(contentBrowserFilter), string_lower(_name)) != 0))
+ {
+ var _item = PEd_guiDrawListItem(_name, _contentX, _contentY, contentBrowserIndexBackground == i, false)
+ if (_item > 0)
+ {
+ contentBrowserIndexBackground = i;
+ guiDnDBackground = i;
+ PEd_guiRequestRedrawAll(guiRoot)
+ }
+ else if (_item < 0)
+ {
+ bkgShow = i;
+ }
+ _contentY += guiLineHeight;
+ }
+ background_get_name(i);
+ i++;
+ }
+ until (background_get_name(i) == "");
+}
+
+return PEd_vec2(_containerWidth, _contentY);
diff --git a/PushEd.gmx/scripts/PEd_guiContentDetails.gml b/PushEd.gmx/scripts/PEd_guiContentDetails.gml
new file mode 100644
index 00000000..2dd3ecea
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiContentDetails.gml
@@ -0,0 +1,646 @@
+/// PEd_guiContentDetails(container)
+/**
+ * @brief Draws the content of a Details to the container.
+ * @param {real} container The id of the container.
+ * @return {vec2} The content size.
+ */
+var _container = argument0;
+var _containerWidth = PEd_guiShapeGetWidth(_container);
+var _containerHeight = PEd_guiShapeGetHeight(_container);
+
+var _contentX = round(guiLineHeight * 0.75);
+var _contentY = 0;
+var __contentY = _contentY;
+var __contentX = _contentX;
+
+////////////////////////////////////////////////////////////////////////////////
+// Left column
+if (is_undefined(_container[? "columnLeftWidth"]))
+{
+ _container[? "columnLeftWidth"] = 100;
+ _container[? "columnLeftResize"] = false;
+}
+
+var _columnLeftWidth = _container[? "columnLeftWidth"];
+var _columnLeftResize = _container[? "columnLeftResize"];
+var _columnLeftX = _contentX + _columnLeftWidth;
+var _mouseOverColumnLeftResizeArea = (PEd_guiShapeIsHovered(_container)
+ && guiMouseX > _columnLeftX - 4
+ && guiMouseX < _columnLeftX + 4);
+
+var _scrollY = 0;
+var _scrollbarVer = _container[? "scrollbarVer"];
+if (!is_undefined(_scrollbarVer))
+{
+ _scrollY = PEd_guiScrollbarGetScroll(_scrollbarVer) * PEd_guiScrollbarIsVisible(_scrollbarVer);
+}
+
+if (_mouseOverColumnLeftResizeArea)
+{
+ guiCursor = cr_size_we;
+ if (mouse_check_button_pressed(mb_left))
+ {
+ // Start resizing
+ guiShapeActive = _container;
+ _container[? "columnLeftResize"] = true;
+ }
+}
+
+if (guiShapeActive == _container
+ && _container[? "columnLeftResize"])
+{
+ // Set new size
+ guiCursor = cr_size_we;
+ _columnLeftWidth = guiMouseX - _contentX;
+ _container[? "columnLeftWidth"] = _columnLeftWidth;
+ if (mouse_check_button_released(mb_left))
+ {
+ // End resizing
+ guiShapeActive = noone;
+ _container[? "columnLeftResize"] = false;
+ }
+}
+
+// Clamp size
+_columnLeftWidth = clamp(_columnLeftWidth, 32, _containerWidth - _contentX * 2 - 32);
+_container[? "columnLeftWidth"] = _columnLeftWidth;
+
+var _inputW = _containerWidth - _contentX - _columnLeftWidth - 4 - 8;
+var _singleInputWidth = _inputW;
+var _textOffsetY = round((guiLineHeight - guiFontHeight) * 0.5);
+
+var _selectedObj = PEd_getSelectedObject();
+if (_selectedObj > 0)
+{
+ PEd_guiDrawRectangle(0, _scrollY, _columnLeftX, _containerHeight, PEdColour.WindowBackground2);
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Draw custom data
+ var _textOffsetY = floor((guiLineHeight - guiFontHeight) * 0.5);
+
+ for (var i = 0; i < ds_list_size(customData); i++)
+ {
+ // Get custom data container
+ var _container = customData[| i];
+ var _name = _container[| 0];
+ var _expanded = _container[| 1];
+
+ if (PEd_guiDrawSection(_name, _contentX, _contentY, _expanded))
+ {
+ _expanded = !_expanded;
+ _container[| 1] = _expanded; // Save state
+ }
+ _contentY += guiLineHeight;
+
+ if (_expanded)
+ {
+ _contentY += 4;
+
+ var _drawOnSingleLine = 0;
+
+ for (var j = 2; j < ds_list_size(_container); j += 4)
+ {
+ // Get metadata
+ var _dataType = _container[| j];
+ var _dataName = _container[| j + 1];
+ var _dataGetter = _container[| j + 2];
+ var _dataSetter = noone;
+ var _dataValue = "";
+
+ if (_drawOnSingleLine == 0)
+ {
+ if (_dataType == PEdDataTypes.Vector2)
+ {
+ __contentY = _contentY;
+ _drawOnSingleLine = 2;
+ _inputW = (_singleInputWidth - 4 * (_drawOnSingleLine - 1)) / _drawOnSingleLine;
+ }
+ else if (_dataType == PEdDataTypes.Vector3)
+ {
+ __contentY = _contentY;
+ _drawOnSingleLine = 3;
+ _inputW = (_singleInputWidth - 4 * (_drawOnSingleLine - 1)) / _drawOnSingleLine;
+ }
+ }
+
+ if (_dataGetter != noone)
+ {
+ _dataValue = script_execute(_dataGetter, _selectedObj);
+
+ // If the data does not have a getter that we don't need
+ // to get the setter.
+ _dataSetter = _container[| j + 3];
+ }
+
+ // Draw data name
+ PEd_guiDrawTextPart(_contentX, _contentY + _textOffsetY, _dataName, _columnLeftWidth);
+
+ // Draw input and get new value
+ var _inputX = _contentX + _columnLeftWidth + 4;
+ var _input = undefined;
+ var _inputH = guiLineHeight;
+
+ switch (_dataType)
+ {
+ case PEdDataTypes.Colour:
+ var _colourInputColour = PEd_argbToColour(_dataValue);
+ var _colourInputAlpha = PEd_argbToAlpha(_dataValue);
+ var _colourPicker = PEd_guiDrawColourInput(_inputX, _contentY, _inputW, _colourInputColour, _colourInputAlpha);
+ if (_colourPicker != noone)
+ {
+ if (!PEd_guiShapeExists(guiColourPickerObject))
+ {
+ guiColourPickerObject = _colourPicker;
+ var _titleBar = _colourPicker[? "titleBar"];
+ _titleBar[? "title"] += " - Instance/Tile";
+ }
+ else
+ {
+ PEd_guiDestroyShape(_colourPicker);
+ }
+ }
+ break;
+
+ case PEdDataTypes.Bool:
+ _input = PEd_guiDrawCheckbox(_inputX, _contentY, _dataValue);
+ break;
+
+ case PEdDataTypes.Background:
+ _input = PEd_guiDrawBackgroundInput(_inputX, _contentY, _inputW, _dataValue);
+ break;
+
+ case PEdDataTypes.Object:
+ _input = PEd_guiDrawObjectInput(_inputX, _contentY, _inputW, _dataValue);
+ break;
+
+ default:
+ _input = PEd_guiDrawInput(_inputX, _contentY, _inputW, _dataValue, _dataSetter == noone);
+ break;
+ }
+
+ // Set new value
+ if (!is_undefined(_input)
+ && _dataSetter != noone)
+ {
+ script_execute(_dataSetter, _selectedObj, _input);
+ }
+
+ if (_drawOnSingleLine > 1)
+ {
+ _contentY = __contentY;
+ }
+
+ if (_drawOnSingleLine > 0)
+ {
+ _drawOnSingleLine--;
+ _contentX += _inputW + 4;
+ }
+
+ if (_drawOnSingleLine == 0)
+ {
+ _contentY += _inputH + 4;
+ _contentX = __contentX;
+ _inputW = _singleInputWidth;
+ }
+ }
+ }
+ }
+} // _selectedObj > 0
+else if (_selectedObj < 0)
+{
+ PEd_guiDrawRectangle(0, _scrollY, _columnLeftX, _containerHeight, PEdColour.WindowBackground2);
+
+ var _room = PEd_getCurrentRoom();
+ var _inputX = _contentX + _columnLeftWidth + 4;
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Room
+ //
+
+ // Draw section
+ if (PEd_guiDrawSection("Room", _contentX, _contentY, detailsRoomShow))
+ {
+ detailsRoomShow = !detailsRoomShow;
+ }
+ _contentY += guiLineHeight;
+
+ // Draw room section
+ if (detailsRoomShow)
+ {
+ _contentY += 4;
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Room properties
+ //
+
+ // Room name
+ var _name = PEd_roomGetName(_room);
+ PEd_guiDrawTextPart(_contentX, _contentY + _textOffsetY, "Name", _columnLeftWidth);
+ var _input = PEd_guiDrawInput(_inputX, _contentY, _singleInputWidth, _name, false);
+ if (!is_undefined(_input))
+ {
+ PEd_roomSetName(_room, _input);
+ if (_input != _name)
+ {
+ PEd_guiRequestRedrawAll(guiRoot)
+ pedFileSave = "";
+ }
+ }
+ _contentY += guiLineHeight + 4;
+
+ // Room caption
+ PEd_guiDrawTextPart(_contentX, _contentY + _textOffsetY, "Caption", _columnLeftWidth);
+ var _input = PEd_guiDrawInput(_inputX, _contentY, _inputW, PEd_roomGetCaption(_room));
+ if (!is_undefined(_input))
+ {
+ PEd_roomSetCaption(_room, _input);
+ }
+ _contentY += guiLineHeight + 4;
+
+ // Room creation code
+ PEd_guiDrawTextPart(_contentX, _contentY + _textOffsetY, "Creation code", _columnLeftWidth);
+ var _input = PEd_guiDrawInput(_inputX, _contentY, _inputW, PEd_roomGetCode(_room));
+ if (!is_undefined(_input))
+ {
+ PEd_roomSetCode(_room, _input);
+ }
+ _contentY += guiLineHeight + 4;
+
+ // Room speed
+ PEd_guiDrawTextPart(_contentX, _contentY + _textOffsetY, "Speed", _columnLeftWidth);
+ var _input = PEd_guiDrawInput(_inputX, _contentY, _inputW, PEd_roomGetSpeed(_room));
+ if (!is_undefined(_input))
+ {
+ PEd_roomSetSpeed(_room, floor(_input));
+ }
+ _contentY += guiLineHeight + 4;
+
+ // Persistent
+ PEd_guiDrawTextPart(_contentX, _contentY + _textOffsetY, "Persistent", _columnLeftWidth);
+ PEd_roomSetPersistent(_room,
+ PEd_guiDrawCheckbox(_inputX, _contentY, PEd_roomGetPersistent(_room)));
+ _contentY += guiLineHeight + 4;
+
+ // Room dimensions
+ PEd_guiDrawTextPart(_contentX, _contentY + _textOffsetY, "Size", _columnLeftWidth);
+ _inputW = floor((_singleInputWidth - 4) * 0.5);
+ var _input = PEd_guiDrawInput(_inputX, _contentY, _inputW, PEd_roomGetWidth(_room));
+ if (!is_undefined(_input))
+ {
+ PEd_roomSetWidth(_room, max(1, _input));
+ }
+ var _input = PEd_guiDrawInput(_inputX + _inputW + 4, _contentY, _inputW, PEd_roomGetHeight(_room));
+ if (!is_undefined(_input))
+ {
+ PEd_roomSetHeight(_room, max(1, _input));
+ }
+ _contentY += guiLineHeight + 4;
+
+ // Grid size
+ PEd_guiDrawTextPart(_contentX, _contentY + _textOffsetY, "Grid size", _columnLeftWidth);
+ _inputW = floor((_singleInputWidth - 8) / 3);
+ var _input = PEd_guiDrawInput(_inputX, _contentY, _inputW, PEd_roomGetSnapH(_room));
+ if (!is_undefined(_input))
+ {
+ PEd_roomSetSnapH(_room, max(1, _input));
+ }
+ var _input = PEd_guiDrawInput(_inputX + _inputW + 4, _contentY, _inputW, PEd_roomGetSnapV(_room));
+ if (!is_undefined(_input))
+ {
+ PEd_roomSetSnapV(_room, max(1, _input));
+ }
+ var _input = PEd_guiDrawInput(_inputX + _inputW * 2 + 8, _contentY, _inputW, PEd_roomGetSnapD(_room));
+ if (!is_undefined(_input))
+ {
+ PEd_roomSetSnapD(_room, max(1, _input));
+ }
+ _contentY += guiLineHeight + 4;
+
+ // Colour
+ PEd_guiDrawTextPart(_contentX, _contentY + _textOffsetY, "Colour", _columnLeftWidth);
+ var _colourPicker = PEd_guiDrawColourInput(_inputX, _contentY, _singleInputWidth, PEd_roomGetColour(_room), 1);
+ if (PEd_guiShapeExists(_colourPicker))
+ {
+ if (!PEd_guiShapeExists(guiColourPickerRoom))
+ {
+ guiColourPickerRoom = _colourPicker;
+ var _titleBar = _colourPicker[? "titleBar"];
+ _titleBar[? "title"] += " - Room";
+ }
+ else
+ {
+ PEd_guiDestroyShape(_colourPicker);
+ }
+ }
+ _contentY += guiLineHeight + 4;
+
+ // Show colour
+ PEd_guiDrawTextPart(_contentX, _contentY + _textOffsetY, "Show colour", _columnLeftWidth);
+ PEd_roomSetShowColour(_room,
+ PEd_guiDrawCheckbox(_inputX, _contentY, PEd_roomGetShowColour(_room)));
+ _contentY += guiLineHeight + 4;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Background
+ //
+
+ // Draw section
+ if (PEd_guiDrawSection("Backgrounds", _contentX, _contentY, detailsRoomBackgroundShow))
+ {
+ detailsRoomBackgroundShow = !detailsRoomBackgroundShow;
+ }
+ _contentY += guiLineHeight;
+
+ if (detailsRoomBackgroundShow)
+ {
+ _contentY += 4;
+
+ for (var i = 0; i < 8; i++)
+ {
+ if (PEd_guiDrawListItem("Background" + string(i), _contentX, _contentY, detailsEditBackground == i, 0))
+ {
+ detailsEditBackground = i;
+ }
+ _contentY += guiLineHeight;
+ }
+ _contentY += 4;
+
+ var _background = PEd_roomGetBackground(_room, detailsEditBackground);
+
+ // Visible when room starts
+ PEd_guiDrawTextPart(_contentX, _contentY + _textOffsetY, "Visible", _columnLeftWidth);
+ PEd_backgroundSetVisible(_background,
+ PEd_guiDrawCheckbox(_inputX, _contentY, PEd_backgroundGetVisible(_background)));
+ _contentY += guiLineHeight + 4;
+
+ // Is foreground
+ PEd_guiDrawTextPart(_contentX, _contentY + _textOffsetY, "Is foreground", _columnLeftWidth);
+ PEd_backgroundSetAsForeground(_background,
+ PEd_guiDrawCheckbox(_inputX, _contentY, PEd_backgroundIsForeground(_background)));
+ _contentY += guiLineHeight + 4;
+
+ // Background image
+ PEd_guiDrawTextPart(_contentX, _contentY + _textOffsetY, "Image", _singleInputWidth);
+ PEd_backgroundSetImage(_background,
+ PEd_guiDrawBackgroundInput(_inputX, _contentY, _singleInputWidth, PEd_backgroundGetImage(_background)));
+ _contentY += guiLineHeight + 4;
+
+ // Background position
+ PEd_guiDrawTextPart(_contentX, _contentY + _textOffsetY, "Position", _columnLeftWidth);
+ _inputW = floor((_singleInputWidth - 4) * 0.5);
+
+ var _input = PEd_guiDrawInput(_inputX, _contentY, _inputW, PEd_backgroundGetX(_background))
+ if (!is_undefined(_input))
+ {
+ PEd_backgroundSetX(_background, floor(_input));
+ }
+ var _input = PEd_guiDrawInput(_inputX + _inputW + 4, _contentY, _inputW, PEd_backgroundGetY(_background))
+ if (!is_undefined(_input))
+ {
+ PEd_backgroundSetY(_background, floor(_input));
+ }
+ _contentY += guiLineHeight + 4;
+
+ // Tile
+ PEd_guiDrawTextPart(_contentX, _contentY + _textOffsetY, "Tile horizontally", _columnLeftWidth);
+ var _bool = PEd_backgroundGetTiledHor(_background);
+ var _checkbox = PEd_guiDrawCheckbox(_inputX, _contentY, _bool);
+ if (_checkbox != _bool)
+ {
+ PEd_backgroundSetTiledHor(_background, _checkbox);
+ if (!_checkbox)
+ {
+ PEd_backgroundSetStretch(_background, false);
+ }
+ }
+ _contentY += guiLineHeight + 4;
+
+ PEd_guiDrawTextPart(_contentX, _contentY + _textOffsetY, "Tile vertically", _columnLeftWidth);
+ var _bool = PEd_backgroundGetTiledVer(_background);
+ var _checkbox = PEd_guiDrawCheckbox(_inputX, _contentY, _bool);
+ if (_checkbox != _bool)
+ {
+ PEd_backgroundSetTiledVer(_background, _checkbox);
+ if (!_checkbox)
+ {
+ PEd_backgroundSetStretch(_background, false);
+ }
+ }
+ _contentY += guiLineHeight + 4;
+
+ // Stretch
+ PEd_guiDrawTextPart(_contentX, _contentY + _textOffsetY, "Stretch", _columnLeftWidth);
+ var _bool = PEd_backgroundGetStretch(_background);
+ var _checkbox = PEd_guiDrawCheckbox(_inputX, _contentY, _bool);
+ if (_checkbox != _bool)
+ {
+ PEd_backgroundSetStretch(_background, _checkbox);
+ if (!_checkbox)
+ {
+ PEd_backgroundSetTiledHor(_background, false);
+ PEd_backgroundSetTiledVer(_background, false);
+ }
+ }
+ _contentY += guiLineHeight + 4;
+
+ // Speed
+ _inputW = floor((_singleInputWidth - 4) * 0.5);
+ PEd_guiDrawTextPart(_contentX, _contentY + _textOffsetY, "Speed", _columnLeftWidth);
+ var _input = PEd_guiDrawInput(_inputX, _contentY, _inputW, PEd_backgroundGetSpeedHor(_background));
+ if (!is_undefined(_input))
+ {
+ PEd_backgroundSetSpeedHor(_background, floor(_input));
+ }
+ var _input = PEd_guiDrawInput(_inputX + _inputW + 8, _contentY, _inputW, PEd_backgroundGetSpeedVer(_background));
+ if (!is_undefined(_input))
+ {
+ PEd_backgroundSetSpeedVer(_background, floor(_input));
+ }
+ _contentY += guiLineHeight + 4;
+
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Views
+ //
+
+ // Draw section
+ if (PEd_guiDrawSection("Views", _contentX, _contentY, detailsViewShow))
+ {
+ detailsViewShow = !detailsViewShow;
+ }
+ _contentY += guiLineHeight;
+
+ // View list
+ if (detailsViewShow)
+ {
+ _contentY += 4;
+ for (var i = 0; i < 8; i++)
+ {
+ if (PEd_guiDrawListItem("View" + string(i), _contentX, _contentY, detailsEditView == i, 0))
+ {
+ detailsEditView = i;
+ }
+ _contentY += guiLineHeight;
+ }
+ _contentY += 4;
+
+ var _viewport = PEd_roomGetViewport(_room, detailsEditView);
+
+ // Settings
+ _inputW = floor((_singleInputWidth - 4) * 0.5);
+
+ // View
+ PEd_guiDrawTextPart(_contentX, _contentY + _textOffsetY, "View position", _columnLeftWidth);
+ var _input = PEd_guiDrawInput(_inputX, _contentY, _inputW, PEd_viewportGetX(_viewport));
+ if (!is_undefined(_input))
+ {
+ PEd_viewportSetX(_viewport, floor(_input));
+ }
+ var _input = PEd_guiDrawInput(_inputX + _inputW + 4, _contentY, _inputW, PEd_viewportGetY(_viewport));
+ if (!is_undefined(_input))
+ {
+ PEd_viewportSetY(_viewport, floor(_input));
+ }
+ _contentY += guiLineHeight + 4;
+
+ PEd_guiDrawTextPart(_contentX, _contentY + _textOffsetY, "View size", _columnLeftWidth);
+ var _input = PEd_guiDrawInput(_inputX, _contentY, _inputW, PEd_viewportGetWidth(_viewport));
+ if (!is_undefined(_input))
+ {
+ PEd_viewportSetWidth(_viewport, floor(_input));
+ }
+ var _input = PEd_guiDrawInput(_inputX + _inputW + 4, _contentY, _inputW, PEd_viewportGetHeight(_viewport));
+ if (!is_undefined(_input))
+ {
+ PEd_viewportSetHeight(_viewport, floor(_input));
+ }
+ _contentY += guiLineHeight + 4;
+
+ // Port on screen
+ PEd_guiDrawTextPart(_contentX, _contentY + _textOffsetY, "Port position", _columnLeftWidth);
+ var _input = PEd_guiDrawInput(_inputX, _contentY, _inputW, PEd_viewportGetPortX(_viewport));
+ if (!is_undefined(_input))
+ {
+ PEd_viewportSetPortX(_viewport, floor(_input));
+ }
+ var _input = PEd_guiDrawInput(_inputX + _inputW + 4, _contentY, _inputW, PEd_viewportGetPortY(_viewport));
+ if (!is_undefined(_input))
+ {
+ PEd_viewportSetPortY(_viewport, floor(_input));
+ }
+ _contentY += guiLineHeight + 4;
+
+ PEd_guiDrawTextPart(_contentX, _contentY + _textOffsetY, "Port size", _columnLeftWidth);
+ var _input = PEd_guiDrawInput(_inputX, _contentY, _inputW, PEd_viewportGetPortWidth(_viewport));
+ if (!is_undefined(_input))
+ {
+ PEd_viewportSetPortWidth(_viewport, floor(_input));
+ }
+ var _input = PEd_guiDrawInput(_inputX + _inputW + 4, _contentY, _inputW, PEd_viewportGetPortHeight(_viewport));
+ if (!is_undefined(_input))
+ {
+ PEd_viewportSetPortHeight(_viewport, floor(_input));
+ }
+ _contentY += guiLineHeight + 4;
+
+ // Visible
+ PEd_guiDrawTextPart(_contentX, _contentY + _textOffsetY, "Visible", _columnLeftWidth);
+ PEd_viewportSetVisible(_viewport,
+ PEd_guiDrawCheckbox(_inputX, _contentY, PEd_viewportGetVisible(_viewport)));
+ _contentY += guiLineHeight + 4;
+
+ // Object following
+ PEd_guiDrawTextPart(_contentX, _contentY + _textOffsetY, "Object following", _columnLeftWidth);
+ PEd_viewportSetObject(_viewport,
+ PEd_guiDrawObjectInput(_inputX, _contentY, _singleInputWidth, PEd_viewportGetObject(_viewport)));
+ _contentY += guiLineHeight + 4;
+
+ // Border
+ _inputW = floor((_singleInputWidth - 4) * 0.5);
+
+ PEd_guiDrawTextPart(_contentX, _contentY + _textOffsetY, "Border", _columnLeftWidth);
+ var _input = PEd_guiDrawInput(_inputX, _contentY, _inputW, PEd_viewportGetBorderHor(_viewport));
+ if (!is_undefined(_input))
+ {
+ PEd_viewportSetBorderHor(_viewport, floor(_input));
+ }
+ var _input = PEd_guiDrawInput(_inputX + _inputW + 4, _contentY, _inputW, PEd_viewportGetBorderVer(_viewport));
+ if (!is_undefined(_input))
+ {
+ PEd_viewportSetBorderVer(_viewport, floor(_input));
+ }
+ _contentY += guiLineHeight + 4;
+
+ // Speed
+ PEd_guiDrawTextPart(_contentX, _contentY + _textOffsetY, "Speed", _columnLeftWidth);
+ var _input = PEd_guiDrawInput(_inputX, _contentY, _inputW, PEd_viewportGetSpeedHor(_viewport));
+ if (!is_undefined(_input))
+ {
+ PEd_viewportSetSpeedHor(_viewport, floor(_input));
+ }
+ var _input = PEd_guiDrawInput(_inputX + _inputW + 4, _contentY, _inputW, PEd_viewportGetSpeedVer(_viewport));
+ if (!is_undefined(_input))
+ {
+ PEd_viewportSetSpeedVer(_viewport, floor(_input));
+ }
+ _contentY += guiLineHeight + 4;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Physics
+ //
+
+ // Draw section
+ if (PEd_guiDrawSection("Physics", _contentX, _contentY, detailsPhysicsShow))
+ {
+ detailsPhysicsShow = !detailsPhysicsShow;
+ }
+ _contentY += guiLineHeight;
+
+ if (detailsPhysicsShow)
+ {
+ _contentY += 4;
+ _inputW = floor((_containerWidth - _contentX * 2 - 4) * 0.5);
+
+ var _physicsWorld = PEd_roomGetPhysicsWorld(_room);
+
+ // Use physics
+ PEd_guiDrawTextPart(_contentX, _contentY + _textOffsetY, "Use physics", _columnLeftWidth);
+ PEd_physicsWorldSetEnabled(_physicsWorld,
+ PEd_guiDrawCheckbox(_inputX, _contentY, PEd_physicsWorldGetEnabled(_physicsWorld)));
+ _contentY += guiLineHeight + 4;
+
+ // Gravity
+ PEd_guiDrawTextPart(_contentX, _contentY + _textOffsetY, "Gravity", _columnLeftWidth);
+ _inputW = floor((_singleInputWidth - 4) * 0.5);
+ var _input = PEd_guiDrawInput(_inputX, _contentY, _inputW, PEd_physicsWorldGetGravityX(_physicsWorld));
+ if (!is_undefined(_input))
+ {
+ PEd_physicsWorldSetGravityX(_physicsWorld, _input);
+ }
+ var _input = PEd_guiDrawInput(_inputX + _inputW + 4, _contentY, _inputW, PEd_physicsWorldGetGravityY(_physicsWorld));
+ if (!is_undefined(_input))
+ {
+ PEd_physicsWorldSetGravityY(_physicsWorld, _input);
+ }
+ _contentY += guiLineHeight + 4;
+
+ // Px to M
+ PEd_guiDrawTextPart(_contentX, _contentY + _textOffsetY, "Pixels to meter", _columnLeftWidth);
+ var _input = PEd_guiDrawInput(_inputX, _contentY, _singleInputWidth, PEd_physicsWorldGetPxToM(_physicsWorld));
+ if (!is_undefined(_input))
+ {
+ PEd_physicsWorldSetPxToM(_physicsWorld, _input);
+ }
+ _contentY += guiLineHeight + 4;
+ }
+} // _selectedObj < 0
+
+return PEd_vec2(_containerWidth, _contentY);
diff --git a/PushEd.gmx/scripts/PEd_guiContentInfo.gml b/PushEd.gmx/scripts/PEd_guiContentInfo.gml
new file mode 100644
index 00000000..06d57a2c
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiContentInfo.gml
@@ -0,0 +1,10 @@
+/// PEd_guiContentInfo(container)
+/**
+ * @brief Draws the content of an Info to the container.
+ * @param {real} container The id of the container.
+ * @return {vec2} The content size.
+ */
+var _container = argument0;
+var _text = "PushEd 1.5.3"; // TODO: Update on new release!
+draw_text(8, 8, _text);
+return PEd_vec2(string_width(_text) + 16, string_height(_text) + 16);
diff --git a/PushEd.gmx/scripts/PEd_guiContentSceneOutline.gml b/PushEd.gmx/scripts/PEd_guiContentSceneOutline.gml
new file mode 100644
index 00000000..f177920b
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiContentSceneOutline.gml
@@ -0,0 +1,266 @@
+/// PEd_guiContentSceneOutline(container)
+/**
+ * @brief Draws the content of a Scene Outline to the container.
+ * @param {real} container The id of the container.
+ * @return {vec2} The content size.
+ */
+var _container = argument0;
+var _containerWidth = PEd_guiShapeGetWidth(_container);
+var _contentX = 8;
+var _contentY = 0;
+var _room = PEd_getCurrentRoom();
+
+var _selectedObj = selectedObjects[| 0];
+if (is_undefined(_selectedObj))
+{
+ _selectedObj = 0;
+}
+
+for (var j = 0; j < ds_list_size(pedRoomList); j++)
+{
+ var _room = pedRoomList[| j];
+ var _roomId = PEd_roomGetId(_room);
+
+ if (PEd_guiDrawListItem(PEd_roomGetName(_room) + " : room", _contentX, _contentY, (_selectedObj == _roomId), false, (pedRoomCurrent == j)))
+ {
+ PEd_selectObject(_roomId, true);
+ pedRoomCurrent = j;
+ PEd_guiRequestRedrawAll(guiRoot)
+ }
+ _contentY += guiLineHeight;
+
+ if (editMode == PEdEditModes.Object)
+ {
+ var _instances = PEd_roomGetInstances(_room);
+ for (var i = 0; i < ds_list_size(_instances); i++)
+ {
+ var _id = _instances[| i];
+ var _name = string(_id) + " : " + PEd_instanceGetObjectName(_id) + " : " + PEd_instanceGetName(_id);
+ var _visible = _id.visible;
+
+ if (sceneOutlineFilter == ""
+ || string_pos(string_lower(sceneOutlineFilter), string_lower(_name)) != 0)
+ {
+ var _pos = ds_list_find_index(selectedObjects, _id);
+ var _item = PEd_guiDrawListItemEye(_name, _contentX, _contentY, _selectedObj == _id || _pos != -1, !_visible);
+ if (_item > 0)
+ {
+ // Hide instance
+ if (_item == 2)
+ {
+ _id.visible = !_id.visible;
+ if (!_id.visible)
+ {
+ PEd_deselectObject(_id);
+ }
+ }
+ else if (_item == 1
+ && _id.visible)
+ {
+ // Jump on objects position if selected
+ if (_selectedObj == _id
+ || _pos != -1)
+ {
+ if (!keyboard_check(vk_control))
+ {
+ x = PEd_instanceGetPosX(_id) + 16;
+ y = PEd_instanceGetPosY(_id) + 16;
+ if (global.pedUsing3D)
+ {
+ z = PEd_instanceGetPosZ(_id) + 8;
+ }
+ else
+ {
+ view_xview[0] = PEd_instanceGetPosX(_id) - view_wview[0] * 0.5;
+ view_yview[0] = PEd_instanceGetPosY(_id) - view_hview[0] * 0.5;
+ }
+ direction = 135;
+ camPitch = -16;
+ }
+ }
+
+ // Remove rooms from selection
+ for (var k = ds_list_size(selectedObjects) - 1; k >= 0; k--)
+ {
+ if (selectedObjects[| k] < 0)
+ {
+ ds_list_delete(selectedObjects, k);
+ ds_list_delete(selectedObjectsData, k);
+ }
+ }
+
+ // Select
+ PEd_selectObject(_id);
+ _selectedObj = selectedObjects[| 0];
+ if (is_undefined(_selectedObj))
+ {
+ _selectedObj = 0;
+ }
+ }
+ }
+ _contentY += guiLineHeight;
+ }
+ }
+ } // objects
+ else if (editMode == PEdEditModes.Tile)
+ {
+ for (var i = 0; i < ds_list_size(tileLayers); i++)
+ {
+ var _layerDepth = ds_list_find_value(tileLayers, i);
+ var _layerVisible = ds_list_find_value(tileVisible, i);
+ if (PEd_guiDrawListItem("depth " + string(_layerDepth), _contentX, _contentY, tileLayerSelected == i, 0))
+ {
+ tileLayerSelected = i;
+ tileDepth = _layerDepth;
+ }
+
+ // Change depth
+ if (PEd_guiDrawSpriteClickable(PEd_guiSprMisc, 4, _containerWidth - 60, _contentY)
+ && show_question("Do you really want to change layer depth?"))
+ {
+ var _layerDepthNew = round(PEd_getFloat("New depth:", string(_layerDepth)));
+ if (_layerDepth != _layerDepthNew)
+ {
+ var _pos = ds_list_find_index(tileLayers, _layerDepthNew);
+ if (_pos == -1)
+ {
+ ds_list_replace(tileLayers, i, _layerDepthNew);
+ tileDepth = _layerDepthNew;
+ tile_layer_depth(_layerDepth, _layerDepthNew);
+ }
+ else if (show_question("Layer with given depth already exists, do you want to merge them? (This can't be taken back)"))
+ {
+ ds_list_delete(tileLayers, i);
+ ds_list_delete(tileVisible, i);
+ tile_layer_depth(_layerDepth, _layerDepthNew);
+ tileDepth = _layerDepthNew;
+ tileLayerSelected = _pos-1;
+ }
+ }
+ }
+
+ // Delete
+ if (PEd_guiDrawSpriteClickable(PEd_guiSprMisc, 2, _containerWidth - 40, _contentY)
+ && show_question("Do you really want to delete this layer (" + string(_layerDepth) + ")?"))
+ {
+ ds_list_delete(tileLayers, i);
+ ds_list_delete(tileVisible, i);
+
+ var _tiles = tile_get_ids_at_depth(_layerDepth);
+ for (var j = array_length_1d(_tiles) - 1; j >= 0; j--)
+ {
+ PEd_tileDelete(_tiles[j]);
+ }
+
+ tile_layer_delete(_layerDepth);
+ tileLayerSelected = 0;
+
+ var _selectedObject = selectedObjects[| 0];
+ if (!is_undefined(_selectedObject)
+ && !tile_exists(_selectedObject))
+ {
+ ds_list_delete(selectedObjects, 0);
+ ds_list_delete(selectedObjectsData, 0);
+ }
+ }
+
+ // Show/hide
+ if (PEd_guiDrawSpriteClickable(PEd_guiSprMisc, 1, _containerWidth - 20, _contentY, lerp(PEdColour.Disabled, c_white, _layerVisible)))
+ {
+ ds_list_replace(tileVisible, i, !_layerVisible);
+ if (!_layerVisible)
+ {
+ tile_layer_show(_layerDepth);
+ }
+ else
+ {
+ tile_layer_hide(_layerDepth);
+
+ var _selectedObject = selectedObjects[| 0];
+ if (!is_undefined(_selectedObject)
+ && !tile_exists(_selectedObject))
+ {
+ ds_list_delete(selectedObjects, 0);
+ ds_list_delete(selectedObjectsData, 0);
+ }
+ }
+ }
+ _contentY += guiLineHeight;
+
+ // Draw layer tiles
+ var _tiles = tile_get_ids_at_depth(_layerDepth);
+ for (var t = 0; t < array_length_1d(_tiles); t++)
+ {
+ var _id = _tiles[t];
+ if (!tile_exists(_id))
+ {
+ continue;
+ }
+
+ var _name = "tile : " + string(_id);
+ var _visible = tile_get_visible(_id);
+
+ if (sceneOutlineFilter == ""
+ || string_pos(string_lower(sceneOutlineFilter), string_lower(_name)) != 0)
+ {
+ var _pos = ds_list_find_index(selectedObjects, _id);
+ var _item = PEd_guiDrawListItemEye(_name, _contentX, _contentY, _selectedObj == _id || _pos != -1, !_visible);
+ if (_item > 0)
+ {
+ // Hide tile
+ if (_item == 2)
+ {
+ tile_set_visible(_id, !_visible);
+ if (!_visible)
+ {
+ PEd_deselectObject(_id);
+ }
+ }
+ else if (_item == 1
+ && _visible)
+ {
+ // Jump on objects position if selected
+ if (_selectedObj == _id
+ || _pos != -1)
+ {
+ if (!keyboard_check(vk_control))
+ {
+ x = tile_get_x(_id) + 16;
+ y = tile_get_y(_id) + 16;
+ view_xview[0] = tile_get_x(_id) - view_wview[0] * 0.5;
+ view_yview[0] = tile_get_y(_id) - view_hview[0] * 0.5;
+ }
+ }
+
+ // Remove rooms from selection
+ for (var k = ds_list_size(selectedObjects) - 1; k >= 0; k--)
+ {
+ if (selectedObjects[| k] < 0)
+ {
+ ds_list_delete(selectedObjects, k);
+ ds_list_delete(selectedObjectsData, k);
+ }
+ }
+
+ // Select
+ PEd_selectObject(_id);
+ _selectedObj = selectedObjects[| 0];
+ if (is_undefined(_selectedObj))
+ {
+ _selectedObj = 0;
+ }
+ else
+ {
+ tileLayerSelected = i;
+ tileDepth = _layerDepth;
+ }
+ }
+ }
+ _contentY += guiLineHeight;
+ }
+ }
+ }
+ } // tiles
+} // rooms
+
+return PEd_vec2(_containerWidth, _contentY);
diff --git a/PushEd.gmx/scripts/PEd_guiContentStatusBar.gml b/PushEd.gmx/scripts/PEd_guiContentStatusBar.gml
new file mode 100644
index 00000000..1122ba4b
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiContentStatusBar.gml
@@ -0,0 +1,79 @@
+/// PEd_guiContentStatusBar(container)
+/**
+ * @brief Draws the content of a Status bar to the container.
+ * @param {real} container The id of the container.
+ * @return {vec2} The content size.
+ */
+var _container = argument0;
+var _containerWidth = PEd_guiShapeGetWidth(_container);
+var _containerHeight = PEd_guiShapeGetHeight(_container);
+var _items = PEd_guiCompoundShapeGetItems(_container);
+var _padding = 4;
+var _x = _padding;
+var _y = _padding;
+
+var _textY = round((_containerHeight - guiFontHeight) * 0.5);
+var _text;
+
+var _inputWidth = 64;
+var _input;
+
+PEd_guiDrawRectangle(0, 0, _containerWidth, 1, PEdColour.WindowBorder);
+
+////////////////////////////////////////////////////////////////////////////////
+// Grid size
+var _room = PEd_getCurrentRoom();
+
+_text = "Grid size:";
+draw_text(_x, _textY, _text);
+_x += string_width(_text) + _padding;
+
+_input = PEd_guiDrawInput(_x, _y, _inputWidth, PEd_roomGetSnapH(_room));
+if (!is_undefined(_input))
+{
+ PEd_roomSetSnapH(_room, max(1, _input));
+}
+_x += _inputWidth + _padding;
+
+_input = PEd_guiDrawInput(_x, _y, _inputWidth, PEd_roomGetSnapV(_room));
+if (!is_undefined(_input))
+{
+ PEd_roomSetSnapV(_room, max(1, _input));
+}
+_x += _inputWidth + _padding;
+
+_input = PEd_guiDrawInput(_x, _y, _inputWidth, PEd_roomGetSnapD(_room));
+if (!is_undefined(_input))
+{
+ PEd_roomSetSnapD(_room, max(1, _input));
+}
+_x += _inputWidth + _padding;
+
+////////////////////////////////////////////////////////////////////////////////
+// Pivot position
+_text = "Pivot:";
+draw_text(_x, _textY, _text);
+_x += string_width(_text) + _padding;
+
+_input = PEd_guiDrawInput(_x, _y, _inputWidth, PEd_oPivot.x);
+if (!is_undefined(_input))
+{
+ PEd_oPivot.x = _input;
+}
+_x += _inputWidth + _padding;
+
+_input = PEd_guiDrawInput(_x, _y, _inputWidth, PEd_oPivot.y);
+if (!is_undefined(_input))
+{
+ PEd_oPivot.y = _input;
+}
+_x += _inputWidth + _padding;
+
+_input = PEd_guiDrawInput(_x, _y, _inputWidth, PEd_oPivot.z);
+if (!is_undefined(_input))
+{
+ PEd_oPivot.z = _input;
+}
+_x += _inputWidth + _padding;
+
+return PEd_vec2(_x + _padding, _containerHeight);
diff --git a/PushEd.gmx/scripts/PEd_guiContentTitleBar.gml b/PushEd.gmx/scripts/PEd_guiContentTitleBar.gml
new file mode 100644
index 00000000..a8499097
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiContentTitleBar.gml
@@ -0,0 +1,24 @@
+/// PEd_guiContentTitleBar(container)
+/**
+ * @brief Draws the content of a Title Bar to the container.
+ * @param {real} container The id of the container.
+ * @return {vec2} The content size.
+ */
+var _container = argument0;
+var _containerWidth = PEd_guiShapeGetWidth(_container);
+var _delegate = PEd_guiShapeGetDelegate(_container);
+var _selectedShape = PEd_guiGetSelectedShape();
+if (_selectedShape == _delegate
+ || PEd_guiShapeDelegatesRecursive(_delegate, _selectedShape))
+{
+ draw_clear(PEdColour.Active);
+}
+
+var _x = 4;
+var _y = 4;
+var _title = _container[? "title"];
+draw_sprite(PEd_guiSprPanel, 0, _x, _y);
+PEd_guiDrawTextBold(_x + guiLineHeight + 4, _y + round((guiLineHeight - guiFontHeight) * 0.5), _title);
+_y += guiLineHeight + 4;
+
+return PEd_vec2(_containerWidth, _y);
diff --git a/PushEd.gmx/scripts/PEd_guiContentTitleBarContentBrowser.gml b/PushEd.gmx/scripts/PEd_guiContentTitleBarContentBrowser.gml
new file mode 100644
index 00000000..1509e711
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiContentTitleBarContentBrowser.gml
@@ -0,0 +1,38 @@
+/// PEd_guiContentTitleBarContentBrowser(container)
+/**
+ * @brief Draws the content of a Content Browser Title Bar to the container.
+ * @param {real} container The id of the container.
+ * @return {vec2} The content size.
+ */
+var _container = argument0;
+var _containerWidth = PEd_guiShapeGetWidth(_container);
+var _containerHeight = PEd_guiShapeGetHeight(_container);
+
+var _delegate = PEd_guiShapeGetDelegate(_container);
+var _selectedShape = PEd_guiGetSelectedShape();
+if (_selectedShape == _delegate
+ || PEd_guiShapeDelegatesRecursive(_delegate, _selectedShape))
+{
+ // FIXME Ugly hack :(
+ PEd_guiDrawRectangle(0, 0, _containerWidth, guiLineHeight + 8, PEdColour.Active);
+ PEd_guiDrawRectangle(0, 0, 1, _containerHeight, PEdColour.Active);
+ PEd_guiDrawRectangle(_containerWidth - 1, 0, 1, _containerHeight, PEdColour.Active);
+}
+
+var _x = 4;
+var _y = 4;
+var _title = _container[? "title"];
+draw_sprite(PEd_guiSprPanel, 0, _x, _y);
+PEd_guiDrawTextBold(_x + guiLineHeight + 4, _y + round((guiLineHeight - guiFontHeight) * 0.5), _title);
+_y += guiLineHeight + 8;
+
+var _inputWidth = _containerWidth - _x * 2;
+var _input = PEd_guiDrawInput(_x, _y, _inputWidth, contentBrowserFilter, false, "Filter...");
+if (!is_undefined(_input))
+{
+ contentBrowserFilter = _input;
+ PEd_guiRequestRedrawAll(guiRoot)
+}
+_y += guiLineHeight + 4;
+
+return PEd_vec2(_containerWidth, _y);
diff --git a/PushEd.gmx/scripts/PEd_guiContentTitleBarSceneOutline.gml b/PushEd.gmx/scripts/PEd_guiContentTitleBarSceneOutline.gml
new file mode 100644
index 00000000..631b881b
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiContentTitleBarSceneOutline.gml
@@ -0,0 +1,38 @@
+/// PEd_guiContentTitleBarSceneOutline(container)
+/**
+ * @brief Draws the content of a Scene Outline Title Bar to the container.
+ * @param {real} container The id of the container.
+ * @return {vec2} The content size.
+ */
+var _container = argument0;
+var _containerWidth = PEd_guiShapeGetWidth(_container);
+var _containerHeight = PEd_guiShapeGetHeight(_container);
+
+var _delegate = PEd_guiShapeGetDelegate(_container);
+var _selectedShape = PEd_guiGetSelectedShape();
+if (_selectedShape == _delegate
+ || PEd_guiShapeDelegatesRecursive(_delegate, _selectedShape))
+{
+ // FIXME Ugly hack :(
+ PEd_guiDrawRectangle(0, 0, _containerWidth, guiLineHeight + 8, PEdColour.Active);
+ PEd_guiDrawRectangle(0, 0, 1, _containerHeight, PEdColour.Active);
+ PEd_guiDrawRectangle(_containerWidth - 1, 0, 1, _containerHeight, PEdColour.Active);
+}
+
+var _x = 4;
+var _y = 4;
+var _title = _container[? "title"];
+draw_sprite(PEd_guiSprPanel, 0, _x, _y);
+PEd_guiDrawTextBold(_x + guiLineHeight + 4, _y + round((guiLineHeight - guiFontHeight) * 0.5), _title);
+_y += guiLineHeight + 8;
+
+var _inputWidth = _containerWidth - _x * 2;
+var _input = PEd_guiDrawInput(_x, _y, _inputWidth, sceneOutlineFilter, false, "Filter...");
+if (!is_undefined(_input))
+{
+ sceneOutlineFilter = _input;
+ PEd_guiRequestRedrawAll(guiRoot)
+}
+_y += guiLineHeight + 4;
+
+return PEd_vec2(_containerWidth, _y);
diff --git a/PushEd.gmx/scripts/PEd_guiContentTitleBarWindow.gml b/PushEd.gmx/scripts/PEd_guiContentTitleBarWindow.gml
new file mode 100644
index 00000000..9f52d4bd
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiContentTitleBarWindow.gml
@@ -0,0 +1,28 @@
+/// PEd_guiContentTitleBarWindow(container)
+/**
+ * @brief Draws the content of a Window Title Bar to the container.
+ * @param {real} container The id of the container.
+ * @return {vec2} The content size.
+ */
+var _container = argument0;
+var _containerWidth = PEd_guiShapeGetWidth(_container);
+
+var _delegate = PEd_guiShapeGetDelegate(_container);
+var _selectedShape = PEd_guiGetSelectedShape();
+if (_selectedShape == _delegate
+ || PEd_guiShapeDelegatesRecursive(_delegate, _selectedShape))
+{
+ draw_clear(PEdColour.Active);
+}
+
+var _x = 4;
+var _y = 4;
+var _title = _container[? "title"];
+PEd_guiDrawTextBold(_x, _y + round((guiLineHeight - guiFontHeight) * 0.5), _title);
+if (PEd_guiDrawSpriteClickable(PEd_guiSprWindowCross, 0, _containerWidth - guiLineHeight, _y + 4, PEdColour.WindowButton))
+{
+ PEd_guiDestroyShape(_delegate);
+}
+_y += guiLineHeight + 4;
+
+return PEd_vec2(_containerWidth, _y);
diff --git a/PushEd.gmx/scripts/PEd_guiContentToolbar.gml b/PushEd.gmx/scripts/PEd_guiContentToolbar.gml
new file mode 100644
index 00000000..5651a65a
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiContentToolbar.gml
@@ -0,0 +1,19 @@
+/// PEd_guiContentToolbar(container)
+/**
+ * @brief Draws the content of a Toolbar to the container.
+ * @param {real} container The id of the container.
+ * @return {vec2} The content size.
+ */
+var _items = PEd_guiCompoundShapeGetItems(argument0);
+var _padding = 1;
+var _x = _padding;
+var _y = _padding;
+
+for (var i = 0; i < ds_list_size(_items); i++)
+{
+ var _item = _items[| i];
+ PEd_guiDrawItem(_item, _x, _y);
+ _x += PEd_guiShapeGetWidth(_item);
+}
+
+return PEd_vec2(_x + _padding, PEd_guiShapeGetHeight(argument0));
diff --git a/PushEd.gmx/scripts/PEd_guiContentTools.gml b/PushEd.gmx/scripts/PEd_guiContentTools.gml
new file mode 100644
index 00000000..5c5bde6a
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiContentTools.gml
@@ -0,0 +1,322 @@
+/// PEd_guiContentTools(container)
+/**
+ * @brief Draws the content of a Tools to the container.
+ * @param {real} container The id of the container.
+ * @return {vec2} The content size.
+ */
+var _container = argument0;
+var _containerWidth = PEd_guiShapeGetWidth(_container);
+var _containerHeight = PEd_guiShapeGetHeight(_container);
+var _contentX = round(guiLineHeight * 0.75);
+var _contentY = 0;
+var __contentX = _contentX;
+
+////////////////////////////////////////////////////////////////////////////////
+// Left column
+if (is_undefined(_container[? "columnLeftWidth"]))
+{
+ _container[? "columnLeftWidth"] = 70;
+ _container[? "columnLeftResize"] = false;
+}
+
+var _columnLeftWidth = _container[? "columnLeftWidth"];
+var _columnLeftResize = _container[? "columnLeftResize"];
+var _columnLeftX = _contentX + _columnLeftWidth;
+var _mouseOverColumnLeftResizeArea = (PEd_guiShapeIsHovered(_container)
+ && guiMouseX > _columnLeftX - 4
+ && guiMouseX < _columnLeftX + 4);
+
+var _scrollY = 0;
+var _scrollbarVer = _container[? "scrollbarVer"];
+if (!is_undefined(_scrollbarVer))
+{
+ _scrollY = PEd_guiScrollbarGetScroll(_scrollbarVer) * PEd_guiScrollbarIsVisible(_scrollbarVer);
+}
+
+if (_mouseOverColumnLeftResizeArea)
+{
+ guiCursor = cr_size_we;
+ if (mouse_check_button_pressed(mb_left))
+ {
+ // Start resizing
+ guiShapeActive = _container;
+ _container[? "columnLeftResize"] = true;
+ }
+}
+
+if (guiShapeActive == _container
+ && _container[? "columnLeftResize"])
+{
+ // Set new size
+ guiCursor = cr_size_we;
+ _columnLeftWidth = guiMouseX - _contentX;
+ _container[? "columnLeftWidth"] = _columnLeftWidth;
+ if (mouse_check_button_released(mb_left))
+ {
+ // End resizing
+ guiShapeActive = noone;
+ _container[? "columnLeftResize"] = false;
+ }
+}
+
+// Clamp size
+_columnLeftWidth = clamp(_columnLeftWidth, 32, _containerWidth - _contentX * 2 - 32);
+_container[? "columnLeftWidth"] = _columnLeftWidth;
+
+PEd_guiDrawRectangle(0, _scrollY, _columnLeftX, _containerHeight, PEdColour.WindowBackground2);
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Tiles
+//
+
+// Draw section
+if (PEd_guiDrawSection("Tiles", _contentX, _contentY, toolsTileEditorShow))
+{
+ toolsTileEditorShow = !toolsTileEditorShow;
+}
+_contentY += guiLineHeight;
+
+var _backgroundWidth = 1;
+
+if (toolsTileEditorShow)
+{
+ _contentY += 4;
+ var _textOffsetY = round((guiLineHeight - guiFontHeight) * 0.5);
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Background
+ PEd_guiDrawTextPart(_contentX, _contentY + _textOffsetY, "Image", _columnLeftWidth);
+ _contentX += _columnLeftWidth + 4;
+
+ _backgroundWidth = background_get_width(tileBground) * tileZoom;
+ var _backgroundHeight = background_get_height(tileBground) * tileZoom;
+
+ var _mouseInArea = (PEd_guiShapeIsHovered(_container)
+ && guiMouseX > _contentX
+ && guiMouseY > _contentY
+ && guiMouseX < _contentX + _backgroundWidth
+ && guiMouseY < _contentY + _backgroundHeight);
+ // Zoom
+ if (_mouseInArea)
+ {
+ var _wheel = mouse_wheel_up() - mouse_wheel_down();
+ if (_wheel == 0)
+ {
+ _wheel = (keyboard_check(vk_pageup) - keyboard_check(vk_pagedown)) * 0.25;
+ }
+ else if (keyboard_check(vk_control))
+ {
+ tileZoom = clamp(tileZoom + _wheel * 0.25, 0.25, 10);
+ }
+ }
+
+ PEd_guiDrawRectangle(_contentX, _contentY, _backgroundWidth, _backgroundHeight, c_black);
+ draw_background_ext(tileBground, _contentX, _contentY, tileZoom, tileZoom, 0, tileColour, tileAlpha);
+
+ // Selected part
+ var rx = _contentX + tilePartX * tileZoom;
+ var ry = _contentY + tilePartY * tileZoom;
+ var rw = rx + tilePartW * tileZoom;
+ var rh = ry + tilePartH * tileZoom;
+
+ var _x0 = max(_contentX, min(rx, _contentX + _backgroundWidth)) - 1;
+ var _x1 = max(_contentX, min(rw, _contentX + _backgroundWidth));
+ var _y0 = max(_contentY, min(ry, _contentY + _backgroundHeight)) - 1;
+ var _y1 = max(_contentY, min(rh, _contentY + _backgroundHeight));
+
+ draw_rectangle_colour(min(_x0, _x1), min(_y0, _y1),
+ max(_x0, _x1), max(_y0, _y1),
+ 0, 0, 0, 0, true);
+
+ draw_rectangle(min(_x0, _x1), min(_y0, _y1),
+ max(_x0, _x1), max(_y0, _y1), true);
+
+ // If mouse is in area
+ if (_mouseInArea)
+ {
+ // Visualisation of selection
+ var tileVisX, tileVisY, tileVisW, tileVisH;
+ tileVisX = floor((guiMouseX - _contentX - tileOffX * tileZoom) / ((tileGridW + tileStepX) * tileZoom)) * ((tileGridW + tileStepX) * tileZoom) + tileOffX * tileZoom;
+ tileVisY = floor((guiMouseY - _contentY - tileOffY * tileZoom) / ((tileGridH + tileStepY) * tileZoom)) * ((tileGridH + tileStepY) * tileZoom) + tileOffY * tileZoom;
+ tileVisX = max(tileOffX * tileZoom, tileVisX);
+ tileVisY = max(tileOffY * tileZoom, tileVisY);
+ tileVisW = tileGridW * tileZoom;
+ tileVisH = tileGridH * tileZoom;
+
+ rx = _contentX + tileVisX;
+ ry = _contentY + tileVisY;
+ rw = rx+tileVisW;
+ rh = ry+tileVisH;
+
+ draw_rectangle_colour(max(_contentX, min(rx, _contentX + _backgroundWidth)),
+ max(_contentY, min(ry, _contentY + _backgroundHeight)),
+ max(_contentX, min(rw, _contentX + _backgroundWidth)) - 1,
+ max(_contentY, min(rh, _contentY + _backgroundHeight)) - 1,
+ PEdColour.Active, PEdColour.Active, PEdColour.Active, PEdColour.Active, true);
+
+ // Set background
+ if (guiDnDBackground != -1)
+ {
+ if (mouse_check_button_released(mb_left))
+ {
+ tileBground = guiDnDBackground;
+ guiDnDBackground = -1;
+
+ tilePartX = 0;
+ tilePartY = 0;
+ tileGridW = clamp(tileGridW, 1, _backgroundWidth);
+ tileGridH = clamp(tileGridH, 1, _backgroundHeight);
+ tilePartW = tileGridW;
+ tilePartH = tileGridH;
+ }
+ }
+ else
+ {
+ // Scroll
+ if (mouse_check_button_pressed(mb_left))
+ {
+ // Selection
+ tilePartX = tileVisX / tileZoom;
+ tilePartY = tileVisY / tileZoom;
+ tilePartW = tileVisW / tileZoom;
+ tilePartH = tileVisH / tileZoom;
+ }
+ else if (mouse_check_button(mb_left)
+ && keyboard_check(vk_shift))
+ {
+ tilePartW = (tileVisX + tileVisW) / tileZoom - tilePartX;
+ tilePartH = (tileVisY + tileVisH) / tileZoom - tilePartY;
+ if (tilePartW <= 0)
+ {
+ tilePartW -= tileVisW / tileZoom;
+ }
+ if (tilePartH <= 0)
+ {
+ tilePartH -= tileVisH / tileZoom;
+ }
+ }
+ else if (mouse_check_button_released(mb_left))
+ {
+ if (tilePartW < 0)
+ {
+ tilePartX += tilePartW;
+ tilePartW = -tilePartW;
+ }
+ if (tilePartH < 0)
+ {
+ tilePartY += tilePartH;
+ tilePartH = -tilePartH;
+ }
+ }
+ }
+ }
+
+ // Highlight area
+ if (guiDnDBackground != -1)
+ {
+ PEd_guiDrawRectangle(_contentX, _contentY, _backgroundWidth, _backgroundHeight, PEdColour.Active, 0.5);
+ }
+
+ _contentX = __contentX;
+ _contentY += _backgroundHeight + 4;
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Grid size
+ PEd_guiDrawTextPart(_contentX, _contentY + _textOffsetY, "Grid size", _columnLeftWidth);
+
+ var _max = max(_containerWidth, _columnLeftX + 4 + _backgroundWidth);
+ var _singleInputWidth = _max - _contentX - _columnLeftWidth - 12;
+ var _inputWidth = round((_singleInputWidth - 4) * 0.5);
+ var _inputX = _contentX + _columnLeftWidth + 4;
+
+ var _input = PEd_guiDrawInput(_inputX, _contentY, _inputWidth, tileGridW);
+ if (!is_undefined(_input))
+ {
+ tileGridW = clamp(round(_input), 1, _backgroundWidth);
+ }
+
+ var _input = PEd_guiDrawInput(_inputX + _inputWidth + 4, _contentY, _inputWidth, tileGridH);
+ if (!is_undefined(_input))
+ {
+ tileGridH = clamp(round(_input), 1, _backgroundHeight);
+ }
+ _contentY += guiLineHeight + 4;
+
+ // Offset
+ PEd_guiDrawTextPart(_contentX, _contentY + _textOffsetY, "Offset", _columnLeftWidth);
+
+ var _input = PEd_guiDrawInput(_inputX, _contentY, _inputWidth, tileOffX);
+ if (!is_undefined(_input))
+ {
+ tileOffX = max(round(_input), 0);
+ }
+
+ var _input = PEd_guiDrawInput(_inputX + _inputWidth + 4, _contentY, _inputWidth, tileOffY);
+ if (!is_undefined(_input))
+ {
+ tileOffY = max(round(_input), 0);
+ }
+ _contentY += guiLineHeight + 4;
+
+ // Step
+ PEd_guiDrawTextPart(_contentX, _contentY + _textOffsetY, "Step", _columnLeftWidth);
+
+ var _input = PEd_guiDrawInput(_inputX, _contentY, _inputWidth, tileStepX);
+ if (!is_undefined(_input))
+ {
+ tileStepX = max(0, round(_input));
+ }
+
+ var _input = PEd_guiDrawInput(_inputX + _inputWidth + 4, _contentY, _inputWidth, tileStepY);
+ if (!is_undefined(_input))
+ {
+ tileStepY = max(0, round(_input));
+ }
+ _contentY += guiLineHeight + 4;
+
+ // Colour
+ PEd_guiDrawTextPart(_contentX, _contentY + _textOffsetY, "Colour", _columnLeftWidth);
+ var _colourPicker = PEd_guiDrawColourInput(_inputX, _contentY, _singleInputWidth, tileColour, tileAlpha);
+ if (PEd_guiShapeExists(_colourPicker))
+ {
+ if (!PEd_guiShapeExists(guiColourPickerTileEditor))
+ {
+ guiColourPickerTileEditor = _colourPicker;
+ var _titleBar = _colourPicker[? "titleBar"];
+ _titleBar[? "title"] += " - Tile Editor";
+ }
+ else
+ {
+ PEd_guiDestroyShape(_colourPicker);
+ }
+ }
+ _contentY += guiLineHeight + 4;
+
+ // Depth
+ PEd_guiDrawTextPart(_contentX, _contentY + _textOffsetY, "Depth", _columnLeftWidth);
+ var _input = PEd_guiDrawInput(_inputX, _contentY, _singleInputWidth, tileDepth);
+ if (!is_undefined(_input))
+ {
+ tileDepth = round(_input);
+ if (ds_list_find_index(tileLayers, tileDepth) == -1)
+ {
+ ds_list_add(tileLayers, tileDepth);
+ ds_list_add(tileVisible, true);
+ tileLayerSelected = ds_list_size(tileLayers)-1;
+ }
+ else
+ {
+ tileLayerSelected = ds_list_find_index(tileLayers, tileDepth);
+ }
+ PEd_guiRequestRedrawAll(guiRoot)
+ }
+ _contentY += guiLineHeight + 4;
+
+ // Delete underlying
+ PEd_guiDrawTextPart(_contentX, _contentY + _textOffsetY, "Delete underlying", _columnLeftWidth);
+ tileDeleteUnderlying = PEd_guiDrawCheckbox(_inputX, _contentY, tileDeleteUnderlying);
+ _contentY += guiLineHeight + 4;
+}
+
+return PEd_vec2(max(_containerWidth, _columnLeftX + 4 + _backgroundWidth), _contentY);
diff --git a/PushEd.gmx/scripts/PEd_guiContextMenuDraw.gml b/PushEd.gmx/scripts/PEd_guiContextMenuDraw.gml
new file mode 100644
index 00000000..52019a12
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiContextMenuDraw.gml
@@ -0,0 +1,49 @@
+/// PEd_guiContextMenuDraw(contextMenu)
+/**
+ * @brief Draws the context menu.
+ * @param {real} contextMenu The id of the context menu.
+ */
+var _contextMenu = argument0;
+if (PEd_guiBeginFill(_contextMenu))
+{
+ var _contextMenuItems = PEd_guiCompoundShapeGetItems(_contextMenu);
+ var _x = 0;
+ var _y = 4;
+ var _width = 1;
+
+ // Draw items
+ var _size = ds_list_size(_contextMenuItems);
+ for (var i = 0; i < _size; i++)
+ {
+ var _item = _contextMenuItems[| i];
+ PEd_guiDrawItem(_item, 0, _y);
+ _width = max(_width, PEd_guiShapeGetWidth(_item));
+ _y += PEd_guiShapeGetHeight(_item);
+ }
+
+ // Set context menu size
+ var _contextMenuWidth = clamp(_width, 200, windowWidth);
+ var _contextMenuHeight = min(_y + 4, windowHeight);
+ PEd_guiShapeSetWidth(_contextMenu, _contextMenuWidth);
+ PEd_guiShapeSetHeight(_contextMenu, _contextMenuHeight);
+ PEd_guiContainerSetContentWidth(_contextMenu, _contextMenuWidth);
+ PEd_guiContainerSetContentHeight(_contextMenu, _contextMenuHeight);
+ PEd_guiEndFill(_contextMenu);
+}
+
+// Draw context menu
+var _surface = PEd_guiCanvasGetSurface(_contextMenu);
+if (surface_exists(_surface))
+{
+ var _x = PEd_guiShapeGetX(_contextMenu);
+ var _y = PEd_guiShapeGetY(_contextMenu);
+ var _width = surface_get_width(_surface);
+ var _height = surface_get_height(_surface);
+ if (_width > 1
+ && _height > 1)
+ {
+ //PEd_guiDrawShadow(_x, _y, _width, _height, PEdColour.Shadow, PEdColour.ShadowAlpha);
+ PEd_guiDrawRectangle(_x - 1, _y - 1, _width + 2, _height + 2, PEdColour.WindowBorder, 1);
+ }
+}
+PEd_guiCanvasDraw(_contextMenu);
diff --git a/PushEd.gmx/scripts/PEd_guiContextMenuItemDraw.gml b/PushEd.gmx/scripts/PEd_guiContextMenuItemDraw.gml
new file mode 100644
index 00000000..8dca116b
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiContextMenuItemDraw.gml
@@ -0,0 +1,46 @@
+/// PEd_guiContextMenuItemDraw(contextMenuItem)
+/**
+ * @brief Dras the context menu item.
+ * @param {real} contextMenuItem The id of the context menu item.
+ */
+var _item = argument0;
+var _x = PEd_guiShapeGetX(_item);
+var _y = PEd_guiShapeGetY(_item);
+var _width = PEd_guiShapeGetWidth(_item);
+var _height = PEd_guiShapeGetHeight(_item);
+var _name = _item[? "name"];
+var _delegate = PEd_guiShapeGetDelegate(_item);
+var _scrAction = _item[? "scrAction"];
+var _textColour = PEdColour.Disabled;
+var _delegateWidth = PEd_guiShapeGetWidth(_delegate);
+
+var _shortcut = _item[? "shortcut"];
+var _shortcutWidth = 0;
+
+// Draw highlight
+if (_scrAction != noone)
+{
+ _textColour = PEdColour.Text;
+ if (PEd_guiShapeIsHovered(_item))
+ {
+ PEd_guiDrawRectangle(_x, _y, _delegateWidth, _height, PEdColour.Active, 1);
+ }
+}
+
+// Draw item name
+draw_text_colour(_x + 8, _y + round((_height - guiFontHeight) * 0.5), _name,
+ _textColour, _textColour, _textColour, _textColour, 1);
+
+// Draw keyboard shortcut
+if (_shortcut != noone)
+{
+ var _shortcutText = PEd_guiKeyboardShortcutToString(_shortcut);
+ _shortcutWidth = string_width(_shortcutText) + 8;
+ draw_text_colour(_x + _delegateWidth - _shortcutWidth,
+ _y + round((_height - guiFontHeight) * 0.5),
+ _shortcutText,
+ _textColour, _textColour, _textColour, _textColour, 1);
+ _shortcutWidth += guiFontWidth * 2;
+}
+
+PEd_guiShapeSetWidth(_item, max(_x + string_width(_name) + _shortcutWidth, _delegateWidth));
diff --git a/PushEd.gmx/scripts/PEd_guiContextMenuItemUpdate.gml b/PushEd.gmx/scripts/PEd_guiContextMenuItemUpdate.gml
new file mode 100644
index 00000000..ddf6e4c4
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiContextMenuItemUpdate.gml
@@ -0,0 +1,19 @@
+/// PEd_guiContextMenuItemUpdate(contextMenuItem)
+/**
+ * @brief Updates the context menu item.
+ * @param {real} contextMenuItem The id of the context menu item.
+ */
+var _item = argument0;
+PEd_guiShapeUpdate(_item);
+
+if (mouse_check_button_pressed(mb_left)
+ && PEd_guiShapeIsHovered(_item))
+{
+ var _scrAction = _item[? "scrAction"];
+ if (_scrAction != noone)
+ {
+ script_execute(_scrAction);
+ PEd_guiDestroyShape(PEd_guiShapeGetDelegate(_item));
+ guiContextMenu = noone;
+ }
+}
diff --git a/PushEd.gmx/scripts/PEd_guiContextMenuUpdate.gml b/PushEd.gmx/scripts/PEd_guiContextMenuUpdate.gml
new file mode 100644
index 00000000..48d21694
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiContextMenuUpdate.gml
@@ -0,0 +1,12 @@
+/// PEd_guiContextMenuUpdate(contextMenu)
+/**
+ * @brief Updates the context menu.
+ * @param {real} contextMenu The id of the context menu.
+ */
+var _contextMenu = argument0;
+PEd_guiContainerUpdate(_contextMenu);
+
+// Clamp position
+PEd_guiShapeSetPosition(_contextMenu,
+ min(PEd_guiShapeGetX(_contextMenu), windowWidth - PEd_guiShapeGetWidth(_contextMenu)),
+ min(PEd_guiShapeGetY(_contextMenu), windowHeight - PEd_guiShapeGetHeight(_contextMenu)));
diff --git a/PushEd.gmx/scripts/PEd_guiCreateCanvas.gml b/PushEd.gmx/scripts/PEd_guiCreateCanvas.gml
new file mode 100644
index 00000000..5f88148f
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiCreateCanvas.gml
@@ -0,0 +1,20 @@
+/// PEd_guiCreateCanvas([type])
+/**
+ * @brief Creates a new canvas.
+ * @param {PEdGUIShape} type The canvas type.
+ * @return {real} The id of the createed canvas.
+ */
+var _canvas;
+if (argument_count == 1)
+{
+ _canvas = PEd_guiCreateCompoundShape(argument[0]);
+}
+else
+{
+ _canvas = PEd_guiCreateCompoundShape(PEdGUIShape.Compound);
+}
+PEd_guiCanvasSetSurface(_canvas, noone);
+PEd_guiCanvasSetBackground(_canvas, PEdColour.WindowBackground);
+_canvas[? "scrDraw"] = PEd_guiCanvasDraw;
+_canvas[? "scrCleanUp"] = PEd_guiCanvasCleanUp;
+return _canvas;
diff --git a/PushEd.gmx/scripts/PEd_guiCreateCompoundShape.gml b/PushEd.gmx/scripts/PEd_guiCreateCompoundShape.gml
new file mode 100644
index 00000000..45b4176d
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiCreateCompoundShape.gml
@@ -0,0 +1,19 @@
+/// PEd_guiCreateCompoundShape([type])
+/**
+ * @brief Creates a new compound shape.
+ * @param {PEdGUIShape} type The compound shape type.
+ * @return {real} The id of the created compound shape.
+ */
+var _compoundShape;
+if (argument_count == 1)
+{
+ _compoundShape = PEd_guiCreateShape(argument[0]);
+}
+else
+{
+ _compoundShape = PEd_guiCreateShape(PEdGUIShape.Compound);
+}
+_compoundShape[? "scrUpdate"] = PEd_guiCompoundShapeUpdate;
+_compoundShape[? "scrCleanUp"] = PEd_guiCompoundShapeCleanUp;
+ds_map_add_list(_compoundShape, "items", ds_list_create());
+return _compoundShape;
diff --git a/PushEd.gmx/scripts/PEd_guiCreateContainer.gml b/PushEd.gmx/scripts/PEd_guiCreateContainer.gml
new file mode 100644
index 00000000..9dda1be0
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiCreateContainer.gml
@@ -0,0 +1,23 @@
+/// PEd_guiCreateContainer([x, y, width, height])
+/**
+ * @brief Creates a new scrollable container.
+ * @param {real} [x] The x positon to create the container at.
+ * @param {real} [y] The y positon to create the container at.
+ * @param {real} [width] The width of the container.
+ * @param {real} [height] The width of the container.
+ * @return {real} The id of the created container.
+ */
+var _container = PEd_guiCreateCanvas(PEdGUIShape.Container);
+if (argument_count == 4)
+{
+ PEd_guiShapeSetRectangle(_container, argument[0], argument[1], argument[2], argument[3]);
+}
+PEd_guiContainerSetContent(_container, noone);
+_container[? "scrDraw"] = PEd_guiContainerDraw;
+_container[? "scrUpdate"] = PEd_guiContainerUpdate;
+_container[? "clickScroll"] = false;
+_container[? "clickScrollMouseX"] = 0;
+_container[? "clickScrollMouseY"] = 0;
+ds_map_add_map(_container, "scrollbarHor", PEd_guiCreateScrollbarHor(_container));
+ds_map_add_map(_container, "scrollbarVer", PEd_guiCreateScrollbarVer(_container));
+return _container;
diff --git a/PushEd.gmx/scripts/PEd_guiCreateContextMenu.gml b/PushEd.gmx/scripts/PEd_guiCreateContextMenu.gml
new file mode 100644
index 00000000..885adc13
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiCreateContextMenu.gml
@@ -0,0 +1,10 @@
+/// PEd_guiCreateContextMenu()
+/**
+ * @brief Creates a new context menu.
+ * @return {real} The id of the created menu.
+ */
+var _menu = PEd_guiCreateContainer(PEdGUIShape.ContextMenu);
+_menu[? "scrUpdate"] = PEd_guiContextMenuUpdate;
+_menu[? "scrDraw"] = PEd_guiContextMenuDraw;
+PEd_guiShapeSetDepth(_menu, 2147483647);
+return _menu;
diff --git a/PushEd.gmx/scripts/PEd_guiCreateContextMenuItem.gml b/PushEd.gmx/scripts/PEd_guiCreateContextMenuItem.gml
new file mode 100644
index 00000000..8603d0ad
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiCreateContextMenuItem.gml
@@ -0,0 +1,27 @@
+/// PEd_guiCreateContextMenuItem(name, action, [shortcut], [tooltip])
+/**
+ * @brief Creates a new context menu item with given name.
+ * @param {string} name The of the context menu item.
+ * @param {real} action The script that will be executed on click, or noone.
+ * @param {real} [shortcut] The id of the keyboard shortcut for this item.
+ * @param {string} [tooltip] The tooltip text that will show up on mouse over.
+ * If you don't specify the action then this is not used.
+ * @return {real} The id of the created context menu item.
+ */
+var _item = PEd_guiCreateShape(PEdGUIShape.ContextMenuItem);
+_item[? "name"] = argument[0];
+_item[? "scrAction"] = argument[1];
+_item[? "scrUpdate"] = PEd_guiContextMenuItemUpdate;
+_item[? "scrDraw"] = PEd_guiContextMenuItemDraw;
+PEd_guiShapeSetHeight(_item, guiLineHeight);
+_item[? "shortcut"] = noone;
+if (argument_count > 2)
+{
+ _item[? "shortcut"] = argument[2];
+}
+if (argument_count > 3
+ && _item[? "scrAction"] != noone)
+{
+ PEd_guiShapeSetTooltip(_item, argument[3]);
+}
+return _item;
diff --git a/PushEd.gmx/scripts/PEd_guiCreateDock.gml b/PushEd.gmx/scripts/PEd_guiCreateDock.gml
new file mode 100644
index 00000000..221ad02e
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiCreateDock.gml
@@ -0,0 +1,22 @@
+/// PEd_guiCreateDock([x, y, width, height])
+/**
+ * @brief Creates a new dock.
+ * @param {real} [x] The x positon to create the dock at.
+ * @param {real} [y] The y positon to create the dock at.
+ * @param {real} [width] The width of the dock.
+ * @param {real} [height] The width of the dock.
+ * @return {real} The id of the created dock.
+ */
+var _dock = PEd_guiCreateCompoundShape(PEdGUIShape.Dock);
+if (argument_count == 4)
+{
+ PEd_guiShapeSetRectangle(_dock, argument[0], argument[1], argument[2], argument[3]);
+}
+PEd_guiShapeSetDepth(_dock, -16777216);
+_dock[? "splitSize"] = 0.5;
+_dock[? "splitType"] = PEdGUISplit.Horizontal;
+_dock[? "scrUpdate"] = PEd_guiDockUpdate;
+_dock[? "scrDraw"] = PEd_guiDockDraw;
+_dock[? "mouseOffset"] = 0;
+_dock[? "padding"] = 3;
+return _dock;
diff --git a/PushEd.gmx/scripts/PEd_guiCreateKeyboardShortcut.gml b/PushEd.gmx/scripts/PEd_guiCreateKeyboardShortcut.gml
new file mode 100644
index 00000000..6a47ebff
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiCreateKeyboardShortcut.gml
@@ -0,0 +1,10 @@
+/// PEd_guiCreateKeyboardShortcut(action)
+/**
+ * @brief Creates a new keyboard shortcut.
+ * @param {real} action The script that will be executed with the keyboard shortcut.
+ * @return {real} The id of the created keyboard shortcut.
+ */
+var _shortcut = ds_map_create();
+ds_map_add_list(_shortcut, "keys", ds_list_create());
+_shortcut[? "scrAction"] = argument0;
+return _shortcut;
diff --git a/PushEd.gmx/scripts/PEd_guiCreateMenuBar.gml b/PushEd.gmx/scripts/PEd_guiCreateMenuBar.gml
new file mode 100644
index 00000000..acfe2a87
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiCreateMenuBar.gml
@@ -0,0 +1,11 @@
+/// PEd_guiCreateMenuBar()
+/**
+ * @brief Creates a new menu bar.
+ * @return {real} The id of the created menu bar.
+ */
+var _menu = PEd_guiCreateCanvas(PEdGUIShape.MenuBar);
+PEd_guiShapeSetHeight(_menu, guiLineHeight);
+_menu[? "scrUpdate"] = PEd_guiMenuBarUpdate;
+_menu[? "scrDraw"] = PEd_guiMenuBarDraw;
+_menu[? "current"] = noone;
+return _menu;
diff --git a/PushEd.gmx/scripts/PEd_guiCreateMenuBarItem.gml b/PushEd.gmx/scripts/PEd_guiCreateMenuBarItem.gml
new file mode 100644
index 00000000..a5380b2a
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiCreateMenuBarItem.gml
@@ -0,0 +1,15 @@
+/// PEd_guiCreateMenuBarItem(name, scrContextMenu)
+/**
+ * @brief Creates a new menu bar item.
+ * @param {string} name The name of the item.
+ * @param {real} scrContextMenu The script that opens a context menu for this item or noone.
+ * @return {real} The id of the created menu bar item.
+ */
+var _item = PEd_guiCreateShape(PEdGUIShape.MenuBarItem);
+_item[? "name"] = argument0;
+_item[? "scrContextMenu"] = argument1;
+_item[? "scrUpdate"] = PEd_guiMenuBarItemUpdate;
+_item[? "scrDraw"] = PEd_guiMenuBarItemDraw;
+_item[? "index"] = noone;
+PEd_guiShapeSetHeight(_item, guiLineHeight);
+return _item;
diff --git a/PushEd.gmx/scripts/PEd_guiCreatePanel.gml b/PushEd.gmx/scripts/PEd_guiCreatePanel.gml
new file mode 100644
index 00000000..d08187d4
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiCreatePanel.gml
@@ -0,0 +1,18 @@
+/// PEd_guiCreatePanel(title)
+/**
+ * @brief Creates a new panel.
+ * @param {string} title The name of the panel.
+ * @return {real} The id of the created panel.
+ */
+var _panel = PEd_guiCreateCompoundShape(PEdGUIShape.Panel);
+var _titleBar = PEd_guiCreateContainer();
+var _container = PEd_guiCreateContainer();
+_panel[? "titleBar"] = _titleBar;
+_titleBar[? "title"] = argument0;
+_titleBar[? "content"] = PEd_guiContentTitleBar;
+PEd_guiCanvasSetBackground(_titleBar, PEdColour.WindowBorder);
+_panel[? "container"] = _container;
+PEd_guiAddItem(_panel, _titleBar);
+PEd_guiAddItem(_panel, _container);
+_panel[? "scrDraw"] = PEd_guiPanelDraw;
+return _panel;
diff --git a/PushEd.gmx/scripts/PEd_guiCreateScrollbar.gml b/PushEd.gmx/scripts/PEd_guiCreateScrollbar.gml
new file mode 100644
index 00000000..a36de6a1
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiCreateScrollbar.gml
@@ -0,0 +1,20 @@
+/// PEd_guiCreateScrollbar(delegate)
+/**
+ * @brief Creates a new scrollbar.
+ * @param {real} delegate The id of the scrollbars delegate.
+ * @return {real} The id of the created scrollbar.
+ */
+var _scrollbar = PEd_guiCreateShape(PEdGUIShape.Scrollbar);
+PEd_guiShapeSetDelegate(_scrollbar, argument0);
+PEd_guiShapeSetDepth(_scrollbar, 16777216);
+_scrollbar[? "sprite"] = noone;
+_scrollbar[? "spriteSize"] = 1;
+_scrollbar[? "contentSize"] = 0.1;
+_scrollbar[? "scrUpdate"] = PEd_guiScrollbarUpdate;
+_scrollbar[? "size"] = 1;
+_scrollbar[? "scroll"] = 0;
+_scrollbar[? "scrollJump"] = 1;
+_scrollbar[? "mouseOffset"] = 0;
+_scrollbar[? "minThumbSize"] = 12;
+_scrollbar[? "thumbSize"] = _scrollbar[? "minThumbSize"];
+return _scrollbar;
diff --git a/PushEd.gmx/scripts/PEd_guiCreateScrollbarHor.gml b/PushEd.gmx/scripts/PEd_guiCreateScrollbarHor.gml
new file mode 100644
index 00000000..2270e0cc
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiCreateScrollbarHor.gml
@@ -0,0 +1,14 @@
+/// PEd_guiCreateScrollbarHor(delegate)
+/**
+ * @brief Creates a new horizontal scrollbar.
+ * @param {real} delegate The id of the scrollbars delegate.
+ * @return {real} The id of the created scrollbar.
+ */
+var _scrollbarHor = PEd_guiCreateScrollbar(argument0);
+var _sprite = PEd_guiSprScrollbarHor;
+_scrollbarHor[? "sprite"] = _sprite;
+_scrollbarHor[? "spriteSize"] = sprite_get_width(_sprite);
+PEd_guiShapeSetHeight(_scrollbarHor, sprite_get_height(_sprite));
+_scrollbarHor[? "scrUpdate"] = PEd_guiScrollbarHorUpdate;
+_scrollbarHor[? "scrDraw"] = PEd_guiScrollbarHorDraw;
+return _scrollbarHor;
diff --git a/PushEd.gmx/scripts/PEd_guiCreateScrollbarVer.gml b/PushEd.gmx/scripts/PEd_guiCreateScrollbarVer.gml
new file mode 100644
index 00000000..8a651a25
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiCreateScrollbarVer.gml
@@ -0,0 +1,14 @@
+/// PEd_guiCreateScrollbarVer(delegate)
+/**
+ * @brief Creates a new horizontal scrollbar.
+ * @param {real} delegate The id of the scrollbars delegate.
+ * @return {real} The id of the created scrollbar.
+ */
+var _scrollbarVer = PEd_guiCreateScrollbar(argument0);
+var _sprite = PEd_guiSprScrollbarVer;
+_scrollbarVer[? "sprite"] = _sprite;
+_scrollbarVer[? "spriteSize"] = sprite_get_height(_sprite);
+PEd_guiShapeSetWidth(_scrollbarVer, sprite_get_width(_sprite));
+_scrollbarVer[? "scrUpdate"] = PEd_guiScrollbarVerUpdate;
+_scrollbarVer[? "scrDraw"] = PEd_guiScrollbarVerDraw;
+return _scrollbarVer;
diff --git a/PushEd.gmx/scripts/PEd_guiCreateShape.gml b/PushEd.gmx/scripts/PEd_guiCreateShape.gml
new file mode 100644
index 00000000..ab682e35
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiCreateShape.gml
@@ -0,0 +1,29 @@
+/// PEd_guiCreateShape([type])
+/**
+ * @brief Creates a new shape.
+ * @param {PEdGUIShape} [type] The type of the shape.
+ * @return {real} The id of the created shape.
+ */
+var _shape = ds_map_create();
+if (argument_count == 1)
+{
+ PEd_guiShapeSetType(_shape, argument[0]);
+}
+else
+{
+ PEd_guiShapeSetType(_shape, PEdGUIShape.Blank);
+}
+PEd_guiShapeSetDepth(_shape, noone);
+PEd_guiShapeSetDelegate(_shape, noone);
+PEd_guiShapeSetX(_shape, 0);
+PEd_guiShapeSetY(_shape, 0);
+PEd_guiShapeSetWidth(_shape, 1);
+PEd_guiShapeSetHeight(_shape, 1);
+PEd_guiShapeSetDepth(_shape, 0);
+PEd_guiShapeSetRedraw(_shape, true);
+
+_shape[? "scrUpdate"] = PEd_guiShapeUpdate;
+_shape[? "scrDraw"] = noone;
+_shape[? "scrCleanUp"] = PEd_guiShapeCleanUp;
+
+return _shape;
diff --git a/PushEd.gmx/scripts/PEd_guiCreateToolbarButton.gml b/PushEd.gmx/scripts/PEd_guiCreateToolbarButton.gml
new file mode 100644
index 00000000..5a789eb8
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiCreateToolbarButton.gml
@@ -0,0 +1,23 @@
+/// PEd_guiCreateToolbarButton(name, sprite, subimg, tooltip, action)
+/**
+ * @brief Creates a new toolbar button.
+ * @param {string} name The name of the button.
+ * @param {real} sprite The button sprite.
+ * @param {real} subimg The sprite subimage.
+ * @param {string} tooltip The tooltip text.
+ * @param {real} action The script that will be executed on click.
+ * @return {real} The id of the created toolbar button.
+ */
+var _tool = PEd_guiCreateShape(PEdGUIShape.ToolbarButton);
+_tool[? "name"] = argument0;
+_tool[? "sprite"] = argument1;
+_tool[? "subimage"] = argument2;
+_tool[? "tooltip"] = argument3;
+_tool[? "scrAction"] = argument4;
+_tool[? "scrUpdate"] = PEd_guiToolbarButtonUpdate;
+_tool[? "scrDraw"] = PEd_guiToolbarButtonDraw;
+PEd_guiShapeSetSize(_tool,
+ sprite_get_width(argument1),
+ sprite_get_height(argument1));
+_tool[? "highlight"] = false;
+return _tool;
diff --git a/PushEd.gmx/scripts/PEd_guiCreateViewport.gml b/PushEd.gmx/scripts/PEd_guiCreateViewport.gml
new file mode 100644
index 00000000..6d215794
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiCreateViewport.gml
@@ -0,0 +1,17 @@
+/// PEd_guiCreateViewport()
+/**
+ * @brief Creates a new viewport.
+ * @return {real} The id of the created viewport.
+ */
+var _viewport = PEd_guiCreateCompoundShape(PEdGUIShape.Viewport);
+_viewport[? "scrUpdate"] = PEd_guiViewportUpdate;
+_viewport[? "scrDraw"] = PEd_guiViewportDraw;
+
+var _titleBar = PEd_guiCreateContainer();
+_viewport[? "titleBar"] = _titleBar;
+_titleBar[? "title"] = "Viewport";
+_titleBar[? "content"] = PEd_guiContentTitleBar;
+PEd_guiCanvasSetBackground(_titleBar, PEdColour.WindowBorder);
+PEd_guiAddItem(_viewport, _titleBar);
+
+return _viewport;
diff --git a/PushEd.gmx/scripts/PEd_guiCreateWindow.gml b/PushEd.gmx/scripts/PEd_guiCreateWindow.gml
new file mode 100644
index 00000000..200fc82d
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiCreateWindow.gml
@@ -0,0 +1,26 @@
+/// PEd_guiCreateWindow(title)
+/**
+ * @brief Creates a new window.
+ * @param {string} title The window title.
+ * @return {real} The id of the created window.
+ */
+var _window = PEd_guiCreateCompoundShape(PEdGUIShape.Window);
+var _titleBar = PEd_guiCreateContainer();
+var _container = PEd_guiCreateContainer();
+_window[? "titleBar"] = _titleBar;
+_titleBar[? "title"] = argument0;
+_titleBar[? "content"] = PEd_guiContentTitleBarWindow;
+PEd_guiCanvasSetBackground(_titleBar, PEdColour.WindowBorder);
+_window[? "container"] = _container;
+PEd_guiAddItem(_window, _titleBar);
+PEd_guiAddItem(_window, _container);
+_window[? "scrUpdate"] = PEd_guiWindowUpdate;
+_window[? "scrDraw"] = PEd_guiWindowDraw;
+_window[? "resize"] = PEdGUIResize.None;
+_window[? "drag"] = false;
+_window[? "mouseOffsetX"] = 0;
+_window[? "mouseOffsetY"] = 0;
+_window[? "border"] = 4;
+PEd_guiShapeSetSize(_window, 300, 200);
+PEd_guiShapeSetDepth(_window, 16777216);
+return _window;
diff --git a/PushEd.gmx/scripts/PEd_guiDecodeID.gml b/PushEd.gmx/scripts/PEd_guiDecodeID.gml
new file mode 100644
index 00000000..043f29b5
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiDecodeID.gml
@@ -0,0 +1,8 @@
+/// PEd_guiDecodeID(id)
+/**
+ * @brief Gets the dynamic shape delegate from its id.
+ * @param {real} id The id of the dynamic shape.
+ * @return {real} The id of the delegate.
+ */
+gml_pragma("forceinline");
+return ((argument0 div 100000) - 1);
diff --git a/PushEd.gmx/scripts/PEd_guiDestroy.gml b/PushEd.gmx/scripts/PEd_guiDestroy.gml
new file mode 100644
index 00000000..a12d8185
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiDestroy.gml
@@ -0,0 +1,14 @@
+/// PEd_guiDestroy()
+/**
+ * @brief Destroys GUI.
+ */
+for (var i = 0; i < ds_list_size(guiShapes); i++)
+{
+ var _shape = guiShapes[| i];
+ var _scrCleanUp = _shape[? "scrCleanUp"];
+ if (_scrCleanUp != noone)
+ {
+ script_execute(_scrCleanUp, _shape);
+ }
+}
+ds_list_destroy(guiShapes);
diff --git a/PushEd.gmx/scripts/PEd_guiDestroyShape.gml b/PushEd.gmx/scripts/PEd_guiDestroyShape.gml
new file mode 100644
index 00000000..cdc28ff3
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiDestroyShape.gml
@@ -0,0 +1,10 @@
+/// PEd_guiDestroyShape(shape)
+/**
+ * @brief Destroys the shape.
+ * @param {real} shape The id of the shape.
+ */
+gml_pragma("forceinline");
+if (PEd_guiShapeExists(argument0))
+{
+ ds_stack_push(guiDestroyStack, argument0);
+}
diff --git a/PushEd.gmx/scripts/PEd_guiDockDraw.gml b/PushEd.gmx/scripts/PEd_guiDockDraw.gml
new file mode 100644
index 00000000..a39b02ce
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiDockDraw.gml
@@ -0,0 +1,77 @@
+/// PEd_guiDockDraw(dock)
+/**
+ * @brief Draws the dock.
+ * @param {real} dock The id of the dock.
+ */
+var _dock = argument0;
+var _x = PEd_guiShapeGetX(_dock);
+var _y = PEd_guiShapeGetY(_dock);
+var _items = PEd_guiCompoundShapeGetItems(_dock);
+var _itemCount = ds_list_size(_items);
+
+PEd_guiMatrixPush(_x, _y);
+
+if (_itemCount == 1)
+{
+ var _item = _items[| 0];
+ PEd_guiShapeSetSize(_item,
+ PEd_guiShapeGetWidth(_dock),
+ PEd_guiShapeGetHeight(_dock));
+ PEd_guiDrawItem(_item, 0, 0);
+}
+else if (_itemCount == 2)
+{
+ var _width = PEd_guiShapeGetWidth(_dock);
+ var _height = PEd_guiShapeGetHeight(_dock);
+ var _splitType = _dock[? "splitType"];
+ var _splitSize = _dock[? "splitSize"];
+ var _padding = _dock[? "padding"];
+ var _middle;
+
+ if (_splitType == PEdGUISplit.Horizontal)
+ {
+ _middle = round(_width * _splitSize);
+ }
+ else
+ {
+ _middle = round(_height * _splitSize);
+ }
+
+ // Left
+ var _left = _items[| 0];
+ if (_splitType == PEdGUISplit.Horizontal)
+ {
+ PEd_guiShapeSetSize(_left,
+ _middle - _padding,
+ _height);
+ }
+ else
+ {
+ PEd_guiShapeSetSize(_left,
+ _width,
+ _middle - _padding);
+ }
+ PEd_guiDrawItem(_left, 0, 0);
+
+ // Right
+ var _right = _items[| 1];
+ if (_splitType == PEdGUISplit.Horizontal)
+ {
+ PEd_guiShapeSetRectangle(_right,
+ _middle + _padding,
+ 0,
+ _width - _middle - _padding,
+ _height);
+ }
+ else
+ {
+ PEd_guiShapeSetRectangle(_right,
+ 0,
+ _middle + _padding,
+ _width,
+ _height - _middle - _padding);
+ }
+ PEd_guiDrawItem(_right);
+}
+
+PEd_guiMatrixRestore();
diff --git a/PushEd.gmx/scripts/PEd_guiDockUpdate.gml b/PushEd.gmx/scripts/PEd_guiDockUpdate.gml
new file mode 100644
index 00000000..da82dbf0
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiDockUpdate.gml
@@ -0,0 +1,77 @@
+/// PEd_guiDockUpdate(dock)
+/**
+ * @brief Updates the dock.
+ * @param {real} dock The id of the dock.
+ */
+var _dock = argument0;
+PEd_guiCompoundShapeUpdate(_dock);
+
+// Start resizing
+if (!PEd_guiShapeExists(guiShapeActive)
+ && PEd_guiShapeIsHovered(_dock))
+{
+ var _x = PEd_guiShapeGetX(_dock);
+ var _y = PEd_guiShapeGetY(_dock);
+ var _width = PEd_guiShapeGetWidth(_dock);
+ var _height = PEd_guiShapeGetHeight(_dock);
+ var _splitType = _dock[? "splitType"];
+ var _splitSize = _dock[? "splitSize"];
+ var _padding = _dock[? "padding"];
+ var _middle;
+
+ if (_splitType == PEdGUISplit.Horizontal)
+ {
+ _middle = round(_width * _splitSize);
+
+ // Horizontally
+ if (guiMouseX >= _middle - _padding
+ && guiMouseX < _middle + _padding)
+ {
+ guiCursor = cr_size_we;
+ if (mouse_check_button_pressed(mb_left))
+ {
+ guiShapeActive = _dock;
+ _dock[? "mouseOffset"] = _middle - guiMouseX;
+ }
+ }
+ }
+ else
+ {
+ _middle = round(_height * _splitSize);
+
+ // Vertically
+ if (guiMouseY >= _middle - _padding
+ && guiMouseY < _middle + _padding)
+ {
+ guiCursor = cr_size_ns;
+ if (mouse_check_button_pressed(mb_left))
+ {
+ guiShapeActive = _dock;
+ _dock[? "mouseOffset"] = _middle - guiMouseY;
+ }
+ }
+ }
+}
+
+// Resize
+if (guiShapeActive == _dock)
+{
+ if (mouse_check_button(mb_left))
+ {
+ if (_dock[? "splitType"] == PEdGUISplit.Horizontal)
+ {
+ _dock[? "splitSize"] = (guiMouseX + _dock[? "mouseOffset"]) / PEd_guiShapeGetWidth(_dock);
+ guiCursor = cr_size_we;
+ }
+ else
+ {
+ _dock[? "splitSize"] = (guiMouseY + _dock[? "mouseOffset"]) / PEd_guiShapeGetHeight(_dock);
+ guiCursor = cr_size_ns;
+ }
+ _dock[? "splitSize"] = clamp(_dock[? "splitSize"], 0.1, 0.9);
+ }
+ else
+ {
+ guiShapeActive = noone;
+ }
+}
diff --git a/PushEd.gmx/scripts/PEd_guiDraw.gml b/PushEd.gmx/scripts/PEd_guiDraw.gml
new file mode 100644
index 00000000..13c43bf7
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiDraw.gml
@@ -0,0 +1,130 @@
+/// PEd_guiDraw()
+/**
+ * @brief Draws GUI.
+ */
+colorShow = -1;
+draw_clear(PEdColour.Background);
+draw_set_font(PEd_fntNormal);
+draw_set_colour(c_white);
+
+var _mouseX = window_mouse_get_x();
+var _mouseY = window_mouse_get_y();
+var _guiWidth = window_get_width();
+var _guiHeight = window_get_height();
+
+// Draw items
+var _items = PEd_guiCompoundShapeGetItems(guiRoot);
+for (var i = 0; i < ds_list_size(_items); i++)
+{
+ var _item = _items[| i];
+ if (PEd_guiShapeExists(_item))
+ {
+ PEd_guiDrawItem(_item);
+ }
+ else
+ {
+ ds_list_delete(_items, i--);
+ }
+}
+
+// Draw hovered background
+if (mouse_check_button_released(mb_left)
+ && guiDnDBackground != -1)
+{
+ PEd_guiRequestRedrawAll(guiRoot)
+ guiDnDBackground = -1;
+}
+
+var _bkg = max(bkgShow, guiDnDBackground);
+if (_bkg != -1)
+{
+ var _bkgW = background_get_width(_bkg);
+ var _bkgH = background_get_height(_bkg);
+ var _aspect = _bkgW / _bkgH;
+ var _w = 64;
+ var _h = 64 / _aspect;
+ var _x = min(_mouseX + 24, _guiWidth - _w - 8);
+ var _y = min(_mouseY + 24, _guiHeight - _h - 8);
+
+ if (_bkg == guiDnDBackground)
+ {
+ draw_background_stretched_ext(_bkg, _x, _y, _w, _h, c_white, 0.5);
+ }
+ else
+ {
+ PEd_guiDrawRectangle(_x - 8, _y - 8, _w + 16, _h + 16, PEdColour.WindowBorder);
+ PEd_guiDrawRectangle(_x - 7, _y - 7, _w + 14, _h + 14, PEdColour.WindowBackground);
+ draw_background_stretched(_bkg, _x, _y, _w, _h);
+ PEd_guiDrawTextShadow(_x, _y, string(_bkgW) + "x" + string(_bkgH), PEdColour.Text, c_black);
+ }
+}
+
+// Draw hovered color
+if (colorShow != -1)
+{
+ var _bkgW = background_get_width(_bkg);
+ var _bkgH = background_get_height(_bkg);
+ var _aspect = _bkgW / _bkgH;
+ var _w = 64;
+ var _h = 64 / _aspect;
+ var _x = min(_mouseX + 24, _guiWidth - _w - 8);
+ var _y = min(_mouseY + 24, _guiHeight - _h - 8);
+
+ PEd_guiDrawRectangle(_x - 8, _y - 8, _w + 16, _h + 16, PEdColour.WindowBorder);
+ PEd_guiDrawRectangle(_x - 7, _y - 7, _w + 14, _h + 14, PEdColour.WindowBackground);
+ PEd_guiDrawRectangle(_x, _y, _w, _h, colorShow);
+ PEd_guiDrawTextShadow(_x, _y, colorShow, PEdColour.Text, c_black);
+}
+
+// Popup message
+if (guiPopupMessage != "")
+{
+ if (current_time - guiPopupTimer < guiPopupDuration)
+ {
+ PEd_guiDrawPopupMessage(viewportX + viewportWidth, viewportY + viewportHeight, guiPopupMessage);
+ }
+ else
+ {
+ guiPopupTimer = noone;
+ }
+}
+
+// Draw tooltip
+if (string_length(guiTooltip) > 0)
+{
+ PEd_guiDrawTooltip(_mouseX + 16, _mouseY + 16, guiTooltip);
+}
+
+// Draw debug
+/*PEd_guiDrawTextShadow(_mouseX + 16, _mouseY + 64,
+ "hoveredShape = " + string(guiShapeHovered)
+ + "#activeShape = " + string(guiShapeActive)
+ + "#mousePos = " + string(PEd_vec2(guiMouseX, guiMouseY)),
+ c_yellow, c_black);*/
+
+// Set cursor
+if (_mouseX > 2
+ && _mouseY > 2
+ && _mouseX < window_get_width() - 2
+ && _mouseY < window_get_height() - 2)
+{
+ window_set_cursor(guiCursor);
+}
+guiCursor = cr_default;
+
+mouseLastX = _mouseX;
+mouseLastY = _mouseY;
+
+// Handle destroy requests
+while (!ds_stack_empty(guiDestroyStack))
+{
+ var _shape = ds_stack_pop(guiDestroyStack);
+ if (PEd_guiShapeExists(_shape))
+ {
+ script_execute(_shape[? "scrCleanUp"], _shape);
+ if (guiShapeActive == _shape)
+ {
+ guiShapeActive = noone;
+ }
+ }
+}
diff --git a/PushEd.gmx/scripts/PEd_guiDrawAlphaMix.gml b/PushEd.gmx/scripts/PEd_guiDrawAlphaMix.gml
new file mode 100644
index 00000000..74ce6f7f
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiDrawAlphaMix.gml
@@ -0,0 +1,50 @@
+/// PEd_guiDrawAlphaMix(x, y, alpha)
+/**
+ * @brief Draws an alpha mix at the given position.
+ * @param {real} x The x position to draw the alpha mix at.
+ * @param {real} y The y position to draw the alpha mix at.
+ * @param {real} alpha The alpha value to mix.
+ * @return {real} The new mixed alpha.
+ */
+var _container = guiShapeFilling;
+var _containerWidth = PEd_guiShapeGetWidth(_container);
+var _x = argument0;
+var _y = argument1;
+var _alpha = argument2;
+
+// Check whether the sliders can be edited
+var _edit = (mouse_check_button(mb_left)
+ && PEd_guiShapeIsHovered(_container)
+ && guiDnDObject == -1
+ && pedDnDInstance == noone
+ && guiDnDBackground == -1
+ && editNow == -1
+ && guiInputActive == noone);
+
+// Alpha
+var _sliderWidth = _containerWidth - _x * 2;
+PEd_guiDrawRectangle(_x, _y, _sliderWidth, guiLineHeight, PEdColour.Input);
+PEd_guiDrawRectangle(_x, _y, _sliderWidth * _alpha, guiLineHeight, merge_colour(c_black, c_white, _alpha));
+
+if (_edit)
+{
+ if (guiMouseX >= _x - 2
+ && guiMouseY > _y
+ && guiMouseX <= _x + _sliderWidth + 2
+ && guiMouseY < _y + guiLineHeight)
+ {
+ _alpha = max(0, min((guiMouseX - _x) / (_containerWidth - _x * 2), 1));
+ }
+}
+_y += guiLineHeight + 4;
+
+// Inputs
+iw = round(_containerWidth - _x * 2);
+var _input = PEd_guiDrawInput(_x, _y, iw, _alpha, false);
+if (!is_undefined(_input))
+{
+ _alpha = max(0, min(_input, 1));
+}
+
+// Return alpha
+return _alpha;
diff --git a/PushEd.gmx/scripts/PEd_guiDrawBackgroundInput.gml b/PushEd.gmx/scripts/PEd_guiDrawBackgroundInput.gml
new file mode 100644
index 00000000..36e4c531
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiDrawBackgroundInput.gml
@@ -0,0 +1,51 @@
+/// PEd_guiDrawBackgroundInput(x, y, width, background)
+/**
+ * @brief Draws a D&D input for a background.
+ * @param {real} x The x position to draw the input at.
+ * @param {real} y The y position to draw the input at.
+ * @param {real} width The width of the input.
+ * @param {real} background The current background.
+ * @return {real} The new background or -1.
+ */
+var _delegate = guiShapeFilling;
+var _x = argument0;
+var _y = argument1;
+var _width = argument2;
+var _background = argument3;
+var _mouseOver = (PEd_guiShapeIsHovered(_delegate)
+ && guiMouseX > _x
+ && guiMouseY > _y
+ && guiMouseX < _x + _width
+ && guiMouseY < _y + guiLineHeight);
+
+PEd_guiDrawInput(_x, _y, _width, background_get_name(_background), true);
+
+if (guiDnDBackground != -1)
+{
+ PEd_guiDrawRectangle(_x, _y, _width, guiLineHeight, PEdColour.Active, 0.5);
+}
+
+draw_sprite(PEd_guiSprMisc, 3, _x + _width - 20, _y);
+
+if (_mouseOver)
+{
+ guiTooltip = "Drag and drop background here from the Content Browser.";
+
+ if (mouse_check_button_released(mb_left)
+ && guiDnDBackground != -1)
+ {
+ var _temp = guiDnDBackground;
+ guiDnDBackground = -1;
+ return _temp;
+ }
+
+ if (guiMouseX > _x + _width - 20)
+ {
+ guiTooltip = "Remove background.";
+ if (mouse_check_button_pressed(mb_left))
+ {
+ return -1;
+ }
+ }
+}
+return _background;
diff --git a/PushEd.gmx/scripts/PEd_guiDrawCheckbox.gml b/PushEd.gmx/scripts/PEd_guiDrawCheckbox.gml
new file mode 100644
index 00000000..f101b601
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiDrawCheckbox.gml
@@ -0,0 +1,31 @@
+/// PEd_guiDrawCheckbox(x, y, state)
+/**
+ * @brief Draws a checkbox at the given position.
+ * @param {real} x The x position to draw the checkbox at.
+ * @param {real} y The y position to draw the checkbox at.
+ * @param {bool} state The current state of the checkbox.
+ * @return {bool} Return the new state of the checkbox.
+ */
+var _delegate = guiShapeFilling;
+var _sprite = PEd_guiSprCheckbox;
+var _state = argument2;
+var _width = sprite_get_width(_sprite);
+var _height = sprite_get_height(_sprite);
+var _x = argument0;
+var _y = argument1 + round((guiLineHeight - _height) * 0.5);
+var _mouseOver = (PEd_guiShapeIsHovered(_delegate)
+ && guiMouseX > _x
+ && guiMouseY > _y
+ && guiMouseX < _x + _width
+ && guiMouseY < _y + _height);
+draw_sprite_ext(_sprite, 0, _x, _y, 1, 1, 0, PEdColour.Input, 1);
+if (_state)
+{
+ draw_sprite_ext(_sprite, 1, _x, _y, 1, 1, 0, PEdColour.Text, 1);
+}
+if (mouse_check_button_pressed(mb_left)
+ && _mouseOver)
+{
+ return !_state;
+}
+return _state;
diff --git a/PushEd.gmx/scripts/PEd_guiDrawColourInput.gml b/PushEd.gmx/scripts/PEd_guiDrawColourInput.gml
new file mode 100644
index 00000000..5abeee1c
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiDrawColourInput.gml
@@ -0,0 +1,47 @@
+/// PEd_guiDrawColourInput(x, y, width, colour, alpha)
+/**
+ * @brief Draws a colour input at the given position.
+ * @param {real} x The x position to draw the input at.
+ * @param {real} y The y position to draw the input at.
+ * @param {real} width The width of the input.
+ * @param {real} colour The current colour.
+ * @param {real} alpha The current alpha.
+ * @return {real} The id of a colour picker on click or noone.
+ */
+var _delegate = guiShapeFilling;
+var _x = argument0;
+var _xStart = _x;
+var _y = argument1;
+var _yStart = _y;
+var _width = argument2;
+var _colour = argument3;
+var _alpha = argument4;
+var _mouseOver = (PEd_guiShapeIsHovered(_delegate)
+ && guiMouseX > _x
+ && guiMouseY > _y
+ && guiMouseX < _x + _width
+ && guiMouseY < _y + guiInputSpriteHeight);
+
+draw_sprite_ext(PEd_guiSprInput, 0, _x, _y, 1, 1, 0, _colour, 1);
+var _w = round((_width - guiInputSpriteWidth * 2) * 0.5);
+draw_sprite_stretched_ext(PEd_guiSprInput, 1, _x + guiInputSpriteWidth, _y, _w, guiInputSpriteHeight, _colour, 1);
+draw_sprite_stretched_ext(PEd_guiSprInput, 1, _x + guiInputSpriteWidth + _w, _y, _w, guiInputSpriteHeight, c_black, 1);
+draw_sprite_stretched_ext(PEd_guiSprInput, 1, _x + guiInputSpriteWidth + _w, _y, _w, guiInputSpriteHeight, c_white, _alpha);
+draw_sprite_ext(PEd_guiSprInput, 2, _x + guiInputSpriteWidth + _w * 2, _y, 1, 1, 0, c_black, 1);
+draw_sprite_ext(PEd_guiSprInput, 2, _x + guiInputSpriteWidth + _w * 2, _y, 1, 1, 0, c_white, _alpha);
+
+if (mouse_check_button_pressed(mb_left)
+ && _mouseOver)
+{
+ var _colourPicker = PEd_guiCreateWindow("Colour Picker");
+ PEd_guiShapeSetPosition(_colourPicker,
+ round((windowWidth - PEd_guiShapeGetWidth(_colourPicker)) * 0.5),
+ round((windowHeight - PEd_guiShapeGetHeight(_colourPicker)) * 0.5));
+ var _colourPickerContainer = PEd_guiWindowGetContainer(_colourPicker);
+ _colourPickerContainer[? "colour"] = argument3;
+ _colourPickerContainer[? "alpha"] = argument4;
+ PEd_guiWindowSetContent(_colourPicker, PEd_guiContentColourPicker);
+ PEd_guiAddItem(guiRoot, _colourPicker);
+ return _colourPicker;
+}
+return noone;
diff --git a/PushEd.gmx/scripts/PEd_guiDrawColourMix.gml b/PushEd.gmx/scripts/PEd_guiDrawColourMix.gml
new file mode 100644
index 00000000..2e070cae
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiDrawColourMix.gml
@@ -0,0 +1,106 @@
+/// PEd_guiDrawColourMix(x, y, colour)
+/**
+ * @brief Draws a colour mix at the given position.
+ * @param {real} x The x position to draw the colour mix at.
+ * @param {real} y The y position to draw the colour mix at.
+ * @param {real} colour The colour to mix.
+ * @return {real} The new mixed colour.
+ */
+var _x = argument0;
+var _y = argument1;
+var _colour = argument2;
+var _container = guiShapeFilling;
+var _containerWidth = PEd_guiShapeGetWidth(_container);
+var _red = colour_get_red(_colour);
+var _green = colour_get_green(_colour);
+var _blue = colour_get_blue(_colour);
+
+// Check whether the sliders can be edited
+var _edit = (PEd_guiShapeIsHovered(_container)
+ && guiDnDObject == -1
+ && pedDnDInstance == noone
+ && guiDnDBackground == -1
+ && editNow == -1
+ && guiInputActive == noone);
+
+// Red
+var _sliderWidth = _containerWidth - _x * 2;
+PEd_guiDrawRectangle(_x, _y, _sliderWidth, guiLineHeight, PEdColour.Input);
+PEd_guiDrawRectangle(_x, _y, _sliderWidth * (_red / 255), guiLineHeight, make_colour_rgb(_red, 0, 0));
+if (_edit)
+{
+ if (guiMouseX >= _x - 2
+ && guiMouseY > _y
+ && guiMouseX <= _x + _sliderWidth + 2
+ && guiMouseY < _y + guiLineHeight)
+ {
+ if (mouse_check_button(mb_left))
+ {
+ _red = round(clamp((guiMouseX - _x) / (_containerWidth - _x * 2), 0, 1) * 255);
+ }
+ colorShow = make_colour_rgb(_red, _green, _blue);
+ }
+}
+_y += guiLineHeight + 4;
+
+// Green
+PEd_guiDrawRectangle(_x, _y, _sliderWidth, guiLineHeight, PEdColour.Input);
+PEd_guiDrawRectangle(_x, _y, _sliderWidth * (_green / 255), guiLineHeight, make_colour_rgb(0, _green, 0));
+if (_edit)
+{
+ if (guiMouseX >= _x - 2
+ && guiMouseY > _y
+ && guiMouseX <= _x + _sliderWidth + 2
+ && guiMouseY < _y + guiLineHeight)
+ {
+ if (mouse_check_button(mb_left))
+ {
+ _green = round(clamp((guiMouseX - _x) / (_containerWidth - _x * 2), 0, 1) * 255);
+ }
+ colorShow = make_colour_rgb(_red, _green, _blue);
+ }
+}
+_y += guiLineHeight + 4;
+
+// Blue
+PEd_guiDrawRectangle(_x, _y, _sliderWidth, guiLineHeight, PEdColour.Input);
+PEd_guiDrawRectangle(_x, _y, _sliderWidth * (_blue / 255), guiLineHeight, make_colour_rgb(0, 0, _blue));
+if (_edit)
+{
+ if (guiMouseX >= _x - 2
+ && guiMouseY > _y
+ && guiMouseX <= _x + _sliderWidth + 2
+ && guiMouseY < _y + guiLineHeight)
+ {
+ if (mouse_check_button(mb_left))
+ {
+ _blue = round(clamp((guiMouseX - _x) / (_containerWidth - _x * 2), 0, 1) * 255);
+ }
+ colorShow = make_colour_rgb(_red, _green, _blue);
+ }
+}
+_y += guiLineHeight + 4;
+
+// Inputs
+var _input;
+var _inputWidth = round((_sliderWidth - 16) / 3);
+
+_input = PEd_guiDrawInput(_x, _y, _inputWidth, _red);
+if (!is_undefined(_input))
+{
+ _red = clamp(round(_input), 0, 255);
+}
+
+_input = PEd_guiDrawInput(_x + _inputWidth + 8, _y, _inputWidth, _green);
+if (!is_undefined(_input))
+{
+ _green = clamp(round(_input), 0, 255);
+}
+
+_input = PEd_guiDrawInput(_x + _inputWidth * 2 + 16, _y, _inputWidth, _blue);
+if (!is_undefined(_input))
+{
+ _blue = clamp(round(_input), 0, 255);
+}
+
+return make_colour_rgb(_red, _green, _blue);
diff --git a/PushEd.gmx/scripts/PEd_guiDrawInput.gml b/PushEd.gmx/scripts/PEd_guiDrawInput.gml
new file mode 100644
index 00000000..5ddbbf53
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiDrawInput.gml
@@ -0,0 +1,220 @@
+/// PEd_guiDrawInput(x, y, width, value, [disabled, [defaultValue]])
+/**
+ * @brief Draws an input at the given position.
+ * @param {real} x The x position to draw the input at.
+ * @param {real} y The y position to draw the input at.
+ * @param {real} width The width of the input.
+ * @param {real/string} value The value in the input.
+ * @param {bool} [disabled] True to disable editing the input value.
+ * @param {real/string} defaultValue The value to draw when the value is an empty string.
+ * @return {real/string/undefined} The new input value when done editing or undefined while
+ * editing.
+ */
+var _padding = ceil(guiFontWidth * 0.5) + 1;
+var _id = PEd_guiEncodeID(guiShapeFilling, guiShapeId++);
+var _delegate = guiShapeFilling;
+var _x = argument[0];
+var _xStart = _x;
+var _y = argument[1];
+var _yStart = _y;
+var _width = argument[2];
+var _active = (guiInputActive == _id);
+
+var _value;
+if (guiInputActive == _id)
+{
+ _value = guiInputString;
+}
+else
+{
+ _value = string(argument[3]);
+}
+var _type = is_real(argument[3]);
+var _stringLength = string_length(_value);
+var _mouseOver = (PEd_guiShapeIsHovered(_delegate)
+ && guiMouseX > _x
+ && guiMouseY > _y
+ && guiMouseX < _x + _width
+ && guiMouseY < _y + guiInputSpriteHeight);
+var _maxCharCount = floor((_width - _padding * 2 ) / guiFontWidth);
+
+var _disabled = false;
+if (argument_count > 4)
+{
+ _disabled = argument[4];
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Draw input
+//
+
+// Background
+draw_sprite_ext(guiInputSprite, 0, _x, _y, 1, 1, 0, PEdColour.Input, 1);
+draw_sprite_stretched_ext(guiInputSprite, 1, _x + guiInputSpriteWidth, _y,
+ _width - guiInputSpriteWidth * 2, guiInputSpriteHeight, PEdColour.Input, 1);
+draw_sprite_ext(guiInputSprite, 2, _x + _width - guiInputSpriteWidth, _y, 1, 1, 0, PEdColour.Input, 1);
+
+// Text
+var _textX = _x + _padding;
+var _textY = _y + round((guiInputSpriteHeight - guiFontHeight) * 0.5);
+var _maxCharCount = floor((_width - _padding * 2) / guiFontWidth);
+var _colSelection = PEdColour.Active;
+
+if (_mouseOver)
+{
+ guiCursor = cr_beam;
+}
+
+if (_active)
+{
+ if (guiInputIndex[1] - guiInputDrawIndexStart > _maxCharCount)
+ {
+ guiInputDrawIndexStart += guiInputIndex[1] - guiInputDrawIndexStart - _maxCharCount;
+ }
+ else if (guiInputDrawIndexStart > guiInputIndex[1])
+ {
+ guiInputDrawIndexStart -= guiInputDrawIndexStart - guiInputIndex[1];
+ }
+
+ _value = string_copy(_value, guiInputDrawIndexStart, _maxCharCount);
+
+ if (guiInputIndex[0] == guiInputIndex[1])
+ {
+ // Beam
+ var _beamX = _textX + guiFontWidth * (guiInputIndex[0] - guiInputDrawIndexStart);
+ draw_text(_textX, _textY, _value);
+ PEd_guiDrawRectangle(_beamX, _textY, 1, guiFontHeight, _colSelection);
+ }
+ else
+ {
+ // Selection
+ //var _stringLength = string_length(_value);
+ var _minIndex = clamp(min(guiInputIndex[0], guiInputIndex[1]) - guiInputDrawIndexStart, 0, _stringLength);
+ var _maxIndex = clamp(max(guiInputIndex[0], guiInputIndex[1]) - guiInputDrawIndexStart, 0, _stringLength);
+ var _rectMinX = _textX + guiFontWidth * _minIndex;
+ var _rectMaxX = _textX + guiFontWidth * _maxIndex;
+
+ draw_text(_textX, _textY, string_copy(_value, 1, _minIndex)); // Text before selection
+ PEd_guiDrawRectangle(_rectMinX, _textY, _rectMaxX - _rectMinX, guiFontHeight, _colSelection); // Selection rectangle
+ draw_text_colour(_rectMinX, _textY, string_copy(_value, _minIndex + 1, _maxIndex - _minIndex), // Selected text
+ PEdColour.TextSelected, PEdColour.TextSelected, PEdColour.TextSelected, PEdColour.TextSelected, 1);
+ draw_text(_rectMaxX, _textY, string_delete(_value, 1, _maxIndex)); // Text after selection
+ }
+}
+else
+{
+ var _drawValue = _value;
+ if (argument_count > 5
+ && _value == "")
+ {
+ _drawValue = argument[5];
+ }
+ PEd_guiDrawTextPart(_textX, _textY, _drawValue, _maxCharCount * guiFontWidth);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Input logic
+//
+if (mouse_check_button_pressed(mb_left)
+ || mouse_check_button_pressed(mb_right))
+{
+ // Select input
+ if (_mouseOver
+ && !_disabled)
+ {
+ if (guiInputActive == noone)
+ {
+ guiInputActive = _id;
+ guiInputString = _value;
+ guiInputDrawIndexStart = 1;
+ guiInputIndex[0] = 1;
+ guiInputIndex[1] = 1;
+ keyboard_string = "";
+ }
+ }
+ else if (_active
+ && (!PEd_guiShapeExists(guiContextMenu)
+ || (guiContextMenu != noone
+ && !PEd_guiShapeDelegatesRecursive(guiContextMenu, guiShapeHovered))))
+ {
+ // Return value when clicked outside of the input
+ guiInputActive = noone;
+ if (PEd_guiShapeExists(_delegate))
+ {
+ PEd_guiRequestRedraw(_delegate);
+ }
+ if (_type)
+ {
+ return real(_value);
+ }
+ return _value;
+ }
+}
+
+if (_active)
+{
+ // Select text
+ if (mouse_check_button(mb_left)
+ && _mouseOver)
+ {
+ var _index = clamp(round((guiMouseX - _xStart - _padding) / guiFontWidth) + guiInputDrawIndexStart, 1, _stringLength + 1);
+ if (mouse_check_button_pressed(mb_left))
+ {
+ guiInputIndex[0] = _index;
+ }
+ guiInputIndex[1] = _index;
+ }
+ else if (mouse_check_button_pressed(mb_right)
+ && _mouseOver)
+ {
+ // Open context menu
+ var _contextMenu = PEd_guiCreateContextMenu();
+ PEd_guiMenuInput(_contextMenu);
+ PEd_guiShowContextMenu(_contextMenu);
+
+ // TODO: Select word in input on double click
+ /*var _index = clamp(round((guiMouseX - _xStart - _padding) / guiFontWidth) + guiInputDrawIndexStart, 1, _stringLength + 1);
+ var i, _char;
+ _char = string_char_at(_value, _index);
+ if (_char != " ")
+ {
+ for (i = _index; i > 1; i--)
+ {
+ _char = string_char_at(_value, i);
+ if (_char == " ")
+ {
+ i++;
+ break;
+ }
+ }
+ guiInputIndex[0] = i;
+
+ for (i = _index; i < _stringLength + 1; i++)
+ {
+ _char = string_char_at(_value, i);
+ if (_char == " ")
+ break;
+ }
+ guiInputIndex[1] = i;
+ }*/
+ }
+
+ // Return value when enter is pressed
+ if (keyboard_check_pressed(vk_enter))
+ {
+ guiInputActive = noone;
+ if (PEd_guiShapeExists(_delegate))
+ {
+ PEd_guiRequestRedraw(_delegate);
+ }
+ if (_type)
+ {
+ return real(_value);
+ }
+ return _value;
+ }
+}
+
+return undefined;
diff --git a/PushEd.gmx/scripts/PEd_guiDrawItem.gml b/PushEd.gmx/scripts/PEd_guiDrawItem.gml
new file mode 100644
index 00000000..f6a81892
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiDrawItem.gml
@@ -0,0 +1,29 @@
+/// PEd_guiDrawItem(item, [x, y])
+/**
+ * @brief Draws the item.
+ * @param {real} item The id of the item.
+ * @param {real} [x] The x position to draw the item at.
+ * @param {real} [y] The y position to draw the item at.
+ */
+var _item = argument[0];
+
+// Set position if passed
+if (argument_count == 3)
+{
+ PEd_guiShapeSetPosition(_item, argument[1], argument[2]);
+}
+
+// Update
+var _scrUpdate = _item[? "scrUpdate"];
+if (_scrUpdate != noone)
+{
+ script_execute(_scrUpdate, _item);
+}
+
+// Draw
+var _scrDraw = _item[? "scrDraw"];
+if (_scrDraw != noone)
+{
+ script_execute(_scrDraw, _item);
+}
+PEd_guiShapeSetRedraw(_item, false);
diff --git a/PushEd.gmx/scripts/PEd_guiDrawListItem.gml b/PushEd.gmx/scripts/PEd_guiDrawListItem.gml
new file mode 100644
index 00000000..f7b143fb
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiDrawListItem.gml
@@ -0,0 +1,78 @@
+/// PEd_guiDrawListItem(name, x, y, active, disabled, [highlighted])
+/**
+ * @brief Draws a list item on the given position.
+ * @param {string} name The item name.
+ * @param {real} x The x position to draw the item at.
+ * @param {real} y The y position to draw the item at.
+ * @param {bool} active True if the item is currently selected.
+ * @param {bool} disabled True to disable clicking on the item.
+ * @param {bool} [highlighted] True to highlight the item.
+ * @return {real} If the item is clicked, then 1 is returned.
+ * If the mouse cursor is currently over the item, then -1 is returned.
+ * In all other cases returns 0.
+ */
+var _container = guiShapeFilling;
+var _containerWidth = PEd_guiShapeGetWidth(_container);
+var _text = string(argument[0]);
+var _x = argument[1];
+var _y = argument[2];
+var _active = argument[3];
+var _disabled = argument[4];
+var _highlight = false;
+if (argument_count > 5)
+{
+ _highlight = argument[5];
+}
+
+// Check mouse over
+var _mouseOver = (PEd_guiShapeIsHovered(_container)
+ && guiMouseY > _y
+ && guiMouseY < _y + guiLineHeight);
+
+// Draw
+var _textColour = PEdColour.Text;
+var _backgroundColour = noone;
+
+if (_active)
+{
+ _textColour = PEdColour.TextSelected;
+ _backgroundColour = PEdColour.Active;
+}
+else if (!_disabled)
+{
+ if (_highlight
+ || _mouseOver)
+ {
+ _backgroundColour = PEdColour.Highlight;
+ }
+}
+else
+{
+ _textColour = PEdColour.Disabled;
+}
+
+if (_backgroundColour != noone)
+{
+ PEd_guiDrawRectangle(0, _y, _containerWidth, guiLineHeight, _backgroundColour);
+}
+if (_textColour != PEdColour.Text)
+{
+ draw_text_colour(_x, _y + round((guiLineHeight - guiFontHeight) * 0.5), _text,
+ _textColour, _textColour, _textColour, _textColour, 1);
+}
+else
+{
+ draw_text(_x, _y + round((guiLineHeight - guiFontHeight) * 0.5), _text);
+}
+
+// Click
+if (!_disabled
+ && _mouseOver)
+{
+ if (mouse_check_button_pressed(mb_left))
+ {
+ return 1;
+ }
+ return -1;
+}
+return 0;
diff --git a/PushEd.gmx/scripts/PEd_guiDrawListItemEye.gml b/PushEd.gmx/scripts/PEd_guiDrawListItemEye.gml
new file mode 100644
index 00000000..204d9417
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiDrawListItemEye.gml
@@ -0,0 +1,49 @@
+/// PEd_guiDrawListItemEye(name, x, y, active, disabled, [highlighted])
+/**
+ * @brief Draws a list item with an eye on the given position.
+ * @param {string} name The item name.
+ * @param {real} x The x position to draw the item at.
+ * @param {real} y The y position to draw the item at.
+ * @param {bool} active True if the item is currently selected.
+ * @param {bool} disabled True to disable clicking on the item.
+ * @param {bool} [highlighted] True to highlight the item.
+ * @return {real} If the item is clicked, then 1 is returned.
+ * If the eye is clicked, then 2 is returned.
+ * In all other cases returns 0.
+ */
+var _container = guiShapeFilling;
+var _containerWidth = PEd_guiShapeGetWidth(_container);
+var _text = string(argument[0]);
+var _x = argument[1];
+var _y = argument[2];
+var _active = argument[3];
+var _disabled = argument[4];
+var _highlight = false;
+if (argument_count > 5)
+{
+ _highlight = argument[5];
+}
+
+// Check mouse over
+var _mouseOver = (PEd_guiShapeIsHovered(_container)
+ && guiMouseY > _y
+ && guiMouseY < _y + guiLineHeight);
+
+// Draw
+PEd_guiDrawListItem(_text, _x + guiLineHeight, _y, _active, _disabled, _highlight);
+draw_sprite_ext(PEd_guiSprMisc, 1, _x, _y, 1, 1, 0, merge_colour(c_white, PEdColour.Disabled, _disabled), 1);
+
+// Click
+if (mouse_check_button_pressed(mb_left)
+ && _mouseOver)
+{
+ if (guiMouseX < _x + guiLineHeight)
+ {
+ return 2;
+ }
+ if (!_disabled)
+ {
+ return 1;
+ }
+}
+return 0;
diff --git a/PushEd.gmx/scripts/PEd_guiDrawObjectInput.gml b/PushEd.gmx/scripts/PEd_guiDrawObjectInput.gml
new file mode 100644
index 00000000..660f47b1
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiDrawObjectInput.gml
@@ -0,0 +1,51 @@
+/// PEd_guiDrawObjectInput(x, y, width, object)
+/**
+ * @brief Draws a D&D input for an object.
+ * @param {real} x The x position to draw the input at.
+ * @param {real} y The y position to draw the input at.
+ * @param {real} width The width of the input.
+ * @param {real} background The current object.
+ * @return {real} The new object or -1.
+ */
+var _delegate = guiShapeFilling;
+var _x = argument0;
+var _y = argument1;
+var _width = argument2;
+var _object = argument3;
+var _mouseOver = (PEd_guiShapeIsHovered(_delegate)
+ && guiMouseX > _x
+ && guiMouseY > _y
+ && guiMouseX < _x + _width
+ && guiMouseY < _y + guiLineHeight);
+
+PEd_guiDrawInput(_x, _y, _width, object_get_name(_object), true);
+
+if (guiDnDObject != -1)
+{
+ PEd_guiDrawRectangle(_x, _y, _width, guiLineHeight, PEdColour.Active, 0.5);
+}
+
+draw_sprite(PEd_guiSprMisc, 3, _x + _width - 20, _y);
+
+if (_mouseOver)
+{
+ guiTooltip = "Drag and drop object here from the Content Browser.";
+
+ if (mouse_check_button_released(mb_left)
+ && guiDnDObject != -1)
+ {
+ var _temp = guiDnDObject;
+ guiDnDObject = -1;
+ return _temp;
+ }
+
+ if (guiMouseX > _x + _width - 20)
+ {
+ guiTooltip = "Remove object.";
+ if (mouse_check_button_pressed(mb_left))
+ {
+ return -1;
+ }
+ }
+}
+return _object;
diff --git a/PushEd.gmx/scripts/PEd_guiDrawPopupMessage.gml b/PushEd.gmx/scripts/PEd_guiDrawPopupMessage.gml
new file mode 100644
index 00000000..3ce17503
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiDrawPopupMessage.gml
@@ -0,0 +1,17 @@
+/// PEd_guiDrawPopupMessage(x, y, message)
+/**
+ * @brief Draws a popup message at the given position.
+ * @param {real} x The x position to draw popup the message at.
+ * @param {real} y The y position to draw popup the message at.
+ * @param {strnig} message The text of the popup message.
+ */
+var _message = argument2;
+var _w = string_width(_message) + 128;
+var _h = string_height(_message) + 32;
+var _x = argument0 - _w - 16;
+var _y = argument1 - _h - 16;
+
+PEd_guiDrawRectangle(_x, _y, _w, _h, PEdColour.WindowBorder);
+PEd_guiDrawRectangle(_x + 1, _y + 1, _w - 2, _h - 2, PEdColour.WindowBackground);
+draw_sprite(PEd_guiSprWarning, 0, _x + 16, _y + _h / 2);
+draw_text(_x + 96, _y + 16, _message);
diff --git a/PushEd.gmx/scripts/PEd_guiDrawRectangle.gml b/PushEd.gmx/scripts/PEd_guiDrawRectangle.gml
new file mode 100644
index 00000000..c9c7e830
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiDrawRectangle.gml
@@ -0,0 +1,19 @@
+/// PEd_guiDrawRectangle(x, y, width, height, colour, [alpha])
+/**
+ * @brief Draws a rectangle of the given size and colour at the given position.
+ * @param {real} x The x position to draw the rectangle at.
+ * @param {real} y The y position to draw the rectangle at.
+ * @param {real} width The width of the rectangle.
+ * @param {real} height The height of the rectangle.
+ * @param {real} colour The colour of the rectangle.
+ * @param {real} [alpha] The alpha of the rectangle.
+ */
+var _alpha = 1;
+if (argument_count > 5)
+{
+ _alpha = argument[5];
+}
+draw_sprite_ext(PEd_guiSprRectangle, 0,
+ argument[0], argument[1],
+ argument[2], argument[3],
+ 0, argument[4], _alpha);
diff --git a/PushEd.gmx/scripts/PEd_guiDrawSection.gml b/PushEd.gmx/scripts/PEd_guiDrawSection.gml
new file mode 100644
index 00000000..d38af68a
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiDrawSection.gml
@@ -0,0 +1,40 @@
+/// PEd_guiDrawSection(name, x, y, expanded)
+/**
+ * @brief Draws a section at the given position.
+ * @param {string} name The name of the section.
+ * @param {real} x The x position to draw the section at.
+ * @param {real} y The y position to draw the section at.
+ * @param {bool} expanded True if the section is expanded.
+ * @return {bool} True if the section is clicked.
+ */
+var _container = guiShapeFilling;
+var _containerWidth = PEd_guiShapeGetWidth(_container);
+var _text = string(argument0);
+var _x = argument1;
+var _y = argument2;
+var _state = argument3;
+
+// Background
+PEd_guiDrawRectangle(0, _y, _containerWidth, guiLineHeight, PEdColour.Section);
+
+// Text
+draw_set_font(PEd_fntBold);
+draw_text(guiLineHeight, _y + round((guiLineHeight - guiFontHeight) * 0.5), _text);
+draw_set_font(PEd_fntNormal);
+
+// Icon
+draw_sprite(PEd_guiSprRoll, _state, 8, _y);
+
+// Mouse over
+if (PEd_guiShapeIsHovered(_container)
+ && guiMouseY > _y
+ && guiMouseY < _y + guiLineHeight)
+{
+ // Click
+ if (mouse_check_button_pressed(mb_left))
+ {
+ PEd_guiRequestRedraw(_container);
+ return true;
+ }
+}
+return false;
diff --git a/PushEd.gmx/scripts/PEd_guiDrawShadow.gml b/PushEd.gmx/scripts/PEd_guiDrawShadow.gml
new file mode 100644
index 00000000..9925fbe7
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiDrawShadow.gml
@@ -0,0 +1,28 @@
+/// PEd_guiDrawShadow(x, y, width, height, colour, alpha)
+/**
+ * @brief Draws shadow of given size at given position.
+ * @param {real} x The x position of the shadow.
+ * @param {real} y The y position of the shadow.
+ * @param {real} width The width of the shadow.
+ * @param {real} height The height of the shadow.
+ * @param {real} colour The shadow colour.
+ * @param {real} alpha The shadow alpha.
+ */
+var _spr = PEd_guiSprShadow;
+var _sprW = sprite_get_width(_spr);
+var _offset = _sprW * 0.5;
+var _x = argument0 + _offset;
+var _y = argument1 + _offset;
+var _w = argument2 - _sprW;
+var _h = argument3 - _sprW;
+var _col = argument4;
+var _a = argument5;
+
+draw_sprite_ext(_spr, 0, _x - _sprW, _y - _sprW, 1, 1, 0, _col, _a);
+draw_sprite_stretched_ext(_spr, 1, _x, _y - _sprW, _w, _sprW, _col, _a);
+draw_sprite_ext(_spr, 0, _x + _w + _sprW, _y - _sprW, -1, 1, 0, _col, _a);
+draw_sprite_stretched_ext(_spr, 2, _x - _sprW, _y, _sprW, _h, _col, _a);
+draw_sprite_ext(_spr, 2, _x + _w + _sprW, _y, -1, _h / _sprW, 0, _col, _a);
+draw_sprite_ext(_spr, 0, _x - _sprW, _y + _h + _sprW, 1, -1, 0, _col, _a);
+draw_sprite_ext(_spr, 1, _x, _y + _h + _sprW, _w / _sprW, -1, 0, _col, _a);
+draw_sprite_ext(_spr, 0, _x + _w + _sprW, _y + _h + _sprW, -1, -1, 0, _col, _a);
diff --git a/PushEd.gmx/scripts/PEd_guiDrawSpriteClickable.gml b/PushEd.gmx/scripts/PEd_guiDrawSpriteClickable.gml
new file mode 100644
index 00000000..e8d4c140
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiDrawSpriteClickable.gml
@@ -0,0 +1,30 @@
+/// PEd_guiDrawSpriteClickable(sprite, subimg, x, y, [colour])
+/**
+ * @brief Draws a clickable sprite at the given position.
+ * @param {real} sprite The id of the sprite.
+ * @param {real} subimg The subimage of the sprite.
+ * @param {real} x The x position to draw the sprite at.
+ * @param {real} y The y position to draw the sprite at.
+ * @param {real} [colour] The colour to blend the sprite with.
+ * @return {bool} True if the sprite is clicked.
+ */
+var _delegate = guiShapeFilling;
+var _sprite = argument[0];
+var _subimg = argument[1];
+var _x = argument[2];
+var _y = argument[3];
+var _colour = c_white;
+if (argument_count > 4)
+{
+ _colour = argument[4];
+}
+var _width = sprite_get_width(_sprite);
+var _height = sprite_get_height(_sprite);
+var _mouseOver = (PEd_guiShapeIsHovered(_delegate)
+ && guiMouseX > _x
+ && guiMouseY > _y
+ && guiMouseX < _x + _width
+ && guiMouseY < _y + _height);
+draw_sprite_ext(_sprite, _subimg, _x, _y, 1, 1, 0, _colour, 1);
+return (mouse_check_button_pressed(mb_left)
+ && _mouseOver);
diff --git a/PushEd.gmx/scripts/PEd_guiDrawTextBold.gml b/PushEd.gmx/scripts/PEd_guiDrawTextBold.gml
new file mode 100644
index 00000000..0756036d
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiDrawTextBold.gml
@@ -0,0 +1,13 @@
+/// PEd_guiDrawTextBold(x, y, text)
+/**
+ * @brief Draws bold text at the given position.
+ * @param {real} x The x position to draw the text at.
+ * @param {real} y The y position to draw the text at.
+ * @param {string} text The text to draw.
+ * @note The sets the font to bold and resets it back to normal
+ * right after rendering the text.
+ */
+gml_pragma("forceinline");
+draw_set_font(PEd_fntBold);
+draw_text(argument0, argument1, argument2);
+draw_set_font(PEd_fntNormal);
diff --git a/PushEd.gmx/scripts/PEd_guiDrawTextPart.gml b/PushEd.gmx/scripts/PEd_guiDrawTextPart.gml
new file mode 100644
index 00000000..f4390898
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiDrawTextPart.gml
@@ -0,0 +1,21 @@
+/// PEd_guiDrawTextPart(x, y, text, maxWidth)
+/**
+ * @brief Draws part of the text at the given position.
+ * @param {real} x The x position to draw the text at.
+ * @param {real} y The y position to draw the text at.
+ * @param {string} text The text to draw.
+ * @param {real} maxWidth The maximum width of the text in pixels. If the text
+ * is longer than this, then it is clipped and
+ * followed by "...".
+ */
+var _maxCharCount = floor(argument3 / guiFontWidth);
+if (string_length(argument2) > _maxCharCount)
+{
+ draw_text(argument0, argument1,
+ string_copy(argument2, 1, _maxCharCount - 3)
+ + string_repeat(".", min(3, _maxCharCount)));
+}
+else
+{
+ draw_text(argument0, argument1, argument2);
+}
diff --git a/PushEd.gmx/scripts/PEd_guiDrawTextShadow.gml b/PushEd.gmx/scripts/PEd_guiDrawTextShadow.gml
new file mode 100644
index 00000000..0f6d7c80
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiDrawTextShadow.gml
@@ -0,0 +1,12 @@
+/// PEd_guiDrawTextShadow(x, y, text, colourText, colourShadow)
+/**
+ * @brief Draws text with shadow at the given position.
+ * @param {real} x The x position to draw the text at.
+ * @param {real} y The y position to draw the text at.
+ * @param {string} text The text to draw.
+ * @param {real} colourText The colour of the text.
+ * @param {real} colourShadow The colour of the shadow.
+ */
+gml_pragma("forceinline");
+draw_text_colour(argument0 + 1, argument1 + 1, argument2, argument4, argument4, argument4, argument4, 1);
+draw_text_colour(argument0, argument1, argument2, argument3, argument3, argument3, argument3, 1);
diff --git a/PushEd.gmx/scripts/PEd_guiDrawTooltip.gml b/PushEd.gmx/scripts/PEd_guiDrawTooltip.gml
new file mode 100644
index 00000000..8c1c7a6e
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiDrawTooltip.gml
@@ -0,0 +1,19 @@
+/// PEd_guiDrawTooltip(x, y, text)
+/**
+ * @brief Dras a tooltip at the given position.
+ * @param {real} x The x position to draw the tooltip at.
+ * @param {real} y The y position to draw the tooltip at.
+ * @param {string} text The text of the tooltip.
+ */
+var _x = argument0;
+var _y = argument1;
+var _text = argument2;
+var _width = string_width(_text) + 16;
+var _height = string_height(_text) + 8;
+
+_x = min(_x, window_get_width() - _width - 1);
+_y = min(_y, window_get_height() - _height - 1);
+
+PEd_guiDrawRectangle(_x, _y, _width, _height, PEdColour.WindowBorder);
+PEd_guiDrawRectangle(_x + 1, _y + 1, _width - 2, _height - 2, PEdColour.WindowBackground);
+draw_text(_x + 8, _y + 4, _text);
diff --git a/PushEd.gmx/scripts/PEd_guiEncodeID.gml b/PushEd.gmx/scripts/PEd_guiEncodeID.gml
new file mode 100644
index 00000000..3563a2d0
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiEncodeID.gml
@@ -0,0 +1,9 @@
+/// PEd_guiEncodeID(delegate, number)
+/**
+ * @brief Gets the id of the dynamic shape.
+ * @param {real} delegate The id of the delegate.
+ * @param {real} number The unique number of the dynamic shape.
+ * @return {real} The id of the dynamic shape.
+ */
+gml_pragma("forceinline");
+return ((argument0 + 1) * 100000 + argument1);
diff --git a/PushEd.gmx/scripts/PEd_guiEndFill.gml b/PushEd.gmx/scripts/PEd_guiEndFill.gml
new file mode 100644
index 00000000..d2c33338
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiEndFill.gml
@@ -0,0 +1,55 @@
+/// PEd_guiEndFill(canvas)
+/**
+ * @brief Finishes drawing into the canvas and resets the render target.
+ * @param {canvas} The id of the canvas.
+ */
+if (guiShapeFilling == argument0)
+{
+ var _scrollX = 0;
+ var _scrollY = 0;
+ var _scrollbarVer = argument0[? "scrollbarVer"];
+ var _scrollbarHor = argument0[? "scrollbarHor"];
+ var _drawVer = false;
+ var _drawHor = false;
+
+ if (!is_undefined(_scrollbarVer))
+ {
+ _drawVer = PEd_guiScrollbarIsVisible(_scrollbarVer);
+ _scrollY = PEd_guiScrollbarGetScroll(_scrollbarVer) * _drawVer;
+ }
+ if (!is_undefined(_scrollbarHor))
+ {
+ _drawHor = PEd_guiScrollbarIsVisible(_scrollbarHor);
+ _scrollX = PEd_guiScrollbarGetScroll(_scrollbarHor) * _drawHor;
+ }
+
+ if (!is_undefined(_scrollbarVer))
+ {
+ var _height = PEd_guiShapeGetHeight(argument0) - _drawHor * PEd_guiShapeGetHeight(_scrollbarHor);
+ PEd_guiShapeSetHeight(_scrollbarVer, _height);
+ _scrollbarVer[? "size"] = _height;
+ PEd_guiScrollbarCalcJumpAndThumbSize(_scrollbarVer);
+
+ if (_drawVer)
+ {
+ PEd_guiDrawItem(_scrollbarVer, _scrollX + PEd_guiShapeGetWidth(argument0) - PEd_guiShapeGetWidth(_scrollbarVer), _scrollY);
+ }
+ }
+
+ if (!is_undefined(_scrollbarHor))
+ {
+ var _width = PEd_guiShapeGetWidth(argument0) - _drawVer * PEd_guiShapeGetWidth(_scrollbarVer);
+ PEd_guiShapeSetWidth(_scrollbarHor, _width);
+ _scrollbarHor[? "size"] = _width;
+ PEd_guiScrollbarCalcJumpAndThumbSize(_scrollbarHor);
+
+ if (_drawHor)
+ {
+ PEd_guiDrawItem(_scrollbarHor, _scrollX, _scrollY + PEd_guiShapeGetHeight(argument0) - PEd_guiShapeGetHeight(_scrollbarHor));
+ }
+ }
+
+ PEd_guiMatrixRestore();
+ surface_reset_target();
+ guiShapeFilling = noone;
+}
diff --git a/PushEd.gmx/scripts/PEd_guiFindShape.gml b/PushEd.gmx/scripts/PEd_guiFindShape.gml
new file mode 100644
index 00000000..d3f90180
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiFindShape.gml
@@ -0,0 +1,76 @@
+/// PEd_guiFindShape(items, x, y)
+/**
+ * @brief Recursively finds shape on the given position in the list of shapes.
+ * @param {real} items The list of shapes.
+ * @param {real} x The x position to find a shape at.
+ * @param {real} y The y position to find a shape at.
+ * @return {real} The id of the found shape or noone.
+ */
+var _items = argument0;
+var _mouseX = argument1;
+var _mouseY = argument2;
+
+for (var i = ds_list_size(_items) - 1; i >= 0; i--)
+{
+ var _item = _items[| i];
+ var _x = PEd_guiShapeGetX(_item);
+ var _y = PEd_guiShapeGetY(_item);
+
+ if (_mouseX > _x
+ && _mouseY > _y
+ && _mouseX < _x + PEd_guiShapeGetWidth(_item)
+ && _mouseY < _y + PEd_guiShapeGetHeight(_item))
+ {
+ PEd_guiShapeSetRedraw(_item, true);
+
+ // Skip hidden scrollbars
+ if (PEd_guiShapeGetType(_item) == PEdGUIShape.Scrollbar
+ && !PEd_guiScrollbarIsVisible(_item))
+ {
+ continue;
+ }
+
+ // Check if scrollbars are hovered
+ var _scrollX = 0;
+ var _scrollY = 0;
+ var _scrollbarHor = _item[? "scrollbarHor"]
+ var _scrollbarVer = _item[? "scrollbarVer"];
+ var _scrollbars = ds_list_create();
+
+ if (!is_undefined(_scrollbarHor))
+ {
+ ds_list_add(_scrollbars, _scrollbarHor);
+ _scrollX = PEd_guiScrollbarGetScroll(_scrollbarHor) * PEd_guiScrollbarIsVisible(_scrollbarHor);
+ }
+ if (!is_undefined(_scrollbarVer))
+ {
+ ds_list_add(_scrollbars, _scrollbarVer);
+ _scrollY = PEd_guiScrollbarGetScroll(_scrollbarVer) * PEd_guiScrollbarIsVisible(_scrollbarVer);
+ }
+
+ var _hovered = noone;
+ if (!ds_list_empty(_scrollbars))
+ {
+ _hovered = PEd_guiFindShape(_scrollbars, _mouseX - _x + _scrollX, _mouseY - _y + _scrollY);
+ }
+ ds_list_destroy(_scrollbars);
+ if (PEd_guiShapeExists(_hovered))
+ {
+ return _hovered;
+ }
+
+ // Find hovered item
+ var _subItems = PEd_guiCompoundShapeGetItems(_item);
+ if (!is_undefined(_subItems))
+ {
+ var _hovered = PEd_guiFindShape(_subItems, _mouseX - _x + _scrollX, _mouseY - _y + _scrollY);
+ if (PEd_guiShapeExists(_hovered))
+ {
+ return _hovered;
+ }
+ }
+
+ return _item;
+ }
+}
+return noone;
diff --git a/PushEd.gmx/scripts/PEd_guiGetActiveShape.gml b/PushEd.gmx/scripts/PEd_guiGetActiveShape.gml
new file mode 100644
index 00000000..0cb3f1d6
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiGetActiveShape.gml
@@ -0,0 +1,7 @@
+/// PEd_guiGetActiveShape()
+/**
+ * @brief Gets the id of the currently active shape.
+ * @return {real} The id of the currently active shape or noone.
+ */
+gml_pragma("forceinline");
+return guiShapeActive;
diff --git a/PushEd.gmx/scripts/PEd_guiGetHoveredShape.gml b/PushEd.gmx/scripts/PEd_guiGetHoveredShape.gml
new file mode 100644
index 00000000..7c981a20
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiGetHoveredShape.gml
@@ -0,0 +1,7 @@
+/// PEd_guiGetHoveredShape()
+/**
+ * @brief Gets the id of the currently hovered shape.
+ * @return {real} The id of the currently hovered shape or noone.
+ */
+gml_pragma("forceinline");
+return guiShapeHovered;
diff --git a/PushEd.gmx/scripts/PEd_guiGetSelectedShape.gml b/PushEd.gmx/scripts/PEd_guiGetSelectedShape.gml
new file mode 100644
index 00000000..b5b84fc3
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiGetSelectedShape.gml
@@ -0,0 +1,7 @@
+/// PEd_guiGetSelectedShape()
+/**
+ * @brief Gets the id of the currently selected shape.
+ * @return {real} The id of the currently selected shape or noone.
+ */
+gml_pragma("forceinline");
+return guiShapeSelected;
diff --git a/PushEd.gmx/scripts/PEd_guiInit.gml b/PushEd.gmx/scripts/PEd_guiInit.gml
new file mode 100644
index 00000000..b4aab730
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiInit.gml
@@ -0,0 +1,109 @@
+/// PEd_guiInit()
+/**
+ * @brief Initializes GUI.
+ * @return {real} The id of the GUI root shape.
+ */
+enum PEdGUIShape
+{
+ Blank,
+ Compound,
+ Canvas,
+ Dock,
+ Viewport,
+ Container,
+ Scrollbar,
+ Toolbar,
+ ToolbarButton,
+ ToolbarSwitch,
+ Panel,
+ Window,
+ ContextMenu,
+ ContextMenuItem,
+ MenuBar,
+ MenuBarItem,
+};
+
+enum PEdGUISplit
+{
+ Horizontal,
+ Vertical,
+};
+
+enum PEdGUIResize
+{
+ None = $0000,
+ Left = $1000,
+ Top = $0100,
+ Right = $0010,
+ Bottom = $0001,
+ Horizontal = $1010,
+ Vertical = $0101,
+};
+
+enum PEdColour
+{
+ Background = $1E1E1E,
+ Text = $FFFFFF,
+ TextSelected = $FFFFFF,
+ Input = $252525,
+ Section = $2D2D2D,
+ WindowBackground = $2D2D2D,
+ WindowBackground2 = $333333,
+ WindowBorder = $3F3F3F,
+ WindowButton = $686868,
+ Scrollbar = $9E9E9E,
+ Highlight = $686868,
+ Active = $A56428,
+ Disabled = c_gray,
+ Shadow = c_black,
+ ShadowAlpha = 1,
+};
+
+display_set_gui_maximise();
+
+draw_set_font(PEd_fntNormal);
+guiFontHeight = string_height("Q");
+guiFontWidth = string_width("Q");
+guiLineHeight = sprite_get_height(PEd_guiSprInput);
+
+guiCursor = cr_default;
+guiShapes = ds_list_create();
+guiShapeId = 0;
+guiShapeHovered = noone;
+guiShapeActive = noone;
+guiShapeFilling = noone;
+guiShapeSelected = noone;
+guiMouseX = 0;
+guiMouseY = 0;
+guiTooltip = "";
+guiMatrixStack = ds_stack_create();
+guiDestroyStack = ds_stack_create();
+
+// Input
+guiInputSprite = PEd_guiSprInput;
+guiInputSpriteWidth = sprite_get_width(guiInputSprite);
+guiInputSpriteHeight = sprite_get_height(guiInputSprite);
+guiInputString = "";
+guiInputIndex[1] = 1;
+guiInputIndex[0] = 1;
+guiInputDrawIndexStart = 1;
+guiInputMultitype = 0;
+guiInputTimer = current_time;
+guiInputActive = noone;
+
+// Popup message
+guiPopupMessage = "";
+guiPopupTimer = 0;
+guiPopupDuration = 0;
+
+// Context menu
+guiContextMenu = noone;
+guiMousePressX = noone;
+guiMousePressY = noone;
+
+// Keyboard shortcuts
+guiKeyLog = ds_list_create();
+
+// Root
+guiRoot = PEd_guiCreateCompoundShape();
+return guiRoot;
diff --git a/PushEd.gmx/scripts/PEd_guiInputCopy.gml b/PushEd.gmx/scripts/PEd_guiInputCopy.gml
new file mode 100644
index 00000000..4c925aa8
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiInputCopy.gml
@@ -0,0 +1,11 @@
+/// PEd_guiInputCopy()
+/**
+ * @brief Copies selected part of text of currently active input
+ * to the clipboard.
+ */
+if (guiInputIndex[0] != guiInputIndex[1])
+{
+ clipboard_set_text(string_copy(guiInputString,
+ min(guiInputIndex[0], guiInputIndex[1]),
+ abs(guiInputIndex[0] - guiInputIndex[1])));
+}
diff --git a/PushEd.gmx/scripts/PEd_guiInputCut.gml b/PushEd.gmx/scripts/PEd_guiInputCut.gml
new file mode 100644
index 00000000..dbc6b334
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiInputCut.gml
@@ -0,0 +1,11 @@
+/// PEd_guiInputCut()
+/**
+ * @brief Cuts selected part of text in currently active input.
+ */
+if (guiInputIndex[0] != guiInputIndex[1])
+{
+ clipboard_set_text(string_copy(guiInputString,
+ min(guiInputIndex[0], guiInputIndex[1]),
+ abs(guiInputIndex[0] - guiInputIndex[1])));
+ PEd_guiInputDeleteSelectedPart();
+}
diff --git a/PushEd.gmx/scripts/PEd_guiInputDelete.gml b/PushEd.gmx/scripts/PEd_guiInputDelete.gml
new file mode 100644
index 00000000..eca106a6
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiInputDelete.gml
@@ -0,0 +1,8 @@
+/// PEd_guiInputDelete()
+/**
+ * @brief Deletes selected part of text in currently active input.
+ */
+if (guiInputIndex[0] != guiInputIndex[1])
+{
+ PEd_guiInputDeleteSelectedPart();
+}
diff --git a/PushEd.gmx/scripts/PEd_guiInputDeleteSelectedPart.gml b/PushEd.gmx/scripts/PEd_guiInputDeleteSelectedPart.gml
new file mode 100644
index 00000000..9ca2396c
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiInputDeleteSelectedPart.gml
@@ -0,0 +1,10 @@
+/// PEd_guiInputDeleteSelectedPart()
+/**
+ * @brief Deletes selected part of input text.
+ */
+var _minIndex = min(guiInputIndex[0], guiInputIndex[1]);
+guiInputString = string_delete(guiInputString,
+ _minIndex,
+ abs(guiInputIndex[0] - guiInputIndex[1]));
+guiInputIndex[0] = _minIndex;
+guiInputIndex[1] = _minIndex;
diff --git a/PushEd.gmx/scripts/PEd_guiInputPaste.gml b/PushEd.gmx/scripts/PEd_guiInputPaste.gml
new file mode 100644
index 00000000..33f469e7
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiInputPaste.gml
@@ -0,0 +1,19 @@
+/// PEd_guiInputPaste()
+/**
+ * @brief Pastes text from the clipboard to currently active input.
+ */
+if (clipboard_has_text())
+{
+ // Delete selected part
+ if (guiInputIndex[0] != guiInputIndex[1])
+ {
+ PEd_guiInputDeleteSelectedPart();
+ }
+
+ // Insert string
+ guiInputString = string_insert(clipboard_get_text(),
+ guiInputString,
+ guiInputIndex[0]);
+ guiInputIndex[0] += string_length(clipboard_get_text());
+ guiInputIndex[1] = guiInputIndex[0];
+}
diff --git a/PushEd.gmx/scripts/PEd_guiInputSelectAll.gml b/PushEd.gmx/scripts/PEd_guiInputSelectAll.gml
new file mode 100644
index 00000000..9f7a1998
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiInputSelectAll.gml
@@ -0,0 +1,7 @@
+/// PEd_guiInputSelectAll()
+/**
+ * @brief Selects all text in currently active input.
+ */
+var _strlen = string_length(guiInputString);
+guiInputIndex[0] = 1;
+guiInputIndex[1] = _strlen + 1;
diff --git a/PushEd.gmx/scripts/PEd_guiKeyboardShorcutAddKey.gml b/PushEd.gmx/scripts/PEd_guiKeyboardShorcutAddKey.gml
new file mode 100644
index 00000000..29065c1a
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiKeyboardShorcutAddKey.gml
@@ -0,0 +1,8 @@
+/// PEd_guiKeyboardShorcutAddKey(keyboardShortcut, key)
+/**
+ * @brief Adds key to the keyboard shortcut.
+ * @param {real} keyboardShortcut The id of the keyboard shortcut.
+ * @param {real} key The key.
+ */
+gml_pragma("forceinline");
+ds_list_add(argument0[? "keys"], argument1);
diff --git a/PushEd.gmx/scripts/PEd_guiKeyboardShortcutToString.gml b/PushEd.gmx/scripts/PEd_guiKeyboardShortcutToString.gml
new file mode 100644
index 00000000..2cec85e5
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiKeyboardShortcutToString.gml
@@ -0,0 +1,19 @@
+/// PEd_guiKeyboardShortcutToString(keyboardShortcut)
+/**
+ * @brief Converts the keyboard shortcut into a human readable string.
+ * @param {real} keyboardShortcut The id of the keyboard shortcut.
+ * @return {string} The keyboard shortcut converted into a human readable string.
+ */
+var _shortcut = argument0;
+var _keys = _shortcut[? "keys"];
+var _string = "";
+var _size = ds_list_size(_keys);
+for (var i = 0; i < _size - 1; i++)
+{
+ _string += PEd_keyToString(_keys[| i]) + "+";
+}
+if (_size >= 1)
+{
+ _string += PEd_keyToString(_keys[| _size - 1]);
+}
+return _string;
diff --git a/PushEd.gmx/scripts/PEd_guiKeyboardShortcutUpdate.gml b/PushEd.gmx/scripts/PEd_guiKeyboardShortcutUpdate.gml
new file mode 100644
index 00000000..37d9f9e1
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiKeyboardShortcutUpdate.gml
@@ -0,0 +1,31 @@
+/// PEd_guiKeyboardShortcutUpdate(keyboardShortcut)
+/**
+ * @brief Updates the keyboard shortcut.
+ * @param {real} keyboardShortcut The id of the keyboard shortcut.
+ */
+var _shortcut = argument0;
+var _action = _shortcut[? "scrAction"];
+if (_action == noone
+ || guiInputActive != noone)
+{
+ exit;
+}
+
+var _keys = _shortcut[? "keys"];
+var _size = ds_list_size(_keys);
+
+if (_size != ds_list_size(guiKeyLog))
+{
+ exit;
+}
+
+for (var i = _size - 1; i >= 0; i--)
+{
+ if (guiKeyLog[| i] != _keys[| i])
+ {
+ exit;
+ }
+}
+
+ds_list_delete(guiKeyLog, _size - 1);
+script_execute(_action);
diff --git a/PushEd.gmx/scripts/PEd_guiLogKey.gml b/PushEd.gmx/scripts/PEd_guiLogKey.gml
new file mode 100644
index 00000000..6bf75937
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiLogKey.gml
@@ -0,0 +1,26 @@
+/// PEd_guiLogKey(key)
+/**
+ * @brief Adds the key to the key log.
+ * @param {real} key The key to be added to the key log.
+ */
+var _key = argument0;
+
+switch (_key)
+{
+ case vk_lshift:
+ case vk_rshift:
+ _key = vk_shift;
+ break;
+
+ case vk_lalt:
+ case vk_ralt:
+ _key = vk_alt;
+ break;
+
+ case vk_lcontrol:
+ case vk_rcontrol:
+ _key = vk_control;
+ break;
+}
+
+ds_list_add(guiKeyLog, _key);
diff --git a/PushEd.gmx/scripts/PEd_guiMatrixPush.gml b/PushEd.gmx/scripts/PEd_guiMatrixPush.gml
new file mode 100644
index 00000000..f4827e5e
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiMatrixPush.gml
@@ -0,0 +1,10 @@
+/// PEd_guiMatrixPush(x, y)
+/**
+ * @brief Stores the current matrix into the matrix stack
+ * and then pushes the coordinate system by the
+ * given values.
+ * @param {real} x The value to push the coordinate system by on the x axis.
+ * @param {real} y The value to push the coordinate system by on the y axis.
+ */
+ds_stack_push(guiMatrixStack, matrix_get(matrix_world));
+d3d_transform_add_translation(argument0, argument1, 0);
diff --git a/PushEd.gmx/scripts/PEd_guiMatrixRestore.gml b/PushEd.gmx/scripts/PEd_guiMatrixRestore.gml
new file mode 100644
index 00000000..9a64cbe6
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiMatrixRestore.gml
@@ -0,0 +1,7 @@
+/// PEd_guiMatrixRestore()
+/**
+ * @brief Restores coordinate system by popping matrix from
+ * the top of the matrix stack.
+ */
+gml_pragma("forceinline");
+matrix_set(matrix_world, ds_stack_pop(guiMatrixStack));
diff --git a/PushEd.gmx/scripts/PEd_guiMatrixSet.gml b/PushEd.gmx/scripts/PEd_guiMatrixSet.gml
new file mode 100644
index 00000000..03ee56a1
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiMatrixSet.gml
@@ -0,0 +1,11 @@
+/// PEd_guiMatrixSet(x, y)
+/**
+ * @brief Stores the current matrix into the matrix stack
+ * and sets the origin of the coordinate system to
+ * the given values.
+ * @param {real} x The origin on the x axis.
+ * @param {real} y The origin on the y axis.
+ */
+ds_stack_push(guiMatrixStack, matrix_get(matrix_world));
+d3d_transform_set_identity();
+d3d_transform_add_translation(argument0, argument1, 0);
diff --git a/PushEd.gmx/scripts/PEd_guiMenuBarAddItem.gml b/PushEd.gmx/scripts/PEd_guiMenuBarAddItem.gml
new file mode 100644
index 00000000..60242959
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiMenuBarAddItem.gml
@@ -0,0 +1,7 @@
+/// PEd_guiMenuBarAddItem(menuBar, menuBarItem)
+/**
+ * @brief Adds the item to the menu bar.
+ * @param {real} menuBar The id of the menu bar.
+ * @param {real} menuBarItem The id of the menu bar item.
+ */
+argument1[? "index"] = PEd_guiAddItem(argument0, argument1);
diff --git a/PushEd.gmx/scripts/PEd_guiMenuBarDraw.gml b/PushEd.gmx/scripts/PEd_guiMenuBarDraw.gml
new file mode 100644
index 00000000..f37f6d7c
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiMenuBarDraw.gml
@@ -0,0 +1,24 @@
+/// PEd_guiMenuBarDraw(menuBar)
+/**
+ * @brief Draws the menu bar.
+ * @param {real} menuBar The id of the menu bar.
+ */
+var _menu = argument0;
+if (PEd_guiBeginFill(_menu))
+{
+ var _x = 0;
+ var _y = 0;
+ var _items = PEd_guiCompoundShapeGetItems(_menu);
+ var _size = ds_list_size(_items);
+
+ for (var i = 0; i < _size; i++)
+ {
+ var _item = _items[| i];
+ PEd_guiDrawItem(_item, _x, _y);
+ _x += PEd_guiShapeGetWidth(_item);
+ }
+
+ PEd_guiEndFill(_menu);
+}
+
+PEd_guiCanvasDraw(_menu);
diff --git a/PushEd.gmx/scripts/PEd_guiMenuBarItemDraw.gml b/PushEd.gmx/scripts/PEd_guiMenuBarItemDraw.gml
new file mode 100644
index 00000000..9808fb49
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiMenuBarItemDraw.gml
@@ -0,0 +1,31 @@
+/// PEd_guiMenuBarItemDraw(menuBarItem)
+/**
+ * @brief Draws the menu bar item.
+ * @param {real} menuBarItem The id of the menu bar item.
+ */
+var _item = argument0;
+var _x = PEd_guiShapeGetX(_item);
+var _y = PEd_guiShapeGetY(_item);
+var _height = PEd_guiShapeGetHeight(_item);
+var _name = _item[? "name"];
+var _padding = 4;
+var _width = PEd_guiShapeGetWidth(_item);
+if (_width == 1)
+{
+ _width = string_width(_name) + _padding * 2;
+ PEd_guiShapeSetWidth(_item, _width);
+}
+
+// Draw background
+var _delegate = PEd_guiShapeGetDelegate(_item);
+if (_delegate[? "current"] == _item[? "index"])
+{
+ PEd_guiDrawRectangle(_x, _y, _width, _height, PEdColour.Active, 1);
+}
+else if (PEd_guiShapeIsHovered(_item))
+{
+ PEd_guiDrawRectangle(_x, _y, _width, _height, PEdColour.Highlight, 1);
+}
+
+// Text
+draw_text(_x + _padding, _y + round((_height - guiFontHeight) * 0.5), _name);
diff --git a/PushEd.gmx/scripts/PEd_guiMenuBarItemUpdate.gml b/PushEd.gmx/scripts/PEd_guiMenuBarItemUpdate.gml
new file mode 100644
index 00000000..f9170af2
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiMenuBarItemUpdate.gml
@@ -0,0 +1,45 @@
+/// PEd_guiMenuBarItemUpdate(menuBarItem)
+/**
+ * @brief Updates the menu bar item.
+ * @param {real} menuBarItem The id of the menu bar item.
+ */
+var _item = argument0;
+PEd_guiShapeUpdate(_item);
+
+var _index = _item[? "index"];
+var _scrContextMenu = _item[? "scrContextMenu"];
+if (_scrContextMenu != noone
+ && PEd_guiShapeIsHovered(_item))
+{
+ var _delegate = PEd_guiShapeGetDelegate(_item);
+ var _current = _delegate[? "current"];
+
+ if (mouse_check_button_pressed(mb_left))
+ {
+ if (_current == noone)
+ {
+ // Enable opening the context menus for the menu bar
+ _delegate[? "current"] = _index;
+ }
+ else
+ {
+ // Close context menu
+ _delegate[? "current"] = noone;
+ PEd_guiDestroyShape(guiContextMenu);
+ }
+ }
+
+ // Open the context menu for this item
+ if (_delegate[? "current"] != noone)
+ {
+ _delegate[? "current"] = _index;
+ if (_current != _index)
+ {
+ var _contextMenu = PEd_guiCreateContextMenu();
+ script_execute(_scrContextMenu, _contextMenu);
+ PEd_guiShowContextMenu(_contextMenu,
+ PEd_guiShapeGetX(_item),
+ PEd_guiShapeGetY(_delegate) + PEd_guiShapeGetHeight(_delegate) + 1);
+ }
+ }
+}
diff --git a/PushEd.gmx/scripts/PEd_guiMenuBarUpdate.gml b/PushEd.gmx/scripts/PEd_guiMenuBarUpdate.gml
new file mode 100644
index 00000000..6da560c3
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiMenuBarUpdate.gml
@@ -0,0 +1,14 @@
+/// PEd_guiMenuBarUpdate(menuBar)
+/**
+ * @brief Updates the menu bar.
+ * @param {real} menuBar The id of the menu bar.
+ */
+var _menu = argument0;
+PEd_guiCompoundShapeUpdate(_menu);
+
+if (_menu[? "current"] != noone
+ && guiContextMenu == noone)
+{
+ _menu[? "current"] = noone;
+ PEd_guiRequestRedraw(_menu);
+}
diff --git a/PushEd.gmx/scripts/PEd_guiMenuFile.gml b/PushEd.gmx/scripts/PEd_guiMenuFile.gml
new file mode 100644
index 00000000..c188e85c
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiMenuFile.gml
@@ -0,0 +1,20 @@
+/// PEd_guiMenuFile(contextMenu)
+/**
+ * @brief Adds options for the "File" menu to the context menu.
+ * @param {real} contextMenu The id of the context menu.
+ */
+var _contextMenu = argument0;
+PEd_guiAddItem(_contextMenu,
+ PEd_guiCreateContextMenuItem("New", PEd_actCreateEmptyRoom, ksNewRoom));
+PEd_guiAddItem(_contextMenu,
+ PEd_guiCreateContextMenuItem("Open", PEd_actOpenRoom, ksOpenRoom));
+PEd_guiAddItem(_contextMenu,
+ PEd_guiCreateContextMenuItem("Import", PEd_actImportRoom, ksImportRoom));
+PEd_guiAddItem(_contextMenu,
+ PEd_guiCreateContextMenuItem("Save", PEd_actSaveRoom, ksSaveRoom));
+PEd_guiAddItem(_contextMenu,
+ PEd_guiCreateContextMenuItem("Save As", PEd_actSaveRoomAs, ksSaveRoomAs));
+PEd_guiAddItem(_contextMenu,
+ PEd_guiCreateContextMenuItem("Export", PEd_actExportRoom, ksExportRoom));
+PEd_guiAddItem(_contextMenu,
+ PEd_guiCreateContextMenuItem("Exit", PEd_actExit));
diff --git a/PushEd.gmx/scripts/PEd_guiMenuHelp.gml b/PushEd.gmx/scripts/PEd_guiMenuHelp.gml
new file mode 100644
index 00000000..4d1f0054
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiMenuHelp.gml
@@ -0,0 +1,8 @@
+/// PEd_guiMenuHelp(contextMenu)
+/**
+ * @brief Adds options for the "Help" menu to the context menu.
+ * @param {real} contextMenu The id of the context menu.
+ */
+var _contextMenu = argument0;
+PEd_guiAddItem(_contextMenu,
+ PEd_guiCreateContextMenuItem("About PushEd...", PEd_actShowInfo));
diff --git a/PushEd.gmx/scripts/PEd_guiMenuInput.gml b/PushEd.gmx/scripts/PEd_guiMenuInput.gml
new file mode 100644
index 00000000..8e2086f1
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiMenuInput.gml
@@ -0,0 +1,11 @@
+/// PEd_guiMenuInput(contextMenu)
+/**
+ * @brief Adds options for an input menu to the context menu.
+ * @param {real} contextMenu The id of the context menu.
+ */
+var _contextMenu = argument0;
+PEd_guiAddItem(_contextMenu, PEd_guiCreateContextMenuItem("Cut", PEd_guiInputCut, ksInputCut));
+PEd_guiAddItem(_contextMenu, PEd_guiCreateContextMenuItem("Copy", PEd_guiInputCopy, ksInputCopy));
+PEd_guiAddItem(_contextMenu, PEd_guiCreateContextMenuItem("Paste", PEd_guiInputPaste, ksInputPaste));
+PEd_guiAddItem(_contextMenu, PEd_guiCreateContextMenuItem("Delete", PEd_guiInputDelete, ksInputDelete));
+PEd_guiAddItem(_contextMenu, PEd_guiCreateContextMenuItem("Select All", PEd_guiInputSelectAll, ksInputSelectAll));
diff --git a/PushEd.gmx/scripts/PEd_guiMenuObjectEdit.gml b/PushEd.gmx/scripts/PEd_guiMenuObjectEdit.gml
new file mode 100644
index 00000000..bf021699
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiMenuObjectEdit.gml
@@ -0,0 +1,14 @@
+/// PEd_guiMenuObjectEdit(contextMenu)
+/**
+ * @brief Adds options for the object "Edit" menu to the context menu.
+ * @param {real} contextMenu The id of the context menu.
+ */
+var _contextMenu = argument0;
+PEd_guiAddItem(_contextMenu,
+ PEd_guiCreateContextMenuItem("Clear Selection", PEd_actClearSelection, ksClearSelection, "Removes all selected objects from selection."));
+PEd_guiAddItem(_contextMenu,
+ PEd_guiCreateContextMenuItem("Copy", PEd_actCopySelectedObjects, ksCopyObjects, "Duplicate selected objects."));
+PEd_guiAddItem(_contextMenu,
+ PEd_guiCreateContextMenuItem("Delete", PEd_actDestroySelectedObjects, ksDestroyObjects, "Delete selected objects."));
+PEd_guiAddItem(_contextMenu,
+ PEd_guiCreateContextMenuItem("Hide", PEd_actHideSelectedObjects, noone, "Hide selected objects."));
diff --git a/PushEd.gmx/scripts/PEd_guiMenuPlay.gml b/PushEd.gmx/scripts/PEd_guiMenuPlay.gml
new file mode 100644
index 00000000..96812da0
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiMenuPlay.gml
@@ -0,0 +1,8 @@
+/// PEd_guiMenuPlay(contextMenu)
+/**
+ * @brief Adds options for the "Play" menu to the context menu.
+ * @param {real} contextMenu The id of the context menu.
+ */
+var _contextMenu = argument0;
+PEd_guiAddItem(_contextMenu,
+ PEd_guiCreateContextMenuItem("Play", PEd_actPlayRoom, ksPlayRoom, "Play the room."));
diff --git a/PushEd.gmx/scripts/PEd_guiMoveItemToTop.gml b/PushEd.gmx/scripts/PEd_guiMoveItemToTop.gml
new file mode 100644
index 00000000..29415ba4
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiMoveItemToTop.gml
@@ -0,0 +1,30 @@
+/// PEd_guiMoveItemToTop(item)
+/**
+ * @brief Moves the item to the top while preserving depth order.
+ * @param {real} item The id of the item to move.
+ */
+var _item = argument0;
+var _delegate = PEd_guiShapeGetDelegate(_item);
+
+if (!PEd_guiShapeExists(_delegate))
+{
+ exit;
+}
+
+var _items = PEd_guiCompoundShapeGetItems(_delegate);
+var _n = ds_list_size(_items);
+var _index = ds_list_find_index(_items, _item);
+
+if (_index >= 0)
+{
+ var i = _index + 1;
+ var _itemDepth = PEd_guiShapeGetDepth(_item);
+
+ while (i < _n && PEd_guiShapeGetDepth(_items[| i]) <= _itemDepth)
+ {
+ ++i;
+ }
+
+ PEd_dsListInsertMap(_items, i, _item);
+ ds_list_delete(_items, _index);
+}
diff --git a/PushEd.gmx/scripts/PEd_guiPanelDraw.gml b/PushEd.gmx/scripts/PEd_guiPanelDraw.gml
new file mode 100644
index 00000000..583039ec
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiPanelDraw.gml
@@ -0,0 +1,35 @@
+/// PEd_guiPanelDraw(panel)
+/**
+ * @brief Draws the panel.
+ * @param {real} panel The id of the panel.
+ */
+var _panel = argument0;
+var _panelW = PEd_guiShapeGetWidth(_panel);
+var _panelH = PEd_guiShapeGetHeight(_panel);
+var _titleBar = PEd_guiPanelGetTitleBar(_panel);
+var _container = PEd_guiPanelGetContainer(_panel);
+
+PEd_guiMatrixPush(PEd_guiShapeGetX(_panel), PEd_guiShapeGetY(_panel));
+
+PEd_guiShapeSetWidth(_titleBar, _panelW);
+PEd_guiDrawItem(_titleBar, 0, 0);
+PEd_guiShapeSetHeight(_titleBar,
+ clamp(PEd_guiContainerGetContentHeight(_titleBar), 1, _panelH - 1));
+
+var _border = 1;
+var _titleBarHeight = PEd_guiShapeGetHeight(_titleBar);
+PEd_guiShapeSetSize(_container,
+ _panelW - _border * 2,
+ max(_panelH - _titleBarHeight - _border, 1));
+
+var _selectedShape = PEd_guiGetSelectedShape();
+var _colourBorder = PEdColour.WindowBorder;
+if (_selectedShape == _panel
+ || PEd_guiShapeDelegatesRecursive(_panel, _selectedShape))
+{
+ _colourBorder = PEdColour.Active;
+}
+PEd_guiDrawRectangle(0, _titleBarHeight, _panelW, _panelH - _titleBarHeight, _colourBorder);
+PEd_guiDrawItem(_container, _border, _titleBarHeight);
+
+PEd_guiMatrixRestore();
diff --git a/PushEd.gmx/scripts/PEd_guiPanelGetContainer.gml b/PushEd.gmx/scripts/PEd_guiPanelGetContainer.gml
new file mode 100644
index 00000000..65526741
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiPanelGetContainer.gml
@@ -0,0 +1,8 @@
+/// PEd_guiPanelGetContainer(panel)
+/**
+ * @brief Gets the container of the panel.
+ * @param {real} panel The id of the panel.
+ * @return {real} The container of the panel.
+ */
+gml_pragma("forceinline");
+return argument0[? "container"];
diff --git a/PushEd.gmx/scripts/PEd_guiPanelGetTitleBar.gml b/PushEd.gmx/scripts/PEd_guiPanelGetTitleBar.gml
new file mode 100644
index 00000000..9f941f4d
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiPanelGetTitleBar.gml
@@ -0,0 +1,8 @@
+/// PEd_guiPanelGetTitleBar(panel)
+/**
+ * @brief Gets the title bar of the panel.
+ * @param {real} panel The id of the panel.
+ * @return {real} The title bar of the panel.
+ */
+gml_pragma("forceinline");
+return argument0[? "titleBar"];
diff --git a/PushEd.gmx/scripts/PEd_guiPanelSetContent.gml b/PushEd.gmx/scripts/PEd_guiPanelSetContent.gml
new file mode 100644
index 00000000..bb7b0dcb
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiPanelSetContent.gml
@@ -0,0 +1,8 @@
+/// PEd_guiPanelSetContent(panel, content)
+/**
+ * @brief Sets the content of the panel.
+ * @param {real} panel The id of the panel.
+ * @param {real} content The new content script.
+ */
+var _container = argument0[? "container"];
+_container[? "content"] = argument1;
diff --git a/PushEd.gmx/scripts/PEd_guiPanelSetTitleBar.gml b/PushEd.gmx/scripts/PEd_guiPanelSetTitleBar.gml
new file mode 100644
index 00000000..69b63558
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiPanelSetTitleBar.gml
@@ -0,0 +1,8 @@
+/// PEd_guiPanelSetTitleBar(panel, content)
+/**
+ * @brief Sets the title bar of the panel.
+ * @param {real} panel The id of the panel.
+ * @param {real} content The new content script of the panels title bar.
+ */
+var _titleBar = argument0[? "titleBar"];
+_titleBar[? "content"] = argument1;
diff --git a/PushEd.gmx/scripts/PEd_guiPushMouseCoordinates.gml b/PushEd.gmx/scripts/PEd_guiPushMouseCoordinates.gml
new file mode 100644
index 00000000..0d85f27c
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiPushMouseCoordinates.gml
@@ -0,0 +1,30 @@
+/// PEd_guiPushMouseCoordinates(shape)
+/**
+ * @brief Pushes mouse coordinates to be relative to the shape.
+ * @param {real} shape The id of the shape.
+ */
+var _shape = argument0;
+var _x = PEd_guiShapeGetX(_shape);
+var _y = PEd_guiShapeGetY(_shape);
+var _scrollX = 0;
+var _scrollY = 0;
+var _scrollbarHor = _shape[? "scrollbarHor"]
+var _scrollbarVer = _shape[? "scrollbarVer"];
+
+if (!is_undefined(_scrollbarHor))
+{
+ _scrollX = PEd_guiScrollbarGetScroll(_scrollbarHor) * PEd_guiScrollbarIsVisible(_scrollbarHor);
+}
+if (!is_undefined(_scrollbarVer))
+{
+ _scrollY = PEd_guiScrollbarGetScroll(_scrollbarVer) * PEd_guiScrollbarIsVisible(_scrollbarVer);
+}
+
+guiMouseX += -_x + _scrollX;
+guiMouseY += -_y + _scrollY;
+
+var _delegate = PEd_guiShapeGetDelegate(_shape);
+if (PEd_guiShapeExists(_delegate))
+{
+ PEd_guiPushMouseCoordinates(_delegate);
+}
diff --git a/PushEd.gmx/scripts/PEd_guiRedrawWindow.gml b/PushEd.gmx/scripts/PEd_guiRedrawWindow.gml
new file mode 100644
index 00000000..c9bc1939
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiRedrawWindow.gml
@@ -0,0 +1,7 @@
+/// PEd_guiRedrawWindow(window)
+/**
+ * @brief Request redraw of the window.
+ * @param {real} window The id of the window.
+ */
+PEd_guiRequestRedraw(argument0[? "container"]);
+PEd_guiRequestRedraw(argument0[? "titleBar"]);
diff --git a/PushEd.gmx/scripts/PEd_guiRequestRedraw.gml b/PushEd.gmx/scripts/PEd_guiRequestRedraw.gml
new file mode 100644
index 00000000..86e4d11e
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiRequestRedraw.gml
@@ -0,0 +1,11 @@
+/// PEd_guiRequestRedraw(shape)
+/**
+ * @brief Pushes a redraw request of the given shape to the delegate.
+ * @param {real} shape The id of the shape to redraw.
+ */
+var _shape = argument0;
+while (PEd_guiShapeExists(_shape))
+{
+ PEd_guiShapeSetRedraw(_shape, true);
+ _shape = PEd_guiShapeGetDelegate(_shape);
+}
diff --git a/PushEd.gmx/scripts/PEd_guiRequestRedrawAll.gml b/PushEd.gmx/scripts/PEd_guiRequestRedrawAll.gml
new file mode 100644
index 00000000..d52d950f
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiRequestRedrawAll.gml
@@ -0,0 +1,14 @@
+/// PEd_guiRequestRedrawAll(shape)
+/**
+ * @brief Requests redraw of all child shapes.
+ * @param {real} shape The id of the shape.
+ */
+PEd_guiShapeSetRedraw(argument0, true);
+var _items = PEd_guiCompoundShapeGetItems(argument0);
+if (!is_undefined(_items))
+{
+ for (var i = ds_list_size(_items) - 1; i >= 0; i--)
+ {
+ PEd_guiRequestRedrawAll(_items[| i]);
+ }
+}
diff --git a/PushEd.gmx/scripts/PEd_guiScrollbarCalcJumpAndThumbSize.gml b/PushEd.gmx/scripts/PEd_guiScrollbarCalcJumpAndThumbSize.gml
new file mode 100644
index 00000000..4f89c250
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiScrollbarCalcJumpAndThumbSize.gml
@@ -0,0 +1,17 @@
+/// PEd_guiScrollbarCalcJumpAndThumbSize(scrollbar)
+/**
+ * @brief Calculates the jump value and thumb size of the scrollbar.
+ * @param {real} scrollbar The id of the scrollbar.
+ */
+var _size = argument0[? "size"];
+var _contentSize = argument0[? "contentSize"];
+var _minThumbSize = argument0[? "minThumbSize"];
+
+var _viewableRatio = _size / _contentSize;
+var _scrollBarArea = _size;
+var _thumbSize = max(_minThumbSize, _scrollBarArea * _viewableRatio);
+argument0[? "thumbSize"] = _thumbSize;
+
+var _scrollTrackSpace = _contentSize - _size;
+var _scrollThumbSpace = _size - _thumbSize;
+argument0[? "scrollJump"] = _scrollTrackSpace / _scrollThumbSpace;
diff --git a/PushEd.gmx/scripts/PEd_guiScrollbarGetScroll.gml b/PushEd.gmx/scripts/PEd_guiScrollbarGetScroll.gml
new file mode 100644
index 00000000..6da9dbb7
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiScrollbarGetScroll.gml
@@ -0,0 +1,8 @@
+/// PEd_guiScrollbarGetScroll(scrollbar)
+/**
+ * @brief Gets the scroll of the given scrollbar.
+ * @param {real} scrollbar The id of the scrollbar.
+ * @return {real} The content scroll.
+ */
+gml_pragma("forceinline");
+return round(argument0[? "scroll"] * argument0[? "scrollJump"]);
diff --git a/PushEd.gmx/scripts/PEd_guiScrollbarHorDraw.gml b/PushEd.gmx/scripts/PEd_guiScrollbarHorDraw.gml
new file mode 100644
index 00000000..44f056ba
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiScrollbarHorDraw.gml
@@ -0,0 +1,18 @@
+/// PEd_guiScrollbarHorDraw(scrollbarHor)
+/**
+ * @brief Draws the horizontal scrollbar.
+ * @param {real} scrollbarHor The id of the horizontal scrollbar.
+ */
+if (PEd_guiScrollbarIsVisible(argument0))
+{
+ // Thumb
+ var _x = PEd_guiShapeGetX(argument0) + argument0[? "scroll"];
+ var _y = PEd_guiShapeGetY(argument0);
+ var _thumbSize = argument0[? "thumbSize"];
+ var _sprite = argument0[? "sprite"];
+ var _spriteSize = argument0[? "spriteSize"];
+ var _alpha = 0.75;
+ draw_sprite_ext(_sprite, 0, _x, _y, 1, 1, 0, PEdColour.Scrollbar, _alpha);
+ draw_sprite_stretched_ext(_sprite, 1, _x + _spriteSize, _y, _thumbSize - _spriteSize * 2, PEd_guiShapeGetHeight(argument0), PEdColour.Scrollbar, _alpha);
+ draw_sprite_ext(_sprite, 2, _x + _thumbSize - _spriteSize, _y, 1, 1, 0, PEdColour.Scrollbar, _alpha);
+}
diff --git a/PushEd.gmx/scripts/PEd_guiScrollbarHorUpdate.gml b/PushEd.gmx/scripts/PEd_guiScrollbarHorUpdate.gml
new file mode 100644
index 00000000..64a86926
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiScrollbarHorUpdate.gml
@@ -0,0 +1,54 @@
+/// PEd_guiScrollbarHorUpdate(scrollbarHor)
+/**
+ * @brief Updates the horizontal scrollbar.
+ * @param {real} scrollbarHor The id of the horizontal scrollbar.
+ */
+var _scrollbar = argument0;
+_scrollbar[? "size"] = PEd_guiShapeGetWidth(_scrollbar);
+
+if (keyboard_check(vk_control))
+{
+ PEd_guiScrollbarUpdate(_scrollbar);
+}
+else
+{
+ PEd_guiShapeUpdate(_scrollbar);
+}
+
+// Start scrolling with mouse click
+// TODO: Fix scrollbars so that the commented code works
+if (guiShapeHovered == _scrollbar
+ && mouse_check_button_pressed(mb_left))
+{
+ var _x = _scrollbar[? "scroll"];
+
+ if (guiShapeHovered == _scrollbar
+ && guiMouseX > _x
+ && guiMouseX < _x + _scrollbar[? "thumbSize"])
+ {
+ //_scrollbar[? "mouseOffset"] = _x - guiMouseX;
+ _scrollbar[? "mouseOffset"] = windowMouseX;
+ guiShapeActive = _scrollbar;
+ }
+}
+
+// Stop scrolling
+if (mouse_check_button_released(mb_left)
+ && guiShapeActive == _scrollbar)
+{
+ guiShapeActive = noone;
+}
+
+// Handle scrolling
+if (PEd_guiScrollbarIsVisible(_scrollbar))
+{
+ var _scroll = _scrollbar[? "scroll"];
+ if (guiShapeActive == _scrollbar)
+ {
+ //_scroll = guiMouseX + _scrollbar[? "mouseOffset"];
+ _scroll += windowMouseX - _scrollbar[? "mouseOffset"];
+ _scrollbar[? "mouseOffset"] = windowMouseX;
+ }
+ _scroll = clamp(_scroll, 0, PEd_guiShapeGetWidth(_scrollbar) - _scrollbar[? "thumbSize"]);
+ _scrollbar[? "scroll"] = _scroll;
+}
diff --git a/PushEd.gmx/scripts/PEd_guiScrollbarIsVisible.gml b/PushEd.gmx/scripts/PEd_guiScrollbarIsVisible.gml
new file mode 100644
index 00000000..83742113
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiScrollbarIsVisible.gml
@@ -0,0 +1,23 @@
+/// PEd_guiScrollbarIsVisible(scrollbar)
+/**
+ * @brief Finds out whether the scrollbar is visible.
+ * @param {real} scrollbar The id of the scrollbar.
+ * @return {bool} True if the scrollbar is visible.
+ */
+var _scrollbar = argument0;
+var _delegate = PEd_guiShapeGetDelegate(_scrollbar);
+if (_delegate[? "scrollbarHor"] == _scrollbar)
+{
+ if (_scrollbar[? "contentSize"] > PEd_guiShapeGetWidth(_delegate))
+ {
+ return true;
+ }
+}
+if (_delegate[? "scrollbarVer"] == _scrollbar)
+{
+ if (_scrollbar[? "contentSize"] > PEd_guiShapeGetHeight(_delegate))
+ {
+ return true;
+ }
+}
+return false;
diff --git a/PushEd.gmx/scripts/PEd_guiScrollbarSetScroll.gml b/PushEd.gmx/scripts/PEd_guiScrollbarSetScroll.gml
new file mode 100644
index 00000000..cceeac4a
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiScrollbarSetScroll.gml
@@ -0,0 +1,7 @@
+/// PEd_guiScrollbarSetScroll(scrollbar, scroll)
+/**
+ * @brief Sets scrollbar's scroll to the given value.
+ * @param {real} scrollbar The id of the scrollbar.
+ * @param {real} scroll The new scroll value.
+ */
+argument0[? "scroll"] = argument1 / argument0[? "scrollJump"];
diff --git a/PushEd.gmx/scripts/PEd_guiScrollbarUpdate.gml b/PushEd.gmx/scripts/PEd_guiScrollbarUpdate.gml
new file mode 100644
index 00000000..de034c1c
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiScrollbarUpdate.gml
@@ -0,0 +1,23 @@
+/// PEd_guiScrollbarUpdate(scrollbar)
+/**
+ * @brief Updates the scrollbar.
+ * @param {real} scrollbar The id of the scrollbar.
+ */
+var _scrollbar = argument0;
+PEd_guiShapeUpdate(_scrollbar);
+PEd_guiScrollbarCalcJumpAndThumbSize(_scrollbar);
+
+var _delegate = PEd_guiShapeGetDelegate(_scrollbar);
+
+if (PEd_guiShapeIsHovered(_scrollbar)
+ || (PEd_guiShapeExists(_delegate)
+ && (PEd_guiShapeIsHovered(_delegate)
+ || PEd_guiShapeDelegatesRecursive(_delegate, guiShapeHovered))))
+{
+ var _wheel = (mouse_wheel_down() - mouse_wheel_up()) * 2 * guiFontHeight / _scrollbar[? "scrollJump"];
+ if (_wheel != 0)
+ {
+ _scrollbar[? "scroll"] = clamp(_scrollbar[? "scroll"] + _wheel, 0, _scrollbar[? "size"] - _scrollbar[? "thumbSize"]);
+ PEd_guiRequestRedraw(_scrollbar);
+ }
+}
diff --git a/PushEd.gmx/scripts/PEd_guiScrollbarVerDraw.gml b/PushEd.gmx/scripts/PEd_guiScrollbarVerDraw.gml
new file mode 100644
index 00000000..e20ae181
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiScrollbarVerDraw.gml
@@ -0,0 +1,18 @@
+/// PEd_guiScrollbarVerDraw(scrollbarVer)
+/**
+ * @brief Draws the vertical scrollbar.
+ * @param {real} scrollbarVer The id of the vertical scrollbar.
+ */
+if (PEd_guiScrollbarIsVisible(argument0))
+{
+ // Thumb
+ var _x = PEd_guiShapeGetX(argument0);
+ var _y = PEd_guiShapeGetY(argument0) + argument0[? "scroll"];
+ var _thumbSize = argument0[? "thumbSize"];
+ var _sprite = argument0[? "sprite"];
+ var _spriteSize = argument0[? "spriteSize"];
+ var _alpha = 0.75;
+ draw_sprite_ext(_sprite, 0, _x, _y, 1, 1, 0, PEdColour.Scrollbar, _alpha);
+ draw_sprite_stretched_ext(_sprite, 1, _x, _y + _spriteSize, PEd_guiShapeGetWidth(argument0), _thumbSize - _spriteSize * 2, PEdColour.Scrollbar, _alpha);
+ draw_sprite_ext(_sprite, 2, _x, _y + _thumbSize - _spriteSize, 1, 1, 0, PEdColour.Scrollbar, _alpha);
+}
diff --git a/PushEd.gmx/scripts/PEd_guiScrollbarVerUpdate.gml b/PushEd.gmx/scripts/PEd_guiScrollbarVerUpdate.gml
new file mode 100644
index 00000000..18b951c6
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiScrollbarVerUpdate.gml
@@ -0,0 +1,54 @@
+/// PEd_guiScrollbarVerUpdate(scrollbarVer)
+/**
+ * @brief Updates the vertical scrollbar.
+ * @param {real} scrollbarVer The id of the vertical scrollbar.
+ */
+var _scrollbar = argument0;
+
+_scrollbar[? "size"] = PEd_guiShapeGetHeight(_scrollbar);
+if (!keyboard_check(vk_control))
+{
+ PEd_guiScrollbarUpdate(_scrollbar);
+}
+else
+{
+ PEd_guiShapeUpdate(_scrollbar);
+}
+
+// Start scrolling with mouse click
+// TODO: Fix scrollbars so that the commented code works
+if (guiShapeHovered == _scrollbar
+ && mouse_check_button_pressed(mb_left))
+{
+ var _y = _scrollbar[? "scroll"];
+
+ if (guiShapeHovered == _scrollbar
+ && guiMouseY > _y
+ && guiMouseY < _y + _scrollbar[? "thumbSize"])
+ {
+ //_scrollbar[? "mouseOffset"] = _y - guiMouseY;
+ _scrollbar[? "mouseOffset"] = windowMouseY;
+ guiShapeActive = _scrollbar;
+ }
+}
+
+// Stop scrolling
+if (mouse_check_button_released(mb_left)
+ && guiShapeActive == _scrollbar)
+{
+ guiShapeActive = noone;
+}
+
+// Handle scrolling
+if (PEd_guiScrollbarIsVisible(_scrollbar))
+{
+ var _scroll = _scrollbar[? "scroll"];
+ if (guiShapeActive == _scrollbar)
+ {
+ //_scroll = guiMouseY + _scrollbar[? "mouseOffset"];
+ _scroll += windowMouseY - _scrollbar[? "mouseOffset"];
+ _scrollbar[? "mouseOffset"] = windowMouseY;
+ }
+ _scroll = clamp(_scroll, 0, PEd_guiShapeGetHeight(_scrollbar) - _scrollbar[? "thumbSize"]);
+ _scrollbar[? "scroll"] = _scroll;
+}
diff --git a/PushEd.gmx/scripts/PEd_guiShapeCleanUp.gml b/PushEd.gmx/scripts/PEd_guiShapeCleanUp.gml
new file mode 100644
index 00000000..8c06aea7
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiShapeCleanUp.gml
@@ -0,0 +1,23 @@
+/// PEd_guiShapeCleanUp(shape)
+/**
+ * @brief Frees resources used by the shape from memory.
+ * @param {real} shape The id of the shape.
+ */
+var _shape = argument0;
+if (PEd_guiShapeExists(_shape))
+{
+ // Remove from delegate
+ var _delegate = PEd_guiShapeGetDelegate(_shape);
+ if (PEd_guiShapeExists(_delegate))
+ {
+ var _items = PEd_guiCompoundShapeGetItems(_delegate);
+ var _pos = ds_list_find_index(_items, _shape);
+ if (_pos >= 0)
+ {
+ ds_list_delete(_items, _pos);
+ }
+ }
+
+ // Destroy self
+ ds_map_destroy(_shape);
+}
diff --git a/PushEd.gmx/scripts/PEd_guiShapeDelegatesRecursive.gml b/PushEd.gmx/scripts/PEd_guiShapeDelegatesRecursive.gml
new file mode 100644
index 00000000..5b000a81
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiShapeDelegatesRecursive.gml
@@ -0,0 +1,25 @@
+/// PEd_guiShapeDelegatesRecursive(shape, item)
+/**
+ * @brief Finds out whether the shape delegates the item.
+ * @param {real} shape The id of the shape.
+ * @param {real} item The id of the shape.
+ * @return {bool} True if the shape delegates the item.
+ * @note If the shape is not the delegate of the item, this
+ * function is called recursively for the item's delegate,
+ * until the shape is found or the item does not have delegate.
+ * This way you can check for chained delegation. Wtf?!
+ */
+if (!PEd_guiShapeExists(argument1))
+{
+ return false;
+}
+var _delegate = PEd_guiShapeGetDelegate(argument1);
+if (!PEd_guiShapeExists(_delegate))
+{
+ return false;
+}
+if (_delegate == argument0)
+{
+ return true;
+}
+return PEd_guiShapeDelegatesRecursive(argument0, _delegate);
diff --git a/PushEd.gmx/scripts/PEd_guiShapeExists.gml b/PushEd.gmx/scripts/PEd_guiShapeExists.gml
new file mode 100644
index 00000000..6019d8b5
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiShapeExists.gml
@@ -0,0 +1,11 @@
+/// PEd_guiShapeExists(shape)
+/**
+ * @brief Finds out whether the shape exists.
+ * @param {real} shape The id of the shape.
+ * @return {bool} True if the shape does exist.
+ */
+if (!is_real(argument0))
+{
+ return false;
+}
+return ds_exists(argument0, ds_type_map);
diff --git a/PushEd.gmx/scripts/PEd_guiShapeGetDelegate.gml b/PushEd.gmx/scripts/PEd_guiShapeGetDelegate.gml
new file mode 100644
index 00000000..5e6b691d
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiShapeGetDelegate.gml
@@ -0,0 +1,8 @@
+/// PEd_guiShapeGetDelegate(shape)
+/**
+ * @brief Gets the delegate of the shape.
+ * @param {real} shape The id of the shape.
+ * @return {real} The id of the delegate or noone.
+ */
+gml_pragma("forceinline");
+return argument0[? "delegate"];
diff --git a/PushEd.gmx/scripts/PEd_guiShapeGetDepth.gml b/PushEd.gmx/scripts/PEd_guiShapeGetDepth.gml
new file mode 100644
index 00000000..36d6f589
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiShapeGetDepth.gml
@@ -0,0 +1,8 @@
+/// PEd_guiShapeGetDepth(shape)
+/**
+ * @brief Gets the depth of the shape.
+ * @param {real} shape The id of the shape.
+ * @return {real} The depth of the shape.
+ */
+gml_pragma("forceinline");
+return argument0[? "depth"];
diff --git a/PushEd.gmx/scripts/PEd_guiShapeGetHeight.gml b/PushEd.gmx/scripts/PEd_guiShapeGetHeight.gml
new file mode 100644
index 00000000..4f3aa073
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiShapeGetHeight.gml
@@ -0,0 +1,8 @@
+/// PEd_guiShapeGetHeight(shape)
+/**
+ * @brief Gets the height of the shape.
+ * @param {real} shape The id of the shape.
+ * @return {real} The height of the shape.
+ */
+gml_pragma("forceinline");
+return argument0[? "height"];
diff --git a/PushEd.gmx/scripts/PEd_guiShapeGetRedraw.gml b/PushEd.gmx/scripts/PEd_guiShapeGetRedraw.gml
new file mode 100644
index 00000000..14bb3b77
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiShapeGetRedraw.gml
@@ -0,0 +1,8 @@
+/// PEd_guiShapeGetRedraw(shape)
+/**
+ * @brief Gets the redraw state of the shape.
+ * @param {real} shape The id of the shape.
+ * @return {bool} The redraw state of the shape.
+ */
+gml_pragma("forceinline");
+return argument0[? "redraw"];
diff --git a/PushEd.gmx/scripts/PEd_guiShapeGetTooltip.gml b/PushEd.gmx/scripts/PEd_guiShapeGetTooltip.gml
new file mode 100644
index 00000000..363a8684
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiShapeGetTooltip.gml
@@ -0,0 +1,12 @@
+/// PEd_guiShapeGetTooltip(shape)
+/**
+ * @brief Gets the tooltip of the shape.
+ * @param {real} shape The id of the shape.
+ * @return {string} The shape tooltip or an empty string if it does not have any.
+ */
+var _tooltip = argument0[? "tooltip"];
+if (is_undefined(_tooltip))
+{
+ return "";
+}
+return _tooltip;
diff --git a/PushEd.gmx/scripts/PEd_guiShapeGetType.gml b/PushEd.gmx/scripts/PEd_guiShapeGetType.gml
new file mode 100644
index 00000000..5231d292
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiShapeGetType.gml
@@ -0,0 +1,8 @@
+/// PEd_guiShapeGetType(shape)
+/**
+ * @brief Gets the type of the shape.
+ * @param {real} shape The id of the shape.
+ * @return {PEdGUIShape} The type of the shape.
+ */
+gml_pragma("forceinline");
+return argument0[? "type"];
diff --git a/PushEd.gmx/scripts/PEd_guiShapeGetWidth.gml b/PushEd.gmx/scripts/PEd_guiShapeGetWidth.gml
new file mode 100644
index 00000000..f8d8586d
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiShapeGetWidth.gml
@@ -0,0 +1,8 @@
+/// PEd_guiShapeGetWidth(shape)
+/**
+ * @brief Gets the width of the shape.
+ * @param {real} shape The id of the shape.
+ * @return {real} The width of the shape.
+ */
+gml_pragma("forceinline");
+return argument0[? "width"];
diff --git a/PushEd.gmx/scripts/PEd_guiShapeGetX.gml b/PushEd.gmx/scripts/PEd_guiShapeGetX.gml
new file mode 100644
index 00000000..abb86353
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiShapeGetX.gml
@@ -0,0 +1,8 @@
+/// PEd_guiShapeGetX(shape)
+/**
+ * @brief Gets the x position of the shape.
+ * @param {real} shape The id of the shape.
+ * @return {real} The x position of the shape relative to it's delegate.
+ */
+gml_pragma("forceinline");
+return argument0[? "x"];
diff --git a/PushEd.gmx/scripts/PEd_guiShapeGetY.gml b/PushEd.gmx/scripts/PEd_guiShapeGetY.gml
new file mode 100644
index 00000000..6f44ce48
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiShapeGetY.gml
@@ -0,0 +1,8 @@
+/// PEd_guiShapeGetY(shape)
+/**
+ * @brief Gets the y position of the shape.
+ * @param {real} shape The id of the shape.
+ * @return {real} The y position of the shape relative to it's delegate.
+ */
+gml_pragma("forceinline");
+return argument0[? "y"];
diff --git a/PushEd.gmx/scripts/PEd_guiShapeIsActive.gml b/PushEd.gmx/scripts/PEd_guiShapeIsActive.gml
new file mode 100644
index 00000000..d1aa461e
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiShapeIsActive.gml
@@ -0,0 +1,8 @@
+/// PEd_guiShapeIsActive(shape)
+/**
+ * @brief Gets whether the shape is active.
+ * @param {real} shape The id of the shape.
+ * @return {bool} True if the shape is active.
+ */
+gml_pragma("forceinline");
+return (guiShapeActive == argument0);
diff --git a/PushEd.gmx/scripts/PEd_guiShapeIsHovered.gml b/PushEd.gmx/scripts/PEd_guiShapeIsHovered.gml
new file mode 100644
index 00000000..fce2bdd8
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiShapeIsHovered.gml
@@ -0,0 +1,10 @@
+/// PEd_guiShapeIsHovered(shape)
+/**
+ * @brief Gets whether the shape is hovered.
+ * @param {real} shape The id of the shape.
+ * @return {bool} True if the shape is hovered.
+ */
+gml_pragma("forceinline");
+return (guiShapeHovered == argument0
+ && (!PEd_guiShapeExists(guiShapeActive)
+ || guiShapeActive == argument0));
diff --git a/PushEd.gmx/scripts/PEd_guiShapeIsSelected.gml b/PushEd.gmx/scripts/PEd_guiShapeIsSelected.gml
new file mode 100644
index 00000000..5fa5a5ad
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiShapeIsSelected.gml
@@ -0,0 +1,8 @@
+/// PEd_guiShapeIsSelected(shape)
+/**
+ * @brief Gets whether the shape is selected.
+ * @param {real} shape The id of the shape.
+ * @return {bool} True if the shape is selected.
+ */
+gml_pragma("forceinline");
+return (guiShapeSelected == argument0);
diff --git a/PushEd.gmx/scripts/PEd_guiShapeSetDelegate.gml b/PushEd.gmx/scripts/PEd_guiShapeSetDelegate.gml
new file mode 100644
index 00000000..f858a557
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiShapeSetDelegate.gml
@@ -0,0 +1,7 @@
+/// PEd_guiShapeSetDelegate(shape, delegate)
+/**
+ * @brief Sets the delegate of the shape.
+ * @param {real} shape The id of the shape.
+ * @param {real} delegate The id of the delegate or noone.
+ */
+argument0[? "delegate"] = argument1;
diff --git a/PushEd.gmx/scripts/PEd_guiShapeSetDepth.gml b/PushEd.gmx/scripts/PEd_guiShapeSetDepth.gml
new file mode 100644
index 00000000..b8ca3a1b
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiShapeSetDepth.gml
@@ -0,0 +1,7 @@
+/// PEd_guiShapeSetDepth(shape, depth)
+/**
+ * @brief Sets the depth of the shape.
+ * @param {real} shape The id of the shape.
+ * @param {real} depth The new depth.
+ */
+argument0[? "depth"] = argument1;
diff --git a/PushEd.gmx/scripts/PEd_guiShapeSetHeight.gml b/PushEd.gmx/scripts/PEd_guiShapeSetHeight.gml
new file mode 100644
index 00000000..863bb3fa
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiShapeSetHeight.gml
@@ -0,0 +1,7 @@
+/// PEd_guiShapeSetHeight(shape, height)
+/**
+ * @brief Sets the width of the shape.
+ * @param {real} shape The id of the shape.
+ * @param {real} height The new height.
+ */
+argument0[? "height"] = max(argument1, 1);
diff --git a/PushEd.gmx/scripts/PEd_guiShapeSetPosition.gml b/PushEd.gmx/scripts/PEd_guiShapeSetPosition.gml
new file mode 100644
index 00000000..b0b9787e
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiShapeSetPosition.gml
@@ -0,0 +1,10 @@
+/// PEd_guiShapeSetPosition(shape, x, y)
+/**
+ * @brief Sets the x and y position of the shape relative to it's delegate.
+ * @param {real} shape The id of the shape.
+ * @param {real} x The new x position.
+ * @param {real} y The new y position.
+ */
+gml_pragma("forceinline");
+PEd_guiShapeSetX(argument0, argument1);
+PEd_guiShapeSetY(argument0, argument2);
diff --git a/PushEd.gmx/scripts/PEd_guiShapeSetRectangle.gml b/PushEd.gmx/scripts/PEd_guiShapeSetRectangle.gml
new file mode 100644
index 00000000..e78a5deb
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiShapeSetRectangle.gml
@@ -0,0 +1,14 @@
+/// PEd_guiShapeSetRectangle(shape, x, y, width, height)
+/**
+ * @brief Sets the x and y position of the shape relative to it's delegate and it's size.
+ * @param {real} shape The id of the shape.
+ * @param {real} x The new x position.
+ * @param {real} y The new y position.
+ * @param {real} width The new width.
+ * @param {real} height The new height.
+ */
+gml_pragma("forceinline");
+PEd_guiShapeSetX(argument0, argument1);
+PEd_guiShapeSetY(argument0, argument2);
+PEd_guiShapeSetWidth(argument0, argument3);
+PEd_guiShapeSetHeight(argument0, argument4);
diff --git a/PushEd.gmx/scripts/PEd_guiShapeSetRedraw.gml b/PushEd.gmx/scripts/PEd_guiShapeSetRedraw.gml
new file mode 100644
index 00000000..d18304e2
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiShapeSetRedraw.gml
@@ -0,0 +1,7 @@
+/// PEd_guiShapeSetRedraw(shape, redraw)
+/**
+ * @brief Sets the redraw state of the shape.
+ * @param {real} shape The id of the shape.
+ * @param {bool} redraw The new redraw state.
+ */
+argument0[? "redraw"] = argument1;
diff --git a/PushEd.gmx/scripts/PEd_guiShapeSetSize.gml b/PushEd.gmx/scripts/PEd_guiShapeSetSize.gml
new file mode 100644
index 00000000..c4d795a5
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiShapeSetSize.gml
@@ -0,0 +1,10 @@
+/// PEd_guiShapeSetSize(shape, width, height)
+/**
+ * @brief Sets the width and height of the shape.
+ * @param {real} shape The id of the shape.
+ * @param {real} width The new width.
+ * @param {real} height The new height.
+ */
+gml_pragma("forceinline");
+PEd_guiShapeSetWidth(argument0, argument1);
+PEd_guiShapeSetHeight(argument0, argument2);
diff --git a/PushEd.gmx/scripts/PEd_guiShapeSetTooltip.gml b/PushEd.gmx/scripts/PEd_guiShapeSetTooltip.gml
new file mode 100644
index 00000000..b946e867
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiShapeSetTooltip.gml
@@ -0,0 +1,7 @@
+/// PEd_guiShapeSetTooltip(shape, text)
+/**
+ * @brief Sets the tooltip of the shape.
+ * @param {real} shape The id of the shape.
+ * @param {string} text The tooltip text.
+ */
+argument0[? "tooltip"] = argument1;
diff --git a/PushEd.gmx/scripts/PEd_guiShapeSetType.gml b/PushEd.gmx/scripts/PEd_guiShapeSetType.gml
new file mode 100644
index 00000000..7fdade56
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiShapeSetType.gml
@@ -0,0 +1,7 @@
+/// PEd_guiShapeSetType(shape, type)
+/**
+ * @brief Sets the type of the shape.
+ * @param {real} shape The id of the shape.
+ * @param {PEdGUIShape} type The new type.
+ */
+argument0[? "type"] = argument1;
diff --git a/PushEd.gmx/scripts/PEd_guiShapeSetWidth.gml b/PushEd.gmx/scripts/PEd_guiShapeSetWidth.gml
new file mode 100644
index 00000000..1a2a6e95
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiShapeSetWidth.gml
@@ -0,0 +1,7 @@
+/// PEd_guiShapeSetWidth(shape, width)
+/**
+ * @brief Sets the width of the shape.
+ * @param {real} shape The id of the shape.
+ * @param {real} width The new width.
+ */
+argument0[? "width"] = max(argument1, 1);
diff --git a/PushEd.gmx/scripts/PEd_guiShapeSetX.gml b/PushEd.gmx/scripts/PEd_guiShapeSetX.gml
new file mode 100644
index 00000000..3e5a9b81
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiShapeSetX.gml
@@ -0,0 +1,7 @@
+/// PEd_guiShapeSetX(shape, x)
+/**
+ * @brief Sets the x position of the shape relative to it's delegate.
+ * @param {real} shape The id of the shape.
+ * @param {real} x The new x position.
+ */
+argument0[? "x"] = argument1;
diff --git a/PushEd.gmx/scripts/PEd_guiShapeSetY.gml b/PushEd.gmx/scripts/PEd_guiShapeSetY.gml
new file mode 100644
index 00000000..2abb6704
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiShapeSetY.gml
@@ -0,0 +1,7 @@
+/// PEd_guiShapeSetY(shape, y)
+/**
+ * @brief Sets the y position of the shape relative to it's delegate.
+ * @param {real} shape The id of the shape.
+ * @param {real} y The new y position.
+ */
+argument0[? "y"] = argument1;
diff --git a/PushEd.gmx/scripts/PEd_guiShapeUpdate.gml b/PushEd.gmx/scripts/PEd_guiShapeUpdate.gml
new file mode 100644
index 00000000..e9c0f2d9
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiShapeUpdate.gml
@@ -0,0 +1,25 @@
+/// PEd_guiShapeUpdate(shape)
+/**
+ * @brief Updates the shape.
+ * @param {real} shape The id of the shape.
+ */
+var _shape = argument0;
+//PEd_guiShapeSetSize(_shape, PEd_guiShapeGetWidth(_shape), PEd_guiShapeGetHeight(_shape));
+
+// Set tooltip on mouse over
+if (PEd_guiShapeIsHovered(_shape))
+{
+ guiTooltip = PEd_guiShapeGetTooltip(_shape);
+
+ // Select shape
+ if (mouse_check_button_pressed(mb_any))
+ {
+ var _exists = PEd_guiShapeExists(guiShapeSelected);
+ if ((_exists && guiShapeSelected != _shape)
+ || !_exists)
+ {
+ PEd_guiRequestRedrawAll(guiRoot);
+ }
+ guiShapeSelected = _shape;
+ }
+}
diff --git a/PushEd.gmx/scripts/PEd_guiShowContextMenu.gml b/PushEd.gmx/scripts/PEd_guiShowContextMenu.gml
new file mode 100644
index 00000000..e51231c5
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiShowContextMenu.gml
@@ -0,0 +1,23 @@
+/// PEd_guiShowContextMenu(contextMenu, [x, y])
+/**
+ * @brief Shows the context menu.
+ * @param {real} contextMenu The id of the context menu to show.
+ * @param {real} [x] The x position to show the context menu at.
+ * @param {real} [y] The y position to show the context menu at.
+ * @note If the position coordinates are not specified, then the current
+ * window mouse position is used.
+ */
+if (guiContextMenu)
+{
+ PEd_guiDestroyShape(guiContextMenu);
+}
+guiContextMenu = argument[0];
+if (argument_count > 1)
+{
+ PEd_guiShapeSetPosition(guiContextMenu, argument[1], argument[2]);
+}
+else
+{
+ PEd_guiShapeSetPosition(guiContextMenu, windowMouseX, windowMouseY);
+}
+PEd_guiAddItem(guiRoot, guiContextMenu);
diff --git a/PushEd.gmx/scripts/PEd_guiShowPopupMessage.gml b/PushEd.gmx/scripts/PEd_guiShowPopupMessage.gml
new file mode 100644
index 00000000..f6f9babc
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiShowPopupMessage.gml
@@ -0,0 +1,16 @@
+/// PEd_guiShowPopupMessage(text, [ms])
+/**
+ * @brief Shows the popup message.
+ * @param {string} text The text of the popup message.
+ * @param {real} [ms] The duration of the popup message in ms.
+ */
+guiPopupMessage = argument[0];
+guiPopupTimer = current_time;
+if (argument_count > 1)
+{
+ guiPopupDuration = argument[1];
+}
+else
+{
+ guiPopupDuration = 2000;
+}
diff --git a/PushEd.gmx/scripts/PEd_guiToolbarButtonDraw.gml b/PushEd.gmx/scripts/PEd_guiToolbarButtonDraw.gml
new file mode 100644
index 00000000..fb51489c
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiToolbarButtonDraw.gml
@@ -0,0 +1,24 @@
+/// PEd_guiToolbarButtonDraw(toolbarButton)
+/**
+ * @brief Draws the toolbar button.
+ * @param {real} toolbarButton The id of the toolbar button.
+ */
+var _button = argument0;
+var _x = PEd_guiShapeGetX(_button);
+var _y = PEd_guiShapeGetY(_button);
+var _width = PEd_guiShapeGetWidth(_button);
+var _height = PEd_guiShapeGetHeight(_button);
+var _backgroundColour = PEdColour.WindowBackground;
+
+if (PEd_guiShapeIsHovered(_button))
+{
+ _backgroundColour = PEdColour.Highlight;
+}
+PEd_guiDrawRectangle(_x, _y, _width, _height, _backgroundColour);
+
+if (_button[? "highlight"])
+{
+ PEd_guiDrawRectangle(_x, _y + _height - 4, _width, 4, PEdColour.Active);
+}
+
+draw_sprite(_button[? "sprite"], _button[? "subimage"], _x, _y);
diff --git a/PushEd.gmx/scripts/PEd_guiToolbarButtonUpdate.gml b/PushEd.gmx/scripts/PEd_guiToolbarButtonUpdate.gml
new file mode 100644
index 00000000..e3c5257b
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiToolbarButtonUpdate.gml
@@ -0,0 +1,16 @@
+/// PEd_guiToolbarButtonUpdate(toolbarButton)
+/**
+ * @brief Updates the toolbar button.
+ * @param {real} toolbarButton The id of the toolbar button.
+ */
+PEd_guiShapeUpdate(argument0);
+
+if (PEd_guiShapeIsHovered(argument0)
+ && mouse_check_button_pressed(mb_left))
+{
+ var _scrAction = argument0[? "scrAction"];
+ if (_scrAction != noone)
+ {
+ script_execute(_scrAction);
+ }
+}
diff --git a/PushEd.gmx/scripts/PEd_guiUpdate.gml b/PushEd.gmx/scripts/PEd_guiUpdate.gml
new file mode 100644
index 00000000..1b903f8b
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiUpdate.gml
@@ -0,0 +1,235 @@
+/// PEd_guiUpdate()
+/**
+ * @brief Updates GUI.
+ */
+guiTooltip = "";
+guiMouseX = window_mouse_get_x();
+guiMouseY = window_mouse_get_y();
+PEd_guiShapeSetSize(guiRoot, windowWidth, windowHeight);
+
+// Find hovered shape
+var _lastHoveredShape = guiShapeHovered;
+if (!PEd_guiShapeExists(_lastHoveredShape))
+{
+ _lastHoveredShape = noone;
+}
+guiShapeHovered = PEd_guiFindShape(PEd_guiCompoundShapeGetItems(guiRoot), guiMouseX, guiMouseY);
+
+// Redraw last hovered shape
+if (PEd_guiShapeExists(_lastHoveredShape)
+ && guiShapeHovered != _lastHoveredShape)
+{
+ PEd_guiRequestRedraw(_lastHoveredShape);
+}
+
+// Reset active shape if it does not exist
+if (!PEd_guiShapeExists(guiShapeActive))
+{
+ guiShapeActive = noone;
+}
+
+// Reset selected shape if it does not exist
+if (!PEd_guiShapeExists(guiShapeSelected))
+{
+ guiShapeSelected = noone;
+}
+
+// Redraw active shape and push mouse coordinates
+if (PEd_guiShapeExists(guiShapeActive))
+{
+ PEd_guiRequestRedraw(guiShapeActive);
+ PEd_guiPushMouseCoordinates(guiShapeActive);
+}
+else if (PEd_guiShapeExists(guiShapeHovered))
+{
+ PEd_guiPushMouseCoordinates(guiShapeHovered);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Handle keyboard shortcuts
+if (keyboard_check_pressed(vk_anykey))
+{
+ PEd_guiLogKey(keyboard_key);
+}
+
+if (mouse_check_button_pressed(mb_any))
+{
+ PEd_guiLogKey(mouse_button);
+}
+
+//var _text = "";
+for (var i = ds_list_size(guiKeyLog) - 1; i >= 0; i--)
+{
+ var _isMouseButton = (guiKeyLog[| i] == mb_left
+ || guiKeyLog[| i] == mb_right
+ || guiKeyLog[| i] == mb_middle);
+ if ((!_isMouseButton && !keyboard_check(guiKeyLog[| i]))
+ || (_isMouseButton && !mouse_check_button(guiKeyLog[| i])))
+ {
+ ds_list_delete(guiKeyLog, i);
+ continue;
+ }
+ //_text = string(guiKeyLog[| i]) + ", " + _text;
+}
+//show_debug_message(_text);
+
+// Global
+var _shortcuts = guiRoot[? "keyboardShortcuts"];
+if (!is_undefined(_shortcuts))
+{
+ for (var i = ds_list_size(_shortcuts) - 1; i >= 0; i--)
+ {
+ PEd_guiKeyboardShortcutUpdate(_shortcuts[| i]);
+ }
+}
+
+// Selected shape
+if (PEd_guiShapeExists(guiShapeSelected))
+{
+ var _shortcuts = guiShapeSelected[? "keyboardShortcuts"];
+ if (!is_undefined(_shortcuts))
+ {
+ for (var i = ds_list_size(_shortcuts) - 1; i >= 0; i--)
+ {
+ PEd_guiKeyboardShortcutUpdate(_shortcuts[| i]);
+ }
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Update input
+if (guiInputActive != noone)
+{
+ var _delegate = PEd_guiDecodeID(guiInputActive);
+ PEd_guiRequestRedraw(_delegate);
+
+ var _inputStringLength = string_length(guiInputString);
+
+ // Multitype
+ guiInputMultitype = false;
+
+ if (keyboard_check_pressed(vk_anykey))
+ {
+ guiInputMultitype = true;
+ guiInputTimer = current_time;
+ }
+
+ if (current_time > guiInputTimer + 300)
+ {
+ guiInputMultitype = true;
+ }
+
+ // Type
+ var _keyboardStringLength = string_length(keyboard_string);
+
+ if (_keyboardStringLength > 0)
+ {
+ // Delete selected part
+ if (guiInputIndex[0] != guiInputIndex[1])
+ {
+ PEd_guiInputDeleteSelectedPart();
+ }
+
+ // Insert string
+ guiInputString = string_insert(keyboard_string, guiInputString, guiInputIndex[0]);
+ guiInputIndex[0] += _keyboardStringLength;
+ guiInputIndex[1] = guiInputIndex[0];
+ keyboard_string = "";
+ }
+
+ // Backspace
+ if (keyboard_check(vk_backspace) && guiInputMultitype)
+ {
+ if (guiInputIndex[0] == guiInputIndex[1])
+ {
+ guiInputString = string_delete(guiInputString, guiInputIndex[0] - 1, 1);
+ guiInputIndex[0] = max(guiInputIndex[0] - 1, 1);
+ guiInputIndex[1] = guiInputIndex[0];
+ }
+ else
+ {
+ PEd_guiInputDeleteSelectedPart();
+ }
+ }
+ else if (keyboard_check(vk_delete) && guiInputMultitype)
+ {
+ // Delete
+ if (guiInputIndex[0] != guiInputIndex[1])
+ {
+ PEd_guiInputDeleteSelectedPart();
+ }
+ else
+ {
+ guiInputString = string_delete(guiInputString, guiInputIndex[0], 1);
+ }
+ }
+
+ // Save string length
+ var _inputStringLength = string_length(guiInputString);
+
+ // Control
+ if (keyboard_check(vk_control))
+ {
+ if (keyboard_check_pressed(ord("A")))
+ {
+ PEd_guiInputSelectAll();
+ }
+ else if (keyboard_check_pressed(ord("D")))
+ {
+ PEd_guiInputDelete();
+ }
+ else if (keyboard_check_pressed(ord("X")))
+ {
+ PEd_guiInputCut();
+ }
+ else if (keyboard_check_pressed(ord("C")))
+ {
+ PEd_guiInputCopy();
+ }
+ else if (keyboard_check_pressed(ord("V")))
+ {
+ PEd_guiInputPaste();
+ _inputStringLength = string_length(guiInputString);
+ }
+ }
+
+ // Arrows
+ if (keyboard_check(vk_left) && guiInputMultitype)
+ {
+ guiInputIndex[1] = max(guiInputIndex[1] - 1, 1);
+
+ if (!keyboard_check(vk_shift))
+ {
+ guiInputIndex[0] = guiInputIndex[1];
+ }
+ }
+ else if (keyboard_check(vk_right) && guiInputMultitype)
+ {
+ guiInputIndex[1] = min(guiInputIndex[1] + 1, _inputStringLength + 1);
+
+ if (!keyboard_check(vk_shift))
+ {
+ guiInputIndex[0] = guiInputIndex[1];
+ }
+ }
+
+ // Home/end
+ if (keyboard_check_pressed(vk_home))
+ {
+ guiInputIndex[1] = 1;
+
+ if (!keyboard_check(vk_shift))
+ {
+ guiInputIndex[0] = guiInputIndex[1];
+ }
+ }
+ else if (keyboard_check_pressed(vk_end))
+ {
+ guiInputIndex[1] = _inputStringLength + 1;
+
+ if (!keyboard_check(vk_shift))
+ {
+ guiInputIndex[0] = guiInputIndex[1];
+ }
+ }
+}
diff --git a/PushEd.gmx/scripts/PEd_guiViewportDraw.gml b/PushEd.gmx/scripts/PEd_guiViewportDraw.gml
new file mode 100644
index 00000000..1eacd0e2
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiViewportDraw.gml
@@ -0,0 +1,122 @@
+/// PEd_guiViewportDraw(viewport)
+/**
+ * @brief Draws the viewport.
+ * @param {real} viewport The id of the viewport.
+ */
+var _viewport = argument0;
+var _viewportX = PEd_guiShapeGetX(_viewport);
+var _viewportY = PEd_guiShapeGetY(_viewport);
+var _viewportW = PEd_guiShapeGetWidth(_viewport);
+var _viewportH = PEd_guiShapeGetHeight(_viewport);
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Draw title bar
+//
+var _titleBar = PEd_guiViewportGetTitleBar(_viewport);
+PEd_guiShapeSetWidth(_titleBar, _viewportW);
+PEd_guiDrawItem(_titleBar, 0, 0);
+var _titleBarHeight = clamp(PEd_guiContainerGetContentHeight(_titleBar), 1, _viewportH - 1);
+PEd_guiShapeSetHeight(_titleBar, _titleBarHeight);
+
+// Draw border
+_viewportY += _titleBarHeight;
+_viewportH = max(_viewportH - _titleBarHeight, 2);
+
+var _selectedShape = PEd_guiGetSelectedShape();
+var _colourBorder = PEdColour.WindowBorder;
+if (_selectedShape == _viewport
+ || PEd_guiShapeDelegatesRecursive(_viewport, _selectedShape))
+{
+ _colourBorder = PEdColour.Active;
+}
+PEd_guiDrawRectangle(_viewportX, _viewportY, _viewportW, _viewportH, _colourBorder);
+
+_viewportX += 1;
+_viewportW = max(_viewportW - 2, 1);
+_viewportH -= 1;
+
+// Save viewport position
+var _pos = d3d_transform_vertex(_viewportX, _viewportY, 0);
+viewportX = _pos[0];
+viewportY = _pos[1];
+viewportWidth = max(_viewportW, 1);
+viewportHeight = max(_viewportH, 1);
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Draw viewport
+//
+if (global.pedUsing3D)
+{
+ draw_surface(application_surface, _viewportX, _viewportY);
+}
+else
+{
+ draw_surface_part(application_surface, 0, 0,
+ min(_viewportW, surface_get_width(application_surface)),
+ min(_viewportH, surface_get_height(application_surface)),
+ _viewportX, _viewportY);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Draw instance highlight
+//
+var _size = ds_list_size(selectedObjects);
+if (_size > 0
+ && surface_exists(surInstSelect))
+{
+ shader_set(PEd_shOutline);
+ shader_set_uniform_f(shader_get_uniform(PEd_shOutline, "uTexel"),
+ 1 / _viewportW, 1 / _viewportH);
+ draw_surface(surInstSelect, _viewportX, _viewportY);
+ shader_reset();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Debug
+//
+if (debugShow)
+{
+ var _x = _viewportX;
+ var _y = _viewportY;
+
+ if (global.pedUsing3D)
+ {
+ PEd_guiDrawTextShadow(_x, _y,
+ "Res = " + string(viewportWidth) + " x " + string(viewportHeight)
+ + "#CamPos = " + string(x) + " " + string(y) + " " + string(z)
+ + " Dir = " + string(direction) + " " + string(camPitch)
+ + "#Fov = " + string(camFOV)
+ + " ClipNear = " + string(camClipNear) + " ClipFar = " + string(camClipFar),
+ c_silver,
+ c_black);
+ _y += guiFontHeight * 3;
+ }
+ else
+ {
+ PEd_guiDrawTextShadow(_x, _y,
+ "Res = " + string(viewportWidth) + " x " + string(viewportHeight)
+ + "#ViewPos = " + string(view_xview[0]) + " " + string(view_yview[0])
+ + "#Zoom = " + string(1 / viewZoom),
+ c_silver,
+ c_black);
+ _y += guiFontHeight * 3;
+ }
+
+ // FPS
+ PEd_guiDrawTextShadow(_x, _y,
+ "Fps = " + string(fps) + " (" + string(fps_real) + ")",
+ merge_colour(c_maroon, c_green, fps / room_speed),
+ c_black);
+ _y += guiFontHeight;
+
+ // Scene details
+ PEd_guiDrawTextShadow(_x, _y,
+ "Inst = " + string(instance_count),
+ c_silver,
+ c_black);
+ _y += guiFontHeight;
+}
diff --git a/PushEd.gmx/scripts/PEd_guiViewportGetTitleBar.gml b/PushEd.gmx/scripts/PEd_guiViewportGetTitleBar.gml
new file mode 100644
index 00000000..0e5a519d
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiViewportGetTitleBar.gml
@@ -0,0 +1,8 @@
+/// PEd_guiViewportGetTitleBar(viewport)
+/**
+ * @brief Gets the title bar of the viewport.
+ * @param {real} viewport The id of the viewport.
+ * @return {real} The title bar of the viewport.
+ */
+gml_pragma("forceinline");
+return argument0[? "titleBar"];
diff --git a/PushEd.gmx/scripts/PEd_guiViewportUpdate.gml b/PushEd.gmx/scripts/PEd_guiViewportUpdate.gml
new file mode 100644
index 00000000..67aa53cf
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiViewportUpdate.gml
@@ -0,0 +1,30 @@
+/// PEd_guiViewportUpdate(viewport)
+/**
+ * @brief Updates the viewport.
+ * @param {real} viewport The id of the viewport.
+ */
+var _viewport = argument0;
+PEd_guiShapeUpdate(_viewport);
+
+// Resize viewport
+var _width = max(viewportWidth, 1);
+var _height = max(viewportHeight, 1);
+
+if (global.pedUsing3D)
+{
+ view_wview[0] = _width;
+ view_hview[0] = _height;
+}
+else
+{
+ view_wport[0] = _width;
+ view_hport[0] = _height;
+ view_wview[0] = view_wport[0] * viewZoom;
+ view_hview[0] = view_hport[0] * viewZoom;
+}
+
+if (surface_get_width(application_surface) != _width
+ || surface_get_height(application_surface) != _height)
+{
+ surface_resize(application_surface, _width, _height);
+}
diff --git a/PushEd.gmx/scripts/PEd_guiWindowDraw.gml b/PushEd.gmx/scripts/PEd_guiWindowDraw.gml
new file mode 100644
index 00000000..9174be69
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiWindowDraw.gml
@@ -0,0 +1,40 @@
+/// PEd_guiWindowDraw(window)
+/**
+ * @brief Draws the window.
+ * @param {real} window The id of the window.
+ */
+var _window = argument0;
+var _windowW = PEd_guiShapeGetWidth(_window);
+var _windowH = PEd_guiShapeGetHeight(_window);
+var _border = _window[? "border"];
+var _titleBar = PEd_guiWindowGetTitleBar(_window);
+var _container = PEd_guiWindowGetContainer(_window);
+
+PEd_guiMatrixPush(PEd_guiShapeGetX(_window), PEd_guiShapeGetY(_window));
+
+// Shadow and border
+PEd_guiDrawShadow(0, 0, _windowW, _windowH, PEdColour.Shadow, PEdColour.ShadowAlpha);
+var _selectedShape = PEd_guiGetSelectedShape();
+var _colourBorder = PEdColour.WindowBorder;
+if (_selectedShape == _window
+ || PEd_guiShapeDelegatesRecursive(_window, _selectedShape))
+{
+ _colourBorder = PEdColour.Active;
+}
+PEd_guiDrawRectangle(0, 0, _windowW, _windowH, _colourBorder);
+
+// Title
+PEd_guiShapeSetWidth(_titleBar, _windowW - _border * 2);
+PEd_guiDrawItem(_titleBar, _border, 0);
+var _titleBarHeight = PEd_guiContainerGetContentHeight(_titleBar);
+PEd_guiShapeSetHeight(_titleBar, _titleBarHeight);
+
+// Content
+PEd_guiShapeSetSize(_container,
+ _windowW - _border * 2,
+ max(_windowH - _titleBarHeight - _border, 1));
+PEd_guiDrawItem(_container, _border, _titleBarHeight);
+
+PEd_guiMatrixRestore();
+
+PEd_guiShapeSetHeight(_window, max(PEd_guiShapeGetHeight(_window), _titleBarHeight + _border));
diff --git a/PushEd.gmx/scripts/PEd_guiWindowGetContainer.gml b/PushEd.gmx/scripts/PEd_guiWindowGetContainer.gml
new file mode 100644
index 00000000..a2aae9aa
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiWindowGetContainer.gml
@@ -0,0 +1,8 @@
+/// PEd_guiWindowGetContainer(window)
+/**
+ * @brief Gets the container of the window.
+ * @param {real} window The id of the window.
+ * @return {real} The container of the window.
+ */
+gml_pragma("forceinline");
+return argument0[? "container"];
diff --git a/PushEd.gmx/scripts/PEd_guiWindowGetContent.gml b/PushEd.gmx/scripts/PEd_guiWindowGetContent.gml
new file mode 100644
index 00000000..35b9fae5
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiWindowGetContent.gml
@@ -0,0 +1,8 @@
+/// PEd_guiWindowGetContent(window)
+/**
+ * @brief Gets the content script of the window.
+ * @param {real} window The id of the window.
+ * @return {real} The content script of the window.
+ */
+var _container = argument0[? "container"];
+return _container[? "content"];
diff --git a/PushEd.gmx/scripts/PEd_guiWindowGetTitleBar.gml b/PushEd.gmx/scripts/PEd_guiWindowGetTitleBar.gml
new file mode 100644
index 00000000..fa5069f3
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiWindowGetTitleBar.gml
@@ -0,0 +1,8 @@
+/// PEd_guiWindowGetTitleBar(window)
+/**
+ * @brief Gets the title bar of the window.
+ * @param {real} window The id of the window.
+ * @return {real} The title bar of the window.
+ */
+gml_pragma("forceinline");
+return argument0[? "titleBar"];
diff --git a/PushEd.gmx/scripts/PEd_guiWindowSetContent.gml b/PushEd.gmx/scripts/PEd_guiWindowSetContent.gml
new file mode 100644
index 00000000..b0f9daf8
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiWindowSetContent.gml
@@ -0,0 +1,8 @@
+/// PEd_guiWindowSetContent(window, content)
+/**
+ * @brief Sets content of the window.
+ * @param {real} window The id of the window.
+ * @param {real} content The new content script of the window.
+ */
+var _container = argument0[? "container"];
+_container[? "content"] = argument1;
diff --git a/PushEd.gmx/scripts/PEd_guiWindowUpdate.gml b/PushEd.gmx/scripts/PEd_guiWindowUpdate.gml
new file mode 100644
index 00000000..97e3f71c
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_guiWindowUpdate.gml
@@ -0,0 +1,170 @@
+/// PEd_guiWindowUpdate(window)
+/**
+ * @brief Updates the window.
+ * @param {real} window The id of the window.
+ */
+var _window = argument0;
+var _width = PEd_guiShapeGetWidth(_window);
+var _height = PEd_guiShapeGetHeight(_window);
+var _border = _window[? "border"];
+var _titleBar = PEd_guiWindowGetTitleBar(_window);
+var _resize = _window[? "resize"];
+
+PEd_guiCompoundShapeUpdate(_window);
+
+if (mouse_check_button_pressed(mb_any)
+ && (PEd_guiShapeIsHovered(_window)
+ || PEd_guiShapeDelegatesRecursive(_window, guiShapeHovered)))
+{
+ PEd_guiMoveItemToTop(_window);
+
+ ////////////////////////////////////////////////////////////////////////////
+ // FIXME Stupid hack :(
+ guiShapeSelected = _window;
+ PEd_guiRequestRedrawAll(guiRoot);
+ ////////////////////////////////////////////////////////////////////////////
+}
+
+var _titleBarHoveredForResize = (PEd_guiShapeIsHovered(_titleBar)
+ && guiMouseY < _border);
+
+if (PEd_guiShapeIsHovered(_titleBar)
+ && !_titleBarHoveredForResize)
+{
+ // Start dragging
+ if (mouse_check_button_pressed(mb_left)
+ && guiMouseX < _width - guiLineHeight - _border)
+ {
+ _window[? "drag"] = true;
+ _window[? "mouseOffsetX"] = PEd_guiShapeGetX(_window) - windowMouseX;
+ _window[? "mouseOffsetY"] = PEd_guiShapeGetY(_window) - windowMouseY;
+ guiShapeActive = _window;
+ }
+}
+else if ((PEd_guiShapeIsHovered(_window)
+ || _titleBarHoveredForResize)
+ && _resize == PEdGUIResize.None)
+{
+ // Start resizing
+ if (guiMouseX < _border)
+ {
+ _resize |= PEdGUIResize.Left;
+ }
+ else if (guiMouseX > _width - _border)
+ {
+ _resize |= PEdGUIResize.Right;
+ }
+
+ if (guiMouseY < _border)
+ {
+ _resize |= PEdGUIResize.Top;
+ }
+ else if (guiMouseY > _height - _border)
+ {
+ _resize |= PEdGUIResize.Bottom;
+ }
+
+ if (mouse_check_button_pressed(mb_left))
+ {
+ if (_resize & PEdGUIResize.Left)
+ {
+ _window[? "mouseOffsetX"] = windowMouseX - PEd_guiShapeGetX(_window);
+ }
+ else if (_resize & PEdGUIResize.Right)
+ {
+ _window[? "mouseOffsetX"] = PEd_guiShapeGetX(_window) + PEd_guiShapeGetWidth(_window) - windowMouseX;
+ }
+
+ if (_resize & PEdGUIResize.Top)
+ {
+ _window[? "mouseOffsetY"] = windowMouseY - PEd_guiShapeGetY(_window);
+ }
+ else if (_resize & PEdGUIResize.Bottom)
+ {
+ _window[? "mouseOffsetY"] = PEd_guiShapeGetY(_window) + PEd_guiShapeGetHeight(_window) - windowMouseY;
+ }
+
+ _window[? "resize"] = _resize;
+ guiShapeActive = _window;
+ }
+}
+
+if (_window[? "drag"])
+{
+ ////////////////////////////////////////////////////////////////////////////
+ // Dragging
+ PEd_guiShapeSetPosition(_window,
+ clamp(windowMouseX, 0, windowWidth) + _window[? "mouseOffsetX"],
+ clamp(windowMouseY, 0, windowHeight) + _window[? "mouseOffsetY"]);
+ if (!mouse_check_button(mb_left))
+ {
+ _window[? "drag"] = false;
+ guiShapeActive = noone;
+ }
+}
+
+if (_resize != PEdGUIResize.None)
+{
+ ////////////////////////////////////////////////////////////////////////////
+ // Resizing
+
+ // Set cursor
+ if ((_resize & PEdGUIResize.Left
+ && _resize & PEdGUIResize.Top)
+ || (_resize & PEdGUIResize.Right
+ && _resize & PEdGUIResize.Bottom))
+ {
+ guiCursor = cr_size_nwse;
+ }
+ else if ((_resize & PEdGUIResize.Left
+ && _resize & PEdGUIResize.Bottom)
+ || (_resize & PEdGUIResize.Right
+ && _resize & PEdGUIResize.Top))
+ {
+ guiCursor = cr_size_nesw;
+ }
+ else if (_resize & PEdGUIResize.Horizontal)
+ {
+ guiCursor = cr_size_we;
+ }
+ else if (_resize & PEdGUIResize.Vertical)
+ {
+ guiCursor = cr_size_ns;
+ }
+
+ // Set size
+ if (guiShapeActive == _window)
+ {
+ var _minWidth = 128 + _border * 2;
+ if (_resize & PEdGUIResize.Right)
+ {
+ PEd_guiShapeSetWidth(_window, max(guiMouseX + _window[? "mouseOffsetX"], _minWidth));
+ }
+ else if (_resize & PEdGUIResize.Left)
+ {
+ var _widthOld = PEd_guiShapeGetWidth(_window);
+ var _widthNew = max(_widthOld - guiMouseX + _window[? "mouseOffsetX"], _minWidth);
+ PEd_guiShapeSetWidth(_window, _widthNew);
+ PEd_guiShapeSetX(_window, PEd_guiShapeGetX(_window) - (_widthNew - _widthOld));
+ }
+
+ var _minHeight = PEd_guiShapeGetHeight(_titleBar) + _border * 2;
+ if (_resize & PEdGUIResize.Bottom)
+ {
+ PEd_guiShapeSetHeight(_window, max(guiMouseY + _window[? "mouseOffsetY"], _minHeight));
+ }
+ else if (_resize & PEdGUIResize.Top)
+ {
+ var _heightOld = PEd_guiShapeGetHeight(_window);
+ var _heightNew = max(_heightOld - guiMouseY + _window[? "mouseOffsetY"], _minHeight)
+ PEd_guiShapeSetHeight(_window, _heightNew);
+ PEd_guiShapeSetY(_window, PEd_guiShapeGetY(_window) - (_heightNew - _heightOld));
+ }
+
+ if (!mouse_check_button(mb_left))
+ {
+ _window[? "resize"] = PEdGUIResize.None;
+ guiShapeActive = noone;
+ }
+ }
+}
diff --git a/PushEd.gmx/scripts/PEd_instanceAddRotX.gml b/PushEd.gmx/scripts/PEd_instanceAddRotX.gml
new file mode 100644
index 00000000..37325296
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_instanceAddRotX.gml
@@ -0,0 +1,41 @@
+/// PEd_instanceAddRotX(id, rot)
+/**
+ * @brief Rotates instance around the x axis by the given angle.
+ * @param {real} id The id of the instance.
+ * @param {real} rot The angle.
+ */
+var _id = argument0;
+var _rot = argument1;
+var _matWorld = matrix_get(matrix_world);
+
+matrix_set(matrix_world,
+ matrix_build(0, 0, 0,
+ PEd_instanceGetRotX(_id),
+ PEd_instanceGetRotY(_id),
+ PEd_instanceGetRotZ(_id),
+ 1, 1, 1));
+d3d_transform_add_rotation_x(_rot);
+
+// Save new rotation angles
+var _euler = PEd_matrixRotToEuler(matrix_get(matrix_world));
+PEd_instanceSetRotX(_id, _euler[0]);
+PEd_instanceSetRotY(_id, _euler[1]);
+PEd_instanceSetRotZ(_id, _euler[2]);
+
+matrix_set(matrix_world, _matWorld);
+
+// Position
+var _dist = point_distance(PEd_oPivot.y, PEd_oPivot.z, PEd_instanceGetPosY(_id), PEd_instanceGetPosZ(_id));
+var _dir = point_direction(PEd_oPivot.y, PEd_oPivot.z, PEd_instanceGetPosY(_id), PEd_instanceGetPosZ(_id));
+PEd_instanceSetPosY(_id, PEd_oPivot.y + lengthdir_x(_dist, _dir + _rot), false);
+PEd_instanceSetPosZ(_id, PEd_oPivot.z + lengthdir_y(_dist, _dir + _rot), false);
+with (PEd_oEditor)
+{
+ var _index = ds_list_find_index(selectedObjects, _id);
+ if (_index != -1)
+ {
+ var _data = selectedObjectsData[| _index];
+ _data[@ 1] = PEd_instanceGetPosY(_id) - PEd_oPivot.y;
+ _data[@ 2] = PEd_instanceGetPosZ(_id) - PEd_oPivot.z;
+ }
+}
diff --git a/PushEd.gmx/scripts/PEd_instanceAddRotY.gml b/PushEd.gmx/scripts/PEd_instanceAddRotY.gml
new file mode 100644
index 00000000..eab10450
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_instanceAddRotY.gml
@@ -0,0 +1,41 @@
+/// PEd_instanceAddRotY(id, rot)
+/**
+ * @brief Rotates instance around the y axis by the given angle.
+ * @param {real} id The id of the instance.
+ * @param {real} rot The angle.
+ */
+var _id = argument0;
+var _rot = argument1;
+var _matWorld = matrix_get(matrix_world);
+
+matrix_set(matrix_world,
+ matrix_build(0, 0, 0,
+ PEd_instanceGetRotX(_id),
+ PEd_instanceGetRotY(_id),
+ PEd_instanceGetRotZ(_id),
+ 1, 1, 1));
+d3d_transform_add_rotation_y(_rot);
+
+// Save new rotation angles
+var _euler = PEd_matrixRotToEuler(matrix_get(matrix_world));
+PEd_instanceSetRotX(_id, _euler[0]);
+PEd_instanceSetRotY(_id, _euler[1]);
+PEd_instanceSetRotZ(_id, _euler[2]);
+
+matrix_set(matrix_world, _matWorld);
+
+// Position
+var _dist = point_distance(PEd_oPivot.x, PEd_oPivot.z, PEd_instanceGetPosX(_id), PEd_instanceGetPosZ(_id));
+var _dir = point_direction(PEd_oPivot.x, PEd_oPivot.z, PEd_instanceGetPosX(_id), PEd_instanceGetPosZ(_id));
+PEd_instanceSetPosX(_id, PEd_oPivot.x + lengthdir_x(_dist, _dir - _rot), false);
+PEd_instanceSetPosZ(_id, PEd_oPivot.z + lengthdir_y(_dist, _dir - _rot), false);
+with (PEd_oEditor)
+{
+ var _index = ds_list_find_index(selectedObjects, _id);
+ if (_index != -1)
+ {
+ var _data = selectedObjectsData[| _index];
+ _data[@ 0] = PEd_instanceGetPosX(_id) - PEd_oPivot.x;
+ _data[@ 2] = PEd_instanceGetPosZ(_id) - PEd_oPivot.z;
+ }
+}
diff --git a/PushEd.gmx/scripts/PEd_instanceAddRotZ.gml b/PushEd.gmx/scripts/PEd_instanceAddRotZ.gml
new file mode 100644
index 00000000..da713650
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_instanceAddRotZ.gml
@@ -0,0 +1,49 @@
+/// PEd_instanceAddRotZ(id, rot)
+/**
+ * @brief Rotates instance around the z axis by the given angle.
+ * @param {real} id The id of the instance.
+ * @param {real} rot The angle.
+ */
+var _id = argument0;
+var _rot = argument1;
+
+if (global.pedUsing3D)
+{
+ var _matWorld = matrix_get(matrix_world);
+
+ matrix_set(matrix_world,
+ matrix_build(0, 0, 0,
+ PEd_instanceGetRotX(_id),
+ PEd_instanceGetRotY(_id),
+ PEd_instanceGetRotZ(_id),
+ 1, 1, 1));
+ d3d_transform_add_rotation_z(_rot);
+
+ // Save new rotation angles
+ var _euler = PEd_matrixRotToEuler(matrix_get(matrix_world));
+ PEd_instanceSetRotX(_id, _euler[0]);
+ PEd_instanceSetRotY(_id, _euler[1]);
+ PEd_instanceSetRotZ(_id, _euler[2]);
+
+ matrix_set(matrix_world, _matWorld);
+}
+else
+{
+ PEd_instanceSetRotZ(_id, PEd_instanceGetRotZ(_id) + _rot);
+}
+
+// Position
+var _dist = point_distance(PEd_oPivot.x, PEd_oPivot.y, PEd_instanceGetPosX(_id), PEd_instanceGetPosY(_id));
+var _dir = point_direction(PEd_oPivot.x, PEd_oPivot.y, PEd_instanceGetPosX(_id), PEd_instanceGetPosY(_id));
+PEd_instanceSetPosX(_id, PEd_oPivot.x + lengthdir_x(_dist, _dir + _rot), false);
+PEd_instanceSetPosY(_id, PEd_oPivot.y + lengthdir_y(_dist, _dir + _rot), false);
+with (PEd_oEditor)
+{
+ var _index = ds_list_find_index(selectedObjects, _id);
+ if (_index != -1)
+ {
+ var _data = selectedObjectsData[| _index];
+ _data[@ 0] = PEd_instanceGetPosX(_id) - PEd_oPivot.x;
+ _data[@ 1] = PEd_instanceGetPosY(_id) - PEd_oPivot.y;
+ }
+}
diff --git a/PushEd.gmx/scripts/PEd_instanceAddScaleX.gml b/PushEd.gmx/scripts/PEd_instanceAddScaleX.gml
new file mode 100644
index 00000000..e4e7608e
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_instanceAddScaleX.gml
@@ -0,0 +1,8 @@
+/// PEd_instanceAddScaleX(id, scale)
+/**
+ * @brief Scales the instance on the x axis by the given value.
+ * @param {real} id The id of the instance.
+ * @param {real} scale The value to scale the instance by.
+ */
+gml_pragma("forceinline");
+PEd_instanceSetScaleX(argument0, PEd_instanceGetScaleX(argument0) + argument1);
diff --git a/PushEd.gmx/scripts/PEd_instanceAddScaleY.gml b/PushEd.gmx/scripts/PEd_instanceAddScaleY.gml
new file mode 100644
index 00000000..d6867c16
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_instanceAddScaleY.gml
@@ -0,0 +1,8 @@
+/// PEd_instanceAddScaleY(id, scale)
+/**
+ * @brief Scales the instance on the y axis by the given value.
+ * @param {real} id The id of the instance.
+ * @param {real} scale The value to scale the instance by.
+ */
+gml_pragma("forceinline");
+PEd_instanceSetScaleY(argument0, PEd_instanceGetScaleY(argument0) + argument1);
diff --git a/PushEd.gmx/scripts/PEd_instanceAddScaleZ.gml b/PushEd.gmx/scripts/PEd_instanceAddScaleZ.gml
new file mode 100644
index 00000000..8dfe87c9
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_instanceAddScaleZ.gml
@@ -0,0 +1,8 @@
+/// PEd_instanceAddScaleZ(id, scale)
+/**
+ * @brief Scales the instance on the z axis by the given value.
+ * @param {real} id The id of the instance.
+ * @param {real} scale The value to scale the instance by.
+ */
+gml_pragma("forceinline");
+PEd_instanceSetScaleZ(argument0, PEd_instanceGetScaleZ(argument0) + argument1);
diff --git a/PushEd.gmx/scripts/PEd_instanceAutocompleteCode.gml b/PushEd.gmx/scripts/PEd_instanceAutocompleteCode.gml
new file mode 100644
index 00000000..c61bb8c6
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_instanceAutocompleteCode.gml
@@ -0,0 +1,29 @@
+/// PEd_instanceAutocompleteCode(id)
+/**
+ * @brief Autocompletes the creation code of the instance.
+ * @param {real} id The id of the instance.
+ */
+var _inst = argument0;
+var _startInd = "/*autocompleteStart*/ ";
+var _endInd = " /*autocompleteEnd*/ ";
+
+var _code = PEd_instanceGetCode(_inst);
+_code = PEd_removeFromStr(_startInd, _endInd, _code);
+
+if (global.pedUsing3D)
+{
+ // Construct code
+ var _autocomplete = _startInd;
+ _autocomplete += "z=" + string(_inst.z) + ";"
+ + "rotX=" + string(_inst.rotX) + ";"
+ + "rotY=" + string(_inst.rotY) + ";"
+ + "scaleZ=" + string(_inst.scaleZ) + ";";
+ _autocomplete += _endInd;
+
+ // Save code
+ PEd_instanceSetCode(_inst, _autocomplete + _code);
+}
+else
+{
+ PEd_instanceSetCode(_inst, _code);
+}
diff --git a/PushEd.gmx/scripts/PEd_instanceCopy.gml b/PushEd.gmx/scripts/PEd_instanceCopy.gml
new file mode 100644
index 00000000..454a67c2
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_instanceCopy.gml
@@ -0,0 +1,18 @@
+/// PEd_instanceCopy(id)
+/**
+ * @brief Creates a copy of the instance.
+ * @param {real} id The id of the instance to make a copy of.
+ * @return {real} The id of the created copy.
+ */
+with (argument0)
+{
+ var _i = instance_copy(false);
+ PEd_instanceSetPosX(_i, PEd_oPivot.x);
+ PEd_instanceSetPosY(_i, PEd_oPivot.y);
+ PEd_instanceSetPosZ(_i, PEd_oPivot.z);
+ PEd_instanceSetCode(_i, PEd_instanceGetCode(id));
+ PEd_instanceSetObjectName(_i, PEd_instanceGetObjectName(id));
+ PEd_instanceSetName(_i, PEd_instanceGenerateName(_i));
+ PEd_instanceSetCode(_i, PEd_instanceGetCode(_i));
+ return _i;
+}
diff --git a/PushEd.gmx/scripts/PEd_instanceCreate.gml b/PushEd.gmx/scripts/PEd_instanceCreate.gml
new file mode 100644
index 00000000..8bfb4b85
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_instanceCreate.gml
@@ -0,0 +1,70 @@
+/// PEd_instanceCreate(room, x, y, object)
+/**
+ * @brief Creates an instance of the object and adds it to the room.
+ * @param {real} room The id of the room.
+ * @param {real} x The x position to create the instance at.
+ * @param {real} y The y position to create the instance at.
+ * @param {real} object The id of the object to create an instance of.
+ * @return {real} The instance id.
+ */
+var _room = argument0;
+var _x = argument1;
+var _y = argument2;
+var _object = argument3;
+var _instances = PEd_roomGetInstances(_room);
+
+////////////////////////////////////////////////////////////////////////////////
+// In game
+if (room != PEd_rmEditor)
+{
+ with (instance_create(_x, _y, _object))
+ {
+ pedRoom = _room;
+ ds_list_add(_instances, id);
+ return id;
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// In editor
+if (PEd_CREATE_DUMMY_2D)
+{
+ var _id;
+
+ if (object_is_ancestor(_object, PEd_oObject3D))
+ {
+ // 3D object
+ _id = instance_create(_x, _y, _object);
+ }
+ else
+ {
+ // 2D dummy
+ _id = instance_create(_x, _y, PEd_oObject2D);
+ _id.pedObjectName = object_get_name(_object);
+ var _sprite = object_get_sprite(_object);
+ if (_sprite >= 0)
+ {
+ _id.sprite_index = _sprite;
+ }
+ }
+
+ with (_id)
+ {
+ pedRoom = _room;
+ ds_list_add(_instances, id);
+ return id;
+ }
+}
+else
+{
+ with (instance_create(_x, _y, _object))
+ {
+ if (!object_is_ancestor(object_index, PEd_oObject3D))
+ {
+ event_perform_object(PEd_oObject3D, ev_create, 0);
+ }
+ pedRoom = _room;
+ ds_list_add(_instances, id);
+ return id;
+ }
+}
diff --git a/PushEd.gmx/scripts/PEd_instanceDestroy.gml b/PushEd.gmx/scripts/PEd_instanceDestroy.gml
new file mode 100644
index 00000000..8879c99b
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_instanceDestroy.gml
@@ -0,0 +1,12 @@
+/// PEd_instanceDestroy(id)
+/**
+ * @brief Destroys the instance and removes it from the room that it is in.
+ * @param {real} id The id of the instance.
+ */
+var _instances = PEd_roomGetInstances(argument0.pedRoom);
+var _pos = ds_list_find_index(_instances, argument0);
+if (_pos != -1)
+{
+ ds_list_delete(_instances, _pos);
+}
+with (argument0) { instance_destroy(); }
diff --git a/PushEd.gmx/scripts/PEd_instanceEdit2D.gml b/PushEd.gmx/scripts/PEd_instanceEdit2D.gml
new file mode 100644
index 00000000..ea67bd45
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_instanceEdit2D.gml
@@ -0,0 +1,184 @@
+/// PEd_instanceEdit2D()
+/**
+ * @brief Handles 2D instance editing.
+ */
+// Save old transformations
+var _oldRotZ = 0;
+var _oldScaleX = 0;
+var _oldScaleY = 0;
+var _room = PEd_getCurrentRoom();
+var _selectedObj = PEd_getSelectedObject();
+
+if (_selectedObj > 0)
+{
+ if (editMode == PEdEditModes.Object)
+ {
+ if (instance_exists(_selectedObj))
+ {
+ _oldRotZ = PEd_instanceGetRotZ(_selectedObj);
+ _oldScaleX = PEd_instanceGetScaleX(_selectedObj);
+ _oldScaleY = PEd_instanceGetScaleY(_selectedObj);
+ }
+ else
+ {
+ PEd_actClearSelection();
+ _selectedObj = 0;
+ }
+ }
+ else if (editMode == PEdEditModes.Tile)
+ {
+ if (tile_exists(_selectedObj))
+ {
+ _oldRotZ = 0;
+ _oldScaleX = tile_get_xscale(_selectedObj);
+ _oldScaleY = tile_get_yscale(_selectedObj);
+ }
+ else
+ {
+ PEd_actClearSelection();
+ _selectedObj = 0;
+ }
+ }
+}
+
+var _tileAddScaleX = 0;
+var _tileAddScaleY = 0;
+
+if (mouseInViewport)
+{
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Control pivot
+ //
+ if (mouse_check_button(mb_left)
+ && editAxis != PEdAxes.None)
+ {
+ var _valX = (windowMouseX - viewportX + mouseOff[0]) * viewZoom + view_xview[0];
+ var _valY = (windowMouseY - viewportY + mouseOff[1]) * viewZoom + view_yview[0];
+
+ if (editAxis == PEdAxes.All
+ && editNow == 1
+ && editMode == PEdEditModes.Object)
+ {
+ // Rotate
+ PEd_instanceAddRotZ(_selectedObj, windowMouseX - mouseLastX);
+ }
+ else
+ {
+ if (editAxis & PEdAxes.X)
+ {
+ if (editNow == PEdTools.Move)
+ {
+ // Move
+ PEd_oPivot.x = _valX;
+ }
+ else if (editNow == PEdTools.Scale
+ && _selectedObj > 0)
+ {
+ // Scale
+ var _scale = (windowMouseX - mouseLastX) * viewZoom / 10
+ + (windowMouseY - mouseLastY) * viewZoom / 10;
+
+ if (editMode == PEdEditModes.Object)
+ {
+ PEd_instanceAddScaleX(_selectedObj, _scale);
+ }
+ else if (editMode == PEdEditModes.Tile)
+ {
+ _tileAddScaleX = _scale;
+ tile_set_scale(_selectedObj,
+ tile_get_xscale(_selectedObj) + _scale,
+ tile_get_yscale(_selectedObj));
+ }
+ }
+ }
+
+ if (editAxis & PEdAxes.Y)
+ {
+ if (editNow == PEdTools.Move)
+ {
+ // Move
+ PEd_oPivot.y = _valY;
+ }
+ else if (editNow == PEdTools.Scale
+ && _selectedObj > 0)
+ {
+ // Scale
+ var _scale = (windowMouseX - mouseLastX) * viewZoom / 10
+ + (windowMouseY - mouseLastY) * viewZoom / 10;
+
+ if (editMode == PEdEditModes.Object)
+ {
+ PEd_instanceAddScaleY(_selectedObj, _scale);
+ }
+ else if (editMode == PEdEditModes.Tile)
+ {
+ _tileAddScaleY = _scale;
+ tile_set_scale(_selectedObj,
+ tile_get_xscale(_selectedObj),
+ tile_get_yscale(_selectedObj) + _scale);
+ }
+ }
+ }
+ }
+
+ // Snap to grid
+ if (PEd_roomGetGrid(_room)
+ && !keyboard_check(vk_alt))
+ {
+ PEd_oPivot.x = round(PEd_oPivot.x / PEd_roomGetSnapH(_room)) * PEd_roomGetSnapH(_room);
+ PEd_oPivot.y = round(PEd_oPivot.y / PEd_roomGetSnapV(_room)) * PEd_roomGetSnapV(_room);
+ }
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Apply transformations
+//
+if (_selectedObj > 0)
+{
+ var _data = selectedObjectsData[| 0];
+ if (editMode == PEdEditModes.Object)
+ {
+ PEd_instanceSetPosX(_selectedObj, PEd_oPivot.x + _data[0]);
+ PEd_instanceSetPosY(_selectedObj, PEd_oPivot.y + _data[1]);
+ }
+ else if (editMode == PEdEditModes.Tile)
+ {
+ tile_set_position(_selectedObj, PEd_oPivot.x + _data[0], PEd_oPivot.y + _data[1]);
+ }
+ PEd_guiRequestRedrawAll(guiRoot)
+}
+
+var _size = ds_list_size(selectedObjects);
+for (var i = 1; i < _size; i++)
+{
+ var _id = selectedObjects[| i];
+ var _data = selectedObjectsData[| i];
+ if (editMode == PEdEditModes.Object)
+ {
+ PEd_instanceSetPosX(_id, PEd_oPivot.x + _data[0]);
+ PEd_instanceSetPosY(_id, PEd_oPivot.y + _data[1]);
+ PEd_instanceAddRotZ(_id, PEd_instanceGetRotZ(_selectedObj) - _oldRotZ);
+ PEd_instanceAddScaleX(_id, PEd_instanceGetScaleX(_selectedObj) - _oldScaleX);
+ PEd_instanceAddScaleY(_id, PEd_instanceGetScaleY(_selectedObj) - _oldScaleY);
+ }
+ else if (editMode == PEdEditModes.Tile)
+ {
+ tile_set_position(_id, PEd_oPivot.x + _data[0], PEd_oPivot.y + _data[1]);
+ tile_set_scale(_id,
+ tile_get_xscale(_id) + _tileAddScaleX,
+ tile_get_yscale(_id) + _tileAddScaleY);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Stop editing
+//
+if (mouse_check_button_released(mb_left))
+{
+ editAxis = PEdAxes.None;
+ editNow = PEdTools.None;
+}
diff --git a/PushEd.gmx/scripts/PEd_instanceEdit3D.gml b/PushEd.gmx/scripts/PEd_instanceEdit3D.gml
new file mode 100644
index 00000000..c06ced66
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_instanceEdit3D.gml
@@ -0,0 +1,197 @@
+/// PEd_instanceEdit3D()
+/**
+ * @brief Handles 3D instance editing.
+ */
+// Save old transformations
+var _oldScaleX = 0;
+var _oldScaleY = 0;
+var _oldScaleZ = 0;
+var _room = PEd_getCurrentRoom();
+var _selectedObj = PEd_getSelectedObject();
+
+if (_selectedObj > 0)
+{
+ _oldScaleX = PEd_instanceGetScaleX(_selectedObj);
+ _oldScaleY = PEd_instanceGetScaleY(_selectedObj);
+ _oldScaleZ = PEd_instanceGetScaleZ(_selectedObj);
+}
+
+if (mouseInViewport)
+{
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Control pivot
+ //
+ if (mouse_check_button(mb_left)
+ && editAxis != PEdAxes.None)
+ {
+ // Get 3D mouse position
+ if (editAxis != PEdAxes.Z)
+ _planeNormal = PEd_vec3(0, 0, 1);
+ else
+ _planeNormal = PEd_vec3Normalize(PEd_vec3(x - PEd_oPivot.x, y - PEd_oPivot.y, 0));
+
+ var _mouse = PEd_rayPlaneIntersect(PEd_cameraGetPosVec3(),
+ PEd_cameraUnprojectVec2(PEd_vec2(windowMouseX, windowMouseY)),
+ PEd_vec3(PEd_oPivot.x, PEd_oPivot.y, PEd_oPivot.z),
+ _planeNormal);
+
+ switch (editAxis)
+ {
+ ////////////////////////////////////////////////////////////////////////
+ // X axis
+ case PEdAxes.X:
+ if (editNow == PEdTools.Move)
+ {
+ PEd_oPivot.x = _mouse[0] + mouseOff[0];
+ }
+ else if (_selectedObj > 0)
+ {
+ if (editNow == PEdTools.Rotate)
+ {
+ var _angle = mouseLastX - windowMouseX;
+ if (PEd_oEditor.x < PEd_oPivot.x)
+ _angle = - _angle;
+ PEd_instanceAddRotX(_selectedObj, _angle);
+ }
+ else if (editNow == PEdTools.Scale)
+ {
+ PEd_instanceAddScaleX(_selectedObj, _mouse[0] - mouseLast[0]);
+ }
+ }
+ break;
+
+ ////////////////////////////////////////////////////////////////////////
+ // Y axis
+ case PEdAxes.Y:
+ if (editNow == PEdTools.Move)
+ {
+ PEd_oPivot.y = _mouse[1] + mouseOff[1];
+ }
+ else if (_selectedObj > 0)
+ {
+ if (editNow == PEdTools.Rotate)
+ {
+ var _angle = mouseLastX - windowMouseX;
+ if (PEd_oEditor.y < PEd_oPivot.y)
+ _angle = - _angle;
+ PEd_instanceAddRotY(_selectedObj, _angle);
+ }
+ else if (editNow = PEdTools.Scale)
+ {
+ PEd_instanceAddScaleY(_selectedObj, _mouse[1] - mouseLast[1]);
+ }
+ }
+ break;
+
+ ////////////////////////////////////////////////////////////////////////
+ // Z axis
+ case PEdAxes.Z:
+ if (editNow == PEdTools.Move)
+ {
+ PEd_oPivot.z = _mouse[2] + mouseOff[2];
+ }
+ else if (_selectedObj > 0)
+ {
+ if (editNow == PEdTools.Rotate)
+ PEd_instanceAddRotZ(_selectedObj, windowMouseX - mouseLastX);
+ else if (editNow == PEdTools.Scale)
+ PEd_instanceAddScaleZ(_selectedObj, _mouse[2] - mouseLast[2]);
+ }
+ break;
+
+ ////////////////////////////////////////////////////////////////////////
+ // Multiple axis
+ case PEdAxes.All:
+ if (editNow == PEdTools.Move)
+ {
+ PEd_oPivot.x = _mouse[0] + mouseOff[0];
+ PEd_oPivot.y = _mouse[1] + mouseOff[1];
+ }
+ else if (editNow == PEdTools.Scale
+ && _selectedObj > 0)
+ {
+ var _s = PEd_vec3Length(PEd_vec3Subtract(_mouse, mouseLast)) * sign(_mouse[0] - mouseLast[0]);
+ PEd_instanceAddScaleX(_selectedObj, _s);
+ PEd_instanceAddScaleY(_selectedObj, _s);
+ PEd_instanceAddScaleZ(_selectedObj, _s);
+ }
+ break;
+ }
+
+ // Snap to grid
+ if (PEd_roomGetGrid(_room)
+ && !keyboard_check(vk_alt))
+ {
+ PEd_oPivot.x = round(PEd_oPivot.x / PEd_roomGetSnapH(_room)) * PEd_roomGetSnapH(_room);
+ PEd_oPivot.y = round(PEd_oPivot.y / PEd_roomGetSnapV(_room)) * PEd_roomGetSnapV(_room);
+ PEd_oPivot.z = round(PEd_oPivot.z / PEd_roomGetSnapD(_room)) * PEd_roomGetSnapD(_room);
+ }
+
+ // Save mouse last position
+ mouseLast = _mouse;
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Apply transformations
+//
+if (_selectedObj > 0)
+{
+ var _data = selectedObjectsData[| 0];
+ PEd_instanceSetPosX(_selectedObj, PEd_oPivot.x + _data[0]);
+ PEd_instanceSetPosY(_selectedObj, PEd_oPivot.y + _data[1]);
+ PEd_instanceSetPosZ(_selectedObj, PEd_oPivot.z + _data[2]);
+ PEd_guiRequestRedrawAll(guiRoot)
+}
+
+var _size = ds_list_size(selectedObjects);
+for (var i = 1; i < _size; i++)
+{
+ var _id = selectedObjects[| i];
+ var _data = selectedObjectsData[| i];
+ PEd_instanceSetPosX(_id, PEd_oPivot.x + _data[0]);
+ PEd_instanceSetPosY(_id, PEd_oPivot.y + _data[1]);
+ PEd_instanceSetPosZ(_id, PEd_oPivot.z + _data[2]);
+
+ // Apply rotations
+ if (editNow == PEdTools.Rotate
+ && mouseInViewport)
+ {
+ if (editAxis == PEdAxes.X)
+ {
+ var _angle = mouseLastX - windowMouseX;
+ if (PEd_oEditor.x < PEd_oPivot.x)
+ _angle = -_angle;
+ PEd_instanceAddRotX(_id, _angle);
+ }
+ else if (editAxis == PEdAxes.Y)
+ {
+ var _angle = mouseLastX - windowMouseX;
+ if (PEd_oEditor.y < PEd_oPivot.y)
+ _angle = -_angle;
+ PEd_instanceAddRotY(_id, _angle);
+ }
+ else if (editAxis == PEdAxes.Z)
+ {
+ var _angle = windowMouseX - mouseLastX;
+ PEd_instanceAddRotZ(_id, _angle);
+ }
+ }
+
+ // Scale
+ PEd_instanceAddScaleX(_id, PEd_instanceGetScaleX(_selectedObj) - _oldScaleX);
+ PEd_instanceAddScaleY(_id, PEd_instanceGetScaleY(_selectedObj) - _oldScaleY);
+ PEd_instanceAddScaleZ(_id, PEd_instanceGetScaleZ(_selectedObj) - _oldScaleZ);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Stop editing
+//
+if (mouse_check_button_released(mb_left))
+{
+ editAxis = 0;
+ editNow = -1;
+}
diff --git a/PushEd.gmx/scripts/PEd_instanceGenerateName.gml b/PushEd.gmx/scripts/PEd_instanceGenerateName.gml
new file mode 100644
index 00000000..8bf9cb68
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_instanceGenerateName.gml
@@ -0,0 +1,15 @@
+/// PEd_instanceGenerateName(id)
+/**
+ * @brief Generates unique name for the given instance.
+ * @param {real} id The id of the instance to generate the name for.
+ * @return {string} The generated name.
+ */
+var _seed = random_get_seed();
+var _name = "inst" + string(abs(argument0 - 100000));
+randomize();
+repeat (6)
+{
+ _name += string(choose(irandom(9), chr(irandom_range(ord("A"), ord("Z")))));
+}
+random_set_seed(_seed);
+return _name;
diff --git a/PushEd.gmx/scripts/PEd_instanceGetAlpha.gml b/PushEd.gmx/scripts/PEd_instanceGetAlpha.gml
new file mode 100644
index 00000000..caaba08a
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_instanceGetAlpha.gml
@@ -0,0 +1,10 @@
+/// PEd_instanceGetAlpha(id)
+/**
+ * @brief Gets the instance alpha.
+ * @param {real} id The id of the instance.
+ * @return {real} The alpha.
+ */
+with (argument0)
+{
+ return PEd_ALPHA;
+}
diff --git a/PushEd.gmx/scripts/PEd_instanceGetCode.gml b/PushEd.gmx/scripts/PEd_instanceGetCode.gml
new file mode 100644
index 00000000..2d4cf490
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_instanceGetCode.gml
@@ -0,0 +1,8 @@
+/// PEd_instanceGetCode(id)
+/**
+ * @brief Gets the creation code of the instance.
+ * @param {real} id The id of the instance.
+ * @return {string} The creation code of the instance.
+ */
+gml_pragma("forceinline");
+return argument0.pedCreationCode;
diff --git a/PushEd.gmx/scripts/PEd_instanceGetColour.gml b/PushEd.gmx/scripts/PEd_instanceGetColour.gml
new file mode 100644
index 00000000..4c2b3833
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_instanceGetColour.gml
@@ -0,0 +1,10 @@
+/// PEd_instanceGetColour(id)
+/**
+ * @brief Gets the instance color.
+ * @param {real} id The id of the instance.
+ * @return {real} The color.
+ */
+with (argument0)
+{
+ return PEd_COLOUR;
+}
diff --git a/PushEd.gmx/scripts/PEd_instanceGetColourARGB.gml b/PushEd.gmx/scripts/PEd_instanceGetColourARGB.gml
new file mode 100644
index 00000000..c4b40df0
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_instanceGetColourARGB.gml
@@ -0,0 +1,10 @@
+/// PEd_instanceGetColourARGB(id)
+/**
+ * @brief Gets the instance ARGB color.
+ * @param {real} id The id of the instance.
+ * @return {real} The ARGB color.
+ */
+with (argument0)
+{
+ return PEd_colourAlphaToArgb(PEd_COLOUR, PEd_ALPHA);
+}
diff --git a/PushEd.gmx/scripts/PEd_instanceGetMatrix.gml b/PushEd.gmx/scripts/PEd_instanceGetMatrix.gml
new file mode 100644
index 00000000..a4994da1
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_instanceGetMatrix.gml
@@ -0,0 +1,13 @@
+/// PEd_instanceGetMatrix(id)
+/**
+ * @brief Gets transformation matrix of the instance.
+ * @param {real} id The id of the instance.
+ * @return {matrix} The transformation matrix of the instance.
+ */
+gml_pragma("forceinline");
+with (argument0)
+{
+ return matrix_multiply(matrix_multiply(matrix_build(0, 0, 0, 0, 0, 0, PEd_SCALE_X, PEd_SCALE_Y, PEd_SCALE_Z),
+ matrix_build(0, 0, 0, PEd_ROT_X, PEd_ROT_Y, PEd_ROT_Z, 1, 1, 1)),
+ matrix_build(PEd_POS_X, PEd_POS_Y, PEd_POS_Z, 0, 0, 0, 1, 1, 1));
+}
diff --git a/PushEd.gmx/scripts/PEd_instanceGetName.gml b/PushEd.gmx/scripts/PEd_instanceGetName.gml
new file mode 100644
index 00000000..9faea5fb
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_instanceGetName.gml
@@ -0,0 +1,8 @@
+/// PEd_instanceGetName(id)
+/**
+ * @brief Gets the name of the instance.
+ * @param {real} id The id of the instance.
+ * @return {string} The name of the instance.
+ */
+gml_pragma("forceinline");
+return argument0.pedInstanceName;
diff --git a/PushEd.gmx/scripts/PEd_instanceGetObjectName.gml b/PushEd.gmx/scripts/PEd_instanceGetObjectName.gml
new file mode 100644
index 00000000..a7050539
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_instanceGetObjectName.gml
@@ -0,0 +1,9 @@
+/// PEd_instanceGetObjectName(id)
+/**
+ * @brief Gets the object name of the instance.
+ * @param {real} id The id of the instance.
+ * @return {string} Object name of the instance.
+ * @note When the room is saved, this name is used as object name.
+ */
+gml_pragma("forceinline");
+return argument0.pedObjectName;
diff --git a/PushEd.gmx/scripts/PEd_instanceGetPosVec2.gml b/PushEd.gmx/scripts/PEd_instanceGetPosVec2.gml
new file mode 100644
index 00000000..b3f6db20
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_instanceGetPosVec2.gml
@@ -0,0 +1,10 @@
+/// PEd_instanceGetPosVec2(id)
+/**
+ * @brief Gets the instance position on the x,y axis.
+ * @param {real} id The id of the instance.
+ * @return {vec2} The position on the x,y axis.
+ */
+with (argument0)
+{
+ return PEd_vec2(PEd_POS_X, PEd_POS_Y);
+}
diff --git a/PushEd.gmx/scripts/PEd_instanceGetPosVec3.gml b/PushEd.gmx/scripts/PEd_instanceGetPosVec3.gml
new file mode 100644
index 00000000..9e6db90a
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_instanceGetPosVec3.gml
@@ -0,0 +1,10 @@
+/// PEd_instanceGetPosVec3(id)
+/**
+ * @brief Gets the instance position on the x,y,z axis.
+ * @param {real} id The id of the instance.
+ * @return {vec3} The position on the x,y,z axis.
+ */
+with (argument0)
+{
+ return PEd_vec3(PEd_POS_X, PEd_POS_Y, PEd_POS_Z);
+}
diff --git a/PushEd.gmx/scripts/PEd_instanceGetPosX.gml b/PushEd.gmx/scripts/PEd_instanceGetPosX.gml
new file mode 100644
index 00000000..660375d2
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_instanceGetPosX.gml
@@ -0,0 +1,10 @@
+/// PEd_instanceGetPosX(id)
+/**
+ * @brief Gets the instance position on the x axis.
+ * @param {real} id The id of the instance.
+ * @return {real} The position on the x axis.
+ */
+with (argument0)
+{
+ return PEd_POS_X;
+}
diff --git a/PushEd.gmx/scripts/PEd_instanceGetPosY.gml b/PushEd.gmx/scripts/PEd_instanceGetPosY.gml
new file mode 100644
index 00000000..d07ce923
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_instanceGetPosY.gml
@@ -0,0 +1,10 @@
+/// PEd_instanceGetPosY(id)
+/**
+ * @brief Gets the instance position on the y axis.
+ * @param {real} id The id of the instance.
+ * @return {real} The position on the y axis.
+ */
+with (argument0)
+{
+ return PEd_POS_Y;
+}
diff --git a/PushEd.gmx/scripts/PEd_instanceGetPosZ.gml b/PushEd.gmx/scripts/PEd_instanceGetPosZ.gml
new file mode 100644
index 00000000..47e00d46
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_instanceGetPosZ.gml
@@ -0,0 +1,10 @@
+/// PEd_instanceGetPosZ(id)
+/**
+ * @brief Gets the instance position on the z axis.
+ * @param {real} id The id of the instance.
+ * @return {real} The position on the z axis.
+ */
+with (argument0)
+{
+ return PEd_POS_Z;
+}
diff --git a/PushEd.gmx/scripts/PEd_instanceGetRotX.gml b/PushEd.gmx/scripts/PEd_instanceGetRotX.gml
new file mode 100644
index 00000000..de0d152a
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_instanceGetRotX.gml
@@ -0,0 +1,10 @@
+/// PEd_instanceGetRotX(id)
+/**
+ * @brief Gets the instance rotation around the x axis.
+ * @param {real} id The id of the instance.
+ * @return {real} The rotation around the x axis.
+ */
+with (argument0)
+{
+ return PEd_ROT_X;
+}
diff --git a/PushEd.gmx/scripts/PEd_instanceGetRotY.gml b/PushEd.gmx/scripts/PEd_instanceGetRotY.gml
new file mode 100644
index 00000000..73978b2c
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_instanceGetRotY.gml
@@ -0,0 +1,10 @@
+/// PEd_instanceGetRotY(id)
+/**
+ * @brief Gets the instance rotation around the y axis.
+ * @param {real} id The id of the instance.
+ * @return {real} The rotation around the y axis.
+ */
+with (argument0)
+{
+ return PEd_ROT_Y;
+}
diff --git a/PushEd.gmx/scripts/PEd_instanceGetRotZ.gml b/PushEd.gmx/scripts/PEd_instanceGetRotZ.gml
new file mode 100644
index 00000000..d2850fb0
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_instanceGetRotZ.gml
@@ -0,0 +1,10 @@
+/// PEd_instanceGetRotZ(id)
+/**
+ * @brief Gets the instance rotation around the z axis.
+ * @param {real} id The id of the instance.
+ * @return {real} The rotation around the z axis.
+ */
+with (argument0)
+{
+ return PEd_ROT_Z;
+}
diff --git a/PushEd.gmx/scripts/PEd_instanceGetScaleVec2.gml b/PushEd.gmx/scripts/PEd_instanceGetScaleVec2.gml
new file mode 100644
index 00000000..15e0e13c
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_instanceGetScaleVec2.gml
@@ -0,0 +1,10 @@
+/// PEd_instanceGetScaleVec2(id)
+/**
+ * @brief Gets the instance scale on the x,y axis.
+ * @param {real} id The id of the instance.
+ * @return {vec2} The scale on the x,y axis.
+ */
+with (argument0)
+{
+ return PEd_vec2(PEd_SCALE_X, PEd_SCALE_Y);
+}
diff --git a/PushEd.gmx/scripts/PEd_instanceGetScaleVec3.gml b/PushEd.gmx/scripts/PEd_instanceGetScaleVec3.gml
new file mode 100644
index 00000000..a93b7705
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_instanceGetScaleVec3.gml
@@ -0,0 +1,10 @@
+/// PEd_instanceGetScaleVec3(id)
+/**
+ * @brief Gets the instance scale on the x,y,z axis.
+ * @param {real} id The id of the instance.
+ * @return {vec3} The scale on the x,y,z axis.
+ */
+with (argument0)
+{
+ return PEd_vec3(PEd_SCALE_X, PEd_SCALE_Y, PEd_SCALE_Z);
+}
diff --git a/PushEd.gmx/scripts/PEd_instanceGetScaleX.gml b/PushEd.gmx/scripts/PEd_instanceGetScaleX.gml
new file mode 100644
index 00000000..213f7875
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_instanceGetScaleX.gml
@@ -0,0 +1,10 @@
+/// PEd_instanceGetScaleX(id)
+/**
+ * @brief Gets the instance scale on the x axis.
+ * @param {real} id The id of the instance.
+ * @return {real} The scale on the x axis.
+ */
+with (argument0)
+{
+ return PEd_SCALE_X;
+}
diff --git a/PushEd.gmx/scripts/PEd_instanceGetScaleY.gml b/PushEd.gmx/scripts/PEd_instanceGetScaleY.gml
new file mode 100644
index 00000000..85c152ba
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_instanceGetScaleY.gml
@@ -0,0 +1,10 @@
+/// PEd_instanceGetScaleY(id)
+/**
+ * @brief Gets the instance scale on the y axis.
+ * @param {real} id The id of the instance.
+ * @return {real} The scale on the y axis.
+ */
+with (argument0)
+{
+ return PEd_SCALE_Y;
+}
diff --git a/PushEd.gmx/scripts/PEd_instanceGetScaleZ.gml b/PushEd.gmx/scripts/PEd_instanceGetScaleZ.gml
new file mode 100644
index 00000000..1e6cdf75
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_instanceGetScaleZ.gml
@@ -0,0 +1,10 @@
+/// PEd_instanceGetScaleZ(id)
+/**
+ * @brief Gets the instance scale on the z axis.
+ * @param {real} id The id of the instance.
+ * @return {real} The scale on the z axis.
+ */
+with (argument0)
+{
+ return PEd_SCALE_Z;
+}
diff --git a/PushEd.gmx/scripts/PEd_instanceSelecting.gml b/PushEd.gmx/scripts/PEd_instanceSelecting.gml
new file mode 100644
index 00000000..021a1791
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_instanceSelecting.gml
@@ -0,0 +1,170 @@
+/// PEd_instanceSelecting()
+/**
+ * @brief Handles instance selecting.
+ */
+if (!mouseInViewport
+ || (editMode == PEdEditModes.Tile
+ && keyboard_check(vk_control)))
+{
+ return 0;
+}
+
+var _room = PEd_getCurrentRoom();
+var _releasedRMB = mouse_check_button_released(mb_right);
+
+if (mouse_check_button_pressed(mb_left)
+ || (_releasedRMB
+ && windowMouseX == guiMousePressX
+ && windowMouseY == guiMousePressY))
+{
+ var _surColSelect = surface_create(viewportWidth, viewportHeight);
+
+ // Draw to the surface
+ surface_set_target(_surColSelect);
+ draw_clear(c_black);
+ PEd_cameraSetProjection();
+ shader_set(PEd_shInstSelect);
+
+ // Draw objects
+ if (editMode == PEdEditModes.Object)
+ {
+ var _instances = PEd_roomGetInstances(_room);
+ for (var i = ds_list_size(_instances) - 1; i >= 0; i--)
+ {
+ with (_instances[| i])
+ {
+ if (visible)
+ {
+ var _color = id - 100000;
+ shader_set_uniform_f(shader_get_uniform(PEd_shInstSelect, "uColour"),
+ colour_get_red(_color) / 255,
+ colour_get_green(_color) / 255,
+ colour_get_blue(_color) / 255);
+ if ((sprite_index != -1
+ && !global.pedUsing3D)
+ || object_is_ancestor(object_index, PEd_oObject2D)
+ || object_index == PEd_oObject2D)
+ {
+ draw_self();
+ }
+ event_perform(ev_draw, 0);
+ }
+ }
+ }
+ }
+
+ // Draw pivot
+ with (PEd_oPivot)
+ {
+ var _color = id - 100000;
+ shader_set_uniform_f(shader_get_uniform(PEd_shInstSelect, "uColour"),
+ colour_get_red(_color) / 255,
+ colour_get_green(_color) / 255,
+ colour_get_blue(_color) / 255);
+ event_perform(ev_draw, 0);
+ }
+
+ shader_reset();
+ surface_reset_target();
+
+ // Colour at mouse coordinate
+ var _colSelect = surface_getpixel(_surColSelect, windowMouseX - viewportX, windowMouseY - viewportY);
+ var _id = (_colSelect + 100000) * (_colSelect > 0);
+ if (!instance_exists(_id))
+ {
+ _id = 0;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Pivot was clicked
+ if (_id == PEd_oPivot.id)
+ {
+ surface_set_target(_surColSelect);
+ draw_clear(c_black);
+ PEd_cameraSetProjection();
+
+ // Draw pivot
+ with (PEd_oPivot) { event_perform(ev_draw, 0); }
+ surface_reset_target();
+
+ // Select axis
+ editAxis = surface_getpixel(_surColSelect, windowMouseX - viewportX, windowMouseY - viewportY);
+
+ // Save mouse offset from the pivot
+ if (editAxis != PEdAxes.None)
+ {
+ if (global.pedUsing3D)
+ {
+ var _mouse;
+ var _pivot = PEd_vec3(PEd_oPivot.x, PEd_oPivot.y, PEd_oPivot.z);
+
+ if (editAxis != PEdAxes.Z)
+ {
+ _mouse = PEd_rayPlaneIntersect(PEd_vec3(x, y, z),
+ PEd_mouseRay3D(),
+ _pivot,
+ PEd_vec3(0, 0, 1));
+ }
+ else
+ {
+ _mouse = PEd_rayPlaneIntersect(PEd_vec3(x, y, z),
+ PEd_mouseRay3D(),
+ _pivot,
+ PEd_vec3Normalize(PEd_vec3(x - PEd_oPivot.x, y - PEd_oPivot.y, 0)));
+ }
+
+ mouseOff = PEd_vec3Subtract(_pivot, _mouse);
+ mouseLast = _mouse;
+ }
+ else
+ {
+ var _x = viewportX + ((PEd_oPivot.x - view_xview[0]) / viewZoom);
+ var _y = viewportY + ((PEd_oPivot.y - view_yview[0]) / viewZoom);
+ mouseOff = PEd_vec3(_x - windowMouseX, _y - windowMouseY, 0);
+ }
+
+ editNow = editTool;
+ }
+ }
+ else
+ {
+ switch (editMode)
+ {
+ case PEdEditModes.Object:
+ // Select instance
+ PEd_selectObject(_id, false, _releasedRMB);
+ break;
+
+ case PEdEditModes.Tile:
+ // Select tile
+ var _mX = (windowMouseX - viewportX) * viewZoom + view_xview[0];
+ var _mY = (windowMouseY - viewportY) * viewZoom + view_yview[0];
+ var _tile = tile_layer_find(tileDepth, _mX, _mY);
+
+ if (!tile_exists(_tile))
+ {
+ _tile = 0;
+ }
+ PEd_selectObject(_tile, false, _releasedRMB);
+ break;
+ }
+
+ // Open context menu for objects
+ if (_releasedRMB
+ && PEd_guiCanShowContextMenu()
+ && ds_list_size(selectedObjects) > 0)
+ {
+ var _contextMenu = PEd_guiCreateContextMenu();
+ PEd_guiMenuObjectEdit(_contextMenu);
+ PEd_guiAddItem(_contextMenu,
+ PEd_guiCreateContextMenuItem("Move pivot here", PEd_actMovePivotHere, ksMovePivotHere, "Move pivot at this position."));
+ PEd_guiShowContextMenu(_contextMenu);
+ }
+ }
+
+ // Redraw all
+ PEd_guiRequestRedrawAll(guiRoot)
+
+ // Free surface from memory
+ surface_free(_surColSelect);
+}
diff --git a/PushEd.gmx/scripts/PEd_instanceSetAlpha.gml b/PushEd.gmx/scripts/PEd_instanceSetAlpha.gml
new file mode 100644
index 00000000..2db6be0b
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_instanceSetAlpha.gml
@@ -0,0 +1,24 @@
+/// PEd_instanceSetAlpha(id, val)
+/**
+ * @brief Sets the instance alpha.
+ * @param {real} id The id of the instance.
+ * @param {real} val The new alpha.
+ */
+with (argument0)
+{
+ PEd_ALPHA = argument1;
+
+ with (PEd_oEditor)
+ {
+ if (PEd_getSelectedObject() == argument0)
+ {
+ for (var i = ds_list_size(selectedObjects) - 1; i > 0; i--)
+ {
+ with (selectedObjects[| i])
+ {
+ PEd_ALPHA = argument1;
+ }
+ }
+ }
+ }
+}
diff --git a/PushEd.gmx/scripts/PEd_instanceSetCode.gml b/PushEd.gmx/scripts/PEd_instanceSetCode.gml
new file mode 100644
index 00000000..79b47285
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_instanceSetCode.gml
@@ -0,0 +1,7 @@
+/// PEd_instanceSetCode(id, str)
+/**
+ * @brief Sets creation code of the instance.
+ * @param {real} id The id of the instance.
+ * @param {string} str The new creation code.
+ */
+argument0.pedCreationCode = string(argument1);
diff --git a/PushEd.gmx/scripts/PEd_instanceSetColour.gml b/PushEd.gmx/scripts/PEd_instanceSetColour.gml
new file mode 100644
index 00000000..00c4649d
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_instanceSetColour.gml
@@ -0,0 +1,24 @@
+/// PEd_instanceSetColour(id, val)
+/**
+ * @brief Sets the instance color.
+ * @param {real} id The id of the instance.
+ * @param {real} val The new color.
+ */
+with (argument0)
+{
+ PEd_COLOUR = argument1;
+
+ with (PEd_oEditor)
+ {
+ if (PEd_getSelectedObject() == argument0)
+ {
+ for (var i = ds_list_size(selectedObjects) - 1; i > 0; i--)
+ {
+ with (selectedObjects[| i])
+ {
+ PEd_COLOUR = argument1;
+ }
+ }
+ }
+ }
+}
diff --git a/PushEd.gmx/scripts/PEd_instanceSetColourARGB.gml b/PushEd.gmx/scripts/PEd_instanceSetColourARGB.gml
new file mode 100644
index 00000000..298634df
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_instanceSetColourARGB.gml
@@ -0,0 +1,28 @@
+/// PEd_instanceSetColourARGB(id, val)
+/**
+ * @brief Sets the instance color and alpha from the ARGB value.
+ * @param {real} id The id of the instance.
+ * @param {real} val The new ARGB colour.
+ */
+with (argument0)
+{
+ var _colour = PEd_argbToAlpha(argument1);
+ var _alpha = PEd_argbToColour(argument1);
+ PEd_ALPHA = _colour;
+ PEd_COLOUR = _alpha;
+
+ with (PEd_oEditor)
+ {
+ if (PEd_getSelectedObject() == argument0)
+ {
+ for (var i = ds_list_size(selectedObjects) - 1; i > 0; i--)
+ {
+ with (selectedObjects[| i])
+ {
+ PEd_ALPHA = _colour;
+ PEd_COLOUR = _alpha;
+ }
+ }
+ }
+ }
+}
diff --git a/PushEd.gmx/scripts/PEd_instanceSetName.gml b/PushEd.gmx/scripts/PEd_instanceSetName.gml
new file mode 100644
index 00000000..3d6a23c2
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_instanceSetName.gml
@@ -0,0 +1,7 @@
+/// PEd_instanceSetName(id, str)
+/**
+ * @brief Sets the instance name.
+ * @param {real} id The id of the instance.
+ * @param {string} str The new name of the instance.
+ */
+argument0.pedInstanceName = argument1;
diff --git a/PushEd.gmx/scripts/PEd_instanceSetObjectName.gml b/PushEd.gmx/scripts/PEd_instanceSetObjectName.gml
new file mode 100644
index 00000000..7c99bb1d
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_instanceSetObjectName.gml
@@ -0,0 +1,8 @@
+/// PEd_instanceSetObjectName(id, str)
+/**
+ * @brief Sets instance object name.
+ * @param {real} id The id of the instance.
+ * @param {string} str The new object name.
+ * @note When room saved, this name is used as objet name.
+ */
+argument0.pedObjectName = argument1;
diff --git a/PushEd.gmx/scripts/PEd_instanceSetPosVec2.gml b/PushEd.gmx/scripts/PEd_instanceSetPosVec2.gml
new file mode 100644
index 00000000..ae2f4bc7
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_instanceSetPosVec2.gml
@@ -0,0 +1,18 @@
+/// PEd_instanceSetPosVec2(id, pos, [updatePivot])
+/**
+ * @brief Sets the x,y position of the instance.
+ * @param {real} id The id of the instance.
+ * @param {vec2} pos The new x,y position of the instance.
+ * @param {bool} [updatePivot] If the instance is currently selected and this
+ * is set to false, then the pivot will not be updated
+ * to it's position (on the x,y axis). By default this is
+ * set to true.
+ */
+var _pos = argument[1];
+var _update = true;
+if (argument_count > 2)
+{
+ _update = argument[2];
+}
+PEd_instanceSetPosX(argument[0], _pos[0], _update);
+PEd_instanceSetPosY(argument[0], _pos[1], _update);
diff --git a/PushEd.gmx/scripts/PEd_instanceSetPosVec3.gml b/PushEd.gmx/scripts/PEd_instanceSetPosVec3.gml
new file mode 100644
index 00000000..dd59bcad
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_instanceSetPosVec3.gml
@@ -0,0 +1,19 @@
+/// PEd_instanceSetPosVec3(id, pos, [updatePivot])
+/**
+ * @brief Sets the x,y,z position of the instance.
+ * @param {real} id The id of the instance.
+ * @param {vec3} pos The new x,y,z position of the instance.
+ * @param {bool} [updatePivot] If the instance is currently selected and this
+ * is set to false, then the pivot will not be updated
+ * to it's position (on the x,y,z axis). By default this is
+ * set to true.
+ */
+var _pos = argument[1];
+var _update = true;
+if (argument_count > 2)
+{
+ _update = argument[2];
+}
+PEd_instanceSetPosX(argument[0], _pos[0], _update);
+PEd_instanceSetPosY(argument[0], _pos[1], _update);
+PEd_instanceSetPosZ(argument[0], _pos[2], _update);
diff --git a/PushEd.gmx/scripts/PEd_instanceSetPosX.gml b/PushEd.gmx/scripts/PEd_instanceSetPosX.gml
new file mode 100644
index 00000000..09a58786
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_instanceSetPosX.gml
@@ -0,0 +1,34 @@
+/// PEd_instanceSetPosX(id, val, [updatePivot])
+/**
+ * @brief Sets the x position of the instance.
+ * @param {real} id The id of the instance.
+ * @param {real} val The new x position of the instance.
+ * @param {bool} [updatePivot] If the instance is currently selected and this
+ * is set to false, then the pivot will not be updated
+ * to it's position (on the x axis). By default this is
+ * set to true.
+ */
+with (argument[0])
+{
+ PEd_POS_X = argument[1];
+
+ // Set pivot position
+ var _offset = true;
+ if (argument_count == 3)
+ {
+ _offset = argument[2];
+ }
+
+ var _selectedObj = PEd_oEditor.selectedObjects[| 0];
+ if (is_undefined(_selectedObj))
+ {
+ _selectedObj = 0;
+ }
+
+ if (_offset
+ && _selectedObj == id)
+ {
+ var _data = PEd_oEditor.selectedObjectsData[| 0];
+ PEd_oPivot.x = argument[1] - _data[0];
+ }
+}
diff --git a/PushEd.gmx/scripts/PEd_instanceSetPosY.gml b/PushEd.gmx/scripts/PEd_instanceSetPosY.gml
new file mode 100644
index 00000000..adc93294
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_instanceSetPosY.gml
@@ -0,0 +1,34 @@
+/// PEd_instanceSetPosY(id, val, [updatePivot])
+/**
+ * @brief Sets the y position of the instance.
+ * @param {real} id The id of the instance.
+ * @param {real} val The new y position of the instance.
+ * @param {bool} [updatePivot] If the instance is currently selected and this
+ * is set to false, then the pivot will not be updated
+ * to it's position (on the x axis). By default this is
+ * set to true.
+ */
+with (argument[0])
+{
+ PEd_POS_Y = argument[1];
+
+ // Set pivot position
+ var _offset = true;
+ if (argument_count == 3)
+ {
+ _offset = argument[2];
+ }
+
+ var _selectedObj = PEd_oEditor.selectedObjects[| 0];
+ if (is_undefined(_selectedObj))
+ {
+ _selectedObj = 0;
+ }
+
+ if (_offset
+ && _selectedObj == id)
+ {
+ var _data = PEd_oEditor.selectedObjectsData[| 0];
+ PEd_oPivot.y = argument[1] - _data[1];
+ }
+}
diff --git a/PushEd.gmx/scripts/PEd_instanceSetPosZ.gml b/PushEd.gmx/scripts/PEd_instanceSetPosZ.gml
new file mode 100644
index 00000000..3f04626e
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_instanceSetPosZ.gml
@@ -0,0 +1,34 @@
+/// PEd_instanceSetPosZ(id, val, [updatePivot])
+/**
+ * @brief Sets the z position of the instance.
+ * @param {real} id The id of the instance.
+ * @param {real} val The new z position of the instance.
+ * @param {bool} [updatePivot] If the instance is currently selected and this
+ * is set to false, then the pivot will not be updated
+ * to it's position (on the x axis). By default this is
+ * set to true.
+ */
+with (argument[0])
+{
+ PEd_POS_Z = argument[1];
+
+ // Set pivot position
+ var _offset = true;
+ if (argument_count == 3)
+ {
+ _offset = argument[2];
+ }
+
+ var _selectedObj = PEd_oEditor.selectedObjects[| 0];
+ if (is_undefined(_selectedObj))
+ {
+ _selectedObj = 0;
+ }
+
+ if (_offset
+ && _selectedObj == id)
+ {
+ var _data = PEd_oEditor.selectedObjectsData[| 0];
+ PEd_oPivot.z = argument[1] - _data[2];
+ }
+}
diff --git a/PushEd.gmx/scripts/PEd_instanceSetRotX.gml b/PushEd.gmx/scripts/PEd_instanceSetRotX.gml
new file mode 100644
index 00000000..7463a6b9
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_instanceSetRotX.gml
@@ -0,0 +1,26 @@
+/// PEd_instanceSetRotX(id, val)
+/**
+ * @brief Sets the instance rotation on the x axis.
+ * @param {real} id The id of the instance.
+ * @param {real} val The new rotation angle around the x axis.
+ * @note If the instance is currently selected, this x rotation
+ * is set to all instances in multiple selection.
+ */
+with (argument0)
+{
+ PEd_ROT_X = argument1;
+
+ with (PEd_oEditor)
+ {
+ if (PEd_getSelectedObject() == argument0)
+ {
+ for (var i = ds_list_size(selectedObjects) - 1; i > 0; i--)
+ {
+ with (selectedObjects[| i])
+ {
+ PEd_ROT_X = argument1;
+ }
+ }
+ }
+ }
+}
diff --git a/PushEd.gmx/scripts/PEd_instanceSetRotY.gml b/PushEd.gmx/scripts/PEd_instanceSetRotY.gml
new file mode 100644
index 00000000..8a430437
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_instanceSetRotY.gml
@@ -0,0 +1,26 @@
+/// PEd_instanceSetRotY(id, val)
+/**
+ * @brief Sets the instance rotation on the y axis.
+ * @param {real} id The id of the instance.
+ * @param {real} val The new rotation angle around the y axis.
+ * @note If the instance is currently selected, this y rotation
+ * is set to all instances in multiple selection.
+ */
+with (argument0)
+{
+ PEd_ROT_Y = argument1;
+
+ with (PEd_oEditor)
+ {
+ if (PEd_getSelectedObject() == argument0)
+ {
+ for (var i = ds_list_size(selectedObjects) - 1; i > 0; i--)
+ {
+ with (selectedObjects[| i])
+ {
+ PEd_ROT_Y = argument1;
+ }
+ }
+ }
+ }
+}
diff --git a/PushEd.gmx/scripts/PEd_instanceSetRotZ.gml b/PushEd.gmx/scripts/PEd_instanceSetRotZ.gml
new file mode 100644
index 00000000..1f927f21
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_instanceSetRotZ.gml
@@ -0,0 +1,26 @@
+/// PEd_instanceSetRotZ(id, val)
+/**
+ * @brief Sets the instance rotation on the z axis.
+ * @param {real} id The id of the instance.
+ * @param {real} val The new rotation angle around the z axis.
+ * @note If the instance is currently selected, this z rotation
+ * is set to all instances in multiple selection.
+ */
+with (argument0)
+{
+ PEd_ROT_Z = argument1;
+
+ with (PEd_oEditor)
+ {
+ if (PEd_getSelectedObject() == argument0)
+ {
+ for (var i = ds_list_size(selectedObjects) - 1; i > 0; i--)
+ {
+ with (selectedObjects[| i])
+ {
+ PEd_ROT_Z = argument1;
+ }
+ }
+ }
+ }
+}
diff --git a/PushEd.gmx/scripts/PEd_instanceSetScaleVec2.gml b/PushEd.gmx/scripts/PEd_instanceSetScaleVec2.gml
new file mode 100644
index 00000000..be7959c3
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_instanceSetScaleVec2.gml
@@ -0,0 +1,11 @@
+/// PEd_instanceSetScaleVec2(id, pos)
+/**
+ * @brief Sets the x,y scale of the instance.
+ * @param {real} id The id of the instance.
+ * @param {vec2} pos The new x,y scale of the instance.
+ * @note If the instance is currently selected, this x,y scale
+ * is set to all instances in multiple selection.
+ */
+var _scale = argument[1];
+PEd_instanceSetScaleX(argument[0], _scale[0]);
+PEd_instanceSetScaleY(argument[0], _scale[1]);
diff --git a/PushEd.gmx/scripts/PEd_instanceSetScaleVec3.gml b/PushEd.gmx/scripts/PEd_instanceSetScaleVec3.gml
new file mode 100644
index 00000000..ad859cda
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_instanceSetScaleVec3.gml
@@ -0,0 +1,12 @@
+/// PEd_instanceSetScaleVec3(id, pos)
+/**
+ * @brief Sets the x,y,z scale of the instance.
+ * @param {real} id The id of the instance.
+ * @param {vec3} pos The new x,y,z scale of the instance.
+ * @note If the instance is currently selected, this x,y,z scale
+ * is set to all instances in multiple selection.
+ */
+var _scale = argument[1];
+PEd_instanceSetScaleX(argument[0], _scale[0]);
+PEd_instanceSetScaleY(argument[0], _scale[1]);
+PEd_instanceSetScaleZ(argument[0], _scale[2]);
diff --git a/PushEd.gmx/scripts/PEd_instanceSetScaleX.gml b/PushEd.gmx/scripts/PEd_instanceSetScaleX.gml
new file mode 100644
index 00000000..b671582e
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_instanceSetScaleX.gml
@@ -0,0 +1,26 @@
+/// PEd_instanceSetScaleX(id, val)
+/**
+ * @brief Sets the instance scale on the x axis.
+ * @param {real} id The id of the instance.
+ * @param {real} val The new scale on the x axis.
+ * @note If the instance is currently selected, this x scale
+ * is set to all instances in multiple selection.
+ */
+with (argument0)
+{
+ PEd_SCALE_X = argument1;
+
+ with (PEd_oEditor)
+ {
+ if (PEd_getSelectedObject() == argument0)
+ {
+ for (var i = ds_list_size(selectedObjects) - 1; i > 0; i--)
+ {
+ with (selectedObjects[| i])
+ {
+ PEd_SCALE_X = argument1;
+ }
+ }
+ }
+ }
+}
diff --git a/PushEd.gmx/scripts/PEd_instanceSetScaleY.gml b/PushEd.gmx/scripts/PEd_instanceSetScaleY.gml
new file mode 100644
index 00000000..10d2500f
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_instanceSetScaleY.gml
@@ -0,0 +1,26 @@
+/// PEd_instanceSetScaleY(id, val)
+/**
+ * @brief Sets the instance scale on the y axis.
+ * @param {real} id The id of the instance.
+ * @param {real} val The new scale on the y axis.
+ * @note If the instance is currently selected, this y scale
+ * is set to all instances in multiple selection.
+ */
+with (argument0)
+{
+ PEd_SCALE_Y = argument1;
+
+ with (PEd_oEditor)
+ {
+ if (PEd_getSelectedObject() == argument0)
+ {
+ for (var i = ds_list_size(selectedObjects) - 1; i > 0; i--)
+ {
+ with (selectedObjects[| i])
+ {
+ PEd_SCALE_Y = argument1;
+ }
+ }
+ }
+ }
+}
diff --git a/PushEd.gmx/scripts/PEd_instanceSetScaleZ.gml b/PushEd.gmx/scripts/PEd_instanceSetScaleZ.gml
new file mode 100644
index 00000000..03637503
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_instanceSetScaleZ.gml
@@ -0,0 +1,26 @@
+/// PEd_instanceSetScaleZ(id, val)
+/**
+ * @brief Sets the instance scale on the z axis.
+ * @param {real} id The id of the instance.
+ * @param {real} val The new scale on the z axis.
+ * @note If the instance is currently selected, this z scale
+ * is set to all instances in multiple selection.
+ */
+with (argument0)
+{
+ PEd_SCALE_Z = argument1;
+
+ with (PEd_oEditor)
+ {
+ if (PEd_getSelectedObject() == argument0)
+ {
+ for (var i = ds_list_size(selectedObjects) - 1; i > 0; i--)
+ {
+ with (selectedObjects[| i])
+ {
+ PEd_SCALE_Z = argument1;
+ }
+ }
+ }
+ }
+}
diff --git a/PushEd.gmx/scripts/PEd_keyToString.gml b/PushEd.gmx/scripts/PEd_keyToString.gml
new file mode 100644
index 00000000..4e4deb3b
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_keyToString.gml
@@ -0,0 +1,65 @@
+/// PEd_keyToString(key)
+/**
+ * @brief Gets the name of the key as a string.
+ * @param {real} key The key to get the name of.
+ * @return {string} The name of the key.
+ */
+// TODO: Convert keys to string
+switch (argument0)
+{
+ case mb_left: return "LMB";
+ case mb_right: return "RMB";
+ case mb_middle: return "MMB";
+ case vk_alt: return "Alt";
+ case vk_backspace: return "Backspace";
+ case vk_control: return "Ctrl";
+ case vk_decimal: return ",";
+ case vk_delete: return "Del";
+ case vk_divide: return "/";
+ case vk_down: return "Down";
+ case vk_end: return "End";
+ case vk_escape: return "Esc";
+ case vk_f10: return "F10";
+ case vk_f11: return "F11";
+ case vk_f12: return "F12";
+ case vk_f1: return "F1";
+ case vk_f2: return "F2";
+ case vk_f3: return "F3";
+ case vk_f4: return "F4";
+ case vk_f5: return "F5";
+ case vk_f6: return "F6";
+ case vk_f7: return "F7";
+ case vk_f8: return "F8";
+ case vk_f9: return "F9";
+ case vk_home: return "Home";
+ case vk_insert: return "Ins";
+ case vk_lalt: return "Alt";
+ case vk_lcontrol: return "Control";
+ case vk_left: return "Left";
+ case vk_lshift: return "Shift";
+ case vk_multiply: return "*";
+ case vk_numpad0: return "Num0";
+ case vk_numpad1: return "Num1";
+ case vk_numpad2: return "Num2";
+ case vk_numpad3: return "Num3";
+ case vk_numpad4: return "Num4";
+ case vk_numpad5: return "Num5";
+ case vk_numpad6: return "Num6";
+ case vk_numpad7: return "Num7";
+ case vk_numpad8: return "Num8";
+ case vk_numpad9: return "Num9";
+ case vk_pagedown: return "PageDown";
+ case vk_pageup: return "PageUp";
+ case vk_pause: return "Pause";
+ case vk_printscreen: return "PrtScr";
+ case vk_ralt: return "Alt";
+ case vk_rcontrol: return "Control";
+ case vk_rshift: return "Shift";
+ case vk_shift: return "Shift";
+ case vk_space: return "Space";
+ case vk_subtract: return "-";
+ case vk_tab: return "Tab";
+ case vk_up: return "Up";
+
+ default: return chr(argument0);
+}
diff --git a/PushEd.gmx/scripts/PEd_loadRoomFromBBMAP.gml b/PushEd.gmx/scripts/PEd_loadRoomFromBBMAP.gml
new file mode 100644
index 00000000..55d1830d
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_loadRoomFromBBMAP.gml
@@ -0,0 +1,400 @@
+/// PEd_loadRoomFromBBMAP(file)
+/**
+ * @brief Loads a room from the external *.bbmap file.
+ * @param {string} file The name of the file to import from.
+ * @return {real} The id of the room or noone on fail.
+ */
+var _path = argument0;
+
+if (!file_exists(_path))
+{
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Variables
+//
+var _isEditor = instance_exists(PEd_oEditor);
+var _map = ds_map_create();
+var _listInstances = ds_list_create();
+var _listBackgrounds = ds_list_create();
+var _listTiles = ds_list_create();
+var _listViews = ds_list_create();
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Read source
+//
+var _source = "";
+var _file = file_text_open_read(_path);
+if (_file != -1)
+{
+ do
+ {
+ _source += file_text_read_string(_file);
+ file_text_readln(_file);
+ }
+ until (file_text_eof(_file));
+ file_text_close(_file);
+}
+else
+{
+ return noone;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Map and lists
+//
+ds_map_read(_map, _source);
+
+var _instances = _map[? "instances"];
+if (!is_undefined(_instances))
+{
+ ds_list_read(_listInstances, _instances);
+}
+
+var _backgrounds = _map[? "backgrounds"];
+if (!is_undefined(_backgrounds))
+{
+ ds_list_read(_listBackgrounds, _backgrounds);
+}
+
+var _tiles = _map[? "tiles"];
+if (!is_undefined(_tiles))
+{
+ ds_list_read(_listTiles, _tiles);
+}
+
+var _views = _map[? "views"];
+if (!is_undefined(_views))
+{
+ ds_list_read(_listViews, _views);
+}
+else
+{
+ show_error("Could not load BBMAP from '" + _path + "'. The file does not contain viewports definition.", false);
+ ds_map_destroy(_map);
+ ds_list_destroy(_listInstances);
+ ds_list_destroy(_listBackgrounds);
+ ds_list_destroy(_listViews);
+ ds_list_destroy(_listTiles);
+ return noone;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Create room
+//
+var _name = string_replace(_path, filename_dir(_path) + "\", "");
+ _name = string_replace(_name, ".bbmap", "");
+var _width = _map[? "width"];
+var _height = _map[? "height"];
+
+if (is_undefined(_width)
+ || is_undefined(_height))
+{
+ show_error("Could not load BBMAP from '" + _path + "'. The file does not contain room size definition.", false);
+ ds_map_destroy(_map);
+ ds_list_destroy(_listInstances);
+ ds_list_destroy(_listBackgrounds);
+ ds_list_destroy(_listViews);
+ ds_list_destroy(_listTiles);
+ return noone;
+}
+
+var _room = PEd_createRoom(_name, _width, _height);
+var _snapVer = _map[? "vsnap"];
+var _snapHor = _map[? "hsnap"];
+var _code = _map[? "code"];
+var _showGrid = _map[? "showGrid"];
+var _caption = _map[? "caption"];
+var _speed = _map[? "speed"];
+var _persistent = _map[? "persistent"];
+var _colour = _map[? "colour"];
+
+if (is_undefined(_snapVer)
+ || is_undefined(_snapHor)
+ || is_undefined(_code)
+ || is_undefined(_showGrid)
+ || is_undefined(_caption)
+ || is_undefined(_speed)
+ || is_undefined(_persistent)
+ || is_undefined(_colour))
+{
+ show_error("Could not load BBMAP from '" + _path + "'. The file is corrupted.", false);
+ ds_map_destroy(_map);
+ ds_list_destroy(_listInstances);
+ ds_list_destroy(_listBackgrounds);
+ ds_list_destroy(_listViews);
+ ds_list_destroy(_listTiles);
+ return noone;
+}
+
+PEd_roomSetSnapV(_room, _snapVer);
+PEd_roomSetSnapH(_room, _snapHor);
+PEd_roomSetCode(_room, _code);
+PEd_roomSetGrid(_room, _showGrid);
+PEd_roomSetCaption(_room, _caption);
+PEd_roomSetSpeed(_room, _speed);
+PEd_roomSetPersistent(_room, _persistent);
+PEd_roomSetColour(_room, _colour);
+
+if (!_isEditor)
+{
+ room_caption = _caption;
+ room_speed = _speed;
+ room_persistent = _persistent;
+ background_colour = _colour;
+
+ room_set_width(room, _width);
+ room_set_height(room, _height);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Physics
+//
+if (_map[? "PhysicsWorld"])
+{
+ var _gravityX = _map[? "PhysicsWorldGravityX"];
+ var _gravityY = _map[? "PhysicsWorldGravityY"];
+ var _pxToM = _map[? "PhysicsWorldPixToMeters"];
+
+ var _physicsWorld = PEd_roomGetPhysicsWorld(_room);
+ PEd_physicsWorldSetEnabled(_physicsWorld, true);
+ PEd_physicsWorldSetGravityX(_physicsWorld, _gravityX);
+ PEd_physicsWorldSetGravityY(_physicsWorld, _gravityY);
+ PEd_physicsWorldSetPxToM(_physicsWorld, _pxToM);
+
+ if (!_isEditor)
+ {
+ physics_world_create(_pxToM);
+ physics_world_gravity(_gravityX, _gravityY);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Backgrounds
+//
+for (var i = 0; i < ds_list_size(_listBackgrounds); i += 10)
+{
+ var j = i div 10;
+
+ var _background = PEd_roomGetBackground(_room, j);
+ var _image = _listBackgrounds[| i + 2];
+
+ if (background_exists(_image))
+ {
+ var _visible = _listBackgrounds[| i];
+ var _isForeground = _listBackgrounds[| i + 1];
+ var _x = _listBackgrounds[| i + 3];
+ var _y = _listBackgrounds[| i + 4];
+ var _tiledHor = _listBackgrounds[| i + 5];
+ var _tiledVer = _listBackgrounds[| i + 6];
+ var _speedHor = _listBackgrounds[| i + 7];
+ var _speedVer = _listBackgrounds[| i + 8];
+ var _stretch = _listBackgrounds[| i + 9];
+
+ PEd_backgroundSetVisible(_background, _visible);
+ PEd_backgroundSetAsForeground(_background, _isForeground);
+ PEd_backgroundSetImage(_background, _image);
+ PEd_backgroundSetX(_background, _x);
+ PEd_backgroundSetY(_background, _y);
+ PEd_backgroundSetTiledHor(_background, _tiledHor);
+ PEd_backgroundSetTiledVer(_background, _tiledVer);
+ PEd_backgroundSetSpeedHor(_background, _speedHor);
+ PEd_backgroundSetSpeedVer(_background, _speedVer);
+ PEd_backgroundSetStretch(_background, _stretch);
+
+ background_visible[j] = _visible;
+ background_foreground[j] = _isForeground;
+ background_index[j] = _image;
+ background_x[j] = _x;
+ background_y[j] = _y;
+ background_htiled[j] = _tiledHor;
+ background_vtiled[j] = _tiledVer;
+ background_hspeed[j] = _speedHor;
+ background_vspeed[j] = _speedVer;
+
+ if (_stretch)
+ {
+ background_xscale[j] = PEd_roomGetWidth(_room) / background_get_width(_image);
+ background_yscale[j] = PEd_roomGetHeight(_room) / background_get_height(_image);
+ }
+ else
+ {
+ background_xscale[j] = 1;
+ background_yscale[j] = 1;
+ }
+ }
+ else
+ {
+ show_error("Could not set background " + string(i) + " to " + string(_image) + ". Background was not found.", false);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Views
+//
+for (var i = 0; i < ds_list_size(_listViews); i += 14)
+{
+ var _viewport = PEd_roomGetViewport(_room, i div 14);
+ var _visible = _listViews[| i];
+ var _object = _listViews[| i + 1];
+ var _x = _listViews[| i + 2];
+ var _y = _listViews[| i + 3];
+ var _width = _listViews[| i + 4];
+ var _height = _listViews[| i + 5];
+ var _portX = _listViews[| i + 6];
+ var _portY = _listViews[| i + 7];
+ var _portWidth = _listViews[| i + 8];
+ var _portHeight = _listViews[| i + 9];
+ var _borderHor = _listViews[| i + 10];
+ var _borderVer = _listViews[| i + 11];
+ var _speedHor = _listViews[| i + 12];
+ var _speedVer = _listViews[| i + 13];
+
+ PEd_viewportSetVisible(_viewport, _visible);
+ if (object_exists(_object))
+ {
+ PEd_viewportSetObject(_viewport, _object);
+ }
+ else
+ {
+ show_error("Could not set view " + string(i) + " to follow object " + string(_object) + ". Object was not found.", false);
+ }
+ PEd_viewportSetX(_viewport, _x);
+ PEd_viewportSetY(_viewport, _y);
+ PEd_viewportSetWidth(_viewport, _portWidth);
+ PEd_viewportSetHeight(_viewport, _portHeight);
+ PEd_viewportSetBorderHor(_viewport, _borderHor);
+ PEd_viewportSetBorderVer(_viewport, _borderVer);
+ PEd_viewportSetSpeedHor(_viewport, _speedHor);
+ PEd_viewportSetSpeedVer(_viewport, _speedVer);
+
+ if (!_isEditor)
+ {
+ view_visible[j] = _visible;
+ view_object[j] = _object;
+ view_xview[j] = _x;
+ view_yview[j] = _y;
+ view_wview[j] = _width;
+ view_hview[j] = _height;
+ view_xport[j] = _portX;
+ view_yport[j] = _portY;
+ view_wport[j] = _portWidth;
+ view_hport[j] = _portHeight;
+ view_hborder[j] = _borderHor;
+ view_vborder[j] = _borderVer;
+ view_hspeed[j] = _speedHor;
+ view_vspeed[j] = _speedVer;
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Tiles
+//
+for (var i = 0; i < ds_list_size(_listTiles); i += 14)
+{
+ var _image = _listTiles[| i];
+
+ if (background_exists(_image))
+ {
+ var _x = _listTiles[| i + 1];
+ var _y = _listTiles[| i + 2];
+ var _depth = _listTiles[| i + 9];
+ var _width = _listTiles[| i + 3];
+ var _height = _listTiles[| i + 4];
+ var _left = _listTiles[| i + 5];
+ var _top = _listTiles[| i + 6],
+ var _blend = _listTiles[| i + 10];
+ var _scaleX = _listTiles[| i + 11];
+ var _scaleY = _listTiles[| i + 12];
+ var _alpha = _listTiles[| i + 13];
+
+ var _id = tile_add(_image, _left, _top, _width, _height, _x, _y, _depth);
+ tile_set_blend(_id, _blend);
+ tile_set_scale(_id, _scaleX, _scaleY);
+ tile_set_alpha(_id, _alpha);
+
+ if (_isEditor)
+ {
+ if (ds_list_find_index(tileLayers, _depth) == -1)
+ {
+ ds_list_add(tileLayers, _depth);
+ ds_list_add(tileVisible, true);
+ }
+
+ var _tiles = PEd_roomGetTiles(_room);
+ ds_list_add(_tiles, _id);
+ }
+ }
+ else
+ {
+ show_error("Could not create tile from " + string(_image) + ". Background was not found.", false);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Instances
+//
+for (var i = 0; i < ds_list_size(_listInstances); i += 10)
+{
+ var _objName = _listInstances[| i];
+ var _object = asset_get_index(_objName);
+ var _name = _listInstances[| i + 4];
+
+ if (_object != -1)
+ {
+ var _x = _listInstances[| i + 1];
+ var _y = _listInstances[| i + 2];
+ var _code = string_replace_all(_listInstances[| i + 3], " = ", "=");
+ var _scaleX = _listInstances[| i + 5];
+ var _scaleY = _listInstances[| i + 6];
+ var _blend = _listInstances[| i + 7];
+ var _alpha = _listInstances[| i + 8];
+ var _rotation = _listInstances[| i + 9];
+
+ var _id = PEd_instanceCreate(_room, _x, _y, _object);
+ PEd_instanceSetPosX(_id, _x);
+ PEd_instanceSetPosY(_id, _y);
+ PEd_instanceSetScaleX(_id, _scaleX);
+ PEd_instanceSetScaleY(_id, _scaleY);
+ PEd_instanceSetColour(_id, _blend);
+ PEd_instanceSetAlpha(_id, _alpha);
+ PEd_instanceSetRotZ(_id, _rotation);
+ PEd_instanceSetCode(_id, _code);
+ PEd_instanceSetName(_id, _name);
+
+ with (_id)
+ {
+ PEd_codeProcess(_code);
+ }
+ }
+ else
+ {
+ show_error("Object '" + _objName + "' was not found. Could not create instance '" + _name + "'.", false);
+ }
+}
+
+// Destroy map and lists
+ds_map_destroy(_map);
+ds_list_destroy(_listInstances);
+ds_list_destroy(_listBackgrounds);
+ds_list_destroy(_listViews);
+ds_list_destroy(_listTiles);
+
+// Redraw panel if in editor
+if (_isEditor)
+{
+ PEd_guiRequestRedrawAll(guiRoot)
+ PEd_guiShowPopupMessage("Room imported!");
+}
+
+return _room;
diff --git a/PushEd.gmx/scripts/PEd_loadRoomFromBBMAP2.gml b/PushEd.gmx/scripts/PEd_loadRoomFromBBMAP2.gml
new file mode 100644
index 00000000..96665f17
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_loadRoomFromBBMAP2.gml
@@ -0,0 +1,193 @@
+/// PEd_loadRoomFromBBMAP2(file)
+/**
+ * @brief Loads a room from the external *.bbmap version 2 file.
+ * @param {string} file The name of the file to load from.
+ * @return {real} The id of the room or noone on fail.
+ */
+var _path = argument0;
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Load buffer
+//
+var _buffer = buffer_load(_path);
+buffer_seek(_buffer, buffer_seek_start, 0);
+
+// Check bbmap version
+if (buffer_read(_buffer, buffer_u8) != 2)
+{
+ buffer_delete(_buffer);
+ return noone;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Create room
+//
+var _roomName = string_replace(filename_name(_path), ".bbmap", "");
+var _room = PEd_createRoom(_roomName, 512, 512);
+
+////////////////////////////////////////////////////////////////////////////////
+// Read room data
+PEd_roomSetWidth(_room, buffer_read(_buffer, buffer_u32));
+PEd_roomSetHeight(_room, buffer_read(_buffer, buffer_u32));
+PEd_roomSetSpeed(_room, buffer_read(_buffer, buffer_u32));
+PEd_roomSetColour(_room, buffer_read(_buffer, buffer_u32));
+PEd_roomSetShowColour(_room, buffer_read(_buffer, buffer_bool));
+PEd_roomSetPersistent(_room, buffer_read(_buffer, buffer_bool));
+PEd_roomSetCode(_room, buffer_read(_buffer, buffer_string));
+PEd_roomSetCaption(_room, buffer_read(_buffer, buffer_string));
+
+////////////////////////////////////////////////////////////////////////////////
+// Read views
+for (var i = 0; i < 8; i++)
+{
+ var _viewport = PEd_roomGetViewport(_room, i);
+ PEd_viewportSetVisible(_viewport, buffer_read(_buffer, buffer_bool));
+ PEd_viewportSetX(_viewport, buffer_read(_buffer, buffer_u32));
+ PEd_viewportSetY(_viewport, buffer_read(_buffer, buffer_u32));
+ PEd_viewportSetWidth(_viewport, buffer_read(_buffer, buffer_u32));
+ PEd_viewportSetHeight(_viewport, buffer_read(_buffer, buffer_u32));
+ PEd_viewportSetPortX(_viewport, buffer_read(_buffer, buffer_u32));
+ PEd_viewportSetPortY(_viewport, buffer_read(_buffer, buffer_u32));
+ PEd_viewportSetPortWidth(_viewport, buffer_read(_buffer, buffer_u32));
+ PEd_viewportSetPortHeight(_viewport, buffer_read(_buffer, buffer_u32));
+ PEd_viewportSetBorderHor(_viewport, buffer_read(_buffer, buffer_u32));
+ PEd_viewportSetBorderVer(_viewport, buffer_read(_buffer, buffer_u32));
+ PEd_viewportSetSpeedHor(_viewport, buffer_read(_buffer, buffer_f32));
+ PEd_viewportSetSpeedVer(_viewport, buffer_read(_buffer, buffer_f32));
+
+ var _objName = buffer_read(_buffer, buffer_string);
+ var _obj = noone;
+ if (_objName != "")
+ {
+ _obj = asset_get_index(_objName);
+ }
+
+ if (_obj >= 0)
+ {
+ PEd_viewportSetObject(_viewport, _obj);
+ }
+ else if (_obj != noone)
+ {
+ show_error("Could not set view " + string(i) + " to follow object '" + _objName + "'. Object was not found.", false);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Read backgrounds
+for (var i = 0; i < 8; i++)
+{
+ var _background = PEd_roomGetBackground(_room, i);
+ PEd_backgroundSetVisible(_background, buffer_read(_buffer, buffer_bool));
+ PEd_backgroundSetAsForeground(_background, buffer_read(_buffer, buffer_bool));
+ PEd_backgroundSetStretch(_background, buffer_read(_buffer, buffer_bool));
+ PEd_backgroundSetTiledHor(_background, buffer_read(_buffer, buffer_bool));
+ PEd_backgroundSetTiledVer(_background, buffer_read(_buffer, buffer_bool));
+ PEd_backgroundSetX(_background, buffer_read(_buffer, buffer_f32));
+ PEd_backgroundSetY(_background, buffer_read(_buffer, buffer_f32));
+ PEd_backgroundSetSpeedHor(_background, buffer_read(_buffer, buffer_f32));
+ PEd_backgroundSetSpeedVer(_background, buffer_read(_buffer, buffer_f32));
+
+ var _bgName = buffer_read(_buffer, buffer_string);
+ var _bg = noone;
+ if (_bgName != "")
+ {
+ _bg = asset_get_index(_bgName);
+ }
+
+ if (_bg >= 0)
+ {
+ PEd_backgroundSetImage(_background, _bg);
+ }
+ else if (_bg != noone)
+ {
+ show_error("Could not set background " + string(i) + " to '" + _bgName + "'. Background was not found.", false);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Read physics
+var _physicsWorld = PEd_roomGetPhysicsWorld(_room);
+PEd_physicsWorldSetEnabled(_physicsWorld, buffer_read(_buffer, buffer_bool));
+PEd_physicsWorldSetGravityX(_physicsWorld, buffer_read(_buffer, buffer_f32));
+PEd_physicsWorldSetGravityY(_physicsWorld, buffer_read(_buffer, buffer_f32));
+PEd_physicsWorldSetPxToM(_physicsWorld, buffer_read(_buffer, buffer_f32));
+
+////////////////////////////////////////////////////////////////////////////////
+// Read instances
+var _instanceCount = buffer_read(_buffer, buffer_u32);
+
+for (var i = 0; i < _instanceCount; i++)
+{
+ var _objName = buffer_read(_buffer, buffer_string);
+ var _instName = buffer_read(_buffer, buffer_string);
+ var _code = buffer_read(_buffer, buffer_string);
+ var _x = buffer_read(_buffer, buffer_f32);
+ var _y = buffer_read(_buffer, buffer_f32);
+ var _scaleX = buffer_read(_buffer, buffer_f32);
+ var _scaleY = buffer_read(_buffer, buffer_f32);
+ var _rotation = buffer_read(_buffer, buffer_f32);
+ var _argb = buffer_read(_buffer, buffer_u32);
+
+ var _obj = asset_get_index(_objName);
+ if (_obj >= 0)
+ {
+ var _inst = PEd_instanceCreate(_room, _x, _y, _obj);
+ PEd_instanceSetName(_inst, _instName);
+ PEd_instanceSetCode(_inst, _code);
+ PEd_instanceSetScaleX(_inst, _scaleX);
+ PEd_instanceSetScaleY(_inst, _scaleY);
+ PEd_instanceSetRotZ(_inst, _rotation);
+ PEd_instanceSetColour(_inst, PEd_argbToColour(_argb));
+ PEd_instanceSetAlpha(_inst, PEd_argbToAlpha(_argb));
+
+ with (_inst)
+ {
+ PEd_codeProcess(_code);
+ }
+ }
+ else
+ {
+ show_error("Object '" + _objName + "' was not found. Could not create instance '" + _instName + "'.", false);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Read tiles
+var _tileCount = buffer_read(_buffer, buffer_u32);
+
+for (var i = 0; i < _tileCount; i++)
+{
+ var _bgName = buffer_read(_buffer, buffer_string);
+ var _left = buffer_read(_buffer, buffer_u32);
+ var _top = buffer_read(_buffer, buffer_u32);
+ var _width = buffer_read(_buffer, buffer_u32);
+ var _height = buffer_read(_buffer, buffer_u32);
+ var _x = buffer_read(_buffer, buffer_f32);
+ var _y = buffer_read(_buffer, buffer_f32);
+ var _depth = buffer_read(_buffer, buffer_u32);
+ var _scaleX = buffer_read(_buffer, buffer_f32);
+ var _scaleY = buffer_read(_buffer, buffer_f32);
+ var _argb = buffer_read(_buffer, buffer_u32);
+
+ var _bg = asset_get_index(_bgName);
+ if (_bg >= 0)
+ {
+ var _tile = PEd_createTile(_room, _bg, _left, _top, _width, _height, _x, _y, _depth);
+ tile_set_scale(_tile, _scaleX, _scaleY);
+ tile_set_blend(_tile, PEd_argbToColour(_argb));
+ tile_set_alpha(_tile, PEd_argbToAlpha(_argb));
+ }
+ else
+ {
+ show_error("Could not create tile from '" + _bgName + "'. Background was not found.", false);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Delete buffer and return room
+//
+buffer_delete(_buffer);
+return _room;
diff --git a/PushEd.gmx/scripts/PEd_loadRoomFromBBMAPAuto.gml b/PushEd.gmx/scripts/PEd_loadRoomFromBBMAPAuto.gml
new file mode 100644
index 00000000..31384c05
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_loadRoomFromBBMAPAuto.gml
@@ -0,0 +1,40 @@
+/// PEd_loadRoomFromBBMAPAuto(file)
+/**
+ * @brief Auto-recognizes the version of the BBMAP file and loads a room that it contains.
+ * @param {string} file Path to the BBMAP file.
+ * @return {real} The loaded room.
+ */
+var _path = argument0;
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Load new BBMAP
+//
+var _file = file_bin_open(_path, 0);
+if (_file != -1)
+{
+ var _version = file_bin_read_byte(_file);
+ file_bin_close(_file);
+
+ if (_version == 2)
+ {
+ return PEd_loadRoomFromBBMAP2(_path);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Load old BBMAP
+//
+_file = file_text_open_read(_path);
+if (_file != -1)
+{
+ file_text_close(_file);
+ return PEd_loadRoomFromBBMAP(_path);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Load failed
+//
+return noone;
diff --git a/PushEd.gmx/scripts/PEd_loadRoomFromGMX.gml b/PushEd.gmx/scripts/PEd_loadRoomFromGMX.gml
new file mode 100644
index 00000000..52f340bd
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_loadRoomFromGMX.gml
@@ -0,0 +1,268 @@
+/// PEd_loadRoomFromGMX(file)
+/**
+ * @brief Loads a room from the XML file.
+ * @param {string} file The path to the XML file.
+ * @return {real} The id of the room or noone on fail.
+ */
+var _path = argument0;
+var _root = PEd_xmlRead(_path);
+if (_root == noone)
+{
+ return noone;
+}
+
+var _roomName = string_replace(filename_name(_path), ".room.gmx", "");
+var _room = PEd_createRoom(_roomName, 512, 512);
+var _physicsWorld = PEd_roomGetPhysicsWorld(_room);
+
+show_debug_message(PEd_xmlWrite(_root));
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Room settings
+//
+for (var i = PEd_xmlGetNumberOfChildElements(_root) - 1; i >= 0; i--)
+{
+ var _element = PEd_xmlGetChildElement(_root, i);
+ var _name = PEd_xmlGetElementName(_element);
+ var _value = PEd_xmlGetElementValue(_element);
+
+ switch (_name)
+ {
+ case "caption":
+ PEd_roomSetCaption(_room, _value);
+ break;
+
+ case "width":
+ PEd_roomSetWidth(_room, _value);
+ break;
+
+ case "height":
+ PEd_roomSetHeight(_room, _value);
+ break;
+
+ case "vsnap":
+ PEd_roomSetSnapV(_room, _value);
+ break;
+
+ case "hsnap":
+ PEd_roomSetSnapH(_room, _value);
+ break;
+
+ case "speed":
+ PEd_roomSetSpeed(_room, _value);
+ break;
+
+ case "persistent":
+ PEd_roomSetPersistent(_room, abs(_value));
+ break;
+
+ case "colour":
+ PEd_roomSetColour(_room, _value);
+ break;
+
+ case "showcolour":
+ PEd_roomSetShowColour(_room, abs(_value));
+ break;
+
+ case "code":
+ PEd_roomSetCode(_room, _value);
+ break;
+
+ case "makerSettings":
+ for (var j = PEd_xmlGetNumberOfChildElements(_element) - 1; j >= 0; j--)
+ {
+ var _child = PEd_xmlGetChildElement(_element, j);
+ var _childName = PEd_xmlGetElementName(_child);
+ var _childValue = PEd_xmlGetElementValue(_child);
+
+ switch (_childName)
+ {
+ case "showGrid":
+ PEd_roomSetGrid(_room, abs(_childValue));
+ break;
+ }
+ }
+ break;
+
+ case "PhysicsWorld":
+ PEd_physicsWorldSetEnabled(_physicsWorld, abs(_value));
+ break;
+
+ case "PhysicsWorldGravityX":
+ PEd_physicsWorldSetGravityX(_physicsWorld, _value);
+ break;
+
+ case "PhysicsWorldGravityY":
+ PEd_physicsWorldSetGravityY(_physicsWorld, _value);
+ break;
+
+ case "PhysicsWorldPixToMeters":
+ PEd_physicsWorldSetPxToM(_physicsWorld, _value);
+ break;
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Load views
+//
+var _views = PEd_xmlFindAllElements(_root, "view");
+var _numberOfViews = ds_list_size(_views);
+
+for (var i = 0; i < _numberOfViews; i++)
+{
+ var _v = _views[| i];
+ var _viewport = PEd_roomGetViewport(_room, i);
+ PEd_viewportSetVisible(_viewport, abs(PEd_xmlGetElementAttribute(_v, "visible")));
+
+ var _objName = PEd_xmlGetElementAttribute(_v, "objName");
+ if (_objName != "")
+ {
+ var _obj = asset_get_index(_objName);
+ if (_obj >= 0)
+ {
+ PEd_viewportSetObject(_viewport, _obj);
+ }
+ else
+ {
+ show_error("Could not set view " + string(i) + " to follow object '" + _objName + "'. Object was not found.", false);
+ }
+ }
+
+ PEd_viewportSetX(_viewport, PEd_xmlGetElementAttribute(_v, "xview"));
+ PEd_viewportSetY(_viewport, PEd_xmlGetElementAttribute(_v, "yview"));
+ PEd_viewportSetWidth(_viewport, PEd_xmlGetElementAttribute(_v, "wview"));
+ PEd_viewportSetHeight(_viewport, PEd_xmlGetElementAttribute(_v, "hview"));
+ PEd_viewportSetPortX(_viewport, PEd_xmlGetElementAttribute(_v, "xport"));
+ PEd_viewportSetPortY(_viewport, PEd_xmlGetElementAttribute(_v, "yport"));
+ PEd_viewportSetPortWidth(_viewport, PEd_xmlGetElementAttribute(_v, "wport"));
+ PEd_viewportSetPortHeight(_viewport, PEd_xmlGetElementAttribute(_v, "hport"));
+ PEd_viewportSetBorderHor(_viewport, PEd_xmlGetElementAttribute(_v, "hborder"));
+ PEd_viewportSetBorderVer(_viewport, PEd_xmlGetElementAttribute(_v, "vborder"));
+ PEd_viewportSetSpeedHor(_viewport, PEd_xmlGetElementAttribute(_v, "hspeed"));
+ PEd_viewportSetSpeedVer(_viewport, PEd_xmlGetElementAttribute(_v, "vspeed"));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Load backgrounds
+//
+var _backgrounds = PEd_xmlFindAllElements(_root, "background");
+var _numberOfBackgrounds = ds_list_size(_backgrounds);
+
+for (var i = 0; i < _numberOfBackgrounds; i++)
+{
+ var _bgr = _backgrounds[| i];
+ var _background = PEd_roomGetBackground(_room, i);
+ PEd_backgroundSetVisible(_background, abs(PEd_xmlGetElementAttribute(_bgr, "visible")));
+ PEd_backgroundSetAsForeground(_background, abs(PEd_xmlGetElementAttribute(_bgr, "foreground")));
+ var _name = PEd_xmlGetElementAttribute(_bgr, "name");
+ if (is_string(_name))
+ {
+ var _bg = asset_get_index(_name)
+ if (_bg >= 0)
+ {
+ PEd_backgroundSetImage(_background, _bg);
+ }
+ else
+ {
+ show_error("Could not set background " + string(i) + " to '" + _bgName + "'. Background was not found.", false);
+ }
+ }
+ PEd_backgroundSetX(_background, PEd_xmlGetElementAttribute(_bgr, "x"));
+ PEd_backgroundSetY(_background, PEd_xmlGetElementAttribute(_bgr, "y"));
+ PEd_backgroundSetTiledHor(_background, abs(PEd_xmlGetElementAttribute(_bgr, "htiled")));
+ PEd_backgroundSetTiledVer(_background, abs(PEd_xmlGetElementAttribute(_bgr, "vtiled")));
+ PEd_backgroundSetSpeedHor(_background, PEd_xmlGetElementAttribute(_bgr, "hspeed"));
+ PEd_backgroundSetSpeedVer(_background, PEd_xmlGetElementAttribute(_bgr, "vspeed"));
+ PEd_backgroundSetStretch(_background, abs(PEd_xmlGetElementAttribute(_bgr, "stretch")));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Load instances
+//
+var _instances = PEd_xmlFindAllElements(_root, "instance");
+var _numberOfInstances = ds_list_size(_instances);
+
+for (var i = 0; i < _numberOfInstances; i++)
+{
+ var _inst = _instances[| i];
+ var _objName = PEd_xmlGetElementAttribute(_inst, "objName");
+ var _instName = PEd_xmlGetElementAttribute(_inst, "name");
+
+ var _obj = asset_get_index(_objName);
+ if (_obj >= 0)
+ {
+ var _id = PEd_instanceCreate(_room,
+ PEd_xmlGetElementAttribute(_inst, "x"),
+ PEd_xmlGetElementAttribute(_inst, "y"),
+ _obj);
+
+ PEd_instanceSetName(_id, _instName);
+
+ var _code = PEd_xmlGetElementAttribute(_inst, "code");
+ PEd_instanceSetCode(_id, _code);
+
+ PEd_instanceSetScaleX(_id, PEd_xmlGetElementAttribute(_inst, "scaleX"));
+ PEd_instanceSetScaleY(_id, PEd_xmlGetElementAttribute(_inst, "scaleY"));
+ PEd_instanceSetRotZ(_id, PEd_xmlGetElementAttribute(_inst, "rotation"));
+
+ var _argb = PEd_xmlGetElementAttribute(_inst, "colour");
+ PEd_instanceSetColour(_id, PEd_argbToColour(_argb));
+ PEd_instanceSetAlpha(_id, PEd_argbToAlpha(_argb));
+
+ with (_id)
+ {
+ PEd_codeProcess(_code);
+ }
+ }
+ else
+ {
+ show_error("Object '" + _objName + "' was not found. Could not create instance '" + _instName + "'.", false);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Load tiles
+//
+var _roomTileList = PEd_roomGetTiles(_room);
+var _tiles = PEd_xmlFindAllElements(_root, "tile");
+var _numberOfTiles = ds_list_size(_tiles);
+
+for (var i = 0; i < _numberOfTiles; i++)
+{
+ var _t = _tiles[| i];
+ var _bgName = PEd_xmlGetElementAttribute(_t, "bgName");
+ var _bg = asset_get_index(_bgName);
+
+ if (_bg >= 0)
+ {
+ var _id = PEd_createTile(_room,
+ _bg,
+ PEd_xmlGetElementAttribute(_t, "xo"),
+ PEd_xmlGetElementAttribute(_t, "yo"),
+ PEd_xmlGetElementAttribute(_t, "w"),
+ PEd_xmlGetElementAttribute(_t, "h"),
+ PEd_xmlGetElementAttribute(_t, "x"),
+ PEd_xmlGetElementAttribute(_t, "y"),
+ PEd_xmlGetElementAttribute(_t, "depth"));
+
+ tile_set_scale(_id,
+ PEd_xmlGetElementAttribute(_t, "scaleX"),
+ PEd_xmlGetElementAttribute(_t, "scaleY"));
+
+ var _argb = PEd_xmlGetElementAttribute(_t, "colour");
+ tile_set_blend(_id, PEd_argbToColour(_argb));
+ tile_set_alpha(_id, PEd_argbToAlpha(_argb));
+ }
+ else
+ {
+ show_error("Could not create tile from '" + _bgName + "'. Background was not found.", false);
+ }
+}
+
+PEd_xmlDestroyElement(_root);
+return _room;
diff --git a/PushEd.gmx/scripts/PEd_loadRoomFromPEd.gml b/PushEd.gmx/scripts/PEd_loadRoomFromPEd.gml
new file mode 100644
index 00000000..a07190a8
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_loadRoomFromPEd.gml
@@ -0,0 +1,138 @@
+/// PEd_loadRoomFromPEd(file)
+/**
+ * @brief Loads a room from the external *.ped file.
+ * @param {string} file The name of the file to load from.
+ * @return {real} The id of the room or noone on fail.
+ */
+var _path = argument0;
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Read source
+//
+var _source = "";
+var _file = file_text_open_read(_path);
+if (_file != -1)
+{
+ do
+ {
+ _source += file_text_read_string(_file);
+ file_text_readln(_file);
+ }
+ until (file_text_eof(_file));
+ file_text_close(_file);
+}
+else
+{
+ return noone;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Load room
+//
+var _room = json_decode(_source);
+if (_room == -1)
+{
+ return noone;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Load camera and editor settings
+global.pedUsing3D = _room[? "using3D"];
+PEd_oEditor.editMode = _room[? "editMode"];
+PEd_oEditor.editTool = _room[? "editTool"];
+PEd_oEditor.editFloor = _room[? "editFloor"];
+PEd_oEditor.x = _room[? "cameraX"];
+PEd_oEditor.y = _room[? "cameraY"];
+PEd_oEditor.z = _room[? "cameraZ"];
+PEd_oEditor.direction = _room[? "cameraDir"];
+PEd_oEditor.camPitch = _room[? "cameraPitch"];
+view_xview[0] = _room[? "viewX"];
+view_yview[0] = _room[? "viewY"];
+PEd_oEditor.viewZoom = _room[? "viewZoom"];
+PEd_oPivot.x = _room[? "pivotX"];
+PEd_oPivot.y = _room[? "pivotY"];
+PEd_oPivot.z = _room[? "pivotZ"];
+
+PEd_oEditor.viewLastX = view_xview[0];
+PEd_oEditor.viewLastY = view_xview[0];
+
+if (global.pedUsing3D)
+{
+ PEd_start3D();
+}
+else
+{
+ PEd_end3D();
+}
+
+// TODO: Following could be put into a separate script, which would just take
+// list as parameter.
+
+////////////////////////////////////////////////////////////////////////////////
+// Load instances
+var _instances = PEd_roomGetInstances(_room);
+var _numberOfInstances = ds_list_size(_instances);
+
+for (var i = 0; i < _numberOfInstances; _numberOfInstances--)
+{
+ var _inst = _instances[| i];
+ var _id = PEd_instanceCreate(_room,
+ _inst[? "x"],
+ _inst[? "y"],
+ asset_get_index(_inst[? "object"]));
+ PEd_instanceSetName(_id, _inst[? "name"]);
+ var _code = _inst[? "code"];
+ PEd_instanceSetCode(_id, _code);
+ PEd_instanceSetScaleX(_id, _inst[? "scaleX"]);
+ PEd_instanceSetScaleY(_id, _inst[? "scaleY"]);
+ PEd_instanceSetRotZ(_id, _inst[? "rotation"]);
+ PEd_instanceSetColour(_id, _inst[? "colour"]);
+ PEd_instanceSetAlpha(_id, _inst[? "alpha"]);
+
+ if (!is_undefined(_inst[? "visible"]))
+ {
+ _id.visible = _inst[? "visible"];
+ }
+
+ with (_id)
+ {
+ PEd_codeProcess(_code);
+ }
+
+ ds_list_delete(_instances, i);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Load tiles
+var _tiles = PEd_roomGetTiles(_room);
+var _numberOfTiles = ds_list_size(_tiles);
+
+for (var i = 0; i < _numberOfTiles; _numberOfTiles--)
+{
+ var _t = _tiles[| i];
+ var _id = PEd_createTile(_room,
+ asset_get_index(_t[? "background"]),
+ _t[? "left"],
+ _t[? "top"],
+ _t[? "width"],
+ _t[? "height"],
+ _t[? "x"],
+ _t[? "y"],
+ _t[? "depth"]);
+ tile_set_scale(_id,
+ _t[? "scaleX"],
+ _t[? "scaleY"]);
+ tile_set_blend(_id, _t[? "colour"]);
+ tile_set_alpha(_id, _t[? "alpha"]);
+
+ if (!is_undefined(_t[? "visible"]))
+ {
+ tile_set_visible(_id, _t[? "visible"]);
+ }
+
+ ds_list_delete(_tiles, i);
+}
+
+return _room;
diff --git a/PushEd.gmx/scripts/PEd_matrix.gml b/PushEd.gmx/scripts/PEd_matrix.gml
new file mode 100644
index 00000000..87a562ae
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_matrix.gml
@@ -0,0 +1,15 @@
+/// PEd_matrix(m0, m1, m2, m3)
+/**
+ * @brief Creates a new 4x4 matrix.
+ * @param {vec4} m0 The first row of the matrix.
+ * @param {vec4} m1 The second row of the matrix.
+ * @param {vec4} m2 The third row of the matrix.
+ * @param {vec4} m3 The fourth row of the matrix.
+ * @return {matrix} The created matrix.
+ */
+var _mat; _mat[15] = 0;
+_mat[ 0] = argument0[0]; _mat[ 4] = argument1[0]; _mat[ 8] = argument2[0]; _mat[12] = argument3[0];
+_mat[ 1] = argument0[1]; _mat[ 5] = argument1[1]; _mat[ 9] = argument2[1]; _mat[13] = argument3[1];
+_mat[ 2] = argument0[2]; _mat[ 6] = argument1[2]; _mat[10] = argument2[2]; _mat[14] = argument3[2];
+_mat[ 3] = argument0[3]; _mat[ 7] = argument1[3]; _mat[11] = argument2[3]; _mat[15] = argument3[3];
+return _mat;
diff --git a/PushEd.gmx/scripts/PEd_matrixDeterminant.gml b/PushEd.gmx/scripts/PEd_matrixDeterminant.gml
new file mode 100644
index 00000000..e6aeeeb8
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_matrixDeterminant.gml
@@ -0,0 +1,13 @@
+/// PEd_matrixDeterminant(matrix)
+/**
+ * @brief Calculates determinant of the given matrix.
+ * @param {matrix} matrix The matrix to calculate the determinant of.
+ * @return {real} The determinant.
+ */
+gml_pragma("forceinline");
+return (argument0[ 3]*argument0[ 6]*argument0[ 9]*argument0[12] - argument0[ 2]*argument0[ 7]*argument0[ 9]*argument0[12] - argument0[ 3]*argument0[ 5]*argument0[10]*argument0[12] + argument0[ 1]*argument0[ 7]*argument0[10]*argument0[12]
+ + argument0[ 2]*argument0[ 5]*argument0[11]*argument0[12] - argument0[ 1]*argument0[ 6]*argument0[11]*argument0[12] - argument0[ 3]*argument0[ 6]*argument0[ 8]*argument0[13] + argument0[ 2]*argument0[ 7]*argument0[ 8]*argument0[13]
+ + argument0[ 3]*argument0[ 4]*argument0[10]*argument0[13] - argument0[ 0]*argument0[ 7]*argument0[10]*argument0[13] - argument0[ 2]*argument0[ 4]*argument0[11]*argument0[13] + argument0[ 0]*argument0[ 6]*argument0[11]*argument0[13]
+ + argument0[ 3]*argument0[ 5]*argument0[ 8]*argument0[14] - argument0[ 1]*argument0[ 7]*argument0[ 8]*argument0[14] - argument0[ 3]*argument0[ 4]*argument0[ 9]*argument0[14] + argument0[ 0]*argument0[ 7]*argument0[ 9]*argument0[14]
+ + argument0[ 1]*argument0[ 4]*argument0[11]*argument0[14] - argument0[ 0]*argument0[ 5]*argument0[11]*argument0[14] - argument0[ 2]*argument0[ 5]*argument0[ 8]*argument0[15] + argument0[ 1]*argument0[ 6]*argument0[ 8]*argument0[15]
+ + argument0[ 2]*argument0[ 4]*argument0[ 9]*argument0[15] - argument0[ 0]*argument0[ 6]*argument0[ 9]*argument0[15] - argument0[ 1]*argument0[ 4]*argument0[10]*argument0[15] + argument0[ 0]*argument0[ 5]*argument0[10]*argument0[15]);
diff --git a/PushEd.gmx/scripts/PEd_matrixIdentity.gml b/PushEd.gmx/scripts/PEd_matrixIdentity.gml
new file mode 100644
index 00000000..bd264824
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_matrixIdentity.gml
@@ -0,0 +1,10 @@
+/// PEd_matrixIdentity()
+/**
+ * @brief Gets an identity matrix.
+ * @return {matrix} An identity matrix.
+ */
+gml_pragma("forceinline");
+return PEd_matrix(PEd_vec4(1, 0, 0, 0),
+ PEd_vec4(0, 1, 0, 0),
+ PEd_vec4(0, 0, 1, 0),
+ PEd_vec4(0, 0, 0, 1));
diff --git a/PushEd.gmx/scripts/PEd_matrixInverse.gml b/PushEd.gmx/scripts/PEd_matrixInverse.gml
new file mode 100644
index 00000000..ea40366b
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_matrixInverse.gml
@@ -0,0 +1,25 @@
+/// PEd_matrixInverse(matrix)
+/**
+ * @brief Calculates the inverse matrix of the given matrix.
+ * @param {matrix} matrix The matrix to calculate the inverse matrix of.
+ * @return {matrix} The inverse matrix.
+ */
+var _m = argument0;
+var _i;
+_i[15] = _m[ 1]*_m[ 6]*_m[ 8] - _m[ 2]*_m[ 5]*_m[ 8] + _m[ 2]*_m[ 4]*_m[ 9] - _m[ 0]*_m[ 6]*_m[ 9] - _m[ 1]*_m[ 4]*_m[10] + _m[ 0]*_m[ 5]*_m[10];
+_i[ 0] = _m[ 6]*_m[11]*_m[13] - _m[ 7]*_m[10]*_m[13] + _m[ 7]*_m[ 9]*_m[14] - _m[ 5]*_m[11]*_m[14] - _m[ 6]*_m[ 9]*_m[15] + _m[ 5]*_m[10]*_m[15];
+_i[ 1] = _m[ 3]*_m[10]*_m[13] - _m[ 2]*_m[11]*_m[13] - _m[ 3]*_m[ 9]*_m[14] + _m[ 1]*_m[11]*_m[14] + _m[ 2]*_m[ 9]*_m[15] - _m[ 1]*_m[10]*_m[15];
+_i[ 2] = _m[ 2]*_m[ 7]*_m[13] - _m[ 3]*_m[ 6]*_m[13] + _m[ 3]*_m[ 5]*_m[14] - _m[ 1]*_m[ 7]*_m[14] - _m[ 2]*_m[ 5]*_m[15] + _m[ 1]*_m[ 6]*_m[15];
+_i[ 3] = _m[ 3]*_m[ 6]*_m[ 9] - _m[ 2]*_m[ 7]*_m[ 9] - _m[ 3]*_m[ 5]*_m[10] + _m[ 1]*_m[ 7]*_m[10] + _m[ 2]*_m[ 5]*_m[11] - _m[ 1]*_m[ 6]*_m[11];
+_i[ 4] = _m[ 7]*_m[10]*_m[12] - _m[ 6]*_m[11]*_m[12] - _m[ 7]*_m[ 8]*_m[14] + _m[ 4]*_m[11]*_m[14] + _m[ 6]*_m[ 8]*_m[15] - _m[ 4]*_m[10]*_m[15];
+_i[ 5] = _m[ 2]*_m[11]*_m[12] - _m[ 3]*_m[10]*_m[12] + _m[ 3]*_m[ 8]*_m[14] - _m[ 0]*_m[11]*_m[14] - _m[ 2]*_m[ 8]*_m[15] + _m[ 0]*_m[10]*_m[15];
+_i[ 6] = _m[ 3]*_m[ 6]*_m[12] - _m[ 2]*_m[ 7]*_m[12] - _m[ 3]*_m[ 4]*_m[14] + _m[ 0]*_m[ 7]*_m[14] + _m[ 2]*_m[ 4]*_m[15] - _m[ 0]*_m[ 6]*_m[15];
+_i[ 7] = _m[ 2]*_m[ 7]*_m[ 8] - _m[ 3]*_m[ 6]*_m[ 8] + _m[ 3]*_m[ 4]*_m[10] - _m[ 0]*_m[ 7]*_m[10] - _m[ 2]*_m[ 4]*_m[11] + _m[ 0]*_m[ 6]*_m[11];
+_i[ 8] = _m[ 5]*_m[11]*_m[12] - _m[ 7]*_m[ 9]*_m[12] + _m[ 7]*_m[ 8]*_m[13] - _m[ 4]*_m[11]*_m[13] - _m[ 5]*_m[ 8]*_m[15] + _m[ 4]*_m[ 9]*_m[15];
+_i[ 9] = _m[ 3]*_m[ 9]*_m[12] - _m[ 1]*_m[11]*_m[12] - _m[ 3]*_m[ 8]*_m[13] + _m[ 0]*_m[11]*_m[13] + _m[ 1]*_m[ 8]*_m[15] - _m[ 0]*_m[ 9]*_m[15];
+_i[10] = _m[ 1]*_m[ 7]*_m[12] - _m[ 3]*_m[ 5]*_m[12] + _m[ 3]*_m[ 4]*_m[13] - _m[ 0]*_m[ 7]*_m[13] - _m[ 1]*_m[ 4]*_m[15] + _m[ 0]*_m[ 5]*_m[15];
+_i[11] = _m[ 3]*_m[ 5]*_m[ 8] - _m[ 1]*_m[ 7]*_m[ 8] - _m[ 3]*_m[ 4]*_m[ 9] + _m[ 0]*_m[ 7]*_m[ 9] + _m[ 1]*_m[ 4]*_m[11] - _m[ 0]*_m[ 5]*_m[11];
+_i[12] = _m[ 6]*_m[ 9]*_m[12] - _m[ 5]*_m[10]*_m[12] - _m[ 6]*_m[ 8]*_m[13] + _m[ 4]*_m[10]*_m[13] + _m[ 5]*_m[ 8]*_m[14] - _m[ 4]*_m[ 9]*_m[14];
+_i[13] = _m[ 1]*_m[10]*_m[12] - _m[ 2]*_m[ 9]*_m[12] + _m[ 2]*_m[ 8]*_m[13] - _m[ 0]*_m[10]*_m[13] - _m[ 1]*_m[ 8]*_m[14] + _m[ 0]*_m[ 9]*_m[14];
+_i[14] = _m[ 2]*_m[ 5]*_m[12] - _m[ 1]*_m[ 6]*_m[12] - _m[ 2]*_m[ 4]*_m[13] + _m[ 0]*_m[ 6]*_m[13] + _m[ 1]*_m[ 4]*_m[14] - _m[ 0]*_m[ 5]*_m[14];
+return PEd_matrixScale(_i, 1 / PEd_matrixDeterminant(_m));
diff --git a/PushEd.gmx/scripts/PEd_matrixLookAt.gml b/PushEd.gmx/scripts/PEd_matrixLookAt.gml
new file mode 100644
index 00000000..c2ae51d3
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_matrixLookAt.gml
@@ -0,0 +1,16 @@
+/// PEd_matrixLookAt(eye, at, up)
+/**
+ * @brief Builds a look-at matrix.
+ * @param {vec3} eye The eye position.
+ * @param {vec3} at The look-at target position.
+ * @param {vec3} up The world's up vector.
+ * @return {matrix} The look-at matrix.
+ */
+// Source: https://msdn.microsoft.com/en-us/library/windows/desktop/bb205342(v=vs.85).aspx
+var _axisZ = PEd_vec3Normalize(PEd_vec3Subtract(argument1, argument0));
+var _axisX = PEd_vec3Normalize(PEd_vec3Cross(argument2, _axisZ));
+var _axisY = PEd_vec3Cross(_axisZ, _axisX);
+return PEd_matrix(PEd_vec4(_axisX[0], _axisY[0], _axisZ[0], 0),
+ PEd_vec4(_axisX[1], _axisY[1], _axisZ[1], 0),
+ PEd_vec4(_axisX[2], _axisY[2], _axisZ[2], 0),
+ PEd_vec4(-PEd_vec3Dot(_axisX, argument0), -PEd_vec3Dot(_axisY, argument0), -PEd_vec3Dot(_axisZ, argument0), 1));
diff --git a/PushEd.gmx/scripts/PEd_matrixPerspectiveFov.gml b/PushEd.gmx/scripts/PEd_matrixPerspectiveFov.gml
new file mode 100644
index 00000000..66d7bf41
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_matrixPerspectiveFov.gml
@@ -0,0 +1,16 @@
+/// PEd_matrixPerspectiveFov(fov, aspectRatio, zNear, zFar)
+/**
+ * @brief Builds a perspective projective matrix based on the field of view.
+ * @param {real} fov The field of view, in degrees.
+ * @param {real} aspectRatio The aspect ratio (screenWidth / screenHeight).
+ * @param {real} zNear The z-value of the near view-plane.
+ * @param {real} zFar The z-value of the far view-plane.
+ * @return {matrix} The perspective projection matrix.
+ */
+// Source: https://msdn.microsoft.com/en-us/library/windows/desktop/bb205350(v=vs.85).aspx
+var _cotHalfFov = 1 / dtan(argument0 * 0.5);
+var _zRange = argument3 - argument2;
+return PEd_matrix(PEd_vec4(_cotHalfFov / argument1, 0, 0, 0),
+ PEd_vec4(0, _cotHalfFov, 0, 0),
+ PEd_vec4(0, 0, argument3 / _zRange, 1),
+ PEd_vec4(0, 0, -argument2 * argument3 / _zRange, 0));
diff --git a/PushEd.gmx/scripts/PEd_matrixPrint.gml b/PushEd.gmx/scripts/PEd_matrixPrint.gml
new file mode 100644
index 00000000..c2e88459
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_matrixPrint.gml
@@ -0,0 +1,16 @@
+/// PEd_matrixPrint(matrix)
+/**
+ * @brief Prints matrix to the debug console.
+ * @param {matrix} matrix The matrix to print.
+ */
+var m = argument0;
+var i = 0;
+repeat (4)
+{
+ var t = "";
+ repeat (4)
+ {
+ t += string(m[i++]) + " ";
+ }
+ show_debug_message(t);
+}
diff --git a/PushEd.gmx/scripts/PEd_matrixRotToEuler.gml b/PushEd.gmx/scripts/PEd_matrixRotToEuler.gml
new file mode 100644
index 00000000..b9a3d9d0
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_matrixRotToEuler.gml
@@ -0,0 +1,35 @@
+/// PEd_matrixRotToEuler(rotationMatrix)
+/**
+ * @brief Gets euler angles from a YXZ rotation matrix.
+ * @param {matrix} rotationMatrix The YXZ rotation matrix.
+ * @return {vec3} vec3(Rx, Ry, Rz).
+ */
+// Source: https://www.geometrictools.com/Documentation/EulerAngles.pdf
+var _mat = argument0;
+var _thetaX, _thetaY, _thetaZ;
+
+if (_mat[6] < 1)
+{
+ if (_mat[6] > -1)
+ {
+ _thetaX = arcsin(-_mat[6]);
+ _thetaY = arctan2(_mat[2], _mat[10]);
+ _thetaZ = arctan2(_mat[4], _mat[5]);
+ }
+ else
+ {
+ _thetaX = pi * 0.5;
+ _thetaY = -arctan2(-_mat[1], _mat[0]);
+ _thetaZ = 0;
+ }
+}
+else
+{
+ _thetaX = -pi * 0.5;
+ _thetaY = arctan2(-_mat[1], _mat[0]);
+ _thetaZ = 0;
+}
+
+return PEd_vec3(radtodeg(_thetaX) mod 360,
+ radtodeg(_thetaY) mod 360,
+ radtodeg(_thetaZ) mod 360);
diff --git a/PushEd.gmx/scripts/PEd_matrixScale.gml b/PushEd.gmx/scripts/PEd_matrixScale.gml
new file mode 100644
index 00000000..26745f8c
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_matrixScale.gml
@@ -0,0 +1,13 @@
+/// PEd_matrixScale(matrix, val)
+/**
+ * @brief Scales the matrix by the given value.
+ * @param {matrix} matrix The matrix to be scaled.
+ * @param {real} val The value to scale the matrix by.
+ * @return {matrix} The scaled matrix.
+ */
+var _m = argument0;
+for (var i = 0; i < 16; i++)
+{
+ _m[i] *= argument1;
+}
+return _m;
diff --git a/PushEd.gmx/scripts/PEd_matrixTranspose.gml b/PushEd.gmx/scripts/PEd_matrixTranspose.gml
new file mode 100644
index 00000000..1809acb3
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_matrixTranspose.gml
@@ -0,0 +1,13 @@
+/// PEd_matrixTranspose(matrix)
+/**
+ * @brief Transposes the matrix.
+ * @param {matrix} matrix The matrix to be transposed.
+ * @return {matrix} The transposed matrix.
+ */
+var _m = argument0;
+var _t;
+_t[ 0] = _m[ 0]; _t[ 4] = _m[ 1]; _t[ 8] = _m[ 2]; _t[12] = _m[ 3];
+_t[ 1] = _m[ 4]; _t[ 5] = _m[ 5]; _t[ 9] = _m[ 6]; _t[13] = _m[ 7];
+_t[ 2] = _m[ 8]; _t[ 6] = _m[ 9]; _t[10] = _m[10]; _t[14] = _m[11];
+_t[ 3] = _m[12]; _t[ 7] = _m[13]; _t[11] = _m[14]; _t[15] = _m[15];
+return _t;
diff --git a/PushEd.gmx/scripts/PEd_meshCreate.gml b/PushEd.gmx/scripts/PEd_meshCreate.gml
new file mode 100644
index 00000000..37e29717
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_meshCreate.gml
@@ -0,0 +1,11 @@
+/// PEd_meshCreate()
+/**
+ * @brief Creates an empty mesh.
+ * @return {real} The id of the created mesh.
+ */
+var _mesh = ds_map_create();
+ds_map_add_list(_mesh, "vertex", ds_list_create());
+ds_map_add_list(_mesh, "normal", ds_list_create());
+ds_map_add_list(_mesh, "texture", ds_list_create());
+ds_map_add_list(_mesh, "face", ds_list_create());
+return _mesh;
diff --git a/PushEd.gmx/scripts/PEd_meshDestroy.gml b/PushEd.gmx/scripts/PEd_meshDestroy.gml
new file mode 100644
index 00000000..cdf6843f
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_meshDestroy.gml
@@ -0,0 +1,7 @@
+/// PEd_meshDestroy(mesh)
+/**
+ * @brief Destroys the mesh.
+ * @param {real} mesh The id of the mesh.
+ */
+gml_pragma("forceinline");
+ds_map_destroy(argument0);
diff --git a/PushEd.gmx/scripts/PEd_meshInit.gml b/PushEd.gmx/scripts/PEd_meshInit.gml
new file mode 100644
index 00000000..b44b70fd
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_meshInit.gml
@@ -0,0 +1,10 @@
+/// PEd_meshInit()
+/**
+ * @brief Initializes mesh functionality.
+ */
+// Create default vertex buffer format
+vertex_format_begin();
+vertex_format_add_position_3d();
+vertex_format_add_normal();
+vertex_format_add_textcoord();
+global.pedVBufferFormat = vertex_format_end();
diff --git a/PushEd.gmx/scripts/PEd_meshLoadFromObj.gml b/PushEd.gmx/scripts/PEd_meshLoadFromObj.gml
new file mode 100644
index 00000000..c1feca3a
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_meshLoadFromObj.gml
@@ -0,0 +1,89 @@
+/// PEd_meshLoadFromObj(file)
+/**
+ * @brief Loads a 3D model data into a ds_map from the *.obj file.
+ * @param {string} file The path to the file.
+ * @return {real} The id of the mesh on success or noone on fail.
+ */
+var _file = file_text_open_read(argument0);
+if (_file == -1)
+ return noone;
+
+// Create mesh structure
+var _mesh = PEd_meshCreate();
+var _vertex = _mesh[? "vertex"];
+var _normal = _mesh[? "normal"];
+var _texture = _mesh[? "texture"];
+var _face = _mesh[? "face"];
+
+// Laod mesh data
+while (!file_text_eof(_file))
+{
+ var _line = file_text_read_string(_file); // Read line
+ var _split = PEd_stringSplitOnFirst(_line, " "); // Read first word
+ _line = _split[1]; // Delete the word from the line
+ var _id = _split[0]; // Identifier
+
+ switch (_id)
+ {
+ // Vertex
+ case "v":
+ _split = PEd_stringSplitOnFirst(_line, " ");
+ ds_list_add(_vertex, real(_split[0]));
+
+ _split = PEd_stringSplitOnFirst(_split[1], " ");
+ ds_list_add(_vertex, real(_split[0]));
+
+ _split = PEd_stringSplitOnFirst(_split[1], " ");
+ ds_list_add(_vertex, real(_split[0]));
+ break;
+
+ // Vertex normal
+ case "vn":
+ _split = PEd_stringSplitOnFirst(_line, " ");
+ ds_list_add(_normal, real(_split[0]));
+
+ _split = PEd_stringSplitOnFirst(_split[1], " ");
+ ds_list_add(_normal, real(_split[0]));
+
+ _split = PEd_stringSplitOnFirst(_split[1], " ");
+ ds_list_add(_normal, real(_split[0]));
+ break;
+
+ // Vertex texture coordinate
+ case "vt":
+ _split = PEd_stringSplitOnFirst(_line, " ");
+ ds_list_add(_texture, real(_split[0]));
+
+ _split = PEd_stringSplitOnFirst(_split[1], " ");
+ ds_list_add(_texture, real(_split[0]));
+ break;
+
+ // Face
+ case "f":
+ var _f; _f[2] = "";
+ _split = PEd_stringSplitOnFirst(_line, " ");
+ _f[0] = _split[0];
+ _split = PEd_stringSplitOnFirst(_split[1], " ");
+ _f[1] = _split[0];
+ _split = PEd_stringSplitOnFirst(_split[1], " ");
+ _f[2] = _split[0];
+
+ for (var i = 0; i < 3; i++)
+ {
+ _split = PEd_stringSplitOnFirst(_f[i], "/");
+ var v = (real(_split[0]) - 1) * 3;
+ _split = PEd_stringSplitOnFirst(_split[1], "/");
+ var t = (real(_split[0]) - 1) * 2;
+ _split = PEd_stringSplitOnFirst(_split[1], "/");
+ var n = (real(_split[0]) - 1) * 3;
+
+ ds_list_add(_face, v, n, t);
+ }
+ break;
+ }
+
+ file_text_readln(_file);
+}
+file_text_close(_file);
+
+return _mesh;
diff --git a/PushEd.gmx/scripts/PEd_meshToModelD3D.gml b/PushEd.gmx/scripts/PEd_meshToModelD3D.gml
new file mode 100644
index 00000000..acd12e09
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_meshToModelD3D.gml
@@ -0,0 +1,32 @@
+/// PEd_meshToModelD3D(mesh)
+/**
+ * @brief Creates a d3d_model from the mesh.
+ * @param {real} mesh The id of the mesh.
+ * @return {real} The id of the model on success or noone on fail.
+ */
+var _mesh = argument0;
+var _vertex = _mesh[? "vertex"];
+var _normal = _mesh[? "normal"];
+var _texture = _mesh[? "texture"];
+var _face = _mesh[? "face"];
+var _model = d3d_model_create();
+
+d3d_model_primitive_begin(_model, pr_trianglelist);
+
+var _size = ds_list_size(_face);
+var i = 0;
+while (i < _size)
+{
+ var v = _face[| i++];
+ var n = _face[| i++];
+ var t = _face[| i++];
+
+ d3d_model_vertex_normal_texture(_model,
+ _vertex[| v], _vertex[| v + 1], _vertex[| v + 2],
+ _normal[| n], _normal[| n + 1], _normal[| n + 2],
+ _texture[| t], _texture[| t + 1]);
+}
+
+d3d_model_primitive_end(_model);
+
+return _model;
diff --git a/PushEd.gmx/scripts/PEd_meshToVBuffer.gml b/PushEd.gmx/scripts/PEd_meshToVBuffer.gml
new file mode 100644
index 00000000..6ce09e4b
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_meshToVBuffer.gml
@@ -0,0 +1,33 @@
+/// PEd_meshToVBuffer(mesh)
+/**
+ * @brief Creates a vertex buffer from the mesh.
+ * @param {real} mesh The id of the mesh.
+ * @return {real} The id of the vertex buffer on success or noone on fail.
+ */
+var _mesh = argument0;
+var _vertex = _mesh[? "vertex"];
+var _normal = _mesh[? "normal"];
+var _texture = _mesh[? "texture"];
+var _face = _mesh[? "face"];
+var _vBuffer = vertex_create_buffer();
+
+vertex_begin(_vBuffer, global.pedVBufferFormat);
+
+var _size = ds_list_size(_face);
+var i = 0;
+while (i < _size)
+{
+ var v = _face[| i++];
+ vertex_position_3d(_vBuffer, _vertex[| v], _vertex[| v + 1], _vertex[| v + 2]);
+
+ var n = _face[| i++];
+ vertex_normal(_vBuffer, _normal[| n], _normal[| n + 1], _normal[| n + 2]);
+
+ var t = _face[| i++];
+ vertex_texcoord(_vBuffer, _texture[| t], _texture[| t + 1]);
+}
+
+vertex_end(_vBuffer);
+vertex_freeze(_vBuffer);
+
+return _vBuffer;
diff --git a/PushEd.gmx/scripts/PEd_mouseRay3D.gml b/PushEd.gmx/scripts/PEd_mouseRay3D.gml
new file mode 100644
index 00000000..a5b58f91
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_mouseRay3D.gml
@@ -0,0 +1,7 @@
+/// PEd_mouseRay3D()
+/**
+ * @brief Gets a ray pointing from the camera in the mouse direction.
+ * @return {vec3} A ray pointing from the camera in the mouse direction.
+ */
+gml_pragma("forceinline");
+return PEd_cameraUnprojectVec2(PEd_vec2(windowMouseX, windowMouseY));
diff --git a/PushEd.gmx/scripts/PEd_objectGetIndex.gml b/PushEd.gmx/scripts/PEd_objectGetIndex.gml
new file mode 100644
index 00000000..3b1ca73e
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_objectGetIndex.gml
@@ -0,0 +1,9 @@
+/// PEd_objectGetIndex(name)
+/**
+ * @brief Finds the object's index in the list of objects.
+ * @param {string} name The object name.
+ * @return {real} The object's index in the list of objects or -1
+ * if the object was not found.
+ */
+gml_pragma("forceinline");
+return ds_list_find_index(PEd_oEditor.pedObjectList, argument0);
diff --git a/PushEd.gmx/scripts/PEd_objectListGetItem.gml b/PushEd.gmx/scripts/PEd_objectListGetItem.gml
new file mode 100644
index 00000000..1d834e37
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_objectListGetItem.gml
@@ -0,0 +1,12 @@
+/// PEd_objectListGetItem(ind)
+/**
+ * @brief Gets the object on given index in the list of objects.
+ * @param {real} ind The index.
+ * @return {real} The object on given index in the list of objects.
+ */
+var i = ds_list_find_value(PEd_oEditor.pedObjectList, argument0);
+if (is_undefined(i))
+{
+ return -1;
+}
+return i;
diff --git a/PushEd.gmx/scripts/PEd_objectListGetSize.gml b/PushEd.gmx/scripts/PEd_objectListGetSize.gml
new file mode 100644
index 00000000..2fdcddae
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_objectListGetSize.gml
@@ -0,0 +1,7 @@
+/// PEd_objectListGetSize()
+/**
+ * @brief Gets the number of objects in the objects list.
+ * @return {real} Number of objects in the objects list.
+ */
+gml_pragma("forceinline");
+return ds_list_size(PEd_oEditor.pedObjectList);
diff --git a/PushEd.gmx/scripts/PEd_physicsWorldGetEnabled.gml b/PushEd.gmx/scripts/PEd_physicsWorldGetEnabled.gml
new file mode 100644
index 00000000..057e977a
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_physicsWorldGetEnabled.gml
@@ -0,0 +1,8 @@
+/// PEd_physicsWorldGetEnabled(physicsWorld)
+/**
+ * @brief Gets the state of the physics world.
+ * @param {real} physicsWorld The id of the physics world.
+ * @return {bool} True if physics is enabled.
+ */
+gml_pragma("forceinline");
+return argument0[? "enabled"];
diff --git a/PushEd.gmx/scripts/PEd_physicsWorldGetGravityX.gml b/PushEd.gmx/scripts/PEd_physicsWorldGetGravityX.gml
new file mode 100644
index 00000000..d48e5ade
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_physicsWorldGetGravityX.gml
@@ -0,0 +1,8 @@
+/// PEd_physicsWorldGetGravityX(physicsWorld)
+/**
+ * @brief Gets the gravity on the x axis in the physics world.
+ * @param {real} physicsWorld The id of the physics world.
+ * @return {real} The gravity on the x axis in the physics world.
+ */
+gml_pragma("forceinline");
+return argument0[? "gravityX"];
diff --git a/PushEd.gmx/scripts/PEd_physicsWorldGetGravityY.gml b/PushEd.gmx/scripts/PEd_physicsWorldGetGravityY.gml
new file mode 100644
index 00000000..368bd852
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_physicsWorldGetGravityY.gml
@@ -0,0 +1,8 @@
+/// PEd_physicsWorldGetGravityY(physicsWorld)
+/**
+ * @brief Gets the gravity on the y axis in the physics world.
+ * @param {real} physicsWorld The id of the physics world.
+ * @return {real} The gravity on the y axis in the physics world.
+ */
+gml_pragma("forceinline");
+return argument0[? "gravityY"];
diff --git a/PushEd.gmx/scripts/PEd_physicsWorldGetPxToM.gml b/PushEd.gmx/scripts/PEd_physicsWorldGetPxToM.gml
new file mode 100644
index 00000000..666b7868
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_physicsWorldGetPxToM.gml
@@ -0,0 +1,8 @@
+/// PEd_physicsWorldGetPxToM(physicsWorld)
+/**
+ * @brief Gets the pixel to meter ratio of the physics world.
+ * @param {real} physicsWorld The id of the physics world.
+ * @return {real} The pixel to meter ratio of the physics world.
+ */
+gml_pragma("forceinline");
+return argument0[? "pxToM"];
diff --git a/PushEd.gmx/scripts/PEd_physicsWorldSetEnabled.gml b/PushEd.gmx/scripts/PEd_physicsWorldSetEnabled.gml
new file mode 100644
index 00000000..34197c73
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_physicsWorldSetEnabled.gml
@@ -0,0 +1,7 @@
+/// PEd_physicsWorldSetEnabled(physicsWorld, enabled)
+/**
+ * @brief Enables/disabled physics in the physics world.
+ * @param {real} physicsWorld The id of the physics world.
+ * @param {bool} enabled True to enable physics.
+ */
+argument0[? "enabled"] = argument1;
diff --git a/PushEd.gmx/scripts/PEd_physicsWorldSetGravityX.gml b/PushEd.gmx/scripts/PEd_physicsWorldSetGravityX.gml
new file mode 100644
index 00000000..45a55242
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_physicsWorldSetGravityX.gml
@@ -0,0 +1,7 @@
+/// PEd_physicsWorldSetGravityX(physicsWorld, val)
+/**
+ * @brief Sets gravity on the x axis in the physics world.
+ * @param {real} physicsWorld The id of the physics world.
+ * @param {real} val The new gravity on the x axis.
+ */
+argument0[? "gravityX"] = argument1;
diff --git a/PushEd.gmx/scripts/PEd_physicsWorldSetGravityY.gml b/PushEd.gmx/scripts/PEd_physicsWorldSetGravityY.gml
new file mode 100644
index 00000000..c9a75250
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_physicsWorldSetGravityY.gml
@@ -0,0 +1,7 @@
+/// PEd_physicsWorldSetGravityY(physicsWorld, val)
+/**
+ * @brief Sets gravity on the y axis in the physics world.
+ * @param {real} physicsWorld The id of the physics world.
+ * @param {real} val The new gravity on the y axis.
+ */
+argument0[? "gravityY"] = argument1;
diff --git a/PushEd.gmx/scripts/PEd_physicsWorldSetPxToM.gml b/PushEd.gmx/scripts/PEd_physicsWorldSetPxToM.gml
new file mode 100644
index 00000000..00b0c262
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_physicsWorldSetPxToM.gml
@@ -0,0 +1,7 @@
+/// PEd_physicsWorldSetPxToM(physicsWorld, val)
+/**
+ * @brief Sets pixel to meter ratio in the physics world.
+ * @param {real} physicsWorld The id of the physics world.
+ * @param {real} val The new pixel to meter ratio.
+ */
+argument0[? "pxToM"] = argument1;
diff --git a/PushEd.gmx/scripts/PEd_pointDistPlane.gml b/PushEd.gmx/scripts/PEd_pointDistPlane.gml
new file mode 100644
index 00000000..aa64cffd
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_pointDistPlane.gml
@@ -0,0 +1,10 @@
+/// PEd_pointDistPlane(point, plane, normal)
+/**
+ * @brief Gets distance from the point to the plane.
+ * @param {vec3} point The point position.
+ * @param {vec3} plane The plane origin.
+ * @param {vec3} normal The plane normal.
+ * @return {real} The distance from the point to the plane.
+ */
+gml_pragma("forceinline");
+return PEd_vec3Dot(PEd_vec3Subtract(argument0, argument1), argument2);
diff --git a/PushEd.gmx/scripts/PEd_rayPlaneIntersect.gml b/PushEd.gmx/scripts/PEd_rayPlaneIntersect.gml
new file mode 100644
index 00000000..b4fc6ad2
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_rayPlaneIntersect.gml
@@ -0,0 +1,11 @@
+/// PEd_rayPlaneIntersect(origin, direction, plane, normal)
+/**
+ * @brief Gets the interestion point of the ray and the plane.
+ * @param {vec3} origin The origin of the ray.
+ * @param {vec3} direction The direction of the ray.
+ * @param {vec3} plane A point on the plane.
+ * @param {vec3} normal The normal vector of the plane.
+ * @return {vec3} The intersection of the ray and the plane.
+ */
+var t = -((PEd_vec3Dot(PEd_vec3Subtract(argument0, argument2), argument3)) / PEd_vec3Dot(argument1, argument3));
+return PEd_vec3Add(argument0, PEd_vec3Scale(argument1, t));
diff --git a/PushEd.gmx/scripts/PEd_registerCustomDataContainer.gml b/PushEd.gmx/scripts/PEd_registerCustomDataContainer.gml
new file mode 100644
index 00000000..6f0fa948
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_registerCustomDataContainer.gml
@@ -0,0 +1,8 @@
+/// PEd_registerCustomDataContainer(container)
+/**
+ * @brief Registers custom data container to the engine.
+ * @param {real} container The id of the container.
+ */
+gml_pragma("forceinline");
+ds_list_add(PEd_oEditor.customData, argument0);
+ds_list_mark_as_list(PEd_oEditor.customData, ds_list_size(PEd_oEditor.customData) - 1);
diff --git a/PushEd.gmx/scripts/PEd_removeFromStr.gml b/PushEd.gmx/scripts/PEd_removeFromStr.gml
new file mode 100644
index 00000000..1fc6a48c
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_removeFromStr.gml
@@ -0,0 +1,13 @@
+/// PEd_removeFromStr(startStr, endStr, string)
+/**
+ * @brief Removes part beginning with startStr and
+ * ending with endStr from the string.
+ * @param {string} startStr The start of the part to remove.
+ * @param {string} endStr The end of the part to remove.
+ * @param {string} string The string to remove the part from.
+ * @return {string} The string with the given part removed.
+ */
+var txt = argument2;
+var s = string_pos(argument0, txt);
+var e = string_pos(argument1, txt);
+return string_delete(txt, s, e - s + string_length(argument1));
diff --git a/PushEd.gmx/scripts/PEd_rgbToBgr.gml b/PushEd.gmx/scripts/PEd_rgbToBgr.gml
new file mode 100644
index 00000000..1562b176
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_rgbToBgr.gml
@@ -0,0 +1,8 @@
+/// PEd_rgbToBgr(colour)
+/**
+ * @brief Converts between RGB and BGR colour format.
+ * @param {real} colour The BGR or RGB colour.
+ * @return {real} The resulting colour.
+ */
+gml_pragma("forceinline");
+return ((argument0 & $FF0000) >> 16) | (argument0 & $FF00) | ((argument0 & $FF) << 16);
diff --git a/PushEd.gmx/scripts/PEd_roomApplySettings.gml b/PushEd.gmx/scripts/PEd_roomApplySettings.gml
new file mode 100644
index 00000000..5e324156
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_roomApplySettings.gml
@@ -0,0 +1,60 @@
+/// PEd_roomApplySettings(room)
+/**
+ * @brief Applies room settings to the current room.
+ * @param {real} room The id of the room.
+ */
+var _room = argument0;
+var _width = PEd_roomGetWidth(_room);
+var _height = PEd_roomGetHeight(_room);
+
+////////////////////////////////////////////////////////////////////////////////
+// Apply view and background settings
+background_colour = PEd_roomGetColour(_room);
+background_showcolour = PEd_roomGetShowColour(_room);
+
+for (var i = 0; i < 8; i++)
+{
+ ////////////////////////////////////////////////////////////////////////////
+ // Views
+ var _viewport = PEd_roomGetViewport(_room, i);
+ view_visible[i] = PEd_viewportGetVisible(_viewport);
+ view_xview[i] = PEd_viewportGetX(_viewport);
+ view_yview[i] = PEd_viewportGetY(_viewport);
+ view_wview[i] = PEd_viewportGetWidth(_viewport);
+ view_hview[i] = PEd_viewportGetHeight(_viewport);
+ view_xport[i] = PEd_viewportGetPortX(_viewport);
+ view_yport[i] = PEd_viewportGetPortY(_viewport);
+ view_wport[i] = PEd_viewportGetPortWidth(_viewport);
+ view_hport[i] = PEd_viewportGetPortHeight(_viewport);
+ view_hborder[i] = PEd_viewportGetBorderHor(_viewport);
+ view_vborder[i] = PEd_viewportGetBorderVer(_viewport);
+ view_hspeed[i] = PEd_viewportGetSpeedHor(_viewport);
+ view_vspeed[i] = PEd_viewportGetSpeedVer(_viewport);
+ view_object[i] = PEd_viewportGetObject(_viewport);
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Backgrounds
+ var _background = PEd_roomGetBackground(_room, i);
+ background_foreground[i] = PEd_backgroundIsForeground(_background);
+ background_index[i] = PEd_backgroundGetImage(_background);
+ background_visible[i] = PEd_backgroundGetVisible(_background);
+
+ if (PEd_backgroundGetStretch(_background))
+ {
+ background_xscale[i] = _width / background_get_width(background_index[i]);
+ background_yscale[i] = _height / background_get_height(background_index[i]);
+ background_htiled[i] = false;
+ background_vtiled[i] = false;
+ background_x[i] = 0;
+ background_y[i] = 0;
+ }
+ else
+ {
+ background_xscale[i] = 1;
+ background_yscale[i] = 1;
+ background_htiled[i] = PEd_backgroundGetTiledHor(_background);
+ background_vtiled[i] = PEd_backgroundGetTiledVer(_background);
+ background_x[i] = PEd_backgroundGetX(_background);
+ background_y[i] = PEd_backgroundGetY(_background);
+ }
+}
diff --git a/PushEd.gmx/scripts/PEd_roomCopy.gml b/PushEd.gmx/scripts/PEd_roomCopy.gml
new file mode 100644
index 00000000..ef110cb8
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_roomCopy.gml
@@ -0,0 +1,54 @@
+/// PEd_roomCopy(room)
+/**
+ * @brief Creates a copy of the room
+ * @param {real} room The id of the room.
+ * @return {real} The copy of the room.
+ */
+var _room = argument0;
+var _copy = ds_map_create();
+
+// TODO: Oh 4 fcks sake...
+_copy[? "name"] = _room[? "name"];
+_copy[? "caption"] = _room[? "caption"];
+_copy[? "width"] = _room[? "width"];
+_copy[? "height"] = _room[? "height"];
+_copy[? "hsnap"] = _room[? "hsnap"];
+_copy[? "vsnap"] = _room[? "vsnap"];
+_copy[? "dsnap"] = _room[? "dsnap"];
+_copy[? "speed"] = _room[? "speed"];
+_copy[? "persistent"] = _room[? "persistent"];
+_copy[? "colour"] = _room[? "colour"];
+_copy[? "code"] = _room[? "code"];
+_copy[? "grid"] = _room[? "grid"];
+_copy[? "showColour"] = _room[? "showColour"];
+
+var _physicsWorld = ds_map_create();
+ds_map_copy(_physicsWorld, PEd_roomGetPhysicsWorld(_room));
+ds_map_add_map(_copy, "physicsWorld", _physicsWorld);
+
+var _backgrounds = ds_list_create();
+var _viewports = ds_list_create();
+var _instances = ds_list_create();
+var _tiles = ds_list_create();
+
+for (var i = 0; i < 8; i++)
+{
+ // TODO: Create scripts for copying backgrounds, viewports, ...
+ var _background = ds_map_create();
+ ds_map_copy(_background, PEd_roomGetBackground(_room, i));
+ PEd_dsListAddMap(_backgrounds, _background);
+
+ var _viewport = ds_map_create();
+ ds_map_copy(_viewport, PEd_roomGetViewport(_room, i));
+ PEd_dsListAddMap(_viewports, _viewport);
+}
+
+ds_list_copy(_instances, PEd_roomGetInstances(_room));
+ds_list_copy(_tiles, PEd_roomGetTiles(_room));
+
+ds_map_add_list(_copy, "backgrounds", _backgrounds);
+ds_map_add_list(_copy, "viewports", _viewports);
+ds_map_add_list(_copy, "instances", _instances);
+ds_map_add_list(_copy, "tiles", _tiles);
+
+return _copy;
diff --git a/PushEd.gmx/scripts/PEd_roomDestroy.gml b/PushEd.gmx/scripts/PEd_roomDestroy.gml
new file mode 100644
index 00000000..94be03fe
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_roomDestroy.gml
@@ -0,0 +1,29 @@
+/// PEd_roomDestroy(room)
+/**
+ * @brief Destroys the room and all instances and tiles that it contains.
+ * @param {real} room The id of the room.
+ */
+var _room = argument0;
+var _instances = PEd_roomGetInstances(_room);
+var _tiles = PEd_roomGetTiles(_room);
+
+for (var i = ds_list_size(_instances) - 1; i >= 0; i--)
+{
+ with (_instances[| i])
+ {
+ instance_destroy();
+ }
+}
+ds_list_clear(_instances);
+
+for (var i = ds_list_size(_tiles) - 1; i >= 0; i--)
+{
+ var _tile = _tiles[| i];
+ if (tile_exists(_tile))
+ {
+ tile_delete(_tile);
+ }
+}
+ds_list_clear(_tiles);
+
+ds_map_destroy(_room);
diff --git a/PushEd.gmx/scripts/PEd_roomGetBackground.gml b/PushEd.gmx/scripts/PEd_roomGetBackground.gml
new file mode 100644
index 00000000..3b661c1f
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_roomGetBackground.gml
@@ -0,0 +1,9 @@
+/// PEd_roomGetBackground(room, ind)
+/**
+ * @brief Gets the background with given index of the room.
+ * @param {real} room The id of the room.
+ * @param {real} ind The background index (0...7).
+ * @return {real} The id of the background.
+ */
+gml_pragma("forceinline");
+return ds_list_find_value(argument0[? "backgrounds"], argument1);
diff --git a/PushEd.gmx/scripts/PEd_roomGetCaption.gml b/PushEd.gmx/scripts/PEd_roomGetCaption.gml
new file mode 100644
index 00000000..a0698671
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_roomGetCaption.gml
@@ -0,0 +1,8 @@
+/// PEd_roomGetCaption(room)
+/**
+ * @brief Gets the caption of the room.
+ * @param {real} room The id of the room.
+ * @return {string} The caption of the room.
+ */
+gml_pragma("forceinline");
+return argument0[? "caption"];
diff --git a/PushEd.gmx/scripts/PEd_roomGetCode.gml b/PushEd.gmx/scripts/PEd_roomGetCode.gml
new file mode 100644
index 00000000..43100d39
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_roomGetCode.gml
@@ -0,0 +1,8 @@
+/// PEd_roomGetCode(room)
+/**
+ * @brief Gets the creation code of the room.
+ * @param {real} room The id of the room.
+ * @return {string} The creation code of the room.
+ */
+gml_pragma("forceinline");
+return argument0[? "code"]
diff --git a/PushEd.gmx/scripts/PEd_roomGetColour.gml b/PushEd.gmx/scripts/PEd_roomGetColour.gml
new file mode 100644
index 00000000..a279ce55
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_roomGetColour.gml
@@ -0,0 +1,8 @@
+/// PEd_roomGetColour(room)
+/**
+ * @brief Gets the colour of the room.
+ * @param {real} room The id of the room.
+ * @return {real} The colour of the room.
+ */
+gml_pragma("forceinline");
+return argument0[? "colour"];
diff --git a/PushEd.gmx/scripts/PEd_roomGetColourARGB.gml b/PushEd.gmx/scripts/PEd_roomGetColourARGB.gml
new file mode 100644
index 00000000..b0a88cc9
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_roomGetColourARGB.gml
@@ -0,0 +1,8 @@
+/// PEd_roomGetColourARGB(room)
+/**
+ * @brief Gets the ARGB colour of the room.
+ * @param {real} room The id of the room.
+ * @return {real} The ARGB colour of the room.
+ */
+gml_pragma("forceinline");
+return PEd_colourAlphaToArgb(argument0[? "colour"], 1);
diff --git a/PushEd.gmx/scripts/PEd_roomGetFromId.gml b/PushEd.gmx/scripts/PEd_roomGetFromId.gml
new file mode 100644
index 00000000..151391e2
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_roomGetFromId.gml
@@ -0,0 +1,8 @@
+/// PEd_roomGetFromId(room)
+/**
+ * @brief Gets the room from the room id.
+ * @param {real} room The id of the room.
+ * @return {real} room The room.
+ */
+gml_pragma("forceinline");
+return (-argument0 - 1);
diff --git a/PushEd.gmx/scripts/PEd_roomGetGrid.gml b/PushEd.gmx/scripts/PEd_roomGetGrid.gml
new file mode 100644
index 00000000..9ad0ca25
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_roomGetGrid.gml
@@ -0,0 +1,8 @@
+/// PEd_roomGetGrid(room)
+/**
+ * @brief Gets the grid of the room.
+ * @param {real} room The id of the room.
+ * @return {bool} True if the grid is enabled.
+ */
+gml_pragma("forceinline");
+return argument0[? "grid"];
diff --git a/PushEd.gmx/scripts/PEd_roomGetHeight.gml b/PushEd.gmx/scripts/PEd_roomGetHeight.gml
new file mode 100644
index 00000000..f7c6f61e
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_roomGetHeight.gml
@@ -0,0 +1,8 @@
+/// PEd_roomGetHeight(room)
+/**
+ * @brief Gets the height of the room.
+ * @param {real} room The id of the room.
+ * @return {real} The height of the room.
+ */
+gml_pragma("forceinline");
+return argument0[? "height"];
diff --git a/PushEd.gmx/scripts/PEd_roomGetId.gml b/PushEd.gmx/scripts/PEd_roomGetId.gml
new file mode 100644
index 00000000..7b3d395a
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_roomGetId.gml
@@ -0,0 +1,8 @@
+/// PEd_roomGetId(room)
+/**
+ * @brief Gets the id of the room.
+ * @param {real} room The room to get the id of.
+ * @return {real} room The id of the room.
+ */
+gml_pragma("forceinline");
+return (-argument0 - 1);
diff --git a/PushEd.gmx/scripts/PEd_roomGetInstances.gml b/PushEd.gmx/scripts/PEd_roomGetInstances.gml
new file mode 100644
index 00000000..c552ae69
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_roomGetInstances.gml
@@ -0,0 +1,8 @@
+/// PEd_roomGetInstances(room)
+/**
+ * @brief Gets the list of room instances.
+ * @param {room} The id of the room.
+ * @return {real} The list of room instances.
+ */
+gml_pragma("forceinline");
+return argument0[? "instances"];
diff --git a/PushEd.gmx/scripts/PEd_roomGetName.gml b/PushEd.gmx/scripts/PEd_roomGetName.gml
new file mode 100644
index 00000000..5e9f6289
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_roomGetName.gml
@@ -0,0 +1,8 @@
+/// PEd_roomGetName(room)
+/**
+ * @brief Gets the name of the room.
+ * @param {real} room The id of the room.
+ * @return {string} The name of the room.
+ */
+gml_pragma("forceinline");
+return argument0[? "name"];
diff --git a/PushEd.gmx/scripts/PEd_roomGetPersistent.gml b/PushEd.gmx/scripts/PEd_roomGetPersistent.gml
new file mode 100644
index 00000000..38f423e7
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_roomGetPersistent.gml
@@ -0,0 +1,8 @@
+/// PEd_roomGetPersistent(room)
+/**
+ * @brief Gets the persistence setting of the room.
+ * @param {real} room The id of the room.
+ * @return {bool} True if the room is persistent.
+ */
+gml_pragma("forceinline");
+return argument0[? "persistent"];
diff --git a/PushEd.gmx/scripts/PEd_roomGetPhysicsWorld.gml b/PushEd.gmx/scripts/PEd_roomGetPhysicsWorld.gml
new file mode 100644
index 00000000..b7b45bbd
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_roomGetPhysicsWorld.gml
@@ -0,0 +1,8 @@
+/// PEd_roomGetPhysicsWorld(room)
+/**
+ * @brief Gets the physics world of the room.
+ * @param {real} room The id of the room.
+ * @return {real} The id of the physics world of the room.
+ */
+gml_pragma("forceinline");
+return argument0[? "physicsWorld"];
diff --git a/PushEd.gmx/scripts/PEd_roomGetShowColour.gml b/PushEd.gmx/scripts/PEd_roomGetShowColour.gml
new file mode 100644
index 00000000..f30a926b
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_roomGetShowColour.gml
@@ -0,0 +1,8 @@
+/// PEd_roomGetShowColour(room)
+/**
+ * @brief Gets the show colour setting of the room.
+ * @param {room} The id of the room.
+ * @return {bool} The state of the show colour setting.
+ */
+gml_pragma("forceinline");
+return argument0[? "showColour"];
diff --git a/PushEd.gmx/scripts/PEd_roomGetSnapD.gml b/PushEd.gmx/scripts/PEd_roomGetSnapD.gml
new file mode 100644
index 00000000..399e2dae
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_roomGetSnapD.gml
@@ -0,0 +1,8 @@
+/// PEd_roomGetSnapD(room)
+/**
+ * @brief Gets the depth snap size of the room.
+ * @param {real} room The id of the room.
+ * @return {real} The depth snap size of the room.
+ */
+gml_pragma("forceinline");
+return argument0[? "dsnap"];
diff --git a/PushEd.gmx/scripts/PEd_roomGetSnapH.gml b/PushEd.gmx/scripts/PEd_roomGetSnapH.gml
new file mode 100644
index 00000000..eac5dbe7
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_roomGetSnapH.gml
@@ -0,0 +1,8 @@
+/// PEd_roomGetSnapH(room)
+/**
+ * @brief Gets the horizontal snap size of the room.
+ * @param {real} room The id of the room.
+ * @return {real} The horizontal snap size of the room.
+ */
+gml_pragma("forceinline");
+return argument0[? "hsnap"];
diff --git a/PushEd.gmx/scripts/PEd_roomGetSnapV.gml b/PushEd.gmx/scripts/PEd_roomGetSnapV.gml
new file mode 100644
index 00000000..01d9d452
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_roomGetSnapV.gml
@@ -0,0 +1,8 @@
+/// PEd_roomGetSnapV(room)
+/**
+ * @brief Gets the vertical snap size of the room.
+ * @param {real} room The id of the room.
+ * @return {real} The vertical snap size of the room.
+ */
+gml_pragma("forceinline");
+return argument0[? "vsnap"];
diff --git a/PushEd.gmx/scripts/PEd_roomGetSpeed.gml b/PushEd.gmx/scripts/PEd_roomGetSpeed.gml
new file mode 100644
index 00000000..4daf9583
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_roomGetSpeed.gml
@@ -0,0 +1,8 @@
+/// PEd_roomGetSpeed(room)
+/**
+ * @brief Gets the speed of the room.
+ * @param {real} room The id of the room.
+ * @return {real} The speed of the room.
+ */
+gml_pragma("forceinline");
+return argument0[? "speed"];
diff --git a/PushEd.gmx/scripts/PEd_roomGetTiles.gml b/PushEd.gmx/scripts/PEd_roomGetTiles.gml
new file mode 100644
index 00000000..69f3ba4b
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_roomGetTiles.gml
@@ -0,0 +1,8 @@
+/// PEd_roomGetTiles(room)
+/**
+ * @brief Gets the list of room tiles.
+ * @param {room} The id of the room.
+ * @return {real} The list of room tiles.
+ */
+gml_pragma("forceinline");
+return argument0[? "tiles"];
diff --git a/PushEd.gmx/scripts/PEd_roomGetViewport.gml b/PushEd.gmx/scripts/PEd_roomGetViewport.gml
new file mode 100644
index 00000000..9b3d16ee
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_roomGetViewport.gml
@@ -0,0 +1,9 @@
+/// PEd_roomGetViewport(room, ind)
+/**
+ * @brief Gets the viewport with given index of the room.
+ * @param {real} room The id of the room.
+ * @param {real} ind The viewport index (0...7).
+ * @return {real} The id of the viewport.
+ */
+gml_pragma("forceinline");
+return ds_list_find_value(argument0[? "viewports"], argument1);
diff --git a/PushEd.gmx/scripts/PEd_roomGetWidth.gml b/PushEd.gmx/scripts/PEd_roomGetWidth.gml
new file mode 100644
index 00000000..c75c37b5
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_roomGetWidth.gml
@@ -0,0 +1,8 @@
+/// PEd_roomGetWidth(room)
+/**
+ * @brief Gets the width of the room.
+ * @param {real} room The id of the room.
+ * @return {real} The width of the room.
+ */
+gml_pragma("forceinline");
+return argument0[? "width"];
diff --git a/PushEd.gmx/scripts/PEd_roomSaveToBBMAP2.gml b/PushEd.gmx/scripts/PEd_roomSaveToBBMAP2.gml
new file mode 100644
index 00000000..33f960a2
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_roomSaveToBBMAP2.gml
@@ -0,0 +1,145 @@
+/// PEd_roomSaveToBBMAP2(room, file)
+/**
+ * @brief Saves the room into the *.bbmap version 2 file.
+ * @param {real} room The id of the room.
+ * @param {string} file The name of the file to export to.
+ * @return {bool} True on success.
+ */
+var _room = argument0;
+var _path = argument1;
+var _roomName = string_replace(filename_name(_path), ".bbmap", "");
+PEd_roomSetName(_room, _roomName);
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Create buffer
+//
+var _buffer = buffer_create(1024, buffer_grow, 1); // TODO: Calculate buffer size
+buffer_seek(_buffer, buffer_seek_start, 0);
+
+// Write bbmap version
+buffer_write(_buffer, buffer_u8, 2);
+
+////////////////////////////////////////////////////////////////////////////////
+// Write room data
+buffer_write(_buffer, buffer_u32, PEd_roomGetWidth(_room));
+buffer_write(_buffer, buffer_u32, PEd_roomGetHeight(_room));
+buffer_write(_buffer, buffer_u32, PEd_roomGetSpeed(_room));
+buffer_write(_buffer, buffer_u32, PEd_roomGetColour(_room));
+buffer_write(_buffer, buffer_bool, PEd_roomGetShowColour(_room));
+buffer_write(_buffer, buffer_bool, PEd_roomGetPersistent(_room));
+buffer_write(_buffer, buffer_string, PEd_roomGetCode(_room));
+buffer_write(_buffer, buffer_string, PEd_roomGetCaption(_room));
+
+////////////////////////////////////////////////////////////////////////////////
+// Write views
+for (var i = 0; i < 8; i++)
+{
+ var _viewport = PEd_roomGetViewport(_room, i);
+ buffer_write(_buffer, buffer_bool, PEd_viewportGetVisible(_viewport));
+ buffer_write(_buffer, buffer_u32, PEd_viewportGetX(_viewport));
+ buffer_write(_buffer, buffer_u32, PEd_viewportGetY(_viewport));
+ buffer_write(_buffer, buffer_u32, PEd_viewportGetWidth(_viewport));
+ buffer_write(_buffer, buffer_u32, PEd_viewportGetHeight(_viewport));
+ buffer_write(_buffer, buffer_u32, PEd_viewportGetPortX(_viewport));
+ buffer_write(_buffer, buffer_u32, PEd_viewportGetPortY(_viewport));
+ buffer_write(_buffer, buffer_u32, PEd_viewportGetPortWidth(_viewport));
+ buffer_write(_buffer, buffer_u32, PEd_viewportGetPortHeight(_viewport));
+ buffer_write(_buffer, buffer_u32, PEd_viewportGetBorderHor(_viewport));
+ buffer_write(_buffer, buffer_u32, PEd_viewportGetBorderVer(_viewport));
+ buffer_write(_buffer, buffer_f32, PEd_viewportGetSpeedHor(_viewport));
+ buffer_write(_buffer, buffer_f32, PEd_viewportGetSpeedVer(_viewport));
+
+ var _objName = "";
+ var _obj = PEd_viewportGetObject(_viewport);
+ if (_obj >= 0)
+ {
+ _objName = object_get_name(_obj);
+ }
+ buffer_write(_buffer, buffer_string, _objName);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Write backgrounds
+for (var i = 0; i < 8; i++)
+{
+ var _background = PEd_roomGetBackground(_room, i);
+ buffer_write(_buffer, buffer_bool, PEd_backgroundGetVisible(_background));
+ buffer_write(_buffer, buffer_bool, PEd_backgroundIsForeground(_background));
+ buffer_write(_buffer, buffer_bool, PEd_backgroundGetStretch(_background));
+ buffer_write(_buffer, buffer_bool, PEd_backgroundGetTiledHor(_background));
+ buffer_write(_buffer, buffer_bool, PEd_backgroundGetTiledVer(_background));
+ buffer_write(_buffer, buffer_f32, PEd_backgroundGetX(_background));
+ buffer_write(_buffer, buffer_f32, PEd_backgroundGetY(_background));
+ buffer_write(_buffer, buffer_f32, PEd_backgroundGetSpeedHor(_background));
+ buffer_write(_buffer, buffer_f32, PEd_backgroundGetSpeedVer(_background));
+
+ var _bgName = "";
+ var _bg = PEd_backgroundGetImage(_background);
+ if (_bg >= 0)
+ {
+ _bgName = background_get_name(_bg);
+ }
+ buffer_write(_buffer, buffer_string, _bgName);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Write physics
+var _physicsWorld = PEd_roomGetPhysicsWorld(_room);
+buffer_write(_buffer, buffer_bool, PEd_physicsWorldGetEnabled(_physicsWorld));
+buffer_write(_buffer, buffer_f32, PEd_physicsWorldGetGravityX(_physicsWorld));
+buffer_write(_buffer, buffer_f32, PEd_physicsWorldGetGravityY(_physicsWorld));
+buffer_write(_buffer, buffer_f32, PEd_physicsWorldGetPxToM(_physicsWorld));
+
+////////////////////////////////////////////////////////////////////////////////
+// Write instances
+var _instances = PEd_roomGetInstances(_room);
+var _instanceCount = ds_list_size(_instances);
+
+buffer_write(_buffer, buffer_u32, _instanceCount);
+
+for (var i = 0; i < _instanceCount; i++)
+{
+ var _inst = _instances[|i];
+ PEd_instanceAutocompleteCode(_inst);
+ buffer_write(_buffer, buffer_string, PEd_instanceGetObjectName(_inst));
+ buffer_write(_buffer, buffer_string, PEd_instanceGetName(_inst));
+ buffer_write(_buffer, buffer_string, PEd_instanceGetCode(_inst));
+ buffer_write(_buffer, buffer_f32, PEd_instanceGetPosX(_inst));
+ buffer_write(_buffer, buffer_f32, PEd_instanceGetPosY(_inst));
+ buffer_write(_buffer, buffer_f32, PEd_instanceGetScaleX(_inst));
+ buffer_write(_buffer, buffer_f32, PEd_instanceGetScaleY(_inst));
+ buffer_write(_buffer, buffer_f32, PEd_instanceGetRotZ(_inst));
+ buffer_write(_buffer, buffer_u32, PEd_colourAlphaToArgb(PEd_instanceGetColour(_inst), PEd_instanceGetAlpha(_inst)));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Write tiles
+var _tiles = PEd_roomGetTiles(_room);
+var _tileCount = ds_list_size(_tiles);
+
+buffer_write(_buffer, buffer_u32, _tileCount);
+
+for (var i = 0; i < _tileCount; i++)
+{
+ var _tile = _tiles[|i];
+ buffer_write(_buffer, buffer_string, background_get_name(tile_get_background(_tile)));
+ buffer_write(_buffer, buffer_u32, tile_get_left(_tile));
+ buffer_write(_buffer, buffer_u32, tile_get_top(_tile));
+ buffer_write(_buffer, buffer_u32, tile_get_width(_tile));
+ buffer_write(_buffer, buffer_u32, tile_get_height(_tile));
+ buffer_write(_buffer, buffer_f32, tile_get_x(_tile));
+ buffer_write(_buffer, buffer_f32, tile_get_y(_tile));
+ buffer_write(_buffer, buffer_u32, tile_get_depth(_tile));
+ buffer_write(_buffer, buffer_f32, tile_get_xscale(_tile));
+ buffer_write(_buffer, buffer_f32, tile_get_yscale(_tile));
+ buffer_write(_buffer, buffer_u32, PEd_colourAlphaToArgb(tile_get_blend(_tile), tile_get_alpha(_tile)));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Save buffer
+//
+buffer_save(_buffer, _path);
+buffer_delete(_buffer);
+return true;
diff --git a/PushEd.gmx/scripts/PEd_roomSaveToGMX.gml b/PushEd.gmx/scripts/PEd_roomSaveToGMX.gml
new file mode 100644
index 00000000..a889673e
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_roomSaveToGMX.gml
@@ -0,0 +1,375 @@
+/// PEd_roomSaveToGMX(room, file)
+/**
+ * @brief Saves the room into the *.room.gmx file.
+ * @return {real} True on success.
+ */
+var _room = argument0;
+var _path = argument1;
+var _roomName = string_replace(filename_name(_path), ".room.gmx", "");
+PEd_roomSetName(_room, _roomName);
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Create XML document
+//
+var _root = PEd_xmlCreateElement("room");
+
+PEd_xmlAddChildElement(_root,
+ PEd_xmlSetElementValue(
+ PEd_xmlCreateElement("caption"),
+ PEd_roomGetCaption(_room)));
+
+PEd_xmlAddChildElement(_root,
+ PEd_xmlSetElementValue(
+ PEd_xmlCreateElement("width"),
+ PEd_roomGetWidth(_room)));
+
+PEd_xmlAddChildElement(_root,
+ PEd_xmlSetElementValue(
+ PEd_xmlCreateElement("height"),
+ PEd_roomGetHeight(_room)));
+
+PEd_xmlAddChildElement(_root,
+ PEd_xmlSetElementValue(
+ PEd_xmlCreateElement("vsnap"),
+ PEd_roomGetSnapV(_room)));
+
+PEd_xmlAddChildElement(_root,
+ PEd_xmlSetElementValue(
+ PEd_xmlCreateElement("hsnap"),
+ PEd_roomGetSnapH(_room)));
+
+PEd_xmlAddChildElement(_root,
+ PEd_xmlSetElementValue(
+ PEd_xmlCreateElement("isometric"), 0));
+
+PEd_xmlAddChildElement(_root,
+ PEd_xmlSetElementValue(
+ PEd_xmlCreateElement("speed"),
+ PEd_roomGetSpeed(_room)));
+
+PEd_xmlAddChildElement(_root,
+ PEd_xmlSetElementValue(
+ PEd_xmlCreateElement("persistent"),
+ -PEd_roomGetPersistent(_room)));
+
+PEd_xmlAddChildElement(_root,
+ PEd_xmlSetElementValue(
+ PEd_xmlCreateElement("colour"),
+ PEd_roomGetColour(_room)));
+
+PEd_xmlAddChildElement(_root,
+ PEd_xmlSetElementValue(
+ PEd_xmlCreateElement("showcolour"),
+ PEd_roomGetShowColour(_room)));
+
+PEd_xmlAddChildElement(_root,
+ PEd_xmlSetElementValue(
+ PEd_xmlCreateElement("code"),
+ PEd_roomGetCode(_room)));
+
+PEd_xmlAddChildElement(_root,
+ PEd_xmlSetElementValue(
+ PEd_xmlCreateElement("enableViews"), -1));
+
+PEd_xmlAddChildElement(_root,
+ PEd_xmlSetElementValue(
+ PEd_xmlCreateElement("clearViewBackground"), -1));
+
+PEd_xmlAddChildElement(_root,
+ PEd_xmlSetElementValue(
+ PEd_xmlCreateElement("clearDisplayBuffer"), -1));
+
+////////////////////////////////////////////////////////////////////////////////
+// Maker
+var _makerSettings = PEd_xmlCreateElement("makerSettings");
+PEd_xmlAddChildElement(_makerSettings,
+ PEd_xmlSetElementValue(
+ PEd_xmlCreateElement("isSet"), 0));
+
+PEd_xmlAddChildElement(_makerSettings,
+ PEd_xmlSetElementValue(
+ PEd_xmlCreateElement("w"), 0));
+
+PEd_xmlAddChildElement(_makerSettings,
+ PEd_xmlSetElementValue(
+ PEd_xmlCreateElement("h"), 0));
+
+PEd_xmlAddChildElement(_makerSettings,
+ PEd_xmlSetElementValue(
+ PEd_xmlCreateElement("showGrid"),
+ -PEd_roomGetGrid(_room)));
+
+PEd_xmlAddChildElement(_makerSettings,
+ PEd_xmlSetElementValue(
+ PEd_xmlCreateElement("showObjects"), 0));
+
+PEd_xmlAddChildElement(_makerSettings,
+ PEd_xmlSetElementValue(
+ PEd_xmlCreateElement("showTiles"), 0));
+
+PEd_xmlAddChildElement(_makerSettings,
+ PEd_xmlSetElementValue(
+ PEd_xmlCreateElement("showBackgrounds"), 0));
+
+PEd_xmlAddChildElement(_makerSettings,
+ PEd_xmlSetElementValue(
+ PEd_xmlCreateElement("showForegrounds"), 0));
+
+PEd_xmlAddChildElement(_makerSettings,
+ PEd_xmlSetElementValue(
+ PEd_xmlCreateElement("showViews"), 0));
+
+PEd_xmlAddChildElement(_makerSettings,
+ PEd_xmlSetElementValue(
+ PEd_xmlCreateElement("deleteUnderlyingObj"), 0));
+
+PEd_xmlAddChildElement(_makerSettings,
+ PEd_xmlSetElementValue(
+ PEd_xmlCreateElement("deleteUnderlyingTiles"), 0));
+
+PEd_xmlAddChildElement(_makerSettings,
+ PEd_xmlSetElementValue(
+ PEd_xmlCreateElement("page"), 0));
+
+PEd_xmlAddChildElement(_makerSettings,
+ PEd_xmlSetElementValue(
+ PEd_xmlCreateElement("xoffset"), 0));
+
+PEd_xmlAddChildElement(_makerSettings,
+ PEd_xmlSetElementValue(
+ PEd_xmlCreateElement("yoffset"), 0));
+
+PEd_xmlAddChildElement(_root, _makerSettings);
+
+var _backgrounds = PEd_xmlCreateElement("backgrounds");
+var _views = PEd_xmlCreateElement("views");
+
+for (var i = 0; i < 8; i++)
+{
+ ////////////////////////////////////////////////////////////////////////////
+ // Backgrounds
+ var _background = PEd_roomGetBackground(_room, i);
+ var _bgrElement = PEd_xmlCreateElement("background");
+
+ PEd_xmlSetElementAttribute(
+ _bgrElement, "visible", -PEd_backgroundGetVisible(_background));
+ PEd_xmlSetElementAttribute(
+ _bgrElement, "foreground", -PEd_backgroundIsForeground(_background));
+
+ var _bgrName = "";
+ var _index = PEd_backgroundGetImage(_background);
+ if (_index >= 0)
+ {
+ _bgrName = background_get_name(_index);
+ }
+ PEd_xmlSetElementAttribute(_bgrElement, "name", _bgrName);
+
+ PEd_xmlSetElementAttribute(
+ _bgrElement, "x", PEd_backgroundGetX(_background));
+ PEd_xmlSetElementAttribute(
+ _bgrElement, "y", PEd_backgroundGetY(_background));
+ PEd_xmlSetElementAttribute(
+ _bgrElement, "htiled", -PEd_backgroundGetTiledHor(_background));
+ PEd_xmlSetElementAttribute(
+ _bgrElement, "vtiled", -PEd_backgroundGetTiledVer(_background));
+ PEd_xmlSetElementAttribute(
+ _bgrElement, "hspeed", PEd_backgroundGetSpeedHor(_background));
+ PEd_xmlSetElementAttribute(
+ _bgrElement, "vspeed", PEd_backgroundGetSpeedVer(_background));
+ PEd_xmlSetElementAttribute(
+ _bgrElement, "stretch", -PEd_backgroundGetStretch(_background));
+
+ PEd_xmlAddChildElement(_backgrounds, _bgrElement);
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Views
+ var _viewport = PEd_roomGetViewport(_room, i);
+ var _viewElem = PEd_xmlCreateElement("view");
+
+ PEd_xmlSetElementAttribute(
+ _viewElem, "visible", -PEd_viewportGetVisible(_viewport));
+
+ var _objName = "";
+ var _index = PEd_viewportGetObject(_viewport);
+ if (_index >= 0)
+ {
+ _objName = object_get_name(_index);
+ }
+ PEd_xmlSetElementAttribute(_viewElem, "objName", _objName);
+
+ PEd_xmlSetElementAttribute(
+ _viewElem, "xview", PEd_viewportGetX(_viewport));
+ PEd_xmlSetElementAttribute(
+ _viewElem, "yview", PEd_viewportGetY(_viewport));
+ PEd_xmlSetElementAttribute(
+ _viewElem, "wview", PEd_viewportGetWidth(_viewport));
+ PEd_xmlSetElementAttribute(
+ _viewElem, "hview", PEd_viewportGetHeight(_viewport));
+ PEd_xmlSetElementAttribute(
+ _viewElem, "xport", PEd_viewportGetPortX(_viewport));
+ PEd_xmlSetElementAttribute(
+ _viewElem, "yport", PEd_viewportGetPortY(_viewport));
+ PEd_xmlSetElementAttribute(
+ _viewElem, "wport", PEd_viewportGetPortWidth(_viewport));
+ PEd_xmlSetElementAttribute(
+ _viewElem, "hport", PEd_viewportGetPortHeight(_viewport));
+ PEd_xmlSetElementAttribute(
+ _viewElem, "hborder", PEd_viewportGetBorderHor(_viewport));
+ PEd_xmlSetElementAttribute(
+ _viewElem, "vborder", PEd_viewportGetBorderVer(_viewport));
+ PEd_xmlSetElementAttribute(
+ _viewElem, "hspeed", PEd_viewportGetSpeedHor(_viewport));
+ PEd_xmlSetElementAttribute(
+ _viewElem, "vspeed", PEd_viewportGetSpeedVer(_viewport));
+
+ PEd_xmlAddChildElement(_views, _viewElem);
+}
+
+PEd_xmlAddChildElement(_root, _backgrounds);
+PEd_xmlAddChildElement(_root, _views);
+
+////////////////////////////////////////////////////////////////////////////////
+// Instances
+var _instances = PEd_xmlCreateElement("instances");
+var _instList = PEd_roomGetInstances(_room);
+var _instCount = ds_list_size(_instList);
+
+for (var i = 0; i < _instCount; i++)
+{
+ var _inst = _instList[| i];
+ PEd_instanceAutocompleteCode(_inst);
+
+ var _instElem = PEd_xmlCreateElement("instance");
+
+ PEd_xmlSetElementAttribute(
+ _instElem, "objName", PEd_instanceGetObjectName(_inst));
+ PEd_xmlSetElementAttribute(
+ _instElem, "x", PEd_instanceGetPosX(_inst));
+ PEd_xmlSetElementAttribute(
+ _instElem, "y", PEd_instanceGetPosY(_inst));
+ PEd_xmlSetElementAttribute(
+ _instElem, "name", PEd_instanceGetName(_inst));
+ PEd_xmlSetElementAttribute(
+ _instElem, "locked", 0);
+ PEd_xmlSetElementAttribute(
+ _instElem, "code", PEd_instanceGetCode(_inst));
+ PEd_xmlSetElementAttribute(
+ _instElem, "scaleX", PEd_instanceGetScaleX(_inst));
+ PEd_xmlSetElementAttribute(
+ _instElem, "scaleY", PEd_instanceGetScaleY(_inst));
+ PEd_xmlSetElementAttribute(
+ _instElem, "colour",
+ PEd_colourAlphaToArgb(PEd_instanceGetColour(_inst),
+ PEd_instanceGetAlpha(_inst)));
+ PEd_xmlSetElementAttribute(
+ _instElem, "rotation", PEd_instanceGetRotZ(_inst));
+
+ PEd_xmlAddChildElement(_instances, _instElem);
+}
+
+PEd_xmlAddChildElement(_root, _instances);
+
+////////////////////////////////////////////////////////////////////////////////
+// Tiles
+var _tiles = PEd_xmlCreateElement("tiles");
+var _tileList = PEd_roomGetTiles(_room);
+var _tileCount = ds_list_size(_tileList);
+
+for (var i = 0; i < _tileCount; i++)
+{
+ var _tile = _tileList[| i];
+ var _tileElem = PEd_xmlCreateElement("tile");
+
+ PEd_xmlSetElementAttribute(
+ _tileElem, "bgName", background_get_name(tile_get_background(_tile)));
+ PEd_xmlSetElementAttribute(
+ _tileElem, "x", tile_get_x(_tile));
+ PEd_xmlSetElementAttribute(
+ _tileElem, "y", tile_get_y(_tile));
+ PEd_xmlSetElementAttribute(
+ _tileElem, "w", tile_get_width(_tile));
+ PEd_xmlSetElementAttribute(
+ _tileElem, "h", tile_get_height(_tile));
+ PEd_xmlSetElementAttribute(
+ _tileElem, "xo", tile_get_left(_tile));
+ PEd_xmlSetElementAttribute(
+ _tileElem, "yo", tile_get_top(_tile));
+ PEd_xmlSetElementAttribute(
+ _tileElem, "id", string(_tile));
+ PEd_xmlSetElementAttribute(
+ _tileElem, "name", "tile_" + string(_tile));
+ PEd_xmlSetElementAttribute(
+ _tileElem, "depth", tile_get_depth(_tile));
+ PEd_xmlSetElementAttribute(
+ _tileElem, "locked", 0);;
+ PEd_xmlSetElementAttribute(
+ _tileElem, "colour",
+ PEd_colourAlphaToArgb(tile_get_blend(_tile), tile_get_alpha(_tile)));
+ PEd_xmlSetElementAttribute(
+ _tileElem, "scaleX", tile_get_xscale(_tile));
+ PEd_xmlSetElementAttribute(
+ _tileElem, "scaleY", tile_get_yscale(_tile))
+
+ PEd_xmlAddChildElement(_tiles, _tileElem);
+}
+
+PEd_xmlAddChildElement(_root, _tiles);
+
+////////////////////////////////////////////////////////////////////////////////
+// Physics
+var _physicsWorld = PEd_roomGetPhysicsWorld(_room);
+
+PEd_xmlAddChildElement(_root,
+ PEd_xmlSetElementValue(
+ PEd_xmlCreateElement("PhysicsWorld"),
+ -PEd_physicsWorldGetEnabled(_physicsWorld)));
+
+PEd_xmlAddChildElement(_root,
+ PEd_xmlSetElementValue(
+ PEd_xmlCreateElement("PhysicsWorldTop"), 0));
+
+PEd_xmlAddChildElement(_root,
+ PEd_xmlSetElementValue(
+ PEd_xmlCreateElement("PhysicsWorldLeft"), 0));
+
+PEd_xmlAddChildElement(_root,
+ PEd_xmlSetElementValue(
+ PEd_xmlCreateElement("PhysicsWorldRight"),
+ PEd_roomGetWidth(_room)));
+
+PEd_xmlAddChildElement(_root,
+ PEd_xmlSetElementValue(
+ PEd_xmlCreateElement("PhysicsWorldBottom"),
+ PEd_roomGetHeight(_room)));
+
+PEd_xmlAddChildElement(_root,
+ PEd_xmlSetElementValue(
+ PEd_xmlCreateElement("PhysicsWorldGravityX"),
+ PEd_physicsWorldGetGravityX(_physicsWorld)));
+
+PEd_xmlAddChildElement(_root,
+ PEd_xmlSetElementValue(
+ PEd_xmlCreateElement("PhysicsWorldGravityY"),
+ PEd_physicsWorldGetGravityY(_physicsWorld)));
+
+PEd_xmlAddChildElement(_root,
+ PEd_xmlSetElementValue(
+ PEd_xmlCreateElement("PhysicsWorldPixToMeters"),
+ PEd_physicsWorldGetPxToM(_physicsWorld)));
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Write xml document
+//
+var _string = PEd_xmlWrite(_root);
+PEd_xmlDestroyElement(_root);
+var _file = file_text_open_write(_path);
+if (_file != -1)
+{
+ file_text_write_string(_file, _string);
+ file_text_close(_file);
+ return true;
+}
+
+return false;
diff --git a/PushEd.gmx/scripts/PEd_roomSaveToPEd.gml b/PushEd.gmx/scripts/PEd_roomSaveToPEd.gml
new file mode 100644
index 00000000..8d78101f
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_roomSaveToPEd.gml
@@ -0,0 +1,74 @@
+/// PEd_roomSaveToPEd(room, file)
+/**
+ * @brief Saves the room into the *.ped file.
+ * @param {real} room The id of the room.
+ * @param {string} file The name of the file to export to.
+ * @return {bool} True on success.
+ */
+var _room = argument0;
+var _path = argument1;
+var _roomName = string_replace(filename_name(_path), ".ped", "");
+PEd_roomSetName(_room, _roomName);
+
+var _roomSave = PEd_roomCopy(_room);
+
+////////////////////////////////////////////////////////////////////////////////
+// Write camera and editor settings
+_roomSave[? "using3D"] = global.pedUsing3D;
+_roomSave[? "editMode"] = PEd_oEditor.editMode;
+_roomSave[? "editTool"] = PEd_oEditor.editTool;
+_roomSave[? "editFloor"] = PEd_oEditor.editFloor;
+_roomSave[? "cameraX"] = PEd_oEditor.x;
+_roomSave[? "cameraY"] = PEd_oEditor.y;
+_roomSave[? "cameraZ"] = PEd_oEditor.z;
+_roomSave[? "cameraDir"] = PEd_oEditor.direction;
+_roomSave[? "cameraPitch"] = PEd_oEditor.camPitch;
+_roomSave[? "viewX"] = view_xview[0];
+_roomSave[? "viewY"] = view_yview[0];
+_roomSave[? "viewZoom"] = PEd_oEditor.viewZoom;
+_roomSave[? "pivotX"] = PEd_oPivot.x;
+_roomSave[? "pivotY"] = PEd_oPivot.y;
+_roomSave[? "pivotZ"] = PEd_oPivot.z;
+
+////////////////////////////////////////////////////////////////////////////////
+// Write instances
+var _instances = PEd_roomGetInstances(_room);
+var _instanceCount = ds_list_size(_instances);
+var _instancesSave = PEd_roomGetInstances(_roomSave);
+ds_list_clear(_instancesSave);
+
+for (var i = 0; i < _instanceCount; i++)
+{
+ PEd_dsListAddMap(_instancesSave, PEd_createSaveInstance(_instances[| i]));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Write tiles
+var _tiles = PEd_roomGetTiles(_room);
+var _tileCount = ds_list_size(_tiles);
+var _tilesSave = PEd_roomGetTiles(_roomSave);
+ds_list_clear(_tilesSave);
+
+for (var i = 0; i < _tileCount; i++)
+{
+ PEd_dsListAddMap(_tilesSave, PEd_createSaveTile(_tiles[| i]));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Write to file
+if (file_exists(_path))
+{
+ file_copy(_path, "Backups\" + _roomName + "_backup.ped");
+}
+
+var _file = file_text_open_write(_path);
+if (_file != -1)
+{
+ file_text_write_string(_file, json_encode(_roomSave));
+ file_text_close(_file);
+ ds_map_destroy(_roomSave);
+ return true;
+}
+
+ds_map_destroy(_roomSave);
+return false;
diff --git a/PushEd.gmx/scripts/PEd_roomSetCaption.gml b/PushEd.gmx/scripts/PEd_roomSetCaption.gml
new file mode 100644
index 00000000..ac68bf75
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_roomSetCaption.gml
@@ -0,0 +1,7 @@
+/// PEd_roomSetCaption(room, str)
+/**
+ * @brief Sets the caption of the room.
+ * @param {real} room The id of the room.
+ * @param {string} str The new caption of the room.
+ */
+argument0[? "caption"] = argument1;
diff --git a/PushEd.gmx/scripts/PEd_roomSetCode.gml b/PushEd.gmx/scripts/PEd_roomSetCode.gml
new file mode 100644
index 00000000..56745d53
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_roomSetCode.gml
@@ -0,0 +1,7 @@
+/// PEd_roomSetCode(room, str)
+/**
+ * @brief Sets the creation code of the room.
+ * @param {real} room The id of the room.
+ * @param {string} str The new creation code of the room.
+ */
+argument0[? "code"] = argument1;
diff --git a/PushEd.gmx/scripts/PEd_roomSetColour.gml b/PushEd.gmx/scripts/PEd_roomSetColour.gml
new file mode 100644
index 00000000..eacb17c4
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_roomSetColour.gml
@@ -0,0 +1,7 @@
+/// PEd_roomSetColour(room, col)
+/**
+ * @brief Sets the colour of the room.
+ * @param {real} room The id of the room.
+ * @param {real} col The new colour of the room.
+ */
+argument0[? "colour"] = argument1;
diff --git a/PushEd.gmx/scripts/PEd_roomSetColourARGB.gml b/PushEd.gmx/scripts/PEd_roomSetColourARGB.gml
new file mode 100644
index 00000000..ace9a2a6
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_roomSetColourARGB.gml
@@ -0,0 +1,7 @@
+/// PEd_roomSetColourARGB(room, col)
+/**
+ * @brief Sets the ARGB colour of the room.
+ * @param {real} room The id of the room.
+ * @param {real} col The new ARGB colour of the room.
+ */
+argument0[? "colour"] = PEd_argbToColour(argument1);
diff --git a/PushEd.gmx/scripts/PEd_roomSetGrid.gml b/PushEd.gmx/scripts/PEd_roomSetGrid.gml
new file mode 100644
index 00000000..836e4ac1
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_roomSetGrid.gml
@@ -0,0 +1,7 @@
+/// PEd_roomSetGrid(room, enabled)
+/**
+ * @brief Enables/disables the grid in the room.
+ * @param {real} room The id of the room.
+ * @param {bool} enabled True to enable the grid.
+ */
+argument0[? "grid"] = argument1;
diff --git a/PushEd.gmx/scripts/PEd_roomSetHeight.gml b/PushEd.gmx/scripts/PEd_roomSetHeight.gml
new file mode 100644
index 00000000..80726691
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_roomSetHeight.gml
@@ -0,0 +1,7 @@
+/// PEd_roomSetHeight(room, val)
+/**
+ * @brief Sets the height of the room.
+ * @param {real} room The id of the room.
+ * @param {real} val The new height of the room.
+ */
+argument0[? "height"] = argument1;
diff --git a/PushEd.gmx/scripts/PEd_roomSetName.gml b/PushEd.gmx/scripts/PEd_roomSetName.gml
new file mode 100644
index 00000000..caa20155
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_roomSetName.gml
@@ -0,0 +1,7 @@
+/// PEd_roomSetName(room, name)
+/**
+ * @brief Sets name of the room.
+ * @param {real} room The id of the room.
+ * @param {string} name The new name of the room.
+ */
+argument0[? "name"] = argument1;
diff --git a/PushEd.gmx/scripts/PEd_roomSetPersistent.gml b/PushEd.gmx/scripts/PEd_roomSetPersistent.gml
new file mode 100644
index 00000000..ba8cc333
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_roomSetPersistent.gml
@@ -0,0 +1,7 @@
+/// PEd_roomSetPersistent(room, enabled)
+/**
+ * @brief Sets the persistence of the room.
+ * @param {real} room The id of the room.
+ * @param {bool} enabled True to make the room persistent.
+ */
+argument0[? "persistent"] = argument1;
diff --git a/PushEd.gmx/scripts/PEd_roomSetShowColour.gml b/PushEd.gmx/scripts/PEd_roomSetShowColour.gml
new file mode 100644
index 00000000..35138f9f
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_roomSetShowColour.gml
@@ -0,0 +1,7 @@
+/// PEd_roomSetShowColour(room, show)
+/**
+ * @brief Sets the show colour state of the room.
+ * @param {real} room The id of the room.
+ * @param {bool} show True to show the room background colour.
+ */
+argument0[? "showColour"] = argument1;
diff --git a/PushEd.gmx/scripts/PEd_roomSetSnapD.gml b/PushEd.gmx/scripts/PEd_roomSetSnapD.gml
new file mode 100644
index 00000000..f8450766
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_roomSetSnapD.gml
@@ -0,0 +1,7 @@
+/// PEd_roomSetSnapD(room, val)
+/**
+ * @brief Sets the depth snap size of the room.
+ * @param {real} room The id of the room.
+ * @param {real} val The new depth snap size of the room.
+ */
+argument0[? "dsnap"] = argument1;
diff --git a/PushEd.gmx/scripts/PEd_roomSetSnapH.gml b/PushEd.gmx/scripts/PEd_roomSetSnapH.gml
new file mode 100644
index 00000000..2b29b3ef
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_roomSetSnapH.gml
@@ -0,0 +1,7 @@
+/// PEd_roomSetSnapH(room, val)
+/**
+ * @brief Sets the horizontal snap size of the room.
+ * @param {real} room The id of the room.
+ * @param {real} val The new horizontal snap size of the room.
+ */
+argument0[? "hsnap"] = argument1;
diff --git a/PushEd.gmx/scripts/PEd_roomSetSnapV.gml b/PushEd.gmx/scripts/PEd_roomSetSnapV.gml
new file mode 100644
index 00000000..aa04fc75
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_roomSetSnapV.gml
@@ -0,0 +1,7 @@
+/// PEd_roomSetSnapV(room, val)
+/**
+ * @brief Sets the vertical snap size of the room.
+ * @param {real} room The id of the room.
+ * @param {real} val The new vertical snap size of the room.
+ */
+argument0[? "vsnap"] = argument1;
diff --git a/PushEd.gmx/scripts/PEd_roomSetSpeed.gml b/PushEd.gmx/scripts/PEd_roomSetSpeed.gml
new file mode 100644
index 00000000..556264f8
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_roomSetSpeed.gml
@@ -0,0 +1,7 @@
+/// PEd_roomSetSpeed(room, val)
+/**
+ * @brief Sets the speed of the room.
+ * @param {real} room The id of the room.
+ * @param {real} val The new speed of the room.
+ */
+argument0[? "speed"] = argument1;
diff --git a/PushEd.gmx/scripts/PEd_roomSetWidth.gml b/PushEd.gmx/scripts/PEd_roomSetWidth.gml
new file mode 100644
index 00000000..c3374dc9
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_roomSetWidth.gml
@@ -0,0 +1,7 @@
+/// PEd_roomSetWidth(room, val)
+/**
+ * @brief Sets the width of the room.
+ * @param {real} room The id of the room.
+ * @param {real} val The new width of the room.
+ */
+argument0[? "width"] = argument1;
diff --git a/PushEd.gmx/scripts/PEd_savePivotOffsets.gml b/PushEd.gmx/scripts/PEd_savePivotOffsets.gml
new file mode 100644
index 00000000..0f6261b2
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_savePivotOffsets.gml
@@ -0,0 +1,82 @@
+/// PEd_savePivotOffsets()
+/**
+ * @brief Stores pivot offsets from all selected objects.
+ */
+var _size = ds_list_size(selectedObjects);
+if (_size > 0)
+{
+ if (PEd_getSelectedObject() <= 0)
+ {
+ return 0;
+ }
+
+ ds_list_clear(selectedObjectsData);
+ PEd_oPivot.x = 0;
+ PEd_oPivot.y = 0;
+ PEd_oPivot.z = 0;
+
+ switch (editMode)
+ {
+ ////////////////////////////////////////////////////////////////////////////
+ // Objects
+ case 0:
+ // Acumulate pivot position
+ for (var i = 0; i < _size; i++)
+ {
+ var _inst = selectedObjects[| i];
+ PEd_oPivot.x += PEd_instanceGetPosX(_inst);
+ PEd_oPivot.y += PEd_instanceGetPosY(_inst);
+ if (global.pedUsing3D)
+ {
+ PEd_oPivot.z += PEd_instanceGetPosZ(_inst);
+ }
+ }
+
+ // Center pivot position
+ PEd_oPivot.x /= _size;
+ PEd_oPivot.y /= _size;
+ PEd_oPivot.z /= _size;
+
+ // Save offsets
+ for (var i = 0; i < _size; i++)
+ {
+ var _inst = selectedObjects[| i];
+ var _offsetZ = 0;
+ if (global.pedUsing3D)
+ {
+ _offsetZ = PEd_instanceGetPosZ(_inst) - PEd_oPivot.z;
+ }
+ ds_list_add(selectedObjectsData,
+ PEd_vec3(PEd_instanceGetPosX(_inst) - PEd_oPivot.x,
+ PEd_instanceGetPosY(_inst) - PEd_oPivot.y,
+ _offsetZ));
+ }
+ break;
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Tiles
+ case 1:
+ // Acumulate pivot position
+ for (var i = 0; i < _size; i++)
+ {
+ var _inst = selectedObjects[| i];
+ PEd_oPivot.x += tile_get_x(_inst);
+ PEd_oPivot.y += tile_get_y(_inst);
+ }
+
+ // Center pivot position
+ PEd_oPivot.x /= _size;
+ PEd_oPivot.y /= _size;
+
+ // Save offsets
+ for (var i = 0; i < _size; i++)
+ {
+ var _inst = selectedObjects[| i];
+ var _offset = PEd_vec3(tile_get_x(_inst) - PEd_oPivot.x,
+ tile_get_y(_inst) - PEd_oPivot.y,
+ 0);
+ ds_list_add(selectedObjectsData, _offset);
+ }
+ break;
+ }
+}
diff --git a/PushEd.gmx/scripts/PEd_selectObject.gml b/PushEd.gmx/scripts/PEd_selectObject.gml
new file mode 100644
index 00000000..96df002c
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_selectObject.gml
@@ -0,0 +1,126 @@
+/// PEd_selectObject(id, [forceSingleSelect], [keepMultipleSelection])
+/**
+ * @brief Handles (multi)selecting and instances and tiles.
+ * @param {real} id The id of the instance or tile to select/add to selection/deselect.
+ * @param {bool} [forceSingleSelect] True to force selecting single object.
+ * @param {bool} [keepMultipleSelection] If true then keeps multiple selection.
+ */
+var _id = argument[0];
+var _key = vk_control;
+
+var _forceSingleSelect = false;
+if (argument_count > 1)
+{
+ _forceSingleSelect = argument[1];
+}
+
+if (editMode == PEdEditModes.Tile)
+{
+ _key = vk_shift;
+}
+
+var _keepMultipleSelection = false;
+if (argument_count > 2)
+{
+ _keepMultipleSelection = argument[2];
+}
+
+// FIXME
+if (_keepMultipleSelection)
+{
+ if (_id != 0
+ && ds_list_find_index(selectedObjects, _id) != -1)
+ {
+ exit;
+ }
+ else
+ {
+ if (keyboard_check(_key))
+ {
+ // Multiple selection
+ if (_id != 0)
+ {
+ var _pos = PEd_dsListInsertUnique(selectedObjects, _id, 0);
+ if (_pos != -1)
+ {
+ ds_list_delete(selectedObjects, _pos);
+ }
+ }
+ }
+ else
+ {
+ // Select single
+ ds_list_clear(selectedObjects);
+ if (_id != 0)
+ {
+ ds_list_insert(selectedObjects, 0, _id);
+ }
+ }
+ }
+}
+else if (!_forceSingleSelect
+ && keyboard_check(_key))
+{
+ // Multiple selection
+ if (_id != 0)
+ {
+ var _pos = PEd_dsListInsertUnique(selectedObjects, _id, 0);
+ if (_pos != -1)
+ {
+ ds_list_delete(selectedObjects, _pos);
+ }
+ }
+}
+else
+{
+ // Select single
+ ds_list_clear(selectedObjects);
+ if (_id != 0)
+ {
+ ds_list_insert(selectedObjects, 0, _id);
+ }
+}
+
+// Debug
+/*var _txt = "";
+for (var i = 0; i < ds_list_size(selectedObjects); i++)
+{
+ _txt += string(selectedObjects[| i]) + ", ";
+}
+show_debug_message(_txt);*/
+
+PEd_savePivotOffsets();
+
+// Load custom data
+ds_list_clear(customData);
+var _selectedObj = PEd_getSelectedObject();
+if (_selectedObj > 0)
+{
+ switch (editMode)
+ {
+ case PEdEditModes.Object:
+ PEd_getCustomData(_selectedObj);
+ break;
+
+ case PEdEditModes.Tile:
+ PEd_getTileCustomData();
+ break;
+ }
+}
+else if (_selectedObj < 0)
+{
+ PEd_getRoomCustomData();
+}
+
+// Close colour pickers
+if (PEd_guiShapeExists(guiColourPickerObject))
+{
+ PEd_guiDestroyShape(guiColourPickerObject);
+ guiColourPickerObject = noone;
+}
+
+if (PEd_guiShapeExists(guiColourPickerRoom))
+{
+ PEd_guiDestroyShape(guiColourPickerRoom);
+ guiColourPickerRoom = noone;
+}
diff --git a/PushEd.gmx/scripts/PEd_start3D.gml b/PushEd.gmx/scripts/PEd_start3D.gml
new file mode 100644
index 00000000..6347bd20
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_start3D.gml
@@ -0,0 +1,12 @@
+/// PEd_start3D()
+/**
+ * @brief Starts 3D mode.
+ */
+viewLastX = view_xview[0];
+viewLastY = view_yview[0];
+global.pedUsing3D = true;
+d3d_start();
+d3d_set_culling(true);
+d3d_set_hidden(true);
+d3d_set_lighting(global.pedLighting);
+draw_set_color(c_white);
diff --git a/PushEd.gmx/scripts/PEd_stringSplitOnFirst.gml b/PushEd.gmx/scripts/PEd_stringSplitOnFirst.gml
new file mode 100644
index 00000000..3721407b
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_stringSplitOnFirst.gml
@@ -0,0 +1,22 @@
+/// PEd_stringSplitOnFirst(string, delimiter)
+/**
+ * @brief Splits the string in two at the first occurence of the delimiter.
+ * @param {string} string The string to split.
+ * @param {string} delimiter The delimiter.
+ * @return {array} An array containing [firstHalf, secondHalf]. If the delimiter
+ * is not found in the string, then firstHalf equals empty string
+ * and secondHalf is the original string.
+ */
+var r;
+var i = string_pos(argument1, argument0);
+if (i == 0)
+{
+ r[1] = "";
+ r[0] = argument0;
+}
+else
+{
+ r[1] = string_delete(argument0, 1, i);
+ r[0] = string_copy(argument0, 1, i - 1);
+}
+return r;
diff --git a/PushEd.gmx/scripts/PEd_switchMode.gml b/PushEd.gmx/scripts/PEd_switchMode.gml
new file mode 100644
index 00000000..0553b86f
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_switchMode.gml
@@ -0,0 +1,18 @@
+/// PEd_switchMode()
+/**
+ * @brief Switches between 2D/3D mode.
+ * @note This cannot be executed in the draw event!
+ */
+global.pedUsing3D = !global.pedUsing3D;
+if (global.pedUsing3D)
+{
+ PEd_start3D();
+}
+else
+{
+ PEd_end3D();
+}
+PEd_guiRequestRedrawAll(guiRoot)
+editMode = 0;
+ds_list_clear(selectedObjects);
+ds_list_clear(selectedObjectsData);
diff --git a/PushEd.gmx/scripts/PEd_tileCopy.gml b/PushEd.gmx/scripts/PEd_tileCopy.gml
new file mode 100644
index 00000000..f85a685d
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_tileCopy.gml
@@ -0,0 +1,21 @@
+/// PEd_tileCopy(room, tile)
+/**
+ * @brief Creates a copy of the tile and adds it to the room.
+ * @param {real} toom The id of the room to add the tile to.
+ * @param {real} tile The id of the tile.
+ * @return {real} The created copy.
+ */
+var _t = PEd_createTile(argument0,
+ tile_get_background(argument1),
+ tile_get_left(argument1),
+ tile_get_top(argument1),
+ tile_get_width(argument1),
+ tile_get_height(argument1),
+ tile_get_x(argument1),
+ tile_get_y(argument1),
+ tile_get_depth(argument1));
+tile_set_blend(_t, tile_get_blend(argument1));
+tile_set_alpha(_t, tile_get_alpha(argument1));
+tile_set_scale(_t, tile_get_xscale(argument1),
+ tile_get_yscale(argument1));
+return _t;
diff --git a/PushEd.gmx/scripts/PEd_tileDelete.gml b/PushEd.gmx/scripts/PEd_tileDelete.gml
new file mode 100644
index 00000000..0ce13917
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_tileDelete.gml
@@ -0,0 +1,19 @@
+/// PEd_tileDelete(tile)
+/**
+ * @brief Delets the tile and removes it from the room that it is in.
+ * @param {real} tile The id of the tile.
+ */
+for (var i = ds_list_size(pedRoomList) - 1; i >= 0; i--)
+{
+ var _tiles = PEd_roomGetTiles(pedRoomList[| i]);
+ var _pos = ds_list_find_index(_tiles, argument0);
+ if (_pos != -1)
+ {
+ ds_list_delete(_tiles, _pos);
+ break;
+ }
+}
+if (tile_exists(argument0))
+{
+ tile_delete(argument0);
+}
diff --git a/PushEd.gmx/scripts/PEd_tileEdit.gml b/PushEd.gmx/scripts/PEd_tileEdit.gml
new file mode 100644
index 00000000..b789e6dd
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_tileEdit.gml
@@ -0,0 +1,135 @@
+/// PEd_tileEdit()
+/**
+ * @brief Handles _tile editing.
+ * @note Use only in 2D mode!
+ */
+if (!mouseInViewport
+ || editMode != PEdEditModes.Tile)
+{
+ return 0;
+}
+
+var _mouseX = windowMouseX;
+var _mouseY = windowMouseY;
+var _room = PEd_getCurrentRoom();
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Tile creating
+//
+
+// Drop tile
+if (keyboard_check(vk_control)
+ && mouse_check_button(mb_left))
+{
+ // Create _tile
+ if (mouse_check_button_pressed(mb_left)
+ || (keyboard_check(vk_shift)
+ && mouseLastX != _mouseX
+ && mouseLastY != _mouseY))
+ {
+ var _posX, _posY;
+
+ if (PEd_roomGetGrid(_room)
+ && !keyboard_check(vk_alt))
+ {
+ // Snap to grid
+ _posX = floor(((_mouseX - viewportX) * viewZoom + view_xview[0]) / PEd_roomGetSnapH(_room)) * PEd_roomGetSnapH(_room);
+ _posY = floor(((_mouseY - viewportY) * viewZoom + view_yview[0]) / PEd_roomGetSnapV(_room)) * PEd_roomGetSnapV(_room);
+ }
+ else
+ {
+ // No snapping
+ _posX = (_mouseX - viewportX) * viewZoom + view_xview[0];
+ _posY = (_mouseY - viewportY) * viewZoom + view_yview[0];
+ }
+
+ // Delete underlying
+ if (tileDeleteUnderlying)
+ {
+ var _tile = tile_layer_find(tileDepth, _posX, _posY);
+ if (_tile != - 1)
+ {
+ PEd_tileDelete(_tile);
+ }
+ }
+
+ // Add new tile
+ var _tile = PEd_createTile(_room, tileBground, tilePartX, tilePartY, tilePartW, tilePartH, _posX, _posY, tileDepth);
+
+ // Set _tile colour and alpha
+ tile_set_blend(_tile, tileColour);
+ tile_set_alpha(_tile, tileAlpha);
+
+ // Add new depth layer if it does not exist
+ if (ds_list_find_index(tileLayers, tileDepth) == - 1)
+ {
+ ds_list_add(tileLayers, tileDepth);
+ ds_list_add(tileVisible, true);
+ tileLayerSelected = ds_list_size(tileLayers) - 1;
+ PEd_guiRequestRedrawAll(guiRoot)
+ }
+
+ tile_set_visible(_tile, ds_list_find_value(tileVisible, ds_list_find_index(tileLayers, tileDepth)));
+
+ // Save tile id
+ if (!keyboard_check(vk_shift))
+ {
+ pedDnDInstance = _tile;
+ }
+ }
+}
+
+if (!tile_exists(pedDnDInstance))
+{
+ pedDnDInstance = noone;
+}
+
+// Move dropped tile
+if (pedDnDInstance != noone)
+{
+ if (mouse_check_button(mb_left))
+ {
+ if (PEd_roomGetGrid(_room)
+ && !keyboard_check(vk_alt))
+ {
+ // Snap to grid
+ tile_set_position(pedDnDInstance,
+ floor(((_mouseX - viewportX) * viewZoom + view_xview[0]) / PEd_roomGetSnapH(_room)) * PEd_roomGetSnapH(_room),
+ floor(((_mouseY - viewportY) * viewZoom + view_yview[0]) / PEd_roomGetSnapV(_room)) * PEd_roomGetSnapV(_room));
+ }
+ else
+ {
+ // No snapping
+ tile_set_position(pedDnDInstance,
+ floor((_mouseX - viewportX) * viewZoom + view_xview[0]),
+ floor((_mouseY - viewportY) * viewZoom + view_yview[0]));
+ }
+ }
+ else
+ {
+ PEd_selectObject(pedDnDInstance, true);
+ PEd_oPivot.x = tile_get_x(pedDnDInstance);
+ PEd_oPivot.y = tile_get_y(pedDnDInstance);
+ pedDnDInstance = noone;
+ }
+}
+else
+{
+ // Tile deleting
+ if (keyboard_check(vk_control)
+ && mouse_check_button(mb_right)
+ && !keyboard_check(vk_alt))
+ {
+ var _tile = tile_layer_find(tileDepth,
+ (_mouseX - viewportX) * viewZoom
+ + view_xview[0], (_mouseY - viewportY) * viewZoom + view_yview[0]);
+
+ if (_tile != - 1)
+ {
+ PEd_tileDelete(_tile);
+ PEd_actClearSelection();
+ PEd_guiRequestRedrawAll(guiRoot)
+ }
+ }
+}
diff --git a/PushEd.gmx/scripts/PEd_tileGetAlpha.gml b/PushEd.gmx/scripts/PEd_tileGetAlpha.gml
new file mode 100644
index 00000000..46579a73
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_tileGetAlpha.gml
@@ -0,0 +1,8 @@
+/// PEd_tileGetAlpha(tile)
+/**
+ * @brief Gets the alpha of the tile.
+ * @param {real} Tile the id of the tile.
+ * @return {real} The alpha of the tile.
+ */
+gml_pragma("forceinline");
+return tile_get_alpha(argument0);
diff --git a/PushEd.gmx/scripts/PEd_tileGetBlend.gml b/PushEd.gmx/scripts/PEd_tileGetBlend.gml
new file mode 100644
index 00000000..d2e9927d
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_tileGetBlend.gml
@@ -0,0 +1,8 @@
+/// PEd_tileGetBlend(tile)
+/**
+ * @brief Gets the blend color of the tile.
+ * @param {real} tile The id of the tile.
+ * @return {real} The blend color of the tile.
+ */
+gml_pragma("forceinline");
+return tile_get_blend(argument0);
diff --git a/PushEd.gmx/scripts/PEd_tileGetColourARGB.gml b/PushEd.gmx/scripts/PEd_tileGetColourARGB.gml
new file mode 100644
index 00000000..0f5049a1
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_tileGetColourARGB.gml
@@ -0,0 +1,9 @@
+/// PEd_tileGetColourARGB(tile)
+/**
+ * @brief Gets the ARGB colour of the tile.
+ * @param {real} tile The id of the tile.
+ * @return {real} The ARGB colour of the tile.
+ */
+gml_pragma("forceinline");
+return PEd_colourAlphaToArgb(tile_get_blend(argument0),
+ tile_get_alpha(argument0));
diff --git a/PushEd.gmx/scripts/PEd_tileGetDepth.gml b/PushEd.gmx/scripts/PEd_tileGetDepth.gml
new file mode 100644
index 00000000..1e115b35
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_tileGetDepth.gml
@@ -0,0 +1,8 @@
+/// PEd_tileGetDepth(tile)
+/**
+ * @brief Gets the depth of the tile.
+ * @param {real} tile The id of the tile.
+ * @return {real} The depth of the tile.
+ */
+gml_pragma("forceinline");
+return tile_get_depth(argument0);
diff --git a/PushEd.gmx/scripts/PEd_tileGetPosVec2.gml b/PushEd.gmx/scripts/PEd_tileGetPosVec2.gml
new file mode 100644
index 00000000..f109f6f2
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_tileGetPosVec2.gml
@@ -0,0 +1,8 @@
+/// PEd_tileGetPosVec2(tile)
+/**
+ * @brief Gets the position of the tile as vec2.
+ * @param {real} tile The id of the tile.
+ * @return {vec2} The position of the tile.
+ */
+gml_pragma("forceinline");
+return PEd_vec2(tile_get_x(argument0), tile_get_y(argument0));
diff --git a/PushEd.gmx/scripts/PEd_tileGetPosX.gml b/PushEd.gmx/scripts/PEd_tileGetPosX.gml
new file mode 100644
index 00000000..6586c1f6
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_tileGetPosX.gml
@@ -0,0 +1,8 @@
+/// PEd_tileGetPosX(tile)
+/**
+ * @brief Gets the x position of the tile.
+ * @param {real} tile The id of the tile.
+ * @return {real} The x position of the tile.
+ */
+gml_pragma("forceinline");
+return tile_get_x(argument0);
diff --git a/PushEd.gmx/scripts/PEd_tileGetPosY.gml b/PushEd.gmx/scripts/PEd_tileGetPosY.gml
new file mode 100644
index 00000000..c46a1d40
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_tileGetPosY.gml
@@ -0,0 +1,8 @@
+/// PEd_tileGetPosY(tile)
+/**
+ * @brief Gets the y position of the tile.
+ * @param {real} tile The id of the tile.
+ * @return {real} The y position of the tile.
+ */
+gml_pragma("forceinline");
+return tile_get_y(argument0);
diff --git a/PushEd.gmx/scripts/PEd_tileGetScaleVec2.gml b/PushEd.gmx/scripts/PEd_tileGetScaleVec2.gml
new file mode 100644
index 00000000..4cb4e304
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_tileGetScaleVec2.gml
@@ -0,0 +1,8 @@
+/// PEd_tileGetScaleVec2(tile)
+/**
+ * @brief Gets the scale of the tile as vec2.
+ * @param {real} tile The id of the tile.
+ * @return {vec2} The scale of the tile.
+ */
+gml_pragma("forceinline");
+return PEd_vec2(tile_get_xscale(argument0), tile_get_yscale(argument0));
diff --git a/PushEd.gmx/scripts/PEd_tileGetScaleX.gml b/PushEd.gmx/scripts/PEd_tileGetScaleX.gml
new file mode 100644
index 00000000..4fc34c33
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_tileGetScaleX.gml
@@ -0,0 +1,8 @@
+/// PEd_tileGetScaleX(tile)
+/**
+ * @brief Gets the x scale of the tile.
+ * @param {real} tile The id of the tile.
+ * @return {real} The x scale of the tile.
+ */
+gml_pragma("forceinline");
+return tile_get_xscale(argument0);
diff --git a/PushEd.gmx/scripts/PEd_tileGetScaleY.gml b/PushEd.gmx/scripts/PEd_tileGetScaleY.gml
new file mode 100644
index 00000000..5ae61ae5
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_tileGetScaleY.gml
@@ -0,0 +1,8 @@
+/// PEd_tileGetScaleY(tile)
+/**
+ * @brief Gets the y scale of the tile.
+ * @param {real} tile The id of the tile.
+ * @return {real} The y scale of the tile.
+ */
+gml_pragma("forceinline");
+return tile_get_yscale(argument0);
diff --git a/PushEd.gmx/scripts/PEd_tileSetAlpha.gml b/PushEd.gmx/scripts/PEd_tileSetAlpha.gml
new file mode 100644
index 00000000..3f613a9e
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_tileSetAlpha.gml
@@ -0,0 +1,21 @@
+/// PEd_tileSetAlpha(tile, val)
+/**
+ * @brief Sets the alpha of the tile.
+ * @param {real} tile The id of the tile.
+ * @param {real} val The new alpha of the tile.
+ * @note If the tile is currently selected, this alpha
+ * is set to all tiles in multiple selection.
+ */
+with (PEd_oEditor)
+{
+ tile_set_alpha(argument0, argument1);
+
+ if (PEd_getSelectedObject() == argument0)
+ {
+ for (var i = ds_list_size(selectedObjects) - 1; i > 0; i--)
+ {
+ var _tile = selectedObjects[| i];
+ tile_set_alpha(_tile, argument1);
+ }
+ }
+}
diff --git a/PushEd.gmx/scripts/PEd_tileSetBlend.gml b/PushEd.gmx/scripts/PEd_tileSetBlend.gml
new file mode 100644
index 00000000..bb2f5a5b
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_tileSetBlend.gml
@@ -0,0 +1,21 @@
+/// PEd_tileSetBlend(tile, val)
+/**
+ * @brief Sets the blend color of the tile.
+ * @param {real} tile The id of the tile.
+ * @param {real} val The new blend color of the tile.
+ * @note If the tile is currently selected, this blend color
+ * is set to all tiles in multiple selection.
+ */
+with (PEd_oEditor)
+{
+ tile_set_blend(argument0, argument1);
+
+ if (PEd_getSelectedObject() == argument0)
+ {
+ for (var i = ds_list_size(selectedObjects) - 1; i > 0; i--)
+ {
+ var _tile = selectedObjects[| i];
+ tile_set_blend(_tile, argument1);
+ }
+ }
+}
diff --git a/PushEd.gmx/scripts/PEd_tileSetColourARGB.gml b/PushEd.gmx/scripts/PEd_tileSetColourARGB.gml
new file mode 100644
index 00000000..ba78b21a
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_tileSetColourARGB.gml
@@ -0,0 +1,25 @@
+/// PEd_tileSetColourARGB(tile, val)
+/**
+ * @brief Sets the blend color of the tile.
+ * @param {real} tile The id of the tile.
+ * @param {real} val The new blend color of the tile.
+ * @note If the tile is currently selected, this blend color
+ * is set to all tiles in multiple selection.
+ */
+with (PEd_oEditor)
+{
+ var _colour = PEd_argbToColour(argument1);
+ var _alpha = PEd_argbToAlpha(argument1);
+ tile_set_blend(argument0, _colour);
+ tile_set_alpha(argument0, _alpha);
+
+ if (PEd_getSelectedObject() == argument0)
+ {
+ for (var i = ds_list_size(selectedObjects) - 1; i > 0; i--)
+ {
+ var _tile = selectedObjects[| i];
+ tile_set_blend(_tile, _colour);
+ tile_set_alpha(_tile, _alpha);
+ }
+ }
+}
diff --git a/PushEd.gmx/scripts/PEd_tileSetDepth.gml b/PushEd.gmx/scripts/PEd_tileSetDepth.gml
new file mode 100644
index 00000000..152fe35f
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_tileSetDepth.gml
@@ -0,0 +1,33 @@
+/// PEd_tileSetDepth(tile, depth)
+/**
+ * @brief Sets the depth of the tile.
+ * @param {real} tile The id of the tile.
+ * @param {real} val The new depth of the tile.
+ * @note If the tile is currently selected, this depth
+ * is set to all tiles in multiple selection.
+ */
+with (PEd_oEditor)
+{
+ var _depth = argument1;
+ tile_set_depth(argument0, _depth);
+
+ if (PEd_getSelectedObject() == argument0)
+ {
+ for (var i = ds_list_size(selectedObjects) - 1; i > 0; i--)
+ {
+ tile_set_depth(selectedObjects[| i], _depth);
+ }
+
+ if (ds_list_find_index(tileLayers, _depth) == -1)
+ {
+ ds_list_add(tileLayers, _depth);
+ ds_list_add(tileVisible, true);
+ tileLayerSelected = ds_list_size(tileLayers) - 1;
+ PEd_guiRequestRedrawAll(guiRoot)
+ }
+ else
+ {
+ tileLayerSelected = ds_list_find_index(tileLayers, _depth);
+ }
+ }
+}
diff --git a/PushEd.gmx/scripts/PEd_tileSetPosVec2.gml b/PushEd.gmx/scripts/PEd_tileSetPosVec2.gml
new file mode 100644
index 00000000..00a225c4
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_tileSetPosVec2.gml
@@ -0,0 +1,8 @@
+/// PEd_tileSetPosVec2(tile, pos)
+/**
+ * @brief Sets the position of the tile.
+ * @param {real} tile The id of the tile.
+ * @param {vec2} pos The new tile postion.
+ */
+gml_pragma("forceinline");
+tile_set_position(argument0, argument1[0], argument1[1]);
diff --git a/PushEd.gmx/scripts/PEd_tileSetPosX.gml b/PushEd.gmx/scripts/PEd_tileSetPosX.gml
new file mode 100644
index 00000000..ff21df15
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_tileSetPosX.gml
@@ -0,0 +1,21 @@
+/// PEd_tileSetPosX(tile, val)
+/**
+ * @brief Sets the x position of the tile.
+ * @param {real} tile The id of the tile.
+ * @param {real} val The new x position of the tile.
+ * @note If the tile is currently selected, all tiles in
+ * multiple selection are moved accordingly.
+ */
+with (PEd_oEditor)
+{
+ var _tile = argument0;
+ if (PEd_getSelectedObject() == _tile)
+ {
+ var _data = selectedObjectsData[| 0];
+ PEd_oPivot.x = argument1 - _data[0];
+ }
+ else
+ {
+ tile_set_position(_tile, argument1, tile_get_y(_tile));
+ }
+}
diff --git a/PushEd.gmx/scripts/PEd_tileSetPosY.gml b/PushEd.gmx/scripts/PEd_tileSetPosY.gml
new file mode 100644
index 00000000..3a6596dd
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_tileSetPosY.gml
@@ -0,0 +1,21 @@
+/// PEd_tileSetPosY(tile, val)
+/**
+ * @brief Sets the y position of the tile.
+ * @param {real} tile The id of the tile.
+ * @param {real} val The new y position of the tile.
+ * @note If the tile is currently selected, all tiles in
+ * multiple selection are moved accordingly.
+ */
+with (PEd_oEditor)
+{
+ var _tile = argument0;
+ if (PEd_getSelectedObject() == _tile)
+ {
+ var _data = selectedObjectsData[| 0];
+ PEd_oPivot.y = argument1 - _data[1];
+ }
+ else
+ {
+ tile_set_position(_tile, tile_get_x(_tile), argument1);
+ }
+}
diff --git a/PushEd.gmx/scripts/PEd_tileSetScaleVec2.gml b/PushEd.gmx/scripts/PEd_tileSetScaleVec2.gml
new file mode 100644
index 00000000..73f1e0cd
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_tileSetScaleVec2.gml
@@ -0,0 +1,8 @@
+/// PEd_tileSetScaleVec2(tile, scale)
+/**
+ * @brief Sets the scale of the tile.
+ * @param {real} tile The id of the tile.
+ * @param {vec2} pos The new tile scale.
+ */
+gml_pragma("forceinline");
+tile_set_scale(argument0, argument1[0], argument1[1]);
diff --git a/PushEd.gmx/scripts/PEd_tileSetScaleX.gml b/PushEd.gmx/scripts/PEd_tileSetScaleX.gml
new file mode 100644
index 00000000..04cd9965
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_tileSetScaleX.gml
@@ -0,0 +1,24 @@
+/// PEd_tileSetScaleX(tile, val)
+/**
+ * @brief Sets the x scale of the tile.
+ * @param {real} tile The id of the tile.
+ * @param {real} val The new x scale of the tile.
+ * @note If the tile is currently selected, this x scale
+ * is set to all tiles in multiple selection.
+ */
+with (PEd_oEditor)
+{
+ var _scale = argument1;
+ var _tile = argument0;
+ var _scalePrev = tile_get_xscale(_tile);
+ tile_set_scale(_tile, _scale, tile_get_yscale(_tile));
+
+ if (PEd_getSelectedObject() == _tile)
+ {
+ for (var i = ds_list_size(selectedObjects) - 1; i > 0; i--)
+ {
+ var _tile = selectedObjects[| i];
+ tile_set_scale(_tile, _scale, tile_get_yscale(_tile));
+ }
+ }
+}
diff --git a/PushEd.gmx/scripts/PEd_tileSetScaleY.gml b/PushEd.gmx/scripts/PEd_tileSetScaleY.gml
new file mode 100644
index 00000000..c3ae949d
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_tileSetScaleY.gml
@@ -0,0 +1,23 @@
+/// PEd_tileSetScaleY(tile, val)
+/**
+ * @brief Sets the y scale of the tile.
+ * @param {real} tile The id of the tile.
+ * @param {real} val The new y scale of the tile.
+ * @note If the tile is currently selected, this y scale
+ * is set to all tiles in multiple selection.
+ */
+with (PEd_oEditor)
+{
+ var _scale = argument1;
+ var _tile = argument0;
+ tile_set_scale(_tile, tile_get_xscale(_tile), _scale);
+
+ if (PEd_getSelectedObject() == _tile)
+ {
+ for (var i = ds_list_size(selectedObjects) - 1; i > 0; i--)
+ {
+ var _tile = selectedObjects[| i];
+ tile_set_scale(_tile, tile_get_xscale(_tile), _scale);
+ }
+ }
+}
diff --git a/PushEd.gmx/scripts/PEd_vec2.gml b/PushEd.gmx/scripts/PEd_vec2.gml
new file mode 100644
index 00000000..964d0711
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_vec2.gml
@@ -0,0 +1,11 @@
+/// PEd_vec2(x, y)
+/**
+ * @brief Creates a new 2D vector.
+ * @param {real} x The first component of the vector.
+ * @param {real} y The second component of the vector.
+ * @return {vec2} The created vector.
+ */
+var _vec;
+_vec[1] = argument1;
+_vec[0] = argument0;
+return _vec;
diff --git a/PushEd.gmx/scripts/PEd_vec2Abs.gml b/PushEd.gmx/scripts/PEd_vec2Abs.gml
new file mode 100644
index 00000000..276d200d
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_vec2Abs.gml
@@ -0,0 +1,9 @@
+/// PEd_vec2Abs(vector)
+/**
+ * @brief Sets the vectors components to their absolute value.
+ * @param {vec2} vector The vector.
+ * @return {vec2} The vectors with its components set to their absolute values.
+ */
+gml_pragma("forceinline");
+return PEd_vec2(abs(argument0[0]),
+ abs(argument0[1]),);
diff --git a/PushEd.gmx/scripts/PEd_vec2Add.gml b/PushEd.gmx/scripts/PEd_vec2Add.gml
new file mode 100644
index 00000000..ba25a489
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_vec2Add.gml
@@ -0,0 +1,10 @@
+/// PEd_vec2Add(v1, v2)
+/**
+ * @brief Adds the two vectors.
+ * @param {vec2} v1 The first vector.
+ * @param {vec2} v2 The second vector.
+ * @return {vec2} The resulting vector.
+ */
+gml_pragma("forceinline");
+return PEd_vec2(argument0[0] + argument1[0],
+ argument0[1] + argument1[1]);
diff --git a/PushEd.gmx/scripts/PEd_vec2Clone.gml b/PushEd.gmx/scripts/PEd_vec2Clone.gml
new file mode 100644
index 00000000..b9378ad4
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_vec2Clone.gml
@@ -0,0 +1,8 @@
+/// PEd_vec2Clone(vector)
+/**
+ * @brief Clones the vector.
+ * @param {vec2} vector The vector to clone.
+ * @return {vec2} The vector clone.
+ */
+gml_pragma("forceinline");
+return PEd_vec2(argument0[0], argument0[1]);
diff --git a/PushEd.gmx/scripts/PEd_vec2Dot.gml b/PushEd.gmx/scripts/PEd_vec2Dot.gml
new file mode 100644
index 00000000..bbb004a7
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_vec2Dot.gml
@@ -0,0 +1,10 @@
+/// PEd_vec2Dot(v1, v2)
+/**
+ * @brief Gets the dot product of the two vectors.
+ * @param {vec2} v1 The first vector.
+ * @param {vec2} v2 The second vector.
+ * @return {vec2} The dot product of the two vectors.
+ */
+gml_pragma("forceinline");
+return (argument0[0] * argument1[0]
+ + argument0[1] * argument1[1]);
diff --git a/PushEd.gmx/scripts/PEd_vec2Equals.gml b/PushEd.gmx/scripts/PEd_vec2Equals.gml
new file mode 100644
index 00000000..ebc34524
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_vec2Equals.gml
@@ -0,0 +1,10 @@
+/// PEd_vec2Equals(v1, v2)
+/**
+ * @brief Gets whether the two vectors are equal.
+ * @param {vec2} v1 The first vector.
+ * @param {vec2} v2 The second vector.
+ * @return {bool} True if the two vectors are equal.
+ */
+gml_pragma("forceinline");
+return (argument0[0] == argument1[0]
+ && argument0[1] == argument1[1]);
diff --git a/PushEd.gmx/scripts/PEd_vec2Length.gml b/PushEd.gmx/scripts/PEd_vec2Length.gml
new file mode 100644
index 00000000..18c657fb
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_vec2Length.gml
@@ -0,0 +1,9 @@
+/// PEd_vec2Length(vector)
+/**
+ * @brief Gets the length of the vector.
+ * @param {vec2} vector The vector.
+ * @return {real} The length of the vector.
+ */
+gml_pragma("forceinline");
+return sqrt(argument0[0] * argument0[0]
+ + argument0[1] * argument0[1]);
diff --git a/PushEd.gmx/scripts/PEd_vec2LengthSq.gml b/PushEd.gmx/scripts/PEd_vec2LengthSq.gml
new file mode 100644
index 00000000..e0168c70
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_vec2LengthSq.gml
@@ -0,0 +1,9 @@
+/// PEd_vec2LengthSq(vector)
+/**
+ * @brief Gets squared length of the vector.
+ * @param {vec2} vector The vector.
+ * @return {real} The length of the vector.
+ */
+gml_pragma("forceinline");
+return (argument0[0] * argument0[0]
+ + argument0[1] * argument0[1]);
diff --git a/PushEd.gmx/scripts/PEd_vec2Lerp.gml b/PushEd.gmx/scripts/PEd_vec2Lerp.gml
new file mode 100644
index 00000000..4bd0b3e9
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_vec2Lerp.gml
@@ -0,0 +1,11 @@
+/// PEd_vec2Lerp(v1, v2, t)
+/**
+ * @brief Linearly interpolates between the two vectors.
+ * @param {vec2} v1 The first vector.
+ * @param {vec2} v2 The second vector.
+ * @param {real} t The interpolation factor.
+ * @return {vec2} The resulting vector.
+ */
+gml_pragma("forceinline");
+return PEd_vec2(lerp(argument0[0], argument1[0], argument2),
+ lerp(argument0[1], argument1[1], argument2));
diff --git a/PushEd.gmx/scripts/PEd_vec2Normalize.gml b/PushEd.gmx/scripts/PEd_vec2Normalize.gml
new file mode 100644
index 00000000..4732058f
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_vec2Normalize.gml
@@ -0,0 +1,9 @@
+/// PEd_vec2Normalize(vector)
+/**
+ * @brief Normalizes the vector.
+ * @param {vec2} vector The vector.
+ * @return {vec2} The normalized vector.
+ */
+var _n = 1 / sqrt(argument0[0]*argument0[0] + argument0[1]*argument0[1]);
+return PEd_vec2(abs(argument0[0] * _n),
+ abs(argument0[1] * _n));
diff --git a/PushEd.gmx/scripts/PEd_vec2Reflect.gml b/PushEd.gmx/scripts/PEd_vec2Reflect.gml
new file mode 100644
index 00000000..cbb18a97
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_vec2Reflect.gml
@@ -0,0 +1,10 @@
+/// PEd_vec2Reflect(v1, v2)
+/**
+ * @brief Reflects the first vector off the second vector.
+ * @param {vec2} v1 The first vector.
+ * @param {vec2} v2 The second vector. Should be normalized.
+ * @return {vec2} The resulting vector.
+ */
+var _dot = PEd_vec2Dot(argument0, argument1);
+return PEd_vec2(-(argument0[0] - 2 * _dot * argument1[0]),
+ -(argument0[1] - 2 * _dot * argument1[1]));
diff --git a/PushEd.gmx/scripts/PEd_vec2Scale.gml b/PushEd.gmx/scripts/PEd_vec2Scale.gml
new file mode 100644
index 00000000..2dabdb30
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_vec2Scale.gml
@@ -0,0 +1,10 @@
+/// PEd_vec2Scale(vector, real)
+/**
+ * @brief Scales the vector by the value.
+ * @param {vec2} vector The vector.
+ * @param {real} real The value to scale the vector by.
+ * @return {vec2} The scaled vector.
+ */
+gml_pragma("forceinline");
+return PEd_vec2(argument0[0] * argument1,
+ argument0[1] * argument1);
diff --git a/PushEd.gmx/scripts/PEd_vec2Subtract.gml b/PushEd.gmx/scripts/PEd_vec2Subtract.gml
new file mode 100644
index 00000000..4fe412cf
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_vec2Subtract.gml
@@ -0,0 +1,10 @@
+/// PEd_vec2Subtract(v1, v2)
+/**
+ * @brief Subtracts the two vectors.
+ * @param {vec2} v1 The first vector.
+ * @param {vec2} v2 The second vector.
+ * @return {vec2} The resulting vector.
+ */
+gml_pragma("forceinline");
+return PEd_vec2(argument0[0] - argument1[0],
+ argument0[1] - argument1[1]);
diff --git a/PushEd.gmx/scripts/PEd_vec3.gml b/PushEd.gmx/scripts/PEd_vec3.gml
new file mode 100644
index 00000000..41dd1a68
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_vec3.gml
@@ -0,0 +1,13 @@
+/// PEd_vec3(x, y, z)
+/**
+ * @brief Creates a new 3D vector.
+ * @param {real} x The first component of the vector.
+ * @param {real} y The second component of the vector.
+ * @param {real} z The third component of the vector.
+ * @return {vec3} The created vector.
+ */
+var _vec;
+_vec[2] = argument2;
+_vec[1] = argument1;
+_vec[0] = argument0;
+return _vec;
diff --git a/PushEd.gmx/scripts/PEd_vec3Abs.gml b/PushEd.gmx/scripts/PEd_vec3Abs.gml
new file mode 100644
index 00000000..646eadf9
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_vec3Abs.gml
@@ -0,0 +1,10 @@
+/// PEd_vec3Abs(vector)
+/**
+ * @brief Sets the vectors components to their absolute value.
+ * @param {vec3} vector The vector.
+ * @return {vec3} The vectors with its components set to their absolute values.
+ */
+gml_pragma("forceinline");
+return PEd_vec3(abs(argument0[0]),
+ abs(argument0[1]),
+ abs(argument0[2]));
diff --git a/PushEd.gmx/scripts/PEd_vec3Add.gml b/PushEd.gmx/scripts/PEd_vec3Add.gml
new file mode 100644
index 00000000..5718e2a7
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_vec3Add.gml
@@ -0,0 +1,11 @@
+/// PEd_vec3Add(v1, v2)
+/**
+ * @brief Adds the two vectors.
+ * @param {vec3} v1 The first vector.
+ * @param {vec3} v2 The second vector.
+ * @return {vec3} The resulting vector.
+ */
+gml_pragma("forceinline");
+return PEd_vec3(argument0[0] + argument1[0],
+ argument0[1] + argument1[1],
+ argument0[2] + argument1[2]);
diff --git a/PushEd.gmx/scripts/PEd_vec3Clone.gml b/PushEd.gmx/scripts/PEd_vec3Clone.gml
new file mode 100644
index 00000000..f9b10972
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_vec3Clone.gml
@@ -0,0 +1,8 @@
+/// PEd_vec3Clone(vector)
+/**
+ * @brief Clones the vector.
+ * @param {vec3} vector The vector to clone.
+ * @return {vec3} The vector clone.
+ */
+gml_pragma("forceinline");
+return PEd_vec3(argument0[0], argument0[1], argument0[2]);
diff --git a/PushEd.gmx/scripts/PEd_vec3Cross.gml b/PushEd.gmx/scripts/PEd_vec3Cross.gml
new file mode 100644
index 00000000..80be20fd
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_vec3Cross.gml
@@ -0,0 +1,11 @@
+/// PEd_vec3Cross(v1, v2)
+/**
+ * @brief Gets the cross product of the two vectors.
+ * @param {vec3} v1 The first vector.
+ * @param {vec3} v2 The second vector.
+ * @return {vec3} The cross product of the two vectors.
+ */
+gml_pragma("forceinline");
+return PEd_vec3(argument0[1]*argument1[2] - argument0[2]*argument1[1],
+ argument0[2]*argument1[0] - argument0[0]*argument1[2],
+ argument0[0]*argument1[1] - argument0[1]*argument1[0]);
diff --git a/PushEd.gmx/scripts/PEd_vec3Dot.gml b/PushEd.gmx/scripts/PEd_vec3Dot.gml
new file mode 100644
index 00000000..cde5c1ba
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_vec3Dot.gml
@@ -0,0 +1,11 @@
+/// PEd_vec3Dot(v1, v2)
+/**
+ * @brief Gets the dot product of the two vectors.
+ * @param {vec3} v1 The first vector.
+ * @param {vec3} v2 The second vector.
+ * @return {vec3} The dot product of the two vectors.
+ */
+gml_pragma("forceinline");
+return (argument0[0] * argument1[0]
+ + argument0[1] * argument1[1]
+ + argument0[2] * argument1[2]);
diff --git a/PushEd.gmx/scripts/PEd_vec3Equals.gml b/PushEd.gmx/scripts/PEd_vec3Equals.gml
new file mode 100644
index 00000000..96153119
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_vec3Equals.gml
@@ -0,0 +1,11 @@
+/// PEd_vec3Equals(v1, v2)
+/**
+ * @brief Gets whether the two vectors are equal.
+ * @param {vec3} v1 The first vector.
+ * @param {vec3} v2 The second vector.
+ * @return {bool} True if the two vectors are equal.
+ */
+gml_pragma("forceinline");
+return (argument0[0] == argument1[0]
+ && argument0[1] == argument1[1]
+ && argument0[2] == argument1[2]);
diff --git a/PushEd.gmx/scripts/PEd_vec3Length.gml b/PushEd.gmx/scripts/PEd_vec3Length.gml
new file mode 100644
index 00000000..9d232489
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_vec3Length.gml
@@ -0,0 +1,10 @@
+/// PEd_vec3Length(vector)
+/**
+ * @brief Gets the length of the vector.
+ * @param {vec3} vector The vector.
+ * @return {real} The length of the vector.
+ */
+gml_pragma("forceinline");
+return sqrt(argument0[0] * argument0[0]
+ + argument0[1] * argument0[1]
+ + argument0[2] * argument0[2]);
diff --git a/PushEd.gmx/scripts/PEd_vec3LengthSq.gml b/PushEd.gmx/scripts/PEd_vec3LengthSq.gml
new file mode 100644
index 00000000..f18af5f3
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_vec3LengthSq.gml
@@ -0,0 +1,10 @@
+/// PEd_vec3LengthSq(vector)
+/**
+ * @brief Gets squared length of the vector.
+ * @param {vec3} vector The vector.
+ * @return {real} The length of the vector.
+ */
+gml_pragma("forceinline");
+return (argument0[0] * argument0[0]
+ + argument0[1] * argument0[1]
+ + argument0[2] * argument0[2]);
diff --git a/PushEd.gmx/scripts/PEd_vec3Lerp.gml b/PushEd.gmx/scripts/PEd_vec3Lerp.gml
new file mode 100644
index 00000000..97408711
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_vec3Lerp.gml
@@ -0,0 +1,12 @@
+/// PEd_vec3Lerp(v1, v2, t)
+/**
+ * @brief Linearly interpolates between the two vectors.
+ * @param {vec3} v1 The first vector.
+ * @param {vec3} v2 The second vector.
+ * @param {real} t The interpolation factor.
+ * @return {vec3} The resulting vector.
+ */
+gml_pragma("forceinline");
+return PEd_vec3(lerp(argument0[0], argument1[0], argument2),
+ lerp(argument0[1], argument1[1], argument2),
+ lerp(argument0[2], argument1[2], argument2));
diff --git a/PushEd.gmx/scripts/PEd_vec3Normalize.gml b/PushEd.gmx/scripts/PEd_vec3Normalize.gml
new file mode 100644
index 00000000..8869c588
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_vec3Normalize.gml
@@ -0,0 +1,10 @@
+/// PEd_vec3Normalize(vector)
+/**
+ * @brief Normalizes the vector.
+ * @param {vec3} vector The vector.
+ * @return {vec3} The normalized vector.
+ */
+var _n = 1 / sqrt(argument0[0]*argument0[0] + argument0[1]*argument0[1] + argument0[2]*argument0[2]);
+return PEd_vec3(argument0[0] * _n,
+ argument0[1] * _n,
+ argument0[2] * _n);
diff --git a/PushEd.gmx/scripts/PEd_vec3Project.gml b/PushEd.gmx/scripts/PEd_vec3Project.gml
new file mode 100644
index 00000000..35f4c627
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_vec3Project.gml
@@ -0,0 +1,17 @@
+/// PEd_vec3Project(vector, screen, world, view, projection)
+/**
+ * @brief Projects the vector from world space into screen space.
+ * @param {vec3} vector The vector.
+ * @param {vec2} screen The screen size.
+ * @param {matrix} world The world matrix.
+ * @param {matrix} view The view matrix.
+ * @param {matrix} projection The projection matrix.
+ * @return {vec4} The projected vector.
+ */
+var _vec = PEd_vec4(argument0[0], argument0[1], argument0[2], 1);
+var _mat = matrix_multiply(matrix_multiply(argument2, argument3), argument4);
+_vec = PEd_vec4Transform(_vec, _mat);
+_vec = PEd_vec4Scale(_vec, 1 / _vec[3]);
+_vec[0] = (_vec[0] * 0.5 + 0.5) * argument1[0];
+_vec[1] = (1 - (_vec[1] * 0.5 + 0.5)) * argument1[1];
+return _vec;
diff --git a/PushEd.gmx/scripts/PEd_vec3Reflect.gml b/PushEd.gmx/scripts/PEd_vec3Reflect.gml
new file mode 100644
index 00000000..237093e4
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_vec3Reflect.gml
@@ -0,0 +1,11 @@
+/// PEd_vec3Reflect(v1, v2)
+/**
+ * @brief Reflects the first vector off the second vector.
+ * @param {vec3} v1 The first vector.
+ * @param {vec3} v2 The second vector. Should be normalized.
+ * @return {vec3} The resulting vector.
+ */
+var _dot = PEd_vec3Dot(argument0, argument1);
+return PEd_vec3(-(argument0[0] - 2 * _dot * argument1[0]),
+ -(argument0[1] - 2 * _dot * argument1[1]),
+ -(argument0[2] - 2 * _dot * argument1[2]));
diff --git a/PushEd.gmx/scripts/PEd_vec3Scale.gml b/PushEd.gmx/scripts/PEd_vec3Scale.gml
new file mode 100644
index 00000000..ff5a9071
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_vec3Scale.gml
@@ -0,0 +1,11 @@
+/// PEd_vec3Scale(vector, real)
+/**
+ * @brief Scales the vector by the value.
+ * @param {vec3} vector The vector.
+ * @param {real} real The value to scale the vector by.
+ * @return {vec3} The scaled vector.
+ */
+gml_pragma("forceinline");
+return PEd_vec3(argument0[0] * argument1,
+ argument0[1] * argument1,
+ argument0[2] * argument1);
diff --git a/PushEd.gmx/scripts/PEd_vec3Subtract.gml b/PushEd.gmx/scripts/PEd_vec3Subtract.gml
new file mode 100644
index 00000000..4bf741c3
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_vec3Subtract.gml
@@ -0,0 +1,11 @@
+/// PEd_vec3Subtract(v1, v2)
+/**
+ * @brief Subtracts the two vectors.
+ * @param {vec3} v1 The first vector.
+ * @param {vec3} v2 The second vector.
+ * @return {vec3} The resulting vector.
+ */
+gml_pragma("forceinline");
+return PEd_vec3(argument0[0] - argument1[0],
+ argument0[1] - argument1[1],
+ argument0[2] - argument1[2]);
diff --git a/PushEd.gmx/scripts/PEd_vec4.gml b/PushEd.gmx/scripts/PEd_vec4.gml
new file mode 100644
index 00000000..cfc8aef3
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_vec4.gml
@@ -0,0 +1,15 @@
+/// PEd_vec4(x, y, z, w)
+/**
+ * @brief Creates a new 4D vector.
+ * @param {real} x The first component of the vector.
+ * @param {real} y The second component of the vector.
+ * @param {real} z The third component of the vector.
+ * @param {real} w The fourth component of the vector.
+ * @return {vec4} The created vector.
+ */
+var _vec;
+_vec[3] = argument3;
+_vec[2] = argument2;
+_vec[1] = argument1;
+_vec[0] = argument0;
+return _vec;
diff --git a/PushEd.gmx/scripts/PEd_vec4Abs.gml b/PushEd.gmx/scripts/PEd_vec4Abs.gml
new file mode 100644
index 00000000..1377d879
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_vec4Abs.gml
@@ -0,0 +1,11 @@
+/// PEd_vec4Abs(vector)
+/**
+ * @brief Sets the vectors components to their absolute value.
+ * @param {vec4} vector The vector.
+ * @return {vec4} The vectors with its components set to their absolute values.
+ */
+gml_pragma("forceinline");
+return PEd_vec4(abs(argument0[0]),
+ abs(argument0[1]),
+ abs(argument0[2]),
+ abs(argument0[3]));
diff --git a/PushEd.gmx/scripts/PEd_vec4Add.gml b/PushEd.gmx/scripts/PEd_vec4Add.gml
new file mode 100644
index 00000000..dc1f6474
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_vec4Add.gml
@@ -0,0 +1,12 @@
+/// PEd_vec4Add(v1, v2)
+/**
+ * @brief Adds the two vectors.
+ * @param {vec4} v1 The first vector.
+ * @param {vec4} v2 The second vector.
+ * @return {vec4} The resulting vector.
+ */
+gml_pragma("forceinline");
+return PEd_vec4(argument0[0] + argument1[0],
+ argument0[1] + argument1[1],
+ argument0[2] + argument1[2],
+ argument0[3] + argument1[3]);
diff --git a/PushEd.gmx/scripts/PEd_vec4Clone.gml b/PushEd.gmx/scripts/PEd_vec4Clone.gml
new file mode 100644
index 00000000..95ec6e6a
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_vec4Clone.gml
@@ -0,0 +1,8 @@
+/// PEd_vec4Clone(vector)
+/**
+ * @brief Clones the vector.
+ * @param {vec4} vector The vector to clone.
+ * @return {vec4} The vector clone.
+ */
+gml_pragma("forceinline");
+return PEd_vec4(argument0[0], argument0[1], argument0[2], argument0[3]);
diff --git a/PushEd.gmx/scripts/PEd_vec4Dot.gml b/PushEd.gmx/scripts/PEd_vec4Dot.gml
new file mode 100644
index 00000000..79bd94ee
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_vec4Dot.gml
@@ -0,0 +1,12 @@
+/// PEd_vec4Dot(v1, v2)
+/**
+ * @brief Gets the dot product of the two vectors.
+ * @param {vec4} v1 The first vector.
+ * @param {vec4} v2 The second vector.
+ * @return {vec4} The dot product of the two vectors.
+ */
+gml_pragma("forceinline");
+return (argument0[0] * argument1[0]
+ + argument0[1] * argument1[1]
+ + argument0[2] * argument1[2]
+ + argument0[3] * argument1[3]);
diff --git a/PushEd.gmx/scripts/PEd_vec4Equals.gml b/PushEd.gmx/scripts/PEd_vec4Equals.gml
new file mode 100644
index 00000000..93699a56
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_vec4Equals.gml
@@ -0,0 +1,12 @@
+/// PEd_vec4Equals(v1, v2)
+/**
+ * @brief Gets whether the two vectors are equal.
+ * @param {vec4} v1 The first vector.
+ * @param {vec4} v2 The second vector.
+ * @return {bool} True if the two vectors are equal.
+ */
+gml_pragma("forceinline");
+return (argument0[0] == argument1[0]
+ && argument0[1] == argument1[1]
+ && argument0[2] == argument1[2]
+ && argument0[3] == argument1[3]);
diff --git a/PushEd.gmx/scripts/PEd_vec4Length.gml b/PushEd.gmx/scripts/PEd_vec4Length.gml
new file mode 100644
index 00000000..b56839be
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_vec4Length.gml
@@ -0,0 +1,11 @@
+/// PEd_vec4Length(vector)
+/**
+ * @brief Gets the length of the vector.
+ * @param {vec4} vector The vector.
+ * @return {real} The length of the vector.
+ */
+gml_pragma("forceinline");
+return sqrt(argument0[0] * argument0[0]
+ + argument0[1] * argument0[1]
+ + argument0[2] * argument0[2]
+ + argument0[3] * argument0[3]);
diff --git a/PushEd.gmx/scripts/PEd_vec4LengthSq.gml b/PushEd.gmx/scripts/PEd_vec4LengthSq.gml
new file mode 100644
index 00000000..94db4914
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_vec4LengthSq.gml
@@ -0,0 +1,11 @@
+/// PEd_vec4LengthSq(vector)
+/**
+ * @brief Gets squared length of the vector.
+ * @param {vec4} vector The vector.
+ * @return {real} The length of the vector.
+ */
+gml_pragma("forceinline");
+return (argument0[0] * argument0[0]
+ + argument0[1] * argument0[1]
+ + argument0[2] * argument0[2]
+ + argument0[3] * argument0[3]);
diff --git a/PushEd.gmx/scripts/PEd_vec4Lerp.gml b/PushEd.gmx/scripts/PEd_vec4Lerp.gml
new file mode 100644
index 00000000..52b9afaf
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_vec4Lerp.gml
@@ -0,0 +1,13 @@
+/// PEd_vec4Lerp(v1, v2, t)
+/**
+ * @brief Linearly interpolates between the two vectors.
+ * @param {vec4} v1 The first vector.
+ * @param {vec4} v2 The second vector.
+ * @param {real} t The interpolation factor.
+ * @return {vec4} The resulting vector.
+ */
+gml_pragma("forceinline");
+return PEd_vec4(lerp(argument0[0], argument1[0], argument2),
+ lerp(argument0[1], argument1[1], argument2),
+ lerp(argument0[2], argument1[2], argument2),
+ lerp(argument0[3], argument1[3], argument2));
diff --git a/PushEd.gmx/scripts/PEd_vec4Normalize.gml b/PushEd.gmx/scripts/PEd_vec4Normalize.gml
new file mode 100644
index 00000000..bda3db9f
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_vec4Normalize.gml
@@ -0,0 +1,11 @@
+/// PEd_vec4Normalize(vector)
+/**
+ * @brief Normalizes the vector.
+ * @param {vec4} vector The vector.
+ * @return {vec4} The normalized vector.
+ */
+var _n = 1 / sqrt(argument0[0]*argument0[0] + argument0[1]*argument0[1] + argument0[2]*argument0[2] + argument0[3]*argument0[3]);
+return PEd_vec4(abs(argument0[0] * _n),
+ abs(argument0[1] * _n),
+ abs(argument0[2] * _n),
+ abs(argument0[3] * _n));
diff --git a/PushEd.gmx/scripts/PEd_vec4Scale.gml b/PushEd.gmx/scripts/PEd_vec4Scale.gml
new file mode 100644
index 00000000..ec15ae35
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_vec4Scale.gml
@@ -0,0 +1,12 @@
+/// PEd_vec4Scale(vector, real)
+/**
+ * @brief Scales the vector by the value.
+ * @param {vec4} vector The vector.
+ * @param {real} real The value to scale the vector by.
+ * @return {vec4} The scaled vector.
+ */
+gml_pragma("forceinline");
+return PEd_vec4(argument0[0] * argument1,
+ argument0[1] * argument1,
+ argument0[2] * argument1,
+ argument0[3] * argument1);
diff --git a/PushEd.gmx/scripts/PEd_vec4Subtract.gml b/PushEd.gmx/scripts/PEd_vec4Subtract.gml
new file mode 100644
index 00000000..8531c24e
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_vec4Subtract.gml
@@ -0,0 +1,12 @@
+/// PEd_vec4Subtract(v1, v2)
+/**
+ * @brief Subtracts the two vectors.
+ * @param {vec4} v1 The first vector.
+ * @param {vec4} v2 The second vector.
+ * @return {vec4} The resulting vector.
+ */
+gml_pragma("forceinline");
+return PEd_vec4(argument0[0] - argument1[0],
+ argument0[1] - argument1[1],
+ argument0[2] - argument1[2],
+ argument0[3] - argument1[3]);
diff --git a/PushEd.gmx/scripts/PEd_vec4Transform.gml b/PushEd.gmx/scripts/PEd_vec4Transform.gml
new file mode 100644
index 00000000..82bec876
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_vec4Transform.gml
@@ -0,0 +1,12 @@
+/// PEd_vec4Transform(vector, matrix)
+/**
+ * @brief Transforms the vector with the matrix.
+ * @param {vec4} vector The vector.
+ * @param {matrix} matrix The matrix.
+ * @return {vec4} The resulting vector.
+ */
+gml_pragma("forceinline");
+return PEd_vec4(argument1[ 0]*argument0[0] + argument1[ 4]*argument0[1] + argument1[ 8]*argument0[2] + argument1[12]*argument0[3],
+ argument1[ 1]*argument0[0] + argument1[ 5]*argument0[1] + argument1[ 9]*argument0[2] + argument1[13]*argument0[3],
+ argument1[ 2]*argument0[0] + argument1[ 6]*argument0[1] + argument1[10]*argument0[2] + argument1[14]*argument0[3],
+ argument1[ 3]*argument0[0] + argument1[ 7]*argument0[1] + argument1[11]*argument0[2] + argument1[15]*argument0[3]);
diff --git a/PushEd.gmx/scripts/PEd_viewportGetBorderHor.gml b/PushEd.gmx/scripts/PEd_viewportGetBorderHor.gml
new file mode 100644
index 00000000..81e54f1d
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_viewportGetBorderHor.gml
@@ -0,0 +1,8 @@
+/// PEd_viewportGetBorderHor(viewport)
+/**
+ * @brief Gets the horizontal border of the viewport.
+ * @param {real} viewport The id of the viewport.
+ * @return {real} The size of horizontal border of the viewport.
+ */
+gml_pragma("forceinline");
+return argument0[? "borderHor"];
diff --git a/PushEd.gmx/scripts/PEd_viewportGetBorderVer.gml b/PushEd.gmx/scripts/PEd_viewportGetBorderVer.gml
new file mode 100644
index 00000000..eebad103
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_viewportGetBorderVer.gml
@@ -0,0 +1,8 @@
+/// PEd_viewportGetBorderVer(viewport)
+/**
+ * @brief Gets vertical border the of the viewport.
+ * @param {real} viewport The id of the viewport.
+ * @return {real} The size of vertical border of the viewport.
+ */
+gml_pragma("forceinline");
+return argument0[? "borderVer"];
diff --git a/PushEd.gmx/scripts/PEd_viewportGetHeight.gml b/PushEd.gmx/scripts/PEd_viewportGetHeight.gml
new file mode 100644
index 00000000..504a701a
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_viewportGetHeight.gml
@@ -0,0 +1,8 @@
+/// PEd_viewportGetHeight(viewport)
+/**
+ * @brief Gets the height of the viewport.
+ * @param {real} viewport The id of the viewport.
+ * @return {real} The height of the viewport.
+ */
+gml_pragma("forceinline");
+return argument0[? "height"];
diff --git a/PushEd.gmx/scripts/PEd_viewportGetObject.gml b/PushEd.gmx/scripts/PEd_viewportGetObject.gml
new file mode 100644
index 00000000..13f67598
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_viewportGetObject.gml
@@ -0,0 +1,8 @@
+/// PEd_viewportGetObject(viewport)
+/**
+ * @brief Gets the id of the object that the viewport follows.
+ * @param {real} viewport The id of the viewport.
+ * @return {real} The id of the object that the viewport follows or noone.
+ */
+gml_pragma("forceinline");
+return argument0[? "object"];
diff --git a/PushEd.gmx/scripts/PEd_viewportGetPortHeight.gml b/PushEd.gmx/scripts/PEd_viewportGetPortHeight.gml
new file mode 100644
index 00000000..a59f0209
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_viewportGetPortHeight.gml
@@ -0,0 +1,8 @@
+/// PEd_viewportGetPortHeight(viewport)
+/**
+ * @brief Gets the port height of the viewport.
+ * @param {real} viewport The id of the viewport.
+ * @return {real} The port height of the viewport.
+ */
+gml_pragma("forceinline");
+return argument0[? "portHeight"];
diff --git a/PushEd.gmx/scripts/PEd_viewportGetPortWidth.gml b/PushEd.gmx/scripts/PEd_viewportGetPortWidth.gml
new file mode 100644
index 00000000..211ddf77
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_viewportGetPortWidth.gml
@@ -0,0 +1,8 @@
+/// PEd_viewportGetPortWidth(viewport)
+/**
+ * @brief Gets the port width of the viewport.
+ * @param {real} viewport The id of the viewport.
+ * @return {real} The port width of the viewport.
+ */
+gml_pragma("forceinline");
+return argument0[? "portWidth"];
diff --git a/PushEd.gmx/scripts/PEd_viewportGetPortX.gml b/PushEd.gmx/scripts/PEd_viewportGetPortX.gml
new file mode 100644
index 00000000..2ba6e552
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_viewportGetPortX.gml
@@ -0,0 +1,8 @@
+/// PEd_viewportGetPortX(viewport)
+/**
+ * @brief Gets the port x position of the viewport.
+ * @param {real} viewport The id of the viewport.
+ * @return {real} The port x position of the viewport.
+ */
+gml_pragma("forceinline");
+return argument0[? "portX"];
diff --git a/PushEd.gmx/scripts/PEd_viewportGetPortY.gml b/PushEd.gmx/scripts/PEd_viewportGetPortY.gml
new file mode 100644
index 00000000..83d30b60
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_viewportGetPortY.gml
@@ -0,0 +1,8 @@
+/// PEd_viewportGetPortY(viewport)
+/**
+ * @brief Gets the port y position of the viewport.
+ * @param {real} viewport The id of the viewport.
+ * @return {real} The port y position of the viewport.
+ */
+gml_pragma("forceinline");
+return argument0[? "portY"];
diff --git a/PushEd.gmx/scripts/PEd_viewportGetSpeedHor.gml b/PushEd.gmx/scripts/PEd_viewportGetSpeedHor.gml
new file mode 100644
index 00000000..81b1dba7
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_viewportGetSpeedHor.gml
@@ -0,0 +1,8 @@
+/// PEd_viewportGetSpeedHor(viewport)
+/**
+ * @brief Gets the horizontal speed of the viewport.
+ * @param {real} viewport The id of the viewport.
+ * @return {real} The horizontal speed of the viewport.
+ */
+gml_pragma("forceinline");
+return argument0[? "speedHor"];
diff --git a/PushEd.gmx/scripts/PEd_viewportGetSpeedVer.gml b/PushEd.gmx/scripts/PEd_viewportGetSpeedVer.gml
new file mode 100644
index 00000000..f6c9e5e1
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_viewportGetSpeedVer.gml
@@ -0,0 +1,8 @@
+/// PEd_viewportGetSpeedVer(viewport)
+/**
+ * @brief Gets the vertical speed of the viewport.
+ * @param {real} viewport The id of the viewport.
+ * @return {real} The vertical speed of the viewport.
+ */
+gml_pragma("forceinline");
+return argument0[? "speedVer"];
diff --git a/PushEd.gmx/scripts/PEd_viewportGetVisible.gml b/PushEd.gmx/scripts/PEd_viewportGetVisible.gml
new file mode 100644
index 00000000..7d38704d
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_viewportGetVisible.gml
@@ -0,0 +1,8 @@
+/// PEd_viewportGetVisible(viewport)
+/**
+ * @brief Gets the visibility setting of the viewport.
+ * @param {real} viewport The id of the viewport.
+ * @return {real} The visibility setting of the viewport.
+ */
+gml_pragma("forceinline");
+return argument0[? "visible"];
diff --git a/PushEd.gmx/scripts/PEd_viewportGetWidth.gml b/PushEd.gmx/scripts/PEd_viewportGetWidth.gml
new file mode 100644
index 00000000..4e022ec5
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_viewportGetWidth.gml
@@ -0,0 +1,8 @@
+/// PEd_viewportGetWidth(viewport)
+/**
+ * @brief Gets the width of the viewport.
+ * @param {real} viewport The id of the viewport.
+ * @return {real} The width of the viewport.
+ */
+gml_pragma("forceinline");
+return argument0[? "width"];
diff --git a/PushEd.gmx/scripts/PEd_viewportGetX.gml b/PushEd.gmx/scripts/PEd_viewportGetX.gml
new file mode 100644
index 00000000..3ccdb21f
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_viewportGetX.gml
@@ -0,0 +1,8 @@
+/// PEd_viewportGetX(viewport)
+/**
+ * @brief Gets the x position of the viewport.
+ * @param {real} viewport The id of the viewport.
+ * @return {real} The x position of the viewport.
+ */
+gml_pragma("forceinline");
+return argument0[? "x"];
diff --git a/PushEd.gmx/scripts/PEd_viewportGetY.gml b/PushEd.gmx/scripts/PEd_viewportGetY.gml
new file mode 100644
index 00000000..326783ca
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_viewportGetY.gml
@@ -0,0 +1,8 @@
+/// PEd_viewportGetY(viewport)
+/**
+ * @brief Gets the y position of the viewport.
+ * @param {real} viewport The id of the viewport.
+ * @return {real} The y position of the viewport.
+ */
+gml_pragma("forceinline");
+return argument0[? "y"];
diff --git a/PushEd.gmx/scripts/PEd_viewportSetBorderHor.gml b/PushEd.gmx/scripts/PEd_viewportSetBorderHor.gml
new file mode 100644
index 00000000..79b10a3c
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_viewportSetBorderHor.gml
@@ -0,0 +1,7 @@
+/// PEd_viewportSetBorderHor(viewport, val)
+/**
+ * @brief Sets the horizontal border size of the viewport.
+ * @param {real} viewport The id of the viewport.
+ * @param {real} val The new size of the horizontal border of the viewport.
+ */
+argument0[? "borderHor"] = argument1;
diff --git a/PushEd.gmx/scripts/PEd_viewportSetBorderVer.gml b/PushEd.gmx/scripts/PEd_viewportSetBorderVer.gml
new file mode 100644
index 00000000..9102a44a
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_viewportSetBorderVer.gml
@@ -0,0 +1,7 @@
+/// PEd_viewportSetBorderVer(viewport, val)
+/**
+ * @brief Sets the vertical border size of of the viewport.
+ * @param {real} viewport The id of the viewport.
+ * @param {real} val The new size of the vertical border of the viewport.
+ */
+argument0[? "borderVer"] = argument1;
diff --git a/PushEd.gmx/scripts/PEd_viewportSetHeight.gml b/PushEd.gmx/scripts/PEd_viewportSetHeight.gml
new file mode 100644
index 00000000..72831766
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_viewportSetHeight.gml
@@ -0,0 +1,7 @@
+/// PEd_viewportSetHeight(viewport, val)
+/**
+ * @brief Sets the height of the viewport.
+ * @param {real} viewport The id of the viewport.
+ * @param {real} val The new height of the viewport.
+ */
+argument0[? "height"] = argument1;
diff --git a/PushEd.gmx/scripts/PEd_viewportSetObject.gml b/PushEd.gmx/scripts/PEd_viewportSetObject.gml
new file mode 100644
index 00000000..1516f88b
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_viewportSetObject.gml
@@ -0,0 +1,8 @@
+/// PEd_viewportSetObject(viewport, object)
+/**
+ * @brief Sets viewport to follow the given object.
+ * @param {real} viewport The id of the viewport.
+ * @param {real} object The object that the viewport will follow. Use noone if
+ * it should follow no object.
+ */
+argument0[? "object"] = argument1;
diff --git a/PushEd.gmx/scripts/PEd_viewportSetPortHeight.gml b/PushEd.gmx/scripts/PEd_viewportSetPortHeight.gml
new file mode 100644
index 00000000..97db1f0b
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_viewportSetPortHeight.gml
@@ -0,0 +1,7 @@
+/// PEd_viewportSetPortHeight(viewport, val)
+/**
+ * @brief Sets the port height of the viewport.
+ * @param {real} viewport The id of the viewport.
+ * @param {real} val The new port height of the viewport.
+ */
+argument0[? "portHeight"] = argument1;
diff --git a/PushEd.gmx/scripts/PEd_viewportSetPortWidth.gml b/PushEd.gmx/scripts/PEd_viewportSetPortWidth.gml
new file mode 100644
index 00000000..c1d3ef35
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_viewportSetPortWidth.gml
@@ -0,0 +1,7 @@
+/// PEd_viewportSetPortWidth(viewport, val)
+/**
+ * @brief Sets the port width of the viewport.
+ * @param {real} viewport The id of the viewport.
+ * @param {real} val The new port width of the viewport.
+ */
+argument0[? "portWidth"] = argument1;
diff --git a/PushEd.gmx/scripts/PEd_viewportSetPortX.gml b/PushEd.gmx/scripts/PEd_viewportSetPortX.gml
new file mode 100644
index 00000000..d2292cb3
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_viewportSetPortX.gml
@@ -0,0 +1,7 @@
+/// PEd_viewportSetPortX(viewport, val)
+/**
+ * @brief Sets the port x position of the viewport.
+ * @param {real} viewport The id of the viewport.
+ * @param {real} val The new port x position of the viewport.
+ */
+argument0[? "portX"] = argument1;
diff --git a/PushEd.gmx/scripts/PEd_viewportSetPortY.gml b/PushEd.gmx/scripts/PEd_viewportSetPortY.gml
new file mode 100644
index 00000000..135b5d6a
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_viewportSetPortY.gml
@@ -0,0 +1,7 @@
+/// PEd_viewportSetPortY(viewport, val)
+/**
+ * @brief Sets the port y position of the viewport.
+ * @param {real} viewport The id of the viewport.
+ * @param {real} val The new port y position of the viewport.
+ */
+argument0[? "portY"] = argument1;
diff --git a/PushEd.gmx/scripts/PEd_viewportSetSpeedHor.gml b/PushEd.gmx/scripts/PEd_viewportSetSpeedHor.gml
new file mode 100644
index 00000000..d69a0e31
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_viewportSetSpeedHor.gml
@@ -0,0 +1,7 @@
+/// PEd_viewportSetSpeedHor(viewport, val)
+/**
+ * @brief Sets the horizontal speed of the viewport.
+ * @param {real} viewport The id of the viewport.
+ * @param {real} val The new horizontal speed of the viewport.
+ */
+argument0[? "speedHor"] = argument1;
diff --git a/PushEd.gmx/scripts/PEd_viewportSetSpeedVer.gml b/PushEd.gmx/scripts/PEd_viewportSetSpeedVer.gml
new file mode 100644
index 00000000..9f9e53bd
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_viewportSetSpeedVer.gml
@@ -0,0 +1,7 @@
+/// PEd_viewportSetSpeedVer(viewport, val)
+/**
+ * @brief Sets the vertical speed of the viewport.
+ * @param {real} viewport The id of the viewport.
+ * @param {real} val The new vertical speed of the viewport.
+ */
+argument0[? "speedVer"] = argument1;
diff --git a/PushEd.gmx/scripts/PEd_viewportSetVisible.gml b/PushEd.gmx/scripts/PEd_viewportSetVisible.gml
new file mode 100644
index 00000000..5a268024
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_viewportSetVisible.gml
@@ -0,0 +1,7 @@
+/// PEd_viewportSetVisible(viewport, visible)
+/**
+ * @brief Sets the visibility setting of the viewport.
+ * @param {real} viewport The id of the viewport.
+ * @param {bool} visible True to make the viewport visible.
+ */
+argument0[? "visible"] = argument1;
diff --git a/PushEd.gmx/scripts/PEd_viewportSetWidth.gml b/PushEd.gmx/scripts/PEd_viewportSetWidth.gml
new file mode 100644
index 00000000..fa52833a
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_viewportSetWidth.gml
@@ -0,0 +1,7 @@
+/// PEd_viewportSetWidth(viewport, val)
+/**
+ * @brief Sets the width of the viewport.
+ * @param {real} viewport The id of the viewport.
+ * @param {real} val The new width of the viewport.
+ */
+argument0[? "width"] = argument1;
diff --git a/PushEd.gmx/scripts/PEd_viewportSetX.gml b/PushEd.gmx/scripts/PEd_viewportSetX.gml
new file mode 100644
index 00000000..1cab5746
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_viewportSetX.gml
@@ -0,0 +1,7 @@
+/// PEd_viewportSetX(viewport, val)
+/**
+ * @brief Sets the x position of the viewport.
+ * @param {real} viewport The id of the viewport.
+ * @param {real} val The new x position of the viewport.
+ */
+argument0[? "x"] = argument1;
diff --git a/PushEd.gmx/scripts/PEd_viewportSetY.gml b/PushEd.gmx/scripts/PEd_viewportSetY.gml
new file mode 100644
index 00000000..de2a8a88
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_viewportSetY.gml
@@ -0,0 +1,7 @@
+/// PEd_viewportSetY(viewport, val)
+/**
+ * @brief Sets the y position of the viewport.
+ * @param {real} viewport The id of the viewport.
+ * @param {real} val The new y position of the viewport.
+ */
+argument0[? "y"] = argument1;
diff --git a/PushEd.gmx/scripts/PEd_xmlAddChildElement.gml b/PushEd.gmx/scripts/PEd_xmlAddChildElement.gml
new file mode 100644
index 00000000..baf275c8
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_xmlAddChildElement.gml
@@ -0,0 +1,10 @@
+/// PEd_xmlAddChildElement(element, child)
+/**
+ * @brief Adds child to the given element.
+ * @param {real} element The id of the element to add the child to.
+ * @param {real} child The id of the child element.
+ */
+var _list = argument0[? "children"];
+ds_list_add(_list, argument1);
+ds_list_mark_as_map(_list, ds_list_size(_list) - 1);
+argument1[? "parent"] = argument0;
diff --git a/PushEd.gmx/scripts/PEd_xmlCreateElement.gml b/PushEd.gmx/scripts/PEd_xmlCreateElement.gml
new file mode 100644
index 00000000..3bf5a6da
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_xmlCreateElement.gml
@@ -0,0 +1,21 @@
+/// PEd_xmlCreateElement([name])
+/**
+ * @brief Creates a new element.
+ * @param {string} name The name of the element.
+ * @return {real} The id of the created element.
+ */
+var _element = ds_map_create();
+ds_map_add_map(_element, "attributes", ds_map_create());
+ds_map_add_list(_element, "children", ds_list_create());
+_element[? "parent"] = noone;
+
+if (argument_count == 1)
+{
+ _element[? "name"] = string(argument[0]);
+}
+else
+{
+ _element[? "name"] = "";
+}
+
+return _element;
diff --git a/PushEd.gmx/scripts/PEd_xmlDestroyElement.gml b/PushEd.gmx/scripts/PEd_xmlDestroyElement.gml
new file mode 100644
index 00000000..81e7bfcc
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_xmlDestroyElement.gml
@@ -0,0 +1,18 @@
+/// PEd_xmlDestroyElement(element)
+/**
+ * @brief Destroys the element and all its children.
+ * @param {real} element The id of the element to be destroyed.
+ */
+var _element = argument0;
+var _parent = _element[? "parent"];
+
+if (_parent != noone)
+{
+ var _index = ds_list_find_index(_parent[? "children"], _element);
+ if (_index >= 0)
+ {
+ ds_list_delete(_parent[? "children"], _index);
+ }
+}
+
+ds_map_destroy(_element);
diff --git a/PushEd.gmx/scripts/PEd_xmlElementHasAttribute.gml b/PushEd.gmx/scripts/PEd_xmlElementHasAttribute.gml
new file mode 100644
index 00000000..b64f395d
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_xmlElementHasAttribute.gml
@@ -0,0 +1,9 @@
+/// PEd_xmlElementHasAttribute(element, attName)
+/**
+ * @brief Finds the attribute with given name in the element.
+ * @param {real} element The id of the element.
+ * @param {string} attName The name of the attribute.
+ * @return {boolean} True if the element has an attribute with given name.
+ */
+gml_pragma("forceinline");
+return ds_map_exists(argument0[? "attributes"], argument1);
diff --git a/PushEd.gmx/scripts/PEd_xmlElementHasValue.gml b/PushEd.gmx/scripts/PEd_xmlElementHasValue.gml
new file mode 100644
index 00000000..642a293d
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_xmlElementHasValue.gml
@@ -0,0 +1,8 @@
+/// PEd_xmlElementHasValue(element)
+/**
+ * @brief Finds out if the given element has a value.
+ * @param {real} element The id of the element.
+ * @return {boolean} True if the element has a value.
+ */
+gml_pragma("forceinline");
+return ds_map_exists(argument0, "value");
diff --git a/PushEd.gmx/scripts/PEd_xmlFindAllElements.gml b/PushEd.gmx/scripts/PEd_xmlFindAllElements.gml
new file mode 100644
index 00000000..6a3d6b02
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_xmlFindAllElements.gml
@@ -0,0 +1,32 @@
+/// PEd_xmlFindAllElements(rootElement, name)
+/**
+ * @brief Finds all elements with given name in the given tree of elements.
+ * @param {real} rootElement The root element of the tree.
+ * @param {string} name The name of elements to be found.
+ * @return {real} A ds_list containing all found elements.
+ */
+var _element = argument[0];
+var _name = argument[1];
+var _list = noone;
+
+if (argument_count == 2)
+{
+ _list = ds_list_create();
+}
+else
+{
+ _list = argument[2];
+}
+
+if (PEd_xmlGetElementName(_element) == _name)
+{
+ ds_list_add(_list, _element);
+}
+
+var _numOfChildElements = PEd_xmlGetNumberOfChildElements(_element);
+for (var i = 0; i < _numOfChildElements; i++)
+{
+ PEd_xmlFindAllElements(PEd_xmlGetChildElement(_element, i), _name, _list);
+}
+
+return _list;
diff --git a/PushEd.gmx/scripts/PEd_xmlFindElement.gml b/PushEd.gmx/scripts/PEd_xmlFindElement.gml
new file mode 100644
index 00000000..2a67d1e9
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_xmlFindElement.gml
@@ -0,0 +1,28 @@
+/// PEd_xmlFindElement(rootElement, name)
+/**
+ * @brief Finds the first element with given name
+ * in the given tree of elements.
+ * @param {real} rootElement The root element of the tree.
+ * @param {string} name The name of the element to be found.
+ * @return {real} The id of the found element or noone,
+ * if no such element has been found.
+ */
+var _element = argument0;
+var _name = argument1;
+
+if (PEd_xmlGetElementName(_element) == _name)
+{
+ return _element;
+}
+
+var _numOfChildElements = PEd_xmlGetNumberOfChildElements(_element);
+for (var i = 0; i < _numOfChildElements; i++)
+{
+ var _foundElement = PEd_xmlFindElement(PEd_xmlGetChildElement(_element, i), _name);
+ if (_foundElement != noone)
+ {
+ return _foundElement;
+ }
+}
+
+return noone;
diff --git a/PushEd.gmx/scripts/PEd_xmlFindFirstElementAttribute.gml b/PushEd.gmx/scripts/PEd_xmlFindFirstElementAttribute.gml
new file mode 100644
index 00000000..ba3f0b51
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_xmlFindFirstElementAttribute.gml
@@ -0,0 +1,9 @@
+/// PEd_xmlFindFirstElementAttribute(element)
+/**
+ * @brief Finds the first attribute of the given element.
+ * @param {real} element The id of the element.
+ * @return {string/undefiend} The name of the first attribute or undefined,
+ * if the element does not have any.
+ */
+gml_pragma("forceinline");
+return ds_map_find_first(argument0[? "attributes"]);
diff --git a/PushEd.gmx/scripts/PEd_xmlFindNextElementAttribute.gml b/PushEd.gmx/scripts/PEd_xmlFindNextElementAttribute.gml
new file mode 100644
index 00000000..018ba4a4
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_xmlFindNextElementAttribute.gml
@@ -0,0 +1,10 @@
+/// PEd_xmlFindNextElementAttribute(element, attribute)
+/**
+ * @brief Finds next element attribute.
+ * @param {real} element The id of the element.
+ * @param {string} attribute Name of the current atribute.
+ * @return {string/undefined} Name of the next attribute or undefined,
+ * if the element does not have more attributes.
+ */
+gml_pragma("forceinline");
+return ds_map_find_next(argument0[? "attributes"], argument1);
diff --git a/PushEd.gmx/scripts/PEd_xmlGetChildElement.gml b/PushEd.gmx/scripts/PEd_xmlGetChildElement.gml
new file mode 100644
index 00000000..72d9420f
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_xmlGetChildElement.gml
@@ -0,0 +1,9 @@
+/// PEd_xmlGetChildElement(element, n)
+/**
+ * @brief Gets n-th child of the given element.
+ * @param {real} element The id of the element.
+ * @param {real} n The index (0...numberOfChildren - 1) of the child element.
+ * @return {real} The id of the n-th child of the given element.
+ */
+gml_pragma("forceinline");
+return ds_list_find_value(argument0[? "children"], argument1);
diff --git a/PushEd.gmx/scripts/PEd_xmlGetElementAttribute.gml b/PushEd.gmx/scripts/PEd_xmlGetElementAttribute.gml
new file mode 100644
index 00000000..42931dcc
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_xmlGetElementAttribute.gml
@@ -0,0 +1,9 @@
+/// PEd_xmlGetElementAttribute(element, attribute)
+/**
+ * @brief Gets value of the element attribute.
+ * @param {real} element The id of the element.
+ * @param {string} attribute The name of the attribute.
+ * @return {real/string/undefiend} The attribute value.
+ */
+gml_pragma("forceinline");
+return ds_map_find_value(argument0[? "attributes"], argument1);
diff --git a/PushEd.gmx/scripts/PEd_xmlGetElementName.gml b/PushEd.gmx/scripts/PEd_xmlGetElementName.gml
new file mode 100644
index 00000000..e1697022
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_xmlGetElementName.gml
@@ -0,0 +1,8 @@
+/// PEd_xmlGetElementName(element)
+/**
+ * @brief Gets the name of the element.
+ * @param {real} element The id of the element.
+ * @return {string} The name of the element.
+ */
+gml_pragma("forceinline");
+return argument0[? "name"];
diff --git a/PushEd.gmx/scripts/PEd_xmlGetElementParent.gml b/PushEd.gmx/scripts/PEd_xmlGetElementParent.gml
new file mode 100644
index 00000000..ce43b41b
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_xmlGetElementParent.gml
@@ -0,0 +1,9 @@
+/// PEd_xmlGetElementParent(element)
+/**
+ * @brief Gets the parent of the given element.
+ * @param {real} element The id of the element.
+ * @return {real} The parent of the element or noone,
+ * if the element does not have a parent.
+ */
+gml_pragma("forceinline");
+return argument0[? "parent"];
diff --git a/PushEd.gmx/scripts/PEd_xmlGetElementValue.gml b/PushEd.gmx/scripts/PEd_xmlGetElementValue.gml
new file mode 100644
index 00000000..b7cf0118
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_xmlGetElementValue.gml
@@ -0,0 +1,9 @@
+/// PEd_xmlGetElementValue(element)
+/**
+ * @brief Gets the value of the given element.
+ * @param {real} element The id of the element.
+ * @return {real/string/undefined} The element value or undefined,
+ * if the element does not have any value.
+ */
+gml_pragma("forceinline");
+return argument0[? "value"];
diff --git a/PushEd.gmx/scripts/PEd_xmlGetNumberOfChildElements.gml b/PushEd.gmx/scripts/PEd_xmlGetNumberOfChildElements.gml
new file mode 100644
index 00000000..c8c12678
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_xmlGetNumberOfChildElements.gml
@@ -0,0 +1,8 @@
+/// PEd_xmlGetNumberOfChildElements(element)
+/**
+ * @brief Gets number of children of the given element.
+ * @param {real} element The id of the element.
+ * @return {real} Number of children of the given element.
+ */
+gml_pragma("forceinline");
+return ds_list_size(argument0[? "children"]);
diff --git a/PushEd.gmx/scripts/PEd_xmlGetNumberOfElementAttributes.gml b/PushEd.gmx/scripts/PEd_xmlGetNumberOfElementAttributes.gml
new file mode 100644
index 00000000..f221b380
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_xmlGetNumberOfElementAttributes.gml
@@ -0,0 +1,8 @@
+/// PEd_xmlGetNumberOfElementAttributes(element)
+/**
+ * @brief Gets the number of attributes of the given element.
+ * @param {real} element The id of the element.
+ * @return {real} The number of attributes of the given element.
+ */
+gml_pragma("forceinline");
+return ds_map_size(argument0[? "attributes"]);
diff --git a/PushEd.gmx/scripts/PEd_xmlInit.gml b/PushEd.gmx/scripts/PEd_xmlInit.gml
new file mode 100644
index 00000000..bc4aac00
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_xmlInit.gml
@@ -0,0 +1,20 @@
+/// PEd_xmlInit()
+/**
+ * @brief Initializes XML reader/writer.
+ */
+enum PEdXml
+{
+ Space = 32,
+ Lt = 60,
+ Gt = 62,
+ Equal = 61,
+ Slash = 47,
+ Qm = 63,
+ Em = 33,
+ Sq = 39,
+ Dq = 34,
+ Cr = 13,
+ Null = 0,
+ Lf = 10,
+ MaximumDecimalSpaces = 16
+};
diff --git a/PushEd.gmx/scripts/PEd_xmlParse.gml b/PushEd.gmx/scripts/PEd_xmlParse.gml
new file mode 100644
index 00000000..15c7bf72
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_xmlParse.gml
@@ -0,0 +1,68 @@
+/// PEd_xmlParse(string)
+/**
+ * @brief Parses value from the string.
+ * @param {string} string The string to parse.
+ * @return {real/string} A real value or a string, where XML character entities are
+ * replaced with their original form.
+ */
+var _string = argument0;
+
+//
+// Clear whitespace, replace character entities
+//
+while (string_byte_at(_string, 1) == 32)
+{
+ _string = string_delete(_string, 1, 1);
+}
+
+_string = string_replace_all(_string, "<", "<");
+_string = string_replace_all(_string, ">", ">");
+_string = string_replace_all(_string, """, '"');
+_string = string_replace_all(_string, "'", "'");
+_string = string_replace_all(_string, "&", "&");
+
+var _stringLength = string_length(_string);
+
+//
+// Parse real
+//
+var _a;
+_a[1] = "";
+_a[0] = "0";
+
+var _j = 0;
+var _s = 1;
+var _char = "";
+
+for (var i = 1; i <= _stringLength; i++)
+{
+ _char = string_char_at(_string, i);
+
+ if (_char == string_digits(_char))
+ {
+ _a[_j] += _char;
+ }
+ else if (_char == '+'
+ && _a[0] == "0"
+ && _j == 0)
+ {
+ // Do nothing...
+ }
+ else if (_char == '-'
+ && _a[0] == "0"
+ && _j == 0)
+ {
+ _s = -_s;
+ }
+ else if (_char == '.'
+ && _j == 0)
+ {
+ _j++;
+ }
+ else if (_char != "")
+ {
+ return _string; // String does not represent a real value...
+ }
+}
+
+return (_s * abs(real(_a[0]) + real(_a[1]) / power(10, string_length(_a[1]))));
diff --git a/PushEd.gmx/scripts/PEd_xmlRead.gml b/PushEd.gmx/scripts/PEd_xmlRead.gml
new file mode 100644
index 00000000..4223566a
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_xmlRead.gml
@@ -0,0 +1,239 @@
+/// PEd_xmlRead(fileName)
+/**
+ * @brief Reads the XML formatted file and stores
+ * the contained data into a tree of elements.
+ * @param {string} fileName The name of the XML formatted file.
+ * @return {real} The root element of the tree on success or noone on fail.
+ */
+var _file = file_bin_open(argument0, 0);
+
+if (_file == -1)
+ return noone;
+
+var _filePos = 0;
+var _fileSize = file_bin_size(_file);
+var _byte = PEdXml.Space;
+var _lastByte;
+var _isSeparator = true;
+var _token = "";
+var _isString = false;
+var _attributeName = "";
+var _root = noone;
+var _element = noone;
+var _lastElement = noone;
+var _parentElement = noone;
+var _isClosing = false;
+var _isComment = false;
+
+do
+{
+ //
+ // Read byte from file
+ //
+ _lastByte = _byte;
+ _byte = file_bin_read_byte(_file);
+
+ //
+ // Process byte
+ //
+ _isSeparator = true;
+
+ switch (_byte)
+ {
+ // Start of new element
+ case PEdXml.Lt:
+ if (_element != noone)
+ {
+ if (_root != noone)
+ {
+ PEd_xmlDestroyElement(_root);
+ }
+ show_debug_message("ERROR: Unexpected symbol '<' at " + string(_filePos) + "!");
+ return noone;
+ }
+
+ // Set element value
+ while (string_byte_at(_token, 1) == 32)
+ {
+ _token = string_delete(_token, 1, 1);
+ }
+
+ if (_token != ""
+ && _parentElement != noone
+ && PEd_xmlGetNumberOfChildElements(_parentElement) == 0)
+ {
+ PEd_xmlSetElementValue(_parentElement, PEd_xmlParse(_token));
+ }
+
+ _element = PEd_xmlCreateElement();
+ break;
+
+ // End of element
+ case PEdXml.Gt:
+ if (_element == noone)
+ {
+ if (_root != noone)
+ {
+ PEd_xmlDestroyElement(_root);
+ }
+ show_debug_message("ERROR: Unexpected symbol '>' at " + string(_filePos) + "!");
+ return noone;
+ }
+
+ _lastElement = _element;
+
+ if (_root == noone && !_isComment)
+ {
+ _root = _element;
+ }
+
+ if (_isComment)
+ {
+ _lastElement = noone;
+ ds_map_destroy(_element);
+ _isComment = false;
+ }
+ else if (_lastByte == PEdXml.Slash)
+ {
+ // Self-closing element
+ if (_parentElement != noone)
+ {
+ PEd_xmlAddChildElement(_parentElement, _element);
+ }
+ }
+ else if (_isClosing)
+ {
+ // If the element is not self-closing and it does not
+ // have a value defined, then set its value to an empty string.
+ if (is_undefined(PEd_xmlGetElementValue(_parentElement))
+ && PEd_xmlGetNumberOfChildElements(_parentElement) == 0)
+ {
+ PEd_xmlSetElementValue(_parentElement, "");
+ }
+ _parentElement = PEd_xmlGetElementParent(_parentElement);
+ _lastElement = noone;
+ ds_map_destroy(_element);
+ _isClosing = false;
+ }
+ else
+ {
+ if (_parentElement != noone)
+ {
+ PEd_xmlAddChildElement(_parentElement, _element);
+ }
+ _parentElement = _element;
+ }
+ _element = noone;
+ _elementName = "";
+ break;
+
+ // Closing element
+ case PEdXml.Slash:
+ if (_isString || _element == noone)
+ {
+ _isSeparator = false;
+ }
+ else if (_lastByte == PEdXml.Lt)
+ {
+ _isClosing = true;
+ }
+ break;
+
+ // Attribute
+ case PEdXml.Equal:
+ if (!_isString)
+ {
+ if (_token != "")
+ {
+ _attributeName = _token;
+ }
+ }
+ else
+ {
+ _isSeparator = false;
+ }
+ break;
+
+ // Start/end of string
+ case PEdXml.Sq:
+ case PEdXml.Dq:
+ if (_isString == _byte)
+ {
+ _isString = false;
+ // Store attribute
+ if (_attributeName != "")
+ {
+ if (_element != noone)
+ {
+ PEd_xmlSetElementAttribute(_element, _attributeName, PEd_xmlParse(_token));
+ }
+ _attributeName = "";
+ }
+ }
+ else if (!_isString)
+ {
+ _isString = _byte;
+ }
+ break;
+
+ // Treat as comments
+ case PEdXml.Qm:
+ case PEdXml.Em:
+ if (_lastByte == PEdXml.Lt)
+ {
+ _isComment = true;
+ }
+ break;
+
+ default:
+ // Whitespace
+ if (!_isString && _element != noone
+ && ((_byte > PEdXml.Null && _byte <= PEdXml.Cr)
+ || _byte == PEdXml.Space))
+ {
+ // ...
+ }
+ else
+ {
+ _isSeparator = false;
+ }
+ break;
+ }
+
+ //
+ // Process tokens
+ //
+ if (_isSeparator)
+ {
+ // End of token
+ if (_token != "")
+ {
+ // Set element name
+ if (_element != noone
+ && PEd_xmlGetElementName(_element) == "")
+ {
+ PEd_xmlSetElementName(_element, _token);
+ }
+ else if (_lastElement != noone
+ && PEd_xmlGetElementName(_lastElement) == "")
+ {
+ PEd_xmlSetElementName(_lastElement, _token);
+ }
+ _token = "";
+ }
+ }
+ else
+ {
+ // Build token
+ if (_byte > PEdXml.Null && _byte <= PEdXml.Cr)
+ {
+ _byte = PEdXml.Space; // Replace new lines, tabs, etc. with spaces
+ }
+ _token += chr(_byte);
+ }
+}
+until (++_filePos == _fileSize);
+
+file_bin_close(_file);
+
+return _root;
diff --git a/PushEd.gmx/scripts/PEd_xmlRemoveElementAttribute.gml b/PushEd.gmx/scripts/PEd_xmlRemoveElementAttribute.gml
new file mode 100644
index 00000000..715fee15
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_xmlRemoveElementAttribute.gml
@@ -0,0 +1,11 @@
+/// PEd_xmlRemoveElementAttribute(element, attribute)
+/**
+ * @brief Removes given attribute from the element.
+ * @param {real} element The id of the element.
+ * @param {string} attribute The name of the attribute.
+ */
+var _attributes = argument0[? "attributes"];
+if (ds_map_exists(_attributes, argument1))
+{
+ ds_map_delete(_attributes, argument1);
+}
diff --git a/PushEd.gmx/scripts/PEd_xmlSetElementAttribute.gml b/PushEd.gmx/scripts/PEd_xmlSetElementAttribute.gml
new file mode 100644
index 00000000..74d875df
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_xmlSetElementAttribute.gml
@@ -0,0 +1,11 @@
+/// PEd_xmlSetElementAttribute(element, attribute, value)
+/**
+ * @brief Sets value of the given element attribute to the given value.
+ * @param {real} element The id of the element.
+ * @param {string} attribute The name of the attribute.
+ * @param {real/string/undefined} value The value of the attribute.
+ * @return {real} The id of the element.
+ */
+var _attributes = argument0[? "attributes"];
+_attributes[? string(argument1)] = argument2;
+return argument0;
diff --git a/PushEd.gmx/scripts/PEd_xmlSetElementName.gml b/PushEd.gmx/scripts/PEd_xmlSetElementName.gml
new file mode 100644
index 00000000..015fadf8
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_xmlSetElementName.gml
@@ -0,0 +1,9 @@
+/// PEd_xmlSetElementName(element, name)
+/**
+ * @brief Sets name of the element to the given value.
+ * @param {real} element The id of the element.
+ * @param {string} name The new name of the element.
+ * @return {real} The id of the element.
+ */
+argument0[? "name"] = string(argument1);
+return argument0;
diff --git a/PushEd.gmx/scripts/PEd_xmlSetElementValue.gml b/PushEd.gmx/scripts/PEd_xmlSetElementValue.gml
new file mode 100644
index 00000000..42b4e696
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_xmlSetElementValue.gml
@@ -0,0 +1,10 @@
+/// PEd_xmlSetElementValue(element, value)
+/**
+ * @brief Sets value of the element to the given value.
+ * @param {real} element The id of the element.
+ * @param {real/string/undefined} value The new element value.
+ * @return {real} The id of the element.
+ */
+gml_pragma("forceinline");
+argument0[? "value"] = argument1;
+return argument0;
diff --git a/PushEd.gmx/scripts/PEd_xmlString.gml b/PushEd.gmx/scripts/PEd_xmlString.gml
new file mode 100644
index 00000000..2c575163
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_xmlString.gml
@@ -0,0 +1,38 @@
+/// PEd_xmlString(value)
+/**
+ * @brief Turns given value into a string. Replaces
+ * characters with their XML-safe form.
+ * @param {any} value The value to be turned into a string.
+ * @return {string} The resulting string.
+ */
+var _value = argument0;
+
+if (is_real(_value))
+{
+ var _dec = PEdXml.MaximumDecimalSpaces;
+ var _string = string_format(_value, -1, _dec);
+ var _stringLength = string_length(_string);
+
+ do
+ {
+ _string = string_format(_value, -1, --_dec);
+ if (string_byte_at(_string, --_stringLength) != 48)
+ {
+ break;
+ }
+ }
+ until (_dec == 0);
+
+ return _string;
+}
+
+if (is_string(_value))
+{
+ _value = string_replace_all(_value, "&", "&");
+ _value = string_replace_all(_value, "<", "<");
+ _value = string_replace_all(_value, ">", ">");
+ _value = string_replace_all(_value, '"', """);
+ _value = string_replace_all(_value, "'", "'");
+}
+
+return string(_value);
diff --git a/PushEd.gmx/scripts/PEd_xmlWrite.gml b/PushEd.gmx/scripts/PEd_xmlWrite.gml
new file mode 100644
index 00000000..38cebd74
--- /dev/null
+++ b/PushEd.gmx/scripts/PEd_xmlWrite.gml
@@ -0,0 +1,73 @@
+/// PEd_xmlWrite(rootElement)
+/**
+ * @brief Writes the tree of elements into a string.
+ * @param {real} rootElement The root element of the tree.
+ * @return {string} The string.
+ */
+var _element = argument[0];
+var _name = PEd_xmlGetElementName(_element);
+var _numberOfAttributes = PEd_xmlGetNumberOfElementAttributes(_element);
+var _numberOfChildElements = PEd_xmlGetNumberOfChildElements(_element);
+var _value = PEd_xmlGetElementValue(_element);
+var _indent = 0;
+
+if (argument_count == 2)
+{
+ _indent = argument[1];
+}
+
+var _spaces = string_repeat(" ", _indent * 2);
+
+//
+// Open element
+//
+var _string = _spaces + "<" + _name;
+
+// Attributes
+var _attribute = PEd_xmlFindFirstElementAttribute(_element);
+
+repeat (_numberOfAttributes)
+{
+ _string += " " + string(_attribute) + '="'
+ + PEd_xmlString(PEd_xmlGetElementAttribute(_element, _attribute))
+ + '"';
+ _attribute = PEd_xmlFindNextElementAttribute(_element, _attribute);
+}
+
+if (_numberOfChildElements == 0
+ && is_undefined(_value))
+{
+ _string += "/";
+}
+
+_string += ">";
+
+if (_numberOfChildElements != 0
+ || is_undefined(_value))
+{
+ _string += chr(10);
+}
+
+//
+// Children
+//
+for (var i = 0; i < _numberOfChildElements; i++)
+{
+ var _childElement = PEd_xmlGetChildElement(_element, i);
+ _string += PEd_xmlWrite(_childElement, _indent + 1);
+}
+
+//
+// Close element
+//
+if (_numberOfChildElements != 0)
+{
+ _string += _spaces + "" + PEd_xmlGetElementName(_element) + ">" + chr(10);
+}
+else if (!is_undefined(_value))
+{
+ _string += PEd_xmlString(_value);
+ _string += "" + PEd_xmlGetElementName(_element) + ">" + chr(10);
+}
+
+return _string;
diff --git a/PushEd.gmx/shaders/PEd_shInstHighlight.shader b/PushEd.gmx/shaders/PEd_shInstHighlight.shader
new file mode 100644
index 00000000..534d61b2
--- /dev/null
+++ b/PushEd.gmx/shaders/PEd_shInstHighlight.shader
@@ -0,0 +1,16 @@
+attribute vec4 in_Position;
+attribute vec2 in_TextureCoord;
+
+varying vec2 vTexCoord;
+
+void main()
+{
+ gl_Position = gm_Matrices[MATRIX_WORLD_VIEW_PROJECTION] * in_Position;
+ vTexCoord = in_TextureCoord;
+}
+//######################_==_YOYO_SHADER_MARKER_==_######################@~varying vec2 vTexCoord;
+
+void main()
+{
+ gl_FragColor = vec4(vec3(1.0), texture2D(gm_BaseTexture, vTexCoord).a);
+}
diff --git a/PushEd.gmx/shaders/PEd_shInstSelect.shader b/PushEd.gmx/shaders/PEd_shInstSelect.shader
new file mode 100644
index 00000000..1fb94813
--- /dev/null
+++ b/PushEd.gmx/shaders/PEd_shInstSelect.shader
@@ -0,0 +1,18 @@
+attribute vec4 in_Position;
+attribute vec2 in_TextureCoord;
+
+varying vec2 vTexCoord;
+
+void main()
+{
+ gl_Position = gm_Matrices[MATRIX_WORLD_VIEW_PROJECTION] * in_Position;
+ vTexCoord = in_TextureCoord;
+}
+//######################_==_YOYO_SHADER_MARKER_==_######################@~uniform vec3 uColour;
+
+varying vec2 vTexCoord;
+
+void main()
+{
+ gl_FragColor = vec4(uColour, texture2D(gm_BaseTexture, vTexCoord).a);
+}
diff --git a/PushEd.gmx/shaders/PEd_shOutline.shader b/PushEd.gmx/shaders/PEd_shOutline.shader
new file mode 100644
index 00000000..a3c6ce72
--- /dev/null
+++ b/PushEd.gmx/shaders/PEd_shOutline.shader
@@ -0,0 +1,27 @@
+attribute vec4 in_Position;
+attribute vec2 in_TextureCoord;
+
+varying vec2 vTexCoord;
+
+void main()
+{
+ gl_Position = gm_Matrices[MATRIX_WORLD_VIEW_PROJECTION] * in_Position;
+ vTexCoord = in_TextureCoord;
+}
+//######################_==_YOYO_SHADER_MARKER_==_######################@~#define BORDER 3
+
+varying vec2 vTexCoord;
+
+uniform vec2 uTexel;
+
+void main()
+{
+ vec2 texCoord = vTexCoord + vec2(0.5) * uTexel;
+ float origin = texture2D(gm_BaseTexture, texCoord).a;
+ float sample = 0.0;
+ sample += texture2D(gm_BaseTexture, texCoord + vec2(-BORDER, 0.0) * uTexel).a - origin;
+ sample += texture2D(gm_BaseTexture, texCoord + vec2(0.0, -BORDER) * uTexel).a - origin;
+ sample += texture2D(gm_BaseTexture, texCoord + vec2(+BORDER, 0.0) * uTexel).a - origin;
+ sample += texture2D(gm_BaseTexture, texCoord + vec2(0.0, +BORDER) * uTexel).a - origin;
+ gl_FragColor = vec4(0.0, 0.5, 1.0, sample);
+}
diff --git a/PushEd.gmx/sprites/PEd_guiSprCheckbox.sprite.gmx b/PushEd.gmx/sprites/PEd_guiSprCheckbox.sprite.gmx
new file mode 100644
index 00000000..1b0e6034
--- /dev/null
+++ b/PushEd.gmx/sprites/PEd_guiSprCheckbox.sprite.gmx
@@ -0,0 +1,26 @@
+
+
+ 0
+ 0
+ 0
+ 1
+ 0
+ 0
+ 0
+ 0
+ 17
+ 0
+ 17
+ 0
+ 0
+
+ 0
+
+ 0
+ 18
+ 18
+
+ images\PEd_guiSprCheckbox_0.png
+ images\PEd_guiSprCheckbox_1.png
+
+
diff --git a/PushEd.gmx/sprites/PEd_guiSprIcons.sprite.gmx b/PushEd.gmx/sprites/PEd_guiSprIcons.sprite.gmx
new file mode 100644
index 00000000..bbaa348c
--- /dev/null
+++ b/PushEd.gmx/sprites/PEd_guiSprIcons.sprite.gmx
@@ -0,0 +1,40 @@
+
+
+ 0
+ 0
+ 0
+ 1
+ 0
+ 0
+ 0
+ 0
+ 41
+ 3
+ 41
+ 0
+ 0
+
+ 0
+
+ 0
+ 42
+ 42
+
+ images\PEd_guiSprIcons_0.png
+ images\PEd_guiSprIcons_1.png
+ images\PEd_guiSprIcons_2.png
+ images\PEd_guiSprIcons_3.png
+ images\PEd_guiSprIcons_4.png
+ images\PEd_guiSprIcons_5.png
+ images\PEd_guiSprIcons_6.png
+ images\PEd_guiSprIcons_7.png
+ images\PEd_guiSprIcons_8.png
+ images\PEd_guiSprIcons_9.png
+ images\PEd_guiSprIcons_10.png
+ images\PEd_guiSprIcons_11.png
+ images\PEd_guiSprIcons_12.png
+ images\PEd_guiSprIcons_13.png
+ images\PEd_guiSprIcons_14.png
+ images\PEd_guiSprIcons_15.png
+
+
diff --git a/PushEd.gmx/sprites/PEd_guiSprInput.sprite.gmx b/PushEd.gmx/sprites/PEd_guiSprInput.sprite.gmx
new file mode 100644
index 00000000..cb75c6a9
--- /dev/null
+++ b/PushEd.gmx/sprites/PEd_guiSprInput.sprite.gmx
@@ -0,0 +1,27 @@
+
+
+ 0
+ 0
+ 0
+ 1
+ 0
+ 0
+ 0
+ 0
+ 3
+ 0
+ 17
+ 0
+ 0
+
+ 0
+
+ 0
+ 4
+ 18
+
+ images\PEd_guiSprInput_0.png
+ images\PEd_guiSprInput_1.png
+ images\PEd_guiSprInput_2.png
+
+
diff --git a/PushEd.gmx/sprites/PEd_guiSprMisc.sprite.gmx b/PushEd.gmx/sprites/PEd_guiSprMisc.sprite.gmx
new file mode 100644
index 00000000..def8b30c
--- /dev/null
+++ b/PushEd.gmx/sprites/PEd_guiSprMisc.sprite.gmx
@@ -0,0 +1,29 @@
+
+
+ 0
+ 0
+ 0
+ 1
+ 0
+ 0
+ 0
+ 0
+ 14
+ 2
+ 15
+ 0
+ 0
+
+ 0
+
+ 0
+ 15
+ 18
+
+ images\PEd_guiSprMisc_0.png
+ images\PEd_guiSprMisc_1.png
+ images\PEd_guiSprMisc_2.png
+ images\PEd_guiSprMisc_3.png
+ images\PEd_guiSprMisc_4.png
+
+
diff --git a/PushEd.gmx/sprites/PEd_guiSprPanel.sprite.gmx b/PushEd.gmx/sprites/PEd_guiSprPanel.sprite.gmx
new file mode 100644
index 00000000..879ee6bb
--- /dev/null
+++ b/PushEd.gmx/sprites/PEd_guiSprPanel.sprite.gmx
@@ -0,0 +1,25 @@
+
+
+ 0
+ 0
+ 0
+ 1
+ 0
+ 0
+ 0
+ 0
+ 17
+ 1
+ 17
+ 0
+ 0
+
+ 0
+
+ 0
+ 18
+ 18
+
+ images\PEd_guiSprPanel_0.png
+
+
diff --git a/PushEd.gmx/sprites/PEd_guiSprRectangle.sprite.gmx b/PushEd.gmx/sprites/PEd_guiSprRectangle.sprite.gmx
new file mode 100644
index 00000000..12ec3c2c
--- /dev/null
+++ b/PushEd.gmx/sprites/PEd_guiSprRectangle.sprite.gmx
@@ -0,0 +1,25 @@
+
+
+ 0
+ 0
+ 0
+ 1
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+
+ 0
+
+ 0
+ 1
+ 1
+
+ images\PEd_guiSprRectangle_0.png
+
+
diff --git a/PushEd.gmx/sprites/PEd_guiSprRoll.sprite.gmx b/PushEd.gmx/sprites/PEd_guiSprRoll.sprite.gmx
new file mode 100644
index 00000000..7d7aed4e
--- /dev/null
+++ b/PushEd.gmx/sprites/PEd_guiSprRoll.sprite.gmx
@@ -0,0 +1,26 @@
+
+
+ 0
+ 2
+ 0
+ 1
+ 0
+ 0
+ 0
+ 0
+ 9
+ 3
+ 14
+ 0
+ 0
+
+ 0
+
+ 0
+ 10
+ 18
+
+ images\PEd_guiSprRoll_0.png
+ images\PEd_guiSprRoll_1.png
+
+
diff --git a/PushEd.gmx/sprites/PEd_guiSprScrollbarHor.sprite.gmx b/PushEd.gmx/sprites/PEd_guiSprScrollbarHor.sprite.gmx
new file mode 100644
index 00000000..47e0a192
--- /dev/null
+++ b/PushEd.gmx/sprites/PEd_guiSprScrollbarHor.sprite.gmx
@@ -0,0 +1,27 @@
+
+
+ 0
+ 0
+ 0
+ 1
+ 0
+ 0
+ 0
+ 0
+ 3
+ 0
+ 7
+ 0
+ 0
+
+ 0
+
+ 0
+ 4
+ 8
+
+ images\PEd_guiSprScrollbarHor_0.png
+ images\PEd_guiSprScrollbarHor_1.png
+ images\PEd_guiSprScrollbarHor_2.png
+
+
diff --git a/PushEd.gmx/sprites/PEd_guiSprScrollbarVer.sprite.gmx b/PushEd.gmx/sprites/PEd_guiSprScrollbarVer.sprite.gmx
new file mode 100644
index 00000000..0ebd7cd9
--- /dev/null
+++ b/PushEd.gmx/sprites/PEd_guiSprScrollbarVer.sprite.gmx
@@ -0,0 +1,27 @@
+
+
+ 0
+ 0
+ 0
+ 1
+ 0
+ 0
+ 0
+ 0
+ 7
+ 0
+ 3
+ 0
+ 0
+
+ 0
+
+ 0
+ 8
+ 4
+
+ images\PEd_guiSprScrollbarVer_0.png
+ images\PEd_guiSprScrollbarVer_1.png
+ images\PEd_guiSprScrollbarVer_2.png
+
+
diff --git a/PushEd.gmx/sprites/PEd_guiSprShadow.sprite.gmx b/PushEd.gmx/sprites/PEd_guiSprShadow.sprite.gmx
new file mode 100644
index 00000000..ae28806c
--- /dev/null
+++ b/PushEd.gmx/sprites/PEd_guiSprShadow.sprite.gmx
@@ -0,0 +1,27 @@
+
+
+ 0
+ 0
+ 0
+ 1
+ 0
+ 0
+ 0
+ 0
+ 31
+ 0
+ 31
+ 0
+ 0
+
+ 0
+
+ 0
+ 32
+ 32
+
+ images\PEd_guiSprShadow_0.png
+ images\PEd_guiSprShadow_1.png
+ images\PEd_guiSprShadow_2.png
+
+
diff --git a/PushEd.gmx/sprites/PEd_guiSprWarning.sprite.gmx b/PushEd.gmx/sprites/PEd_guiSprWarning.sprite.gmx
new file mode 100644
index 00000000..5f6055cf
--- /dev/null
+++ b/PushEd.gmx/sprites/PEd_guiSprWarning.sprite.gmx
@@ -0,0 +1,25 @@
+
+
+ 0
+ 0
+ 32
+ 1
+ 0
+ 0
+ 0
+ 0
+ 63
+ 0
+ 63
+ 0
+ 0
+
+ 0
+
+ 0
+ 64
+ 64
+
+ images\PEd_guiSprWarning_0.png
+
+
diff --git a/PushEd.gmx/sprites/PEd_guiSprWindowCross.sprite.gmx b/PushEd.gmx/sprites/PEd_guiSprWindowCross.sprite.gmx
new file mode 100644
index 00000000..f463d772
--- /dev/null
+++ b/PushEd.gmx/sprites/PEd_guiSprWindowCross.sprite.gmx
@@ -0,0 +1,25 @@
+
+
+ 0
+ 0
+ 0
+ 1
+ 0
+ 0
+ 0
+ 0
+ 17
+ 0
+ 11
+ 0
+ 0
+
+ 0
+
+ 0
+ 18
+ 12
+
+ images\PEd_guiSprWindowCross_0.png
+
+
diff --git a/PushEd.gmx/sprites/PEd_sprDummy2D.sprite.gmx b/PushEd.gmx/sprites/PEd_sprDummy2D.sprite.gmx
new file mode 100644
index 00000000..960a892e
--- /dev/null
+++ b/PushEd.gmx/sprites/PEd_sprDummy2D.sprite.gmx
@@ -0,0 +1,25 @@
+
+
+ 0
+ 12
+ 12
+ 1
+ 0
+ 0
+ 0
+ 0
+ 23
+ 0
+ 23
+ 0
+ 0
+
+ 0
+
+ 0
+ 24
+ 24
+
+ images\PEd_sprDummy2D_0.png
+
+
diff --git a/PushEd.gmx/sprites/PEd_sprTest.sprite.gmx b/PushEd.gmx/sprites/PEd_sprTest.sprite.gmx
new file mode 100644
index 00000000..1a71d70d
--- /dev/null
+++ b/PushEd.gmx/sprites/PEd_sprTest.sprite.gmx
@@ -0,0 +1,25 @@
+
+
+ 0
+ 32
+ 32
+ 1
+ 0
+ 0
+ 0
+ 1
+ 61
+ 1
+ 61
+ 0
+ 0
+
+ 0
+
+ 0
+ 64
+ 64
+
+ images\PEd_sprTest_0.png
+
+
diff --git a/PushEd.gmx/sprites/images/PEd_guiSprCheckbox_0.png b/PushEd.gmx/sprites/images/PEd_guiSprCheckbox_0.png
new file mode 100644
index 00000000..2aff2c1e
Binary files /dev/null and b/PushEd.gmx/sprites/images/PEd_guiSprCheckbox_0.png differ
diff --git a/PushEd.gmx/sprites/images/PEd_guiSprCheckbox_1.png b/PushEd.gmx/sprites/images/PEd_guiSprCheckbox_1.png
new file mode 100644
index 00000000..cb96bc5b
Binary files /dev/null and b/PushEd.gmx/sprites/images/PEd_guiSprCheckbox_1.png differ
diff --git a/PushEd.gmx/sprites/images/PEd_guiSprIcons_0.png b/PushEd.gmx/sprites/images/PEd_guiSprIcons_0.png
new file mode 100644
index 00000000..7103d73f
Binary files /dev/null and b/PushEd.gmx/sprites/images/PEd_guiSprIcons_0.png differ
diff --git a/PushEd.gmx/sprites/images/PEd_guiSprIcons_1.png b/PushEd.gmx/sprites/images/PEd_guiSprIcons_1.png
new file mode 100644
index 00000000..9836fb02
Binary files /dev/null and b/PushEd.gmx/sprites/images/PEd_guiSprIcons_1.png differ
diff --git a/PushEd.gmx/sprites/images/PEd_guiSprIcons_10.png b/PushEd.gmx/sprites/images/PEd_guiSprIcons_10.png
new file mode 100644
index 00000000..7fbc90c5
Binary files /dev/null and b/PushEd.gmx/sprites/images/PEd_guiSprIcons_10.png differ
diff --git a/PushEd.gmx/sprites/images/PEd_guiSprIcons_11.png b/PushEd.gmx/sprites/images/PEd_guiSprIcons_11.png
new file mode 100644
index 00000000..54ee8e4b
Binary files /dev/null and b/PushEd.gmx/sprites/images/PEd_guiSprIcons_11.png differ
diff --git a/PushEd.gmx/sprites/images/PEd_guiSprIcons_12.png b/PushEd.gmx/sprites/images/PEd_guiSprIcons_12.png
new file mode 100644
index 00000000..536c53a7
Binary files /dev/null and b/PushEd.gmx/sprites/images/PEd_guiSprIcons_12.png differ
diff --git a/PushEd.gmx/sprites/images/PEd_guiSprIcons_13.png b/PushEd.gmx/sprites/images/PEd_guiSprIcons_13.png
new file mode 100644
index 00000000..2a5f6715
Binary files /dev/null and b/PushEd.gmx/sprites/images/PEd_guiSprIcons_13.png differ
diff --git a/PushEd.gmx/sprites/images/PEd_guiSprIcons_14.png b/PushEd.gmx/sprites/images/PEd_guiSprIcons_14.png
new file mode 100644
index 00000000..c0d1b421
Binary files /dev/null and b/PushEd.gmx/sprites/images/PEd_guiSprIcons_14.png differ
diff --git a/PushEd.gmx/sprites/images/PEd_guiSprIcons_15.png b/PushEd.gmx/sprites/images/PEd_guiSprIcons_15.png
new file mode 100644
index 00000000..7297e8d3
Binary files /dev/null and b/PushEd.gmx/sprites/images/PEd_guiSprIcons_15.png differ
diff --git a/PushEd.gmx/sprites/images/PEd_guiSprIcons_2.png b/PushEd.gmx/sprites/images/PEd_guiSprIcons_2.png
new file mode 100644
index 00000000..90c71a2b
Binary files /dev/null and b/PushEd.gmx/sprites/images/PEd_guiSprIcons_2.png differ
diff --git a/PushEd.gmx/sprites/images/PEd_guiSprIcons_3.png b/PushEd.gmx/sprites/images/PEd_guiSprIcons_3.png
new file mode 100644
index 00000000..5a9f0051
Binary files /dev/null and b/PushEd.gmx/sprites/images/PEd_guiSprIcons_3.png differ
diff --git a/PushEd.gmx/sprites/images/PEd_guiSprIcons_4.png b/PushEd.gmx/sprites/images/PEd_guiSprIcons_4.png
new file mode 100644
index 00000000..2ac14268
Binary files /dev/null and b/PushEd.gmx/sprites/images/PEd_guiSprIcons_4.png differ
diff --git a/PushEd.gmx/sprites/images/PEd_guiSprIcons_5.png b/PushEd.gmx/sprites/images/PEd_guiSprIcons_5.png
new file mode 100644
index 00000000..fee51ec6
Binary files /dev/null and b/PushEd.gmx/sprites/images/PEd_guiSprIcons_5.png differ
diff --git a/PushEd.gmx/sprites/images/PEd_guiSprIcons_6.png b/PushEd.gmx/sprites/images/PEd_guiSprIcons_6.png
new file mode 100644
index 00000000..abc4862d
Binary files /dev/null and b/PushEd.gmx/sprites/images/PEd_guiSprIcons_6.png differ
diff --git a/PushEd.gmx/sprites/images/PEd_guiSprIcons_7.png b/PushEd.gmx/sprites/images/PEd_guiSprIcons_7.png
new file mode 100644
index 00000000..610de971
Binary files /dev/null and b/PushEd.gmx/sprites/images/PEd_guiSprIcons_7.png differ
diff --git a/PushEd.gmx/sprites/images/PEd_guiSprIcons_8.png b/PushEd.gmx/sprites/images/PEd_guiSprIcons_8.png
new file mode 100644
index 00000000..92cf27c0
Binary files /dev/null and b/PushEd.gmx/sprites/images/PEd_guiSprIcons_8.png differ
diff --git a/PushEd.gmx/sprites/images/PEd_guiSprIcons_9.png b/PushEd.gmx/sprites/images/PEd_guiSprIcons_9.png
new file mode 100644
index 00000000..fe1571fb
Binary files /dev/null and b/PushEd.gmx/sprites/images/PEd_guiSprIcons_9.png differ
diff --git a/PushEd.gmx/sprites/images/PEd_guiSprInput_0.png b/PushEd.gmx/sprites/images/PEd_guiSprInput_0.png
new file mode 100644
index 00000000..ac22def7
Binary files /dev/null and b/PushEd.gmx/sprites/images/PEd_guiSprInput_0.png differ
diff --git a/PushEd.gmx/sprites/images/PEd_guiSprInput_1.png b/PushEd.gmx/sprites/images/PEd_guiSprInput_1.png
new file mode 100644
index 00000000..bbe58ce9
Binary files /dev/null and b/PushEd.gmx/sprites/images/PEd_guiSprInput_1.png differ
diff --git a/PushEd.gmx/sprites/images/PEd_guiSprInput_2.png b/PushEd.gmx/sprites/images/PEd_guiSprInput_2.png
new file mode 100644
index 00000000..f47383ef
Binary files /dev/null and b/PushEd.gmx/sprites/images/PEd_guiSprInput_2.png differ
diff --git a/PushEd.gmx/sprites/images/PEd_guiSprMisc_0.png b/PushEd.gmx/sprites/images/PEd_guiSprMisc_0.png
new file mode 100644
index 00000000..94b82628
Binary files /dev/null and b/PushEd.gmx/sprites/images/PEd_guiSprMisc_0.png differ
diff --git a/PushEd.gmx/sprites/images/PEd_guiSprMisc_1.png b/PushEd.gmx/sprites/images/PEd_guiSprMisc_1.png
new file mode 100644
index 00000000..6d5ef5a7
Binary files /dev/null and b/PushEd.gmx/sprites/images/PEd_guiSprMisc_1.png differ
diff --git a/PushEd.gmx/sprites/images/PEd_guiSprMisc_2.png b/PushEd.gmx/sprites/images/PEd_guiSprMisc_2.png
new file mode 100644
index 00000000..eb767116
Binary files /dev/null and b/PushEd.gmx/sprites/images/PEd_guiSprMisc_2.png differ
diff --git a/PushEd.gmx/sprites/images/PEd_guiSprMisc_3.png b/PushEd.gmx/sprites/images/PEd_guiSprMisc_3.png
new file mode 100644
index 00000000..3f293e5f
Binary files /dev/null and b/PushEd.gmx/sprites/images/PEd_guiSprMisc_3.png differ
diff --git a/PushEd.gmx/sprites/images/PEd_guiSprMisc_4.png b/PushEd.gmx/sprites/images/PEd_guiSprMisc_4.png
new file mode 100644
index 00000000..5aa7edf9
Binary files /dev/null and b/PushEd.gmx/sprites/images/PEd_guiSprMisc_4.png differ
diff --git a/PushEd.gmx/sprites/images/PEd_guiSprPanel_0.png b/PushEd.gmx/sprites/images/PEd_guiSprPanel_0.png
new file mode 100644
index 00000000..a148b8df
Binary files /dev/null and b/PushEd.gmx/sprites/images/PEd_guiSprPanel_0.png differ
diff --git a/PushEd.gmx/sprites/images/PEd_guiSprRectangle_0.png b/PushEd.gmx/sprites/images/PEd_guiSprRectangle_0.png
new file mode 100644
index 00000000..58a2d46a
Binary files /dev/null and b/PushEd.gmx/sprites/images/PEd_guiSprRectangle_0.png differ
diff --git a/PushEd.gmx/sprites/images/PEd_guiSprRoll_0.png b/PushEd.gmx/sprites/images/PEd_guiSprRoll_0.png
new file mode 100644
index 00000000..c50a19c7
Binary files /dev/null and b/PushEd.gmx/sprites/images/PEd_guiSprRoll_0.png differ
diff --git a/PushEd.gmx/sprites/images/PEd_guiSprRoll_1.png b/PushEd.gmx/sprites/images/PEd_guiSprRoll_1.png
new file mode 100644
index 00000000..14474358
Binary files /dev/null and b/PushEd.gmx/sprites/images/PEd_guiSprRoll_1.png differ
diff --git a/PushEd.gmx/sprites/images/PEd_guiSprScrollbarHor_0.png b/PushEd.gmx/sprites/images/PEd_guiSprScrollbarHor_0.png
new file mode 100644
index 00000000..209766d2
Binary files /dev/null and b/PushEd.gmx/sprites/images/PEd_guiSprScrollbarHor_0.png differ
diff --git a/PushEd.gmx/sprites/images/PEd_guiSprScrollbarHor_1.png b/PushEd.gmx/sprites/images/PEd_guiSprScrollbarHor_1.png
new file mode 100644
index 00000000..a9c329dd
Binary files /dev/null and b/PushEd.gmx/sprites/images/PEd_guiSprScrollbarHor_1.png differ
diff --git a/PushEd.gmx/sprites/images/PEd_guiSprScrollbarHor_2.png b/PushEd.gmx/sprites/images/PEd_guiSprScrollbarHor_2.png
new file mode 100644
index 00000000..08b4f568
Binary files /dev/null and b/PushEd.gmx/sprites/images/PEd_guiSprScrollbarHor_2.png differ
diff --git a/PushEd.gmx/sprites/images/PEd_guiSprScrollbarVer_0.png b/PushEd.gmx/sprites/images/PEd_guiSprScrollbarVer_0.png
new file mode 100644
index 00000000..461a2a77
Binary files /dev/null and b/PushEd.gmx/sprites/images/PEd_guiSprScrollbarVer_0.png differ
diff --git a/PushEd.gmx/sprites/images/PEd_guiSprScrollbarVer_1.png b/PushEd.gmx/sprites/images/PEd_guiSprScrollbarVer_1.png
new file mode 100644
index 00000000..7890839b
Binary files /dev/null and b/PushEd.gmx/sprites/images/PEd_guiSprScrollbarVer_1.png differ
diff --git a/PushEd.gmx/sprites/images/PEd_guiSprScrollbarVer_2.png b/PushEd.gmx/sprites/images/PEd_guiSprScrollbarVer_2.png
new file mode 100644
index 00000000..e5b26215
Binary files /dev/null and b/PushEd.gmx/sprites/images/PEd_guiSprScrollbarVer_2.png differ
diff --git a/PushEd.gmx/sprites/images/PEd_guiSprShadow_0.png b/PushEd.gmx/sprites/images/PEd_guiSprShadow_0.png
new file mode 100644
index 00000000..23e9770d
Binary files /dev/null and b/PushEd.gmx/sprites/images/PEd_guiSprShadow_0.png differ
diff --git a/PushEd.gmx/sprites/images/PEd_guiSprShadow_1.png b/PushEd.gmx/sprites/images/PEd_guiSprShadow_1.png
new file mode 100644
index 00000000..492cc2f1
Binary files /dev/null and b/PushEd.gmx/sprites/images/PEd_guiSprShadow_1.png differ
diff --git a/PushEd.gmx/sprites/images/PEd_guiSprShadow_2.png b/PushEd.gmx/sprites/images/PEd_guiSprShadow_2.png
new file mode 100644
index 00000000..d4144516
Binary files /dev/null and b/PushEd.gmx/sprites/images/PEd_guiSprShadow_2.png differ
diff --git a/PushEd.gmx/sprites/images/PEd_guiSprWarning_0.png b/PushEd.gmx/sprites/images/PEd_guiSprWarning_0.png
new file mode 100644
index 00000000..1bf5e955
Binary files /dev/null and b/PushEd.gmx/sprites/images/PEd_guiSprWarning_0.png differ
diff --git a/PushEd.gmx/sprites/images/PEd_guiSprWindowCross_0.png b/PushEd.gmx/sprites/images/PEd_guiSprWindowCross_0.png
new file mode 100644
index 00000000..4e57475d
Binary files /dev/null and b/PushEd.gmx/sprites/images/PEd_guiSprWindowCross_0.png differ
diff --git a/PushEd.gmx/sprites/images/PEd_sprDummy2D_0.png b/PushEd.gmx/sprites/images/PEd_sprDummy2D_0.png
new file mode 100644
index 00000000..ed072bfe
Binary files /dev/null and b/PushEd.gmx/sprites/images/PEd_sprDummy2D_0.png differ
diff --git a/PushEd.gmx/sprites/images/PEd_sprTest_0.png b/PushEd.gmx/sprites/images/PEd_sprTest_0.png
new file mode 100644
index 00000000..eb783821
Binary files /dev/null and b/PushEd.gmx/sprites/images/PEd_sprTest_0.png differ
diff --git a/README.md b/README.md
index 3bbc2a4f..d85a2ea6 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,113 @@
# PushEd
-2D/3D level editor for GameMaker: Studio 1.4
+Or shortly just PEd, is a 2D/3D level editor for GameMaker: Studio 1.4, originally developed by our team [BlueBurn](blueburn.bluefile.cz/) as a paid extension, now available for free, for everyone, forever!
+
+Maintained by: [kraifpatrik](https://github.com/kraifpatrik)
+
+![Screenshot 2D](2d.png)
+![Screenshot 3D](3d.png)
+
+# Table of Contents
+ * [Features](#features)
+ * [Setup](#setup)
+ * [Controls](#controls)
+
+# Features
+ * Intuitive controlls
+ * Change all room and views settings
+ * Drag&drop objects into the level
+ * Grid alignment
+ * Set instance creation code
+ * Move, rotate and scale instances
+ * Set instance colour and alpha
+ * Multiple selection
+ * Copy selected instances
+ * Tile editing
+ * Resizable window and panels
+ * Save and load rooms (*.room.gmx)
+ * Level export and import (external file loadable on runtime, suitable for modding)
+
+# Setup
+It can happen that PEd uses different variables for object transformations than you have in you project. So before starting to use PEd, we recommend you to check if the variables are matching, otherwise you would not be able to use the editor. To do so, go to Macros > Default and you should see a short list of constants. Each constants represents a variable.
+
+Macro | Description
+------------------ | -----------
+`PEd_VAR_POS_X` | Instance position on X axis
+`PEd_VAR_POS_Y` | Instance position on Y axis
+`PEd_VAR_POS_Z*` | Instance position on Z axis
+`PEd_VAR_ROT_X*` | Instance rotation around X axis
+`PEd_VAR_ROT_Y*` | Instance rotation around Y axis
+`PEd_VAR_ROT_Z` | Instance rotation around Z axis
+`PEd_VAR_SCALE_X` | Instance scale on X axis
+`PEd_VAR_SCALE_Y` | Instance scale on Y axis
+`PEd_VAR_SCALE_Z*` | Instance scale on Z axis
+`PEd_VAR_COLOUR` | Instance colour
+`PEd_VAR_ALPHA` | Instance alpha
+
+**List of automatically saved variables:**
+ `x`, `y`, `image_angle`, `image_xscale`, `image_yscale`, `image_blend`, `image_alpha`
+
+These variables are ALWAYS saved into the room file (both GM's native and external). Any other additional variable has to be written into instance's creation code. Writing variables into the creation code is done by PEd_instanceAutocompleteCode. Loading variables from creation code, which is necessary only for external "*.bbmap" room files (in GM's native rooms this is done automatically), is done in `PEd_assignVariable`. So if you make any changes to the constants, make sure that you have also rewritten variable names in those two scripts.
+
+> \* - Additional variables, necessary only for 3D transformations. If you are using PEd for creating 2D levels, you can leave these constants as they are.
+
+# Controls
+## Global
+Controls | Action
+------------ | ------
+Alt | Hold down to temporarily disable snapping to grid.
+Ctrl+A | Clear selection.
+Ctrl+C | Copy selected instanes/tiles.
+Ctrl+E | Export room.
+Ctrl+F | Switch drawing of the floor.
+Ctrl+G | Switch snapping to grid.
+Ctrl+I | Import room.
+Ctrl+N | Create new room.
+Ctrl+O | Open room.
+Ctrl+S | Save room.
+Ctrl+Shift+S | Save room as...
+Del | Destroy selected instances/tiles.
+F10 | Switch between Play and Editor.
+F1 | Show/hide debug.
+F2 | Switch between 2D/3D mode.
+F3 | Cycle between editing modes.
+LMB | Move pivot.
+Num0..9 | Set camera speed. The higher the number the faster will camera move.
+Tab | Cycle between tools.
+
+## 3D mode
+Controls | Action
+---------------- | ------
+Arrow keys | Look around.
+MW | Move camera.
+RMB | Look around.
+W, S, A, D, Q, E | Move camera.
+Shift | Hold down to move faster.
+
+## 2D mode
+Controls | Action
+----------------- | ------
+Arrow keys | Move camera.
+MW | Zoom camera.
+Page Up/Page Down | Zoom camera.
+RMB | Move camera.
+Shift | Hold down to move faster.
+
+## Object edit mode
+Controls | Action
+-------- | ------
+Ctrl+LMB | Add/remove instance to/from multiple selection.
+LMB | Select instance.
+
+## Tile edit mode
+Controls | Action
+-------------- | ------
+Ctr+LMB | Create tile.
+Ctr+RMB | Delete tile on the mouse position.
+LMB | Select tile.
+Shift+Ctrl+LMB | Spawn tiles.
+Shift+LMB | Add/remove tile to/from multiple selection.
+
+> LMB - left mouse button
+> MMB - middle mouse button
+> MW - mouse wheel
+> RMB - right mouse button
\ No newline at end of file