Page MenuHomePhabricator (Chris)

No OneTemporary

Authored By
Unknown
Size
224 KB
Referenced Files
None
Subscribers
None
This document is not UTF8. It was detected as Shift JIS and converted to UTF8 for display.
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..31bb4e5
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,31 @@
+language: cpp
+git:
+ depth: 10
+matrix:
+ include:
+ - os: linux
+ compiler: gcc
+ addons:
+ apt:
+ packages:
+ - libsdl1.2-dev
+ - libsdl-image1.2-dev
+ - libsdl-ttf2.0-dev
+ - libsdl-mixer1.2-dev
+ - libsdl-gfx1.2-dev
+ - libcurl4-openssl-dev
+ - libarchive-dev
+ - liblua5.2-dev
+ - os: osx
+ compiler: clang
+ before_install:
+ - brew install sdl sdl_image sdl_ttf sdl_mixer sdl_gfx libarchive
+ - export PATH=/usr/local/opt/libarchive/bin:/usr/local/opt/libarchive/lib:/usr/local/opt/libarchive/include:$PATH
+ # libcurl (xcode builtin)
+ # brew only has lua 5.1 and 5.3, so we need to compile it from source
+ - export LUA52_VERSION=5.2.4
+ - wget http://www.lua.org/ftp/lua-$LUA52_VERSION.tar.gz
+ - tar zxf lua-$LUA52_VERSION.tar.gz
+ - cd lua-$LUA52_VERSION && make macosx && sudo make install && cd ..
+script:
+ - mkdir build && cd build && cmake .. && make
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..50cd179
--- /dev/null
+++ b/README.md
@@ -0,0 +1,83 @@
+(new README under construction)
+
+[![Build Status](https://travis-ci.org/acmepjz/meandmyshadow.svg?branch=master)](https://travis-ci.org/acmepjz/meandmyshadow)
+
+[![Build status](https://ci.appveyor.com/api/projects/status/t0cfcb54fppa501c/branch/master?svg=true)](https://ci.appveyor.com/project/acmepjz/meandmyshadow/branch/master) <--
+Click here for Windows nightly build (you also need `data` forder from latest git repository, just download from GitHub)
+
+Me and My Shadow
+====================
+Me and My Shadow is a free libre puzzle/platform game in which you try to reach
+the exit by solving puzzles. Spikes, moving blocks, fragile blocks and much
+more stand between you and the exit. Record your moves and let your shadow
+mimic them to reach blocks you couldn't reach alone.
+
+ - Tutorial for beginners
+ - 2 level packs containing over 40 levels
+ - 18 different block types
+ - Built-in level editor
+ - Easily installable addons
+ - Original music by Juho-Petteri Yliuntinen
+ - Cross platform
+
+
+
+Compiling on Linux
+====================
+
+You will need the following packages (and their -dev(el) files) to be installed:
+
+ * libSDL
+ * libSDL_image
+ * libSDL_ttf
+ * libSDL_mixer
+ * libSDL_gfx
+ * libcurl
+ * libarchive
+ * cmake
+ * C++ compiler (found in packages like g++, gcc-c++, gcc)
+
+The process is simple. Enter a terminal and move to directory containing
+MeAndMyShadow. Then just type
+
+ cmake .
+
+to generate the Makefile. If everything configured properly you don't see any
+errors and then you can start compiling by typing
+
+ make
+
+Finally you can run MeAndMyShadow with
+
+ ./meandmyshadow
+
+To install MeAndMyShadow on your system, run following as root
+
+ make install
+
+
+
+Compiling without GL
+====================
+In some cases you might want to compile Me and My Shadow without openGL
+hardware accelaration. In order to do this follow the steps above but replace the
+cmake call with:
+
+ cmake -DHARDWARE_ACCELERATION=OFF .
+
+Note that you can always run Me and My Shadow in SDL mode by setting gl to 0 in
+the config file or by running it as following:
+
+ meandmyshadow -set gl 0
+
+Compiling on Mac
+====================
+You need to have Xcode 4+ and following frameworks:
+
+ * SDL.framework
+ * SDL_image.framework
+ * SDL_ttf.framewrok
+ * SDL_mixer.framework
+ * SDL_gfx.framework
+
+Offical SDL binaries builded not builded for embeding with app. So you need to build them from sources or use unofficial binaries, please refer to this article: http://thirdcog.eu/apps/frameworks
diff --git a/appveyor.yml b/appveyor.yml
new file mode 100644
index 0000000..d1ba3a6
--- /dev/null
+++ b/appveyor.yml
@@ -0,0 +1,121 @@
+version: 1.0.{build}
+clone_depth: 10
+shallow_clone: true
+image:
+ - Visual Studio 2013
+ - Visual Studio 2015
+platform: Win32
+configuration:
+ - Debug
+ - Release
+install:
+ - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2013" set CMAKE_GENERATOR=Visual Studio 12 2013
+ - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2015" set CMAKE_GENERATOR=Visual Studio 14 2015
+ - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2017" set CMAKE_GENERATOR=Visual Studio 15 2017
+ - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2013" set VSVERSION=VS2013
+ - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2015" set VSVERSION=VS2015
+ - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2017" set VSVERSION=VS2017
+ # SDL2
+ - ps: Start-FileDownload 'http://www.libsdl.org/release/SDL2-devel-2.0.8-VC.zip'
+ - 7z x SDL2-devel-2.0.8-VC.zip
+ - set PATH=%APPVEYOR_BUILD_FOLDER%\SDL2-2.0.8\include;%APPVEYOR_BUILD_FOLDER%\SDL2-2.0.8\lib\x86;%PATH%
+ # SDL2_image
+ - ps: Start-FileDownload 'https://www.libsdl.org/projects/SDL_image/release/SDL2_image-devel-2.0.3-VC.zip'
+ - 7z x SDL2_image-devel-2.0.3-VC.zip
+ - set PATH=%APPVEYOR_BUILD_FOLDER%\SDL2_image-2.0.3\include;%APPVEYOR_BUILD_FOLDER%\SDL2_image-2.0.3\lib\x86;%PATH%
+ # SDL2_ttf
+ - ps: Start-FileDownload 'https://www.libsdl.org/projects/SDL_ttf/release/SDL2_ttf-devel-2.0.14-VC.zip'
+ - 7z x SDL2_ttf-devel-2.0.14-VC.zip
+ # ???
+ - del SDL2_ttf-2.0.14\lib\x86\zlib1.dll
+ - set PATH=%APPVEYOR_BUILD_FOLDER%\SDL2_ttf-2.0.14\include;%APPVEYOR_BUILD_FOLDER%\SDL2_ttf-2.0.14\lib\x86;%PATH%
+ # SDL2_mixer
+ - ps: Start-FileDownload 'https://www.libsdl.org/projects/SDL_mixer/release/SDL2_mixer-devel-2.0.2-VC.zip'
+ - 7z x SDL2_mixer-devel-2.0.2-VC.zip
+ - set PATH=%APPVEYOR_BUILD_FOLDER%\SDL2_mixer-2.0.2\include;%APPVEYOR_BUILD_FOLDER%\SDL2_mixer-2.0.2\lib\x86;%PATH%
+ # setup Visual Studio
+ - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2013" call "C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat" x86
+ - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2015" call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x86
+ - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2017" call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars32.bat"
+ # libcurl...
+ - ps: Start-FileDownload 'https://bintray.com/artifact/download/vszakats/generic/curl-7.59.0-win32-mingw.zip'
+ - 7z x curl-7.59.0-win32-mingw.zip
+ # ???
+ - rename curl-7.59.0-win32-mingw\lib\libcurl.dll.a libcurl.lib
+ # ??? again
+ - ps: Start-FileDownload 'https://skanthak.homepage.t-online.de/download/curl-7.59.0.cab'
+ - 7z x curl-7.59.0.cab
+ - copy /y I386\LIBCURL.DLL curl-7.59.0-win32-mingw\bin\libcurl.dll
+ - set PATH=%APPVEYOR_BUILD_FOLDER%\curl-7.59.0-win32-mingw\include;%APPVEYOR_BUILD_FOLDER%\curl-7.59.0-win32-mingw\lib;%PATH%
+ # zlib...
+ - ps: Start-FileDownload 'http://www.zlib.net/zlib1211.zip'
+ - 7z x zlib1211.zip && cd zlib-1.2.11
+ - mkdir build && cd build
+ - cmake -G "%CMAKE_GENERATOR%" ..
+ - msbuild zlib.sln /target:zlib /p:Configuration=%CONFIGURATION%
+ # ???
+ - copy zconf.h ..\zconf.h
+ - cd ..\..
+ - set PATH=%APPVEYOR_BUILD_FOLDER%\zlib-1.2.11;%APPVEYOR_BUILD_FOLDER%\zlib-1.2.11\build\%CONFIGURATION%;%PATH%
+ # libarchive, need to compile from source (very slow)
+ - cd ..
+ - git clone --depth 3 https://github.com/libarchive/libarchive.git
+ - cd libarchive
+ - mkdir builddd && cd builddd
+ - cmake -G "%CMAKE_GENERATOR%" ..
+ - msbuild libarchive.sln /target:archive /p:Configuration=%CONFIGURATION%
+ - cd /d %APPVEYOR_BUILD_FOLDER%
+ - set PATH=%APPVEYOR_BUILD_FOLDER%\..\libarchive\libarchive;%APPVEYOR_BUILD_FOLDER%\..\libarchive\builddd\libarchive\%CONFIGURATION%;%PATH%
+ # %APPVEYOR_BUILD_FOLDER%\..\libarchive\builddd\bin\%CONFIGURATION%;
+ # lua5.2, need to compile from source
+ - ps: Start-FileDownload 'http://www.lua.org/ftp/lua-5.2.4.tar.gz'
+ - 7z x lua-5.2.4.tar.gz && 7z x lua-5.2.4.tar && cd lua-5.2.4\src
+ - echo project(lua52) > CMakeLists.txt
+ - echo add_definitions(-DLUA_BUILD_AS_DLL) >> CMakeLists.txt
+ - echo add_library(lua52 SHARED lapi.c lcode.c lctype.c ldebug.c ldo.c ldump.c lfunc.c lgc.c llex.c lmem.c lobject.c lopcodes.c lparser.c lstate.c lstring.c ltable.c ltm.c lundump.c lvm.c lzio.c lauxlib.c lbaselib.c lbitlib.c lcorolib.c ldblib.c liolib.c lmathlib.c loslib.c lstrlib.c ltablib.c loadlib.c linit.c) >> CMakeLists.txt
+ - mkdir build && cd build
+ - cmake -G "%CMAKE_GENERATOR%" ..
+ - msbuild lua52.sln /target:lua52 /p:Configuration=%CONFIGURATION%
+ # ??????
+ - copy %CONFIGURATION%\lua52.lib %CONFIGURATION%\lua.lib
+ - cd ..\..\..
+ - set PATH=%APPVEYOR_BUILD_FOLDER%\lua-5.2.4\src;%APPVEYOR_BUILD_FOLDER%\lua-5.2.4\src\build\%CONFIGURATION%;%PATH%
+ # ???
+ # - set LUA_INCLUDE_DIR=%APPVEYOR_BUILD_FOLDER%\lua-5.2.4\src
+ # now we try to package 3rdparty libraries, for simplicity we put the header and binary together
+ - set BINSUFFIX=%PLATFORM%-%VSVERSION%-%CONFIGURATION%
+ - cd /d %APPVEYOR_BUILD_FOLDER%\curl-7.59.0-win32-mingw\include
+ - echo %APPVEYOR_BUILD_FOLDER%\zlib-1.2.11\build\%CONFIGURATION%\*.dll >> lst.txt
+ - echo %APPVEYOR_BUILD_FOLDER%\zlib-1.2.11\build\%CONFIGURATION%\*.lib >> lst.txt
+ - echo %APPVEYOR_BUILD_FOLDER%\zlib-1.2.11\build\%CONFIGURATION%\*.pdb >> lst.txt
+ - echo %APPVEYOR_BUILD_FOLDER%\zlib-1.2.11\*.h >> lst.txt
+ - echo %APPVEYOR_BUILD_FOLDER%\..\libarchive\builddd\bin\%CONFIGURATION%\*.dll >> lst.txt
+ - echo %APPVEYOR_BUILD_FOLDER%\..\libarchive\builddd\libarchive\%CONFIGURATION%\*.lib >> lst.txt
+ - echo %APPVEYOR_BUILD_FOLDER%\..\libarchive\builddd\bin\%CONFIGURATION%\*.pdb >> lst.txt
+ - echo %APPVEYOR_BUILD_FOLDER%\..\libarchive\libarchive\*.h >> lst.txt
+ - echo %APPVEYOR_BUILD_FOLDER%\lua-5.2.4\src\build\%CONFIGURATION%\*.dll >> lst.txt
+ - echo %APPVEYOR_BUILD_FOLDER%\lua-5.2.4\src\build\%CONFIGURATION%\*.lib >> lst.txt
+ - echo %APPVEYOR_BUILD_FOLDER%\lua-5.2.4\src\build\%CONFIGURATION%\*.pdb >> lst.txt
+ - echo %APPVEYOR_BUILD_FOLDER%\lua-5.2.4\src\*.h >> lst.txt
+ - echo %APPVEYOR_BUILD_FOLDER%\SDL2-2.0.8\lib\x86\*.dll >> lst.txt
+ - echo %APPVEYOR_BUILD_FOLDER%\SDL2-2.0.8\lib\x86\*.lib >> lst.txt
+ - echo %APPVEYOR_BUILD_FOLDER%\SDL2-2.0.8\include\*.h >> lst.txt
+ - echo %APPVEYOR_BUILD_FOLDER%\SDL2_image-2.0.3\lib\x86\*.dll >> lst.txt
+ - echo %APPVEYOR_BUILD_FOLDER%\SDL2_image-2.0.3\lib\x86\*.lib >> lst.txt
+ - echo %APPVEYOR_BUILD_FOLDER%\SDL2_image-2.0.3\include\*.h >> lst.txt
+ - echo %APPVEYOR_BUILD_FOLDER%\SDL2_ttf-2.0.14\lib\x86\*.dll >> lst.txt
+ - echo %APPVEYOR_BUILD_FOLDER%\SDL2_ttf-2.0.14\lib\x86\*.lib >> lst.txt
+ - echo %APPVEYOR_BUILD_FOLDER%\SDL2_ttf-2.0.14\include\*.h >> lst.txt
+ - echo %APPVEYOR_BUILD_FOLDER%\SDL2_mixer-2.0.2\lib\x86\*.dll >> lst.txt
+ - echo %APPVEYOR_BUILD_FOLDER%\SDL2_mixer-2.0.2\lib\x86\*.lib >> lst.txt
+ - echo %APPVEYOR_BUILD_FOLDER%\SDL2_mixer-2.0.2\include\*.h >> lst.txt
+ - echo %APPVEYOR_BUILD_FOLDER%\curl-7.59.0-win32-mingw\bin\*.dll >> lst.txt
+ # - echo %APPVEYOR_BUILD_FOLDER%\curl-7.59.0-win32-mingw\bin\*.crt >> lst.txt
+ - echo %APPVEYOR_BUILD_FOLDER%\curl-7.59.0-win32-mingw\lib\*.lib >> lst.txt
+ - echo curl\*.h >> lst.txt
+ - 7z a -t7z -mx=9 temp.7z @lst.txt
+ - move temp.7z "%APPVEYOR_BUILD_FOLDER%\3rdparty-%BINSUFFIX%.7z"
+ - cd /d %APPVEYOR_BUILD_FOLDER%
+artifacts:
+ - path: '3rdparty-$(BINSUFFIX).7z'
+build: off
diff --git a/appveyor.yml-build-dependencies b/appveyor.yml-build-dependencies
new file mode 100644
index 0000000..707bc74
--- /dev/null
+++ b/appveyor.yml-build-dependencies
@@ -0,0 +1,160 @@
+version: 1.0.{build}
+clone_depth: 10
+shallow_clone: true
+image:
+ - Visual Studio 2013
+ - Visual Studio 2015
+platform: Win32
+configuration:
+ - Debug
+ - Release
+install:
+ - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2013" set CMAKE_GENERATOR=Visual Studio 12 2013
+ - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2015" set CMAKE_GENERATOR=Visual Studio 14 2015
+ - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2017" set CMAKE_GENERATOR=Visual Studio 15 2017
+ - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2013" set VSVERSION=VS2013
+ - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2015" set VSVERSION=VS2015
+ - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2017" set VSVERSION=VS2017
+ # SDL
+ - ps: Start-FileDownload 'https://www.libsdl.org/release/SDL-devel-1.2.15-VC.zip'
+ - 7z x SDL-devel-1.2.15-VC.zip
+ - set PATH=%APPVEYOR_BUILD_FOLDER%\SDL-1.2.15\include;%APPVEYOR_BUILD_FOLDER%\SDL-1.2.15\lib\x86;%PATH%
+ # SDL_image
+ - ps: Start-FileDownload 'https://www.libsdl.org/projects/SDL_image/release/SDL_image-devel-1.2.12-VC.zip'
+ - 7z x SDL_image-devel-1.2.12-VC.zip
+ - set PATH=%APPVEYOR_BUILD_FOLDER%\SDL_image-1.2.12\include;%APPVEYOR_BUILD_FOLDER%\SDL_image-1.2.12\lib\x86;%PATH%
+ # SDL_ttf
+ - ps: Start-FileDownload 'https://www.libsdl.org/projects/SDL_ttf/release/SDL_ttf-devel-2.0.11-VC.zip'
+ - 7z x SDL_ttf-devel-2.0.11-VC.zip
+ # ???
+ - del SDL_ttf-2.0.11\lib\x86\zlib1.dll
+ - set PATH=%APPVEYOR_BUILD_FOLDER%\SDL_ttf-2.0.11\include;%APPVEYOR_BUILD_FOLDER%\SDL_ttf-2.0.11\lib\x86;%PATH%
+ # SDL_mixer
+ - ps: Start-FileDownload 'https://www.libsdl.org/projects/SDL_mixer/release/SDL_mixer-devel-1.2.12-VC.zip'
+ - 7z x SDL_mixer-devel-1.2.12-VC.zip
+ - set PATH=%APPVEYOR_BUILD_FOLDER%\SDL_mixer-1.2.12\include;%APPVEYOR_BUILD_FOLDER%\SDL_mixer-1.2.12\lib\x86;%PATH%
+ # setup Visual Studio
+ - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2013" call "C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat" x86
+ - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2015" call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x86
+ - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2017" call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars32.bat"
+ # SDL_gfx, need to compile from source
+ - ps: Start-FileDownload 'http://www.ferzkopp.net/Software/SDL_gfx-2.0/SDL_gfx-2.0.26.tar.gz'
+ - 7z x SDL_gfx-2.0.26.tar.gz && 7z x SDL_gfx-2.0.26.tar && cd SDL_gfx-2.0.26
+ - echo project(SDL_gfx) > CMakeLists.txt
+ - echo Find_Package(SDL REQUIRED) >> CMakeLists.txt
+ - echo add_definitions(-DDLL_EXPORT) >> CMakeLists.txt
+ - echo include_directories(${SDL_INCLUDE_DIR}) >> CMakeLists.txt
+ - echo add_library(SDL_gfx SHARED SDL_framerate.c SDL_gfxBlitFunc.c SDL_gfxPrimitives.c SDL_imageFilter.c SDL_rotozoom.c) >> CMakeLists.txt
+ - echo Target_Link_Libraries(SDL_gfx ${SDL_LIBRARY}) >> CMakeLists.txt
+ - mkdir build && cd build
+ - cmake -G "%CMAKE_GENERATOR%" ..
+ - msbuild SDL_gfx.sln /target:SDL_gfx /p:Configuration=%CONFIGURATION%
+ - cd ..\..
+ - set PATH=%APPVEYOR_BUILD_FOLDER%\SDL_gfx-2.0.26;%APPVEYOR_BUILD_FOLDER%\SDL_gfx-2.0.26\build\%CONFIGURATION%;%PATH%
+ # libcurl...
+ - ps: Start-FileDownload 'https://bintray.com/artifact/download/vszakats/generic/curl-7.59.0-win32-mingw.zip'
+ - 7z x curl-7.59.0-win32-mingw.zip
+ # ???
+ - rename curl-7.59.0-win32-mingw\lib\libcurl.dll.a libcurl.lib
+ # ??? again
+ - ps: Start-FileDownload 'https://skanthak.homepage.t-online.de/download/curl-7.59.0.cab'
+ - 7z x curl-7.59.0.cab
+ - copy /y I386\LIBCURL.DLL curl-7.59.0-win32-mingw\bin\libcurl.dll
+ - set PATH=%APPVEYOR_BUILD_FOLDER%\curl-7.59.0-win32-mingw\include;%APPVEYOR_BUILD_FOLDER%\curl-7.59.0-win32-mingw\lib;%PATH%
+ # zlib...
+ - ps: Start-FileDownload 'http://www.zlib.net/zlib1211.zip'
+ - 7z x zlib1211.zip && cd zlib-1.2.11
+ - mkdir build && cd build
+ - cmake -G "%CMAKE_GENERATOR%" ..
+ - msbuild zlib.sln /target:zlib /p:Configuration=%CONFIGURATION%
+ # ???
+ - copy zconf.h ..\zconf.h
+ - cd ..\..
+ - set PATH=%APPVEYOR_BUILD_FOLDER%\zlib-1.2.11;%APPVEYOR_BUILD_FOLDER%\zlib-1.2.11\build\%CONFIGURATION%;%PATH%
+ # libarchive, need to compile from source (very slow)
+ - cd ..
+ - git clone --depth 3 https://github.com/libarchive/libarchive.git
+ - cd libarchive
+ - mkdir builddd && cd builddd
+ - cmake -G "%CMAKE_GENERATOR%" ..
+ - msbuild libarchive.sln /target:archive /p:Configuration=%CONFIGURATION%
+ - cd /d %APPVEYOR_BUILD_FOLDER%
+ - set PATH=%APPVEYOR_BUILD_FOLDER%\..\libarchive\libarchive;%APPVEYOR_BUILD_FOLDER%\..\libarchive\builddd\libarchive\%CONFIGURATION%;%PATH%
+ # %APPVEYOR_BUILD_FOLDER%\..\libarchive\builddd\bin\%CONFIGURATION%;
+ # lua5.2, need to compile from source
+ - ps: Start-FileDownload 'http://www.lua.org/ftp/lua-5.2.4.tar.gz'
+ - 7z x lua-5.2.4.tar.gz && 7z x lua-5.2.4.tar && cd lua-5.2.4\src
+ - echo project(lua52) > CMakeLists.txt
+ - echo add_definitions(-DLUA_BUILD_AS_DLL) >> CMakeLists.txt
+ - echo add_library(lua52 SHARED lapi.c lcode.c lctype.c ldebug.c ldo.c ldump.c lfunc.c lgc.c llex.c lmem.c lobject.c lopcodes.c lparser.c lstate.c lstring.c ltable.c ltm.c lundump.c lvm.c lzio.c lauxlib.c lbaselib.c lbitlib.c lcorolib.c ldblib.c liolib.c lmathlib.c loslib.c lstrlib.c ltablib.c loadlib.c linit.c) >> CMakeLists.txt
+ - mkdir build && cd build
+ - cmake -G "%CMAKE_GENERATOR%" ..
+ - msbuild lua52.sln /target:lua52 /p:Configuration=%CONFIGURATION%
+ # ??????
+ - copy %CONFIGURATION%\lua52.lib %CONFIGURATION%\lua.lib
+ - cd ..\..\..
+ - set PATH=%APPVEYOR_BUILD_FOLDER%\lua-5.2.4\src;%APPVEYOR_BUILD_FOLDER%\lua-5.2.4\src\build\%CONFIGURATION%;%PATH%
+ # ???
+ # - set LUA_INCLUDE_DIR=%APPVEYOR_BUILD_FOLDER%\lua-5.2.4\src
+ # now we try to package 3rdparty libraries, for simplicity we put the header and binary together
+ - set BINSUFFIX=%PLATFORM%-%VSVERSION%-%CONFIGURATION%
+ - cd /d %APPVEYOR_BUILD_FOLDER%\curl-7.59.0-win32-mingw\include
+ - echo %APPVEYOR_BUILD_FOLDER%\SDL_gfx-2.0.26\build\%CONFIGURATION%\*.dll > lst.txt
+ - echo %APPVEYOR_BUILD_FOLDER%\SDL_gfx-2.0.26\build\%CONFIGURATION%\*.lib >> lst.txt
+ - echo %APPVEYOR_BUILD_FOLDER%\SDL_gfx-2.0.26\build\%CONFIGURATION%\*.pdb >> lst.txt
+ - echo %APPVEYOR_BUILD_FOLDER%\SDL_gfx-2.0.26\*.h >> lst.txt
+ - echo %APPVEYOR_BUILD_FOLDER%\zlib-1.2.11\build\%CONFIGURATION%\*.dll >> lst.txt
+ - echo %APPVEYOR_BUILD_FOLDER%\zlib-1.2.11\build\%CONFIGURATION%\*.lib >> lst.txt
+ - echo %APPVEYOR_BUILD_FOLDER%\zlib-1.2.11\build\%CONFIGURATION%\*.pdb >> lst.txt
+ - echo %APPVEYOR_BUILD_FOLDER%\zlib-1.2.11\*.h >> lst.txt
+ - echo %APPVEYOR_BUILD_FOLDER%\..\libarchive\builddd\bin\%CONFIGURATION%\*.dll >> lst.txt
+ - echo %APPVEYOR_BUILD_FOLDER%\..\libarchive\builddd\libarchive\%CONFIGURATION%\*.lib >> lst.txt
+ - echo %APPVEYOR_BUILD_FOLDER%\..\libarchive\builddd\bin\%CONFIGURATION%\*.pdb >> lst.txt
+ - echo %APPVEYOR_BUILD_FOLDER%\..\libarchive\libarchive\*.h >> lst.txt
+ - echo %APPVEYOR_BUILD_FOLDER%\lua-5.2.4\src\build\%CONFIGURATION%\*.dll >> lst.txt
+ - echo %APPVEYOR_BUILD_FOLDER%\lua-5.2.4\src\build\%CONFIGURATION%\*.lib >> lst.txt
+ - echo %APPVEYOR_BUILD_FOLDER%\lua-5.2.4\src\build\%CONFIGURATION%\*.pdb >> lst.txt
+ - echo %APPVEYOR_BUILD_FOLDER%\lua-5.2.4\src\*.h >> lst.txt
+ - echo %APPVEYOR_BUILD_FOLDER%\SDL-1.2.15\lib\x86\*.dll >> lst.txt
+ - echo %APPVEYOR_BUILD_FOLDER%\SDL-1.2.15\lib\x86\*.lib >> lst.txt
+ - echo %APPVEYOR_BUILD_FOLDER%\SDL-1.2.15\include\*.h >> lst.txt
+ - echo %APPVEYOR_BUILD_FOLDER%\SDL_image-1.2.12\lib\x86\*.dll >> lst.txt
+ - echo %APPVEYOR_BUILD_FOLDER%\SDL_image-1.2.12\lib\x86\*.lib >> lst.txt
+ - echo %APPVEYOR_BUILD_FOLDER%\SDL_image-1.2.12\include\*.h >> lst.txt
+ - echo %APPVEYOR_BUILD_FOLDER%\SDL_ttf-2.0.11\lib\x86\*.dll >> lst.txt
+ - echo %APPVEYOR_BUILD_FOLDER%\SDL_ttf-2.0.11\lib\x86\*.lib >> lst.txt
+ - echo %APPVEYOR_BUILD_FOLDER%\SDL_ttf-2.0.11\include\*.h >> lst.txt
+ - echo %APPVEYOR_BUILD_FOLDER%\SDL_mixer-1.2.12\lib\x86\*.dll >> lst.txt
+ - echo %APPVEYOR_BUILD_FOLDER%\SDL_mixer-1.2.12\lib\x86\*.lib >> lst.txt
+ - echo %APPVEYOR_BUILD_FOLDER%\SDL_mixer-1.2.12\include\*.h >> lst.txt
+ - echo %APPVEYOR_BUILD_FOLDER%\curl-7.59.0-win32-mingw\bin\*.dll >> lst.txt
+ # - echo %APPVEYOR_BUILD_FOLDER%\curl-7.59.0-win32-mingw\bin\*.crt >> lst.txt
+ - echo %APPVEYOR_BUILD_FOLDER%\curl-7.59.0-win32-mingw\lib\*.lib >> lst.txt
+ - echo curl\*.h >> lst.txt
+ - 7z a -t7z -mx=9 temp.7z @lst.txt
+ - move temp.7z "%APPVEYOR_BUILD_FOLDER%\3rdparty-%BINSUFFIX%.7z"
+ - cd /d %APPVEYOR_BUILD_FOLDER%
+artifacts:
+ - path: '3rdparty-$(BINSUFFIX).7z'
+ - path: 'binonly-$(BINSUFFIX).7z'
+before_build:
+ - mkdir build && cd build
+ - cmake -G "%CMAKE_GENERATOR%" -D "LUA_INCLUDE_DIR=%APPVEYOR_BUILD_FOLDER%\lua-5.2.4\src" ..
+build:
+ project: build\meandmyshadow.sln
+ verbosity: normal
+after_build:
+ # now we try to package the binary
+ - cd /d %APPVEYOR_BUILD_FOLDER%
+ - echo %APPVEYOR_BUILD_FOLDER%\SDL_gfx-2.0.26\build\%CONFIGURATION%\*.dll > lst.txt
+ - echo %APPVEYOR_BUILD_FOLDER%\zlib-1.2.11\build\%CONFIGURATION%\*.dll >> lst.txt
+ - echo %APPVEYOR_BUILD_FOLDER%\..\libarchive\builddd\bin\%CONFIGURATION%\*.dll >> lst.txt
+ - echo %APPVEYOR_BUILD_FOLDER%\lua-5.2.4\src\build\%CONFIGURATION%\*.dll >> lst.txt
+ - echo %APPVEYOR_BUILD_FOLDER%\SDL-1.2.15\lib\x86\*.dll >> lst.txt
+ - echo %APPVEYOR_BUILD_FOLDER%\SDL_image-1.2.12\lib\x86\*.dll >> lst.txt
+ - echo %APPVEYOR_BUILD_FOLDER%\SDL_ttf-2.0.11\lib\x86\*.dll >> lst.txt
+ - echo %APPVEYOR_BUILD_FOLDER%\SDL_mixer-1.2.12\lib\x86\*.dll >> lst.txt
+ - echo %APPVEYOR_BUILD_FOLDER%\curl-7.59.0-win32-mingw\bin\*.dll >> lst.txt
+ # - echo %APPVEYOR_BUILD_FOLDER%\curl-7.59.0-win32-mingw\bin\*.crt >> lst.txt
+ - echo %APPVEYOR_BUILD_FOLDER%\build\%CONFIGURATION%\*.exe >> lst.txt
+ - 7z a -t7z -mx=9 "binonly-%BINSUFFIX%.7z" @lst.txt
diff --git a/data/levels/Abitoff.map b/data/levels/Abitoff.map
new file mode 100644
index 0000000..f3b5a41
--- /dev/null
+++ b/data/levels/Abitoff.map
@@ -0,0 +1,38 @@
+name="A bit off."
+size=800,600
+time=800
+tile(PlayerStart,50,50)
+tile(ShadowStart,50,0)
+tile(Button,50,250){
+ behaviour=toggle
+ id=0
+}
+tile(MovingBlock,350,250){
+ MovingPosCount=2
+ disabled=0
+ id=0
+ loop=1
+ t0=166
+ t1=166
+ x0=-250
+ x1=0
+ y0=0
+ y1=0
+}
+tile(Fragile,400,250){
+ state=0
+}
+tile(Collectable,400,300)
+tile(MovingShadowBlock,450,250){
+ MovingPosCount=2
+ disabled=0
+ id=2
+ loop=1
+ t0=150
+ t1=150
+ x0=150
+ x1=0
+ y0=0
+ y1=0
+}
+tile(Exit,750,250)
diff --git a/data/levels/Gauntlet.map b/data/levels/Gauntlet.map
new file mode 100644
index 0000000..daf5b2e
--- /dev/null
+++ b/data/levels/Gauntlet.map
@@ -0,0 +1,386 @@
+name=Gauntlet
+size=850,900
+time=3600
+tile(Spikes,150,200)
+tile(ShadowBlock,150,150)
+tile(Block,50,250)
+tile(ShadowConveyorBelt,200,150){
+ disabled=0
+ speed=1
+}
+tile(MovingSpikes,350,200){
+ MovingPosCount=2
+ disabled=0
+ id=0
+ loop=1
+ t0=375
+ t1=375
+ x0=0
+ x1=0
+ y0=-150
+ y1=0
+}
+tile(Switch,400,100){
+ behaviour=on
+ id=26
+}
+tile(Spikes,450,150)
+tile(Spikes,450,100)
+tile(Spikes,450,50)
+tile(MovingBlock,100,250){
+ MovingPosCount=1
+ disabled=1
+ id=26
+ loop=0
+ t0=100
+ x0=0
+ y0=50
+}
+tile(Block,0,250)
+tile(Block,0,200)
+tile(Block,0,150)
+tile(Block,0,100)
+tile(Block,0,50)
+tile(Block,0,0)
+tile(Block,50,0)
+tile(Block,100,0)
+tile(Block,200,0)
+tile(Block,150,0)
+tile(Block,250,0)
+tile(Block,300,0)
+tile(Block,350,0)
+tile(Block,400,0)
+tile(Block,450,0)
+tile(Block,500,0)
+tile(Block,500,50)
+tile(Block,500,100)
+tile(Block,500,150)
+tile(Block,500,200)
+tile(Block,500,250)
+tile(Block,450,250)
+tile(Block,400,250)
+tile(Block,350,250)
+tile(Block,300,250)
+tile(Block,250,250)
+tile(Block,200,250)
+tile(MovingBlock,150,250){
+ MovingPosCount=1
+ disabled=1
+ id=26
+ loop=0
+ t0=200
+ x0=0
+ y0=100
+}
+tile(Spikes,200,200)
+tile(Spikes,250,200)
+tile(Spikes,300,200)
+tile(Spikes,400,200)
+tile(Spikes,450,200)
+tile(ShadowConveyorBelt,250,150){
+ disabled=0
+ speed=1
+}
+tile(ShadowConveyorBelt,300,150){
+ disabled=0
+ speed=1
+}
+tile(ShadowConveyorBelt,350,150){
+ disabled=0
+ speed=1
+}
+tile(ShadowBlock,400,150)
+tile(Block,250,300)
+tile(Block,250,350)
+tile(Block,250,400)
+tile(Block,250,450)
+tile(Block,250,500)
+tile(Block,250,600)
+tile(Block,150,600)
+tile(Block,100,600)
+tile(Block,50,600)
+tile(Block,0,600)
+tile(Block,0,500)
+tile(Block,0,450)
+tile(Block,0,400)
+tile(Block,0,350)
+tile(ShadowBlock,200,350)
+tile(MovingSpikes,200,450){
+ MovingPosCount=2
+ disabled=1
+ id=26
+ loop=1
+ t0=150
+ t1=150
+ x0=-150
+ x1=0
+ y0=0
+ y1=0
+}
+tile(MovingSpikes,50,550){
+ MovingPosCount=2
+ disabled=1
+ id=26
+ loop=1
+ t0=300
+ t1=300
+ x0=150
+ x1=0
+ y0=0
+ y1=0
+}
+tile(MovingBlock,200,600){
+ MovingPosCount=3
+ disabled=1
+ id=7
+ loop=0
+ t0=50
+ t1=150
+ t2=200
+ x0=0
+ x1=-150
+ x2=-150
+ y0=-50
+ y1=-50
+ y2=-250
+}
+tile(Switch,200,550){
+ behaviour=toggle
+ id=7
+}
+tile(Teleporter,50,300){
+ automatic=1
+ destination=10
+ id=8
+}
+tile(Block,0,300)
+tile(Block,0,550)
+tile(Block,250,550)
+tile(Block,550,0)
+tile(Block,600,0)
+tile(Block,650,0)
+tile(Block,700,0)
+tile(Block,750,0)
+tile(Block,800,0)
+tile(Block,800,50)
+tile(Block,800,100)
+tile(Block,800,200)
+tile(Block,800,150)
+tile(Block,800,250)
+tile(Block,800,300)
+tile(Block,800,350)
+tile(Block,800,400)
+tile(Block,800,450)
+tile(Block,800,550)
+tile(Block,800,500)
+tile(Block,800,600)
+tile(Block,750,600)
+tile(Block,700,600)
+tile(Block,650,600)
+tile(Block,600,600)
+tile(Block,500,600)
+tile(Block,550,600)
+tile(Block,400,600)
+tile(Teleporter,550,50){
+ automatic=0
+ id=10
+}
+tile(Spikes,550,250)
+tile(Swap,50,350)
+tile(Switch,0,650){
+ behaviour=on
+ id=11
+}
+tile(MovingShadowBlock,550,100){
+ MovingPosCount=3
+ disabled=1
+ id=11
+ loop=0
+ t0=60
+ t1=100
+ t2=140
+ x0=150
+ x1=150
+ x2=-200
+ y0=0
+ y1=250
+ y2=250
+}
+tile(MovingSpikes,700,100){
+ MovingPosCount=1
+ disabled=1
+ id=11
+ loop=0
+ t0=182
+ x0=-100
+ y0=350
+}
+tile(MovingSpikes,750,50){
+ MovingPosCount=1
+ disabled=1
+ id=11
+ loop=0
+ t0=201
+ x0=-50
+ y0=400
+}
+tile(MovingSpikes,750,200){
+ MovingPosCount=2
+ disabled=1
+ id=11
+ loop=1
+ t0=87
+ t1=87
+ x0=0
+ x1=0
+ y0=350
+ y1=0
+}
+tile(MovingBlock,0,700){
+ MovingPosCount=2
+ disabled=1
+ id=30
+ loop=0
+ t0=150
+ t1=350
+ x0=0
+ x1=350
+ y0=150
+ y1=150
+}
+tile(MovingBlock,50,700){
+ MovingPosCount=2
+ disabled=1
+ id=30
+ loop=0
+ t0=150
+ t1=350
+ x0=0
+ x1=350
+ y0=150
+ y1=150
+}
+tile(MovingBlock,100,700){
+ MovingPosCount=2
+ disabled=1
+ id=30
+ loop=0
+ t0=150
+ t1=350
+ x0=0
+ x1=350
+ y0=150
+ y1=150
+}
+tile(MovingBlock,150,700){
+ MovingPosCount=2
+ disabled=1
+ id=30
+ loop=0
+ t0=150
+ t1=350
+ x0=0
+ x1=350
+ y0=150
+ y1=150
+}
+tile(Checkpoint,150,300)
+tile(Checkpoint,100,650)
+tile(Exit,550,650)
+tile(Spikes,400,400)
+tile(Spikes,500,400)
+tile(Spikes,600,400)
+tile(Spikes,700,400)
+tile(Spikes,650,450)
+tile(Spikes,550,450)
+tile(Spikes,750,450)
+tile(Spikes,450,450)
+tile(Spikes,350,450)
+tile(Switch,750,550){
+ behaviour=on
+ id=30
+}
+tile(MovingSpikes,350,300){
+ MovingPosCount=2
+ disabled=1
+ id=11
+ loop=1
+ t0=250
+ t1=250
+ x0=0
+ x1=0
+ y0=250
+ y1=0
+}
+tile(MovingSpikes,600,200){
+ MovingPosCount=1
+ disabled=1
+ id=11
+ loop=0
+ t0=160
+ x0=-200
+ y0=250
+}
+tile(MovingSpikes,650,150){
+ MovingPosCount=1
+ disabled=1
+ id=11
+ loop=0
+ t0=167
+ x0=-150
+ y0=300
+}
+tile(MovingSpikes,450,300){
+ MovingPosCount=2
+ disabled=1
+ id=11
+ loop=1
+ t0=250
+ t1=250
+ x0=0
+ x1=0
+ y0=250
+ y1=0
+}
+tile(MovingSpikes,650,200){
+ MovingPosCount=2
+ disabled=1
+ id=11
+ loop=1
+ t0=87
+ t1=87
+ x0=0
+ x1=0
+ y0=350
+ y1=0
+}
+tile(MovingSpikes,550,200){
+ MovingPosCount=2
+ disabled=1
+ id=11
+ loop=1
+ t0=87
+ t1=87
+ x0=0
+ x1=0
+ y0=350
+ y1=0
+}
+tile(Block,450,600)
+tile(Block,350,600)
+tile(MovingBlock,300,600){
+ MovingPosCount=2
+ disabled=1
+ id=30
+ loop=0
+ t0=125
+ t1=125
+ x0=0
+ x1=250
+ y0=250
+ y1=250
+}
+tile(PlayerStart,50,200)
+tile(ShadowStart,100,200)
+tile(Checkpoint,700,550)
+tile(Block,200,700)
diff --git a/data/levels/QuantumTunnel.map b/data/levels/QuantumTunnel.map
new file mode 100644
index 0000000..f40706d
--- /dev/null
+++ b/data/levels/QuantumTunnel.map
@@ -0,0 +1,1264 @@
+name="Quantum Tunnel"
+size=5200,850
+tile(MovingBlock,500,250){
+ MovingPosCount=2
+ disabled=1
+ id=3
+ loop=1
+ t0=20
+ t1=200
+ x0=200
+ x1=0
+ y0=0
+ y1=0
+}
+tile(Block,550,300)
+tile(Block,600,300)
+tile(Block,650,300)
+tile(Block,700,300)
+tile(ConveyorBelt,750,300){
+ disabled=0
+ speed=500
+}
+tile(Block,1100,300)
+tile(Block,1100,250)
+tile(Block,1100,200)
+tile(Block,550,150)
+tile(Block,500,150)
+tile(Block,600,150)
+tile(Block,700,150)
+tile(Block,650,150)
+tile(Block,650,200)
+tile(Block,700,200)
+tile(Block,750,150)
+tile(Block,750,200)
+tile(Block,800,150)
+tile(Block,800,200)
+tile(Block,850,200)
+tile(Block,900,200)
+tile(Block,900,300)
+tile(Block,850,300)
+tile(Block,800,300)
+tile(Block,500,300)
+tile(Block,450,150)
+tile(Block,400,200)
+tile(Block,400,150)
+tile(Block,400,250)
+tile(Block,400,300)
+tile(Block,500,350)
+tile(Block,400,450)
+tile(Block,500,400)
+tile(Block,500,450)
+tile(Block,400,400)
+tile(Block,400,350)
+tile(Block,1200,300)
+tile(Block,1150,300)
+tile(Block,1300,300)
+tile(Block,1250,300)
+tile(Block,1150,150)
+tile(Block,1100,150)
+tile(Block,1200,150)
+tile(Block,1250,150)
+tile(Block,1300,150)
+tile(Block,1350,150)
+tile(Block,1350,200)
+tile(Block,1350,250)
+tile(Block,1350,300)
+tile(Exit,1300,250)
+tile(Block,600,400)
+tile(Block,650,350)
+tile(Teleporter,600,350){
+ automatic=0
+ destination=33
+ id=1
+}
+tile(Button,550,400){
+ behaviour=toggle
+ id=3
+}
+tile(Spikes,1100,100)
+tile(Spikes,1150,100)
+tile(Spikes,1250,100)
+tile(Spikes,1200,100)
+tile(Spikes,1300,100)
+tile(Spikes,1350,100)
+tile(Spikes,1400,100)
+tile(Spikes,1400,150)
+tile(Spikes,1400,250)
+tile(Spikes,1400,200)
+tile(Spikes,1400,300)
+tile(Spikes,1400,350)
+tile(Spikes,1350,350)
+tile(Spikes,1250,350)
+tile(Spikes,1300,350)
+tile(Spikes,1200,350)
+tile(Spikes,1150,350)
+tile(Spikes,1100,350)
+tile(Spikes,1050,350)
+tile(Spikes,1050,300)
+tile(Spikes,1050,250)
+tile(Spikes,1050,150)
+tile(Spikes,1050,200)
+tile(Spikes,1050,100)
+tile(Block,1450,250)
+tile(Block,1500,250)
+tile(ConveyorBelt,1550,250){
+ disabled=0
+ speed=5
+}
+tile(ConveyorBelt,1700,250){
+ disabled=0
+ speed=5
+}
+tile(ConveyorBelt,1650,250){
+ disabled=0
+ speed=5
+}
+tile(ConveyorBelt,1750,250){
+ disabled=0
+ speed=5
+}
+tile(ConveyorBelt,1600,250){
+ disabled=0
+ speed=5
+}
+tile(ConveyorBelt,1800,250){
+ disabled=0
+ speed=5
+}
+tile(ConveyorBelt,1900,250){
+ disabled=0
+ speed=5
+}
+tile(ConveyorBelt,1850,250){
+ disabled=0
+ speed=5
+}
+tile(ConveyorBelt,1950,250){
+ disabled=0
+ speed=5
+}
+tile(ConveyorBelt,2000,250){
+ disabled=0
+ speed=5
+}
+tile(ConveyorBelt,2050,250){
+ disabled=0
+ speed=5
+}
+tile(ConveyorBelt,2100,250){
+ disabled=0
+ speed=5
+}
+tile(ConveyorBelt,2150,250){
+ disabled=0
+ speed=5
+}
+tile(ConveyorBelt,2200,250){
+ disabled=0
+ speed=5
+}
+tile(ConveyorBelt,2250,250){
+ disabled=0
+ speed=5
+}
+tile(MovingSpikes,1600,350){
+ MovingPosCount=2
+ disabled=0
+ id=9
+ loop=1
+ t0=125
+ t1=125
+ x0=0
+ x1=0
+ y0=-250
+ y1=0
+}
+tile(MovingSpikes,1650,150){
+ MovingPosCount=2
+ disabled=0
+ id=10
+ loop=1
+ t0=233
+ t1=233
+ x0=350
+ x1=0
+ y0=0
+ y1=0
+}
+tile(ShadowBlock,1800,200)
+tile(Spikes,2100,200)
+tile(Block,2150,200)
+tile(MovingSpikes,2250,350){
+ MovingPosCount=2
+ disabled=0
+ id=11
+ loop=1
+ t0=117
+ t1=117
+ x0=250
+ x1=0
+ y0=-250
+ y1=0
+}
+tile(MovingSpikes,2600,100){
+ MovingPosCount=2
+ disabled=0
+ id=12
+ loop=1
+ t0=117
+ t1=117
+ x0=-250
+ x1=0
+ y0=250
+ y1=0
+}
+tile(ConveyorBelt,2500,250){
+ disabled=0
+ speed=5
+}
+tile(ConveyorBelt,2450,250){
+ disabled=0
+ speed=5
+}
+tile(ConveyorBelt,2600,250){
+ disabled=0
+ speed=5
+}
+tile(ConveyorBelt,2550,250){
+ disabled=0
+ speed=5
+}
+tile(ConveyorBelt,2650,250){
+ disabled=0
+ speed=5
+}
+tile(ConveyorBelt,2350,250){
+ disabled=0
+ speed=5
+}
+tile(ConveyorBelt,2300,250){
+ disabled=0
+ speed=5
+}
+tile(ConveyorBelt,2400,250){
+ disabled=0
+ speed=5
+}
+tile(MovingSpikes,2650,350){
+ MovingPosCount=2
+ disabled=0
+ id=13
+ loop=1
+ t0=117
+ t1=117
+ x0=250
+ x1=0
+ y0=-250
+ y1=0
+}
+tile(MovingSpikes,2950,100){
+ MovingPosCount=2
+ disabled=0
+ id=14
+ loop=1
+ t0=117
+ t1=117
+ x0=-250
+ x1=0
+ y0=250
+ y1=0
+}
+tile(ConveyorBelt,2700,250){
+ disabled=0
+ speed=5
+}
+tile(ConveyorBelt,2800,250){
+ disabled=0
+ speed=5
+}
+tile(ConveyorBelt,2750,250){
+ disabled=0
+ speed=5
+}
+tile(ConveyorBelt,2850,250){
+ disabled=0
+ speed=5
+}
+tile(ConveyorBelt,2950,250){
+ disabled=0
+ speed=5
+}
+tile(ConveyorBelt,2900,250){
+ disabled=0
+ speed=5
+}
+tile(ConveyorBelt,3000,250){
+ disabled=0
+ speed=5
+}
+tile(ConveyorBelt,3100,250){
+ disabled=0
+ speed=5
+}
+tile(ConveyorBelt,3050,250){
+ disabled=0
+ speed=5
+}
+tile(ConveyorBelt,3150,250){
+ disabled=0
+ speed=5
+}
+tile(ConveyorBelt,3200,250){
+ disabled=0
+ speed=5
+}
+tile(ConveyorBelt,3300,250){
+ disabled=0
+ speed=5
+}
+tile(ConveyorBelt,3250,250){
+ disabled=0
+ speed=5
+}
+tile(ConveyorBelt,3350,250){
+ disabled=0
+ speed=5
+}
+tile(ConveyorBelt,3400,250){
+ disabled=0
+ speed=5
+}
+tile(ConveyorBelt,3500,250){
+ disabled=0
+ speed=5
+}
+tile(ConveyorBelt,3450,250){
+ disabled=0
+ speed=5
+}
+tile(MovingSpikes,3150,400){
+ MovingPosCount=18
+ disabled=0
+ id=15
+ loop=1
+ t0=100
+ t1=101
+ t10=100
+ t11=101
+ t12=100
+ t13=101
+ t14=100
+ t15=101
+ t16=101
+ t17=153
+ t2=100
+ t3=101
+ t4=100
+ t5=101
+ t6=100
+ t7=101
+ t8=100
+ t9=101
+ x0=0
+ x1=50
+ x10=250
+ x11=300
+ x12=300
+ x13=350
+ x14=350
+ x15=400
+ x16=350
+ x17=0
+ x2=50
+ x3=100
+ x4=100
+ x5=150
+ x6=150
+ x7=200
+ x8=200
+ x9=250
+ y0=-300
+ y1=0
+ y10=-300
+ y11=0
+ y12=-300
+ y13=0
+ y14=-300
+ y15=0
+ y16=-300
+ y17=0
+ y2=-300
+ y3=0
+ y4=-300
+ y5=0
+ y6=-300
+ y7=0
+ y8=-300
+ y9=0
+}
+tile(Block,3100,200)
+tile(MovingSpikes,3600,400){
+ MovingPosCount=18
+ disabled=0
+ id=16
+ loop=1
+ t0=100
+ t1=101
+ t10=100
+ t11=101
+ t12=100
+ t13=101
+ t14=100
+ t15=101
+ t16=101
+ t17=153
+ t2=100
+ t3=101
+ t4=100
+ t5=101
+ t6=100
+ t7=101
+ t8=100
+ t9=101
+ x0=0
+ x1=50
+ x10=250
+ x11=300
+ x12=300
+ x13=350
+ x14=350
+ x15=400
+ x16=350
+ x17=0
+ x2=50
+ x3=100
+ x4=100
+ x5=150
+ x6=150
+ x7=200
+ x8=200
+ x9=250
+ y0=-300
+ y1=0
+ y10=-300
+ y11=0
+ y12=-300
+ y13=0
+ y14=-300
+ y15=0
+ y16=-300
+ y17=0
+ y2=-300
+ y3=0
+ y4=-300
+ y5=0
+ y6=-300
+ y7=0
+ y8=-300
+ y9=0
+}
+tile(MovingSpikes,4050,400){
+ MovingPosCount=18
+ disabled=0
+ id=17
+ loop=1
+ t0=100
+ t1=101
+ t10=100
+ t11=101
+ t12=100
+ t13=101
+ t14=100
+ t15=101
+ t16=101
+ t17=153
+ t2=100
+ t3=101
+ t4=100
+ t5=101
+ t6=100
+ t7=101
+ t8=100
+ t9=101
+ x0=0
+ x1=50
+ x10=250
+ x11=300
+ x12=300
+ x13=350
+ x14=350
+ x15=400
+ x16=350
+ x17=0
+ x2=50
+ x3=100
+ x4=100
+ x5=150
+ x6=150
+ x7=200
+ x8=200
+ x9=250
+ y0=-300
+ y1=0
+ y10=-300
+ y11=0
+ y12=-300
+ y13=0
+ y14=-300
+ y15=0
+ y16=-300
+ y17=0
+ y2=-300
+ y3=0
+ y4=-300
+ y5=0
+ y6=-300
+ y7=0
+ y8=-300
+ y9=0
+}
+tile(Block,3550,250)
+tile(ConveyorBelt,3600,250){
+ disabled=0
+ speed=5
+}
+tile(ConveyorBelt,3650,250){
+ disabled=0
+ speed=5
+}
+tile(ConveyorBelt,3700,250){
+ disabled=0
+ speed=5
+}
+tile(ConveyorBelt,3750,250){
+ disabled=0
+ speed=5
+}
+tile(ConveyorBelt,3800,250){
+ disabled=0
+ speed=5
+}
+tile(ConveyorBelt,3850,250){
+ disabled=0
+ speed=5
+}
+tile(ConveyorBelt,3900,250){
+ disabled=0
+ speed=5
+}
+tile(ConveyorBelt,3950,250){
+ disabled=0
+ speed=5
+}
+tile(Block,4000,250)
+tile(ShadowBlock,4000,200)
+tile(MovingSpikes,4150,400){
+ MovingPosCount=18
+ disabled=0
+ id=18
+ loop=1
+ t0=100
+ t1=101
+ t10=100
+ t11=101
+ t12=100
+ t13=101
+ t14=100
+ t15=101
+ t16=101
+ t17=153
+ t2=100
+ t3=101
+ t4=100
+ t5=101
+ t6=100
+ t7=101
+ t8=100
+ t9=101
+ x0=0
+ x1=50
+ x10=250
+ x11=300
+ x12=300
+ x13=350
+ x14=350
+ x15=400
+ x16=350
+ x17=0
+ x2=50
+ x3=100
+ x4=100
+ x5=150
+ x6=150
+ x7=200
+ x8=200
+ x9=250
+ y0=-300
+ y1=0
+ y10=-300
+ y11=0
+ y12=-300
+ y13=0
+ y14=-300
+ y15=0
+ y16=-300
+ y17=0
+ y2=-300
+ y3=0
+ y4=-300
+ y5=0
+ y6=-300
+ y7=0
+ y8=-300
+ y9=0
+}
+tile(MovingSpikes,4250,400){
+ MovingPosCount=18
+ disabled=0
+ id=19
+ loop=1
+ t0=100
+ t1=101
+ t10=100
+ t11=101
+ t12=100
+ t13=101
+ t14=100
+ t15=101
+ t16=101
+ t17=153
+ t2=100
+ t3=101
+ t4=100
+ t5=101
+ t6=100
+ t7=101
+ t8=100
+ t9=101
+ x0=0
+ x1=50
+ x10=250
+ x11=300
+ x12=300
+ x13=350
+ x14=350
+ x15=400
+ x16=350
+ x17=0
+ x2=50
+ x3=100
+ x4=100
+ x5=150
+ x6=150
+ x7=200
+ x8=200
+ x9=250
+ y0=-300
+ y1=0
+ y10=-300
+ y11=0
+ y12=-300
+ y13=0
+ y14=-300
+ y15=0
+ y16=-300
+ y17=0
+ y2=-300
+ y3=0
+ y4=-300
+ y5=0
+ y6=-300
+ y7=0
+ y8=-300
+ y9=0
+}
+tile(MovingSpikes,3350,300){
+ MovingPosCount=2
+ disabled=0
+ id=20
+ loop=1
+ t0=117
+ t1=117
+ x0=250
+ x1=0
+ y0=-250
+ y1=0
+}
+tile(MovingSpikes,3700,350){
+ MovingPosCount=2
+ disabled=0
+ id=21
+ loop=1
+ t0=117
+ t1=117
+ x0=250
+ x1=0
+ y0=-250
+ y1=0
+}
+tile(MovingSpikes,4000,300){
+ MovingPosCount=2
+ disabled=0
+ id=22
+ loop=1
+ t0=117
+ t1=117
+ x0=250
+ x1=0
+ y0=-250
+ y1=0
+}
+tile(MovingSpikes,3500,50){
+ MovingPosCount=2
+ disabled=0
+ id=23
+ loop=1
+ t0=117
+ t1=117
+ x0=-250
+ x1=0
+ y0=250
+ y1=0
+}
+tile(MovingSpikes,3900,100){
+ MovingPosCount=2
+ disabled=0
+ id=24
+ loop=1
+ t0=117
+ t1=117
+ x0=-250
+ x1=0
+ y0=250
+ y1=0
+}
+tile(MovingSpikes,4000,100){
+ MovingPosCount=2
+ disabled=0
+ id=25
+ loop=1
+ t0=117
+ t1=117
+ x0=-250
+ x1=0
+ y0=250
+ y1=0
+}
+tile(MovingSpikes,4300,50){
+ MovingPosCount=2
+ disabled=0
+ id=26
+ loop=1
+ t0=117
+ t1=117
+ x0=-250
+ x1=0
+ y0=250
+ y1=0
+}
+tile(MovingSpikes,4400,100){
+ MovingPosCount=2
+ disabled=0
+ id=27
+ loop=1
+ t0=117
+ t1=117
+ x0=-250
+ x1=0
+ y0=250
+ y1=0
+}
+tile(MovingSpikes,4450,100){
+ MovingPosCount=2
+ disabled=0
+ id=28
+ loop=1
+ t0=141
+ t1=141
+ x0=-200
+ x1=0
+ y0=200
+ y1=0
+}
+tile(ConveyorBelt,4100,250){
+ disabled=0
+ speed=5
+}
+tile(ConveyorBelt,4150,250){
+ disabled=0
+ speed=5
+}
+tile(ConveyorBelt,4200,250){
+ disabled=0
+ speed=5
+}
+tile(ConveyorBelt,4250,250){
+ disabled=0
+ speed=5
+}
+tile(ConveyorBelt,4350,250){
+ disabled=0
+ speed=5
+}
+tile(ConveyorBelt,4300,250){
+ disabled=0
+ speed=5
+}
+tile(ConveyorBelt,4400,250){
+ disabled=0
+ speed=5
+}
+tile(ConveyorBelt,4450,250){
+ disabled=0
+ speed=5
+}
+tile(ConveyorBelt,4500,250){
+ disabled=0
+ speed=5
+}
+tile(ConveyorBelt,4550,250){
+ disabled=0
+ speed=5
+}
+tile(ConveyorBelt,4600,250){
+ disabled=0
+ speed=5
+}
+tile(ConveyorBelt,4600,250){
+ disabled=0
+ speed=5
+}
+tile(ConveyorBelt,4650,250){
+ disabled=0
+ speed=5
+}
+tile(ConveyorBelt,4700,250){
+ disabled=0
+ speed=5
+}
+tile(ConveyorBelt,4750,250){
+ disabled=0
+ speed=5
+}
+tile(Fragile,4800,250){
+ state=2
+}
+tile(Fragile,4850,250){
+ state=2
+}
+tile(Fragile,4900,250){
+ state=2
+}
+tile(Fragile,5000,250){
+ state=2
+}
+tile(Fragile,4950,250){
+ state=2
+}
+tile(Fragile,5050,250){
+ state=2
+}
+tile(Fragile,5100,250){
+ state=2
+}
+tile(ShadowBlock,2500,200)
+tile(Block,2500,150)
+tile(Checkpoint,3100,150)
+tile(Checkpoint,4000,150)
+tile(Spikes,5150,0)
+tile(Spikes,5150,50)
+tile(Spikes,5150,100)
+tile(Spikes,5150,150)
+tile(Spikes,5150,200)
+tile(Spikes,5150,250)
+tile(Spikes,5150,300)
+tile(Spikes,5150,350)
+tile(Spikes,5150,400)
+tile(Spikes,5150,450)
+tile(Spikes,5150,500)
+tile(Spikes,5150,550)
+tile(Spikes,5150,600)
+tile(Spikes,5150,650)
+tile(Spikes,5150,700)
+tile(Block,5100,550)
+tile(Block,5000,550)
+tile(Block,5050,550)
+tile(Block,4900,550)
+tile(Block,4950,550)
+tile(Block,4850,550)
+tile(Block,4800,550)
+tile(Block,4750,550)
+tile(ShadowBlock,5100,450)
+tile(ShadowBlock,5050,450)
+tile(ShadowBlock,5000,450)
+tile(ShadowBlock,4950,450)
+tile(ShadowBlock,4900,450)
+tile(ShadowBlock,4850,450)
+tile(ShadowBlock,4750,450)
+tile(ShadowBlock,4800,450)
+tile(ShadowBlock,4700,450)
+tile(ShadowConveyorBelt,4650,450){
+ disabled=0
+ speed=-2
+}
+tile(ShadowConveyorBelt,4550,450){
+ disabled=0
+ speed=-2
+}
+tile(ShadowConveyorBelt,4600,450){
+ disabled=0
+ speed=-2
+}
+tile(ShadowConveyorBelt,4500,450){
+ disabled=0
+ speed=-2
+}
+tile(ShadowConveyorBelt,4450,450){
+ disabled=0
+ speed=-2
+}
+tile(ShadowConveyorBelt,4400,450){
+ disabled=0
+ speed=-2
+}
+tile(ShadowConveyorBelt,4350,450){
+ disabled=0
+ speed=-2
+}
+tile(ShadowConveyorBelt,4300,450){
+ disabled=0
+ speed=-2
+}
+tile(ShadowConveyorBelt,4200,450){
+ disabled=0
+ speed=-2
+}
+tile(ShadowConveyorBelt,4250,450){
+ disabled=0
+ speed=-2
+}
+tile(ShadowConveyorBelt,4150,450){
+ disabled=0
+ speed=-2
+}
+tile(ShadowConveyorBelt,4050,450){
+ disabled=0
+ speed=-2
+}
+tile(ShadowConveyorBelt,4100,450){
+ disabled=0
+ speed=-2
+}
+tile(ShadowConveyorBelt,4000,450){
+ disabled=0
+ speed=-2
+}
+tile(ShadowConveyorBelt,3950,450){
+ disabled=0
+ speed=-2
+}
+tile(ShadowConveyorBelt,3850,450){
+ disabled=0
+ speed=-2
+}
+tile(ShadowConveyorBelt,3900,450){
+ disabled=0
+ speed=-2
+}
+tile(ShadowConveyorBelt,3800,450){
+ disabled=0
+ speed=-2
+}
+tile(ShadowConveyorBelt,3750,450){
+ disabled=0
+ speed=-2
+}
+tile(ShadowConveyorBelt,3650,450){
+ disabled=0
+ speed=-2
+}
+tile(ShadowConveyorBelt,3700,450){
+ disabled=0
+ speed=-2
+}
+tile(ShadowConveyorBelt,3600,450){
+ disabled=0
+ speed=-2
+}
+tile(ShadowConveyorBelt,3500,450){
+ disabled=0
+ speed=-2
+}
+tile(ShadowConveyorBelt,3550,450){
+ disabled=0
+ speed=-2
+}
+tile(ShadowConveyorBelt,3450,450){
+ disabled=0
+ speed=-2
+}
+tile(ShadowConveyorBelt,3350,450){
+ disabled=0
+ speed=-2
+}
+tile(ShadowConveyorBelt,3400,450){
+ disabled=0
+ speed=-2
+}
+tile(ShadowConveyorBelt,3300,450){
+ disabled=0
+ speed=-2
+}
+tile(ShadowConveyorBelt,3250,450){
+ disabled=0
+ speed=-2
+}
+tile(ShadowConveyorBelt,3200,450){
+ disabled=0
+ speed=-2
+}
+tile(ShadowConveyorBelt,3100,450){
+ disabled=0
+ speed=-2
+}
+tile(ShadowConveyorBelt,3150,450){
+ disabled=0
+ speed=-2
+}
+tile(ShadowConveyorBelt,3050,450){
+ disabled=0
+ speed=-2
+}
+tile(ShadowConveyorBelt,3000,450){
+ disabled=0
+ speed=-2
+}
+tile(MovingBlock,4700,550){
+ MovingPosCount=14
+ disabled=1
+ id=30
+ loop=0
+ t0=111
+ t1=134
+ t10=176
+ t11=250
+ t12=75
+ t13=145
+ t2=111
+ t3=125
+ t4=145
+ t5=145
+ t6=158
+ t7=100
+ t8=150
+ t9=160
+ x0=-200
+ x1=-450
+ x10=-2400
+ x11=-2900
+ x12=-3050
+ x13=-3300
+ x2=-650
+ x3=-850
+ x4=-1100
+ x5=-1350
+ x6=-1650
+ x7=-1650
+ x8=-1950
+ x9=-2150
+ y0=100
+ y1=0
+ y10=50
+ y11=50
+ y12=50
+ y13=-100
+ y2=100
+ y3=-50
+ y4=100
+ y5=-50
+ y6=-150
+ y7=50
+ y8=50
+ y9=-200
+}
+tile(ShadowConveyorBelt,2950,450){
+ disabled=0
+ speed=-2
+}
+tile(ShadowConveyorBelt,2900,450){
+ disabled=0
+ speed=-2
+}
+tile(ShadowConveyorBelt,2850,450){
+ disabled=0
+ speed=-2
+}
+tile(ShadowConveyorBelt,2800,450){
+ disabled=0
+ speed=-2
+}
+tile(ShadowConveyorBelt,2700,450){
+ disabled=0
+ speed=-2
+}
+tile(ShadowConveyorBelt,2750,450){
+ disabled=0
+ speed=-2
+}
+tile(ShadowConveyorBelt,2650,450){
+ disabled=0
+ speed=-2
+}
+tile(ShadowConveyorBelt,2600,450){
+ disabled=0
+ speed=-2
+}
+tile(ShadowConveyorBelt,2550,450){
+ disabled=0
+ speed=-2
+}
+tile(ShadowConveyorBelt,2500,450){
+ disabled=0
+ speed=-2
+}
+tile(ShadowConveyorBelt,2450,450){
+ disabled=0
+ speed=-2
+}
+tile(ShadowConveyorBelt,2350,450){
+ disabled=0
+ speed=-2
+}
+tile(ShadowConveyorBelt,2400,450){
+ disabled=0
+ speed=-2
+}
+tile(ShadowConveyorBelt,2300,450){
+ disabled=0
+ speed=-2
+}
+tile(ShadowConveyorBelt,2250,450){
+ disabled=0
+ speed=-2
+}
+tile(ShadowConveyorBelt,2200,450){
+ disabled=0
+ speed=-2
+}
+tile(ShadowConveyorBelt,2100,450){
+ disabled=0
+ speed=-2
+}
+tile(ShadowConveyorBelt,2150,450){
+ disabled=0
+ speed=-2
+}
+tile(ShadowConveyorBelt,2000,450){
+ disabled=0
+ speed=-2
+}
+tile(ShadowConveyorBelt,2050,450){
+ disabled=0
+ speed=-2
+}
+tile(ShadowConveyorBelt,1950,450){
+ disabled=0
+ speed=-2
+}
+tile(ShadowConveyorBelt,1900,450){
+ disabled=0
+ speed=-2
+}
+tile(ShadowConveyorBelt,1800,450){
+ disabled=0
+ speed=-2
+}
+tile(ShadowConveyorBelt,1850,450){
+ disabled=0
+ speed=-2
+}
+tile(ShadowConveyorBelt,1750,450){
+ disabled=0
+ speed=-2
+}
+tile(ShadowConveyorBelt,1700,450){
+ disabled=0
+ speed=-2
+}
+tile(ShadowConveyorBelt,1650,450){
+ disabled=0
+ speed=-2
+}
+tile(ShadowConveyorBelt,1550,450){
+ disabled=0
+ speed=-2
+}
+tile(ShadowConveyorBelt,1600,450){
+ disabled=0
+ speed=-2
+}
+tile(ShadowConveyorBelt,1500,450){
+ disabled=0
+ speed=-2
+}
+tile(ShadowConveyorBelt,1450,450){
+ disabled=0
+ speed=-2
+}
+tile(Spikes,3000,400)
+tile(ShadowBlock,3750,400)
+tile(ShadowBlock,3450,400)
+tile(ShadowBlock,3400,350)
+tile(ShadowBlock,2650,400)
+tile(Spikes,1950,400)
+tile(Spikes,2150,400)
+tile(Spikes,1500,400)
+tile(Checkpoint,4750,500)
+tile(Checkpoint,3750,350)
+tile(Checkpoint,3050,400)
+tile(Checkpoint,1800,550)
+tile(ConveyorBelt,4050,250){
+ disabled=0
+ speed=5
+}
+tile(Switch,4750,350){
+ behaviour=on
+ id=30
+}
+tile(Block,1350,450)
+tile(Block,1300,450)
+tile(Block,1250,450)
+tile(Block,1150,450)
+tile(Block,1200,450)
+tile(Block,1150,450)
+tile(Block,1100,450)
+tile(Block,1050,450)
+tile(MovingBlock,1000,750){
+ MovingPosCount=4
+ disabled=1
+ id=3
+ loop=1
+ t0=55
+ t1=50
+ t2=50
+ t3=55
+ x0=-550
+ x1=-550
+ x2=-550
+ x3=0
+ y0=0
+ y1=-500
+ y2=0
+ y3=0
+}
+tile(Teleporter,1050,400){
+ automatic=0
+ destination=1
+ id=33
+}
+tile(ShadowBlock,1400,450)
+tile(PlayerStart,1450,200)
+tile(ShadowStart,1500,200)
+tile(Block,850,150)
+tile(Block,900,150)
diff --git a/data/levels/Reflection.map b/data/levels/Reflection.map
new file mode 100644
index 0000000..939a1bf
--- /dev/null
+++ b/data/levels/Reflection.map
@@ -0,0 +1,24 @@
+name=Reflection
+size=900,600
+time=1200
+tile(Block,0,450)
+tile(Block,100,400)
+tile(Block,200,500)
+tile(Block,300,450)
+tile(Block,450,450)
+tile(Block,650,450)
+tile(Block,700,400)
+tile(Block,850,550)
+tile(ShadowBlock,0,200)
+tile(ShadowBlock,150,50)
+tile(ShadowBlock,200,100)
+tile(ShadowBlock,400,100)
+tile(ShadowBlock,550,100)
+tile(ShadowBlock,650,150)
+tile(ShadowBlock,750,50)
+tile(ShadowBlock,850,100)
+tile(PlayerStart,0,400)
+tile(Collectable,0,150)
+tile(Collectable,850,500)
+tile(Exit,450,400)
+tile(ShadowStart,850,50)
diff --git a/data/levels/ShiftingSands.map b/data/levels/ShiftingSands.map
new file mode 100644
index 0000000..4fd2231
--- /dev/null
+++ b/data/levels/ShiftingSands.map
@@ -0,0 +1,2162 @@
+name="Shifting Sands"
+size=1050,1450
+time=360040
+tile(Block,100,600)
+tile(ShadowBlock,900,600)
+tile(Exit,500,600)
+tile(Block,450,600)
+tile(Block,450,550)
+tile(Block,500,550)
+tile(Block,550,550)
+tile(Block,550,600)
+tile(Block,450,650)
+tile(Block,550,650)
+tile(Block,450,800)
+tile(Block,500,800)
+tile(Block,550,800)
+tile(PlayerStart,100,550)
+tile(ShadowStart,900,550)
+tile(MovingBlock,200,600){
+ MovingPosCount=5
+ disabled=0
+ id=1
+ loop=1
+ t0=112
+ t1=150
+ t2=212
+ t3=150
+ t4=100
+ x0=0
+ x1=600
+ x2=600
+ x3=0
+ x4=0
+ y0=450
+ y1=450
+ y2=-400
+ y3=-400
+ y4=0
+}
+tile(MovingBlock,200,650){
+ MovingPosCount=5
+ disabled=0
+ id=2
+ loop=1
+ t0=100
+ t1=150
+ t2=212
+ t3=150
+ t4=112
+ x0=0
+ x1=600
+ x2=600
+ x3=0
+ x4=0
+ y0=400
+ y1=400
+ y2=-450
+ y3=-450
+ y4=0
+}
+tile(MovingBlock,200,700){
+ MovingPosCount=5
+ disabled=0
+ id=3
+ loop=1
+ t0=87
+ t1=150
+ t2=212
+ t3=150
+ t4=125
+ x0=0
+ x1=600
+ x2=600
+ x3=0
+ x4=0
+ y0=350
+ y1=350
+ y2=-500
+ y3=-500
+ y4=0
+}
+tile(MovingBlock,200,750){
+ MovingPosCount=5
+ disabled=0
+ id=4
+ loop=1
+ t0=75
+ t1=150
+ t2=212
+ t3=150
+ t4=137
+ x0=0
+ x1=600
+ x2=600
+ x3=0
+ x4=0
+ y0=300
+ y1=300
+ y2=-550
+ y3=-550
+ y4=0
+}
+tile(MovingBlock,200,550){
+ MovingPosCount=5
+ disabled=0
+ id=5
+ loop=1
+ t0=125
+ t1=150
+ t2=212
+ t3=150
+ t4=87
+ x0=0
+ x1=600
+ x2=600
+ x3=0
+ x4=0
+ y0=500
+ y1=500
+ y2=-350
+ y3=-350
+ y4=0
+}
+tile(MovingBlock,200,450){
+ MovingPosCount=5
+ disabled=0
+ id=6
+ loop=1
+ t0=150
+ t1=150
+ t2=212
+ t3=150
+ t4=62
+ x0=0
+ x1=600
+ x2=600
+ x3=0
+ x4=0
+ y0=600
+ y1=600
+ y2=-250
+ y3=-250
+ y4=0
+}
+tile(MovingBlock,200,400){
+ MovingPosCount=5
+ disabled=0
+ id=7
+ loop=1
+ t0=162
+ t1=150
+ t2=212
+ t3=150
+ t4=50
+ x0=0
+ x1=600
+ x2=600
+ x3=0
+ x4=0
+ y0=650
+ y1=650
+ y2=-200
+ y3=-200
+ y4=0
+}
+tile(MovingBlock,200,350){
+ MovingPosCount=5
+ disabled=0
+ id=8
+ loop=1
+ t0=175
+ t1=150
+ t2=212
+ t3=150
+ t4=37
+ x0=0
+ x1=600
+ x2=600
+ x3=0
+ x4=0
+ y0=700
+ y1=700
+ y2=-150
+ y3=-150
+ y4=0
+}
+tile(MovingBlock,200,300){
+ MovingPosCount=5
+ disabled=0
+ id=9
+ loop=1
+ t0=187
+ t1=150
+ t2=212
+ t3=150
+ t4=25
+ x0=0
+ x1=600
+ x2=600
+ x3=0
+ x4=0
+ y0=750
+ y1=750
+ y2=-100
+ y3=-100
+ y4=0
+}
+tile(MovingBlock,200,250){
+ MovingPosCount=5
+ disabled=0
+ id=10
+ loop=1
+ t0=200
+ t1=150
+ t2=212
+ t3=150
+ t4=12
+ x0=0
+ x1=600
+ x2=600
+ x3=0
+ x4=0
+ y0=800
+ y1=800
+ y2=-50
+ y3=-50
+ y4=0
+}
+tile(MovingBlock,200,200){
+ MovingPosCount=4
+ disabled=0
+ id=11
+ loop=1
+ t0=212
+ t1=150
+ t2=212
+ t3=150
+ x0=0
+ x1=600
+ x2=600
+ x3=0
+ y0=850
+ y1=850
+ y2=0
+ y3=0
+}
+tile(MovingBlock,200,850){
+ MovingPosCount=5
+ disabled=0
+ id=12
+ loop=1
+ t0=50
+ t1=150
+ t2=212
+ t3=150
+ t4=162
+ x0=0
+ x1=600
+ x2=600
+ x3=0
+ x4=0
+ y0=200
+ y1=200
+ y2=-650
+ y3=-650
+ y4=0
+}
+tile(MovingBlock,200,900){
+ MovingPosCount=5
+ disabled=0
+ id=13
+ loop=1
+ t0=37
+ t1=150
+ t2=212
+ t3=150
+ t4=175
+ x0=0
+ x1=600
+ x2=600
+ x3=0
+ x4=0
+ y0=150
+ y1=150
+ y2=-700
+ y3=-700
+ y4=0
+}
+tile(MovingBlock,200,950){
+ MovingPosCount=5
+ disabled=0
+ id=14
+ loop=1
+ t0=25
+ t1=150
+ t2=212
+ t3=150
+ t4=187
+ x0=0
+ x1=600
+ x2=600
+ x3=0
+ x4=0
+ y0=100
+ y1=100
+ y2=-750
+ y3=-750
+ y4=0
+}
+tile(MovingBlock,200,1000){
+ MovingPosCount=5
+ disabled=0
+ id=15
+ loop=1
+ t0=12
+ t1=150
+ t2=212
+ t3=150
+ t4=200
+ x0=0
+ x1=600
+ x2=600
+ x3=0
+ x4=0
+ y0=50
+ y1=50
+ y2=-800
+ y3=-800
+ y4=0
+}
+tile(MovingBlock,200,1050){
+ MovingPosCount=4
+ disabled=0
+ id=16
+ loop=1
+ t0=150
+ t1=212
+ t2=150
+ t3=212
+ x0=600
+ x1=600
+ x2=0
+ x3=0
+ y0=0
+ y1=-850
+ y2=-850
+ y3=0
+}
+tile(MovingBlock,300,1050){
+ MovingPosCount=5
+ disabled=0
+ id=17
+ loop=1
+ t0=125
+ t1=212
+ t2=150
+ t3=212
+ t4=25
+ x0=500
+ x1=500
+ x2=-100
+ x3=-100
+ x4=0
+ y0=0
+ y1=-850
+ y2=-850
+ y3=0
+ y4=0
+}
+tile(MovingBlock,350,1050){
+ MovingPosCount=5
+ disabled=0
+ id=18
+ loop=1
+ t0=112
+ t1=212
+ t2=150
+ t3=212
+ t4=37
+ x0=450
+ x1=450
+ x2=-150
+ x3=-150
+ x4=0
+ y0=0
+ y1=-850
+ y2=-850
+ y3=0
+ y4=0
+}
+tile(MovingBlock,400,1050){
+ MovingPosCount=5
+ disabled=0
+ id=19
+ loop=1
+ t0=100
+ t1=212
+ t2=150
+ t3=212
+ t4=50
+ x0=400
+ x1=400
+ x2=-200
+ x3=-200
+ x4=0
+ y0=0
+ y1=-850
+ y2=-850
+ y3=0
+ y4=0
+}
+tile(MovingBlock,450,1050){
+ MovingPosCount=5
+ disabled=0
+ id=20
+ loop=1
+ t0=87
+ t1=212
+ t2=150
+ t3=212
+ t4=62
+ x0=350
+ x1=350
+ x2=-250
+ x3=-250
+ x4=0
+ y0=0
+ y1=-850
+ y2=-850
+ y3=0
+ y4=0
+}
+tile(MovingBlock,500,1050){
+ MovingPosCount=5
+ disabled=0
+ id=21
+ loop=1
+ t0=75
+ t1=212
+ t2=150
+ t3=212
+ t4=75
+ x0=300
+ x1=300
+ x2=-300
+ x3=-300
+ x4=0
+ y0=0
+ y1=-850
+ y2=-850
+ y3=0
+ y4=0
+}
+tile(MovingBlock,600,1050){
+ MovingPosCount=5
+ disabled=0
+ id=22
+ loop=1
+ t0=50
+ t1=212
+ t2=150
+ t3=212
+ t4=100
+ x0=200
+ x1=200
+ x2=-400
+ x3=-400
+ x4=0
+ y0=0
+ y1=-850
+ y2=-850
+ y3=0
+ y4=0
+}
+tile(MovingBlock,650,1050){
+ MovingPosCount=5
+ disabled=0
+ id=23
+ loop=1
+ t0=37
+ t1=212
+ t2=150
+ t3=212
+ t4=112
+ x0=150
+ x1=150
+ x2=-450
+ x3=-450
+ x4=0
+ y0=0
+ y1=-850
+ y2=-850
+ y3=0
+ y4=0
+}
+tile(MovingBlock,700,1050){
+ MovingPosCount=5
+ disabled=0
+ id=24
+ loop=1
+ t0=25
+ t1=212
+ t2=150
+ t3=212
+ t4=125
+ x0=100
+ x1=100
+ x2=-500
+ x3=-500
+ x4=0
+ y0=0
+ y1=-850
+ y2=-850
+ y3=0
+ y4=0
+}
+tile(MovingBlock,750,1050){
+ MovingPosCount=5
+ disabled=0
+ id=25
+ loop=1
+ t0=12
+ t1=212
+ t2=150
+ t3=212
+ t4=137
+ x0=50
+ x1=50
+ x2=-550
+ x3=-550
+ x4=0
+ y0=0
+ y1=-850
+ y2=-850
+ y3=0
+ y4=0
+}
+tile(MovingBlock,800,1050){
+ MovingPosCount=4
+ disabled=0
+ id=26
+ loop=1
+ t0=212
+ t1=150
+ t2=212
+ t3=150
+ x0=0
+ x1=-600
+ x2=-600
+ x3=0
+ y0=-850
+ y1=-850
+ y2=0
+ y3=0
+}
+tile(MovingBlock,800,950){
+ MovingPosCount=5
+ disabled=0
+ id=27
+ loop=1
+ t0=187
+ t1=150
+ t2=212
+ t3=150
+ t4=25
+ x0=0
+ x1=-600
+ x2=-600
+ x3=0
+ x4=0
+ y0=-750
+ y1=-750
+ y2=100
+ y3=100
+ y4=0
+}
+tile(MovingBlock,800,900){
+ MovingPosCount=5
+ disabled=0
+ id=28
+ loop=1
+ t0=175
+ t1=150
+ t2=212
+ t3=150
+ t4=37
+ x0=0
+ x1=-600
+ x2=-600
+ x3=0
+ x4=0
+ y0=-700
+ y1=-700
+ y2=150
+ y3=150
+ y4=0
+}
+tile(MovingBlock,800,850){
+ MovingPosCount=5
+ disabled=0
+ id=29
+ loop=1
+ t0=162
+ t1=150
+ t2=212
+ t3=150
+ t4=50
+ x0=0
+ x1=-600
+ x2=-600
+ x3=0
+ x4=0
+ y0=-650
+ y1=-650
+ y2=200
+ y3=200
+ y4=0
+}
+tile(MovingBlock,800,800){
+ MovingPosCount=5
+ disabled=0
+ id=30
+ loop=1
+ t0=150
+ t1=150
+ t2=212
+ t3=150
+ t4=62
+ x0=0
+ x1=-600
+ x2=-600
+ x3=0
+ x4=0
+ y0=-600
+ y1=-600
+ y2=250
+ y3=250
+ y4=0
+}
+tile(MovingBlock,800,750){
+ MovingPosCount=5
+ disabled=0
+ id=31
+ loop=1
+ t0=137
+ t1=150
+ t2=212
+ t3=150
+ t4=75
+ x0=0
+ x1=-600
+ x2=-600
+ x3=0
+ x4=0
+ y0=-550
+ y1=-550
+ y2=300
+ y3=300
+ y4=0
+}
+tile(MovingBlock,800,650){
+ MovingPosCount=5
+ disabled=0
+ id=32
+ loop=1
+ t0=112
+ t1=150
+ t2=212
+ t3=150
+ t4=100
+ x0=0
+ x1=-600
+ x2=-600
+ x3=0
+ x4=0
+ y0=-450
+ y1=-450
+ y2=400
+ y3=400
+ y4=0
+}
+tile(MovingBlock,800,600){
+ MovingPosCount=5
+ disabled=0
+ id=33
+ loop=1
+ t0=100
+ t1=150
+ t2=212
+ t3=150
+ t4=112
+ x0=0
+ x1=-600
+ x2=-600
+ x3=0
+ x4=0
+ y0=-400
+ y1=-400
+ y2=450
+ y3=450
+ y4=0
+}
+tile(MovingBlock,800,550){
+ MovingPosCount=5
+ disabled=0
+ id=34
+ loop=1
+ t0=87
+ t1=150
+ t2=212
+ t3=150
+ t4=125
+ x0=0
+ x1=-600
+ x2=-600
+ x3=0
+ x4=0
+ y0=-350
+ y1=-350
+ y2=500
+ y3=500
+ y4=0
+}
+tile(MovingBlock,800,500){
+ MovingPosCount=5
+ disabled=0
+ id=35
+ loop=1
+ t0=75
+ t1=150
+ t2=212
+ t3=150
+ t4=137
+ x0=0
+ x1=-600
+ x2=-600
+ x3=0
+ x4=0
+ y0=-300
+ y1=-300
+ y2=550
+ y3=550
+ y4=0
+}
+tile(MovingBlock,800,450){
+ MovingPosCount=5
+ disabled=0
+ id=36
+ loop=1
+ t0=62
+ t1=150
+ t2=212
+ t3=150
+ t4=150
+ x0=0
+ x1=-600
+ x2=-600
+ x3=0
+ x4=0
+ y0=-250
+ y1=-250
+ y2=600
+ y3=600
+ y4=0
+}
+tile(MovingBlock,800,350){
+ MovingPosCount=5
+ disabled=0
+ id=37
+ loop=1
+ t0=37
+ t1=150
+ t2=212
+ t3=150
+ t4=175
+ x0=0
+ x1=-600
+ x2=-600
+ x3=0
+ x4=0
+ y0=-150
+ y1=-150
+ y2=700
+ y3=700
+ y4=0
+}
+tile(MovingBlock,800,300){
+ MovingPosCount=5
+ disabled=0
+ id=38
+ loop=1
+ t0=25
+ t1=150
+ t2=212
+ t3=150
+ t4=187
+ x0=0
+ x1=-600
+ x2=-600
+ x3=0
+ x4=0
+ y0=-100
+ y1=-100
+ y2=750
+ y3=750
+ y4=0
+}
+tile(MovingBlock,800,250){
+ MovingPosCount=5
+ disabled=0
+ id=39
+ loop=1
+ t0=12
+ t1=150
+ t2=212
+ t3=150
+ t4=200
+ x0=0
+ x1=-600
+ x2=-600
+ x3=0
+ x4=0
+ y0=-50
+ y1=-50
+ y2=800
+ y3=800
+ y4=0
+}
+tile(MovingBlock,800,200){
+ MovingPosCount=4
+ disabled=0
+ id=40
+ loop=1
+ t0=150
+ t1=212
+ t2=150
+ t3=212
+ x0=-600
+ x1=-600
+ x2=0
+ x3=0
+ y0=0
+ y1=850
+ y2=850
+ y3=0
+}
+tile(MovingBlock,750,200){
+ MovingPosCount=5
+ disabled=0
+ id=41
+ loop=1
+ t0=137
+ t1=212
+ t2=150
+ t3=212
+ t4=12
+ x0=-550
+ x1=-550
+ x2=50
+ x3=50
+ x4=0
+ y0=0
+ y1=850
+ y2=850
+ y3=0
+ y4=0
+}
+tile(MovingBlock,650,200){
+ MovingPosCount=5
+ disabled=0
+ id=42
+ loop=1
+ t0=112
+ t1=212
+ t2=150
+ t3=212
+ t4=37
+ x0=-450
+ x1=-450
+ x2=150
+ x3=150
+ x4=0
+ y0=0
+ y1=850
+ y2=850
+ y3=0
+ y4=0
+}
+tile(MovingBlock,600,200){
+ MovingPosCount=5
+ disabled=0
+ id=43
+ loop=1
+ t0=100
+ t1=212
+ t2=150
+ t3=212
+ t4=50
+ x0=-400
+ x1=-400
+ x2=200
+ x3=200
+ x4=0
+ y0=0
+ y1=850
+ y2=850
+ y3=0
+ y4=0
+}
+tile(MovingBlock,550,200){
+ MovingPosCount=5
+ disabled=0
+ id=44
+ loop=1
+ t0=87
+ t1=212
+ t2=150
+ t3=212
+ t4=62
+ x0=-350
+ x1=-350
+ x2=250
+ x3=250
+ x4=0
+ y0=0
+ y1=850
+ y2=850
+ y3=0
+ y4=0
+}
+tile(MovingBlock,450,200){
+ MovingPosCount=5
+ disabled=0
+ id=45
+ loop=1
+ t0=62
+ t1=212
+ t2=150
+ t3=212
+ t4=87
+ x0=-250
+ x1=-250
+ x2=350
+ x3=350
+ x4=0
+ y0=0
+ y1=850
+ y2=850
+ y3=0
+ y4=0
+}
+tile(MovingBlock,500,200){
+ MovingPosCount=5
+ disabled=0
+ id=46
+ loop=1
+ t0=75
+ t1=212
+ t2=150
+ t3=212
+ t4=75
+ x0=-300
+ x1=-300
+ x2=300
+ x3=300
+ x4=0
+ y0=0
+ y1=850
+ y2=850
+ y3=0
+ y4=0
+}
+tile(MovingBlock,300,200){
+ MovingPosCount=5
+ disabled=0
+ id=47
+ loop=1
+ t0=25
+ t1=212
+ t2=150
+ t3=212
+ t4=125
+ x0=-100
+ x1=-100
+ x2=500
+ x3=500
+ x4=0
+ y0=0
+ y1=850
+ y2=850
+ y3=0
+ y4=0
+}
+tile(MovingBlock,350,200){
+ MovingPosCount=5
+ disabled=0
+ id=48
+ loop=1
+ t0=37
+ t1=212
+ t2=150
+ t3=212
+ t4=112
+ x0=-150
+ x1=-150
+ x2=450
+ x3=450
+ x4=0
+ y0=0
+ y1=850
+ y2=850
+ y3=0
+ y4=0
+}
+tile(MovingBlock,250,200){
+ MovingPosCount=5
+ disabled=0
+ id=49
+ loop=1
+ t0=12
+ t1=212
+ t2=150
+ t3=212
+ t4=137
+ x0=-50
+ x1=-50
+ x2=550
+ x3=550
+ x4=0
+ y0=0
+ y1=850
+ y2=850
+ y3=0
+ y4=0
+}
+tile(MovingBlock,400,200){
+ MovingPosCount=5
+ disabled=0
+ id=50
+ loop=1
+ t0=50
+ t1=212
+ t2=150
+ t3=212
+ t4=100
+ x0=-200
+ x1=-200
+ x2=400
+ x3=400
+ x4=0
+ y0=0
+ y1=850
+ y2=850
+ y3=0
+ y4=0
+}
+tile(Block,450,700)
+tile(Block,550,700)
+tile(MovingBlock,250,500){
+ MovingPosCount=5
+ disabled=0
+ id=93
+ loop=1
+ t0=71
+ t1=71
+ t2=107
+ t3=71
+ t4=35
+ x0=0
+ x1=500
+ x2=500
+ x3=0
+ x4=0
+ y0=500
+ y1=500
+ y2=-250
+ y3=-250
+ y4=0
+}
+tile(MovingBlock,250,550){
+ MovingPosCount=5
+ disabled=0
+ id=94
+ loop=1
+ t0=64
+ t1=71
+ t2=107
+ t3=71
+ t4=42
+ x0=0
+ x1=500
+ x2=500
+ x3=0
+ x4=0
+ y0=450
+ y1=450
+ y2=-300
+ y3=-300
+ y4=0
+}
+tile(MovingBlock,250,800){
+ MovingPosCount=5
+ disabled=0
+ id=95
+ loop=1
+ t0=28
+ t1=71
+ t2=107
+ t3=71
+ t4=78
+ x0=0
+ x1=500
+ x2=500
+ x3=0
+ x4=0
+ y0=200
+ y1=200
+ y2=-550
+ y3=-550
+ y4=0
+}
+tile(MovingBlock,250,750){
+ MovingPosCount=5
+ disabled=0
+ id=96
+ loop=1
+ t0=35
+ t1=71
+ t2=107
+ t3=71
+ t4=71
+ x0=0
+ x1=500
+ x2=500
+ x3=0
+ x4=0
+ y0=250
+ y1=250
+ y2=-500
+ y3=-500
+ y4=0
+}
+tile(MovingBlock,250,1000){
+ MovingPosCount=4
+ disabled=0
+ id=97
+ loop=1
+ t0=71
+ t1=107
+ t2=71
+ t3=107
+ x0=500
+ x1=500
+ x2=0
+ x3=0
+ y0=0
+ y1=-750
+ y2=-750
+ y3=0
+}
+tile(MovingBlock,300,1000){
+ MovingPosCount=5
+ disabled=0
+ id=98
+ loop=1
+ t0=64
+ t1=107
+ t2=71
+ t3=107
+ t4=7
+ x0=450
+ x1=450
+ x2=-50
+ x3=-50
+ x4=0
+ y0=0
+ y1=-750
+ y2=-750
+ y3=0
+ y4=0
+}
+tile(MovingBlock,500,1000){
+ MovingPosCount=5
+ disabled=0
+ id=99
+ loop=1
+ t0=35
+ t1=107
+ t2=71
+ t3=107
+ t4=35
+ x0=250
+ x1=250
+ x2=-250
+ x3=-250
+ x4=0
+ y0=0
+ y1=-750
+ y2=-750
+ y3=0
+ y4=0
+}
+tile(MovingBlock,550,1000){
+ MovingPosCount=5
+ disabled=0
+ id=100
+ loop=1
+ t0=28
+ t1=107
+ t2=71
+ t3=107
+ t4=42
+ x0=200
+ x1=200
+ x2=-300
+ x3=-300
+ x4=0
+ y0=0
+ y1=-750
+ y2=-750
+ y3=0
+ y4=0
+}
+tile(MovingBlock,750,1000){
+ MovingPosCount=4
+ disabled=0
+ id=101
+ loop=1
+ t0=107
+ t1=71
+ t2=107
+ t3=71
+ x0=0
+ x1=-500
+ x2=-500
+ x3=0
+ y0=-750
+ y1=-750
+ y2=0
+ y3=0
+}
+tile(MovingBlock,750,950){
+ MovingPosCount=5
+ disabled=0
+ id=102
+ loop=1
+ t0=100
+ t1=71
+ t2=107
+ t3=71
+ t4=7
+ x0=0
+ x1=-500
+ x2=-500
+ x3=0
+ x4=0
+ y0=-700
+ y1=-700
+ y2=50
+ y3=50
+ y4=0
+}
+tile(MovingBlock,750,750){
+ MovingPosCount=5
+ disabled=0
+ id=103
+ loop=1
+ t0=71
+ t1=71
+ t2=107
+ t3=71
+ t4=35
+ x0=0
+ x1=-500
+ x2=-500
+ x3=0
+ x4=0
+ y0=-500
+ y1=-500
+ y2=250
+ y3=250
+ y4=0
+}
+tile(MovingBlock,750,700){
+ MovingPosCount=5
+ disabled=0
+ id=104
+ loop=1
+ t0=64
+ t1=71
+ t2=107
+ t3=71
+ t4=42
+ x0=0
+ x1=-500
+ x2=-500
+ x3=0
+ x4=0
+ y0=-450
+ y1=-450
+ y2=300
+ y3=300
+ y4=0
+}
+tile(MovingBlock,750,500){
+ MovingPosCount=5
+ disabled=0
+ id=105
+ loop=1
+ t0=35
+ t1=71
+ t2=107
+ t3=71
+ t4=71
+ x0=0
+ x1=-500
+ x2=-500
+ x3=0
+ x4=0
+ y0=-250
+ y1=-250
+ y2=500
+ y3=500
+ y4=0
+}
+tile(MovingBlock,750,450){
+ MovingPosCount=5
+ disabled=0
+ id=106
+ loop=1
+ t0=28
+ t1=71
+ t2=107
+ t3=71
+ t4=78
+ x0=0
+ x1=-500
+ x2=-500
+ x3=0
+ x4=0
+ y0=-200
+ y1=-200
+ y2=550
+ y3=550
+ y4=0
+}
+tile(MovingBlock,750,250){
+ MovingPosCount=4
+ disabled=0
+ id=107
+ loop=1
+ t0=71
+ t1=107
+ t2=71
+ t3=107
+ x0=-500
+ x1=-500
+ x2=0
+ x3=0
+ y0=0
+ y1=750
+ y2=750
+ y3=0
+}
+tile(MovingBlock,700,250){
+ MovingPosCount=5
+ disabled=0
+ id=108
+ loop=1
+ t0=64
+ t1=107
+ t2=71
+ t3=107
+ t4=7
+ x0=-450
+ x1=-450
+ x2=50
+ x3=50
+ x4=0
+ y0=0
+ y1=750
+ y2=750
+ y3=0
+ y4=0
+}
+tile(MovingBlock,500,250){
+ MovingPosCount=5
+ disabled=0
+ id=109
+ loop=1
+ t0=35
+ t1=107
+ t2=71
+ t3=107
+ t4=35
+ x0=-250
+ x1=-250
+ x2=250
+ x3=250
+ x4=0
+ y0=0
+ y1=750
+ y2=750
+ y3=0
+ y4=0
+}
+tile(MovingBlock,450,250){
+ MovingPosCount=5
+ disabled=0
+ id=110
+ loop=1
+ t0=28
+ t1=107
+ t2=71
+ t3=107
+ t4=42
+ x0=-200
+ x1=-200
+ x2=300
+ x3=300
+ x4=0
+ y0=0
+ y1=750
+ y2=750
+ y3=0
+ y4=0
+}
+tile(MovingBlock,250,250){
+ MovingPosCount=4
+ disabled=0
+ id=111
+ loop=1
+ t0=107
+ t1=71
+ t2=107
+ t3=71
+ x0=0
+ x1=500
+ x2=500
+ x3=0
+ y0=750
+ y1=750
+ y2=0
+ y3=0
+}
+tile(MovingBlock,250,300){
+ MovingPosCount=5
+ disabled=0
+ id=112
+ loop=1
+ t0=100
+ t1=71
+ t2=107
+ t3=71
+ t4=7
+ x0=0
+ x1=500
+ x2=500
+ x3=0
+ x4=0
+ y0=700
+ y1=700
+ y2=-50
+ y3=-50
+ y4=0
+}
+tile(MovingBlock,250,350){
+ MovingPosCount=5
+ disabled=0
+ id=113
+ loop=1
+ t0=92
+ t1=71
+ t2=107
+ t3=71
+ t4=14
+ x0=0
+ x1=500
+ x2=500
+ x3=0
+ x4=0
+ y0=650
+ y1=650
+ y2=-100
+ y3=-100
+ y4=0
+}
+tile(MovingBlock,250,600){
+ MovingPosCount=5
+ disabled=0
+ id=114
+ loop=1
+ t0=57
+ t1=71
+ t2=107
+ t3=71
+ t4=50
+ x0=0
+ x1=500
+ x2=500
+ x3=0
+ x4=0
+ y0=400
+ y1=400
+ y2=-350
+ y3=-350
+ y4=0
+}
+tile(MovingBlock,250,850){
+ MovingPosCount=5
+ disabled=0
+ id=115
+ loop=1
+ t0=21
+ t1=71
+ t2=107
+ t3=71
+ t4=85
+ x0=0
+ x1=500
+ x2=500
+ x3=0
+ x4=0
+ y0=150
+ y1=150
+ y2=-600
+ y3=-600
+ y4=0
+}
+tile(MovingBlock,350,1000){
+ MovingPosCount=5
+ disabled=0
+ id=116
+ loop=1
+ t0=57
+ t1=107
+ t2=71
+ t3=107
+ t4=14
+ x0=400
+ x1=400
+ x2=-100
+ x3=-100
+ x4=0
+ y0=0
+ y1=-750
+ y2=-750
+ y3=0
+ y4=0
+}
+tile(MovingBlock,600,1000){
+ MovingPosCount=5
+ disabled=0
+ id=117
+ loop=1
+ t0=21
+ t1=107
+ t2=71
+ t3=107
+ t4=50
+ x0=150
+ x1=150
+ x2=-350
+ x3=-350
+ x4=0
+ y0=0
+ y1=-750
+ y2=-750
+ y3=0
+ y4=0
+}
+tile(MovingBlock,750,900){
+ MovingPosCount=5
+ disabled=0
+ id=118
+ loop=1
+ t0=92
+ t1=71
+ t2=107
+ t3=71
+ t4=14
+ x0=0
+ x1=-500
+ x2=-500
+ x3=0
+ x4=0
+ y0=-650
+ y1=-650
+ y2=100
+ y3=100
+ y4=0
+}
+tile(MovingBlock,750,650){
+ MovingPosCount=5
+ disabled=0
+ id=119
+ loop=1
+ t0=57
+ t1=71
+ t2=107
+ t3=71
+ t4=50
+ x0=0
+ x1=-500
+ x2=-500
+ x3=0
+ x4=0
+ y0=-400
+ y1=-400
+ y2=350
+ y3=350
+ y4=0
+}
+tile(MovingBlock,750,400){
+ MovingPosCount=5
+ disabled=0
+ id=120
+ loop=1
+ t0=21
+ t1=71
+ t2=107
+ t3=71
+ t4=85
+ x0=0
+ x1=-500
+ x2=-500
+ x3=0
+ x4=0
+ y0=-150
+ y1=-150
+ y2=600
+ y3=600
+ y4=0
+}
+tile(MovingBlock,650,250){
+ MovingPosCount=5
+ disabled=0
+ id=121
+ loop=1
+ t0=57
+ t1=107
+ t2=71
+ t3=107
+ t4=14
+ x0=-400
+ x1=-400
+ x2=100
+ x3=100
+ x4=0
+ y0=0
+ y1=750
+ y2=750
+ y3=0
+ y4=0
+}
+tile(MovingBlock,400,250){
+ MovingPosCount=5
+ disabled=0
+ id=122
+ loop=1
+ t0=21
+ t1=107
+ t2=71
+ t3=107
+ t4=50
+ x0=-150
+ x1=-150
+ x2=350
+ x3=350
+ x4=0
+ y0=0
+ y1=750
+ y2=750
+ y3=0
+ y4=0
+}
+tile(Checkpoint,500,750)
+tile(MovingShadowBlock,600,350){
+ MovingPosCount=5
+ disabled=0
+ id=124
+ loop=1
+ t0=12
+ t1=137
+ t2=75
+ t3=137
+ t4=62
+ x0=50
+ x1=50
+ x2=-250
+ x3=-250
+ x4=0
+ y0=0
+ y1=550
+ y2=550
+ y3=0
+ y4=0
+}
+tile(MovingBlock,500,350){
+ MovingPosCount=5
+ disabled=0
+ id=125
+ loop=1
+ t0=50
+ t1=183
+ t2=100
+ t3=183
+ t4=50
+ x0=-150
+ x1=-150
+ x2=150
+ x3=150
+ x4=0
+ y0=0
+ y1=550
+ y2=550
+ y3=0
+ y4=0
+}
+tile(MovingBlock,450,350){
+ MovingPosCount=5
+ disabled=0
+ id=126
+ loop=1
+ t0=33
+ t1=183
+ t2=100
+ t3=183
+ t4=66
+ x0=-100
+ x1=-100
+ x2=200
+ x3=200
+ x4=0
+ y0=0
+ y1=550
+ y2=550
+ y3=0
+ y4=0
+}
+tile(MovingShadowBlock,350,350){
+ MovingPosCount=4
+ disabled=0
+ id=127
+ loop=1
+ t0=75
+ t1=137
+ t2=75
+ t3=137
+ x0=300
+ x1=300
+ x2=0
+ x3=0
+ y0=0
+ y1=550
+ y2=550
+ y3=0
+}
+tile(MovingBlock,350,550){
+ MovingPosCount=5
+ disabled=0
+ id=149
+ loop=1
+ t0=116
+ t1=100
+ t2=183
+ t3=100
+ t4=66
+ x0=0
+ x1=300
+ x2=300
+ x3=0
+ x4=0
+ y0=350
+ y1=350
+ y2=-200
+ y3=-200
+ y4=0
+}
+tile(MovingBlock,350,500){
+ MovingPosCount=5
+ disabled=0
+ id=150
+ loop=1
+ t0=133
+ t1=100
+ t2=183
+ t3=100
+ t4=50
+ x0=0
+ x1=300
+ x2=300
+ x3=0
+ x4=0
+ y0=400
+ y1=400
+ y2=-150
+ y3=-150
+ y4=0
+}
+tile(MovingShadowBlock,350,700){
+ MovingPosCount=5
+ disabled=0
+ id=151
+ loop=1
+ t0=87
+ t1=75
+ t2=137
+ t3=75
+ t4=50
+ x0=0
+ x1=300
+ x2=300
+ x3=0
+ x4=0
+ y0=-350
+ y1=-350
+ y2=200
+ y3=200
+ y4=0
+}
+tile(MovingBlock,350,800){
+ MovingPosCount=5
+ disabled=0
+ id=153
+ loop=1
+ t0=33
+ t1=100
+ t2=183
+ t3=100
+ t4=150
+ x0=0
+ x1=300
+ x2=300
+ x3=0
+ x4=0
+ y0=100
+ y1=100
+ y2=-450
+ y3=-450
+ y4=0
+}
+tile(MovingBlock,350,850){
+ MovingPosCount=5
+ disabled=0
+ id=154
+ loop=1
+ t0=16
+ t1=100
+ t2=183
+ t3=100
+ t4=166
+ x0=0
+ x1=300
+ x2=300
+ x3=0
+ x4=0
+ y0=50
+ y1=50
+ y2=-500
+ y3=-500
+ y4=0
+}
+tile(MovingShadowBlock,400,900){
+ MovingPosCount=5
+ disabled=0
+ id=160
+ loop=1
+ t0=12
+ t1=137
+ t2=75
+ t3=137
+ t4=62
+ x0=-50
+ x1=-50
+ x2=250
+ x3=250
+ x4=0
+ y0=0
+ y1=-550
+ y2=-550
+ y3=0
+ y4=0
+}
+tile(MovingBlock,450,900){
+ MovingPosCount=5
+ disabled=0
+ id=161
+ loop=1
+ t0=66
+ t1=183
+ t2=100
+ t3=183
+ t4=33
+ x0=200
+ x1=200
+ x2=-100
+ x3=-100
+ x4=0
+ y0=0
+ y1=-550
+ y2=-550
+ y3=0
+ y4=0
+}
+tile(MovingBlock,500,900){
+ MovingPosCount=5
+ disabled=0
+ id=162
+ loop=1
+ t0=50
+ t1=183
+ t2=100
+ t3=183
+ t4=50
+ x0=150
+ x1=150
+ x2=-150
+ x3=-150
+ x4=0
+ y0=0
+ y1=-550
+ y2=-550
+ y3=0
+ y4=0
+}
+tile(MovingShadowBlock,600,900){
+ MovingPosCount=5
+ disabled=0
+ id=163
+ loop=1
+ t0=62
+ t1=137
+ t2=75
+ t3=137
+ t4=12
+ x0=-250
+ x1=-250
+ x2=50
+ x3=50
+ x4=0
+ y0=0
+ y1=-550
+ y2=-550
+ y3=0
+ y4=0
+}
+tile(MovingBlock,650,750){
+ MovingPosCount=5
+ disabled=0
+ id=165
+ loop=1
+ t0=133
+ t1=100
+ t2=183
+ t3=100
+ t4=50
+ x0=0
+ x1=-300
+ x2=-300
+ x3=0
+ x4=0
+ y0=-400
+ y1=-400
+ y2=150
+ y3=150
+ y4=0
+}
+tile(MovingBlock,650,700){
+ MovingPosCount=5
+ disabled=0
+ id=166
+ loop=1
+ t0=116
+ t1=100
+ t2=183
+ t3=100
+ t4=66
+ x0=0
+ x1=-300
+ x2=-300
+ x3=0
+ x4=0
+ y0=-350
+ y1=-350
+ y2=200
+ y3=200
+ y4=0
+}
+tile(MovingShadowBlock,650,550){
+ MovingPosCount=5
+ disabled=0
+ id=168
+ loop=1
+ t0=87
+ t1=75
+ t2=137
+ t3=75
+ t4=50
+ x0=0
+ x1=-300
+ x2=-300
+ x3=0
+ x4=0
+ y0=350
+ y1=350
+ y2=-200
+ y3=-200
+ y4=0
+}
+tile(MovingBlock,650,450){
+ MovingPosCount=5
+ disabled=0
+ id=169
+ loop=1
+ t0=33
+ t1=100
+ t2=183
+ t3=100
+ t4=150
+ x0=0
+ x1=-300
+ x2=-300
+ x3=0
+ x4=0
+ y0=-100
+ y1=-100
+ y2=450
+ y3=450
+ y4=0
+}
+tile(MovingBlock,650,400){
+ MovingPosCount=5
+ disabled=0
+ id=170
+ loop=1
+ t0=16
+ t1=100
+ t2=183
+ t3=100
+ t4=166
+ x0=0
+ x1=-300
+ x2=-300
+ x3=0
+ x4=0
+ y0=-50
+ y1=-50
+ y2=500
+ y3=500
+ y4=0
+}
+tile(MovingSpikes,400,850){
+ MovingPosCount=4
+ disabled=0
+ id=171
+ loop=1
+ t0=66
+ t1=116
+ t2=66
+ t3=116
+ x0=200
+ x1=200
+ x2=0
+ x3=0
+ y0=0
+ y1=-350
+ y2=-350
+ y3=0
+}
+tile(MovingSpikes,600,850){
+ MovingPosCount=4
+ disabled=0
+ id=172
+ loop=1
+ t0=66
+ t1=116
+ t2=66
+ t3=116
+ x0=-200
+ x1=-200
+ x2=0
+ x3=0
+ y0=0
+ y1=-350
+ y2=-350
+ y3=0
+}
+tile(MovingSpikes,600,500){
+ MovingPosCount=4
+ disabled=0
+ id=173
+ loop=1
+ t0=116
+ t1=66
+ t2=116
+ t3=66
+ x0=0
+ x1=-200
+ x2=-200
+ x3=0
+ y0=350
+ y1=350
+ y2=0
+ y3=0
+}
+tile(MovingSpikes,400,500){
+ MovingPosCount=4
+ disabled=0
+ id=174
+ loop=1
+ t0=116
+ t1=66
+ t2=116
+ t3=66
+ x0=0
+ x1=200
+ x2=200
+ x3=0
+ y0=350
+ y1=350
+ y2=0
+ y3=0
+}
+tile(MovingBlock,400,400){
+ MovingPosCount=4
+ disabled=0
+ id=175
+ loop=1
+ t0=45
+ t1=20
+ t2=45
+ t3=20
+ x0=0
+ x1=200
+ x2=200
+ x3=0
+ y0=450
+ y1=450
+ y2=0
+ y3=0
+}
+tile(MovingShadowBlock,600,400){
+ MovingPosCount=4
+ disabled=0
+ id=176
+ loop=1
+ t0=45
+ t1=20
+ t2=45
+ t3=20
+ x0=0
+ x1=-200
+ x2=-200
+ x3=0
+ y0=450
+ y1=450
+ y2=0
+ y3=0
+}
+tile(Block,150,600)
+tile(ShadowBlock,850,600)
diff --git a/data/levels/SkyHigh.map b/data/levels/SkyHigh.map
new file mode 100644
index 0000000..cf4dd46
--- /dev/null
+++ b/data/levels/SkyHigh.map
@@ -0,0 +1,859 @@
+name="Sky High"
+size=4300,10250
+time=2400
+tile(Block,150,3200)
+tile(Block,0,3200)
+tile(Block,300,3200)
+tile(Block,450,3200)
+tile(Block,650,3200)
+tile(Block,700,3200)
+tile(Block,750,3200)
+tile(Block,800,3200)
+tile(Block,850,3200)
+tile(Block,900,3200)
+tile(Block,950,3200)
+tile(Block,1000,3200)
+tile(Block,1000,3150)
+tile(Block,1100,3200)
+tile(Block,1050,3200)
+tile(Block,1150,3200)
+tile(Block,1200,3200)
+tile(Block,1200,3150)
+tile(Block,1250,3100)
+tile(Block,1250,3150)
+tile(Block,1300,3200)
+tile(Block,1250,3200)
+tile(Block,1300,3150)
+tile(Block,1300,3050)
+tile(Block,1300,3100)
+tile(Block,1350,3000)
+tile(Block,1350,3050)
+tile(Block,1350,3100)
+tile(Block,1350,3150)
+tile(Block,1350,3200)
+tile(Block,1400,2950)
+tile(Block,1400,3000)
+tile(Block,1400,3050)
+tile(Block,1400,3100)
+tile(Block,1400,3150)
+tile(Block,1400,3200)
+tile(MovingBlock,750,3150){
+ MovingPosCount=1
+ disabled=1
+ id=4
+ loop=0
+ t0=100
+ x0=0
+ y0=-100
+}
+tile(MovingBlock,750,3100){
+ MovingPosCount=1
+ disabled=1
+ id=4
+ loop=0
+ t0=100
+ x0=0
+ y0=-100
+}
+tile(MovingBlock,800,3100){
+ MovingPosCount=1
+ disabled=1
+ id=4
+ loop=0
+ t0=100
+ x0=0
+ y0=-100
+}
+tile(MovingBlock,800,3150){
+ MovingPosCount=1
+ disabled=1
+ id=4
+ loop=0
+ t0=100
+ x0=0
+ y0=-100
+}
+tile(Spikes,1050,3150)
+tile(Spikes,1100,3150)
+tile(Spikes,1150,3150)
+tile(Switch,850,3150){
+ behaviour=toggle
+ id=4
+}
+tile(Spikes,1300,3000)
+tile(Block,1450,2900)
+tile(Block,1500,2850)
+tile(Block,1550,2800)
+tile(Block,1450,2950)
+tile(Block,1450,3000)
+tile(Block,1450,3050)
+tile(Block,1450,3100)
+tile(Block,1450,3150)
+tile(Block,1450,3200)
+tile(Block,1600,2750)
+tile(Block,1650,2700)
+tile(Block,1700,2650)
+tile(Block,1750,2600)
+tile(Block,1800,2550)
+tile(Block,1850,2500)
+tile(Block,1900,2450)
+tile(Spikes,3050,5900)
+tile(Spikes,3200,5900)
+tile(Spikes,2800,7100)
+tile(Block,3400,10200)
+tile(Block,3350,10200)
+tile(Block,3500,10200)
+tile(Block,3450,10200)
+tile(Block,3550,10200)
+tile(Block,3600,10200)
+tile(Block,3700,10200)
+tile(Block,3650,10200)
+tile(Block,3750,10200)
+tile(Block,3800,10200)
+tile(Block,3850,10200)
+tile(Block,3900,10200)
+tile(Block,3950,10200)
+tile(Block,4000,10200)
+tile(Spikes,3700,4350)
+tile(Spikes,3650,3950)
+tile(Block,2350,0)
+tile(Block,2300,0)
+tile(Block,2450,0)
+tile(Block,2400,0)
+tile(Block,2500,0)
+tile(Block,2550,0)
+tile(Block,2600,0)
+tile(Block,2650,0)
+tile(Spikes,3300,5650)
+tile(Spikes,3300,5100)
+tile(Exit,3800,10000)
+tile(ShadowBlock,3800,10150)
+tile(Swap,3700,10150)
+tile(MovingBlock,1950,2400){
+ MovingPosCount=2
+ disabled=0
+ id=6
+ loop=1
+ t0=481
+ t1=481
+ x0=200
+ x1=0
+ y0=-2400
+ y1=0
+}
+tile(Block,4250,10150)
+tile(Block,4250,10200)
+tile(Block,4250,10100)
+tile(Block,4250,10000)
+tile(Block,4250,9950)
+tile(Block,4250,10050)
+tile(Block,4250,9900)
+tile(Block,4250,9850)
+tile(Block,4250,9800)
+tile(Block,4250,9750)
+tile(Block,4250,9700)
+tile(Block,4250,9650)
+tile(Block,4250,9600)
+tile(Block,4250,9550)
+tile(Block,4250,9500)
+tile(Block,4250,9450)
+tile(Block,4250,9400)
+tile(Block,4250,9350)
+tile(Block,4250,9300)
+tile(Block,4250,9250)
+tile(Block,4250,9200)
+tile(Block,4250,9150)
+tile(Block,4250,9100)
+tile(Block,4250,9050)
+tile(Block,4250,9000)
+tile(Block,4250,8950)
+tile(Block,4250,8900)
+tile(Block,4250,8850)
+tile(Block,4250,8800)
+tile(Block,4250,8750)
+tile(Block,4250,8700)
+tile(Block,4250,8650)
+tile(Block,4250,8600)
+tile(Block,4250,8550)
+tile(Block,4250,8500)
+tile(Block,4250,8450)
+tile(Block,4250,8350)
+tile(Block,4250,8300)
+tile(Block,4250,8250)
+tile(Block,4250,8400)
+tile(Block,4250,8150)
+tile(Block,4250,8100)
+tile(Block,4250,8200)
+tile(Block,4250,8000)
+tile(Block,4250,8050)
+tile(Block,4250,7900)
+tile(Block,4250,7950)
+tile(Block,4250,7800)
+tile(Block,4250,7850)
+tile(Block,4250,7700)
+tile(Block,4250,7750)
+tile(Block,4250,7600)
+tile(Block,4250,7650)
+tile(Block,4250,7450)
+tile(Block,4250,7500)
+tile(Block,4250,7350)
+tile(Block,4250,7550)
+tile(Block,4250,7400)
+tile(Block,4250,7300)
+tile(Block,4250,7250)
+tile(Block,4250,7200)
+tile(Block,4250,7150)
+tile(Block,4250,7100)
+tile(Block,4250,7050)
+tile(Block,4250,7000)
+tile(Block,4250,6950)
+tile(Block,4250,6900)
+tile(Block,4250,6850)
+tile(Block,4250,6800)
+tile(Block,4250,6750)
+tile(Block,4250,6700)
+tile(Block,4250,6650)
+tile(Block,4250,6600)
+tile(Block,4250,6550)
+tile(Block,4250,6500)
+tile(Block,4250,6450)
+tile(Block,4250,6400)
+tile(Block,4250,6350)
+tile(Block,4250,6300)
+tile(Block,4250,6250)
+tile(Block,4250,6200)
+tile(Block,4250,6150)
+tile(Block,4250,6100)
+tile(Block,4250,6050)
+tile(Block,4250,6000)
+tile(Block,4250,5950)
+tile(Block,4250,5900)
+tile(Block,4250,5850)
+tile(Block,4250,5800)
+tile(Block,4250,5750)
+tile(Block,4250,5700)
+tile(Block,4250,5650)
+tile(Block,4250,5600)
+tile(Block,4250,5550)
+tile(Block,4250,5500)
+tile(Block,4250,5450)
+tile(Block,4250,5400)
+tile(Block,4250,5350)
+tile(Block,4250,5300)
+tile(Block,4250,5250)
+tile(Block,4250,5250)
+tile(Block,4250,5200)
+tile(Block,4250,5150)
+tile(Block,4250,5100)
+tile(Block,4250,5050)
+tile(Block,4250,5000)
+tile(Block,4250,4950)
+tile(Block,4250,4900)
+tile(Block,4250,4850)
+tile(Block,4250,4800)
+tile(Block,4250,4750)
+tile(Block,4250,4700)
+tile(Block,4250,4650)
+tile(Block,4250,4600)
+tile(Block,4250,4550)
+tile(Block,4250,4500)
+tile(Block,4250,4450)
+tile(Block,4250,4400)
+tile(Block,4250,4400)
+tile(Block,4250,4350)
+tile(Block,4250,4300)
+tile(Block,4250,4250)
+tile(Block,4250,4200)
+tile(Block,4250,4200)
+tile(Block,4250,4150)
+tile(Block,4250,4100)
+tile(Block,4250,4050)
+tile(Block,4250,4000)
+tile(Block,4250,3950)
+tile(Block,4250,3900)
+tile(Block,4250,3850)
+tile(Block,4250,3800)
+tile(Block,4250,3750)
+tile(Block,4250,3700)
+tile(Block,4250,3650)
+tile(Block,4250,3600)
+tile(Block,4250,3550)
+tile(Block,4250,3500)
+tile(Block,4250,3450)
+tile(Block,4250,3400)
+tile(Block,4250,3350)
+tile(Block,4250,3300)
+tile(Block,4250,3250)
+tile(Block,4250,3200)
+tile(Block,4250,3150)
+tile(Block,4250,3100)
+tile(Block,4250,3050)
+tile(Block,4250,3000)
+tile(Block,4250,2950)
+tile(Block,4250,2900)
+tile(Block,4250,2850)
+tile(Block,4250,2800)
+tile(Block,4250,2750)
+tile(Block,4250,2700)
+tile(Block,4250,2650)
+tile(Block,4250,2600)
+tile(Block,4250,2550)
+tile(Block,4250,2500)
+tile(Block,4250,2450)
+tile(Block,4250,2400)
+tile(Block,4250,2350)
+tile(Block,4250,2300)
+tile(Block,4250,2250)
+tile(Block,4250,2200)
+tile(Block,4250,2150)
+tile(Block,4250,2100)
+tile(Block,4250,2050)
+tile(Block,4250,2000)
+tile(Block,4250,1950)
+tile(Block,4250,1900)
+tile(Block,4250,1850)
+tile(Block,4250,1800)
+tile(Block,4250,1750)
+tile(Block,4250,1700)
+tile(Block,4250,1650)
+tile(Block,4250,1600)
+tile(Block,4250,1550)
+tile(Block,4250,1500)
+tile(Block,4250,1450)
+tile(Block,4250,1400)
+tile(Block,4250,1350)
+tile(Block,4250,1300)
+tile(Block,4250,1250)
+tile(Block,4250,1200)
+tile(Block,4250,1150)
+tile(Block,4250,1100)
+tile(Block,4250,1050)
+tile(Block,4250,1000)
+tile(Block,4250,950)
+tile(Block,4250,900)
+tile(Block,4250,850)
+tile(Block,4250,800)
+tile(Block,4250,750)
+tile(Block,4250,700)
+tile(Block,4250,650)
+tile(Block,4250,600)
+tile(Block,4250,550)
+tile(Block,4250,500)
+tile(Block,4250,450)
+tile(Block,4250,400)
+tile(Block,4250,350)
+tile(Block,4250,300)
+tile(Block,4250,250)
+tile(Block,4250,200)
+tile(Block,4250,150)
+tile(Block,4250,100)
+tile(Block,4250,50)
+tile(Block,4250,0)
+tile(Spikes,3550,2250)
+tile(Spikes,3600,2300)
+tile(Spikes,3650,2350)
+tile(Spikes,3700,2450)
+tile(Spikes,3700,2600)
+tile(Spikes,3750,2600)
+tile(Spikes,3750,2650)
+tile(Spikes,4150,7800)
+tile(Spikes,4100,8000)
+tile(ShadowStart,650,850)
+tile(Spikes,3250,1100)
+tile(Spikes,3250,1250)
+tile(Spikes,3300,1300)
+tile(Spikes,3350,1350)
+tile(Spikes,3400,1400)
+tile(Spikes,3450,1400)
+tile(Spikes,3450,1450)
+tile(Spikes,3450,1550)
+tile(Spikes,3450,1600)
+tile(Spikes,3250,1300)
+tile(Spikes,3250,1200)
+tile(Spikes,3250,1150)
+tile(Spikes,3250,1050)
+tile(Spikes,3300,1350)
+tile(Spikes,3350,1400)
+tile(Spikes,3450,1500)
+tile(Spikes,3450,1700)
+tile(Spikes,3450,1750)
+tile(Spikes,3450,1650)
+tile(Spikes,3450,1800)
+tile(Spikes,3450,1900)
+tile(Spikes,3450,1950)
+tile(Spikes,3450,1850)
+tile(Spikes,3450,2000)
+tile(Spikes,3450,2050)
+tile(Spikes,3450,2100)
+tile(Spikes,3450,2150)
+tile(Spikes,3450,2200)
+tile(Spikes,3500,2250)
+tile(Spikes,3550,2300)
+tile(Spikes,3600,2350)
+tile(Spikes,3700,2400)
+tile(Spikes,3700,2500)
+tile(Spikes,3700,2550)
+tile(Spikes,3700,2650)
+tile(Spikes,3750,2700)
+tile(Spikes,3700,2700)
+tile(Spikes,3750,2750)
+tile(Spikes,3800,2750)
+tile(Spikes,3800,2800)
+tile(Spikes,3800,2850)
+tile(Spikes,3800,2900)
+tile(Spikes,3800,2950)
+tile(Spikes,3800,3000)
+tile(Spikes,3800,3050)
+tile(Spikes,3800,3100)
+tile(Spikes,3850,3100)
+tile(Spikes,3850,3150)
+tile(Spikes,3850,3200)
+tile(Spikes,3850,3250)
+tile(Spikes,3850,3300)
+tile(Spikes,3850,3350)
+tile(Spikes,3850,3400)
+tile(Spikes,3850,3500)
+tile(Spikes,3850,3450)
+tile(Spikes,3850,3550)
+tile(Spikes,3850,3600)
+tile(Spikes,3950,3650)
+tile(Spikes,3850,3700)
+tile(Spikes,3850,3700)
+tile(Spikes,3900,3650)
+tile(Spikes,3900,3700)
+tile(Spikes,4200,3700)
+tile(Spikes,4200,3750)
+tile(Spikes,4200,3800)
+tile(Spikes,4200,3850)
+tile(Spikes,4200,3900)
+tile(Spikes,4200,3950)
+tile(Spikes,4200,4000)
+tile(Spikes,4200,4050)
+tile(Spikes,4200,4100)
+tile(Spikes,4200,4150)
+tile(Spikes,4200,4200)
+tile(Spikes,4150,4150)
+tile(Spikes,4100,4300)
+tile(Spikes,4100,4200)
+tile(Spikes,4100,4150)
+tile(Spikes,4100,4250)
+tile(Spikes,4050,4300)
+tile(Spikes,4100,4350)
+tile(Spikes,4050,4350)
+tile(Spikes,4100,4400)
+tile(Spikes,4050,4400)
+tile(Spikes,4050,4450)
+tile(Spikes,4050,4500)
+tile(Spikes,4050,4600)
+tile(Spikes,4050,4550)
+tile(Spikes,4050,4650)
+tile(Spikes,3700,4250)
+tile(Spikes,3700,4150)
+tile(Spikes,3700,4000)
+tile(Spikes,3700,4100)
+tile(Spikes,3700,4050)
+tile(Spikes,3700,4200)
+tile(Spikes,3750,3900)
+tile(Spikes,3700,3950)
+tile(Spikes,3700,3900)
+tile(Spikes,3700,3850)
+tile(Spikes,3750,3850)
+tile(Spikes,3750,3750)
+tile(Spikes,3700,3750)
+tile(Spikes,3750,3700)
+tile(Spikes,3800,3700)
+tile(Spikes,3650,4000)
+tile(Spikes,3650,4050)
+tile(Spikes,3650,4100)
+tile(Spikes,3650,4150)
+tile(Spikes,3650,4200)
+tile(Spikes,3650,4250)
+tile(Spikes,3650,4300)
+tile(Spikes,3600,4350)
+tile(Spikes,3600,4400)
+tile(Spikes,3550,4400)
+tile(Spikes,3550,4450)
+tile(Spikes,3500,4450)
+tile(Spikes,3500,4500)
+tile(Spikes,3450,4550)
+tile(Spikes,3650,4350)
+tile(Spikes,3600,4450)
+tile(Spikes,3500,4550)
+tile(Spikes,3450,4600)
+tile(Spikes,3450,4650)
+tile(Spikes,3450,4700)
+tile(Spikes,3450,4750)
+tile(Spikes,3400,4800)
+tile(Spikes,3400,4850)
+tile(Spikes,3350,4850)
+tile(Spikes,3350,4900)
+tile(Spikes,3300,4950)
+tile(Spikes,3300,5000)
+tile(Spikes,4050,4700)
+tile(Spikes,4050,4750)
+tile(Spikes,4050,4800)
+tile(Spikes,4000,4850)
+tile(Spikes,4050,4850)
+tile(Spikes,4050,4900)
+tile(Spikes,4050,4950)
+tile(Spikes,4000,5050)
+tile(Spikes,4000,5100)
+tile(Spikes,4000,5000)
+tile(Spikes,4000,4950)
+tile(Spikes,4000,4900)
+tile(Spikes,4000,5200)
+tile(Spikes,4000,5300)
+tile(Spikes,4000,5400)
+tile(Spikes,4000,5150)
+tile(Spikes,4000,5250)
+tile(Spikes,4000,5350)
+tile(Spikes,3300,5050)
+tile(Spikes,3300,5150)
+tile(Spikes,3300,5200)
+tile(Spikes,3300,5300)
+tile(Spikes,3300,5350)
+tile(Spikes,3300,5400)
+tile(Spikes,3300,5250)
+tile(Spikes,3300,5550)
+tile(Spikes,3300,5600)
+tile(Spikes,3300,5500)
+tile(Spikes,3300,5450)
+tile(Spikes,3950,5450)
+tile(Spikes,4000,5500)
+tile(Spikes,4000,5550)
+tile(Spikes,4000,5450)
+tile(Spikes,3350,5700)
+tile(Spikes,3300,5700)
+tile(Spikes,3350,5750)
+tile(Spikes,3350,5800)
+tile(Spikes,3300,5750)
+tile(Spikes,3300,5900)
+tile(Spikes,3300,6000)
+tile(Spikes,3250,6050)
+tile(Spikes,3250,6100)
+tile(Spikes,3250,6000)
+tile(Spikes,3300,5950)
+tile(Spikes,1950,2450)
+tile(Spikes,4000,5650)
+tile(Spikes,4000,5600)
+tile(Spikes,4000,5700)
+tile(Spikes,4000,5750)
+tile(Spikes,4000,5800)
+tile(Spikes,4000,5850)
+tile(Spikes,4000,5900)
+tile(Spikes,4000,5950)
+tile(Spikes,4000,6000)
+tile(Spikes,4000,6050)
+tile(Spikes,4000,6100)
+tile(Spikes,4000,6200)
+tile(Spikes,4000,6150)
+tile(Spikes,4050,6300)
+tile(Spikes,4000,6250)
+tile(Spikes,4100,6300)
+tile(Spikes,4000,6300)
+tile(Spikes,4000,6350)
+tile(Spikes,4000,6400)
+tile(Spikes,3400,6850)
+tile(Spikes,3500,6950)
+tile(Spikes,3800,6750)
+tile(Spikes,3400,6700)
+tile(Spikes,3400,6800)
+tile(Spikes,3500,6850)
+tile(Spikes,3250,6350)
+tile(Spikes,3250,6300)
+tile(Spikes,3300,6350)
+tile(Spikes,3350,6400)
+tile(Spikes,3300,6400)
+tile(Spikes,3350,6450)
+tile(Spikes,3950,6400)
+tile(Spikes,3900,6450)
+tile(Spikes,3800,6700)
+tile(Spikes,3800,6600)
+tile(Spikes,3750,6650)
+tile(Spikes,3800,6650)
+tile(Spikes,3750,6700)
+tile(Spikes,3850,6500)
+tile(Spikes,3800,6550)
+tile(Spikes,3400,6750)
+tile(Spikes,3450,6850)
+tile(Spikes,3350,6650)
+tile(Spikes,3400,6650)
+tile(Spikes,3350,6600)
+tile(Spikes,3400,6600)
+tile(Spikes,3350,6550)
+tile(Spikes,3350,6500)
+tile(Spikes,3500,6900)
+tile(Spikes,3800,6800)
+tile(Spikes,3800,6900)
+tile(Spikes,3800,6850)
+tile(Spikes,3800,7000)
+tile(Spikes,3800,7050)
+tile(Spikes,3800,7100)
+tile(Spikes,3700,7250)
+tile(Spikes,3750,7250)
+tile(Spikes,3750,7150)
+tile(Spikes,3750,7200)
+tile(Spikes,3750,7100)
+tile(Spikes,3700,7250)
+tile(Spikes,3700,7300)
+tile(Spikes,3700,7350)
+tile(Spikes,3700,7400)
+tile(Spikes,3700,7450)
+tile(Spikes,3650,7350)
+tile(Spikes,3650,7450)
+tile(Spikes,3600,7500)
+tile(Spikes,3650,7550)
+tile(Spikes,3650,7500)
+tile(Spikes,3650,7650)
+tile(Spikes,3600,7600)
+tile(Spikes,3600,7550)
+tile(Spikes,3600,7650)
+tile(Spikes,3600,7700)
+tile(Spikes,3650,7600)
+tile(Spikes,3650,7700)
+tile(Spikes,3600,7800)
+tile(Spikes,3650,7750)
+tile(Spikes,3600,7750)
+tile(Spikes,3650,7800)
+tile(Spikes,3650,7850)
+tile(Spikes,3600,7850)
+tile(Spikes,3600,7900)
+tile(Spikes,3650,7900)
+tile(Spikes,3650,7950)
+tile(Spikes,3600,8000)
+tile(Spikes,3600,7950)
+tile(Spikes,3650,8000)
+tile(Spikes,3650,8050)
+tile(Spikes,3600,8100)
+tile(Spikes,3650,8150)
+tile(Spikes,3600,8050)
+tile(Spikes,3050,7850)
+tile(Spikes,3050,8050)
+tile(Spikes,3050,7900)
+tile(Spikes,3050,8000)
+tile(Spikes,3050,7950)
+tile(Spikes,3600,8150)
+tile(Spikes,3650,8100)
+tile(Spikes,3450,7000)
+tile(Spikes,3400,7050)
+tile(Spikes,3500,7000)
+tile(Spikes,3350,7100)
+tile(Spikes,3300,7150)
+tile(Spikes,3250,7200)
+tile(Spikes,3200,7250)
+tile(Spikes,3150,7400)
+tile(Spikes,3200,7300)
+tile(Spikes,3100,7450)
+tile(Spikes,3200,7350)
+tile(Spikes,3050,7500)
+tile(Spikes,3050,7800)
+tile(Spikes,3050,7750)
+tile(Spikes,3050,7700)
+tile(Spikes,3050,7650)
+tile(Spikes,3050,7550)
+tile(Spikes,3050,7600)
+tile(Spikes,3250,6200)
+tile(Spikes,3250,6250)
+tile(Spikes,3250,6150)
+tile(Spikes,3300,6100)
+tile(Spikes,3350,5900)
+tile(Spikes,3350,5650)
+tile(Spikes,3350,5600)
+tile(Spikes,3600,8200)
+tile(Spikes,3650,8200)
+tile(Spikes,3650,8250)
+tile(Spikes,3600,8300)
+tile(Spikes,3600,8250)
+tile(Spikes,3650,8300)
+tile(Spikes,3650,8350)
+tile(Spikes,3600,8350)
+tile(Spikes,3600,8400)
+tile(Spikes,3650,8400)
+tile(Spikes,3600,8450)
+tile(Spikes,3650,8450)
+tile(Spikes,3350,8550)
+tile(Spikes,3150,8200)
+tile(Spikes,3100,8050)
+tile(Spikes,3100,8150)
+tile(Spikes,3100,8100)
+tile(Spikes,3150,8150)
+tile(Spikes,3250,8300)
+tile(Spikes,3200,8300)
+tile(Spikes,3200,8250)
+tile(Spikes,3250,8350)
+tile(Spikes,3250,8450)
+tile(Spikes,3250,8400)
+tile(Spikes,3300,8450)
+tile(Spikes,3350,8500)
+tile(Spikes,2000,2450)
+tile(Spikes,2100,2450)
+tile(Spikes,2050,2450)
+tile(Spikes,2150,2450)
+tile(Spikes,2200,2450)
+tile(Spikes,2250,2450)
+tile(Spikes,1950,2550)
+tile(Spikes,1950,2500)
+tile(Spikes,1950,2600)
+tile(Spikes,1950,2650)
+tile(Spikes,2250,2500)
+tile(Spikes,2250,2550)
+tile(Spikes,2250,2600)
+tile(Spikes,2250,2650)
+tile(Spikes,2250,2700)
+tile(Spikes,2250,2750)
+tile(Spikes,1950,2700)
+tile(Spikes,1950,2750)
+tile(Spikes,2000,2750)
+tile(Spikes,2050,2750)
+tile(Spikes,2100,2750)
+tile(Spikes,2150,2750)
+tile(Spikes,2200,2750)
+tile(Spikes,2700,2000)
+tile(Spikes,2700,2100)
+tile(Spikes,2700,2050)
+tile(Spikes,2700,2150)
+tile(Spikes,2700,2200)
+tile(Spikes,2700,2250)
+tile(Spikes,2700,2300)
+tile(Spikes,2700,2400)
+tile(Spikes,2700,2450)
+tile(Spikes,2700,2350)
+tile(Spikes,2350,2600)
+tile(Spikes,2400,2600)
+tile(Spikes,2450,2600)
+tile(Spikes,2500,2600)
+tile(Spikes,2550,2600)
+tile(Spikes,2700,2500)
+tile(Spikes,2700,2550)
+tile(Spikes,2700,2600)
+tile(Spikes,2700,2650)
+tile(Spikes,2700,2700)
+tile(Spikes,2700,2750)
+tile(Spikes,2700,2800)
+tile(Spikes,2700,2850)
+tile(Spikes,2700,2900)
+tile(Spikes,2700,2950)
+tile(Spikes,2700,3000)
+tile(Spikes,2700,3050)
+tile(Spikes,2700,3100)
+tile(Spikes,2700,3150)
+tile(Spikes,2700,3200)
+tile(Spikes,2600,2600)
+tile(Spikes,2650,2600)
+tile(Spikes,2800,2600)
+tile(Spikes,2850,2600)
+tile(Spikes,2750,2600)
+tile(Spikes,2900,2600)
+tile(Spikes,2950,2600)
+tile(Spikes,3000,2600)
+tile(Spikes,3050,2600)
+tile(Spikes,3100,2600)
+tile(Spikes,2700,3300)
+tile(Spikes,2700,3350)
+tile(Spikes,3700,3800)
+tile(Spikes,2750,3800)
+tile(Spikes,2700,3250)
+tile(Spikes,3600,7300)
+tile(Spikes,3800,6950)
+tile(Spikes,1500,3200)
+tile(Spikes,1600,3250)
+tile(Spikes,1550,3250)
+tile(Spikes,1650,3250)
+tile(Spikes,1750,3300)
+tile(Spikes,1700,3300)
+tile(Spikes,1800,3350)
+tile(Spikes,1850,3350)
+tile(Spikes,1900,3350)
+tile(Spikes,1900,3400)
+tile(Spikes,1950,3400)
+tile(Spikes,2000,3450)
+tile(Spikes,2050,3500)
+tile(Spikes,2100,3550)
+tile(Spikes,2200,3650)
+tile(Spikes,2150,3600)
+tile(Spikes,2250,3700)
+tile(Spikes,2350,3700)
+tile(Spikes,2300,3700)
+tile(Spikes,2400,3750)
+tile(Spikes,2500,3750)
+tile(Spikes,2450,3750)
+tile(Spikes,2550,3800)
+tile(Spikes,2600,3800)
+tile(Spikes,2700,3800)
+tile(Spikes,2650,3800)
+tile(Spikes,2800,3800)
+tile(Spikes,2900,3800)
+tile(Spikes,2850,3800)
+tile(Spikes,2950,3800)
+tile(Spikes,3050,3800)
+tile(Spikes,3000,3800)
+tile(Spikes,3100,3800)
+tile(Spikes,3200,3800)
+tile(Spikes,3250,3800)
+tile(Spikes,3150,3800)
+tile(Spikes,3300,3800)
+tile(Spikes,3400,3800)
+tile(Spikes,3350,3800)
+tile(Spikes,3450,3800)
+tile(Spikes,3500,3800)
+tile(Spikes,3600,3800)
+tile(Spikes,3650,3800)
+tile(NotificationBlock,150,3150){
+ message="Don't look. It's a long way down."
+}
+tile(Block,2700,50)
+tile(Block,2750,50)
+tile(Block,2850,50)
+tile(Block,2900,50)
+tile(Block,2800,50)
+tile(Block,2950,50)
+tile(Checkpoint,2950,0)
+tile(NotificationBlock,2900,0){
+ message="Fly like a rock."
+}
+tile(NotificationBlock,1900,2400){
+ message="Need a lift?"
+}
+tile(MovingBlock,2200,0){
+ MovingPosCount=2
+ disabled=0
+ id=7
+ loop=1
+ t0=481
+ t1=481
+ x0=-200
+ x1=0
+ y0=2400
+ y1=0
+}
+tile(PlayerStart,0,3150)
+tile(MovingBlock,2000,1100){
+ MovingPosCount=3
+ disabled=0
+ id=8
+ loop=1
+ t0=261
+ t1=482
+ t2=220
+ x0=-150
+ x1=100
+ x2=0
+ y0=1300
+ y1=-1100
+ y2=0
+}
+tile(MovingBlock,2150,1350){
+ MovingPosCount=3
+ disabled=0
+ id=9
+ loop=1
+ t0=270
+ t1=481
+ t2=210
+ x0=100
+ x1=-100
+ x2=0
+ y0=-1350
+ y1=1050
+ y2=0
+}
diff --git a/data/levels/StairwaytoHell.map b/data/levels/StairwaytoHell.map
new file mode 100644
index 0000000..9bd89a3
--- /dev/null
+++ b/data/levels/StairwaytoHell.map
@@ -0,0 +1,352 @@
+name="Stairway to Hell"
+size=1000,1350
+time=280
+tile(Block,100,700)
+tile(Block,50,700)
+tile(Block,150,700)
+tile(Block,250,700)
+tile(Block,200,700)
+tile(Spikes,300,700)
+tile(Spikes,0,700)
+tile(Spikes,0,600)
+tile(Spikes,0,650)
+tile(Spikes,0,550)
+tile(Spikes,0,500)
+tile(Spikes,0,450)
+tile(Spikes,0,400)
+tile(Spikes,0,350)
+tile(Spikes,0,300)
+tile(Spikes,0,250)
+tile(Spikes,0,200)
+tile(Spikes,0,150)
+tile(Spikes,0,100)
+tile(Spikes,0,50)
+tile(Spikes,50,50)
+tile(Spikes,150,50)
+tile(Spikes,100,50)
+tile(Spikes,250,50)
+tile(Spikes,200,50)
+tile(Spikes,300,50)
+tile(Spikes,350,50)
+tile(Spikes,850,50)
+tile(Spikes,800,50)
+tile(Spikes,750,50)
+tile(Spikes,600,50)
+tile(Spikes,550,50)
+tile(Spikes,450,50)
+tile(Spikes,400,50)
+tile(Spikes,500,50)
+tile(Spikes,650,50)
+tile(Spikes,700,50)
+tile(Spikes,350,700)
+tile(Spikes,350,650)
+tile(Spikes,400,700)
+tile(Spikes,400,650)
+tile(Spikes,400,600)
+tile(Spikes,450,700)
+tile(Spikes,450,650)
+tile(Spikes,450,600)
+tile(Spikes,450,550)
+tile(Spikes,500,700)
+tile(Spikes,500,650)
+tile(Spikes,500,600)
+tile(Spikes,500,550)
+tile(Spikes,500,500)
+tile(Spikes,550,700)
+tile(Spikes,550,650)
+tile(Spikes,550,600)
+tile(Spikes,550,550)
+tile(Spikes,550,500)
+tile(Spikes,550,450)
+tile(Spikes,600,700)
+tile(Spikes,600,650)
+tile(Spikes,600,550)
+tile(Spikes,600,600)
+tile(Spikes,600,500)
+tile(Spikes,600,450)
+tile(Spikes,600,400)
+tile(Spikes,650,700)
+tile(Spikes,650,650)
+tile(Spikes,650,600)
+tile(Spikes,650,550)
+tile(Spikes,650,500)
+tile(Spikes,650,450)
+tile(Spikes,650,400)
+tile(Spikes,650,350)
+tile(Spikes,700,700)
+tile(Spikes,700,650)
+tile(Spikes,700,600)
+tile(Spikes,700,500)
+tile(Spikes,700,550)
+tile(Spikes,700,450)
+tile(Spikes,700,400)
+tile(Spikes,700,350)
+tile(Spikes,700,300)
+tile(Spikes,750,700)
+tile(Spikes,750,650)
+tile(Spikes,750,600)
+tile(Spikes,750,550)
+tile(Spikes,750,500)
+tile(Spikes,750,450)
+tile(Spikes,750,400)
+tile(Spikes,750,350)
+tile(Spikes,750,350)
+tile(Spikes,750,300)
+tile(Spikes,750,250)
+tile(Spikes,800,700)
+tile(Spikes,800,650)
+tile(Spikes,800,600)
+tile(Spikes,800,500)
+tile(Spikes,800,550)
+tile(Spikes,800,450)
+tile(Spikes,800,400)
+tile(Spikes,800,300)
+tile(Spikes,800,350)
+tile(Spikes,800,250)
+tile(Spikes,800,200)
+tile(Teleporter,250,650){
+ automatic=0
+ destination=16
+ id=0
+}
+tile(Teleporter,50,450){
+ automatic=0
+ destination=22
+ id=15
+}
+tile(Teleporter,300,450){
+ automatic=0
+ destination=17
+ id=16
+}
+tile(Teleporter,350,250){
+ automatic=0
+ destination=19
+ id=17
+}
+tile(Teleporter,500,350){
+ automatic=0
+ destination=20
+ id=18
+}
+tile(Teleporter,550,100){
+ automatic=0
+ id=19
+}
+tile(Teleporter,700,100){
+ automatic=0
+ destination=21
+ id=20
+}
+tile(Teleporter,50,100){
+ automatic=0
+ id=21
+}
+tile(Teleporter,850,100){
+ automatic=0
+ id=22
+}
+tile(Spikes,900,50)
+tile(Spikes,900,100)
+tile(Spikes,900,150)
+tile(Spikes,900,200)
+tile(Spikes,900,250)
+tile(Spikes,900,300)
+tile(Spikes,900,350)
+tile(Spikes,900,400)
+tile(Spikes,900,500)
+tile(Spikes,900,450)
+tile(Spikes,900,550)
+tile(Spikes,900,600)
+tile(Spikes,900,750)
+tile(Spikes,900,650)
+tile(Spikes,900,700)
+tile(Spikes,900,800)
+tile(Spikes,900,850)
+tile(Spikes,900,900)
+tile(Spikes,900,950)
+tile(Spikes,800,750)
+tile(Spikes,750,750)
+tile(Spikes,700,750)
+tile(Spikes,650,750)
+tile(Spikes,600,750)
+tile(Spikes,550,750)
+tile(Spikes,500,750)
+tile(Spikes,450,750)
+tile(Spikes,350,750)
+tile(Spikes,400,750)
+tile(Spikes,300,750)
+tile(Spikes,300,800)
+tile(Spikes,400,800)
+tile(Spikes,350,800)
+tile(Spikes,300,850)
+tile(Spikes,350,850)
+tile(Spikes,350,900)
+tile(Spikes,300,900)
+tile(Spikes,400,900)
+tile(Spikes,400,850)
+tile(Spikes,500,800)
+tile(Spikes,450,800)
+tile(Spikes,450,850)
+tile(Spikes,450,900)
+tile(Spikes,500,900)
+tile(Spikes,500,850)
+tile(Spikes,550,800)
+tile(Spikes,550,850)
+tile(Spikes,550,900)
+tile(Spikes,600,900)
+tile(Spikes,600,850)
+tile(Spikes,600,800)
+tile(Spikes,650,800)
+tile(Spikes,700,800)
+tile(Spikes,750,800)
+tile(Spikes,800,800)
+tile(Spikes,700,850)
+tile(Spikes,650,850)
+tile(Spikes,650,900)
+tile(Spikes,700,900)
+tile(Spikes,700,950)
+tile(Spikes,650,950)
+tile(Spikes,600,950)
+tile(Spikes,550,950)
+tile(Spikes,500,950)
+tile(Spikes,450,950)
+tile(Spikes,400,950)
+tile(Spikes,350,950)
+tile(Spikes,300,950)
+tile(Spikes,300,1000)
+tile(Spikes,350,1000)
+tile(Spikes,450,1000)
+tile(Spikes,400,1000)
+tile(Spikes,500,1000)
+tile(Spikes,600,1000)
+tile(Spikes,550,1000)
+tile(Spikes,650,1000)
+tile(Spikes,900,1000)
+tile(Spikes,900,1050)
+tile(Spikes,900,1100)
+tile(Spikes,900,1150)
+tile(Spikes,900,1200)
+tile(Spikes,900,1250)
+tile(Spikes,800,1200)
+tile(Spikes,800,1250)
+tile(Spikes,700,1300)
+tile(Spikes,750,1300)
+tile(Spikes,850,1300)
+tile(Spikes,800,1300)
+tile(Spikes,900,1300)
+tile(Teleporter,750,1250){
+ automatic=0
+ destination=26
+ id=23
+}
+tile(Teleporter,850,1250){
+ automatic=0
+ destination=25
+ id=24
+}
+tile(Spikes,300,1050)
+tile(Spikes,350,1050)
+tile(Spikes,400,1050)
+tile(Spikes,500,1050)
+tile(Spikes,450,1050)
+tile(Spikes,550,1050)
+tile(Spikes,600,1050)
+tile(Spikes,650,1050)
+tile(Spikes,650,1100)
+tile(Spikes,650,1150)
+tile(Spikes,650,1200)
+tile(Spikes,650,1250)
+tile(Spikes,650,1300)
+tile(Spikes,550,1300)
+tile(Spikes,600,1300)
+tile(Spikes,500,1300)
+tile(Spikes,450,1300)
+tile(Spikes,400,1300)
+tile(Spikes,350,1300)
+tile(Spikes,300,1300)
+tile(Spikes,300,1250)
+tile(Spikes,300,1200)
+tile(Spikes,300,1150)
+tile(Spikes,300,1100)
+tile(Block,0,750)
+tile(Block,50,750)
+tile(Block,250,750)
+tile(Block,150,750)
+tile(Block,100,750)
+tile(Block,200,750)
+tile(Block,0,800)
+tile(Block,0,850)
+tile(Block,0,900)
+tile(Block,0,950)
+tile(Block,0,1050)
+tile(Block,0,1150)
+tile(Block,0,1200)
+tile(Block,0,1250)
+tile(Block,0,1300)
+tile(Block,50,1300)
+tile(Block,100,1300)
+tile(Block,150,1300)
+tile(Block,200,1300)
+tile(Block,250,1300)
+tile(Block,250,1200)
+tile(Block,250,1250)
+tile(Block,200,1250)
+tile(Block,100,1250)
+tile(Block,50,1250)
+tile(Block,150,1250)
+tile(Block,0,1100)
+tile(Block,0,1000)
+tile(Block,250,1150)
+tile(Block,250,1100)
+tile(Block,250,1050)
+tile(Block,250,1000)
+tile(Block,250,950)
+tile(Block,250,900)
+tile(Block,250,850)
+tile(Block,250,800)
+tile(Block,200,800)
+tile(Block,100,800)
+tile(Block,50,850)
+tile(Block,150,850)
+tile(Block,100,900)
+tile(Block,200,900)
+tile(Block,50,950)
+tile(Block,150,950)
+tile(Block,200,1000)
+tile(Block,100,1000)
+tile(Block,50,1050)
+tile(Block,150,1050)
+tile(Block,100,1100)
+tile(Block,200,1100)
+tile(Block,50,1150)
+tile(Block,150,1150)
+tile(Block,100,1200)
+tile(Exit,150,1200)
+tile(Teleporter,200,1150){
+ automatic=0
+ id=25
+}
+tile(Teleporter,350,1100){
+ automatic=0
+ id=26
+}
+tile(PlayerStart,50,500)
+tile(ShadowStart,100,500)
+tile(Block,700,150)
+tile(Spikes,750,850)
+tile(Spikes,750,900)
+tile(Spikes,800,850)
+tile(Spikes,700,1250)
+tile(Spikes,700,1200)
+tile(Spikes,700,1150)
+tile(Spikes,700,1100)
+tile(Spikes,700,1050)
+tile(Spikes,700,1000)
+tile(Spikes,850,1000)
+tile(Spikes,750,950)
+tile(Spikes,750,1000)
+tile(Swap,400,1250)
+tile(NotificationBlock,200,650){
+ message="A lot of people have gone a long way to go to hell.\nCan you match their determination?"
+}
diff --git a/icons/windows-icon/.gitignore b/icons/windows-icon/.gitignore
new file mode 100644
index 0000000..3d9ea8c
--- /dev/null
+++ b/icons/windows-icon/.gitignore
@@ -0,0 +1,2 @@
+res.aps
+
diff --git a/icons/windows-icon/res.rc b/icons/windows-icon/res.rc
index b41742a..47404aa 100644
--- a/icons/windows-icon/res.rc
+++ b/icons/windows-icon/res.rc
@@ -1,72 +1,119 @@
// Microsoft Visual C++ generated resource script.
//
#include "resource.h"
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "afxres.h"
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
-// ヨミホト(ヨミサェネヒテ?ケイコヘケ?) resources
+// 荳ュ譁?邂?菴難シ御クュ蝗ス) resources
-#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS)
-#ifdef _WIN32
-LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED
-#pragma code_page(936)
-#endif //_WIN32
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//
1 TEXTINCLUDE
BEGIN
"resource.h\0"
END
2 TEXTINCLUDE
BEGIN
"#include ""afxres.h""\r\n"
"\0"
END
3 TEXTINCLUDE
BEGIN
"\r\n"
"\0"
END
#endif // APSTUDIO_INVOKED
+#endif // 荳ュ譁?邂?菴難シ御クュ蝗ス) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+/////////////////////////////////////////////////////////////////////////////
+// 闍ア隸ュ(鄒主嵜) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
/////////////////////////////////////////////////////////////////////////////
//
// Icon
//
// Icon with lowest ID value placed first to ensure application icon
// remains consistent on all systems.
IDI_ICON1 ICON "meandmyshadow.ico"
-#endif // ヨミホト(ヨミサェネヒテ?ケイコヘケ?) resources
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 0,0,0,0
+ PRODUCTVERSION 0,0,0,0
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x40004L
+ FILETYPE 0x0L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+ VALUE "CompanyName", "https://github.com/acmepjz/meandmyshadow"
+ VALUE "FileDescription", "Me and My Shadow"
+ VALUE "FileVersion", "0.0.0.0 git deadbeef"
+ VALUE "InternalName", "meandmyshadow"
+ VALUE "LegalCopyright", "Copyright (C) 2011-2014, 2016-2018 Me and My Shadow Developers"
+ VALUE "OriginalFilename", "meandmyshadow.exe"
+ VALUE "ProductName", "Me and My Shadow"
+ VALUE "ProductVersion", "0.0.0.0 git deadbeef"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
+
+#endif // 闍ア隸ュ(鄒主嵜) resources
/////////////////////////////////////////////////////////////////////////////
#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
/////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED
diff --git a/icons/windows-icon/res.rc.in b/icons/windows-icon/res.rc.in
new file mode 100644
index 0000000..f2714e0
--- /dev/null
+++ b/icons/windows-icon/res.rc.in
@@ -0,0 +1,119 @@
+// Microsoft Visual C++ generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "afxres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// 荳ュ譁?邂?菴難シ御クュ蝗ス) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+ "#include ""afxres.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+#endif // 荳ュ譁?邂?菴難シ御クュ蝗ス) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+/////////////////////////////////////////////////////////////////////////////
+// 闍ア隸ュ(鄒主嵜) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+IDI_ICON1 ICON "meandmyshadow.ico"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION @MNMS_VERSION_NUM@
+ PRODUCTVERSION @MNMS_VERSION_NUM@
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x40004L
+ FILETYPE 0x0L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+ VALUE "CompanyName", "https://github.com/acmepjz/meandmyshadow"
+ VALUE "FileDescription", "Me and My Shadow"
+ VALUE "FileVersion", "@MNMS_VERSION_STR@"
+ VALUE "InternalName", "meandmyshadow"
+ VALUE "LegalCopyright", "Copyright (C) 2011-2014, 2016-2018 Me and My Shadow Developers"
+ VALUE "OriginalFilename", "meandmyshadow.exe"
+ VALUE "ProductName", "Me and My Shadow"
+ VALUE "ProductVersion", "@MNMS_VERSION_STR@"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
+
+#endif // 闍ア隸ュ(鄒主嵜) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/src/Addons.h b/src/Addons.h
index fee701b..9ff556c 100644
--- a/src/Addons.h
+++ b/src/Addons.h
@@ -1,160 +1,164 @@
/*
* Copyright (C) 2011-2013 Me and My Shadow
*
* This file is part of Me and My Shadow.
*
* Me and My Shadow is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Me and My Shadow is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Me and My Shadow. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ADDONS_H
#define ADDONS_H
#include "GameState.h"
#include "GameObjects.h"
#include "GUIObject.h"
#include "GUIListBox.h"
-#include "Render.h"
-
-#include <array>
#include <vector>
#include <string>
+#ifdef __APPLE__
+#include <SDL_mixer/SDL_mixer.h>
+#include <SDL_ttf/SDL_ttf.h>
+#else
+#include <SDL/SDL_mixer.h>
+#include <SDL/SDL_ttf.h>
+#endif
//The addons menu.
class Addons: public GameState,public GUIEventCallback{
private:
//The minimum addon version that is supported.
static const int MIN_VERSION=2;
//The maximum addon version that is supported.
static const int MAX_VERSION=2;
//An addon entry.
struct Addon{
//The name of the addon.
string name;
//The type of addon. (Level, Levelpack, Theme)
string type;
//The link to the addon file.
string file;
//The name of the author.
string author;
//The description of the addon.
string description;
//Icon for the addon.
SharedTexture icon;
//Screenshot for the addon.
SharedTexture screenshot;
//The latest version of the addon.
int version;
//The version that the user has installed, if installed.
int installedVersion;
//Boolean if the addon is installed.
bool installed;
//Boolean if the addon is upToDate. (installedVersion==version)
bool upToDate;
//Map that contains the content of the addon.
//NOTE: This is only filled if the addon is installed.
std::vector<std::pair<std::string,std::string> > content;
//Array that holds the name of the addons it's dependent on.
//NOTE: This is only filled if the addon is installed.
std::vector<std::pair<std::string,std::string> > dependencies;
};
//The title.
TexturePtr title;
//Placeholder icons for addons in case they don't provide custom icons.
std::array<SharedTexture, 3> addonIcon;
//Placeholder screenshot for addons in case they don't provide one.
SharedTexture screenshot;
//Map containing a vector of Addons for each addon category.
std::vector<Addon> addons;
//File pointing to the addon file in the userpath.
FILE* addon;
//String that should contain the error when something fails.
string error;
//The type of addon that is currently selected.
string type;
//Pointer to the addon that is selected.
Addon* selected;
//The list used for the selecting of the category.
GUISingleLineListBox* categoryList;
//The list used for listing the addons.
GUIListBox* list;
public:
//Constructor.
Addons(SDL_Renderer& renderer, ImageManager& imageManager);
//Destructor.
~Addons();
//Method that will create the GUI.
void createGUI(SDL_Renderer &renderer, ImageManager &imageManager);
//Method that loads that downloads the addons list.
//file: Pointer to the file to download the list to.
//Returns: True if the file is downloaded successfuly.
bool getAddonsList(FILE* file, SDL_Renderer& renderer, ImageManager& imageManager);
//
void fillAddonList(TreeStorageNode &objAddons,TreeStorageNode &objInstalledAddons,SDL_Renderer& renderer, ImageManager& imageManager);
//Put all the addons of a given type in a vector.
//type: The type the addons must be.
//Returns: Vector containing the addons.
void addonsToList(const string &type, SDL_Renderer &renderer, ImageManager &);
//Method that will save the installed addons to the installed_addons file.
//Returns: True if the file is saved successfuly.
bool saveInstalledAddons();
//Method for loading a cached image and downloading if it isn't cached.
//url: The url to the image.
//md5sum: The md5sum used for caching.
//Returns: Shared pointer to the loaded image.
SharedTexture loadCachedImage(const char* url,const char* md5sum, SDL_Renderer& renderer, ImageManager& imageManager);
//Method that will open a GUIOverlay with the an overview of the selected addon.
void showAddon(ImageManager& imageManager,SDL_Renderer& renderer);
//Inherited from GameState.
void handleEvents(ImageManager&, SDL_Renderer&) override;
void logic(ImageManager&, SDL_Renderer&) override;
void render(ImageManager&, SDL_Renderer& renderer) override;
void resize(ImageManager &imageManager, SDL_Renderer& renderer) override;
//Method used for GUI event handling.
//name: The name of the callback.
//obj: Pointer to the GUIObject that caused the event.
//eventType: The type of event: click, change, etc..
void GUIEventCallback_OnEvent(ImageManager& imageManager, SDL_Renderer& renderer, std::string name,GUIObject* obj,int eventType);
//This method will remove the addon based on the content vector.
//NOTE It doesn't check if the addon is installed or not.
//addon: The addon to remove.
void removeAddon(ImageManager& imageManager,SDL_Renderer &renderer, Addon* addon);
//This method will install the addon by downloading,extracting and reading.
//NOTE It doesn't check if the addon is installed or not.
//addon: The addon to install.
void installAddon(ImageManager& imageManager, SDL_Renderer &renderer, Addon* addon);
};
#endif
diff --git a/src/Functions.cpp b/src/Functions.cpp
index 9544df2..3eacafb 100644
--- a/src/Functions.cpp
+++ b/src/Functions.cpp
@@ -1,1769 +1,1767 @@
/*
* Copyright (C) 2011-2013 Me and My Shadow
*
* This file is part of Me and My Shadow.
*
* Me and My Shadow is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Me and My Shadow is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Me and My Shadow. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <math.h>
#include <locale.h>
#include <algorithm>
#include <SDL.h>
#include <SDL_mixer.h>
#include <SDL_syswm.h>
#include <SDL_ttf.h>
#include <string>
#include "Globals.h"
#include "Functions.h"
#include "FileManager.h"
#include "GameObjects.h"
#include "LevelPack.h"
#include "TitleMenu.h"
#include "LevelEditSelect.h"
#include "LevelEditor.h"
#include "Game.h"
#include "LevelPlaySelect.h"
#include "Addons.h"
#include "InputManager.h"
#include "ImageManager.h"
#include "MusicManager.h"
#include "SoundManager.h"
#include "ScriptExecutor.h"
#include "LevelPackManager.h"
#include "ThemeManager.h"
#include "GUIListBox.h"
#include "GUIOverlay.h"
#include "StatisticsManager.h"
#include "StatisticsScreen.h"
#include "Cursors.h"
#include "ScriptAPI.h"
#include "libs/tinyformat/tinyformat.h"
#include "libs/tinygettext/tinygettext.hpp"
#include "libs/tinygettext/log.hpp"
#include "libs/findlocale/findlocale.h"
#ifdef HARDWARE_ACCELERATION
#include <GL/gl.h>
#include <GL/glu.h>
//fix some Windows header bug
#ifndef GL_BGR
#define GL_BGR GL_BGR_EXT
#endif
#ifndef GL_BGRA
#define GL_BGRA GL_BGRA_EXT
#endif
#endif
using namespace std;
#ifdef WIN32
#include <windows.h>
#include <shlobj.h>
#else
#include <strings.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <dirent.h>
#endif
//Workaround for the resizing below 800x600 for X systems.
#if defined(__linux__) && !defined(ANDROID)
#include<X11/Xlib.h>
#include<X11/Xutil.h>
#define __X11_INCLUDED__
#endif
//Initialise the musicManager.
//The MusicManager is used to prevent loading music files multiple times and for playing/fading music.
MusicManager musicManager;
//Initialise the soundManager.
//The SoundManager is used to keep track of the sfx in the game.
SoundManager soundManager;
//Initialise the levelPackManager.
//The LevelPackManager is used to prevent loading levelpacks multiple times and for the game to know which levelpacks there are.
LevelPackManager levelPackManager;
//The scriptExecutor used for executing scripts.
ScriptExecutor scriptExecutor;
//Map containing changed settings using command line arguments.
map<string,string> tmpSettings;
//Pointer to the settings object.
//It is used to load and save the settings file and change the settings.
Settings* settings=nullptr;
SDL_Renderer* sdlRenderer=nullptr;
#ifdef HARDWARE_ACCELERATION
GLuint screenTexture;
#endif
void applySurface(int x,int y,SDL_Surface* source,SDL_Surface* dest,SDL_Rect* clip){
//The offset is needed to draw at the right location.
SDL_Rect offset;
offset.x=x;
offset.y=y;
//Let SDL do the drawing of the surface.
SDL_BlitSurface(source,clip,dest,&offset);
}
void drawRect(int x,int y,int w,int h,SDL_Renderer& renderer,Uint32 color){
//NOTE: We let SDL_gfx render it.
SDL_SetRenderDrawColor(&renderer,color >> 24,color >> 16,color >> 8,255);
//rectangleRGBA(&renderer,x,y,x+w,y+h,color >> 24,color >> 16,color >> 8,255);
const SDL_Rect r{x,y,w,h};
SDL_RenderDrawRect(&renderer,&r);
}
//Draw a box with anti-aliased borders using SDL_gfx.
void drawGUIBox(int x,int y,int w,int h,SDL_Renderer& renderer,Uint32 color){
SDL_Renderer* rd = &renderer;
//FIXME, this may get the wrong color on system with different endianness.
//Fill content's background color from function parameter
SDL_SetRenderDrawColor(rd,color >> 24,color >> 16,color >> 8,color >> 0);
{
const SDL_Rect r{x+1,y+1,w-2,h-2};
SDL_RenderFillRect(rd, &r);
}
SDL_SetRenderDrawColor(rd,0,0,0,255);
//Draw first black borders around content and leave 1 pixel in every corner
SDL_RenderDrawLine(rd,x+1,y,x+w-2,y);
SDL_RenderDrawLine(rd,x+1,y+h-1,x+w-2,y+h-1);
SDL_RenderDrawLine(rd,x,y+1,x,y+h-2);
SDL_RenderDrawLine(rd,x+w-1,y+1,x+w-1,y+h-2);
//Fill the corners with transperent color to create anti-aliased borders
SDL_SetRenderDrawColor(rd,0,0,0,160);
SDL_RenderDrawPoint(rd,x,y);
SDL_RenderDrawPoint(rd,x,y+h-1);
SDL_RenderDrawPoint(rd,x+w-1,y);
SDL_RenderDrawPoint(rd,x+w-1,y+h-1);
//Draw second lighter border around content
SDL_SetRenderDrawColor(rd,0,0,0,64);
{
const SDL_Rect r{x+1,y+1,w-2,h-2};
SDL_RenderDrawRect(rd,&r);
}
SDL_SetRenderDrawColor(rd,0,0,0,50);
//Create anti-aliasing in corners of second border
SDL_RenderDrawPoint(rd,x+1,y+1);
SDL_RenderDrawPoint(rd,x+1,y+h-2);
SDL_RenderDrawPoint(rd,x+w-2,y+1);
SDL_RenderDrawPoint(rd,x+w-2,y+h-2);
}
void drawLine(int x1,int y1,int x2,int y2,SDL_Renderer& renderer,Uint32 color){
SDL_SetRenderDrawColor(&renderer,color >> 24,color >> 16,color >> 8,255);
//NOTE: We let SDL_gfx render it.
//lineRGBA(&renderer,x1,y1,x2,y2,color >> 24,color >> 16,color >> 8,255);
SDL_RenderDrawLine(&renderer,x1,y1,x2,y2);
}
void drawLineWithArrow(int x1,int y1,int x2,int y2,SDL_Renderer& renderer,Uint32 color,int spacing,int offset,int xsize,int ysize){
//Draw line first
drawLine(x1,y1,x2,y2,renderer,color);
//calc delta and length
double dx=x2-x1;
double dy=y2-y1;
double length=sqrt(dx*dx+dy*dy);
if(length<0.001) return;
//calc the unit vector
dx/=length; dy/=length;
//Now draw arrows on it
for(double p=offset;p<length;p+=spacing){
drawLine(int(x1+p*dx+0.5),int(y1+p*dy+0.5),
int(x1+(p-xsize)*dx-ysize*dy+0.5),int(y1+(p-xsize)*dy+ysize*dx+0.5),renderer,color);
drawLine(int(x1+p*dx+0.5),int(y1+p*dy+0.5),
int(x1+(p-xsize)*dx+ysize*dy+0.5),int(y1+(p-xsize)*dy-ysize*dx+0.5),renderer,color);
}
}
ScreenData creationFailed() {
return ScreenData{ nullptr };
}
ScreenData createScreen(){
//Check if we are going fullscreen.
if(settings->getBoolValue("fullscreen"))
pickFullscreenResolution();
//Set the screen_width and height.
SCREEN_WIDTH=atoi(settings->getValue("width").c_str());
SCREEN_HEIGHT=atoi(settings->getValue("height").c_str());
//Update the camera.
camera.w=SCREEN_WIDTH;
camera.h=SCREEN_HEIGHT;
//Check if we should use gl or software rendering.
if(settings->getBoolValue("gl")){
#ifdef HARDWARE_ACCELERATION
SDL_GL_SetAttribute(SDL_GL_RED_SIZE,8);
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE,8);
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE,8);
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE,8);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE,16);
SDL_GL_SetAttribute(SDL_GL_BUFFER_SIZE,32);
SDL_GL_SetAttribute(SDL_GL_ACCUM_RED_SIZE,8);
SDL_GL_SetAttribute(SDL_GL_ACCUM_GREEN_SIZE,8);
SDL_GL_SetAttribute(SDL_GL_ACCUM_BLUE_SIZE,8);
SDL_GL_SetAttribute(SDL_GL_ACCUM_ALPHA_SIZE,8);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS,0);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES,0);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER,1);
//Set the video mode.
Uint32 flags=SDL_HWSURFACE | SDL_OPENGL;
if(settings->getBoolValue("fullscreen"))
flags|=SDL_FULLSCREEN;
else if(settings->getBoolValue("resizable"))
flags|=SDL_RESIZABLE;
if(SDL_SetVideoMode(SCREEN_WIDTH,SCREEN_HEIGHT,SCREEN_BPP,flags)==NULL){
fprintf(stderr,"FATAL ERROR: SDL_SetVideoMode failed\n");
return false;
}
//Delete the old screen.
//Warning: only if previous mode is OpenGL mode.
//NOTE: The previous mode can't switch during runtime.
if(screen){
SDL_FreeSurface(screen);
screen=NULL;
}
//Create a screen
screen=SDL_CreateRGBSurface(SDL_HWSURFACE,SCREEN_WIDTH,SCREEN_HEIGHT,32,0x00FF0000,0x0000FF00,0x000000FF,0);
//Create a texture.
glDeleteTextures(1,&screenTexture);
glGenTextures(1,&screenTexture);
//And set up gl correctly.
glClearColor(0, 0, 0, 0);
glClearDepth(1.0f);
glViewport(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 1, -1);
glMatrixMode(GL_MODELVIEW);
glEnable(GL_TEXTURE_2D);
glLoadIdentity();
#else
//NOTE: Hardware accelerated rendering requested but compiled without.
fprintf(stderr,"FATAL ERROR: Unable to use hardware acceleration (compiled without).\n");
return creationFailed();
#endif
}else{
//Set the flags.
// Uint32 flags=SCREEN_FLAGS;
//Flags are not used in SDL2.
Uint32 flags = 0;
Uint32 currentFlags = SDL_GetWindowFlags(sdlWindow);
//#if !defined(ANDROID)
// flags |= SDL_DOUBLEBUF;
//#endif
if(settings->getBoolValue("fullscreen")) {
flags|=SDL_WINDOW_FULLSCREEN; //TODO with SDL2 we can also do SDL_WINDOW_FULLSCREEN_DESKTOP
}
else if(settings->getBoolValue("resizable"))
flags|=SDL_WINDOW_RESIZABLE;
//Create the window and renderer if they don't exist and check if there weren't any errors.
if (!sdlWindow && !sdlRenderer) {
// screen=SDL_SetVideoMode(SCREEN_WIDTH,SCREEN_HEIGHT,SCREEN_BPP,flags);
SDL_CreateWindowAndRenderer(SCREEN_WIDTH, SCREEN_HEIGHT, flags, &sdlWindow, &sdlRenderer);
if(!sdlWindow || !sdlRenderer){
std::cerr << "FATAL ERROR: SDL_CreateWindowAndRenderer failed.\nError: " << SDL_GetError() << std::endl;
return creationFailed();
}
SDL_SetRenderDrawBlendMode(sdlRenderer, SDL_BlendMode::SDL_BLENDMODE_BLEND);
// White background so we see the menu on failure.
SDL_SetRenderDrawColor(sdlRenderer, 255, 255, 255, 255);
} else if (sdlWindow) {
if(SDL_SetWindowFullscreen(sdlWindow, flags & SDL_WINDOW_FULLSCREEN) != 0) {
std::cerr << "WARNING: Failed to switch to fullscreen: " << SDL_GetError() << std::endl;
};
currentFlags = SDL_GetWindowFlags(sdlWindow);
if((flags & SDL_WINDOW_FULLSCREEN ) || (currentFlags & SDL_WINDOW_FULLSCREEN_DESKTOP)) {
SDL_DisplayMode m{0,0,0,0,nullptr};
SDL_GetWindowDisplayMode(sdlWindow,&m);
m.w = SCREEN_WIDTH;
m.h = SCREEN_HEIGHT;
if(SDL_SetWindowDisplayMode(sdlWindow, &m) != 0) {
std::cerr << "WARNING: Failed to set display mode: " << SDL_GetError() << std::endl;
}
} else {
SDL_SetWindowSize(sdlWindow, SCREEN_WIDTH, SCREEN_HEIGHT);
}
}
}
//Now configure the newly created window (if windowed).
if(settings->getBoolValue("fullscreen")==false)
configureWindow();
//Set the the window caption.
SDL_SetWindowTitle(sdlWindow, ("Me and My Shadow "+version).c_str());
//FIXME Seems to be obsolete
// SDL_EnableUNICODE(1);
//Nothing went wrong so return true.
return ScreenData{sdlRenderer};
}
//Workaround for the resizing below 800x600 for Windows.
#ifdef WIN32
static WNDPROC m_OldWindowProc=NULL;
static LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam){
if(msg==WM_GETMINMAXINFO){
if(m_OldWindowProc){
CallWindowProc(m_OldWindowProc,hwnd,msg,wParam,lParam);
}else{
DefWindowProc(hwnd,msg,wParam,lParam);
}
RECT r={0,0,800,600};
AdjustWindowRect(&r,GetWindowLong(hwnd,GWL_STYLE),FALSE);
MINMAXINFO *info=(MINMAXINFO*)lParam;
info->ptMinTrackSize.x=r.right-r.left;
info->ptMinTrackSize.y=r.bottom-r.top;
return 0;
}else{
if(m_OldWindowProc){
return CallWindowProc(m_OldWindowProc,hwnd,msg,wParam,lParam);
}else{
return DefWindowProc(hwnd,msg,wParam,lParam);
}
}
}
#endif
vector<_res> getResolutionList(){
//Vector that will hold the resolutions to choose from.
vector<_res> resolutionList;
//Enumerate available resolutions using SDL_ListModes()
//NOTE: we enumerate fullscreen resolutions because
// windowed resolutions always can be arbitrary
if(resolutionList.empty()){
// SDL_Rect **modes=SDL_ListModes(NULL,SDL_FULLSCREEN|SCREEN_FLAGS|SDL_ANYFORMAT);
//NOTe - currently only using the first display (0)
int numDisplayModes = SDL_GetNumDisplayModes(0);
if(numDisplayModes < 1){
cerr<<"ERROR: Can't enumerate available screen resolutions."
" Use predefined screen resolutions list instead."<<endl;
static const _res predefinedResolutionList[] = {
{800,600},
{1024,600},
{1024,768},
{1152,864},
{1280,720},
{1280,768},
{1280,800},
{1280,960},
{1280,1024},
{1360,768},
{1366,768},
{1440,900},
{1600,900},
{1600,1200},
{1680,1080},
{1920,1080},
{1920,1200},
{2560,1440},
{3840,2160}
};
//Fill the resolutionList.
for(unsigned int i=0;i<sizeof(predefinedResolutionList)/sizeof(_res);i++){
resolutionList.push_back(predefinedResolutionList[i]);
}
}else{
//Fill the resolutionList.
for(int i=0;i < numDisplayModes; ++i){
SDL_DisplayMode mode;
int error = SDL_GetDisplayMode(0, i, &mode);
if(error < 0) {
//We failed to get a display mode. Should we crash here?
std::cerr << "ERROR: Failed to get display mode " << i << " " << std::endl;
}
//Check if the resolution is higher than the minimum (800x600).
if(mode.w >= 800 && mode.h >= 600){
_res res={mode.w, mode.h};
resolutionList.push_back(res);
}
}
//Reverse it so that we begin with the lowest resolution.
reverse(resolutionList.begin(),resolutionList.end());
}
}
//Return the resolution list.
return resolutionList;
}
void pickFullscreenResolution(){
//Get the resolution list.
vector<_res> resolutionList=getResolutionList();
//The resolution that will hold the final result, we start with the minimum (800x600).
_res closestMatch={800,600};
int width=atoi(getSettings()->getValue("width").c_str());
//int height=atoi(getSettings()->getValue("height").c_str());
//Now loop through the resolutionList.
for(int i=0;i<(int)resolutionList.size();i++){
//The delta between the closestMatch and the resolution from the list.
int dM=(closestMatch.w-resolutionList[i].w);
//The delta between the target width and the resolution from the list.
int dT=(width-resolutionList[i].w);
//Since the resolutions are getting higher the lower (more negative) the further away it is.
//That's why we check if the deltaMatch is lower than the the deltaTarget.
if((dM)<(dT)){
closestMatch.w=resolutionList[i].w;
closestMatch.h=resolutionList[i].h;
}
}
//Now set the resolution to the closest match.
char s[64];
sprintf(s,"%d",closestMatch.w);
getSettings()->setValue("width",s);
sprintf(s,"%d",closestMatch.h);
getSettings()->setValue("height",s);
}
#ifdef __X11_INCLUDED__
int handleXError(Display* disp,XErrorEvent* event){
//NOTE: This is UNTESTED code, there are still some things that should be tested/changed.
//NOTE: It checks against hardcoded opcodes, this should be based on included defines from the xf86vid headers instead.
//NOTE: This code assumes Xlib is in use, just like the resize restriction code for Linux.
//Print out the error message as normal.
char output[256];
XGetErrorText(disp,event->error_code,output,256);
cerr<<output<<endl;
//Check if the game is fullscreen.
if(getSettings()->getBoolValue("fullscreen")){
//Check for the exact error we want to handle differently.
if(event->error_code==BadValue && event->minor_code==10/*X_XF86VidModeSwitchToMode*/){
//The cause of this problem has likely something to do with fullscreen mode, so fallback to windowed.
cerr<<"ERROR: Xlib error code "<<event->error_code<<", request code "<<event->request_code<<"."<<endl;
cerr<<"ERROR: Falling back to windowed mode!"<<endl;
getSettings()->setValue("fullscreen","false");
createScreen();
return 0;
}
}
//Do the normal Xlib behaviour.
exit(1);
return 0;
}
#endif
void configureWindow(){
//We only need to configure the window if it's resizable.
if(!getSettings()->getBoolValue("resizable"))
return;
//Retrieve the WM info from SDL containing the window handle.
struct SDL_SysWMinfo wmInfo;
SDL_VERSION(&wmInfo.version);
SDL_GetWindowWMInfo(sdlWindow, &wmInfo);
#ifdef __X11_INCLUDED__
//We assume that a linux system running meandmyshadow is also running an Xorg server.
if(wmInfo.subsystem==SDL_SYSWM_X11){
//Create the size hints to give to the window.
XSizeHints* sizeHints;
if(!(sizeHints=XAllocSizeHints())){
std::cerr<<"ERROR: Unable to allocate memory for XSizeHints."<<endl;
return;
}
//Configure the size hint.
sizeHints->flags=PMinSize;
sizeHints->min_width=800;
sizeHints->min_height=600;
//Set the normal hints of the window.
//TODO: Lock func no longer exists in SDL2, is using
//XLockDisplay is correct?
//(void)wmInfo.info.x11.lock_func;
XLockDisplay(wmInfo.info.x11.display);
XSetWMNormalHints(wmInfo.info.x11.display,wmInfo.info.x11.window,sizeHints);
XUnlockDisplay(wmInfo.info.x11.display);
//(void)wmInfo.info.x11.unlock_func;
//Free size hint structure
XFree(sizeHints);
-
}else{
//No X11 so an unsupported window manager.
std::cerr<<"WARNING: Untested windowing system!"<<endl;
}
#elif defined(WIN32)
//We overwrite the window proc of SDL
WNDPROC wndproc=(WNDPROC)GetWindowLong(wmInfo.info.win.window,GWL_WNDPROC);
if(wndproc!=NULL && wndproc!=(WNDPROC)WindowProc){
m_OldWindowProc=wndproc;
SetWindowLong(wmInfo.info.win.window,GWL_WNDPROC,(LONG)(WNDPROC)WindowProc);
}
#endif
}
void onVideoResize(ImageManager& imageManager, SDL_Renderer &renderer){
//Check if the resize event isn't malformed.
if(event.window.data1<=0 || event.window.data2<=0)
return;
//Check the size limit.
//TODO: SDL2 porting note: This may break on systems non-X11 or Windows systems as the window size won't be limited
//there.
if(event.window.data1<800)
event.window.data1=800;
if(event.window.data2<600)
event.window.data2=600;
//Check if it really resizes.
if(SCREEN_WIDTH==event.window.data1 && SCREEN_HEIGHT==event.window.data2)
return;
char s[32];
//Set the new width and height.
snprintf(s,32,"%d",event.window.data1);
getSettings()->setValue("width",s);
snprintf(s,32,"%d",event.window.data2);
getSettings()->setValue("height",s);
//FIXME: THIS doesn't work properly.
//Do resizing.
if(!createScreen())
return;
//Tell the theme to resize.
if(!loadTheme(imageManager,renderer,""))
return;
//And let the currentState update it's GUI to the new resolution.
currentState->resize(imageManager, renderer);
}
ScreenData init(){
//Initialze SDL.
if(SDL_Init(SDL_INIT_EVERYTHING)==-1) {
std::cerr << "FATAL ERROR: SDL_Init failed\nError: " << SDL_GetError() << std::endl;
return creationFailed();
}
//Initialze SDL_mixer (audio).
//Note for SDL2 port: Changed frequency from 22050 to 44100.
//22050 caused some sound artifacts on my system, and I'm not sure
//why one would use it in this day and age anyhow.
//unless it's for compatability with some legacy system.
if(Mix_OpenAudio(44100,MIX_DEFAULT_FORMAT,2,512)==-1){
std::cerr << "FATAL ERROR: Mix_OpenAudio failed\nError: " << Mix_GetError() << std::endl;
return creationFailed();
}
//Set the volume.
Mix_Volume(-1,atoi(settings->getValue("sound").c_str()));
//Initialze SDL_ttf (fonts).
if(TTF_Init()==-1){
std::cerr << "FATAL ERROR: TTF_Init failed\nError: " << TTF_GetError() << std::endl;
return creationFailed();
}
#ifdef __X11_INCLUDED__
//Before creating the screen set the XErrorHandler in case of X11.
XSetErrorHandler(handleXError);
#endif
//Create the screen.
ScreenData screenData(createScreen());
if(!screenData) {
return creationFailed();
}
//Load key config. Then initialize joystick support.
inputMgr.loadConfig();
inputMgr.openAllJoysitcks();
//Init tinygettext for translations for the right language
dictionaryManager = new tinygettext::DictionaryManager();
dictionaryManager->add_directory(getDataPath()+"locale");
dictionaryManager->set_charset("UTF-8");
//Check if user have defined own language. If not, find it out for the player using findlocale
string lang=getSettings()->getValue("lang");
if(lang.length()>0){
printf("Locale set by user to %s\n",lang.c_str());
language=lang;
}else{
FL_Locale *locale;
FL_FindLocale(&locale,FL_MESSAGES);
printf("Locale isn't set by user: %s\n",locale->lang);
language=locale->lang;
if(locale->country!=NULL){
language+=string("_")+string(locale->country);
}
if(locale->variant!=NULL){
language+=string("@")+string(locale->variant);
}
FL_FreeLocale(&locale);
}
//Now set the language in the dictionaryManager.
dictionaryManager->set_language(tinygettext::Language::from_name(language));
//Disable annoying 'Couldn't translate: blah blah blah'
tinygettext::Log::set_log_info_callback(NULL);
//Set time format to the user-preference of the system.
setlocale(LC_TIME,"");
//Create the types of blocks.
for(int i=0;i<TYPE_MAX;i++){
Game::blockNameMap[Game::blockName[i]]=i;
}
//Structure that holds the event type/name pair.
struct EventTypeName{
int type;
const char* name;
};
//Create the types of game object event types.
{
const EventTypeName types[]={
{GameObjectEvent_PlayerWalkOn,"playerWalkOn"},
{GameObjectEvent_PlayerIsOn,"playerIsOn"},
{GameObjectEvent_PlayerLeave,"playerLeave"},
{GameObjectEvent_OnCreate,"onCreate"},
{GameObjectEvent_OnEnterFrame,"onEnterFrame"},
{GameObjectEvent_OnToggle,"onToggle"},
{GameObjectEvent_OnSwitchOn,"onSwitchOn"},
{GameObjectEvent_OnSwitchOff,"onSwitchOff"},
{0,NULL}
};
for(int i=0;types[i].name;i++){
Game::gameObjectEventNameMap[types[i].name]=types[i].type;
Game::gameObjectEventTypeMap[types[i].type]=types[i].name;
}
}
//Create the types of level event types.
{
const EventTypeName types[]={
{LevelEvent_OnCreate,"onCreate"},
{LevelEvent_OnSave,"onSave"},
{LevelEvent_OnLoad,"onLoad"},
{LevelEvent_OnReset,"onReset"},
{0,NULL}
};
for(int i=0;types[i].name;i++){
Game::levelEventNameMap[types[i].name]=types[i].type;
Game::levelEventTypeMap[types[i].type]=types[i].name;
}
}
//Nothing went wrong so we return true.
return screenData;
}
static TTF_Font* loadFont(const char* name,int size){
TTF_Font* tmpFont=TTF_OpenFont((getDataPath()+"font/"+name+".ttf").c_str(),size);
if(tmpFont){
return tmpFont;
}else{
#if defined(ANDROID)
//Android has built-in DroidSansFallback.ttf. (?)
return TTF_OpenFont("/system/fonts/DroidSansFallback.ttf",size);
#else
return TTF_OpenFont((getDataPath()+"font/DroidSansFallback.ttf").c_str(),size);
#endif
}
}
bool loadFonts(){
//Load the fonts.
//NOTE: This is a separate method because it will be called separately when re-initing in case of language change.
//First close the fonts if needed.
if(fontTitle)
TTF_CloseFont(fontTitle);
if(fontGUI)
TTF_CloseFont(fontGUI);
if(fontGUISmall)
TTF_CloseFont(fontGUISmall);
if(fontText)
TTF_CloseFont(fontText);
if(fontMono)
TTF_CloseFont(fontMono);
/// TRANSLATORS: Font used in GUI:
/// - Use "knewave" for languages using Latin and Latin-derived alphabets
/// - "DroidSansFallback" can be used for non-Latin writing systems
fontTitle=loadFont(_("knewave"),55);
fontGUI=loadFont(_("knewave"),32);
fontGUISmall=loadFont(_("knewave"),24);
/// TRANSLATORS: Font used for normal text:
/// - Use "Blokletters-Viltstift" for languages using Latin and Latin-derived alphabets
/// - "DroidSansFallback" can be used for non-Latin writing systems
fontText=loadFont(_("Blokletters-Viltstift"),16);
fontMono=loadFont("VeraMono",12);
if(fontTitle==NULL || fontGUI==NULL || fontGUISmall==NULL || fontText==NULL || fontMono==NULL){
printf("ERROR: Unable to load fonts! \n");
return false;
}
//Nothing went wrong so return true.
return true;
}
//Generate small arrows used for some GUI widgets.
static void generateArrows(SDL_Renderer& renderer){
TTF_Font* fontArrow=loadFont(_("knewave"),18);
arrowLeft1=textureFromText(renderer,*fontArrow,"<",themeTextColor);
arrowRight1=textureFromText(renderer,*fontArrow,">",themeTextColor);
arrowLeft2=textureFromText(renderer,*fontArrow,"<",themeTextColorDialog);
arrowRight2=textureFromText(renderer,*fontArrow,">",themeTextColorDialog);
TTF_CloseFont(fontArrow);
}
bool loadTheme(ImageManager& imageManager,SDL_Renderer& renderer,std::string name){
//Load default fallback theme if it isn't loaded yet
if(objThemes.themeCount()==0){
if(objThemes.appendThemeFromFile(getDataPath()+"themes/Cloudscape/theme.mnmstheme", imageManager, renderer)==NULL){
printf("ERROR: Can't load default theme file\n");
return false;
}
}
//Resize background or load specific theme
bool success=true;
if(name==""||name.empty()){
objThemes.scaleToScreen();
}else{
string theme=processFileName(name);
if(objThemes.appendThemeFromFile(theme+"/theme.mnmstheme", imageManager, renderer)==NULL){
printf("ERROR: Can't load theme %s\n",theme.c_str());
success=false;
}
}
generateArrows(renderer);
//Everything went fine so return true.
return success;
}
static SDL_Cursor* loadCursor(const char* image[]){
int i,row,col;
//The array that holds the data (0=white 1=black)
Uint8 data[4*32];
//The array that holds the alpha mask (0=transparent 1=visible)
Uint8 mask[4*32];
//The coordinates of the hotspot of the cursor.
int hotspotX, hotspotY;
i=-1;
//Loop through the rows and columns.
//NOTE: We assume a cursor size of 32x32.
for(row=0;row<32;++row){
for(col=0; col<32;++col){
if(col % 8) {
data[i]<<=1;
mask[i]<<=1;
}else{
++i;
data[i]=mask[i]=0;
}
switch(image[4+row][col]){
case '+':
data[i] |= 0x01;
mask[i] |= 0x01;
break;
case '.':
mask[i] |= 0x01;
break;
default:
break;
}
}
}
//Get the hotspot x and y locations from the last line of the cursor.
sscanf(image[4+row],"%d,%d",&hotspotX,&hotspotY);
return SDL_CreateCursor(data,mask,32,32,hotspotX,hotspotY);
}
bool loadFiles(ImageManager& imageManager, SDL_Renderer& renderer){
//Load the fonts.
if(!loadFonts())
return false;
//Show a loading screen
{
int w = 0,h = 0;
SDL_GetRendererOutputSize(&renderer, &w, &h);
SDL_Color fg={255,255,255,0};
TexturePtr loadingTexture = textureFromText(renderer, *fontTitle, _("Loading..."),fg);
SDL_Rect loadingRect = rectFromTexture(*loadingTexture);
loadingRect.x = (w-loadingRect.w)/2;
loadingRect.y = (h-loadingRect.h)/2;
SDL_RenderCopy(sdlRenderer, loadingTexture.get(), NULL, &loadingRect);
SDL_RenderPresent(sdlRenderer);
SDL_RenderClear(sdlRenderer);
}
musicManager.destroy();
//Load the music and play it.
if(musicManager.loadMusic((getDataPath()+"music/menu.music")).empty()){
printf("WARNING: Unable to load background music! \n");
}
musicManager.playMusic("menu",false);
//Load all the music lists from the data and user data path.
{
vector<string> musicLists=enumAllFiles((getDataPath()+"music/"),"list",true);
for(unsigned int i=0;i<musicLists.size();i++)
getMusicManager()->loadMusicList(musicLists[i]);
musicLists=enumAllFiles((getUserPath(USER_DATA)+"music/"),"list",true);
for(unsigned int i=0;i<musicLists.size();i++)
getMusicManager()->loadMusicList(musicLists[i]);
}
//Set the list to the configured one.
getMusicManager()->setMusicList(getSettings()->getValue("musiclist"));
//Check if music is enabled.
if(getSettings()->getBoolValue("music"))
getMusicManager()->setEnabled();
//Load the sound effects
soundManager.loadSound((getDataPath()+"sfx/jump.wav").c_str(),"jump");
soundManager.loadSound((getDataPath()+"sfx/hit.wav").c_str(),"hit");
soundManager.loadSound((getDataPath()+"sfx/checkpoint.wav").c_str(),"checkpoint");
soundManager.loadSound((getDataPath()+"sfx/swap.wav").c_str(),"swap");
soundManager.loadSound((getDataPath()+"sfx/toggle.ogg").c_str(),"toggle");
soundManager.loadSound((getDataPath()+"sfx/error.wav").c_str(),"error");
soundManager.loadSound((getDataPath()+"sfx/collect.wav").c_str(),"collect");
soundManager.loadSound((getDataPath()+"sfx/achievement.ogg").c_str(),"achievement");
//Load the cursor images from the Cursor.h file.
cursors[CURSOR_POINTER]=loadCursor(pointer);
cursors[CURSOR_CARROT]=loadCursor(ibeam);
cursors[CURSOR_DRAG]=loadCursor(closedhand);
cursors[CURSOR_SIZE_HOR]=loadCursor(size_hor);
cursors[CURSOR_SIZE_VER]=loadCursor(size_ver);
cursors[CURSOR_SIZE_FDIAG]=loadCursor(size_fdiag);
cursors[CURSOR_SIZE_BDIAG]=loadCursor(size_bdiag);
cursors[CURSOR_REMOVE]=loadCursor(remove_cursor);
//Set the default cursor right now.
SDL_SetCursor(cursors[CURSOR_POINTER]);
levelPackManager.destroy();
//Now sum up all the levelpacks.
vector<string> v=enumAllDirs(getDataPath()+"levelpacks/");
for(vector<string>::iterator i=v.begin(); i!=v.end(); ++i){
levelPackManager.loadLevelPack(getDataPath()+"levelpacks/"+*i);
}
v=enumAllDirs(getUserPath(USER_DATA)+"levelpacks/");
for(vector<string>::iterator i=v.begin(); i!=v.end(); ++i){
levelPackManager.loadLevelPack(getUserPath(USER_DATA)+"levelpacks/"+*i);
}
v=enumAllDirs(getUserPath(USER_DATA)+"custom/levelpacks/");
for(vector<string>::iterator i=v.begin(); i!=v.end(); ++i){
levelPackManager.loadLevelPack(getUserPath(USER_DATA)+"custom/levelpacks/"+*i);
}
//Now we add a special levelpack that will contain the levels not in a levelpack.
LevelPack* levelsPack=new LevelPack;
levelsPack->levelpackName="Levels";
levelsPack->levelpackPath="Levels/";
//NOTE: Set the type of 'levels' to main so it won't be added to the custom packs, even though it contains non-main levels.
levelsPack->type=COLLECTION;
LevelPack* customLevelsPack=new LevelPack;
customLevelsPack->levelpackName="Custom Levels";
customLevelsPack->levelpackPath="Custom Levels/";
customLevelsPack->type=COLLECTION;
//List the addon levels and add them one for one.
v=enumAllFiles(getUserPath(USER_DATA)+"levels/");
for(vector<string>::iterator i=v.begin(); i!=v.end(); ++i){
levelsPack->addLevel(getUserPath(USER_DATA)+"levels/"+*i);
levelsPack->setLocked(levelsPack->getLevelCount()-1);
}
//List the custom levels and add them one for one.
v=enumAllFiles(getUserPath(USER_DATA)+"custom/levels/");
for(vector<string>::iterator i=v.begin(); i!=v.end(); ++i){
levelsPack->addLevel(getUserPath(USER_DATA)+"custom/levels/"+*i);
levelsPack->setLocked(levelsPack->getLevelCount()-1);
customLevelsPack->addLevel(getUserPath(USER_DATA)+"custom/levels/"+*i);
customLevelsPack->setLocked(customLevelsPack->getLevelCount()-1);
}
//Add them to the manager.
levelPackManager.addLevelPack(levelsPack);
levelPackManager.addLevelPack(customLevelsPack);
//Load statistics
statsMgr.loadPicture(renderer, imageManager);
statsMgr.registerAchievements(imageManager);
statsMgr.loadFile(getUserPath(USER_CONFIG)+"statistics");
//Do something ugly and slow
statsMgr.reloadCompletedLevelsAndAchievements();
statsMgr.reloadOtherAchievements();
//Load the theme, both menu and default.
//NOTE: Loading theme may fail and returning false would stop everything, default theme will be used instead.
if (!loadTheme(imageManager,renderer,getSettings()->getValue("theme"))){
getSettings()->setValue("theme","%DATA%/themes/Cloudscape");
saveSettings();
}
//Nothing failed so return true.
return true;
}
bool loadSettings(){
settings=new Settings(getUserPath(USER_CONFIG)+"meandmyshadow.cfg");
settings->parseFile();
//Now apply settings changed through command line arguments, if any.
map<string,string>::iterator it;
for(it=tmpSettings.begin();it!=tmpSettings.end();++it){
settings->setValue(it->first,it->second);
}
tmpSettings.clear();
//Always return true?
return true;
}
bool saveSettings(){
return settings->save();
}
Settings* getSettings(){
return settings;
}
MusicManager* getMusicManager(){
return &musicManager;
}
SoundManager* getSoundManager(){
return &soundManager;
}
LevelPackManager* getLevelPackManager(){
return &levelPackManager;
}
ScriptExecutor* getScriptExecutor(){
return &scriptExecutor;
}
void flipScreen(SDL_Renderer& renderer){
// Render the data from the back buffer.
SDL_RenderPresent(&renderer);
}
void clean(){
//Save statistics
statsMgr.saveFile(getUserPath(USER_CONFIG)+"statistics");
//We delete the settings.
if(settings){
delete settings;
settings=NULL;
}
//Delete dictionaryManager.
delete dictionaryManager;
//Get rid of the currentstate.
//NOTE: The state is probably already deleted by the changeState function.
if(currentState)
delete currentState;
//Destroy the GUI if present.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
//These calls to destroy makes sure stuff is
//deleted before SDL is uninitialised (as these managers are stack allocated
//globals.)
//Destroy the musicManager.
musicManager.destroy();
//Destroy all sounds
soundManager.destroy();
//Destroy the cursors.
for(int i=0;i<CURSOR_MAX;i++){
SDL_FreeCursor(cursors[i]);
cursors[i]=NULL;
}
//Destroy the levelPackManager.
levelPackManager.destroy();
levels=NULL;
//Close all joysticks.
inputMgr.closeAllJoysticks();
//Close the fonts and quit SDL_ttf.
TTF_CloseFont(fontTitle);
TTF_CloseFont(fontGUI);
TTF_CloseFont(fontGUISmall);
TTF_CloseFont(fontText);
TTF_CloseFont(fontMono);
TTF_Quit();
//Remove the temp surface.
SDL_DestroyRenderer(sdlRenderer);
SDL_DestroyWindow(sdlWindow);
arrowLeft1.reset(nullptr);
arrowLeft2.reset(nullptr);
arrowRight1.reset(nullptr);
arrowRight2.reset(nullptr);
//Stop audio.and quit
Mix_CloseAudio();
//SDL2 porting note. Not sure why this was only done on apple.
//#ifndef __APPLE__
Mix_Quit();
//#endif
//And finally quit SDL.
SDL_Quit();
}
void setNextState(int newstate){
//Only change the state when we aren't already exiting.
if(nextState!=STATE_EXIT){
nextState=newstate;
}
}
void changeState(ImageManager& imageManager, SDL_Renderer& renderer){
//Check if there's a nextState.
if(nextState!=STATE_NULL){
//Delete the currentState.
delete currentState;
currentState=NULL;
//Set the currentState to the nextState.
stateID=nextState;
nextState=STATE_NULL;
//Init the state.
switch(stateID){
case STATE_GAME:
{
currentState=NULL;
Game* game=new Game(renderer, imageManager);
currentState=game;
//Check if we should load record file or a level.
if(!Game::recordFile.empty()){
game->loadRecord(imageManager,renderer,Game::recordFile.c_str());
Game::recordFile.clear();
}else{
game->loadLevel(imageManager,renderer,levels->getLevelFile());
levels->saveLevelProgress();
}
}
break;
case STATE_MENU:
currentState=new Menu(imageManager, renderer);
break;
case STATE_LEVEL_SELECT:
currentState=new LevelPlaySelect(imageManager, renderer);
break;
case STATE_LEVEL_EDIT_SELECT:
currentState=new LevelEditSelect(imageManager, renderer);
break;
case STATE_LEVEL_EDITOR:
{
currentState=NULL;
LevelEditor* levelEditor=new LevelEditor(renderer, imageManager);
currentState=levelEditor;
//Load the selected level.
levelEditor->loadLevel(imageManager,renderer,levels->getLevelFile());
}
break;
case STATE_OPTIONS:
currentState=new Options(imageManager, renderer);
break;
case STATE_ADDONS:
currentState=new Addons(renderer, imageManager);
break;
case STATE_CREDITS:
currentState=new Credits(imageManager,renderer);
break;
case STATE_STATISTICS:
currentState=new StatisticsScreen(imageManager,renderer);
break;
}
//NOTE: STATE_EXIT isn't mentioned, meaning that currentState is null.
//This way the game loop will break and the program will exit.
//Fade out, if fading is enabled.
int fade=0;
if(settings->getBoolValue("fading"))
fade=255;
while(fade>0){
// FIXME: Disabled for now
fade-=17;
if(fade<0)
fade=0;
dimScreen(renderer, static_cast<Uint8>(255-fade));
flipScreen(renderer);
SDL_Delay(25);
}
}
}
void musicStoppedHook(){
//We just call the musicStopped method of the MusicManager.
musicManager.musicStopped();
}
void channelFinishedHook(int channel){
soundManager.channelFinished(channel);
}
bool checkCollision(const SDL_Rect& a,const SDL_Rect& b){
//Check if the left side of box a isn't past the right side of b.
if(a.x>=b.x+b.w){
return false;
}
//Check if the right side of box a isn't left of the left side of b.
if(a.x+a.w<=b.x){
return false;
}
//Check if the top side of box a isn't under the bottom side of b.
if(a.y>=b.y+b.h){
return false;
}
//Check if the bottom side of box a isn't above the top side of b.
if(a.y+a.h<=b.y){
return false;
}
//We have collision.
return true;
}
int parseArguments(int argc, char** argv){
//Loop through all arguments.
//We start at one since 0 is the command itself.
for(int i=1;i<argc;i++){
string argument=argv[i];
//Check if the argument is the data-dir.
if(argument=="--data-dir"){
//We need a second argument so we increase i.
i++;
if(i>=argc){
printf("ERROR: Missing argument for command '%s'\n\n",argument.c_str());
return -1;
}
//Configure the dataPath with the given path.
dataPath=argv[i];
if(!getDataPath().empty()){
char c=dataPath[dataPath.size()-1];
if(c!='/'&&c!='\\') dataPath+="/";
}
}else if(argument=="--user-dir"){
//We need a second argument so we increase i.
i++;
if(i>=argc){
printf("ERROR: Missing argument for command '%s'\n\n",argument.c_str());
return -1;
}
//Configure the userPath with the given path.
userPath=argv[i];
if(!userPath.empty()){
char c=userPath[userPath.size()-1];
if(c!='/'&&c!='\\') userPath+="/";
}
}else if(argument=="-f" || argument=="-fullscreen" || argument=="--fullscreen"){
tmpSettings["fullscreen"]="1";
}else if(argument=="-w" || argument=="-windowed" || argument=="--windowed"){
tmpSettings["fullscreen"]="0";
}else if(argument=="-mv" || argument=="-music" || argument=="--music"){
//We need a second argument so we increase i.
i++;
if(i>=argc){
printf("ERROR: Missing argument for command '%s'\n\n",argument.c_str());
return -1;
}
//Now set the music volume.
tmpSettings["music"]=argv[i];
}else if(argument=="-sv" || argument=="-sound" || argument=="--sound"){
//We need a second argument so we increase i.
i++;
if(i>=argc){
printf("ERROR: Missing argument for command '%s'\n\n",argument.c_str());
return -1;
}
//Now set sound volume.
tmpSettings["sound"]=argv[i];
}else if(argument=="-set" || argument=="--set"){
//We need a second and a third argument so we increase i.
i+=2;
if(i>=argc){
printf("ERROR: Missing argument for command '%s'\n\n",argument.c_str());
return -1;
}
//And set the setting.
tmpSettings[argv[i-1]]=argv[i];
}else if(argument=="-v" || argument=="-version" || argument=="--version"){
//Print the version.
printf("%s\n",version.c_str());
return 0;
}else if(argument=="-h" || argument=="-help" || argument=="--help"){
//If the help is requested we'll return false without printing an error.
//This way the usage/help text will be printed.
return -1;
}else{
//Any other argument is unknow so we return false.
printf("ERROR: Unknown argument %s\n\n",argument.c_str());
return -1;
}
}
//If everything went well we can return true.
return 1;
}
//Special structure that will recieve the GUIEventCallbacks of the messagebox.
struct msgBoxHandler:public GUIEventCallback{
public:
//Integer containing the ret(urn) value of the messageBox.
int ret;
public:
//Constructor.
msgBoxHandler():ret(0){}
void GUIEventCallback_OnEvent(ImageManager& imageManager, SDL_Renderer& renderer, std::string name,GUIObject* obj,int eventType){
//Make sure it's a click event.
if(eventType==GUIEventClick){
//Set the return value.
ret=obj->value;
//After a click event we can delete the GUI.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
}
}
};
msgBoxResult msgBox(ImageManager& imageManager,SDL_Renderer& renderer, string prompt,msgBoxButtons buttons,const string& title){
//Create the event handler.
msgBoxHandler objHandler;
//The GUI objects.
GUIObject* obj;
//Create the GUIObjectRoot, the height and y location is temp.
//It depends on the content what it will be.
GUIObject* root=new GUIFrame(imageManager,renderer,(SCREEN_WIDTH-600)/2,200,600,200,title.c_str());
//Integer containing the current y location used to grow dynamic depending on the content.
int y=50;
//Now process the prompt.
{
//Pointer to the string.
char* lps=(char*)prompt.c_str();
//Pointer to a character.
char* lp=NULL;
//We keep looping forever.
//The only way out is with the break statement.
for(;;){
//As long as it's still the same sentence we continue.
//It will stop when there's a newline or end of line.
for(lp=lps;*lp!='\n'&&*lp!='\r'&&*lp!=0;lp++);
//Store the character we stopped on. (End or newline)
char c=*lp;
//Set the character in the string to 0, making lps a string containing one sentence.
*lp=0;
//Add a GUIObjectLabel with the sentence.
root->addChild(new GUILabel(imageManager,renderer,0,y,root->width,25,lps,0,true,true,GUIGravityCenter));
//Increase y with 25, about the height of the text.
y+=25;
//Check the stored character if it was a stop.
if(c==0){
//It was so break out of the for loop.
lps=lp;
break;
}
//It wasn't meaning more will follow.
//We set lps to point after the "newline" forming a new string.
lps=lp+1;
}
}
//Add 70 to y to leave some space between the content and the buttons.
y+=70;
//Recalc the size of the message box.
root->top=(SCREEN_HEIGHT-y)/2;
root->height=y;
//Now we need to add the buttons.
//Integer containing the number of buttons to add.
int count=0;
//Array with the return codes for the buttons.
int value[3]={0};
//Array containing the captation for the buttons.
string button[3]={"","",""};
switch(buttons){
case MsgBoxOKCancel:
count=2;
button[0]=_("OK");value[0]=MsgBoxOK;
button[1]=_("Cancel");value[1]=MsgBoxCancel;
break;
case MsgBoxAbortRetryIgnore:
count=3;
button[0]=_("Abort");value[0]=MsgBoxAbort;
button[1]=_("Retry");value[1]=MsgBoxRetry;
button[2]=_("Ignore");value[2]=MsgBoxIgnore;
break;
case MsgBoxYesNoCancel:
count=3;
button[0]=_("Yes");value[0]=MsgBoxYes;
button[1]=_("No");value[1]=MsgBoxNo;
button[2]=_("Cancel");value[2]=MsgBoxCancel;
break;
case MsgBoxYesNo:
count=2;
button[0]=_("Yes");value[0]=MsgBoxYes;
button[1]=_("No");value[1]=MsgBoxNo;
break;
case MsgBoxRetryCancel:
count=2;
button[0]=_("Retry");value[0]=MsgBoxRetry;
button[1]=_("Cancel");value[1]=MsgBoxCancel;
break;
default:
count=1;
button[0]=_("OK");value[0]=MsgBoxOK;
break;
}
//Now we start making the buttons.
{
//Reduce y so that the buttons fit inside the frame.
y-=40;
double places[3]={0.0};
if(count==1){
places[0]=0.5;
}else if(count==2){
places[0]=0.4;
places[1]=0.6;
}else if(count==3){
places[0]=0.3;
places[1]=0.5;
places[2]=0.7;
}
//Loop to add the buttons.
for(int i=0;i<count;i++){
obj=new GUIButton(imageManager,renderer,root->width*places[i],y,-1,36,button[i].c_str(),value[i],true,true,GUIGravityCenter);
obj->eventCallback=&objHandler;
root->addChild(obj);
}
}
//Now we dim the screen and keep the GUI rendering/updating.
GUIOverlay* overlay=new GUIOverlay(renderer,root);
overlay->enterLoop(imageManager,renderer, true);
//And return the result.
return (msgBoxResult)objHandler.ret;
}
struct fileDialogHandler:public GUIEventCallback{
public:
//The ret(urn) value, true=ok and false=cancel
bool ret;
//Boolean if it's a save dialog.
bool isSave;
//Boolean if the file should be verified.
bool verifyFile;
//Boolean if files should be listed instead of directories.
bool files;
//Pointer to the textfield containing the filename.
GUIObject* txtName;
//Pointer to the listbox containing the different files.
GUIListBox* lstFile;
//The extension the files listed should have.
const char* extension;
//The current filename.
string fileName;
//The current search path.
string path;
//Vector containing the search paths.
vector<string> searchPath;
public:
//Constructor.
fileDialogHandler(bool isSave=false,bool verifyFile=false, bool files=true):ret(false),
isSave(isSave),verifyFile(verifyFile),
files(files),txtName(NULL),lstFile(NULL),extension(NULL){}
void GUIEventCallback_OnEvent(ImageManager& imageManager, SDL_Renderer& renderer,std::string name,GUIObject* obj,int /*eventType*/) override {
//Check for the ok event.
if(name=="cmdOK"){
//Get the entered fileName from the text field.
std::string s=txtName->caption;
//If it doesn't contain a slash we need to add the path to the fileName.
if(s.find_first_of("/")==string::npos)
s=path+s;
//If the string empty we return.
if(s.empty() || s.find_first_of("*?")!=string::npos)
return;
//We only need to check for extensions if it isn't a folder dialog.
if(files){
//If there isn't right extension add it.
size_t found=s.find_first_of(".");
if(found!=string::npos)
s.replace(s.begin()+found+1,s.end(),extension);
else if (s.substr(found+1)!=extension)
s.append(string(".")+extension);
}
//Check if we should save or load the file.
//
if(isSave){
//Open the file with read permission to check if it already exists.
FILE* f;
f=fopen(processFileName(s).c_str(),"rb");
//Check if it exists.
if(f){
//Close the file.
fclose(f);
//Let the currentState render once to prevent multiple GUI overlapping and prevent the screen from going black.
currentState->render(imageManager,renderer);
//Prompt the user with a Yes or No question.
/// TRANSLATORS: Filename is coming before this text
if(msgBox(imageManager,renderer, tfm::format(_("%s already exists.\nDo you want to overwrite it?"),s),MsgBoxYesNo,_("Overwrite Prompt"))!=MsgBoxYes){
//He answered no, so we return.
return;
}
}
//Check if we should verify the file.
//Verifying only applies to files not to directories.
if(verifyFile && files){
//Open the file with write permission.
f=fopen(processFileName(s).c_str(),"wb");
//Check if their aren't problems.
if(f){
//Close the file.
fclose(f);
}else{
//Let the currentState render once to prevent multiple GUI overlapping and prevent the screen from going black.
currentState->render(imageManager,renderer);
//The file can't be opened so tell the user.
msgBox(imageManager,renderer, tfm::format(_("Can't open file %s."),s),MsgBoxOKOnly,_("Error"));
return;
}
}
}else if(verifyFile && files){
//We need to verify a file for opening.
FILE *f;
f=fopen(processFileName(s).c_str(),"rb");
//Check if it didn't fail.
if(f){
//Succes, so close the file.
fclose(f);
}else{
//Let the currentState render once to prevent multiple GUI overlapping and prevent the screen from going black.
currentState->render(imageManager,renderer);
//Unable to open file so tell the user.
msgBox(imageManager,renderer, tfm::format(_("Can't open file %s."),s),MsgBoxOKOnly,_("Error"));
return;
}
}
//If we haven't returned then it's fine.
//Set the fileName to the chosen file.
fileName=s;
//Delete the GUI.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
//Set return to true.
ret=true;
}else if(name=="cmdCancel"){
//Cancel means we can kill the gui.
if(GUIObjectRoot){
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
}else if(name=="lstFile"){
//Get a pointer to the listbox.
GUIListBox* obj1=lstFile;
//Make sure the option exist and change textfield to it.
if(obj1!=NULL && txtName!=NULL && obj1->value>=0 && obj1->value<(int)obj1->item.size()){
txtName->caption=obj1->item[obj1->value];
}
}else if(name=="lstSearchIn"){
//Get the searchpath listbox.
GUISingleLineListBox *obj1=dynamic_cast<GUISingleLineListBox*>(obj);
//Check if the entry exists.
if(obj1!=NULL && lstFile!=NULL && obj1->value>=0 && obj1->value<(int)searchPath.size()){
//Temp string.
string s;
//Get the new search path.
path=searchPath[obj1->value];
//Make sure it isn't empty.
if(!path.empty()){
//Process the filename.
s=processFileName(path);
}else{
//It's empty so we give the userpath.
s=getUserPath();
}
//Fill the list with files or directories.
if(files) {
lstFile->item=enumAllFiles(s,extension);
}else
lstFile->item=enumAllDirs(s);
//Remove any selection from the list.
lstFile->value=-1;
}
}
}
};
//SDL2 port note: Commented this out since it was unused.
/*
bool fileDialog(ImageManager& imageManager,SDL_Renderer& renderer, string& fileName,const char* title,const char* extension,const char* path,bool isSave,bool verifyFile,bool files){
//Pointer to GUIObject to make the GUI with.
GUIObject* obj;
//Create the fileDialogHandler, used for event handling.
fileDialogHandler objHandler(isSave,verifyFile,files);
//Vector containing the pathNames.
vector<string> pathNames;
//Set the extension of the objHandler.
objHandler.extension=extension;
//We now need to splits the given path into multiple path names.
if(path && path[0]){
//The string isn't empty.
//Pointer to the paths string.
char* lp=(char*)path;
//Pointer to the first newline.
char* lps=strchr(lp,'\n');
//Pointer used for checking if their's another newline.
//It will indicate if it's the last set or not.
char* lpe;
//Check for a newline.
if(lps){
//We have newline(s) so loop forever.
//We can only break out of the loop when the string ends.
for(;;){
//Add the first searchpath.
//This is the beginning of the string (lp) to the first newline. (lps)
objHandler.searchPath.push_back(string(lp,lps-lp));
//We should have another newline so search for it.
lpe=strchr(lps+1,'\n');
if(lpe){
//We found it so we add that to the pathname.
pathNames.push_back(string(lps+1,lpe-lps-1));
//And start over again by setting lp to the start of a new set of searchPath/pathName.
lp=lpe+1;
}else{
//There is no newline anymore, meaning the last entry, the rest of the string must be the pathName.
pathNames.push_back(string(lps+1));
//And break out of the loop.
break;
}
//We haven't broken out so search for a newline.
lps=strchr(lp,'\n');
//If there isn't a newline break.
if(!lps)
break;
}
}else{
//There is no newline thus the whole string is the searchPath.
objHandler.searchPath.push_back(path);
}
}else{
//Empty so put an empty string as searchPath.
objHandler.searchPath.push_back(string());
}
//It's time to create the GUI.
//If there are more than one pathNames we need to add a GUISingleLineListBox.
int base_y=pathNames.empty()?20:60;
//Create the frame.
GUIObject* root=new GUIFrame(100,100-base_y/2,600,400+base_y,title?title:(isSave?_("Save File"):_("Load File")));
//Create the search path list box if needed.
if(!pathNames.empty()){
root->addChild(new GUILabel(8,40,184,36,_("Search In")));
GUISingleLineListBox* obj1=new GUISingleLineListBox(160,40,432,36);
obj1->addItems(pathNames);
obj1->value=0;
obj1->name="lstSearchIn";
obj1->eventCallback=&objHandler;
root->addChild(obj1);
}
//Add the FileName label and textfield.
root->addChild(new GUILabel(8,20+base_y,184,36,_("File Name")));
{
//Fill the textbox with the given fileName.
string s=fileName;
if(!isSave){
//But only if it isn't empty.
if(s.empty() && extension && extension[0])
s=string("*.")+string(extension);
}
//Create the textbox and add it to the GUI.
objHandler.txtName=new GUITextBox(160,20+base_y,432,36,s.c_str());
root->addChild(objHandler.txtName);
}
//Now we add the ListBox containing the files or directories.
{
GUIListBox* obj1=new GUIListBox(8,60+base_y,584,292);
//Get the searchPath.
string s=objHandler.searchPath[0];
//Make sure it isn't empty.
if(!s.empty()){
objHandler.path=s;
s=processFileName(s);
}else{
s=getUserPath();
}
//Check if we should list files or directories.
if(files){
//Fill the list with files.
obj1->item=enumAllFiles(s,extension);
}else{
//Fill the list with directories.
obj1->item=enumAllDirs(s);
}
obj1->name="lstFile";
obj1->eventCallback=&objHandler;
root->addChild(obj1);
objHandler.lstFile=obj1;
}
//Now create the OK and Cancel buttons.
obj=new GUIButton(200,360+base_y,192,36,_("OK"));
obj->name="cmdOK";
obj->eventCallback=&objHandler;
root->addChild(obj);
obj=new GUIButton(400,360+base_y,192,36,_("Cancel"));
obj->name="cmdCancel";
obj->eventCallback=&objHandler;
root->addChild(obj);
//Create the gui overlay.
GUIOverlay* overlay=new GUIOverlay(renderer,root);
overlay->enterLoop(imageManager,renderer);
//Now determine what the return value is (and if there is one).
if(objHandler.ret)
fileName=objHandler.fileName;
return objHandler.ret;
}
-*/
diff --git a/src/GUIListBox.cpp b/src/GUIListBox.cpp
index 9dbaac7..491675b 100644
--- a/src/GUIListBox.cpp
+++ b/src/GUIListBox.cpp
@@ -1,556 +1,555 @@
/*
* Copyright (C) 2011-2012 Me and My Shadow
*
* This file is part of Me and My Shadow.
*
* Me and My Shadow is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Me and My Shadow is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Me and My Shadow. If not, see <http://www.gnu.org/licenses/>.
*/
-#include "Functions.h"
#include "GUIListBox.h"
using namespace std;
namespace {
inline int tHeight(const SharedTexture& t) {
if(t) {
return rectFromTexture(*t).h;
} else {
return 0;
}
}
inline int tWidth(const SharedTexture& t) {
if(t) {
return rectFromTexture(*t).w;
} else {
return 0;
}
}
}
GUIListBox::GUIListBox(ImageManager& imageManager, SDL_Renderer& renderer,int left,int top,int width,int height,bool enabled,bool visible,int gravity):
GUIObject(imageManager,renderer,left,top,width,height,NULL,-1,enabled,visible,gravity),selectable(true),clickEvents(false){
//Set the state -1.
state=-1;
//Create the scrollbar and add it to the children.
scrollBar=new GUIScrollBar(imageManager,renderer,width-16,0,16,height,1,0,0,0,1,1,true,true);
childControls.push_back(scrollBar);
}
GUIListBox::~GUIListBox(){
//Remove all items and cache.
clearItems();
//We need to delete every child we have.
for(unsigned int i=0;i<childControls.size();i++){
delete childControls[i];
}
//Deleted the childs now empty the childControls vector.
childControls.clear();
}
bool GUIListBox::handleEvents(SDL_Renderer& renderer,int x,int y,bool enabled,bool visible,bool processed){
//Boolean if the event is processed.
bool b=processed;
//The GUIObject is only enabled when he and his parent are enabled.
enabled=enabled && this->enabled;
//The GUIObject is only enabled when he and his parent are enabled.
visible=visible && this->visible;
//Get the absolute position.
x+=left;
y+=top;
//Update the scrollbar.
if(scrollBar->visible)
b=b||scrollBar->handleEvents(renderer,x,y,enabled,visible,b);
//Set state negative.
state=-1;
//Check if the GUIListBox is visible, enabled and no event has been processed before.
if(enabled&&visible&&!b){
//The mouse location (x=i, y=j) and the mouse button (k).
int i,j;
SDL_GetMouseState(&i,&j);
//Convert the mouse location to a relative location.
i-=x;
j-=y;
//Check if the mouse is inside the GUIListBox.
if(i>=0&&i<width-4&&j>=0&&j<height-4){
//Calculate selected item.
int idx=-1;
int yPos=-firstItemY;
int i=scrollBar->value;
if(yPos!=0) i--;
for(;i<images.size();++i){
const SharedTexture& tex = images.at(i);
if(tex){
yPos+=tHeight(tex);
}
if(j<yPos){
idx=i;
break;
}
}
//If the entry isn't above the max we have an event.
if(idx>=0&&idx<(int)item.size()&&selectable){
state=idx;
//Check if the left mouse button is pressed.
if(event.type==SDL_MOUSEBUTTONDOWN && event.button.button==SDL_BUTTON_LEFT){
//Check if the slected item changed.
if(value!=idx){
value=idx;
//If event callback is configured then add an event to the queue.
if(eventCallback){
GUIEvent e={eventCallback,name,this,GUIEventChange};
GUIEventQueue.push_back(e);
}
}
//After possibly a change event, there will always be a click event.
if(eventCallback && clickEvents){
GUIEvent e={eventCallback,name,this,GUIEventClick};
GUIEventQueue.push_back(e);
}
}
}
//Check for mouse wheel scrolling.
if(event.type==SDL_MOUSEWHEEL && event.wheel.y < 0 && scrollBar->enabled){
scrollBar->value++;
if(scrollBar->value>scrollBar->maxValue)
scrollBar->value=scrollBar->maxValue;
}else if(event.type==SDL_MOUSEWHEEL && event.wheel.y > 0 && scrollBar->enabled){
scrollBar->value--;
if(scrollBar->value<0)
scrollBar->value=0;
}
}
}
//Process child controls event except for the scrollbar.
//That's why i starts at one.
for(unsigned int i=1;i<childControls.size();i++){
bool b1=childControls[i]->handleEvents(renderer,x,y,enabled,visible,b);
//The event is processed when either our or the childs is true (or both).
b=b||b1;
}
return b;
}
void GUIListBox::render(SDL_Renderer& renderer, int x,int y,bool draw){
if(updateScrollbar){
//Calculate the height of the content.
int maxY=0;
for(const SharedTexture& t: images){
if(t){
maxY+=textureHeight(*t);
} else {
std::cerr << "WARNING: Null texture in GUIListBox!" << std::endl;
}
}
//Check if we need to show the scrollbar for many entries.
if(maxY<height){
scrollBar->maxValue=0;
scrollBar->value=0;
scrollBar->visible=false;
}else{
scrollBar->visible=true;
scrollBar->maxValue=item.size();
int yy=0;
for(int i=images.size()-1;i>0;i--){
yy+=textureHeight(*images.at(i));
if(yy>height)
break;
else
scrollBar->maxValue--;
}
scrollBar->largeChange=item.size()/4;
}
updateScrollbar=false;
}
//Rectangle the size of the GUIObject, used to draw borders.
//SDL_Rect r; //Unused local variable :/
//There's no need drawing the GUIObject when it's invisible.
if(!visible||!draw)
return;
//Get the absolute x and y location.
x+=left;
y+=top;
//Draw the background box.
const SDL_Rect r={x,y,width,height};
SDL_SetRenderDrawColor(&renderer,255,255,255,220);
SDL_RenderFillRect(&renderer, &r);
firstItemY=0;
//Loop through the entries and draw them.
if(scrollBar->value==scrollBar->maxValue&&scrollBar->visible){
int lowNumber=height;
int currentItem=images.size()-1;
while(lowNumber>=0&&currentItem>=0){
const SharedTexture& currentTexture = images.at(currentItem);
lowNumber-=tHeight(currentTexture);//->h;
if(lowNumber>0){
if(selectable){
//Check if the mouse is hovering on current entry. If so draw borders around it.
if(state==currentItem)
drawGUIBox(x,y+lowNumber-1,width,tHeight(currentTexture)+1,renderer,0x00000000);
//Check if the current entry is selected. If so draw a gray background.
if(value==currentItem)
drawGUIBox(x,y+lowNumber-1,width,tHeight(currentTexture)+1,renderer,0xDDDDDDFF);
}
applyTexture(x,y+lowNumber,*currentTexture,renderer);
}else{
// This is the top item that is partially obscured.
if(selectable){
//Check if the mouse is hovering on current entry. If so draw borders around it.
if(state==currentItem)
drawGUIBox(x,y,width,tHeight(currentTexture)+lowNumber+1,renderer,0x00000000);
//Check if the current entry is selected. If so draw a gray background.
if(value==currentItem)
drawGUIBox(x,y,width,tHeight(currentTexture)+lowNumber+1,renderer,0xDDDDDDFF);
}
firstItemY=-lowNumber;
const SDL_Rect clip = rectFromTexture(0, -lowNumber, *currentTexture);
const SDL_Rect dstRect{x, y, clip.w, clip.h};
SDL_RenderCopy(&renderer, currentTexture.get(), &clip, &dstRect);
break;
}
currentItem--;
}
}else{
- for(int i=scrollBar->value,j=y+1;/*j>height,*/i<static_cast<int>(item.size());i++){
+ for(int i=scrollBar->value,j=y+1;j<=height&&i<(int)item.size();i++){
//Check if the current item is out side of the widget.
int yOver=tHeight(images[i]);
if(j+yOver>y+height)
yOver=y+height-j;
if(yOver>0){
if(selectable){
//Check if the mouse is hovering on current entry. If so draw borders around it.
if(state==i)
drawGUIBox(x,j-1,width,yOver+1,renderer,0x00000000);
//Check if the current entry is selected. If so draw a gray background.
if(value==i)
drawGUIBox(x,j-1,width,yOver+1,renderer,0xDDDDDDFF);
}
//Draw the image.
const SDL_Rect clip{0, 0, tWidth(images[i]), yOver};
const SDL_Rect dstRect{x, j, clip.w, clip.h};
SDL_RenderCopy(&renderer, images[i].get(), &clip, &dstRect);
}else{
break;
}
j+=tHeight(images[i]);
}
}
//Draw borders around the whole thing.
drawGUIBox(x,y,width,height,renderer,0x00000000);
//We now need to draw all the children of the GUIObject.
for(unsigned int i=0;i<childControls.size();i++){
childControls[i]->render(renderer,x,y,draw);
}
}
void GUIListBox::clearItems(){
item.clear();
images.clear();
}
void GUIListBox::addItem(SDL_Renderer &renderer, std::string name, SharedTexture texture) {
if(texture){
images.push_back(texture);
}else if(!texture&&!name.empty()){
SDL_Color black={0,0,0,0};
auto tex=SharedTexture(textureFromText(renderer, *fontText, name.c_str(), black));
// Make sure we don't create any empty textures.
if(!tex) {
std::cerr << "WARNING: Failed to create texture from text: \"" << name << "\"" << std::endl;
return;
}
images.push_back(tex);
} else {
// If nothing was added, ignore it.
return;
}
item.push_back(name);
updateScrollbar=true;
}
void GUIListBox::updateItem(SDL_Renderer &renderer, int index, string newText, SharedTexture newTexture) {
if(newTexture) {
images.at(index) = newTexture;
} else if (!newTexture&&!newText.empty()) {
SDL_Color black={0,0,0,0};
auto tex=SharedTexture(textureFromText(renderer, *fontText, newText.c_str(), black));
// Make sure we don't create any empty textures.
if(!tex) {
std::cerr << "WARNING: Failed to update texture at index" << index << " \"" << newText << "\"" << std::endl;
return;
}
images.at(index)=tex;
} else {
return;
}
item.at(index)=newText;
updateScrollbar=true;
}
std::string GUIListBox::getItem(int index){
return item.at(index);
}
GUISingleLineListBox::GUISingleLineListBox(ImageManager& imageManager, SDL_Renderer& renderer, int left, int top, int width, int height, bool enabled, bool visible, int gravity):
GUIObject(imageManager,renderer,left,top,width,height,NULL,-1,enabled,visible,gravity),animation(0){}
void GUISingleLineListBox::addItem(string name,string label){
//Check if the label is set, if not use the name.
if(label.size()==0)
label=name;
item.push_back(pair<string,string>(name,label));
}
void GUISingleLineListBox::addItems(vector<pair<string,string> > items){
vector<pair<string,string> >::iterator it;
for(it=items.begin();it!=items.end();++it){
addItem(it->first,it->second);
}
}
void GUISingleLineListBox::addItems(vector<string> items){
vector<string>::iterator it;
for(it=items.begin();it!=items.end();++it){
addItem(*it);
}
}
string GUISingleLineListBox::getName(unsigned int index){
if(index==-1)
index=value;
if(index<0||index>item.size())
return "";
return item[index].first;
}
bool GUISingleLineListBox::handleEvents(SDL_Renderer&,int x,int y,bool enabled,bool visible,bool processed){
//Boolean if the event is processed.
bool b=processed;
//The GUIObject is only enabled when he and his parent(s) are enabled.
enabled=enabled && this->enabled;
//The GUIObject is only enabled when he and his parent(s) are enabled.
visible=visible && this->visible;
//Get the absolute position.
x+=left-gravityX;
y+=top;
state&=~0xF;
if(enabled&&visible){
//The mouse location (x=i, y=j) and the mouse button (k).
int i,j,k;
k=SDL_GetMouseState(&i,&j);
//Convert the mouse location to a relative location.
i-=x;
j-=y;
//The selected button.
//0=nothing 1=left 2=right.
int idx=0;
//Check which button the mouse is above.
if(i>=0&&i<width&&j>=0&&j<height){
if(i<26 && i<width/2){
//The left arrow.
idx=1;
}else if(i>=width-26){
//The right arrow.
idx=2;
}
}
//If idx is 0 it means the mous doesn't hover any arrow so reset animation.
if(idx==0)
animation=0;
//Check if there's a mouse button press or not.
if(k&SDL_BUTTON(1)){
if(((state>>4)&0xF)==idx)
state|=idx;
}else{
state|=idx;
}
//Check if there's a mouse press.
if(event.type==SDL_MOUSEBUTTONDOWN && event.button.button==SDL_BUTTON_LEFT && idx){
state=idx|(idx<<4);
}else if(event.type==SDL_MOUSEBUTTONUP && event.button.button==SDL_BUTTON_LEFT && idx && ((state>>4)&0xF)==idx){
int m=(int)item.size();
if(m>0){
if(idx==2){
idx=value+1;
if(idx<0||idx>=m) idx=0;
if(idx!=value){
value=idx;
//If there is an event callback then call it.
if(eventCallback){
GUIEvent e={eventCallback,name,this,GUIEventClick};
GUIEventQueue.push_back(e);
}
}
}else if(idx==1){
idx=value-1;
if(idx<0||idx>=m) idx=m-1;
if(idx!=value){
value=idx;
//If there is an event callback then call it.
if(eventCallback){
GUIEvent e={eventCallback,name,this,GUIEventClick};
GUIEventQueue.push_back(e);
}
}
}
}
}
if(event.type==SDL_MOUSEBUTTONUP) state&=0xF;
}else{
//Set state zero.
state=0;
}
return b;
}
void GUISingleLineListBox::render(SDL_Renderer& renderer, int x,int y,bool draw){
//Rectangle the size of the GUIObject, used to draw borders.
SDL_Rect r;
//There's no need drawing the GUIObject when it's invisible.
if(!visible)
return;
//NOTE: logic in the render method since it's the only part that gets called every frame.
if((state&0xF)==0x1 || (state&0xF)==0x2){
animation++;
if(animation>20)
animation=-20;
}
//Get the absolute x and y location.
x+=left;
y+=top;
if(gravity==GUIGravityCenter)
gravityX=int(width/2);
else if(gravity==GUIGravityRight)
gravityX=width;
x-=gravityX;
//Check if the enabled state changed or the caption, if so we need to clear the (old) cache.
if(enabled!=cachedEnabled || item[value].second.compare(cachedCaption)!=0){
//Free the cache.
cacheTex.reset(nullptr);
//And cache the new values.
cachedEnabled=enabled;
cachedCaption=item[value].second;
}
//Draw the text.
if(value>=0 && value<(int)item.size()){
//Get the text.
const std::string& lp=item[value].second;
//Check if the text is empty or not.
if(!lp.empty()){
if(!cacheTex){
SDL_Color color;
if(inDialog)
color=themeTextColorDialog;
else
color=themeTextColor;
cacheTex=textureFromText(renderer, *fontGUI, lp.c_str(), color);
//If the text is too wide then we change to smaller font (?)
//NOTE: The width is 32px smaller (2x16px for the arrows).
if(rectFromTexture(*cacheTex).w>width-32){
cacheTex=textureFromText(renderer, *fontGUISmall,lp.c_str(),color);
}
}
if(draw){
//Center the text both vertically as horizontally.
const SDL_Rect textureSize = rectFromTexture(*cacheTex);
r.x=x+(width-textureSize.w)/2;
r.y=y+(height-textureSize.h)/2-GUI_FONT_RAISE;
//Draw the text.
applyTexture(r.x, r.y, cacheTex, renderer);
}
}
}
if(draw){
//Draw the arrows.
r.x=x;
if((state&0xF)==0x1)
r.x+=abs(animation/2);
r.y=y+4;
if(inDialog)
applyTexture(r.x,r.y,*arrowLeft2,renderer);
else
applyTexture(r.x,r.y,*arrowLeft1,renderer);
r.x=x+width-16;
if((state&0xF)==0x2)
r.x-=abs(animation/2);
if(inDialog)
applyTexture(r.x,r.y,*arrowRight2,renderer);
else
applyTexture(r.x,r.y,*arrowRight1,renderer);
}
}
diff --git a/src/GameObjects.h b/src/GameObjects.h
index f21865a..92da49d 100644
--- a/src/GameObjects.h
+++ b/src/GameObjects.h
@@ -1,173 +1,174 @@
/*
* Copyright (C) 2011-2013 Me and My Shadow
*
* This file is part of Me and My Shadow.
*
* Me and My Shadow is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Me and My Shadow is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Me and My Shadow. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef GAME_OBJECTS_H
#define GAME_OBJECTS_H
+#include "Globals.h"
#include "TreeStorageNode.h"
#include "Player.h"
#include <SDL.h>
#include <string>
#include <vector>
#include <utility>
#include <map>
class Game;
class Player;
//The different gameObject events.
enum GameObjectEventType{
//Event called when the player walks on the gameObject.
GameObjectEvent_PlayerWalkOn=1,
//Event called when the player is on the gameObject.
GameObjectEvent_PlayerIsOn,
//Event called when the player leaves the gameObject.
GameObjectEvent_PlayerLeave,
//Event called when the gameObject is created.
//Only used for scripting purpose.
GameObjectEvent_OnCreate,
//Event called every frame.
//Only used for scripting purpose.
GameObjectEvent_OnEnterFrame,
//Event called when the player toggles it. (DOWN key)
GameObjectEvent_OnToggle=0x10000,
//Event called when the player switches it on. (DOWN key)
GameObjectEvent_OnSwitchOn=0x10001,
//Event called when the player switches it off. (DOWN key)
GameObjectEvent_OnSwitchOff=0x10002,
};
//The different gameObject properties.
enum GameObjectPropertyType{
//If the player can walk on the gameObject.
GameObjectProperty_PlayerCanWalkOn=1,
//If the object is spiked.
GameObjectProperty_IsSpikes,
//If the gameObject has some flags.
GameObjectProperty_Flags,
};
//The different box types that can be requested using the getBox(int boxType) method.
enum GameObjectBoxType{
//Box of the current position.
BoxType_Current=0,
//Box of the base/start position.
BoxType_Base,
//Box of the previous position.
BoxType_Previous,
//The movement of the block since last position.
BoxType_Delta,
//The velocity for when the player is standing on it.
BoxType_Velocity,
};
//The GameObject class.
class GameObject{
protected:
//The box of the gameObject.
//It's used for the current location of the gameObject and its size.
SDL_Rect box;
//The base location of the game object.
SDL_Rect boxBase;
public:
//The type of the GameObject.
int type;
//Pointer to the Game state.
Game* parent;
//Constructor.
//parent: Pointer to the Game state.
GameObject(Game* parent);
//Destructor.
- virtual ~GameObject();
+ virtual ~GameObject();
//Method used to retrieve a certain box from the GameObject.
//boxType: The type of box that is requested. (default=0)
//Returns: An SDL_Rect.
virtual SDL_Rect getBox(int boxType=0);
//This method is used to place the location on a given location.
//x: The x location to place the gameObject.
//y: The y location to place the gameObject.
virtual void setLocation(int x,int y);
//This method is used to set the base of an object to a given location.
//x: The x location to place the gameObject.
//y: The y location to place the gameObject.
virtual void setBaseLocation(int x,int y);
//This method sets the size of the object to a given size.
//w: The new width of the gameObject.
//h: The new height the gameObject.
virtual void setSize(int w,int h);
//This method sets the size of the base of the object to a given size.
//w: The new width of the gameObject.
//h: The new height of the gameObject.
virtual void setBaseSize(int w,int h);
//Method used to draw the GameObject.
virtual void show(SDL_Renderer& renderer)=0;
//Save the state of the GameObject, used for moving blocks, etc.
virtual void saveState();
//Load the state of the GameObject, used for moving blocks, etc.
virtual void loadState();
//Reset the state of the GameObject, used for moving blocks, etc.
//save: Boolean if the saved state should also be reset.
virtual void reset(bool save);
//Play an animation.
virtual void playAnimation();
//Invoke an event of the GameObject.
//eventType: The event type.
virtual void onEvent(int eventType);
//Method used to request certain properties of the GameObject.
//propertyType: The property that is requested.
//obj: Pointer to the player.
virtual int queryProperties(int propertyType,Player* obj);
//Method used to retrieve the additional editor data for the GameObject.
//Used for messages, moving positions, etc...
//obj: Vector containing the editorData pairs. (key, value)
virtual void getEditorData(std::vector<std::pair<std::string,std::string> >& obj);
//Set the editorData.
//obj: Map containing the key/value for the editor data.
virtual void setEditorData(std::map<std::string,std::string>& obj);
//Get a single property of the block.
//property: The property to return.
//Returns: The value for the requested property.
virtual std::string getEditorProperty(std::string property);
//Set a single property of the block.
//property: The property to set.
//value: The new value for the property.
virtual void setEditorProperty(std::string property,std::string value);
//Method for loading the GameObject from a node.
//objNode: Pointer to the storage node to load from.
//Returns: True if it succeeds without errors.
virtual bool loadFromNode(ImageManager&, SDL_Renderer&, TreeStorageNode*);
//Method that is called before the move method.
//It can be used to reset variables like delta movement and velocity.
virtual void prepareFrame();
//Update method for GameObjects, used for moving blocks.
virtual void move();
};
#endif
diff --git a/src/LevelSelect.h b/src/LevelSelect.h
index dd230b2..36b5444 100644
--- a/src/LevelSelect.h
+++ b/src/LevelSelect.h
@@ -1,172 +1,171 @@
/*
* Copyright (C) 2011-2012 Me and My Shadow
*
* This file is part of Me and My Shadow.
*
* Me and My Shadow is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Me and My Shadow is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Me and My Shadow. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef LEVELSELECT_H
#define LEVELSELECT_H
#include "GameState.h"
#include "GameObjects.h"
#include "GUIObject.h"
-#include "LevelPackManager.h"
#include <vector>
#include <string>
class GUIScrollBar;
class GUISingleLineListBox;
//Class that represents a level in the levelselect menu.
class Number{
private:
//The background image of the number.
ThemeBlockInstance block;
//The background image of the number when it's locked.
ThemeBlockInstance blockLocked;
//The (text) image of the number.
SharedTexture image;
//Image containing the three stars a player can earn.
SharedTexture medals;
//The number (or text).
int number;
//Integer containing the medal the player got.
//0 = none, 1 = bronze, 2 = silver, 3 = gold
int medal;
//Boolean if the number is locked or not.
bool locked;
public:
//The location and size of the number.
SDL_Rect box;
//If the Number is selected then we draw something indicates it.
bool selected;
//Constructor.
Number(ImageManager &imageManager, SDL_Renderer &renderer);
//Method used for initialising the number.
//number: The number.
//box: The location and size of the number.
void init(SDL_Renderer &renderer, int number, SDL_Rect box);
//Method used for initialising the number.
//text: The caption of the number.
//box: The location and size of the number.
void init(SDL_Renderer& renderer,std::string text,SDL_Rect box);
//get current number.
inline int getNumber(){return number;}
//Method used to set the locked status of the number.
//locked: Boolean if it should be locked or not.
void setLocked(bool locked=true);
//Method used to retrieve the locked status of the number.
//Returns: True if the number is locked.
inline bool getLocked(){return locked;}
//Method used to set the medal for this number.
//medal: The new medal for this number.
void setMedal(int medal);
//Method that is used to draw the number.
//dy: The y offset.
void show(SDL_Renderer &renderer, int dy);
};
struct ToolTip {
TexturePtr name;
TexturePtr time;
TexturePtr recordings;
size_t number;
};
//This is the LevelSelect state, here you can select levelpacks and levels.
class LevelSelect : public GameState,public GUIEventCallback{
protected:
//Surface containing the title.
TexturePtr title;
ToolTip toolTip;
//Vector containing the numbers.
std::vector<Number> numbers;
//Contains selected level number (displayed at bottom left corner).
//If it's NULL then nothing selected.
Number* selectedNumber;
//Pointer to the scrollbar.
GUIScrollBar* levelScrollBar;
//Pointer to the description.
GUIObject* levelpackDescription;
//Pointer to the levelpack list.
GUISingleLineListBox* levelpacks;
//Check where and if the mouse clicked on a number.
//If so select that number.
virtual void checkMouse(ImageManager& imageManager, SDL_Renderer& renderer);
//Selected section for keyboard/gamepad control
int section;
//The number of blocks in a row.
int LEVELS_PER_ROW;
//The number of levels displayed on screen at once.
int LEVELS_DISPLAYED_IN_SCREEN;
public:
//Constructor.
//titleText: The title that is shown at the top of the screen.
//packType: The type of levelpacks that should be listed (See LevelPackManager.h).
LevelSelect(ImageManager& imageManager, SDL_Renderer &renderer, const char* titleText, LevelPackManager::LevelPackLists packType=LevelPackManager::ALL_PACKS);
//Destructor.
virtual ~LevelSelect();
//Method that will calculate the number of rows and the number of levels per row.
void calcRows();
//Method used to update the numbers and the scrollbar.
//change: Boolean if the levelpack changed, if not only the numbers need to be replaced.
virtual void refresh(ImageManager& imageManager, SDL_Renderer& renderer, bool change=true)=0;
//Method that is called when a number is selected.
//number: The selected number.
//selected: Boolean if the number was already selected.
virtual void selectNumber(ImageManager &imageManager, SDL_Renderer &renderer, unsigned int number,bool selected)=0;
//Used for keyboard/gamepad navigation
void selectNumberKeyboard(ImageManager &imageManager, SDL_Renderer &renderer, int x, int y);
//Inherited from GameState.
void handleEvents(ImageManager& imageManager, SDL_Renderer& renderer) override;
void logic(ImageManager&, SDL_Renderer&) override;
void render(ImageManager&, SDL_Renderer& renderer) override;
void resize(ImageManager &imageManager, SDL_Renderer& renderer) override;
//Method that is called to render the tooltip.
//number: The number that the tooltip should be drawn for.
//dy: The y offset of the number, used to draw the tooltip in the right place.
virtual void renderTooltip(SDL_Renderer& renderer,unsigned int number,int dy)=0;
//GUI events will be handled here.
virtual void GUIEventCallback_OnEvent(ImageManager& imageManager, SDL_Renderer& renderer, std::string name,GUIObject* obj,int eventType)=0;
};
#endif
diff --git a/src/Main.cpp b/src/Main.cpp
index a20b301..7c43d0a 100644
--- a/src/Main.cpp
+++ b/src/Main.cpp
@@ -1,262 +1,274 @@
/*
* Copyright (C) 2011-2013 Me and My Shadow
*
* This file is part of Me and My Shadow.
*
* Me and My Shadow is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Me and My Shadow is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Me and My Shadow. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Functions.h"
#include "Globals.h"
#include "TitleMenu.h"
#include "GUIObject.h"
#include "InputManager.h"
#include "StatisticsManager.h"
#include "Timer.h"
-
#include <SDL.h>
#include <stdlib.h>
#include <time.h>
#include <stdio.h>
#include <string.h>
-
#ifdef HARDWARE_ACCELERATION
#include <GL/gl.h>
#include <GL/glu.h>
#endif
+// the following piece of code fixes the combination of VC2015 with official SDL1.2 binary
+#if defined(_MSC_VER) && (_MSC_VER >= 1900)
+
+#pragma comment(lib, "legacy_stdio_definitions.lib")
+
+FILE _iob[] = {*stdin, *stdout, *stderr};
+
+extern "C" FILE * __cdecl __iob_func(void)
+{
+ return _iob;
+}
+
+#endif
+
//Variables for recording.
//To enable picture recording uncomment the next line:
//#define RECORD_PICUTRE_SEQUENCE
#ifdef RECORD_PICUTRE_SEQUENCE
bool recordPictureSequence=false;
int recordPictureIndex=0;
#endif
#ifdef __APPLE__
int SDL_main(int argc, char** argv) {
#else
int main(int argc, char** argv) {
#endif
#ifdef _MSC_VER
//Fix the non-latin file name bug under Visual Studio
setlocale(LC_ALL,"");
#endif
//Relocate the standard output for debug purpose (?)
#if defined(ANDROID)
freopen("stdout.txt","w",stdout);
freopen("stderr.txt","w",stderr);
#endif
//First parse the comand line arguments.
int s=parseArguments(argc,argv);
if(s==-1){
printf("Usage: %s [OPTIONS] ...\n",argv[0]);
printf("%s","Available options:\n");
printf(" %-5s%-30s %s\n","","--data-dir <dir>","Specifies the data directory.");
printf(" %-5s%-30s %s\n","","--user-dir <dir>","Specifies the user preferences directory.");
printf(" %-5s%-30s %s\n","-f,","--fullscreen","Run the game fullscreen.");
printf(" %-5s%-30s %s\n","-w,","--windowed","Run the game windowed.");
printf(" %-5s%-30s %s\n","-mv,","--music <volume>","Set the music volume.");
printf(" %-5s%-30s %s\n","-sv,","--sound <volume>","Set the sound volume.");
printf(" %-5s%-30s %s\n","-s,","--set <setting> <value>","Change a setting to a given value.");
printf(" %-5s%-30s %s\n","-v,","--version","Display the version and quit.");
printf(" %-5s%-30s %s\n","-h,","--help","Display this help message.");
return 0;
}else if(s==0){
return 0;
}
//Try to configure the dataPath, userPath, etc...
if(configurePaths()==false){
fprintf(stderr,"FATAL ERROR: Failed to configure paths.\n");
return 1;
}
//Load the settings.
if(loadSettings()==false){
fprintf(stderr,"ERROR: Unable to load config file, default values will be used.\n");
}
ScreenData screenData = init();
//Initialise some stuff like SDL, the window, SDL_Mixer.
if(!screenData) {
fprintf(stderr,"FATAL ERROR: Failed to initialize game.\n");
return 1;
}
SDL_Renderer& renderer = *screenData.renderer;
//Initialise the imagemanager.
//The ImageManager is used to prevent loading images multiple times.
ImageManager imageManager;
//Load some important files like the background music, default theme.
if(loadFiles(imageManager,renderer)==false){
fprintf(stderr,"FATAL ERROR: Failed to load necessary files.\n");
return 1;
}
//Seed random.
srand((unsigned)time(NULL));
//Set the currentState id to the main menu and create it.
stateID=STATE_MENU;
currentState=new Menu(imageManager,renderer);
//Set the fadeIn value to zero.
int fadeIn=0;
//Keep the last resize event, this is to only process one.
SDL_Event lastResize={};
Timer timer;
//Start the game loop.
while(stateID!=STATE_EXIT){
//We start the timer.
timer.start();
//Loop the SDL events.
while(SDL_PollEvent(&event)){
//Check if user resizes the window.
if(event.type==SDL_WINDOWEVENT){
if(event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED)
{
lastResize=event;
//Don't let other objects process this event (?)
continue;
}
}
//Check if the fullscreen toggle shortcut is pressed (Alt+Enter).
if(event.type==SDL_KEYUP && event.key.keysym.sym==SDLK_RETURN && (event.key.keysym.mod & KMOD_ALT)){
getSettings()->setValue("fullscreen",getSettings()->getBoolValue("fullscreen")?"0":"1");
//We need to create a new screen.
if(!createScreen()){
//Screen creation failed so set to safe settings.
getSettings()->setValue("fullscreen","0");
getSettings()->setValue("width","800");
getSettings()->setValue("height","600");
//Try it with the safe settings.
if(!createScreen()){
//Everything fails so quit.
setNextState(STATE_EXIT);
std::cerr<<"ERROR: Unable to create screen."<<std::endl;
}
}
//The screen is created, now load the (menu) theme.
if(!loadTheme(imageManager,renderer,"")){
//Loading the theme failed so quit.
setNextState(STATE_EXIT);
std::cerr<<"ERROR: Unable to load theme after toggling fullscreen."<<std::endl;
}
//Don't let other objects process this event.
continue;
}
#ifdef RECORD_PICUTRE_SEQUENCE
if(event.type==SDL_KEYDOWN && event.key.keysym.sym==SDLK_F10){
recordPictureSequence=!recordPictureSequence;
printf("Record Picture Sequence %s\n",recordPictureSequence?"ON":"OFF");
}
#endif
//Set the cursor type to the default one, the GUI can change that if needed.
currentCursor=CURSOR_POINTER;
//Let the input manager handle the events.
inputMgr.updateState(true);
//Let the currentState handle the events.
currentState->handleEvents(imageManager,renderer);
//Also pass the events to the GUI.
GUIObjectHandleEvents(imageManager,renderer);
}
//Process the resize event.
if(lastResize.type==SDL_WINDOWEVENT){
//TODO - used to be SDL_VIDEORESIZE
// so this may trigger on more events than intended
event=lastResize;
onVideoResize(imageManager,renderer);
//After resize we erase the event type
//TODO - used to be SDL_NOEVENT
lastResize.type=SDL_FIRSTEVENT;
}
//maybe we should add a check here (??) to fix some bugs (ticket #47)
if(nextState!=STATE_NULL){
//Check if fading is enabled.
if(getSettings()->getBoolValue("fading"))
fadeIn=17;
else
fadeIn=255;
changeState(imageManager,renderer);
}
if(stateID==STATE_EXIT) break;
//update input state (??)
inputMgr.updateState(false);
//Now it's time for the state to do his logic.
currentState->logic(imageManager,renderer);
currentState->render(imageManager,renderer);
//TODO: Shouldn't the gamestate take care of rendering the GUI?
if(GUIObjectRoot) GUIObjectRoot->render(renderer);
//draw new achievements (if any)
statsMgr.render(imageManager,renderer);
//draw fading effect
if(fadeIn>0&&fadeIn<255){
dimScreen(renderer, static_cast<Uint8>(255-fadeIn));
fadeIn+=17;
}
#ifdef RECORD_PICUTRE_SEQUENCE
//TODO: This needs fixing for SDL2 port
if(recordPictureSequence){
char s[64];
recordPictureIndex++;
sprintf(s,"pic%08d.bmp",recordPictureIndex);
printf("Save screen to %s\n",s);
SDL_SaveBMP(screen,(getUserPath(USER_CACHE)+s).c_str());
}
#endif
//Before flipping the screen set the cursor.
SDL_SetCursor(cursors[currentCursor]);
//And draw the screen surface to the actual screen.
flipScreen(renderer);
//Now calcualte how long we need to wait to keep a constant framerate.
int t=timer.getTicks();
t=(1000/FPS)-t;
if(t>0){
SDL_Delay(t);
}
}
//The game has ended, save the settings just to be sure.
if(!saveSettings()){
fprintf(stderr,"ERROR: Unable to save settings in config file.\n");
}
//Clean everything up.
clean();
//End of program.
return 0;
}
diff --git a/src/ScriptUserData.cpp b/src/ScriptUserData.cpp
new file mode 100644
index 0000000..bb85865
--- /dev/null
+++ b/src/ScriptUserData.cpp
@@ -0,0 +1,25 @@
+#include "ScriptUserData.h"
+#include <stdio.h>
+
+//Some debug functions
+void scriptUserClassDebugCreate(char sig1,char sig2,char sig3,char sig4,const void* p1,const void* p2) {
+#ifdef _DEBUG
+ printf("ScriptUserClass '%c%c%c%c' (%p) created userdata: %p\n",
+ sig1,sig2,sig3,sig4,p1,p2);
+#endif
+}
+
+void scriptUserClassDebugInvalidate(char sig1,char sig2,char sig3,char sig4,const void* p1,const void* p2) {
+#ifdef _DEBUG
+ printf("ScriptUserClass '%c%c%c%c' (%p) invalidated userdata: %p\n",
+ sig1,sig2,sig3,sig4,p1,p2);
+#endif
+}
+
+void scriptUserClassDebugUnlink(char sig1,char sig2,char sig3,char sig4,const void* p1,const void* p2) {
+#ifdef _DEBUG
+ printf("ScriptUserClass '%c%c%c%c' (%p) unlinked userdata: %p\n",
+ sig1,sig2,sig3,sig4,p1,p2);
+#endif
+}
+
diff --git a/src/StatisticsManager.cpp b/src/StatisticsManager.cpp
index a3bafb4..f771a03 100644
--- a/src/StatisticsManager.cpp
+++ b/src/StatisticsManager.cpp
@@ -1,783 +1,782 @@
/*
* Copyright (C) 2012 Me and My Shadow
*
* This file is part of Me and My Shadow.
*
* Me and My Shadow is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Me and My Shadow is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Me and My Shadow. If not, see <http://www.gnu.org/licenses/>.
*/
#include "StatisticsManager.h"
#include "FileManager.h"
#include "TreeStorageNode.h"
#include "POASerializer.h"
#include "Functions.h"
#include "LevelPackManager.h"
#include "MusicManager.h"
#include "SoundManager.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <map>
#include "libs/tinyformat/tinyformat.h"
#include <SDL_ttf.h>
+#if defined(WIN32)
+#define PRINTF_LONGLONG "%I64d"
+#else
+#define PRINTF_LONGLONG "%lld"
+#endif
using namespace std;
StatisticsManager statsMgr;
static const int achievementDisplayTime=(FPS*4500)/1000;
static const int achievementIntervalTime=achievementDisplayTime+(FPS*500)/1000;
#include "AchievementList.h"
static map<string,AchievementInfo*> avaliableAchievements;
//================================================================
StatisticsManager::StatisticsManager(){
bmDropShadow=NULL;
bmQuestionMark=NULL;
bmAchievement=NULL;
startTime=time(NULL);
tutorialLevels=0;
clear();
}
void StatisticsManager::clear(){
playerTravelingDistance=shadowTravelingDistance=0.0f;
playerJumps=shadowJumps
=playerDies=shadowDies
=playerSquashed=shadowSquashed
=completedLevels=silverLevels=goldLevels
=recordTimes=switchTimes=swapTimes=saveTimes=loadTimes
=playTime=levelEditTime
=createdLevels=tutorialCompleted=tutorialGold=0;
achievements.clear();
queuedAchievements.clear();
achievementTime=0;
currentAchievement=0;
if(bmAchievement){
bmAchievement.reset();
}
}
#define LOAD_STATS(var,func) { \
vector<string> &v=node.attributes[ #var ]; \
if(!v.empty() && !v[0].empty()) \
var=func(v[0].c_str()); \
}
void StatisticsManager::loadFile(const std::string& fileName){
clear();
ifstream file(fileName.c_str());
if(!file) return;
TreeStorageNode node;
POASerializer serializer;
if(!serializer.readNode(file,&node,true)) return;
//load statistics
LOAD_STATS(playerTravelingDistance,atof);
LOAD_STATS(shadowTravelingDistance,atof);
LOAD_STATS(playerJumps,atoi);
LOAD_STATS(shadowJumps,atoi);
LOAD_STATS(playerDies,atoi);
LOAD_STATS(shadowDies,atoi);
LOAD_STATS(playerSquashed,atoi);
LOAD_STATS(shadowSquashed,atoi);
LOAD_STATS(recordTimes,atoi);
LOAD_STATS(switchTimes,atoi);
LOAD_STATS(swapTimes,atoi);
LOAD_STATS(saveTimes,atoi);
LOAD_STATS(loadTimes,atoi);
LOAD_STATS(playTime,atoi);
LOAD_STATS(levelEditTime,atoi);
LOAD_STATS(createdLevels,atoi);
//load achievements.
//format is: name;time,name;time,...
{
vector<string> &v=node.attributes["achievements"];
for(unsigned int i=0;i<v.size();i++){
string s=v[i];
time_t t=0;
string::size_type lps=s.find(';');
if(lps!=string::npos){
string s1=s.substr(lps+1);
s=s.substr(0,lps);
long long n;
- sscanf(s1.c_str(),
-#ifdef WIN32
- "%I64d",
-#else
- "%Ld",
-#endif
- &n);
+ sscanf(s1.c_str(),PRINTF_LONGLONG,&n);
t=(time_t)n;
}
map<string,AchievementInfo*>::iterator it=avaliableAchievements.find(s);
if(it!=avaliableAchievements.end()){
OwnedAchievement ach={t,it->second};
achievements[it->first]=ach;
}
}
}
}
//Call when level edit is start
void StatisticsManager::startLevelEdit(){
levelEditStartTime=time(NULL);
}
//Call when level edit is end
void StatisticsManager::endLevelEdit(){
levelEditTime+=time(NULL)-levelEditStartTime;
}
//update in-game time
void StatisticsManager::updatePlayTime(){
time_t endTime=time(NULL);
playTime+=endTime-startTime;
startTime=endTime;
}
#define SAVE_STATS(var,pattern) { \
sprintf(s,pattern,var); \
node.attributes[ #var ].push_back(s); \
}
void StatisticsManager::saveFile(const std::string& fileName){
char s[64];
//update in-game time
updatePlayTime();
ofstream file(fileName.c_str());
if(!file) return;
TreeStorageNode node;
//save statistics
SAVE_STATS(playerTravelingDistance,"%.2f");
SAVE_STATS(shadowTravelingDistance,"%.2f");
SAVE_STATS(playerJumps,"%d");
SAVE_STATS(shadowJumps,"%d");
SAVE_STATS(playerDies,"%d");
SAVE_STATS(shadowDies,"%d");
SAVE_STATS(playerSquashed,"%d");
SAVE_STATS(shadowSquashed,"%d");
SAVE_STATS(recordTimes,"%d");
SAVE_STATS(switchTimes,"%d");
SAVE_STATS(swapTimes,"%d");
SAVE_STATS(saveTimes,"%d");
SAVE_STATS(loadTimes,"%d");
SAVE_STATS(playTime,"%d");
SAVE_STATS(levelEditTime,"%d");
SAVE_STATS(createdLevels,"%d");
//save achievements.
//format is: name;time,name;time,...
{
vector<string>& v=node.attributes["achievements"];
for(map<string,OwnedAchievement>::iterator it=achievements.begin();it!=achievements.end();++it){
stringstream strm;
char s[32];
long long n=it->second.achievedTime;
sprintf(s,
#ifdef WIN32
"%I64d",
#else
"%Ld",
#endif
n);
strm<<it->first<<";"<<s;
v.push_back(strm.str());
}
}
POASerializer serializer;
serializer.writeNode(&node,file,true,true);
}
void StatisticsManager::loadPicture(SDL_Renderer& renderer, ImageManager& imageManager){
//Load drop shadow picture
bmDropShadow=imageManager.loadTexture(getDataPath()+"gfx/dropshadow.png", renderer);
bmQuestionMark=imageManager.loadImage(getDataPath()+"gfx/menu/questionmark.png");
}
void StatisticsManager::registerAchievements(ImageManager& imageManager){
if(!avaliableAchievements.empty()) return;
for(int i=0;achievementList[i].id!=NULL;i++){
avaliableAchievements[achievementList[i].id]=&achievementList[i];
if(achievementList[i].imageFile!=NULL){
achievementList[i].imageSurface = imageManager.loadImage(getDataPath()+achievementList[i].imageFile);
}
}
}
void StatisticsManager::render(ImageManager&,SDL_Renderer &renderer){
if(achievementTime==0 && !bmAchievement && currentAchievement<(int)queuedAchievements.size()){
//create surface
bmAchievement=createAchievementSurface(renderer, queuedAchievements[currentAchievement++]);
//FIXME: Draw the box.
//drawGUIBox(0,0,bmAchievement->w,bmAchievement->h,bmAchievement,0xFFFFFF00);
//check if queue is empty
if(currentAchievement>=(int)queuedAchievements.size()){
queuedAchievements.clear();
currentAchievement=0;
}
//play a sound
getSoundManager()->playSound("achievement");
}
//check if we need to display achievements
if(bmAchievement){
achievementTime++;
if(achievementTime<=0){
return;
}else if(achievementTime<=5){
drawAchievement(renderer,achievementTime);
}else if(achievementTime<=achievementDisplayTime-5){
drawAchievement(renderer,5);
}else if(achievementTime<achievementDisplayTime){
drawAchievement(renderer,achievementDisplayTime-achievementTime);
}else if(achievementTime>=achievementIntervalTime){
if(bmAchievement){
bmAchievement.reset();
}
achievementTime=0;
}
}
}
void StatisticsManager::newAchievement(const std::string& id,bool save){
//check avaliable achievements
map<string,AchievementInfo*>::iterator it=avaliableAchievements.find(id);
if(it==avaliableAchievements.end()) return;
//check if already have this achievement
if(save){
map<string,OwnedAchievement>::iterator it2=achievements.find(id);
if(it2!=achievements.end()) return;
OwnedAchievement ach={time(NULL),it->second};
achievements[id]=ach;
}
//add it to queue
queuedAchievements.push_back(it->second);
}
float StatisticsManager::getAchievementProgress(AchievementInfo* info){
if(!strcmp(info->id,"experienced")){
return float(completedLevels)/50.0f*100.0f;
}
if(!strcmp(info->id,"expert")){
return float(goldLevels)/50.0f*100.0f;
}
if(!strcmp(info->id,"tutorial")){
if(tutorialLevels>0)
return float(tutorialCompleted)/float(tutorialLevels)*100.0f;
else
return 0.0f;
}
if(!strcmp(info->id,"tutorialGold")){
if(tutorialLevels>0)
return float(tutorialGold)/float(tutorialLevels)*100.0f;
else
return 0.0f;
}
if(!strcmp(info->id,"create50")){
return float(createdLevels)/50.0f*100.0f;
}
if(!strcmp(info->id,"frog")){
return float(playerJumps+shadowJumps)/1000.0f*100.0f;
}
if(!strcmp(info->id,"die50")){
return float(playerDies+shadowDies)/50.0f*100.0f;
}
if(!strcmp(info->id,"die1000")){
return float(playerDies+shadowDies)/1000.0f*100.0f;
}
if(!strcmp(info->id,"suqash50")){
return float(playerSquashed+shadowSquashed)/50.0f*100.0f;
}
if(!strcmp(info->id,"travel100")){
return (playerTravelingDistance+shadowTravelingDistance)/100.0f*100.0f;
}
if(!strcmp(info->id,"travel1k")){
return (playerTravelingDistance+shadowTravelingDistance)/1000.0f*100.0f;
}
if(!strcmp(info->id,"travel10k")){
return (playerTravelingDistance+shadowTravelingDistance)/10000.0f*100.0f;
}
if(!strcmp(info->id,"travel42k")){
return (playerTravelingDistance+shadowTravelingDistance)/42195.0f*100.0f;
}
if(!strcmp(info->id,"record100")){
return float(recordTimes)/100.0f*100.0f;
}
if(!strcmp(info->id,"record1k")){
return float(recordTimes)/1000.0f*100.0f;
}
if(!strcmp(info->id,"switch100")){
return float(switchTimes)/100.0f*100.0f;
}
if(!strcmp(info->id,"switch1k")){
return float(switchTimes)/1000.0f*100.0f;
}
if(!strcmp(info->id,"swap100")){
return float(swapTimes)/100.0f*100.0f;
}
if(!strcmp(info->id,"swap1k")){
return float(swapTimes)/1000.0f*100.0f;
}
//not found
return 0.0f;
}
SharedTexture StatisticsManager::createAchievementSurface(SDL_Renderer& renderer, AchievementInfo* info,SDL_Rect* rect,bool showTip,const time_t *achievedTime){
if(info==NULL || info->id==NULL) return NULL;
//prepare text
SurfacePtr title0(nullptr);
SurfacePtr title1(nullptr);
vector<SDL_Surface*> descSurfaces;
SDL_Color fg={0,0,0};
int fontHeight=TTF_FontLineSkip(fontText);
bool showDescription=false;
bool showImage=false;
float achievementProgress=0.0f;
if(showTip){
title0.reset(TTF_RenderUTF8_Blended(fontText,_("New achievement:"),fg));
title1.reset(TTF_RenderUTF8_Blended(fontGUISmall,_(info->name),fg));
showDescription=showImage=true;
}else if(achievedTime){
char s[128];
strftime(s,sizeof(s),"%c",localtime(achievedTime));
stringstream strm;
tinyformat::format(strm,_("Achieved on %s"),s);
title0.reset(TTF_RenderUTF8_Blended(fontGUISmall,_(info->name),fg));
title1.reset(TTF_RenderUTF8_Blended(fontText,strm.str().c_str(),fg));
showDescription=showImage=true;
}else if(info->displayStyle==ACHIEVEMENT_HIDDEN){
title0.reset(TTF_RenderUTF8_Blended(fontGUISmall,_("Unknown achievement"),fg));
}else{
if(info->displayStyle==ACHIEVEMENT_PROGRESS){
achievementProgress=getAchievementProgress(info);
stringstream strm;
tinyformat::format(strm,_("Achieved %1.0f%%"),achievementProgress);
title1.reset(TTF_RenderUTF8_Blended(fontText,strm.str().c_str(),fg));
}else{
title1.reset(TTF_RenderUTF8_Blended(fontText,_("Not achieved"),fg));
}
title0.reset(TTF_RenderUTF8_Blended(fontGUISmall,_(info->name),fg));
showDescription= info->displayStyle==ACHIEVEMENT_ALL || info->displayStyle==ACHIEVEMENT_PROGRESS;
showImage=true;
}
if(info->description!=NULL && showDescription){
string description=_(info->description);
string::size_type lps=0,lpe;
for(;;){
lpe=description.find('\n',lps);
if(lpe==string::npos){
descSurfaces.push_back(TTF_RenderUTF8_Blended(fontText,(description.substr(lps)+' ').c_str(),fg));
break;
}else{
descSurfaces.push_back(TTF_RenderUTF8_Blended(fontText,(description.substr(lps,lpe-lps)+' ').c_str(),fg));
lps=lpe+1;
}
}
}
//calculate the size
int w=0,h=0,w1=8,h1=0;
if(title0!=NULL){
if(title0->w>w) w=title0->w;
h1+=title0->h;
}
if(title1!=NULL){
if(title1->w>w) w=title1->w;
h1+=title1->h;
/*//calc progress bar size
if(!showTip && !achievedTime && info->displayStyle==ACHIEVEMENT_PROGRESS){
h1+=4;
}*/
}
if(showImage){
if(info->imageSurface!=NULL){
w1+=info->r.w+8;
w+=info->r.w+8;
if(info->r.h>h1) h1=info->r.h;
}
}else{
w1+=bmQuestionMark->w+8;
w+=bmQuestionMark->w+8;
if(bmQuestionMark->h>h1) h1=bmQuestionMark->h;
}
h=h1+8;
for(unsigned int i=0;i<descSurfaces.size();i++){
if(descSurfaces[i]!=NULL){
if(descSurfaces[i]->w>w) w=descSurfaces[i]->w;
}
}
h+=descSurfaces.size()*fontHeight;
w+=16;
h+=16;
//check if size is specified
int left=0,top=0;
if(rect!=NULL){
//NOTE: SDL2 port. This was never used.
/* if(surface!=NULL){
left=rect->x;
top=rect->y;
}*/
if(rect->w>0) w=rect->w;
else rect->w=w;
rect->h=h;
}
//create surface if necessary
SurfacePtr surface = createSurface(w, h);
std::unique_ptr<SDL_Renderer,decltype(&SDL_DestroyRenderer)> surfaceRenderer(
SDL_CreateSoftwareRenderer(surface.get()), &SDL_DestroyRenderer);
//draw background
const SDL_Rect r={left,top,w,h};
if(showTip || achievedTime){
SDL_FillRect(surface.get(),&r,SDL_MapRGB(surface->format,255,255,255));
}else{
SDL_FillRect(surface.get(),&r,SDL_MapRGB(surface->format,192,192,192));
}
//draw picture
if(showImage){
if(info->imageSurface){
SDL_Rect r={left+8,top+8+(h1-info->r.h)/2,0,0};
SDL_BlitSurface(info->imageSurface,&info->r,surface.get(),&r);
}
}else{
SDL_Rect r={left+8,top+8+(h1-bmQuestionMark->h)/2,0,0};
SDL_BlitSurface(bmQuestionMark,NULL,surface.get(),&r);
}
//draw text
h=8;
if(title0){
SDL_Rect r={left+w1,top+h,0,0};
SDL_BlitSurface(title0.get(),NULL,surface.get(),&r);
h+=title0->h;
}
if(title1){
SDL_Rect r={left+w1,top+h,0,0};
//Draw progress bar.
if(!showTip && !achievedTime && info->displayStyle==ACHIEVEMENT_PROGRESS){
//Draw borders.
SDL_Rect r1={r.x,r.y,w-8-r.x,title1->h};
drawGUIBox(r1.x,r1.y,r1.w,r1.h,*surfaceRenderer,0x1D);
//Draw progress.
r1.x++;
r1.y++;
r1.w=int(achievementProgress/100.0f*float(r1.w));
r1.h-=3;
SDL_SetRenderDrawColor(surfaceRenderer.get(),0,0,0,100);
SDL_RenderFillRect(surfaceRenderer.get(),&r);
//???
r.x+=2;
r.y+=2;
}
//Draw text.
SDL_BlitSurface(title1.get(),NULL,surface.get(),&r);
}
h=h1+16;
for(unsigned int i=0;i<descSurfaces.size();i++){
if(descSurfaces[i]!=NULL){
SDL_Rect r={left+8,top+h+static_cast<int>(i)*fontHeight,0,0};
SDL_BlitSurface(descSurfaces[i],NULL,surface.get(),&r);
}
}
//clean up
for(unsigned int i=0;i<descSurfaces.size();i++){
if(descSurfaces[i]!=NULL){
SDL_FreeSurface(descSurfaces[i]);
}
}
//FIXME: Should we clear the vector here?
//over
return textureFromSurface(renderer, std::move(surface));
}
void StatisticsManager::drawAchievement(SDL_Renderer& renderer,int alpha){
if(!bmAchievement || alpha<=0) {
return;
}
if(alpha>5) alpha=5;
SDL_Rect r = rectFromTexture(*bmAchievement);
int w=0,h=0;
SDL_GetRendererOutputSize(&renderer, &w, &h);
r.x = w-32-r.w;
r.y = 32;
SDL_SetTextureAlphaMod(bmAchievement.get(), alpha*40);
applyTexture(r.x, r.y,bmAchievement, renderer);
if(!bmDropShadow) {
return;
}
//draw drop shadow - corner
{
int w1=r.w/2,w2=r.w-w1,h1=r.h/2,h2=r.h-h1;
if(w1>16) w1=16;
if(w2>16) w2=16;
if(h1>16) h1=16;
if(h2>16) h2=16;
const int x=(5-alpha)*64;
//top-left
SDL_Rect r1={x,0,w1+16,h1+16};//),r2={r.x-16,r.y-16,0,0};
SDL_Rect r2 ={r.x-16, r.y-16, r1.w, r1.h};
SDL_RenderCopy(&renderer, bmDropShadow.get(), &r1, &r2);
//top-right
r1.x=x+48-w2;r2.w=r1.w =w2+16;r2.x=r.x+r.w-w2;
SDL_RenderCopy(&renderer, bmDropShadow.get(), &r1, &r2);
//bottom-right
r1.y=48-h2;r2.h=r1.h=h2+16;r2.y=r.y+r.h-h2;
SDL_RenderCopy(&renderer, bmDropShadow.get(), &r1, &r2);
//bottom-left
r1.x=x;r2.w=r1.w=w1+16;r2.x=r.x-16;
SDL_RenderCopy(&renderer, bmDropShadow.get(), &r1, &r2);
}
//draw drop shadow - border
int i=r.w-32;
while(i>0){
const int ii=i>128?128:i;
//top
SDL_Rect r1={0,256-alpha*16,ii,16};
SDL_Rect r2={r.x+r.w-16-i,r.y-16,r1.w,r1.h};
SDL_RenderCopy(&renderer, bmDropShadow.get(), &r1, &r2);
//bottom
r1.x=128;r2.y=r.y+r.h;
SDL_RenderCopy(&renderer, bmDropShadow.get(), &r1, &r2);
i-=ii;
}
i=r.h-32;
while(i>0){
const int ii=i>128?128:i;
//top
SDL_Rect r1={512-alpha*16,0,16,ii};
SDL_Rect r2={r.x-16,r.y+r.h-16-i, r1.w, r1.h};
SDL_RenderCopy(&renderer, bmDropShadow.get(), &r1, &r2);
//bottom
r1.y=128;r2.x=r.x+r.w;
SDL_RenderCopy(&renderer, bmDropShadow.get(), &r1, &r2);
i-=ii;
}
}
void StatisticsManager::reloadCompletedLevelsAndAchievements(){
completedLevels=silverLevels=goldLevels=0;
LevelPackManager *lpm=getLevelPackManager();
vector<pair<string,string> > v=lpm->enumLevelPacks();
bool tutorial=false,tutorialIsGold=false;
for(unsigned int i=0;i<v.size();i++){
string& s=v[i].first;
LevelPack *levels=lpm->getLevelPack(s);
levels->loadProgress();
bool b=false;
if(s==lpm->tutorialLevelPackPath){
tutorialLevels=levels->getLevelCount();
tutorialCompleted=tutorialGold=0;
b=tutorial=tutorialIsGold=true;
}
for(int n=0,m=levels->getLevelCount();n<m;n++){
LevelPack::Level *lv=levels->getLevel(n);
int medal=lv->won;
if(medal){
if(lv->targetTime<0 || lv->time<=lv->targetTime)
medal++;
if(lv->targetRecordings<0 || lv->recordings<=lv->targetRecordings)
medal++;
completedLevels++;
if(b) tutorialCompleted++;
if(medal==2) silverLevels++;
if(medal==3){
goldLevels++;
if(b) tutorialGold++;
}
if(medal!=3 && b) tutorialIsGold=false;
}else if(b){
tutorial=tutorialIsGold=false;
}
}
}
//upadte achievements
updateLevelAchievements();
updateTutorialAchievementsInternal((tutorial?1:0)|(tutorialIsGold?2:0));
}
void StatisticsManager::reloadOtherAchievements(){
int i;
if(playTime>=7200) newAchievement("addicted");
if(playTime>=86400) newAchievement("loyalFan");
if(levelEditTime>=7200) newAchievement("constructor");
if(levelEditTime>=86400) newAchievement("constructor2");
if(createdLevels>=1) newAchievement("create1");
if(createdLevels>=50) newAchievement("create50");
i=playerJumps+shadowJumps;
if(i>=1000) newAchievement("frog");
i=playerDies+shadowDies;
if(i>=1) newAchievement("die1");
if(i>=50) newAchievement("die50");
if(i>=1000) newAchievement("die1000");
i=playerSquashed+shadowSquashed;
if(i>=1) newAchievement("squash1");
if(i>=50) newAchievement("squash50");
float d=playerTravelingDistance+shadowTravelingDistance;
if(d>=100.0f) newAchievement("travel100");
if(d>=1000.0f) newAchievement("travel1k");
if(d>=10000.0f) newAchievement("travel10k");
if(d>=42195.0f) newAchievement("travel42k");
if(recordTimes>=100) newAchievement("record100");
if(recordTimes>=1000) newAchievement("record1k");
if(switchTimes>=100) newAchievement("switch100");
if(switchTimes>=1000) newAchievement("switch1k");
if(swapTimes>=100) newAchievement("swap100");
if(swapTimes>=1000) newAchievement("swap1k");
if(saveTimes>=1000) newAchievement("save1k");
if(loadTimes>=1000) newAchievement("load1k");
if(version.find("Development")!=string::npos) newAchievement("programmer");
}
//Update level specified achievements.
//Make sure the completed level count is correct.
void StatisticsManager::updateLevelAchievements(){
if(completedLevels>=1) newAchievement("newbie");
if(goldLevels>=1) newAchievement("goodjob");
if(completedLevels>=50) newAchievement("experienced");
if(goldLevels>=50) newAchievement("expert");
}
//Update tutorial specified achievements.
//Make sure the level progress of tutorial is correct.
void StatisticsManager::updateTutorialAchievements(){
//find tutorial level pack
LevelPackManager *lpm=getLevelPackManager();
LevelPack *levels=lpm->getTutorialLevelPack();
if(levels==NULL) return;
bool tutorial=true,tutorialIsGold=true;
tutorialLevels=levels->getLevelCount();
tutorialCompleted=tutorialGold=0;
for(int n=0,m=levels->getLevelCount();n<m;n++){
LevelPack::Level *lv=levels->getLevel(n);
int medal=lv->won;
if(medal){
if(lv->targetTime<0 || lv->time<=lv->targetTime)
medal++;
if(lv->targetRecordings<0 || lv->recordings<=lv->targetRecordings)
medal++;
tutorialCompleted++;
if(medal!=3) tutorialIsGold=false;
else tutorialGold++;
}else{
tutorial=tutorialIsGold=false;
break;
}
}
//upadte achievements
updateTutorialAchievementsInternal((tutorial?1:0)|(tutorialIsGold?2:0));
}
//internal function
//flags: a bit-field value indicates which achievements we have.
void StatisticsManager::updateTutorialAchievementsInternal(int flags){
if(flags&1) newAchievement("tutorial");
if(flags&2) newAchievement("tutorialGold");
}

File Metadata

Mime Type
text/x-diff
Expires
Tue, Jun 16, 12:18 AM (2 w, 2 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
71455
Default Alt Text
(224 KB)

Event Timeline