convert \r\n to \n
authorPavol Rusnak <stick@gk2.sk>
Mon, 1 Dec 2008 14:34:36 +0000 (1 15:34 +0100)
committerPavol Rusnak <stick@gk2.sk>
Mon, 1 Dec 2008 14:34:36 +0000 (1 15:34 +0100)
gfx.cpp
gfx_list.h
hex_puzzzle.cpp
level_list.h
levels.dat
menus.h
packfile.h
savestate.h
state.h
tiletypes.h

diff --git a/gfx.cpp b/gfx.cpp
index 0c8e5ae..020caba 100644 (file)
--- a/gfx.cpp
+++ b/gfx.cpp
-/*\r
-    Copyright (C) 2005-2007 Tom Beaumont\r
-\r
-    This program is free software; you can redistribute it and/or modify\r
-    it under the terms of the GNU General Public License as published by\r
-    the Free Software Foundation; either version 2 of the License, or\r
-    (at your option) any later version.\r
-\r
-    This program is distributed in the hope that it will be useful,\r
-    but WITHOUT ANY WARRANTY; without even the implied warranty of\r
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
-    GNU General Public License for more details.\r
-\r
-    You should have received a copy of the GNU General Public License\r
-    along with this program; if not, write to the Free Software\r
-    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
-*/\r
-\r
-#include "i18n.h"\r
-\r
-#include "state.h"\r
-#include <cassert>\r
-\r
-#ifdef WIN32\r
-       #include <SDL_syswm.h>\r
-       #include <shellapi.h> // Windows header for drag & drop\r
-       #ifdef USE_BBTABLET\r
-               #include "bbtablet/bbtablet.h"\r
-       #endif\r
-#else\r
-       #undef USE_BBTABLET\r
-#endif\r
-\r
-// If included multiple times:\r
-// BUG: multiple definition of `MATRIX_WHITE_BACK'\r
-// see http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=437517\r
-#include "SDL_Pango.h"\r
-\r
-#include <algorithm>\r
-#include <string>\r
-\r
-#ifndef DATA_DIR\r
-#define DATA_DIR "."\r
-#endif\r
-\r
-StateMakerBase* StateMakerBase::first = 0;\r
-State* StateMakerBase::current = 0;\r
-\r
-int SDL_focus = SDL_APPACTIVE | SDL_APPINPUTFOCUS;     // Initial focus state\r
-\r
-#ifdef WIN32\r
-       #include <windows.h>\r
-       #include <winuser.h>\r
-       #include <commdlg.h>\r
-       #include <direct.h>\r
-\r
-       bool tablet_system = false;\r
-\r
-       char* LoadSaveDialog(bool save, bool levels, const char * title)\r
-       {\r
-               OPENFILENAME f;\r
-               static char filename[1025] = "";\r
-               static char path[1025] = "C:\\WINDOWS\\Desktop\\New Folder\\Foo\\Levels";\r
-               char backupPath[1025];\r
-               _getcwd(backupPath, sizeof(backupPath)/sizeof(backupPath[0])-1);\r
-               \r
-               memset(&f, 0, sizeof(f));\r
-\r
-               #define FILTER(desc, f) desc " (" f ")\0" f "\0"\r
-               f.lpstrFilter = FILTER("All known files","*.lev;*.sol")\r
-                                               FILTER("Level files","*.lev")\r
-                                               FILTER("Solution files","*.sol")\r
-                                               FILTER("All files","*.*");\r
-               #undef FILTER\r
-\r
-               f.lStructSize = sizeof(f);\r
-               f.lpstrFile = filename;\r
-               f.nMaxFile = sizeof(filename);\r
-               f.lpstrInitialDir = path;\r
-               f.lpstrTitle = title;\r
-\r
-               if (GetSaveFileName(&f)==TRUE)\r
-               {\r
-                       // Remember user's choice of path!\r
-                       _getcwd(path, sizeof(path)/sizeof(path[0])-1);\r
-\r
-                       if (save)\r
-                       {\r
-                               int i = strlen(filename)-1;\r
-                               while (i>0 && filename[i]!='.' && filename[i]!='\\' && filename[i]!='/') i--;\r
-                               if (filename[i]!='.' && levels)\r
-                                       strcat(filename, ".lev");\r
-                               if (filename[i]!='.' && !levels)\r
-                                       strcat(filename, ".sol");\r
-                       }\r
-                       _chdir(backupPath);\r
-                       return filename;\r
-               }\r
-\r
-               _chdir(backupPath);\r
-               return 0;\r
-       }\r
-#else\r
-       char* LoadSaveDialog(bool /*save*/, bool /*levels*/, const char * /*title*/)\r
-       {\r
-               return 0;\r
-       }\r
-#endif\r
-\r
-extern void test();\r
-\r
-int mouse_buttons = 0;\r
-int mousex= 10, mousey = 10;\r
-int noMouse = 0;\r
-int quitting = 0;\r
-\r
-double stylusx= 0, stylusy= 0;\r
-int stylusok= 0;\r
-float styluspressure = 0;\r
-SDL_Surface * screen = 0;\r
-SDL_Surface * realScreen = 0;\r
-SDLPango_Context *context = 0;\r
-\r
-extern State* MakeWorld();\r
-\r
-bool fullscreen = false;\r
-\r
-void InitScreen()\r
-{\r
-#ifdef USE_OPENGL\r
-       SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 5 );\r
-       SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 5 );\r
-       SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 5 );\r
-       SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, 16 );\r
-       SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );\r
-\r
-//     printf("SDL_SetVideoMode (OpenGL)\n");\r
-       realScreen = SDL_SetVideoMode(\r
-               SCREEN_W, SCREEN_H, // Width, Height\r
-               0, // Current BPP\r
-               SDL_OPENGL | (fullscreen ? SDL_FULLSCREEN : 0) );\r
-#else\r
-//     printf("SDL_SetVideoMode (non-OpenGL)\n");\r
-       realScreen = SDL_SetVideoMode(\r
-               SCREEN_W, SCREEN_H, // Width, Height\r
-               0, // Current BPP\r
-               SDL_SWSURFACE | SDL_DOUBLEBUF | (fullscreen ? SDL_FULLSCREEN : 0) );\r
-#endif\r
-\r
-       if (screen)\r
-               SDL_FreeSurface(screen);\r
-\r
-       SDL_Surface* tempscreen = SDL_CreateRGBSurface(\r
-               SDL_SWSURFACE, \r
-               SCREEN_W, SCREEN_H,\r
-               16, 0xf800, 0x07e0, 0x001f, 0);\r
-\r
-       screen = SDL_DisplayFormat(tempscreen);\r
-       SDL_FreeSurface(tempscreen);\r
-}\r
-\r
-void ToggleFullscreen()\r
-{\r
-       fullscreen = !fullscreen;\r
-       InitScreen();\r
-       StateMakerBase::current->ScreenModeChanged();\r
-}\r
-String base_path;\r
-\r
-/// determine length of longest line with current font (wrapping allowed if text_width != -1)\r
-int SDLPangoTextHeight(const std::string &text_utf8, int text_width)\r
-{\r
-       // SDLPango_SetMinimumSize limits indeed the maximal size! See\r
-       // http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=438691\r
-       SDLPango_SetMinimumSize(context, text_width, 0);\r
-       SDLPango_SetText(context, text_utf8.c_str(), -1);\r
-       return SDLPango_GetLayoutHeight(context);\r
-}\r
-\r
-/** \brief Determine length of longest line with current font\r
- *\r
- * Whether line breaks are allowed or not needs to be set before using\r
- * SDLPango_SetMinimumSize!\r
- */\r
-int SDLPangoTextWidth(const std::string &text_utf8)\r
-{\r
-       SDLPango_SetText(context, text_utf8.c_str(), -1);\r
-       return SDLPango_GetLayoutWidth(context);\r
-}\r
-\r
-/// Display the specified UTF-8 text left aligned at (x,y)\r
-void Print_Pango(int x, int y, const std::string &text_utf8)\r
-{\r
-       // Workaround for possible crash, see\r
-       // http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=439071\r
-       if (text_utf8.size() == 0 || text_utf8.size() == 1 && text_utf8[0]==127)\r
-               return;\r
-       assert(text_utf8.find("\n") == std::string::npos);\r
-       SDLPango_SetMinimumSize(context, SCREEN_W, 0);\r
-       SDLPango_SetText(context, text_utf8.c_str(), -1);\r
-       SDL_Surface *surface = SDLPango_CreateSurfaceDraw(context);\r
-       SDL_Rect dst = {x, y, 1, 1};\r
-       SDL_BlitSurface(surface, NULL, screen, &dst);\r
-       SDL_FreeSurface(surface);\r
-}\r
-\r
-/** \brief Display the specified UTF-8 text according to the alignment\r
- *\r
- *  If line breaks are already properly set (manually) the will be respected\r
- *  and no new line breaks will be added. This assumes that th text is not too\r
- *  wide.\r
- *\r
- *  \param x the displayed text is horizontally centered around x\r
- *  \param y the displayed text starts at y\r
- *  \param width background window size into which the text needs to fit\r
- *  \param text_utf8 the text to be displayed, in UTF8 encoding\r
- *  \param align=1: horizontally centered around (x,y)\r
- * */\r
-void Print_Pango_Aligned(int x, int y, int width, const std::string &text_utf8, int align)\r
-{\r
-       // Workaround for possible crash, see\r
-       // http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=439071\r
-       if (text_utf8.size() == 0 || text_utf8.size() == 1 && text_utf8[0]==127)\r
-               return;\r
-       if (width<=0)\r
-               return;\r
-       SDLPango_SetMinimumSize(context, width, 0);\r
-       int real_width = SDLPangoTextWidth(text_utf8);\r
-       // Workaround for a crash in SDL Pango, see\r
-       // http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=439855\r
-       if (real_width>width)\r
-               SDLPango_SetMinimumSize(context, real_width, 0);\r
-\r
-  SDLPango_Alignment alignment;\r
-  if (align==0)\r
-    alignment = SDLPANGO_ALIGN_LEFT;\r
-  else if (align==2) {\r
-    alignment = SDLPANGO_ALIGN_RIGHT;\r
-    x -= width;\r
-  } else {\r
-    alignment = SDLPANGO_ALIGN_CENTER;\r
-    x -= width/2;\r
-  }\r
-       // SDLPango_SetText_GivenAlignment is not (yet?) part of the official Pango\r
-       // distribution, see http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=437865\r
-       SDLPango_SetText_GivenAlignment(context, text_utf8.c_str(), -1, alignment);\r
-       SDL_Surface *surface = SDLPango_CreateSurfaceDraw(context);\r
-       SDL_Rect dst = {x, y, 1, 1};\r
-       SDL_BlitSurface(surface, NULL, screen, &dst);\r
-       SDL_FreeSurface(surface);\r
-}\r
-\r
-int TickTimer()\r
-{\r
-       static int time = SDL_GetTicks();\r
-       int cap=40;\r
-\r
-       int x = SDL_GetTicks() - time;\r
-       time += x;\r
-       if (x<0) x = 0, time = SDL_GetTicks();\r
-       if (x>cap) x = cap;\r
-\r
-       return x;\r
-}\r
-\r
-int main(int /*argc*/, char * /*argv*/[])\r
-{\r
-       base_path = DATA_DIR "/";\r
-       for (int i=strlen(base_path)-1; i>=0; i--)\r
-               if (base_path[i]=='/' || base_path[i]=='\\')\r
-               {\r
-                       base_path.truncate(i+1);\r
-                       break;\r
-               }\r
-       // Check the path ends with a directory seperator\r
-       if (strlen(base_path)>0)\r
-       {\r
-               char last = base_path[strlen(base_path)-1];\r
-               if (last!='/' && last!='\\')\r
-                       base_path = "";\r
-       }\r
-#ifdef WIN32\r
-       if (strstr(base_path, "\\foo2___Win32_Debug\\"))\r
-               strstr(base_path, "\\foo2___Win32_Debug\\")[1] = '\0';\r
-       if (strstr(base_path, "\\Release\\"))\r
-               strstr(base_path, "\\Release\\")[1] = '\0';\r
-#endif\r
-//     printf("SDL_Init\n");\r
-       \r
-/*\r
-       // Experimental - create a splash screen window whilst loading\r
-       SDL_Init(SDL_INIT_VIDEO);\r
-       screen = SDL_SetVideoMode( 200,200,0,SDL_NOFRAME );\r
-       SDL_Rect r = {0,0,200,200};\r
-       SDL_FillRect(screen, &r, SDL_MapRGB(screen->format, 0, 0, 50));\r
-       SDL_Flip(screen);\r
-*/\r
-\r
-       SDL_Init(SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE);\r
-       SDLPango_Init();\r
-       context = SDLPango_CreateContext_GivenFontDesc("sans-serif bold 12");\r
-       SDLPango_SetDefaultColor(context, MATRIX_TRANSPARENT_BACK_WHITE_LETTER);\r
-       SDLPango_SetMinimumSize(context, SCREEN_W, 0);\r
-\r
-       SDL_Surface* icon = SDL_LoadBMP("graphics/icon.bmp");\r
-       if (icon)\r
-       {\r
-               static unsigned int mask[32] = {\r
-                       0x00001fc0,\r
-                       0x00003fe0,\r
-                       0x00007ff0,\r
-                       0x00007df8,\r
-                       0x0000f0f8,\r
-                       0x0000f07c,\r
-                       0x0005f87c,\r
-                       0x0fbfff3c,\r
-\r
-                       0x1ffffffe,\r
-                       0x3ffffffe,\r
-                       0x3ffffffe,\r
-                       0x7ffffffe,\r
-                       0x7ffffffe,\r
-                       0x7ffffffe,\r
-                       0x7ffffffe,\r
-                       0xefffffff,\r
-\r
-                       0x1fffffff,\r
-                       0x3fffffff,\r
-                       0x3fffffff,\r
-                       0x3fffffff,\r
-                       0x3fffffff,\r
-                       0x3fffffff,\r
-                       0x3fffffff,\r
-                       0x3ffffffe,\r
-\r
-                       0x3ffffff8,\r
-                       0x3ffffff0,\r
-                       0x3ffffff0,\r
-                       0x3ffffff0,\r
-                       0x3fffffe0,\r
-                       0x3fffffe0,\r
-                       0x1ffffff0,\r
-                       0x1ffffff1,\r
-               };\r
-               for (int i=0; i<32; i++)\r
-                       mask[i] = mask[i]>>24 | (mask[i]>>8)&0xff00 | (mask[i]<<8)&0xff0000 | (mask[i]<<24)&0xff000000;\r
-               SDL_WM_SetIcon(icon, (unsigned char*) mask);\r
-               SDL_FreeSurface(icon);\r
-       }\r
-\r
-       InitScreen();\r
-\r
-       SDL_WarpMouse(SCREEN_W/2, SCREEN_H/2);\r
-\r
-       int videoExposed = 1;\r
-\r
-#ifdef WIN32\r
-       HWND hwnd = 0;\r
-#endif\r
-#ifdef USE_BBTABLET\r
-       bbTabletDevice &td = bbTabletDevice::getInstance( );\r
-       SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE);\r
-#endif\r
-\r
-//     printf("Main loop...\n");\r
-       \r
-       StateMakerBase::GetNew();\r
-\r
-       while(!quitting)\r
-       {\r
-               SDL_Event e;\r
-               while(!SDL_PollEvent(&e) && !quitting)\r
-               {\r
-                       int x = 0;\r
-\r
-                       if ((SDL_focus & 6)==6)\r
-                       {\r
-                               videoExposed = 1;\r
-\r
-                               x = TickTimer();\r
-\r
-                               while (x<10)\r
-                               {\r
-                                       SDL_Delay(10-x);\r
-                                       x += TickTimer();\r
-                               }\r
-                       \r
-                               StateMakerBase::current->Update(x / 1000.0);\r
-                       }\r
-                       else\r
-                       {\r
-                               // Not focussed. Try not to eat too much CPU!\r
-                               SDL_Delay(150);\r
-                       }\r
-\r
-                       // experimental...\r
-                       if (!noMouse)\r
-                               StateMakerBase::current->Mouse(mousex, mousey, 0, 0, 0, 0, mouse_buttons);\r
-\r
-                       if (videoExposed)\r
-                       {\r
-                               StateMakerBase::current->Render();\r
-\r
-                               #ifdef USE_OPENGL\r
-                                       SDL_GL_SwapBuffers();\r
-                               #else\r
-                                       if (screen && realScreen!=screen)\r
-                                       {\r
-                                               SDL_Rect r = {0,0,SCREEN_W,SCREEN_H};\r
-                                               SDL_BlitSurface(screen, &r, realScreen, &r);\r
-                                       }\r
-                                       SDL_Flip(realScreen);\r
-                               #endif\r
-                               videoExposed = 0;\r
-                       }\r
-\r
-                       SDL_Delay(10);\r
-\r
-#ifdef USE_BBTABLET\r
-                       // Tablet ////////////////////////\r
-                       bbTabletEvent evt;\r
-                       while(hwnd!=NULL && td.getNextEvent(evt))\r
-                       {\r
-                               stylusok = 1;\r
-                               RECT r;\r
-                               if (tablet_system)\r
-                               {\r
-                                       GetWindowRect(hwnd, &r);\r
-                                       stylusx = evt.x * GetSystemMetrics(SM_CXSCREEN);\r
-                                       stylusy = (1.0 - evt.y) * GetSystemMetrics(SM_CYSCREEN);\r
-                                       stylusx -= (r.left + GetSystemMetrics(SM_CXFIXEDFRAME));\r
-                                       stylusy -= (r.top + GetSystemMetrics(SM_CYFIXEDFRAME) + GetSystemMetrics(SM_CYCAPTION));;\r
-                               }\r
-                               else\r
-                               {\r
-                                       GetClientRect(hwnd, &r);\r
-                                       stylusx = evt.x * r.right;\r
-                                       stylusy = (1.0 - evt.y) * r.bottom;\r
-                               }\r
-                               styluspressure = (evt.buttons & 1) ? evt.pressure : 0;\r
\r
-                               /*\r
-                               printf("id=%d csrtype=%d b=%x (%0.3f, %0.3f, %0.3f) p=%0.3f tp=%0.3f\n", \r
-                                          evt.id,\r
-                                          evt.type,\r
-                                          evt.buttons,\r
-                                          evt.x,\r
-                                          evt.y,\r
-                                          evt.z,\r
-                                          evt.pressure,\r
-                                          evt.tpressure\r
-                                          );\r
-                               */\r
-                       }\r
-\r
-#endif\r
-               }\r
-\r
-               switch (e.type)\r
-               {\r
-                       case SDL_VIDEOEXPOSE:\r
-                               videoExposed = 1;\r
-                               break;\r
-\r
-#ifdef WIN32\r
-                       case SDL_SYSWMEVENT:\r
-                       {\r
-                               SDL_SysWMmsg* m = e.syswm.msg;\r
-                               hwnd = m->hwnd;\r
-                               static bool init=false;\r
-                               if (!init)\r
-                               {\r
-                                       init = true;\r
-                                       DragAcceptFiles(hwnd, TRUE);\r
-                                       #ifdef USE_BBTABLET\r
-                                               td.initTablet(hwnd, tablet_system ? bbTabletDevice::SYSTEM_POINTER : bbTabletDevice::SEPARATE_POINTER );\r
-                                               if (!td.isValid())\r
-                                                        printf("No tablet/driver found\n");\r
-                                       #endif\r
-                               }\r
-                               if (m->msg == WM_DROPFILES)\r
-                               {\r
-                                       HDROP h = (HDROP)m->wParam;\r
-                                       \r
-                                       char name[512];\r
-                                       if (DragQueryFile(h, 0xffffffff, 0, 0) == 1)\r
-                                       {\r
-                                               DragQueryFile(h, 0, name, sizeof(name)/sizeof(name[0]));\r
-\r
-                                               StateMakerBase::current->FileDrop(name);\r
-                                       }\r
-\r
-                                       DragFinish(h);\r
-                               }\r
-\r
-                               break;\r
-                       }\r
-#endif\r
-\r
-                       case SDL_ACTIVEEVENT:\r
-                       {\r
-                               int gain = e.active.gain ? e.active.state : 0;\r
-                               int loss = e.active.gain ? 0 : e.active.state;\r
-                               SDL_focus = (SDL_focus | gain) & ~loss;\r
-                               if (gain & SDL_APPACTIVE)\r
-                                       StateMakerBase::current->ScreenModeChanged();\r
-                               if (loss & SDL_APPMOUSEFOCUS)\r
-                                       noMouse = 1;\r
-                               else if (gain & SDL_APPMOUSEFOCUS)\r
-                                       noMouse = 0;\r
-\r
-                               break;\r
-                       }\r
-\r
-                       case SDL_MOUSEMOTION:\r
-                               noMouse = false;\r
-                               StateMakerBase::current->Mouse(e.motion.x, e.motion.y, e.motion.x-mousex, e.motion.y-mousey, 0, 0, mouse_buttons);\r
-                               mousex = e.motion.x; mousey = e.motion.y;\r
-                               break;\r
-                       case SDL_MOUSEBUTTONUP:\r
-                               noMouse = false;\r
-                               mouse_buttons &= ~(1<<(e.button.button-1));\r
-                               StateMakerBase::current->Mouse(e.button.x, e.button.y, e.button.x-mousex, e.button.y-mousey, \r
-                                                                               0, 1<<(e.button.button-1), mouse_buttons);\r
-                               mousex = e.button.x; mousey = e.button.y ;\r
-                               break;\r
-                       case SDL_MOUSEBUTTONDOWN:\r
-                               noMouse = false;\r
-                               mouse_buttons |= 1<<(e.button.button-1);\r
-                               StateMakerBase::current->Mouse(e.button.x, e.button.y, e.button.x-mousex, e.button.y-mousey, \r
-                                                                               1<<(e.button.button-1), 0, mouse_buttons);\r
-                               mousex = e.button.x; mousey = e.button.y ;\r
-                               break;\r
-\r
-                       case SDL_KEYUP:\r
-                               StateMakerBase::current->KeyReleased(e.key.keysym.sym);\r
-                               break;\r
-\r
-                       case SDL_KEYDOWN:\r
-                       {\r
-                               SDL_KeyboardEvent & k = e.key;\r
-\r
-                               if (k.keysym.sym==SDLK_F4 && (k.keysym.mod & KMOD_ALT))\r
-                               {\r
-                                       quitting = 1;\r
-                               }\r
-                               else if (k.keysym.sym==SDLK_F12)        \r
-                               {\r
-                                       // Toggle system pointer controlled by tablet or not\r
-                                       #ifdef USE_BBTABLET\r
-                                               if (td.isValid())\r
-                                               {\r
-                                                       tablet_system = !tablet_system;\r
-                                                       td.setPointerMode(tablet_system ? bbTabletDevice::SYSTEM_POINTER : bbTabletDevice::SEPARATE_POINTER);\r
-                                               }\r
-                                       #endif\r
-                               }\r
-                               else if (k.keysym.sym==SDLK_RETURN && (k.keysym.mod & KMOD_ALT) && !(k.keysym.mod & KMOD_CTRL))\r
-                               {\r
-                                       ToggleFullscreen();\r
-                               }\r
-                               else if (StateMakerBase::current->KeyPressed(k.keysym.sym, k.keysym.mod))\r
-                               {\r
-                               }\r
-                               else if ((k.keysym.mod & (KMOD_ALT | KMOD_CTRL))==0)\r
-                               {\r
-                                       StateMakerBase::GetNew(k.keysym.sym);\r
-                               }\r
-                       }\r
-                       break;\r
-\r
-                       case SDL_QUIT:\r
-                               quitting = 1;\r
-                               break;\r
-               }\r
-       }\r
-\r
-       SDLPango_FreeContext(context);\r
-       SDL_Quit();\r
-       return 0;\r
-}\r
+/*
+    Copyright (C) 2005-2007 Tom Beaumont
+
+    This program 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 2 of the License, or
+    (at your option) any later version.
+
+    This program 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 this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include "i18n.h"
+
+#include "state.h"
+#include <cassert>
+
+#ifdef WIN32
+       #include <SDL_syswm.h>
+       #include <shellapi.h> // Windows header for drag & drop
+       #ifdef USE_BBTABLET
+               #include "bbtablet/bbtablet.h"
+       #endif
+#else
+       #undef USE_BBTABLET
+#endif
+
+// If included multiple times:
+// BUG: multiple definition of `MATRIX_WHITE_BACK'
+// see http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=437517
+#include "SDL_Pango.h"
+
+#include <algorithm>
+#include <string>
+
+#ifndef DATA_DIR
+#define DATA_DIR "."
+#endif
+
+StateMakerBase* StateMakerBase::first = 0;
+State* StateMakerBase::current = 0;
+
+int SDL_focus = SDL_APPACTIVE | SDL_APPINPUTFOCUS;     // Initial focus state
+
+#ifdef WIN32
+       #include <windows.h>
+       #include <winuser.h>
+       #include <commdlg.h>
+       #include <direct.h>
+
+       bool tablet_system = false;
+
+       char* LoadSaveDialog(bool save, bool levels, const char * title)
+       {
+               OPENFILENAME f;
+               static char filename[1025] = "";
+               static char path[1025] = "C:\\WINDOWS\\Desktop\\New Folder\\Foo\\Levels";
+               char backupPath[1025];
+               _getcwd(backupPath, sizeof(backupPath)/sizeof(backupPath[0])-1);
+               
+               memset(&f, 0, sizeof(f));
+
+               #define FILTER(desc, f) desc " (" f ")\0" f "\0"
+               f.lpstrFilter = FILTER("All known files","*.lev;*.sol")
+                                               FILTER("Level files","*.lev")
+                                               FILTER("Solution files","*.sol")
+                                               FILTER("All files","*.*");
+               #undef FILTER
+
+               f.lStructSize = sizeof(f);
+               f.lpstrFile = filename;
+               f.nMaxFile = sizeof(filename);
+               f.lpstrInitialDir = path;
+               f.lpstrTitle = title;
+
+               if (GetSaveFileName(&f)==TRUE)
+               {
+                       // Remember user's choice of path!
+                       _getcwd(path, sizeof(path)/sizeof(path[0])-1);
+
+                       if (save)
+                       {
+                               int i = strlen(filename)-1;
+                               while (i>0 && filename[i]!='.' && filename[i]!='\\' && filename[i]!='/') i--;
+                               if (filename[i]!='.' && levels)
+                                       strcat(filename, ".lev");
+                               if (filename[i]!='.' && !levels)
+                                       strcat(filename, ".sol");
+                       }
+                       _chdir(backupPath);
+                       return filename;
+               }
+
+               _chdir(backupPath);
+               return 0;
+       }
+#else
+       char* LoadSaveDialog(bool /*save*/, bool /*levels*/, const char * /*title*/)
+       {
+               return 0;
+       }
+#endif
+
+extern void test();
+
+int mouse_buttons = 0;
+int mousex= 10, mousey = 10;
+int noMouse = 0;
+int quitting = 0;
+
+double stylusx= 0, stylusy= 0;
+int stylusok= 0;
+float styluspressure = 0;
+SDL_Surface * screen = 0;
+SDL_Surface * realScreen = 0;
+SDLPango_Context *context = 0;
+
+extern State* MakeWorld();
+
+bool fullscreen = false;
+
+void InitScreen()
+{
+#ifdef USE_OPENGL
+       SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 5 );
+       SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 5 );
+       SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 5 );
+       SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, 16 );
+       SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
+
+//     printf("SDL_SetVideoMode (OpenGL)\n");
+       realScreen = SDL_SetVideoMode(
+               SCREEN_W, SCREEN_H, // Width, Height
+               0, // Current BPP
+               SDL_OPENGL | (fullscreen ? SDL_FULLSCREEN : 0) );
+#else
+//     printf("SDL_SetVideoMode (non-OpenGL)\n");
+       realScreen = SDL_SetVideoMode(
+               SCREEN_W, SCREEN_H, // Width, Height
+               0, // Current BPP
+               SDL_SWSURFACE | SDL_DOUBLEBUF | (fullscreen ? SDL_FULLSCREEN : 0) );
+#endif
+
+       if (screen)
+               SDL_FreeSurface(screen);
+
+       SDL_Surface* tempscreen = SDL_CreateRGBSurface(
+               SDL_SWSURFACE, 
+               SCREEN_W, SCREEN_H,
+               16, 0xf800, 0x07e0, 0x001f, 0);
+
+       screen = SDL_DisplayFormat(tempscreen);
+       SDL_FreeSurface(tempscreen);
+}
+
+void ToggleFullscreen()
+{
+       fullscreen = !fullscreen;
+       InitScreen();
+       StateMakerBase::current->ScreenModeChanged();
+}
+String base_path;
+
+/// determine length of longest line with current font (wrapping allowed if text_width != -1)
+int SDLPangoTextHeight(const std::string &text_utf8, int text_width)
+{
+       // SDLPango_SetMinimumSize limits indeed the maximal size! See
+       // http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=438691
+       SDLPango_SetMinimumSize(context, text_width, 0);
+       SDLPango_SetText(context, text_utf8.c_str(), -1);
+       return SDLPango_GetLayoutHeight(context);
+}
+
+/** \brief Determine length of longest line with current font
+ *
+ * Whether line breaks are allowed or not needs to be set before using
+ * SDLPango_SetMinimumSize!
+ */
+int SDLPangoTextWidth(const std::string &text_utf8)
+{
+       SDLPango_SetText(context, text_utf8.c_str(), -1);
+       return SDLPango_GetLayoutWidth(context);
+}
+
+/// Display the specified UTF-8 text left aligned at (x,y)
+void Print_Pango(int x, int y, const std::string &text_utf8)
+{
+       // Workaround for possible crash, see
+       // http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=439071
+       if (text_utf8.size() == 0 || text_utf8.size() == 1 && text_utf8[0]==127)
+               return;
+       assert(text_utf8.find("\n") == std::string::npos);
+       SDLPango_SetMinimumSize(context, SCREEN_W, 0);
+       SDLPango_SetText(context, text_utf8.c_str(), -1);
+       SDL_Surface *surface = SDLPango_CreateSurfaceDraw(context);
+       SDL_Rect dst = {x, y, 1, 1};
+       SDL_BlitSurface(surface, NULL, screen, &dst);
+       SDL_FreeSurface(surface);
+}
+
+/** \brief Display the specified UTF-8 text according to the alignment
+ *
+ *  If line breaks are already properly set (manually) the will be respected
+ *  and no new line breaks will be added. This assumes that th text is not too
+ *  wide.
+ *
+ *  \param x the displayed text is horizontally centered around x
+ *  \param y the displayed text starts at y
+ *  \param width background window size into which the text needs to fit
+ *  \param text_utf8 the text to be displayed, in UTF8 encoding
+ *  \param align=1: horizontally centered around (x,y)
+ * */
+void Print_Pango_Aligned(int x, int y, int width, const std::string &text_utf8, int align)
+{
+       // Workaround for possible crash, see
+       // http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=439071
+       if (text_utf8.size() == 0 || text_utf8.size() == 1 && text_utf8[0]==127)
+               return;
+       if (width<=0)
+               return;
+       SDLPango_SetMinimumSize(context, width, 0);
+       int real_width = SDLPangoTextWidth(text_utf8);
+       // Workaround for a crash in SDL Pango, see
+       // http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=439855
+       if (real_width>width)
+               SDLPango_SetMinimumSize(context, real_width, 0);
+
+  SDLPango_Alignment alignment;
+  if (align==0)
+    alignment = SDLPANGO_ALIGN_LEFT;
+  else if (align==2) {
+    alignment = SDLPANGO_ALIGN_RIGHT;
+    x -= width;
+  } else {
+    alignment = SDLPANGO_ALIGN_CENTER;
+    x -= width/2;
+  }
+       // SDLPango_SetText_GivenAlignment is not (yet?) part of the official Pango
+       // distribution, see http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=437865
+       SDLPango_SetText_GivenAlignment(context, text_utf8.c_str(), -1, alignment);
+       SDL_Surface *surface = SDLPango_CreateSurfaceDraw(context);
+       SDL_Rect dst = {x, y, 1, 1};
+       SDL_BlitSurface(surface, NULL, screen, &dst);
+       SDL_FreeSurface(surface);
+}
+
+int TickTimer()
+{
+       static int time = SDL_GetTicks();
+       int cap=40;
+
+       int x = SDL_GetTicks() - time;
+       time += x;
+       if (x<0) x = 0, time = SDL_GetTicks();
+       if (x>cap) x = cap;
+
+       return x;
+}
+
+int main(int /*argc*/, char * /*argv*/[])
+{
+       base_path = DATA_DIR "/";
+       for (int i=strlen(base_path)-1; i>=0; i--)
+               if (base_path[i]=='/' || base_path[i]=='\\')
+               {
+                       base_path.truncate(i+1);
+                       break;
+               }
+       // Check the path ends with a directory seperator
+       if (strlen(base_path)>0)
+       {
+               char last = base_path[strlen(base_path)-1];
+               if (last!='/' && last!='\\')
+                       base_path = "";
+       }
+#ifdef WIN32
+       if (strstr(base_path, "\\foo2___Win32_Debug\\"))
+               strstr(base_path, "\\foo2___Win32_Debug\\")[1] = '\0';
+       if (strstr(base_path, "\\Release\\"))
+               strstr(base_path, "\\Release\\")[1] = '\0';
+#endif
+//     printf("SDL_Init\n");
+       
+/*
+       // Experimental - create a splash screen window whilst loading
+       SDL_Init(SDL_INIT_VIDEO);
+       screen = SDL_SetVideoMode( 200,200,0,SDL_NOFRAME );
+       SDL_Rect r = {0,0,200,200};
+       SDL_FillRect(screen, &r, SDL_MapRGB(screen->format, 0, 0, 50));
+       SDL_Flip(screen);
+*/
+
+       SDL_Init(SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE);
+       SDLPango_Init();
+       context = SDLPango_CreateContext_GivenFontDesc("sans-serif bold 12");
+       SDLPango_SetDefaultColor(context, MATRIX_TRANSPARENT_BACK_WHITE_LETTER);
+       SDLPango_SetMinimumSize(context, SCREEN_W, 0);
+
+       SDL_Surface* icon = SDL_LoadBMP("graphics/icon.bmp");
+       if (icon)
+       {
+               static unsigned int mask[32] = {
+                       0x00001fc0,
+                       0x00003fe0,
+                       0x00007ff0,
+                       0x00007df8,
+                       0x0000f0f8,
+                       0x0000f07c,
+                       0x0005f87c,
+                       0x0fbfff3c,
+
+                       0x1ffffffe,
+                       0x3ffffffe,
+                       0x3ffffffe,
+                       0x7ffffffe,
+                       0x7ffffffe,
+                       0x7ffffffe,
+                       0x7ffffffe,
+                       0xefffffff,
+
+                       0x1fffffff,
+                       0x3fffffff,
+                       0x3fffffff,
+                       0x3fffffff,
+                       0x3fffffff,
+                       0x3fffffff,
+                       0x3fffffff,
+                       0x3ffffffe,
+
+                       0x3ffffff8,
+                       0x3ffffff0,
+                       0x3ffffff0,
+                       0x3ffffff0,
+                       0x3fffffe0,
+                       0x3fffffe0,
+                       0x1ffffff0,
+                       0x1ffffff1,
+               };
+               for (int i=0; i<32; i++)
+                       mask[i] = mask[i]>>24 | (mask[i]>>8)&0xff00 | (mask[i]<<8)&0xff0000 | (mask[i]<<24)&0xff000000;
+               SDL_WM_SetIcon(icon, (unsigned char*) mask);
+               SDL_FreeSurface(icon);
+       }
+
+       InitScreen();
+
+       SDL_WarpMouse(SCREEN_W/2, SCREEN_H/2);
+
+       int videoExposed = 1;
+
+#ifdef WIN32
+       HWND hwnd = 0;
+#endif
+#ifdef USE_BBTABLET
+       bbTabletDevice &td = bbTabletDevice::getInstance( );
+       SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE);
+#endif
+
+//     printf("Main loop...\n");
+       
+       StateMakerBase::GetNew();
+
+       while(!quitting)
+       {
+               SDL_Event e;
+               while(!SDL_PollEvent(&e) && !quitting)
+               {
+                       int x = 0;
+
+                       if ((SDL_focus & 6)==6)
+                       {
+                               videoExposed = 1;
+
+                               x = TickTimer();
+
+                               while (x<10)
+                               {
+                                       SDL_Delay(10-x);
+                                       x += TickTimer();
+                               }
+                       
+                               StateMakerBase::current->Update(x / 1000.0);
+                       }
+                       else
+                       {
+                               // Not focussed. Try not to eat too much CPU!
+                               SDL_Delay(150);
+                       }
+
+                       // experimental...
+                       if (!noMouse)
+                               StateMakerBase::current->Mouse(mousex, mousey, 0, 0, 0, 0, mouse_buttons);
+
+                       if (videoExposed)
+                       {
+                               StateMakerBase::current->Render();
+
+                               #ifdef USE_OPENGL
+                                       SDL_GL_SwapBuffers();
+                               #else
+                                       if (screen && realScreen!=screen)
+                                       {
+                                               SDL_Rect r = {0,0,SCREEN_W,SCREEN_H};
+                                               SDL_BlitSurface(screen, &r, realScreen, &r);
+                                       }
+                                       SDL_Flip(realScreen);
+                               #endif
+                               videoExposed = 0;
+                       }
+
+                       SDL_Delay(10);
+
+#ifdef USE_BBTABLET
+                       // Tablet ////////////////////////
+                       bbTabletEvent evt;
+                       while(hwnd!=NULL && td.getNextEvent(evt))
+                       {
+                               stylusok = 1;
+                               RECT r;
+                               if (tablet_system)
+                               {
+                                       GetWindowRect(hwnd, &r);
+                                       stylusx = evt.x * GetSystemMetrics(SM_CXSCREEN);
+                                       stylusy = (1.0 - evt.y) * GetSystemMetrics(SM_CYSCREEN);
+                                       stylusx -= (r.left + GetSystemMetrics(SM_CXFIXEDFRAME));
+                                       stylusy -= (r.top + GetSystemMetrics(SM_CYFIXEDFRAME) + GetSystemMetrics(SM_CYCAPTION));;
+                               }
+                               else
+                               {
+                                       GetClientRect(hwnd, &r);
+                                       stylusx = evt.x * r.right;
+                                       stylusy = (1.0 - evt.y) * r.bottom;
+                               }
+                               styluspressure = (evt.buttons & 1) ? evt.pressure : 0;
+                               /*
+                               printf("id=%d csrtype=%d b=%x (%0.3f, %0.3f, %0.3f) p=%0.3f tp=%0.3f\n", 
+                                          evt.id,
+                                          evt.type,
+                                          evt.buttons,
+                                          evt.x,
+                                          evt.y,
+                                          evt.z,
+                                          evt.pressure,
+                                          evt.tpressure
+                                          );
+                               */
+                       }
+
+#endif
+               }
+
+               switch (e.type)
+               {
+                       case SDL_VIDEOEXPOSE:
+                               videoExposed = 1;
+                               break;
+
+#ifdef WIN32
+                       case SDL_SYSWMEVENT:
+                       {
+                               SDL_SysWMmsg* m = e.syswm.msg;
+                               hwnd = m->hwnd;
+                               static bool init=false;
+                               if (!init)
+                               {
+                                       init = true;
+                                       DragAcceptFiles(hwnd, TRUE);
+                                       #ifdef USE_BBTABLET
+                                               td.initTablet(hwnd, tablet_system ? bbTabletDevice::SYSTEM_POINTER : bbTabletDevice::SEPARATE_POINTER );
+                                               if (!td.isValid())
+                                                        printf("No tablet/driver found\n");
+                                       #endif
+                               }
+                               if (m->msg == WM_DROPFILES)
+                               {
+                                       HDROP h = (HDROP)m->wParam;
+                                       
+                                       char name[512];
+                                       if (DragQueryFile(h, 0xffffffff, 0, 0) == 1)
+                                       {
+                                               DragQueryFile(h, 0, name, sizeof(name)/sizeof(name[0]));
+
+                                               StateMakerBase::current->FileDrop(name);
+                                       }
+
+                                       DragFinish(h);
+                               }
+
+                               break;
+                       }
+#endif
+
+                       case SDL_ACTIVEEVENT:
+                       {
+                               int gain = e.active.gain ? e.active.state : 0;
+                               int loss = e.active.gain ? 0 : e.active.state;
+                               SDL_focus = (SDL_focus | gain) & ~loss;
+                               if (gain & SDL_APPACTIVE)
+                                       StateMakerBase::current->ScreenModeChanged();
+                               if (loss & SDL_APPMOUSEFOCUS)
+                                       noMouse = 1;
+                               else if (gain & SDL_APPMOUSEFOCUS)
+                                       noMouse = 0;
+
+                               break;
+                       }
+
+                       case SDL_MOUSEMOTION:
+                               noMouse = false;
+                               StateMakerBase::current->Mouse(e.motion.x, e.motion.y, e.motion.x-mousex, e.motion.y-mousey, 0, 0, mouse_buttons);
+                               mousex = e.motion.x; mousey = e.motion.y;
+                               break;
+                       case SDL_MOUSEBUTTONUP:
+                               noMouse = false;
+                               mouse_buttons &= ~(1<<(e.button.button-1));
+                               StateMakerBase::current->Mouse(e.button.x, e.button.y, e.button.x-mousex, e.button.y-mousey, 
+                                                                               0, 1<<(e.button.button-1), mouse_buttons);
+                               mousex = e.button.x; mousey = e.button.y ;
+                               break;
+                       case SDL_MOUSEBUTTONDOWN:
+                               noMouse = false;
+                               mouse_buttons |= 1<<(e.button.button-1);
+                               StateMakerBase::current->Mouse(e.button.x, e.button.y, e.button.x-mousex, e.button.y-mousey, 
+                                                                               1<<(e.button.button-1), 0, mouse_buttons);
+                               mousex = e.button.x; mousey = e.button.y ;
+                               break;
+
+                       case SDL_KEYUP:
+                               StateMakerBase::current->KeyReleased(e.key.keysym.sym);
+                               break;
+
+                       case SDL_KEYDOWN:
+                       {
+                               SDL_KeyboardEvent & k = e.key;
+
+                               if (k.keysym.sym==SDLK_F4 && (k.keysym.mod & KMOD_ALT))
+                               {
+                                       quitting = 1;
+                               }
+                               else if (k.keysym.sym==SDLK_F12)        
+                               {
+                                       // Toggle system pointer controlled by tablet or not
+                                       #ifdef USE_BBTABLET
+                                               if (td.isValid())
+                                               {
+                                                       tablet_system = !tablet_system;
+                                                       td.setPointerMode(tablet_system ? bbTabletDevice::SYSTEM_POINTER : bbTabletDevice::SEPARATE_POINTER);
+                                               }
+                                       #endif
+                               }
+                               else if (k.keysym.sym==SDLK_RETURN && (k.keysym.mod & KMOD_ALT) && !(k.keysym.mod & KMOD_CTRL))
+                               {
+                                       ToggleFullscreen();
+                               }
+                               else if (StateMakerBase::current->KeyPressed(k.keysym.sym, k.keysym.mod))
+                               {
+                               }
+                               else if ((k.keysym.mod & (KMOD_ALT | KMOD_CTRL))==0)
+                               {
+                                       StateMakerBase::GetNew(k.keysym.sym);
+                               }
+                       }
+                       break;
+
+                       case SDL_QUIT:
+                               quitting = 1;
+                               break;
+               }
+       }
+
+       SDLPango_FreeContext(context);
+       SDL_Quit();
+       return 0;
+}
index a0a631e..f60f91f 100644 (file)
@@ -1,34 +1,34 @@
-/*\r
-    Copyright (C) 2005-2007 Tom Beaumont\r
-\r
-    This program is free software; you can redistribute it and/or modify\r
-    it under the terms of the GNU General Public License as published by\r
-    the Free Software Foundation; either version 2 of the License, or\r
-    (at your option) any later version.\r
-\r
-    This program is distributed in the hope that it will be useful,\r
-    but WITHOUT ANY WARRANTY; without even the implied warranty of\r
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
-    GNU General Public License for more details.\r
-\r
-    You should have received a copy of the GNU General Public License\r
-    along with this program; if not, write to the Free Software\r
-    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
-*/\r
-\r
-\r
-//X(fontImage,         "font",                         true)\r
-X(gradient,                    "gradient",                     false)\r
-X(mapBG,                       "map",                          false)\r
-X(mapBG2,                      "map_top",                      true)\r
-X(tileGraphics,                "tiles",                        true)\r
-X(tileGraphicsR,       "tiles_reflect",        true)\r
-X(girlGraphics,                "emi",                          true)\r
-X(titlePage,           "title",                        false)\r
-//X(iconGfx,                   "icon",                         false)\r
-\r
-//X(uiGraphics,                "ui",                           true)\r
-\r
-\r
-#undef X\r
-\r
+/*
+    Copyright (C) 2005-2007 Tom Beaumont
+
+    This program 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 2 of the License, or
+    (at your option) any later version.
+
+    This program 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 this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+
+//X(fontImage,         "font",                         true)
+X(gradient,                    "gradient",                     false)
+X(mapBG,                       "map",                          false)
+X(mapBG2,                      "map_top",                      true)
+X(tileGraphics,                "tiles",                        true)
+X(tileGraphicsR,       "tiles_reflect",        true)
+X(girlGraphics,                "emi",                          true)
+X(titlePage,           "title",                        false)
+//X(iconGfx,                   "icon",                         false)
+
+//X(uiGraphics,                "ui",                           true)
+
+
+#undef X
+
index 4fe0030..3f341c6 100644 (file)
-/*\r
-    Copyright (C) 2005-2007 Tom Beaumont\r
-\r
-    This program is free software; you can redistribute it and/or modify\r
-    it under the terms of the GNU General Public License as published by\r
-    the Free Software Foundation; either version 2 of the License, or\r
-    (at your option) any later version.\r
-\r
-    This program is distributed in the hope that it will be useful,\r
-    but WITHOUT ANY WARRANTY; without even the implied warranty of\r
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
-    GNU General Public License for more details.\r
-\r
-    You should have received a copy of the GNU General Public License\r
-    along with this program; if not, write to the Free Software\r
-    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
-*/\r
-\r
-#include "i18n.h"\r
-#include <string>\r
-#include <iostream>\r
-#include <cctype> // TODO: remove it later\r
-#include <errno.h>\r
-#include <iconv.h>\r
-\r
-//////////////////////////////////////////////////////\r
-// Config\r
-\r
-\r
-#ifdef _DEBUG\r
-#define EDIT\r
-#endif\r
-\r
-//#define MAP_LOCKED_VISIBLE\r
-\r
-#ifndef GAME_NAME\r
-#define GAME_NAME "Hex-a-hop"\r
-#endif\r
-\r
-#ifndef DATA_DIR\r
-#define DATA_DIR "."\r
-#endif\r
-\r
-#ifdef EDIT\r
-//     #define MAP_EDIT_HACKS\r
-       #define MAP_EDIT_HACKS_DISPLAY_UNLOCK 0\r
-       #define CHEAT\r
-       #define BMP_SUFFIX ".bmp"\r
-#else\r
-       #define USE_LEVEL_PACKFILE\r
-       #define BMP_SUFFIX ".dat"\r
-#endif\r
-\r
-\r
-\r
-#ifdef EDIT\r
-#define GAMENAME GAME_NAME " (EDIT MODE)"\r
-#endif\r
-#ifndef GAMENAME\r
-#define GAMENAME GAME_NAME\r
-#endif\r
-\r
-#define IMAGE_DAT_OR_MASK 0xff030303 // Reduce colour depth of images slightly for better compression (and remove useless top 8 bits!)\r
-#define STARTING_LEVEL "Levels\\0_green\\triangular.lev"\r
-#define UNLOCK_SCORING 75\r
-const char * mapname = "Levels\\map_maybe\\map.lev";\r
-\r
-//////////////////////////////////////////////////////\r
-\r
-\r
-\r
-#ifndef USE_OPENGL\r
-\r
-#include "state.h"\r
-\r
-#include "tiletypes.h"\r
-\r
-#ifdef USE_LEVEL_PACKFILE\r
-#include "packfile.h"\r
-#endif\r
-\r
-#include <unistd.h>\r
-#include <limits.h>\r
-#include <sys/stat.h>\r
-#include <sys/types.h>\r
-\r
-#ifndef PATH_MAX \r
-#define PATH_MAX 4096 \r
-#endif \r
-\r
-void RenderTile(bool reflect, int t, int x, int y, int cliplift=-1);\r
-\r
-int keyState[SDLK_LAST] = {0};\r
-\r
-FILE *file_open( const char *file, const char *flags )\r
-{\r
-//     printf("file_open( \"%s\", \"%s\" )\n", file, flags );\r
-       extern String base_path;\r
-       static String filename; // static to reduce memory alloc/free calls.\r
-       if (file[0]=='/') //If a full path is specified, don't prepend base_path\r
-               filename = "";\r
-       else\r
-       {\r
-               if (strncmp(file, "save", 4) == 0)\r
-               {\r
-                       const char *home = getenv("HOME");\r
-                       if (home) \r
-                       {\r
-                               char save_path[PATH_MAX];\r
-                               snprintf(save_path, sizeof(save_path), "%s/.hex-a-hop", home);\r
-                               if (!strchr(flags, 'r'))\r
-                                       if (mkdir(save_path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != -1)\r
-                                               printf("Creating directory \"%s\"\n", (const char *)save_path);\r
-                               strncat(save_path, "/", sizeof(save_path));\r
-                               filename = save_path;\r
-                       }\r
-                       else filename = "/tmp/";\r
-               }\r
-               else filename = base_path;\r
-       }\r
-       filename += file;\r
-//     printf("   -> \"%s\"\n", filename );\r
-\r
-       filename.fix_backslashes();\r
-       FILE* f = fopen( filename, flags );\r
-\r
-       if (!f && strncmp(file, "save", 4) != 0)\r
-       {\r
-               printf("Warning: unable to open file \"%s\" for %s\n", (const char*)filename, strchr(flags, 'r') ? "reading" : "writing");\r
-       }\r
-\r
-       return f;\r
-}\r
-\r
-\r
-#ifdef MAP_EDIT_HACKS\r
-       static const short value_order[]={\r
-               //WALL,\r
-               //COLLAPSE_DOOR2,\r
-               //COLLAPSABLE3\r
-               //SWITCH\r
-               //EMPTY, NORMAL,\r
-\r
-               COLLAPSABLE,\r
-               TRAMPOLINE,\r
-               COLLAPSE_DOOR, COLLAPSABLE2,\r
-               GUN,\r
-               FLOATING_BALL,\r
-               SPINNER,\r
-               TRAP,\r
-               0x100,\r
-               LIFT_DOWN, LIFT_UP,\r
-               BUILDER,\r
-               0x200,\r
-       };\r
-#endif\r
-\r
-//#define PROGRESS_FILE "progress.dat"\r
-\r
-#define PI (3.1415926535897931)\r
-#define PI2 (PI*2)\r
-#define MAX(a,b) ((a)>(b) ? (a) : (b))\r
-#define MIN(a,b) ((a)<(b) ? (a) : (b))\r
-#define ABS(a) ((a)<0 ? -(a) : (a))\r
-\r
-#define WATER_COLOUR 31 | (IMAGE_DAT_OR_MASK>>16)&255, 37 | (IMAGE_DAT_OR_MASK>>8)&255, 135 | (IMAGE_DAT_OR_MASK>>0)&255\r
-\r
-#define ROTATION_TIME 0.25\r
-#define BUILD_TIME 1\r
-#define LASER_LINE_TIME 0.7\r
-#define LASER_FADE_TIME 0.1\r
-#define LASER_SEGMENT_TIME 0.01\r
-#define LIFT_TIME 0.5\r
-#define JUMP_TIME 0.4\r
-\r
-#define X(NAME,FILE,ALPHA) SDL_Surface* NAME = 0;\r
-#include "gfx_list.h"\r
-int scrollX=0, scrollY=0, initScrollX=0, initScrollY=0;\r
-int mapRightBound = 0;\r
-int mapScrollX = 0;\r
-bool showScoring = false;\r
-bool hintsDone = false;\r
-\r
-enum {\r
-       TILE_SPLASH_1 = 17,\r
-       TILE_SPLASH_2,\r
-       TILE_SPLASH_3,\r
-\r
-       TILE_SPHERE = 20,\r
-       TILE_SPHERE_OPEN,\r
-       TILE_SPHERE_DONE,\r
-       TILE_SPHERE_PERFECT,\r
-       TILE_LOCK,\r
-\r
-       TILE_LIFT_BACK,\r
-       TILE_LIFT_FRONT,\r
-       TILE_LIFT_SHAFT,\r
-       TILE_BLUE_FRONT,\r
-       TILE_GREEN_FRONT,\r
-\r
-       TILE_LINK_0 = 30,\r
-       TILE_LINK_1,\r
-       TILE_LINK_2,\r
-       TILE_LINK_3,\r
-       TILE_LINK_4,\r
-       TILE_LINK_5,\r
-       TILE_GREEN_FRAGMENT,\r
-       TILE_GREEN_FRAGMENT_1,\r
-       TILE_GREEN_FRAGMENT_2,\r
-       TILE_ITEM2,\r
-\r
-       TILE_WATER_MAP = 40,\r
-       TILE_GREEN_CRACKED,\r
-       TILE_GREEN_CRACKED_WALL,\r
-       TILE_BLUE_CRACKED,\r
-       TILE_BLUE_CRACKED_WALL,\r
-       TILE_LASER_HEAD,\r
-       TILE_FIRE_PARTICLE_1,\r
-       TILE_FIRE_PARTICLE_2,\r
-       TILE_WATER_PARTICLE,\r
-\r
-       TILE_LASER_0 = 50,\r
-       TILE_LASER_FADE_0 = 53,\r
-       TILE_BLUE_FRAGMENT = 56,\r
-       TILE_BLUE_FRAGMENT_1,\r
-       TILE_BLUE_FRAGMENT_2,\r
-       TILE_ITEM1,\r
-       TILE_LASER_REFRACT = 60,\r
-       TILE_ICE_LASER_REFRACT = TILE_LASER_REFRACT+6,\r
-       TILE_WHITE_TILE,\r
-       TILE_WHITE_WALL,\r
-       TILE_BLACK_TILE,\r
-\r
-};\r
-\r
-const int colours[] = {\r
-       #define X(n,col, solid) col,\r
-       #include "tiletypes.h"\r
-};\r
-\r
-const int tileSolid[] = {\r
-       #define X(n,col, solid) solid,\r
-       #include "tiletypes.h"\r
-};\r
-\r
-void ChangeSuffix(char* filename, char* newsuffix)\r
-{\r
-       int len = strlen(filename);\r
-       int i = len-1;\r
-       while (i>=0 && filename[i]!='\\' && filename[i]!='.' && filename[i]!='/') \r
-               i--;\r
-       if (filename[i]=='.')\r
-               strcpy(filename+i+1, newsuffix);\r
-       else\r
-       {\r
-               strcat(filename, ".");\r
-               strcat(filename, newsuffix);\r
-       }\r
-}\r
-\r
-bool isMap=false, isRenderMap=false;\r
-int isFadeRendering=0;\r
-\r
-/*\r
-        |--|     |--|   TILE_W1\r
-        |--------|              TILE_W2\r
-               |-----|          TILE_WL\r
-        |-----------|   TILE_W3\r
-\r
-               *-----*         -                       -\r
-          /       \    |TILE_H1        |TILE_H2\r
-         /         \   |                       |\r
-        *           *  -                       |\r
-         \         /                           |\r
-          \       /                            |\r
-               *-----*                                 -\r
-\r
-       WL = sqrt(h1*h1 + w1*w1)\r
-       wl**2 = h1**2 + w1**2\r
-\r
-       w1 = sin60.wL\r
-       \r
-*/\r
-\r
-#if 1\r
-       #define TILE_W1 18\r
-       #define TILE_W3 64\r
-       #define GFX_SIZE TILE_W3\r
-       #define TILE_W2 (TILE_W3-TILE_W1)\r
-       #define TILE_H1 TILE_W1\r
-       #define TILE_HUP 22     //extra visible height of wall (used for determining whether a wall was clicked on)\r
-       #define TILE_H2 (TILE_H1*2)\r
-       #define TILE_WL (TILE_W2-TILE_W1)\r
-       #define TILE_H_LIFT_UP   26\r
-       #define TILE_H_REFLECT_OFFSET 24\r
-       #define TILE_HUP2 TILE_H_LIFT_UP        // Displacement of object on top of wall\r
-       #define FONT_SPACING 25\r
-       #define FONT_X_SPACING (-1)     // -1 in order to try and overlap the black borders of adjacent characters\r
-#else\r
-       #define TILE_WL 30\r
-       #define TILE_W1 (TILE_WL/2)\r
-       #define TILE_W2 (TILE_W1+TILE_WL)\r
-       #define TILE_W3 (TILE_W1+TILE_W2)\r
-       #define TILE_H1 (TILE_WL*0.8660254037844386)\r
-       #define TILE_H2 (TILE_H1*2)\r
-#endif\r
-\r
-#define MAX_DIR 6\r
-\r
-SDL_Rect tile[2][70];\r
-short tileOffset[2][70][2];\r
-int Peek(SDL_Surface* i, int x, int y)\r
-{\r
-       if (x<0 || y<0 || x>=i->w || y>=i->h)\r
-               return 0;\r
-       unsigned int p=0;\r
-       const int BytesPerPixel = i->format->BytesPerPixel;\r
-       const int BitsPerPixel = i->format->BitsPerPixel;\r
-       if (BitsPerPixel==8)\r
-               p = ((unsigned char*)i->pixels)[i->pitch*y + x*BytesPerPixel];\r
-       else if (BitsPerPixel==15 || BitsPerPixel==16)\r
-               p = *(short*)(((char*)i->pixels) + (i->pitch*y + x*BytesPerPixel));\r
-       else if (BitsPerPixel==32)\r
-               p = *(unsigned int*)(((char*)i->pixels) + (i->pitch*y + x*BytesPerPixel));\r
-       else if (BitsPerPixel==24)\r
-               p = (int)((unsigned char*)i->pixels)[i->pitch*y + x*BytesPerPixel]\r
-                 | (int)((unsigned char*)i->pixels)[i->pitch*y + x*BytesPerPixel] << 8\r
-                 | (int)((unsigned char*)i->pixels)[i->pitch*y + x*BytesPerPixel] << 16;\r
-       \r
-       return p;\r
-}\r
-bool IsEmpty(SDL_Surface* im, int x, int y, int w, int h)\r
-{\r
-       for (int i=x; i<x+w; i++)\r
-               for (int j=y; j<y+h; j++)\r
-                       if (Peek(im,i,j) != Peek(im,0,im->h-1)) \r
-                               return false;\r
-       return true;\r
-}\r
-\r
-void MakeTileInfo()\r
-{\r
-       for (int i=0; i<140; i++)\r
-       {\r
-               SDL_Rect r = {(i%10)*GFX_SIZE, ((i/10)%7)*GFX_SIZE, GFX_SIZE, GFX_SIZE};\r
-               short * outOffset = tileOffset[i/70][i%70];\r
-               SDL_Surface * im = (i/70) ? tileGraphicsR : tileGraphics;\r
-\r
-               outOffset[0] = outOffset[1] = 0;\r
-\r
-               while (r.h>1 && IsEmpty(im, r.x, r.y, r.w, 1)) r.h--, r.y++, outOffset[1]++;\r
-               while (r.h>1 && IsEmpty(im, r.x, r.y+r.h-1, r.w, 1)) r.h--;\r
-               while (r.w>1 && IsEmpty(im, r.x, r.y, 1, r.h)) r.w--, r.x++, outOffset[0]++;\r
-               while (r.w>1 && IsEmpty(im, r.x+r.w-1, r.y, 1, r.h)) r.w--;\r
-\r
-               tile[i/70][i%70] = r;\r
-       }\r
-}\r
-\r
-void ConvertToUTF8(const std::string &text_locally_encoded, char *text_utf8, size_t text_utf8_length)\r
-{\r
-       // Is this portable?\r
-       size_t text_length = text_locally_encoded.length()+1;\r
-       errno = 0;\r
-       static const char *locale_enc = gettext_init.GetEncoding();\r
-       iconv_t cd = iconv_open("UTF-8", locale_enc);\r
-       char *in_buf = const_cast<char *>(&text_locally_encoded[0]);\r
-       char *out_buf = &text_utf8[0];\r
-       iconv(cd, &in_buf, &text_length, &out_buf, &text_utf8_length);\r
-       iconv_close(cd);\r
-       if (errno != 0)\r
-               std::cerr << "An error occurred recoding " << text_locally_encoded << " to UTF8" << std::endl;\r
-}\r
-\r
-int SDLPangoTextWidth(const std::string &text_utf8);\r
-void Print_Pango(int x, int y, const std::string &text_utf8);\r
-void Print_Pango_Aligned(int x, int y, int width, const std::string &text_utf8, int align);\r
-\r
-/// Prints a left aligned string (a single line) beginning at (x,y)\r
-// TODO: Check that the maximal text width is already set\r
-void Print(int x, int y, const char * string, ...)\r
-{\r
-       va_list marker;\r
-       va_start( marker, string );     /* Initialize variable arguments. */\r
-\r
-       char tmp[1000], tmp_utf8[5000]; // FIXME: Check this limit\r
-       vsprintf((char*)tmp, string, marker);\r
-\r
-       ConvertToUTF8(tmp, tmp_utf8, sizeof(tmp_utf8)/sizeof(char));\r
-       Print_Pango(x, y, tmp_utf8);\r
-\r
-       va_end( marker );              /* Reset variable arguments.      */\r
-}\r
-\r
-/// Prints a string right aligned so that it ends at (x,y)\r
-// TODO: Check that the maximal text width is already set\r
-void PrintR(int x, int y, const char * string, ...)\r
-{\r
-       va_list marker;\r
-       va_start( marker, string );     /* Initialize variable arguments. */\r
-\r
-       char tmp[1000], tmp_utf8[5000]; // FIXME: Check this limit\r
-       vsprintf((char*)tmp, string, marker);\r
-\r
-       ConvertToUTF8(tmp, tmp_utf8, sizeof(tmp_utf8)/sizeof(char));\r
-       Print_Pango(x-SDLPangoTextWidth(tmp_utf8), y, tmp_utf8);\r
-\r
-       va_end( marker );              /* Reset variable arguments.      */\r
-}\r
-\r
-/** \brief Prints a string horizontally centered around (x,y)\r
- *\r
- *  "  " in the string is interpreted as linebreak\r
-*/\r
-void Print_Aligned(bool split, int x, int y, int width, const char * string, int align)\r
-{\r
-       char tmp_utf8[5000]; // FIXME: Check this limit\r
-\r
-       ConvertToUTF8(string, tmp_utf8, sizeof(tmp_utf8)/sizeof(char));\r
-\r
-       std::string msg(tmp_utf8);\r
-       while (split && msg.find("  ") != std::string::npos)\r
-               msg.replace(msg.find("  "), 2, "\n");\r
-\r
-       Print_Pango_Aligned(x, y, width, msg, align);\r
-}\r
-\r
-void PrintC(bool split, int x, int y, const char * string, ...)\r
-{\r
-       va_list marker;\r
-       va_start( marker, string );     /* Initialize variable arguments. */\r
-\r
-       char tmp[1000]; // FIXME: Check this limit\r
-       vsprintf((char*)tmp, string, marker);\r
-\r
-       va_end( marker );              /* Reset variable arguments.      */\r
-\r
-       static bool print = true; // avoid flickering!\r
-       if (print) {\r
-               std::cerr << "Warning: don't know window width for message:\n" << tmp << "\n";\r
-               for (unsigned int i=0; i<strlen(tmp); ++i)\r
-                       if (!std::isspace(tmp[i]))\r
-                               print = false;\r
-       }\r
-       Print_Aligned(split, x, y, 2*std::min(x, SCREEN_W-x), tmp, 1);\r
-}\r
-\r
-#include "savestate.h"\r
-#include "menus.h"\r
-#include "level_list.h"\r
-\r
-void SaveState::GetStuff()\r
-{\r
-       general.hintFlags = HintMessage::flags;\r
-}\r
-void SaveState::ApplyStuff()\r
-{\r
-       HintMessage::flags = general.hintFlags;\r
-}\r
-\r
-\r
-// somewhere else Tile map[][] is assigned to an unsigned char not int32_t\r
-// but the data file format expects it to be 32 bit wide!??\r
-typedef int32_t Tile;\r
-typedef int Dir;\r
-struct Pos{\r
-       int32_t x,y;\r
-       Pos() : x(0), y(0) {}\r
-       Pos(int a, int b) : x(a), y(b) {}\r
-       bool operator == (Pos const & p) const \r
-       {\r
-               return x==p.x && y==p.y;\r
-       }\r
-       Pos operator + (Dir const d) const\r
-       {\r
-               return Pos(\r
-                       x + ((d==1 || d==2) ?  1 : (d==4 || d==5) ? -1 : 0),\r
-                       y + ((d==0 || d==1) ? -1 : (d==3 || d==4) ?  1 : 0)\r
-               );\r
-       }\r
-       int getScreenX() const {\r
-               return x*TILE_W2;\r
-       }\r
-       int getScreenY() const {\r
-               return x*TILE_H1 + y*TILE_H2;\r
-       }\r
-       static Pos GetFromWorld(double x, double y)\r
-       {\r
-               x += TILE_W3/2;\r
-               y += TILE_H1;\r
-               int tx, ty;\r
-               tx = (int)floor(x/TILE_W2);\r
-               y -= tx*TILE_H1;\r
-               ty = (int)floor(y/TILE_H2);\r
-\r
-               y -= ty * TILE_H2;\r
-               x -= tx * TILE_W2;\r
-\r
-               if (x < TILE_W1 && y < TILE_H1)\r
-                       if (x*TILE_H1 + y * TILE_W1 < TILE_H1*TILE_W1)  \r
-                               tx--;\r
-               if (x < TILE_W1 && y > TILE_H1)\r
-                       if (x*TILE_H1 + (TILE_H2-y) * TILE_W1 < TILE_H1*TILE_W1)  \r
-                               tx--, ty++;\r
-\r
-               return Pos(tx, ty);\r
-       }\r
-};\r
-Pos mousep(0,0), keyboardp(4,20);\r
-\r
-class RenderObject;\r
-\r
-struct RenderStage\r
-{\r
-       virtual ~RenderStage() {}\r
-       virtual void Render(RenderObject* r, double time, bool reflect) = 0;\r
-       virtual int GetDepth(double /*time*/) { return 1; }\r
-};\r
-\r
-class RenderObject\r
-{\r
-       RenderStage** stage;\r
-       double* time;\r
-       int numStages;\r
-       int maxStages;\r
-       int currentStage;\r
-public: \r
-       double seed;\r
-       double currentTime;\r
-private:\r
-\r
-       void Reserve()\r
-       {\r
-               if (maxStages <= numStages)\r
-               {\r
-                       maxStages = maxStages ? maxStages*2 : 4;\r
-                       stage = (RenderStage**) realloc(stage, sizeof(stage[0])*maxStages);\r
-                       time  = (double*)               realloc(time, sizeof(time[0])*maxStages);\r
-               }\r
-       }\r
-public:\r
-       RenderObject() : stage(0), time(0), numStages(0), maxStages(0), currentStage(0)\r
-       {\r
-               // TODO:        use a random number with better range\r
-               //                      or maybe make seed an int or float...\r
-               seed = rand() / (double)RAND_MAX;\r
-       }\r
-       ~RenderObject()\r
-       {\r
-               free(stage); free(time);\r
-       }\r
-       bool Active(double t)\r
-       {\r
-               if (numStages==0) return false;\r
-               if (t < time[0]) return false;\r
-               return true;\r
-       }\r
-       void UpdateCurrent(double t)\r
-       {\r
-               if (currentStage >= numStages) currentStage = numStages-1;\r
-               if (currentStage < 0) currentStage = 0;\r
-\r
-               while (currentStage>0 && time[currentStage]>t)\r
-                       currentStage--;\r
-               while (currentStage<numStages-1 && time[currentStage+1]<=t)\r
-                       currentStage++;\r
-\r
-               currentTime = t;\r
-       }\r
-       RenderStage* GetStage(double t)\r
-       {\r
-               if (t==-1 && numStages>0)\r
-                       return stage[numStages-1];\r
-\r
-               if (!Active(t)) return 0;\r
-               UpdateCurrent(t);\r
-               return stage[currentStage];\r
-       }\r
-       double GetLastTime()\r
-       {\r
-               return numStages>0 ? time[numStages-1] : -1;\r
-       }\r
-       void Render(double t, bool reflect)\r
-       {\r
-               if (!Active(t)) \r
-                       return;\r
-               UpdateCurrent(t);\r
-               stage[currentStage]->Render(this, t - time[currentStage], reflect);\r
-       }\r
-       int GetDepth(double t)\r
-       {\r
-               if (!Active(t)) \r
-                       return -1;\r
-               UpdateCurrent(t);\r
-               return stage[currentStage]->GetDepth(t - time[currentStage]);\r
-       }\r
-       void Reset(double t)\r
-       {\r
-               if (t<0)\r
-                       numStages = currentStage = 0;\r
-               else\r
-               {\r
-                       while (numStages > 0 && time[numStages-1] >= t)\r
-                               numStages--;\r
-                       if (currentStage > 0 && currentStage >= numStages)\r
-                               currentStage = numStages - 1;\r
-               }\r
-               if (currentStage < 0) currentStage = 0;\r
-       }\r
-       void Wipe()\r
-       {\r
-               if (currentStage > 0 && numStages > 0)\r
-               {\r
-                       memmove(&time[0], &time[currentStage], sizeof(time[0]) * (numStages-currentStage));\r
-                       memmove(&stage[0], &stage[currentStage], sizeof(stage[0]) * (numStages-currentStage));\r
-                       numStages -= currentStage;\r
-                       currentStage = 0;\r
-               }\r
-       }\r
-       void Add(RenderStage* s, double t)\r
-       {\r
-               int i=0;\r
-               \r
-               if (currentStage<numStages && time[currentStage]<=t)\r
-                       i = currentStage;\r
-\r
-               while (i<numStages && time[i]<t)\r
-                       i++;\r
-\r
-               if (i<numStages && time[i]==t)\r
-                       stage[i]=s;\r
-               else\r
-               {\r
-                       Reserve();\r
-\r
-                       if (i<numStages)\r
-                       {\r
-                               memmove(&time[i+1], &time[i], (numStages-i) * sizeof(time[0]));\r
-                               memmove(&stage[i+1], &stage[i], (numStages-i) * sizeof(stage[0]));\r
-                       }\r
-\r
-                       numStages++;\r
-                       time[i] = t;\r
-                       stage[i] = s;\r
-               }\r
-       }\r
-};\r
-\r
-class WorldRenderer\r
-{\r
-       #define SIZE 30\r
-       #define FX 10\r
-       RenderObject tile[SIZE][SIZE][2];\r
-       RenderObject fx[FX];\r
-       int fxPos;\r
-\r
-public:\r
-       RenderObject player;\r
-       RenderObject dummy;\r
-\r
-       WorldRenderer()\r
-       {\r
-               Reset();\r
-       }\r
-\r
-       void Reset(double t = -1)\r
-       {\r
-               fxPos = 0;\r
-               player.Reset(t);\r
-               dummy.Reset(-1);\r
-\r
-               for (int i=0; i<SIZE; i++)\r
-                       for (int j=0; j<SIZE; j++)\r
-                               for (int q=0; q<2; q++)\r
-                                       tile[i][j][q].Reset(t);\r
-\r
-               for (int j=0; j<FX; j++)\r
-                       fx[j].Reset(t);\r
-       }\r
-\r
-       void Wipe()\r
-       {\r
-               player.Wipe();\r
-               dummy.Reset(-1);\r
-\r
-               for (int i=0; i<SIZE; i++)\r
-                       for (int j=0; j<SIZE; j++)\r
-                               for (int q=0; q<2; q++)\r
-                                       tile[i][j][q].Wipe();\r
-\r
-               for (int j=0; j<FX; j++)\r
-                       fx[j].Wipe();\r
-       }\r
-\r
-       bool Visible(Pos p)\r
-       {\r
-               int x0 = (scrollX+TILE_W2) / TILE_W2;\r
-               int x1 = (scrollX+SCREEN_W+TILE_W3+TILE_W1) / TILE_W2;\r
-               if (p.x<0 || p.y<0 || p.x>=SIZE || p.y>=SIZE) return false;\r
-               if (p.x<x0) return false;\r
-               if (p.x>=x1-1) return false;\r
-               for (int j0=0; j0<SIZE*3; j0++)\r
-               {\r
-                       if (j0 * TILE_H1 < scrollY-TILE_H1) continue;\r
-                       if (j0 * TILE_H1 > scrollY+SCREEN_H+TILE_H1) break;\r
-                       int i = j0&1;\r
-                       int j = j0>>1;\r
-                       j -= (x0-i)/2;\r
-                       i += (x0-i)/2*2;\r
-                       if (j>=SIZE) i+=(j+1-SIZE)*2, j=SIZE-1;\r
-                       for (; i<x1 && j>=0; i+=2, j--)\r
-                       {\r
-                               if (Pos(i,j)==p)\r
-                                       return true;\r
-                       }\r
-               }\r
-               return false;\r
-       }\r
-\r
-       void Render(double t, bool reflect)\r
-       {\r
-               dummy.Reset(-1);\r
-\r
-               int playerDepth = player.GetDepth(t);\r
-               if (reflect) playerDepth-=4;\r
-               if (playerDepth<0)\r
-                       player.Render(t, reflect);\r
-\r
-               int x0 = (scrollX+TILE_W2) / TILE_W2;\r
-               int x1 = (scrollX+SCREEN_W+TILE_W3+TILE_W1) / TILE_W2;\r
-               x0 = MAX(x0, 0);\r
-               x1 = MIN(x1, SIZE);\r
-               for (int j0=0; j0<SIZE*3; j0++)\r
-               {\r
-                       if (j0 * TILE_H1 < scrollY-TILE_H1) continue;\r
-                       if (j0 * TILE_H1 > scrollY+SCREEN_H+TILE_H1) break;\r
-                       int i = j0&1;\r
-                       int j = j0>>1;\r
-                       j -= (x0-i)/2;\r
-                       i += (x0-i)/2*2;\r
-                       if (j>=SIZE) i+=(j+1-SIZE)*2, j=SIZE-1;\r
-                       for (; i<x1 && j>=0; i+=2, j--)\r
-                       {\r
-                               for (int q=reflect?1:0; q!=2 && q!=-1; q += (reflect ? -1 : 1))\r
-                                       if (tile[i][j][q].Active(t))\r
-                                       {\r
-                                               tile[i][j][q].Render(t, reflect);\r
-                                       }\r
-                       }\r
-\r
-                       if (playerDepth==j0 || j0==SIZE*3 && playerDepth>j0)\r
-                               player.Render(t, reflect);\r
-               }\r
-\r
-               for (int j=0; j<FX; j++)\r
-                       if(fx[j].Active(t))\r
-                       {\r
-                               fx[j].Render(t, reflect);\r
-                       }\r
-\r
-       }\r
-       RenderObject & operator () ()\r
-       {\r
-               fxPos++;\r
-               if (fxPos==FX) fxPos = 0;\r
-               return fx[fxPos];\r
-       }\r
-       RenderObject & operator () (Pos const & p, bool item=false)\r
-       {\r
-               if (p.x<0 || p.y<0 || p.x>=SIZE || p.y>=SIZE)\r
-                       return dummy;\r
-               return tile[p.x][p.y][item ? 1 : 0];\r
-       }\r
-};\r
-\r
-void RenderTile(bool reflect, int t, int x, int y, int cliplift)\r
-{\r
-       SDL_Rect src = tile[reflect][t];\r
-       SDL_Rect dst = {x-scrollX-GFX_SIZE/2, y-scrollY-GFX_SIZE+TILE_H1, 0, 0};\r
-       dst.x += tileOffset[reflect][t][0];\r
-       dst.y += tileOffset[reflect][t][1];\r
-       if (reflect)\r
-               dst.y += TILE_H_REFLECT_OFFSET;\r
-       if (cliplift==-1 || reflect)\r
-       {\r
-       //      dst.w=src.w; dst.h=src.h;\r
-       //      SDL_FillRect(screen, &dst, rand());\r
-               SDL_BlitSurface(reflect ? tileGraphicsR : tileGraphics, &src, screen, &dst);\r
-       }\r
-       else\r
-       {\r
-               src.h -= cliplift;\r
-               if (src.h > TILE_W1)\r
-               {\r
-                       src.h -= TILE_W1/2;\r
-                       SDL_BlitSurface(tileGraphics, &src, screen, &dst);\r
-                       src.y += src.h;\r
-                       dst.y += src.h;\r
-                       src.h = TILE_W1/2;\r
-               }\r
-               if (src.h > 0)\r
-               {\r
-                       src.w -= TILE_W1*2, src.x += TILE_W1;\r
-                       dst.x += TILE_W1;\r
-                       SDL_BlitSurface(tileGraphics, &src, screen, &dst);\r
-               }\r
-       }       \r
-}\r
-void RenderGirl(bool reflect, int r, int frame, int x, int y, int h)\r
-{\r
-       int sx = r * 64;\r
-       int sy = frame * 80*2;\r
-       if (reflect) \r
-               y += TILE_H_REFLECT_OFFSET+20+h, sy += 80;\r
-       else\r
-               y -= h;\r
-       SDL_Rect src = {sx, sy, 64, 80};\r
-       SDL_Rect dst = {x-scrollX-32, y-scrollY-65, 0, 0};\r
-       SDL_BlitSurface(girlGraphics, &src, screen, &dst);\r
-}\r
-\r
-struct ItemRender : public RenderStage\r
-{\r
-       int item;\r
-       Pos p;\r
-       int water;\r
-       \r
-       ItemRender(int i2, int _water, Pos const & _p) :  item(i2), p(_p), water(_water)\r
-       {}\r
-\r
-       double Translate(double seed, double time)\r
-       {\r
-               double bob = time*2 + seed*PI2;\r
-               return sin(bob)*4;\r
-       }\r
-\r
-       void Render(RenderObject* r, double time, bool reflect)\r
-       {\r
-               if (item==0)\r
-                       return;\r
-\r
-               int y = -5 + (int)Translate(r->seed, r->currentTime + time);\r
-               if (reflect) \r
-                       y=-y;\r
-               if (!reflect && !water)\r
-                       RenderTile( false, TILE_SPHERE, p.getScreenX(), p.getScreenY());\r
-               RenderTile(\r
-                       reflect,\r
-                       item==1 ? TILE_ITEM1 : TILE_ITEM2,\r
-                       p.getScreenX(), p.getScreenY()+y\r
-               );\r
-       }\r
-};\r
-\r
-void RenderFade(double time, int dir, int seed)\r
-{\r
-       int ys=0;\r
-       srand(seed);\r
-       for(int x=rand()%22-11; x<SCREEN_W+22; x+=32, ys ^= 1)\r
-       {                       \r
-               for (int y=ys*20; y<SCREEN_H+30; y+=40)\r
-               {\r
-                       double a = (rand()&0xff)*dir;\r
-                       double b = (time * 0x400 + (y - SCREEN_H) * 0x140/SCREEN_H)*dir;\r
-                       if (a >= b)\r
-                       {\r
-                               RenderTile(false, TILE_BLACK_TILE, x+scrollX, y+scrollY);\r
-                       }\r
-               }\r
-       }\r
-}\r
-\r
-struct FadeRender : public RenderStage\r
-{\r
-       int seed;\r
-       int dir;\r
-       FadeRender(int d=-1) : seed(rand()), dir(d) \r
-       {\r
-               isFadeRendering = d;\r
-       }\r
-\r
-       void Render(RenderObject* /*r*/, double time, bool reflect)\r
-       {\r
-               if (reflect) return;\r
-               if (time > 0.5)\r
-               {\r
-                       if (dir==1) dir=0, isFadeRendering=0;\r
-                       return;\r
-               }\r
-               RenderFade(time, dir, seed);\r
-       }\r
-};\r
-\r
-struct ScrollRender : public RenderStage\r
-{\r
-       int x,y;\r
-       bool done;\r
-       ScrollRender(int a,int b) : x(a), y(b), done(false) {}\r
-\r
-       void Render(RenderObject* /*r*/, double /*time*/, bool /*reflect*/)\r
-       {\r
-               if (done) return;\r
-               scrollX = x, scrollY = y;\r
-               isRenderMap = isMap;\r
-               done = true;\r
-       }\r
-};\r
-\r
-struct LevelSelectRender : public RenderStage\r
-{\r
-       Pos p;\r
-       int item;\r
-       int adj;\r
-#ifdef MAP_EDIT_HACKS\r
-       int magic;\r
-#endif\r
-       \r
-       LevelSelectRender(Pos const & _p, int i2, int adj) : p(_p), item(i2), adj(adj)\r
-       {}\r
-\r
-       void Render(RenderObject* /*r*/, double /*time*/, bool reflect)\r
-       {\r
-               if (item==0)\r
-                       return;\r
-\r
-       #ifndef MAP_LOCKED_VISIBLE\r
-               if (item==1) return;\r
-       #endif\r
-\r
-       if (!reflect && adj)\r
-               for (int i=0; i<MAX_DIR; i++)\r
-                       if (adj & (1 << i))\r
-                               RenderTile( false, TILE_LINK_0+i, p.getScreenX(), p.getScreenY());\r
-\r
-       if (item < 0)\r
-               return;\r
-\r
-       if (!reflect)\r
-       {\r
-               RenderTile(\r
-                       reflect, \r
-                       TILE_SPHERE + item-1, \r
-                       p.getScreenX(), p.getScreenY()\r
-               );\r
-\r
-               #ifdef MAP_EDIT_HACKS\r
-                       int x = p.getScreenX()-scrollX, y = p.getScreenY()-scrollY;\r
-                       Print(x+5,y-25,"%d",magic);\r
-               #endif\r
-       }\r
-       }\r
-};\r
-\r
-struct ItemCollectRender : public ItemRender\r
-{\r
-       ItemCollectRender(int i2, Pos const & p) :  ItemRender(i2, 0, p)\r
-       {}\r
-\r
-       void Render(RenderObject* /*r*/, double /*time*/, bool /*reflect*/)\r
-       {\r
-       }\r
-};\r
-\r
-int GetLiftHeight(double time, int t)\r
-{      \r
-       if (t==LIFT_UP)\r
-               time = LIFT_TIME-time;\r
-       time = time / LIFT_TIME;\r
-       if (time > 1) \r
-               time = 1;\r
-       if (time < 0)\r
-               time = 0;\r
-       time = (3 - 2*time)*time*time;\r
-       if (t==LIFT_UP)\r
-               time = (3 - 2*time)*time*time;\r
-       if (t==LIFT_UP)\r
-               return (int)((TILE_H_LIFT_UP+4) * time);\r
-       else\r
-               return (int)((TILE_H_LIFT_UP-4) * time) + 4;\r
-}\r
-\r
-struct TileRender : public RenderStage\r
-{\r
-       int special;\r
-       int t;\r
-       Pos p;\r
-       double specialDuration;\r
-       \r
-       TileRender(int i, Pos const & _p, int _special=0) : special(_special), t(i), p(_p), specialDuration(LASER_LINE_TIME)\r
-       {}\r
-\r
-       void Render(RenderObject* r, double time, bool reflect)\r
-       {\r
-               if (t==0 && special==0)\r
-                       return;\r
-\r
-               if (special && (t==LIFT_UP || t==LIFT_DOWN) && time<LIFT_TIME)\r
-               {\r
-                       int y = GetLiftHeight(time, t);\r
-                       if (!reflect)\r
-                       {\r
-                               RenderTile(reflect, TILE_LIFT_BACK, p.getScreenX(), p.getScreenY());\r
-                               RenderTile(reflect, TILE_LIFT_SHAFT, p.getScreenX(), p.getScreenY()+y, y-8);\r
-                               RenderTile(reflect, TILE_LIFT_FRONT, p.getScreenX(), p.getScreenY());\r
-                       }\r
-                       else\r
-                       {\r
-                               RenderTile(reflect, TILE_LIFT_SHAFT, p.getScreenX(), p.getScreenY()-y, y);\r
-                               RenderTile(reflect, LIFT_DOWN, p.getScreenX(), p.getScreenY());\r
-                       }\r
-               }\r
-               else if (special && (t==EMPTY || t==TRAP) && !reflect && time < specialDuration)\r
-               {\r
-                       if (t == TRAP)\r
-                               if (time < specialDuration-LASER_FADE_TIME)\r
-                                       RenderTile(reflect, TILE_ICE_LASER_REFRACT, p.getScreenX(), p.getScreenY());\r
-                               else\r
-                                       RenderTile(reflect, t, p.getScreenX(), p.getScreenY());\r
-                       int base = ((t==EMPTY) ? TILE_LASER_0 : TILE_LASER_REFRACT);\r
-                       if (t==EMPTY && time >= specialDuration-LASER_FADE_TIME)\r
-                               base = TILE_LASER_FADE_0;\r
-                       \r
-                       int foo=special;\r
-                       for(int i=0; foo; foo>>=1, i++)\r
-                               if (foo & 1)\r
-                                       RenderTile(reflect, base+i, p.getScreenX(), p.getScreenY());\r
-               }\r
-               else if (t==FLOATING_BALL)\r
-               {\r
-                       int y = int(1.8 * sin(r->seed*PI + time*4));\r
-                       if (special==512)\r
-                       {\r
-                               if (time > 2) return;\r
-                               if (reflect) return;\r
-                               srand(int(r->seed * 0xfff));\r
-                               for (int i=0; i<20 - int(time*10); i++)\r
-                               {\r
-                                       int x = int((((rand() & 0xfff) - 0x800) / 10) * time);\r
-                                       int y = int((((rand() & 0xfff) - 0x800) / 10) * time);\r
-                                       RenderTile(true, 19 + ((i+int(time*5))&1)*10, p.getScreenX() + x, p.getScreenY() - 14 + y);\r
-                               }\r
-\r
-                               if (time < 0.05)\r
-                                       RenderTile(true, 18, p.getScreenX(), p.getScreenY() - 14);\r
-                       }\r
-                       else if (special)\r
-                               RenderBoat(reflect, int(special)&255,  p.getScreenX(), p.getScreenY(), y);\r
-                       else\r
-                               RenderTile(reflect, t, p.getScreenX(), p.getScreenY() + (reflect ? -y : y));\r
-               }\r
-               else if (t != EMPTY)\r
-                       RenderTile(reflect, t, p.getScreenX(), p.getScreenY());\r
-       }\r
-       static void RenderBoat(bool reflect, int d, int x, int y, int yo)\r
-       {\r
-               if (reflect)\r
-                       RenderGirl(reflect, d, 0, x, y, -yo);\r
-               RenderTile(reflect, FLOATING_BALL, x, y+yo);\r
-               if (!reflect)\r
-               {\r
-                       RenderGirl(reflect, d, 0, x, y, -yo);\r
-                       RenderTile(true, 17, x, y+yo-TILE_H_REFLECT_OFFSET);\r
-               }\r
-       }\r
-};\r
-\r
-struct TileRotateRender : public TileRender\r
-{\r
-       Dir d;\r
-//     int range;\r
-       int mode;\r
-       TileRotateRender(int i, Pos const & p, Dir _d, int m) : TileRender(i, p), d(_d), mode(m)\r
-       {}\r
-       void Render(RenderObject* r, double time, bool reflect)\r
-       {\r
-               if (t==0)\r
-                       return;\r
-               double f = time / ROTATION_TIME;\r
-\r
-               if (mode & 1) f += 0.5;\r
-               if (f<1 && f>0)\r
-               {\r
-                       if (mode & 2)\r
-                               ;\r
-                       else\r
-                               f = (3-2*f)*f*f;\r
-               }\r
-\r
-               if (mode & 1) f=1-f; else f=f;\r
-               if (f<0) f=0;\r
-\r
-               if (f >= 1)\r
-                       TileRender::Render(r, time, reflect);\r
-               else\r
-               {\r
-                       Pos dd = (Pos(0,0)+d);\r
-                       int x = p.getScreenX() + int(dd.getScreenX()*(f));\r
-                       int y = p.getScreenY() + int(dd.getScreenY()*(f));\r
-\r
-                       if (mode & 2)\r
-                               RenderBoat(reflect, (mode&1) ? (d+MAX_DIR/2)%MAX_DIR : d, x, y, 2);\r
-                       else\r
-                               RenderTile(reflect, t, x, y);\r
-               }\r
-       }\r
-};\r
-\r
-struct LaserRender : public RenderStage\r
-{\r
-       Pos p;\r
-       Dir d;\r
-       int range;\r
-\r
-       LaserRender(Pos _p, int dir, int r) : p(_p), d(dir), range(r)\r
-       {}\r
-\r
-       void Render(RenderObject* /*r*/, double /*time*/)\r
-       {\r
-       }\r
-};\r
-\r
-struct ExplosionRender : public RenderStage\r
-{\r
-       Pos p;\r
-       int seed;\r
-       int power;\r
-       int type;\r
-\r
-       ExplosionRender(Pos _p, int _pow=0, int t=0) : p(_p), power(_pow), type(t)\r
-       {\r
-               seed = rand();\r
-       }\r
-\r
-       virtual int GetDepth(double /*time*/) \r
-       {\r
-               return p.x + p.y*2;\r
-       }\r
-\r
-       void Render(RenderObject* /*r*/, double time, bool reflect)\r
-       {\r
-               if (type==1 && time > 2.5)\r
-                       type = -1, new WinLoseScreen(false);\r
-\r
-       //      if (reflect) return;\r
-               if (time > 3) return;\r
-               srand(seed);\r
-               int q = 50 - int(time * 35);\r
-               if (power) q*=2;\r
-               if (type) q = 50;\r
-               for (int i=0; i<q; i++)\r
-               {\r
-                       int x = p.getScreenX();\r
-                       int y = p.getScreenY() + (rand() & 31)-16;\r
-                       int xs = ((rand() & 63) - 32);\r
-                       int ys = (-10 - (rand() & 127)) * (1+power);\r
-                       if (type) ys*=2, xs/=2;\r
-                       x += int(xs * (1+time*(2+power)));\r
-                       int yo = int(time*time*128 + ys*time);\r
-                       //if (yo > 0) yo=-yo;//continue;\r
-                       if (type)\r
-                       {\r
-                               \r
-                               if (yo > 0)\r
-                               {\r
-                                       if (!reflect && ys<-60)\r
-                                       {\r
-                                               const double T = 0.06;\r
-                                               double ct = -ys / 128.0;\r
-                                               if (time < ct+T*4)\r
-                                               {\r
-                                                       x = p.getScreenX() + int(xs * (1+ct*(2+power)));\r
-                                                       RenderTile(\r
-                                                               reflect, \r
-                                                               time > ct+3*T ? TILE_SPLASH_3 : time > ct+2*T ? TILE_SPLASH_2 :  time > ct+T ?  TILE_SPLASH_1 : TILE_WATER_PARTICLE+1, \r
-                                                               x, y);\r
-                                               }\r
-                                       }\r
-                               }\r
-                               else\r
-                                       RenderTile(\r
-                                               reflect, \r
-                                               time - i*0.003 < 0.2 ? TILE_WATER_PARTICLE+1 : TILE_WATER_PARTICLE, \r
-                                               x, y+(reflect?-1:1)*yo);\r
-                       }\r
-                       else\r
-                       {\r
-                               if (yo > 0)\r
-                                       ;\r
-                               else\r
-                                       RenderTile(\r
-                                               reflect, \r
-                                               i<q-20 || time<0.3 ? TILE_LASER_HEAD : i<q-10 || time<0.6 ? TILE_FIRE_PARTICLE_1 : TILE_FIRE_PARTICLE_2, \r
-                                               x, y+(reflect?-1:1)*yo);\r
-                       }\r
-               }\r
-       }\r
-};\r
-struct DisintegrateRender : public RenderStage\r
-{\r
-       Pos p;\r
-       int seed;\r
-       int height;\r
-       int type;\r
-\r
-       DisintegrateRender(Pos _p, int _pow=0, int _t=0) : p(_p), height(_pow), type(_t)\r
-       {\r
-               seed = rand();\r
-       }\r
-\r
-       void Render(RenderObject* /*r*/, double time, bool reflect)\r
-       {\r
-               if (type)\r
-                       RenderTile(reflect, height ? COLLAPSE_DOOR : COLLAPSABLE, p.getScreenX(), p.getScreenY());\r
-\r
-               if (time > 50.0/70.0) return;\r
-               if (reflect) return;\r
-               srand(seed);\r
-               int q = 50 - int(time * 70);\r
-               if (height) q*=2;\r
-               for (int i=0; i<q; i++)\r
-               {\r
-                       int x = (rand() % (TILE_W3-8))-TILE_W3/2+4;\r
-                       int y = (rand() % (TILE_H2-8))-TILE_H1+4;\r
-                       if (x<-TILE_WL/2 && ABS(y)<-TILE_WL/2-x) continue;\r
-                       if (x>TILE_WL/2 && ABS(y)>x-TILE_WL/2) continue;\r
-                       int yo=0;\r
-                       if (height) yo -= rand() % TILE_HUP;\r
-                       x += p.getScreenX();\r
-                       y += p.getScreenY() + 4;\r
-                       int xs = 0;//((rand() & 63) - 32);\r
-                       int ys = (- (rand() & 31));\r
-                       x += int(xs * (1+time*(2)));\r
-                       if (type) yo = -yo;\r
-                       yo += int(time*time*128 + ys*time);\r
-                       if (type) yo = -yo*2;\r
-                       //if (yo > 0) yo=-yo;//continue;\r
-                       int t = type ? TILE_BLUE_FRAGMENT : TILE_GREEN_FRAGMENT;\r
-                       if (i>q-20) t++;\r
-                       if (i>q-10) t++;\r
-                       if (yo > 5) yo = 5;\r
-                       RenderTile(false, t, x, y+(reflect?-yo:yo));\r
-               }\r
-       }\r
-};\r
-struct BuildRender : public RenderStage\r
-{\r
-       Pos p;\r
-       Dir dir;\r
-       int reverse;\r
-       int height;\r
-       int type;\r
-\r
-       BuildRender(Pos _p, Dir _d, int _h, int _r=0, int _type=0) : p(_p), dir(_d), reverse(_r), height(_h), type(_type)\r
-       {\r
-       }\r
-\r
-       void Render(RenderObject* /*r*/, double time, bool reflect)\r
-       {\r
-               if (time >= BUILD_TIME)\r
-                       RenderTile(reflect, height ^ reverse ? (type ? COLLAPSE_DOOR2 : COLLAPSE_DOOR) : (type ? COLLAPSABLE2 : COLLAPSABLE), p.getScreenX(), p.getScreenY());\r
-               else \r
-               {\r
-                       if (height)\r
-                               RenderTile(reflect, type ? COLLAPSABLE2 : COLLAPSABLE, p.getScreenX(), p.getScreenY());\r
-\r
-                       double dist = time * 2 / BUILD_TIME;\r
-                       if (dir>-1)\r
-                       {\r
-                               Pos from = p + ((dir+MAX_DIR/2)%MAX_DIR);\r
-                               if (dist <= 1)\r
-                               //if (dist > 1)\r
-                               {\r
-                                       double offset = (dist*0.7) + 0.3;\r
-                                       int x = from.getScreenX() + int((p.getScreenX()-from.getScreenX()) * offset);\r
-                                       int y = from.getScreenY() + int((p.getScreenY()-from.getScreenY()) * offset - dist*(1-dist)*(TILE_HUP*4));\r
-                                       RenderTile(reflect, TILE_GREEN_FRAGMENT, x, y);                         \r
-                               }\r
-                               dist -= 1;\r
-                       }\r
-                       else\r
-                       {\r
-                               if (reverse) dist = 1-dist;\r
-                       }\r
-                       if (dist > 0 && !height)\r
-                       {\r
-                               if (!reflect)\r
-                                       for (int i=0; i<=int(dist*15); i++)\r
-                                       {\r
-                                               int x = p.getScreenX(), y = p.getScreenY();\r
-                                               double d = (i + fmod(dist*15, 1))/10.0;\r
-                                               int x1 = int(sin(d*5+time)*MIN(d,1)*TILE_W2/2);\r
-                                               int y1 = int(cos(d*5+time)*MIN(d,1)*TILE_H1*0.7);\r
-                                               RenderTile(reflect, TILE_GREEN_FRAGMENT, x+x1, y+y1+4);\r
-                                               RenderTile(reflect, TILE_GREEN_FRAGMENT, x-x1, y-y1+4);\r
-                                       }\r
-                       }\r
-                       if (dist > 0 && height)\r
-                       {\r
-                               int yo = int((1-dist)*(TILE_HUP*1.3));\r
-                               if (yo > TILE_HUP*1.1)\r
-                                       RenderTile(reflect, TILE_WHITE_TILE, p.getScreenX(), p.getScreenY());\r
-                               else if (!reflect)\r
-                               {\r
-                                       RenderTile(reflect, type ? COLLAPSABLE2 : COLLAPSABLE, p.getScreenX(), p.getScreenY());\r
-                                       RenderTile(reflect, type ? COLLAPSE_DOOR2 : COLLAPSE_DOOR, p.getScreenX(), p.getScreenY()+(reflect ? -yo : yo), yo+6);\r
-                                       RenderTile(reflect, type ? TILE_BLUE_FRONT : TILE_GREEN_FRONT, p.getScreenX(), p.getScreenY());\r
-                               }\r
-                               else\r
-                               {\r
-                                       if (yo < TILE_HUP/2)\r
-                                       {\r
-                                               RenderTile(reflect, type ? COLLAPSE_DOOR2 : COLLAPSE_DOOR, p.getScreenX(), p.getScreenY()+(reflect ? -yo : yo), yo);\r
-                                       \r
-                                       }\r
-                                       RenderTile(reflect, type ? COLLAPSABLE2 : COLLAPSABLE, p.getScreenX(), p.getScreenY());\r
-                                       \r
-                               }\r
-                       }\r
-               }\r
-       }\r
-};\r
-\r
-struct PlayerRender : public RenderStage\r
-{\r
-       Pos p;\r
-       Pos target;\r
-       int p_h, target_h;\r
-       int r;\r
-       int type;\r
-       double speed;\r
-       bool dead;\r
-       \r
-       PlayerRender(Pos a, int h, bool d) : p(a), target(a), p_h(h), target_h(h), r(3), type(0), speed(0), dead(d)\r
-       {}\r
-       PlayerRender(int _r, Pos a, int h1, Pos t, int h2, bool d) : p(a), target(t), p_h(h1), target_h(h2), r(_r), type(0), speed(JUMP_TIME), dead(d)\r
-       {\r
-               int dist = MAX(ABS(p.x-target.x), ABS(p.y-target.y));\r
-               if (dist > 1)\r
-                       speed *= 1.5;\r
-               if(dist==0)\r
-                       speed = 0;\r
-       }\r
-\r
-       virtual int GetDepth(double time)\r
-       {\r
-               double f = speed ? time / speed : 1;\r
-               if (f>1) f=1;\r
-               if (f==1) dead = this->dead;\r
-\r
-               if (f==1 || f>0.5 && p_h>target_h)\r
-                       return target.x+target.y*2;\r
-               return MAX(target.x+target.y*2 , p.x+p.y*2);\r
-       }\r
-\r
-       void Render(RenderObject* /*ro*/, double time, bool reflect)\r
-       {\r
-               bool dead = false;\r
-               double f = speed ? time / speed : 1;\r
-               if (f>1) f=1;\r
-               if (f==1) dead = this->dead;\r
-\r
-               int x = p.getScreenX();\r
-               int y = p.getScreenY();\r
-               int x2 = target.getScreenX();\r
-               int y2 = target.getScreenY();\r
-               int h = 0;\r
-               int shadow_h = (int)((p_h+(target_h-p_h)*f)*TILE_HUP2);\r
-\r
-               if (x==x2 && y==y2 && p_h!=target_h)\r
-               {\r
-                       h = TILE_H_LIFT_UP - GetLiftHeight(time, p_h ? LIFT_DOWN : LIFT_UP);\r
-               }\r
-               else\r
-               {\r
-                       \r
-                       int dist = MAX(ABS(p.x-target.x), ABS(p.y-target.y));\r
-                       int arc = dist*dist;\r
-                       int h1 = p_h * TILE_HUP2;;\r
-                       int h2 = target_h * TILE_HUP2;\r
-                       if (dist==2 && h1!=0) \r
-                       {\r
-                               arc += h2 ? 1 : 3;\r
-                               h1 = 0;\r
-                               shadow_h = f>=0.7 ? int(shadow_h*(f-0.7)/0.3) : 0;\r
-                       }\r
-                       if (dist==0)\r
-                               arc = speed > JUMP_TIME ? 7 : 2;\r
-\r
-                       h = (int)(h1+(h2-h1)*f);\r
-               //      if (x==x2 && y==y2)\r
-               //              ;\r
-               //      else\r
-                       {\r
-                               //h += int(TILE_H_LIFT_UP/3 * (1-f));\r
-                               h += (int)(f*(1-f)*TILE_HUP2*arc);\r
-                       }\r
-\r
-                       if (type==2)\r
-                               h=0;\r
-               }\r
-\r
-               if (!dead)\r
-               {\r
-                       int frame = 0;\r
-                       if (type==2 && f<1)\r
-                       {\r
-                               //frame = ((int)(f*4) % 4);\r
-                               //if (frame==2) frame=0; else if (frame==3) frame=2;\r
-                               frame = 0;\r
-                       }\r
-                       else if (f==1 || x==x2 && y==y2)        // stationary\r
-                               frame = 0;\r
-                       else if (f > 0.7)\r
-                               frame = 0;\r
-                       else\r
-                       {\r
-                               frame = type ? 2 : 1;\r
-                               if (f<0.1 || f>0.6)\r
-                                       frame += 2;\r
-                       }\r
-\r
-                       if (!reflect)\r
-                               RenderTile( false, TILE_SPHERE,\r
-                                       (int)(x+(x2-x)*f),\r
-                                       (int)(y+(y2-y)*f) - shadow_h\r
-                               );\r
-\r
-                       RenderGirl(\r
-                               reflect,\r
-                               r, frame, \r
-                               (int)(x+(x2-x)*f),\r
-                               (int)(y+(y2-y)*f),\r
-                               h\r
-                               );\r
-\r
-               }\r
-       /*      RenderTile(\r
-                       dead ? TILE_SPHERE_OPEN : TILE_SPHERE_DONE, \r
-                       (int)(x+(x2-x)*f),\r
-                       (int)(y+(y2-y)*f),\r
-                       true\r
-                       );*/\r
-       }\r
-};\r
-\r
-\r
-struct HexPuzzle : public State\r
-{\r
-       struct Undo\r
-       {\r
-               #define MAX_TILECHANGE 64 // TODO: don't have a magic upper limit\r
-               struct TileChange\r
-               {\r
-                       Pos p;\r
-                       Tile t;\r
-                       int item;\r
-                       \r
-                       TileChange()\r
-                       {}\r
-                       TileChange(Pos _p, Tile _t, int _i) : p(_p), t(_t), item(_i)\r
-                       {}\r
-                       void Restore(HexPuzzle* w)\r
-                       {\r
-                               w->SetTile(p,t,false,false);\r
-                               w->SetItem(p,item,false,false);\r
-                       }\r
-               };\r
-\r
-               TileChange t[MAX_TILECHANGE];\r
-               Pos playerPos;\r
-               Dir playerMovement;\r
-               int numT;\r
-               int numItems[2];\r
-               int score;\r
-               double time;\r
-               double endTime;\r
-\r
-               void Add(TileChange const & tc)\r
-               {\r
-                       for (int i=0; i<numT; i++)\r
-                               if (t[i].p==tc.p)\r
-                                       return;\r
-                       if (numT>=MAX_TILECHANGE)\r
-                               FATAL("numT>=MAX_TILECHANGE"); \r
-                       else \r
-                               t[numT++] = tc;\r
-               }\r
-               void New(Dir pmove, Pos & pp, int* items, double t, int sc)\r
-               {\r
-                       numItems[0] = items[0];\r
-                       numItems[1] = items[1];\r
-                       playerPos = pp;\r
-                       playerMovement = pmove;\r
-                       score = sc;\r
-                       time = t;\r
-                       numT = 0;\r
-               }\r
-               void Restore(HexPuzzle* w)\r
-               {\r
-                       for (int i=numT-1; i>=0; i--)\r
-                               t[i].Restore(w);\r
-                       w->dead = false;\r
-                       w->win = false;\r
-                       w->player = playerPos;\r
-                       w->player_items[0] = numItems[0];\r
-                       w->player_items[1] = numItems[1];\r
-                       w->player_score = score;\r
-\r
-                       //w->renderer.player.Add(new PlayerRender(playerPos, w->GetHeight(playerPos), false), w->time);\r
-               }\r
-       };\r
-\r
-       #define MAP_SIZE 30\r
-       char* special[MAP_SIZE][MAP_SIZE];\r
-       Tile map[MAP_SIZE][MAP_SIZE];\r
-       int32_t map_item[MAP_SIZE][MAP_SIZE];\r
-       int tileCount[NumTileTypes];\r
-       int32_t levelPar, levelDiff;\r
-       int turboAnim;\r
-       Pos player;\r
-       int player_items[2];\r
-       int player_score;\r
-       int numComplete, numLevels, numMastered, numLevelsFound;\r
-       bool dead;\r
-       bool win;\r
-       int winFinal;\r
-\r
-       SaveState progress;\r
-\r
-       WorldRenderer renderer;\r
-       double time;\r
-       double undoTime;\r
-\r
-       #define MAX_UNDO 6\r
-       Undo undo[MAX_UNDO];\r
-       int numUndo;\r
-       LevelInfo* currentLevelInfo;\r
-\r
-       char currentFile[1000];\r
-\r
-       ~HexPuzzle()\r
-       {\r
-               FreeGraphics();\r
-       }\r
-\r
-       LevelInfo* GetLevelInfo(const char* f)\r
-       {\r
-               if (strstr(f, "Levels\\") == f)\r
-                       f += 7;\r
-               if (currentLevelInfo!=0 && strcmp(currentLevelInfo->file, f)==0)\r
-                       return currentLevelInfo;\r
-               \r
-               if (f[0]=='_')\r
-               {\r
-                       int t = atoi(f+1);\r
-                       if (t <= numComplete)\r
-                               return 0;\r
-\r
-                       static char tmp1[1000];\r
-                       static LevelInfo tmp = {0, "", tmp1};\r
-                       sprintf(tmp1, ngettext("Complete 1  more level  to unlock!", "Complete %d  more levels  to unlock!", t-numComplete), t-numComplete);\r
-                       return &tmp;\r
-               }\r
-\r
-               for (unsigned int i=0; i<sizeof(levelNames)/sizeof(levelNames[0]); i++)\r
-                       if (strcmp(f, levelNames[i].file)==0)\r
-                               return &levelNames[i];\r
-               static LevelInfo tmp = {0, "", _("<<NO NAME>>")};\r
-               return &tmp;\r
-       }\r
-\r
-#ifdef MAP_EDIT_HACKS\r
-       int GetAutoTile(const char * level, bool tiletype)\r
-       {\r
-               FILE* f = file_open(filename, "rb");\r
-               int tile = EMPTY;\r
-               int version;\r
-\r
-               if (f && fscanf(f, "%d", &version)==1 && (version==3 || version==4))\r
-               {\r
-                       if (strstr(level,"mk"))\r
-                               level+=0;\r
-\r
-                       fgetc(f); // Remove '\n' character\r
-\r
-                       int32_t par, diff;\r
-                       unsigned char bounds[4];\r
-                       Pos playerStart;\r
-                       fread(&par, sizeof(par), 1, f);\r
-                       par = SWAP32(par);\r
-\r
-                       if (version >= 4) {\r
-                               fread(&diff, sizeof(diff), 1, f);\r
-                       diff = SWAP32(diff);\r
-      }\r
-                       fread(bounds, sizeof(bounds), 1, f);\r
-                       fread(&playerStart, sizeof(playerStart), 1, f);\r
-                       playerStart.x = SWAP32(playerStart.x);\r
-                       playerStart.y = SWAP32(playerStart.y);\r
-\r
-                       int highval=0;\r
-\r
-                       for (int i=bounds[0]; i<=bounds[1]; i++)\r
-                               for (int j=bounds[2]; j<=bounds[3]; j++)\r
-                               {\r
-                                       unsigned char comp = map[i][j] | (map_item[i][j]<<5);\r
-                                       fread(&comp, sizeof(comp), 1, f);\r
-                                       int t = comp & 0x1f;\r
-                                       int item = (comp >> 5) & 3;\r
-                                       for (int i=highval+1; i<sizeof(value_order)/sizeof(value_order[0]); i++)\r
-                                               if (t!=0 && t==value_order[i] \r
-                                                ||     item!=0 && item==(value_order[i]>>8))\r
-                                                       highval = i;\r
-                               }\r
-\r
-                       if (tiletype)\r
-                       {\r
-                               tile = value_order[highval];\r
-                               if (tile==0x100) tile = COLLAPSABLE3;\r
-                               if (tile==0x200) tile = SWITCH;\r
-                               if (tile==LIFT_UP) tile = LIFT_DOWN;\r
-                       }\r
-                       else\r
-                       {\r
-                               if (value_order[highval] == LIFT_UP)\r
-                                       tile = highval-1;\r
-                               else\r
-                                       tile = highval;\r
-                       }\r
-               }\r
-               else\r
-               {\r
-                       level+=0;\r
-               }\r
-               if (f)\r
-                       fclose(f);\r
-               return tile;\r
-       }\r
-#endif\r
-\r
-       void InitSpecials()\r
-       {\r
-               numComplete = numLevels = numMastered = numLevelsFound = 0;\r
-               for (int i=0; i<MAP_SIZE; i++)\r
-                       for (int j=0; j<MAP_SIZE; j++)\r
-                               ActivateSpecial(Pos(i,j), 0);\r
-               for (int i=0; i<MAP_SIZE; i++)\r
-                       for (int j=0; j<MAP_SIZE; j++)\r
-                               ActivateSpecial(Pos(i,j), 2);\r
-               numComplete = numLevels = numMastered = numLevelsFound = 0;\r
-               for (int i=0; i<MAP_SIZE; i++)\r
-                       for (int j=0; j<MAP_SIZE; j++)\r
-                               ActivateSpecial(Pos(i,j), 0);\r
-\r
-       }\r
-       void DoHints()\r
-       {\r
-               #ifndef EDIT\r
-                       if (strcmp(mapname, currentFile)==0)\r
-                       {\r
-//                             for (int i=0; i<32; i++)\r
-//                                     HintMessage::FlagTile(i);\r
-                               if (numComplete >= UNLOCK_SCORING && !progress.general.scoringOn)\r
-                               {\r
-                                       HintMessage::FlagTile(26);\r
-                                       progress.general.scoringOn = 1;\r
-                                       InitSpecials(); // Re-initialise with gold ones available\r
-                               }\r
-                               HintMessage::FlagTile(25);\r
-                       }\r
-                       else\r
-                       {\r
-                               for (int i=0; i<MAP_SIZE; i++)\r
-                                       for (int j=0; j<MAP_SIZE; j++)\r
-                                       {\r
-                                               int t = GetTile(Pos(i,j));\r
-                                               int item = GetItem(Pos(i,j));\r
-                                               if (t)\r
-                                                       HintMessage::FlagTile(t);\r
-                                               if (item)\r
-                                                       HintMessage::FlagTile(item+20);\r
-                                       }\r
-                               HintMessage::FlagTile(EMPTY);\r
-                       }\r
-               #endif\r
-               hintsDone = true;\r
-       }\r
-       void ResetLevel()\r
-       {\r
-               hintsDone = false;\r
-\r
-               UpdateCursor(Pos(-1,-1));\r
-\r
-               isMap = false;\r
-\r
-               player_score = 0;\r
-\r
-               numUndo = 0;\r
-               undoTime = -1;\r
-\r
-               dead = false;\r
-               win = false;\r
-               winFinal = false;\r
-               player_items[0] = player_items[1] = 0;\r
-//             time = 0;\r
-               if (strlen(currentSlot) == 0)\r
-               {\r
-                       new TitleMenu();\r
-                       new Fader(1, -3);\r
-               }\r
-               else\r
-               {\r
-                       if (!isFadeRendering && time!=0)\r
-                       {\r
-                               renderer().Add(new FadeRender(-1), time);\r
-                               time += 0.5;\r
-                       }\r
-               }\r
-\r
-               // Reset renderer\r
-               renderer.Reset(time);\r
-               renderer.Wipe();\r
-\r
-               for (int t=0; t<NumTileTypes; t++)\r
-                       tileCount[t] = 0;\r
-\r
-               for (int i=0; i<MAP_SIZE; i++)\r
-                       for (int j=0; j<MAP_SIZE; j++)\r
-                       {\r
-                               Pos p(i,j);\r
-                               int item = GetItem(p);\r
-                               //if (item)\r
-                                       renderer(p,true).Add(new ItemRender(item, GetTile(p)==EMPTY, p), time);\r
-                       }\r
-\r
-               InitSpecials();\r
-\r
-               for (int i=0; i<MAP_SIZE; i++)\r
-                       for (int j=0; j<MAP_SIZE; j++)\r
-                       {\r
-                               Pos p(i,j);\r
-                               int t = GetTile(p);\r
-                               tileCount[t]++;\r
-\r
-                               if (isMap)\r
-                                       t = EMPTY;\r
-                               \r
-                               //if (t)\r
-                                       renderer(p).Add(new TileRender(t, p), time);\r
-                       }\r
-\r
-               if (!isMap)\r
-                       renderer.player.Add(new PlayerRender(player, GetHeight(player), dead), time);\r
-               else\r
-                       renderer.player.Add(new PlayerRender(Pos(-100,-100), 0, true), time);\r
-                       \r
-\r
-               int bounds[4] = {player.getScreenX(),player.getScreenX(),player.getScreenY(),player.getScreenY()};\r
-               for (int i=0; i<MAP_SIZE; i++)\r
-                       for (int j=0; j<MAP_SIZE; j++)\r
-                       {\r
-                               Pos p(i,j);\r
-                               if (map[i][j] !=0 || map_item[i][j]!=0)\r
-                               {\r
-                                       int x1 = p.getScreenX();\r
-                                       int y1 = p.getScreenY();\r
-                                       int x2 = x1 + TILE_W3;\r
-                                       int y2 = y1 + TILE_H2;\r
-                                       y1 -= TILE_H2;  // Make sure objects/player will be properly visible\r
-\r
-                                       if (x1<bounds[0]) bounds[0] = x1;\r
-                                       if (x2>bounds[1]) bounds[1] = x2;\r
-                                       if (y1<bounds[2]) bounds[2] = y1;\r
-                                       if (y2>bounds[3]) bounds[3] = y2;\r
-                               }\r
-                       }\r
-\r
-               int sx, sy;\r
-               if (isMap)\r
-               {\r
-                       sx = bounds[0] - int(TILE_W2*6.35);\r
-                       sy = (bounds[3] + bounds[2] - SCREEN_H) / 2 - TILE_H2/2;\r
-               }\r
-               else\r
-               {\r
-                       sx = (bounds[1] + bounds[0] - SCREEN_W) / 2 - TILE_W3/2;\r
-                       sy = (bounds[3] + bounds[2] - SCREEN_H) / 2 - TILE_H2/2;\r
-               }\r
-               if (isMap) \r
-               {\r
-                       initScrollX = sx;\r
-                       initScrollY = sy;\r
-                       if (mapScrollX==0)\r
-                               mapScrollX = sx;\r
-                       else\r
-                               sx = mapScrollX;\r
-               }\r
-\r
-//             time = 1;       // Guarantee we can't try and do things at time=0\r
-\r
-               renderer().Add(new ScrollRender(sx, sy), time);\r
-               renderer().Add(new FadeRender(1), time);\r
-               if (time != 0)\r
-                       time -= 0.5;\r
-       }\r
-\r
-       char* ReadAll(FILE* f)\r
-       {\r
-               int size;\r
-    // FIXME: According to http://userpage.fu-berlin.de/~ram/pub/pub_jf47ht20Ht/c_faq_de\r
-    // undefined for binary streams! (POSIX does not differ between ascii and binary, so\r
-    // we are on the safe side in Linux)\r
-               fseek(f, 0, SEEK_END);\r
-               size = ftell(f);\r
-               fseek(f, 0, SEEK_SET);\r
-               char* c = loadPtr = new char [size];\r
-               endLoad = loadPtr + size;\r
-               fread(c, 1, size, f);\r
-               return c;\r
-       }\r
-\r
-       static char *loadPtr, *endLoad;\r
-       static unsigned int fread_replace(void* d, unsigned int size, unsigned int num, FILE*)\r
-       {\r
-               unsigned int remain = (endLoad - loadPtr) / size;\r
-               if (remain < num) num = remain;\r
-               memcpy(d, loadPtr, size*num);\r
-               loadPtr += size*num;\r
-               return num;\r
-       }\r
-\r
-       int GetPar(const char * level, bool getdiff=false)\r
-       {\r
-               if (strcmp(level, currentFile)==0)\r
-                       return getdiff ? levelDiff : levelPar;\r
-\r
-               #ifdef USE_LEVEL_PACKFILE\r
-                       PackFile1::Entry* e = levelFiles.Find(level);\r
-                       if (!e) return 999;\r
-                       loadPtr = (char*)e->Data();\r
-                       endLoad = loadPtr + e->DataLen();\r
-                       FILE* f = 0;\r
-               #else\r
-                       loadPtr = 0;\r
-                       FILE* f = file_open(level, "rb");\r
-               #endif\r
-\r
-               typedef unsigned int _fn(void*, unsigned int, unsigned int, FILE*);\r
-               _fn * fn = (loadPtr ? (_fn*)fread_replace : (_fn*)fread);\r
-\r
-               int32_t par = 99999, diff = 0;\r
-               int16_t version;\r
-               \r
-               if (!f && !loadPtr)\r
-                       return getdiff ? diff : par;\r
-\r
-               fn(&version, 2, 1, f); // skip to relevant point\r
-\r
-               if (fn(&par, sizeof(par), 1, f) != 1)\r
-                       par = 99999;\r
-               else\r
-                       par = SWAP32(par);\r
-               size_t ret = fn(&diff, sizeof(diff), 1, f);\r
-               diff = SWAP32(diff);\r
-               if (ret != 1 || diff<0 || diff>10)\r
-                       diff = 0;\r
-\r
-               #ifdef USE_LEVEL_PACKFILE\r
-                       loadPtr = endLoad = 0;\r
-               #else\r
-                       if (f)\r
-                               fclose(f);\r
-               #endif\r
-\r
-               return getdiff ? diff : par;\r
-       }\r
-\r
-       bool LoadSave(const char * filename, bool save)\r
-       {\r
-               if (!filename) \r
-                       return false;\r
-\r
-               if (!save)\r
-               {\r
-                       showScoring = false;\r
-                       LevelSave* l = progress.GetLevel(filename, true);\r
-                       if (progress.general.scoringOn && l && l->Completed() )\r
-                               showScoring = true;\r
-               }\r
-\r
-               #ifdef USE_LEVEL_PACKFILE\r
-                       if (!save)\r
-                       {\r
-                               PackFile1::Entry* e = levelFiles.Find(filename);\r
-                               if (!e) return false;\r
-\r
-                               if (currentFile != filename) // equal (overlapping) strings are forbidden\r
-                                       strcpy(currentFile, filename);\r
-                               currentLevelInfo = GetLevelInfo(currentFile);\r
-                               \r
-                               loadPtr = (char*)e->Data();\r
-                               endLoad = loadPtr + e->DataLen();\r
-                               _LoadSave(NULL, save);\r
-                               loadPtr = endLoad = 0;\r
-\r
-                               return true;\r
-                       }\r
-               #else\r
-                       loadPtr = 0;\r
-                       FILE* f = file_open(filename, save ? "wb" : "rb");\r
-                       if (f)\r
-                       {\r
-                               strcpy(currentFile, filename);\r
-                               if (!save)\r
-                                       currentLevelInfo = GetLevelInfo(currentFile);\r
-\r
-                               if (!save)\r
-                               {\r
-                                       char* data = ReadAll(f);\r
-                                       _LoadSave(f, save);\r
-                                       delete [] data;\r
-                                       loadPtr = endLoad = 0;\r
-                               }\r
-                               else\r
-                               {\r
-                                       _LoadSave(f, save);\r
-                               }\r
-                               fclose(f);\r
-\r
-                               return true;\r
-                       }\r
-               #endif\r
-                       \r
-               return false;\r
-       }\r
-\r
-  /** \brief Writes/reads game status to/from a file\r
-   *\r
-   *  The game data file is written in little endian so it can be shared\r
-   *  across different machines.\r
-   */\r
-       void _LoadSave(FILE* f, bool save)\r
-       {\r
-               typedef unsigned int _fn(void*, unsigned int, unsigned int, FILE*);\r
-               _fn * fn = save ? (_fn*)fwrite : (loadPtr ? (_fn*)fread_replace : (_fn*)fread);\r
-\r
-               #define VERSION 4\r
-               int version = VERSION; // 1--9\r
-               if (save)\r
-                       fprintf(f, "%d\n", version);\r
-               else\r
-               {\r
-                       char c;\r
-                       if (fn(&c, 1, 1, f) != 1)\r
-                               return;\r
-                       version = c-'0';\r
-\r
-                       // Remove '\n' character\r
-                       fn(&c, 1, 1, f);\r
-               }\r
-\r
-               if (!save)\r
-               {\r
-                       for (int i=0; i<MAP_SIZE; i++)\r
-                               for (int j=0; j<MAP_SIZE; j++)\r
-                               {\r
-                                       delete [] special[i][j];\r
-                                       special[i][j] = 0;\r
-                               }\r
-               }\r
-\r
-               if (version==1)\r
-               {\r
-                       for (int i=0; i<MAP_SIZE; i++)\r
-                               for (int j=0; j<MAP_SIZE; j++) {\r
-                                       map[i][j] = SWAP32(map[i][j]);\r
-                                       fn(&map[i][j], sizeof(map[i][j]), 1, f);\r
-                                       map[i][j] = SWAP32(map[i][j]);\r
-                               }\r
-\r
-                       player.x = SWAP32(player.x);\r
-                       player.y = SWAP32(player.y);\r
-                       fn(&player, sizeof(player), 1, f);\r
-                       player.x = SWAP32(player.x);\r
-                       player.y = SWAP32(player.y);\r
-\r
-                       for (int i=0; i<MAP_SIZE; ++i)\r
-                               for (int j=0; j<MAP_SIZE; ++j)\r
-                                       map_item[i][j] = SWAP32(map_item[i][j]);\r
-                       if (fn(map_item, sizeof(map_item), 1, f) == 0)\r
-                               memset(map_item, 0, sizeof(map_item));\r
-                       for (int i=0; i<MAP_SIZE; ++i)\r
-                               for (int j=0; j<MAP_SIZE; ++j)\r
-                                       map_item[i][j] = SWAP32(map_item[i][j]);\r
-               }\r
-               else if (version>=2 && version<=4)\r
-               {\r
-                       unsigned char bounds[4];\r
-                       if (save)\r
-                       {\r
-                               bounds[0]=bounds[1]=player.x;\r
-                               bounds[2]=bounds[3]=player.y;\r
-                               for (int i=0; i<MAP_SIZE; i++)\r
-                                       for (int j=0; j<MAP_SIZE; j++)\r
-                                               if (map[i][j] !=0 || map_item[i][j]!=0 || special[i][j]!=0)\r
-                                               {\r
-                                                       if (i<bounds[0]) bounds[0] = i;\r
-                                                       if (i>bounds[1]) bounds[1] = i;\r
-                                                       if (j<bounds[2]) bounds[2] = j;\r
-                                                       if (j>bounds[3]) bounds[3] = j;\r
-                                               }\r
-                       }\r
-                       else\r
-                       {\r
-                               memset(map, 0, sizeof(map));\r
-                               memset(map_item, 0, sizeof(map_item));\r
-                       }\r
-\r
-                       if (version>=3) {\r
-                               levelPar = SWAP32(levelPar);\r
-                               fn(&levelPar, 1, sizeof(levelPar), f);\r
-                               levelPar = SWAP32(levelPar);\r
-                       }\r
-                       else if (!save)\r
-                               levelPar = 0;\r
-\r
-                       if (version>=4) {\r
-                               levelDiff = SWAP32(levelDiff);\r
-                               fn(&levelDiff, 1, sizeof(levelDiff), f);\r
-                               levelDiff = SWAP32(levelDiff);\r
-                       }\r
-                       else if (!save)\r
-                               levelDiff = 0;\r
-\r
-                       fn(bounds, sizeof(bounds), 1, f);\r
-                       player.x = SWAP32(player.x);\r
-                       player.y = SWAP32(player.y);\r
-                       fn(&player, sizeof(player), 1, f);\r
-                       player.x = SWAP32(player.x);\r
-                       player.y = SWAP32(player.y);\r
-\r
-                       int offsetx=0, offsety=0;\r
-\r
-                       if (!save && bounds[1]-bounds[0]<15) // Hacky - don't recenter map...\r
-                       {\r
-                               // Re-position map to top left (but leave a bit of space)\r
-                               // (This ensures the laser/boat effects don't clip prematurely against the edges of the screen)\r
-                               offsetx = SCREEN_W/2/TILE_W2 + 1 - (bounds[0]+bounds[1]/2);\r
-                               offsety = SCREEN_H/2/TILE_H2 + SCREEN_W/2/TILE_W2 - (bounds[2]+bounds[3]/2);\r
-                               offsetx = MAX(0, offsetx);\r
-                               offsety = MAX(0, offsety);\r
-//                             if (bounds[0] > 2)\r
-//                                     offsetx = 2 - bounds[0];\r
-//                             if (bounds[2] > 2)\r
-//                                     offsety = 2 - bounds[2];\r
-                       }\r
-                       bounds[0] += offsetx;\r
-                       bounds[1] += offsetx;\r
-                       bounds[2] += offsety;\r
-                       bounds[3] += offsety;\r
-                       player.x += offsetx;\r
-                       player.y += offsety;\r
-\r
-                       for (int i=bounds[0]; i<=bounds[1]; i++)\r
-                               for (int j=bounds[2]; j<=bounds[3]; j++)\r
-                               {\r
-                                       unsigned char comp = map[i][j] | (map_item[i][j]<<5);\r
-                                       fn(&comp, sizeof(comp), 1, f);\r
-                                       map[i][j] = comp & 0x1f;\r
-                                       map_item[i][j] = (comp >> 5) & 3;\r
-                               }\r
-\r
-                       if (save)\r
-                       {\r
-                               for (int i=bounds[0]; i<=bounds[1]; i++)\r
-                                       for (int j=bounds[2]; j<=bounds[3]; j++)\r
-                                               if (special[i][j])\r
-                                               {\r
-                                                       int16_t len = strlen(special[i][j]);\r
-                                                       unsigned char x=i, y=j;\r
-                                                       fn(&x, sizeof(x), 1, f);\r
-                                                       fn(&y, sizeof(y), 1, f);\r
-                                                       len = SWAP16(len);\r
-                                                       fn(&len, sizeof(len), 1, f);\r
-                                                       len = SWAP16(len);\r
-                                                       fn(special[i][j], 1, len, f);\r
-                                               }\r
-                       }\r
-                       else\r
-                       {\r
-                               while(1){\r
-                                       int16_t len;\r
-                                       unsigned char x, y;\r
-                                       if (!fn(&x, sizeof(x), 1, f))\r
-                                               break;\r
-                                       fn(&y, sizeof(y), 1, f);\r
-                                       x += offsetx; y += offsety;\r
-                                       fn(&len, sizeof(len), 1, f);\r
-                                       len = SWAP16(len);\r
-                                       if (len<0) break;\r
-                                       char* tmp = new char[len+1];\r
-                                       tmp[len] = 0;\r
-                                       fn(tmp, 1, len, f);\r
-\r
-                                       SetSpecial(Pos(x,y), tmp, true, false);\r
-                               }\r
-                       }\r
-               }\r
-               else\r
-                       return; // Unsupported version!\r
-\r
-               ResetLevel();\r
-\r
-               // Save when returning to map!\r
-               if (isMap)\r
-               {\r
-                       progress.general.completionPercentage = numComplete*100/numLevels;\r
-                       progress.general.masteredPercentage = numMastered*100/numLevels;                        \r
-                       LoadSaveProgress(true);\r
-               }\r
-       }\r
-\r
-       void SetTile(Pos const & p, Tile t, bool updateRenderer=true, bool undoBuffer=true)\r
-       {\r
-               if (p.x<0 || p.x>MAP_SIZE)\r
-                       return;\r
-               if (p.y<0 || p.y>MAP_SIZE)\r
-                       return;\r
-               if (map[p.x][p.y] == t)\r
-                       return;\r
-               if (map[p.x][p.y] == t)\r
-                       return;\r
-\r
-               tileCount[map[p.x][p.y]]--;\r
-               tileCount[t]++;\r
-\r
-               if (undoBuffer)\r
-                       undo[numUndo].Add(Undo::TileChange(p,GetTile(p),GetItem(p)));\r
-\r
-               map[p.x][p.y] = t;\r
-\r
-               if (updateRenderer)\r
-                       renderer(p).Add(new TileRender(t, p), time);\r
-       }\r
-\r
-       Tile GetTile(Pos const & p)\r
-       {\r
-               if (p.x<0 || p.x>=MAP_SIZE)\r
-                       return EMPTY;\r
-               if (p.y<0 || p.y>=MAP_SIZE)\r
-                       return EMPTY;\r
-               return map[p.x][p.y];\r
-       }\r
-\r
-       int GetHeight(Pos const & p)\r
-       {\r
-               return tileSolid[GetTile(p)]==1;\r
-       }\r
-\r
-       char* GetSpecial(Pos const & p)\r
-       {\r
-               if (p.x<0 || p.x>=MAP_SIZE)\r
-                       return NULL;\r
-               if (p.y<0 || p.y>=MAP_SIZE)\r
-                       return NULL;\r
-               return special[p.x][p.y];\r
-       }\r
-\r
-       void SetSpecial(Pos const & p, char * d, bool use_pointer=false, bool auto_activate=true)\r
-       {\r
-               if (p.x<0 || p.x>=MAP_SIZE || p.y<0 || p.y>=MAP_SIZE)\r
-               {\r
-                       if (use_pointer)\r
-                               delete [] d;\r
-                       return;\r
-               }\r
-\r
-               delete [] special[p.x][p.y];\r
-               if (!use_pointer && d)\r
-               {\r
-                       \r
-                       special[p.x][p.y] = new char [strlen(d) + 1];\r
-                       strcpy(special[p.x][p.y], d);\r
-               }\r
-               else\r
-                       special[p.x][p.y] = d;\r
-\r
-               if (special[p.x][p.y]==0)\r
-                       renderer(p,true).Add(new ItemRender(GetItem(p), GetTile(p)==EMPTY, p), time);\r
-               else if (auto_activate)\r
-                       ActivateSpecial(p, 0);\r
-       }\r
-\r
-       int GetLevelState(Pos const & p, int recurse=0)\r
-       {\r
-               char* x = GetSpecial(p);\r
-               if (!x) return 0;\r
-\r
-               LevelSave* l = progress.GetLevel(x, false);\r
-\r
-               int t = 1;\r
-\r
-               if (strcmp(x, STARTING_LEVEL)==0)\r
-                       t = 2;\r
-               if (x[0]=='_' && l && l->unlocked)\r
-                       t=3;\r
-\r
-               if (l && l->Completed())\r
-               {\r
-                       t = 3;\r
-                       \r
-                       if (recurse) \r
-                               return t;\r
-\r
-                       int par = GetPar(x);\r
-                       if (progress.general.scoringOn && l->PassesPar( par ))\r
-                               t = l->BeatsPar( par ) ? 40 : 4;\r
-               }\r
-               if (recurse) \r
-                       return t;\r
-\r
-               int adj=0;\r
-               for (Dir d=0; d<MAX_DIR; d++)\r
-               {\r
-                       int i = GetLevelState(p+d, 1);\r
-//                     if (i>1 || i==1 && t>1)\r
-                       if (i>=1 && t>2 || t>=1 && i>2)\r
-                       {\r
-                               adj |= 1<<d;\r
-                               if (t==1)\r
-                                       t = 2;\r
-                       }\r
-               }\r
-\r
-               return t | adj<<8;\r
-       }\r
-\r
-       void ActivateSpecial(Pos const & p, int type)\r
-       {\r
-               if (p.x<0 || p.x>=MAP_SIZE || p.y<0 || p.y>=MAP_SIZE)\r
-                       return;\r
-\r
-               char * x = special[p.x][p.y];\r
-\r
-               if (x==0 || x[0]==0)\r
-                       return;\r
-\r
-               if (type==2 && x[0]=='_') // Phase2 init - unlock\r
-               {\r
-                       int t = GetLevelState(p);\r
-                       int target = atoi(x+1), targetM = 0;\r
-                       if (target>1000) targetM=target=target-100;\r
-                       if (t > 1 && numComplete >= target && numMastered >= targetM)\r
-                       {\r
-                               LevelSave* l = progress.GetLevel(x, true);\r
-                               if (!l->unlocked)\r
-                               {\r
-                                       l->unlocked = true;\r
-\r
-                                       renderer(p, true).Add(new LevelSelectRender(p, 5, GetLevelState(p)>>8), time+0.01);\r
-                                       renderer().Add(new ExplosionRender(p, 0), time + 0.6);\r
-                                       renderer().Add(new ExplosionRender(p, 1), time + 1.1);\r
-                                       renderer(p, true).Add(new LevelSelectRender(p, -1, GetLevelState(p)>>8), time + 1.1);\r
-                               }\r
-                       }\r
-               }\r
-               \r
-               if (type==0) // Init & count levels\r
-               {\r
-                       if (x[0]=='_')\r
-                       {\r
-                               int t = GetLevelState(p);\r
-                               int unlock = progress.GetLevel(x, true)->unlocked;\r
-                               LevelSelectRender* lsr = new LevelSelectRender( p, unlock ? -1 : (t>>8) ? 5 : 1, t>>8 );\r
-                               if ((t>>8) && p.x > mapRightBound) mapRightBound = p.x;\r
-                               #ifdef MAP_EDIT_HACKS\r
-                                       lsr->magic = -atoi(x+1);\r
-                                       SetTile(p, LIFT_DOWN, true, false);\r
-                               #else\r
-                                       SetTile(p, EMPTY, true, false);\r
-                               #endif\r
-                               renderer(p,true).Add(lsr, time);\r
-                       }\r
-                       else\r
-                       {\r
-                               //printf("Level: %s\n", x);\r
-\r
-                               int t = GetLevelState(p);\r
-                               numLevels++;\r
-                               if (t && !GetItem(p))\r
-                               {\r
-                                       if (!isMap)\r
-                                       {\r
-                                               isMap = true;\r
-                                               mapRightBound = 0;\r
-                                       }\r
-                                       currentLevelInfo = 0;\r
-\r
-                                       if ((t&0xff)>=2)\r
-                                       {\r
-                                               LevelSave* l = progress.GetLevel(x, true);\r
-                                               if (!l->unlocked)\r
-                                               {\r
-                                                       l->unlocked = true;\r
-\r
-                                                       renderer(p, true).Add(new LevelSelectRender(p, -1, 0), time+0.01);\r
-                                                       renderer().Add(new ExplosionRender(p, 0), time + 0.6);\r
-                                                       renderer(p, true).Add(new LevelSelectRender(p, t & 0xff, t>>8), time + 0.6);\r
-                                               }\r
-\r
-                                               numLevelsFound++;\r
-                                               if (p.x > mapRightBound) mapRightBound = p.x;\r
-                                       }\r
-                                       if ((t&0xff)>=3)\r
-                                               numComplete++;\r
-                                       if ((t&0xff)>=4)\r
-                                               numMastered++;\r
-\r
-                                       LevelSelectRender* lsr = new LevelSelectRender( p, t & 0xff, t>>8 );\r
-\r
-                                       #ifdef MAP_EDIT_HACKS\r
-                                               lsr->magic = 0;\r
-                                               int t = GetAutoTile(x, true);\r
-                                               int v = GetAutoTile(x, false);\r
-                                               if (MAP_EDIT_HACKS_DISPLAY_UNLOCK)\r
-                                                       lsr->magic = v;\r
-                                               else\r
-                                                       lsr->magic = GetPar(x, true);\r
-                                               t = 1;\r
-                                               SetTile(p, t, true, false);\r
-                                       #else\r
-                                               SetTile(p, EMPTY, true, false);\r
-                                       #endif\r
-\r
-                                       renderer(p,true).Add(lsr, time);\r
-                               }\r
-                       }\r
-               }\r
-               \r
-               if (type==1 && x[0]!='_') // Clicked on\r
-               {\r
-                       int t = GetLevelState(p);\r
-                       if (t>1)\r
-                       {\r
-                               LoadSave(x, false);\r
-                       }\r
-               }\r
-       }\r
-\r
-       void SetItem(Pos const & p, int t, bool updateRenderer=true, bool undoBuffer=true)\r
-       {\r
-               if (p.x<0 || p.x>MAP_SIZE)\r
-                       return;\r
-               if (p.y<0 || p.y>MAP_SIZE)\r
-                       return;\r
-               if (map_item[p.x][p.y] == t)\r
-                       return;\r
-\r
-               if (undoBuffer)\r
-                       undo[numUndo].Add(Undo::TileChange(p,GetTile(p),GetItem(p)));\r
-\r
-               map_item[p.x][p.y] = t;\r
-\r
-               if (updateRenderer)\r
-                       renderer(p,true).Add(new ItemRender(t, GetTile(p)==EMPTY, p), time);\r
-       }\r
-\r
-       Tile GetItem(Pos const & p)\r
-       {\r
-               if (p.x<0 || p.x>=MAP_SIZE)\r
-                       return EMPTY;\r
-               if (p.y<0 || p.y>=MAP_SIZE)\r
-                       return EMPTY;\r
-               return map_item[p.x][p.y];\r
-       }\r
-\r
-       void LoadSaveProgress(bool save)\r
-       {\r
-               FILE* f = file_open(currentSlot, save ? "wb" : "rb");\r
-               if (f)\r
-               {\r
-                       progress.LoadSave(f, save);\r
-                       fclose(f);\r
-               }\r
-               else\r
-               {\r
-                       if (!save)\r
-                               progress.Clear();\r
-               }\r
-       }\r
-       void LoadProgress()\r
-       {\r
-               LoadSaveProgress(false);\r
-       }\r
-       void SaveProgress()\r
-       {\r
-               LoadSaveProgress(true);\r
-       }\r
-\r
-       SDL_Surface* Load(const char * bmp, bool colourKey=true)\r
-       {\r
-               typedef unsigned int uint32;\r
-               uint32* tmp = 0;\r
-\r
-               SDL_Surface * g = 0;\r
-\r
-#ifdef EDIT\r
-               if (strstr(bmp, ".bmp"))\r
-               {\r
-                       g = SDL_LoadBMP(bmp);\r
-\r
-                       char out[1024];\r
-                       strcpy(out, bmp);\r
-                       strcpy(strstr(out, ".bmp"), ".dat");\r
-\r
-//                     SDL_PixelFormat p;\r
-//                     p.sf = 1;\r
-//                     SDL_Surface* tmp = SDL_ConvertSurface(g, &p, SDL_SWSURFACE);\r
-\r
-                       short w=g->w, h=g->h;\r
-                       char* buf = (char*) g->pixels;\r
-                       if (colourKey)\r
-                       {\r
-                               while (IsEmpty(g, w-1, 0, 1, h) && w>1)\r
-                                       w--;\r
-                               while (IsEmpty(g, 0, h-1, w, 1) && h>1)\r
-                                       h--;\r
-                       }\r
-\r
-                       FILE* f = file_open(out, "wb");\r
-                       fwrite(&w, sizeof(w), 1, f);\r
-                       fwrite(&h, sizeof(h), 1, f);\r
-\r
-                       uint32 mask = IMAGE_DAT_OR_MASK;\r
-                       for (int i=0; i<(int)w*h; )\r
-                       {\r
-                               uint32 c = (*(uint32*)&buf[(i%w)*3 + (i/w)*g->pitch] | mask);\r
-                               int i0 = i;\r
-                               while (i < (int)w*h && c == (*(uint32*)&buf[(i%w)*3 + (i/w)*g->pitch] | mask))\r
-                                       i++;\r
-                               c &= 0xffffff;\r
-                               i0 = i-i0-1;\r
-                               if (i0 < 0xff)\r
-                                       c |= i0 << 24;\r
-                               else\r
-                                       c |= 0xff000000;\r
-                               \r
-                               fwrite(&c, sizeof(c), 1, f);\r
-                               \r
-                               if (i0 >= 0xff)\r
-                                       fwrite(&i0, sizeof(i0), 1, f);\r
-                       }\r
-                       fclose(f);\r
-\r
-                       SDL_FreeSurface(g);\r
-\r
-                       bmp = out;\r
-               }\r
-#endif                 \r
-\r
-               FILE* f = file_open(bmp, "rb");\r
-               if (!f) FATAL("Unable to open file", bmp);\r
-\r
-               int16_t w,h;\r
-               fread(&w, sizeof(w), 1, f);\r
-               fread(&h, sizeof(h), 1, f);\r
-               w = SWAP16(w);\r
-               h = SWAP16(h);\r
-               if (w>1500 || h>1500 || w<=0 || h<=0) FATAL("Invalid file", bmp);\r
-\r
-               tmp = new uint32[(int)w*h];\r
-               \r
-               uint32 c = 0;\r
-               uint32 cnt = 0;\r
-               for (int p=0; p<(int)w*h; p++)\r
-               {\r
-                       if (cnt)\r
-                               cnt -= 0x1;\r
-                       else\r
-                       {\r
-                               fread(&c, sizeof(c), 1, f);\r
-                               c = SWAP32(c);\r
-                               cnt = c >> 24;\r
-                               if (cnt==255) {\r
-                                       fread(&cnt, sizeof(cnt), 1, f);\r
-                                       cnt = SWAP32(cnt);\r
-                               }\r
-                       }\r
-                       tmp[p] = c | 0xff000000;\r
-               }\r
-\r
-               g = SDL_CreateRGBSurfaceFrom(tmp, w, h, 32, w*4, \r
-                       0xff0000,\r
-                       0xff00,\r
-                       0xff,\r
-                       0xff000000 );\r
-\r
-               fclose(f);\r
-\r
-\r
-               if (!g) FATAL("Unable to create SDL surface");\r
-               if (colourKey)\r
-                       SDL_SetColorKey(g, SDL_SRCCOLORKEY, SDL_MapRGB(g->format, WATER_COLOUR));\r
-               SDL_Surface * out = SDL_DisplayFormat(g);\r
-               SDL_FreeSurface(g);\r
-               delete [] tmp;\r
-               if (!out) FATAL("Unable to create SDL surface (SDL_DisplayFormat)");\r
-               return out;\r
-       }\r
-\r
-       #ifdef USE_LEVEL_PACKFILE\r
-               PackFile1 levelFiles;\r
-       #endif\r
-       HexPuzzle()\r
-       {\r
-               SDL_WM_SetCaption(GAMENAME, 0);\r
-\r
-               time = 0;\r
-\r
-               #ifdef USE_LEVEL_PACKFILE\r
-                       FILE* f = file_open("levels.dat", "rb");\r
-                       if (!f)\r
-                               FATAL("Unable to open file", "levels.dat");\r
-                       levelFiles.Read(f);\r
-                       fclose(f);\r
-               #endif\r
-\r
-               LoadGraphics();\r
-\r
-               isMap = false;\r
-               editMode = false;\r
-\r
-               currentLevelInfo = 0;\r
-\r
-               editTile = 0;\r
-               levelPar = 0;\r
-               levelDiff = 5;\r
-               turboAnim = 0;\r
-\r
-               memset(map, 0, sizeof(map));\r
-               memset(map_item, 0, sizeof(map_item));\r
-               memset(special, 0, sizeof(special));\r
-               \r
-               LoadProgress();\r
-\r
-//             player = Pos(1,11);\r
-\r
-//             ResetLevel();\r
-\r
-               LoadMap();\r
-       }\r
-\r
-       void LoadMap()\r
-       {\r
-               #ifndef EDIT\r
-                       progress.GetLevel(STARTING_LEVEL, true)->unlocked = 1;\r
-                       if (!progress.GetLevel(STARTING_LEVEL, true)->Completed())\r
-                       {\r
-                               LoadSave(STARTING_LEVEL, false);\r
-                               return;\r
-                       }\r
-               #endif\r
-               \r
-               //editMode = false;\r
-               LoadSave(mapname, false);\r
-       }\r
-\r
-       void Render()\r
-       {\r
-               if (!activeMenu || activeMenu->renderBG)\r
-               {\r
-                       SDL_Rect src  = {0,0,screen->w,screen->h};\r
-                       SDL_Rect dst  = {0,0,screen->w,screen->h};\r
-                       if (isRenderMap)\r
-                       {\r
-                               int boundW = mapBG->w;\r
-       #ifndef EDIT\r
-                               boundW = MIN(boundW, (mapRightBound+4) * TILE_W2 - TILE_W1);\r
-       #endif\r
-                               src.x = scrollX - initScrollX;\r
-                               if (src.x+src.w > boundW)\r
-                               {\r
-                                       int diff = src.x+src.w - boundW;\r
-                                       src.x -= diff;\r
-                                       if (isMap)\r
-                                               scrollX -= diff;\r
-                               }\r
-                               if (src.x < 0)\r
-                               {\r
-                                       if (isMap)\r
-                                               scrollX -= src.x;\r
-                                       src.x = 0;\r
-                               }\r
-                               //scrollY = initScrollY;\r
-\r
-                               if (isMap)\r
-                                       mapScrollX = scrollX;\r
-\r
-                               SDL_BlitSurface(mapBG, &src, screen, &dst);\r
-                       }\r
-                       else\r
-                               SDL_BlitSurface(gradient, &src, screen, &dst);\r
-\r
-                       renderer.Render(time, true);\r
-\r
-                       if (!hintsDone && !isFadeRendering)\r
-                       {\r
-                               DoHints();\r
-                       }\r
-\r
-                       if (1)\r
-                       {\r
-                               SDL_Rect src = {0,SCREEN_H-1,SCREEN_W,1};\r
-                               SDL_Rect dst = {0,SCREEN_H-1,SCREEN_W,1};\r
-                               for (int i=0; i<SCREEN_H; i++)\r
-                               {\r
-                                       dst.x = src.x = 0;\r
-                                       dst.y = src.y = SCREEN_H-1-i;\r
-                                       src.w = SCREEN_W;\r
-                                       src.h = 1;\r
-\r
-                                       if (isRenderMap)\r
-                                       {\r
-                                               src.x += (int)( sin(i*0.9 + time*3.7) * sin(i*0.3 + time*0.7)*4 );\r
-                                               src.y += (int)( (sin(i*0.3 - time*2.2) * sin(i*0.48 + time*0.47) - 1) * 1.99 );\r
-                                       }\r
-                                       else\r
-                                       {\r
-                                               src.x += (int)( sin(i*0.5 + time*6.2) * sin(i*0.3 + time*1.05) * 5 );\r
-                                               src.y += (int)( (sin(i*0.4 - time*4.3) * sin(i*0.08 + time*1.9) - 1) * 2.5 );\r
-                                       }\r
-                                       SDL_BlitSurface(screen, &src, screen, &dst);\r
-                               }\r
-                       }\r
-\r
-                       if(isRenderMap)\r
-                               SDL_BlitSurface(mapBG2, &src, screen, &dst);\r
-\r
-                       renderer.Render(time, false);\r
-\r
-                       if (!isRenderMap && !isMap && !isFadeRendering)\r
-                       {\r
-                               int v[3] = {player_items[0], player_items[1], player_score};\r
-                               if (numUndo > 1 && time < undo[numUndo-2].endTime)\r
-                               {\r
-                                       int i = numUndo-1;\r
-                                       while (i>1 && time<undo[i-1].time)\r
-                                               i--;\r
-                                       v[0] = undo[i].numItems[0];\r
-                                       v[1] = undo[i].numItems[1];\r
-                                       v[2] = undo[i].score;\r
-                               }\r
-                               if (numUndo>1 && time < undo[0].time)\r
-                                       v[0]=v[1]=v[2]=0;\r
-       #ifdef EDIT\r
-        /* TRANSLATORS: Anti-Ice are pickups, which turn ice plates into solid\r
-           plates once you step on them. Each pickup changes one ice plate */\r
-                               Print(0,0,_("Anti-Ice: %d"), v[0]);\r
-                               Print(0,FONT_SPACING,_("Jumps: %d"), v[1]);\r
-                               Print(0,FONT_SPACING*2,_("Score: %d (%d)"), v[2], player_score);\r
-        /* TRANSLATORS: Par is similar to golf, a pre defined score which you\r
-           can attempt to beat */\r
-                               Print(0,FONT_SPACING*3,_("Par:   %d"), levelPar);\r
-                               Print(0,FONT_SPACING*4,_("Diff:  %d"), levelDiff);\r
-       #else\r
-                               if (showScoring)\r
-                                       Print(0, SCREEN_H-FONT_SPACING, _(" Par: %d   Current: %d"), levelPar, v[2]);\r
-\r
-                               if (v[0])\r
-                                       Print(0,0,_(" Anti-Ice: %d"), v[0]);\r
-                               else if (v[1])\r
-                                       Print(0,0,_(" Jumps: %d"), v[1]);\r
-       #endif\r
-                       }\r
-                       if (isRenderMap && isMap && !isFadeRendering)\r
-                       {\r
-       #if 0//def EDIT\r
-                               Print(0,0,_("Points: %d"), numComplete+numMastered);\r
-                               Print(0,FONT_SPACING,_("Discovered: %d%% (%d/%d)"), numLevelsFound*100/numLevels, numLevelsFound, numLevels);\r
-                               Print(0,FONT_SPACING*2,_("Complete: %d%% (%d)"), numComplete*100/numLevels, numComplete);\r
-                               Print(0,FONT_SPACING*3,_("Mastered: %d%% (%d)"), numMastered*100/numLevels, numMastered);\r
-       #else\r
-                               if (numComplete==numLevels && progress.general.endSequence>0)\r
-                                       Print(0, SCREEN_H-FONT_SPACING, _(" %d%% Mastered"), numMastered*100/numLevels);\r
-                               else\r
-                                       Print(0, SCREEN_H-FONT_SPACING, _(" %d%% Complete"), numComplete*100/numLevels);\r
-\r
-                               if (numMastered >= numLevels && progress.general.endSequence < 2)\r
-                               {\r
-                                       progress.general.endSequence = 2;\r
-                                       LoadSaveProgress(true);\r
-\r
-                                       new Fader(-1, -7, 0.3);\r
-                               }\r
-                               if (numComplete >= numLevels && progress.general.endSequence < 1)\r
-                               {\r
-                                       progress.general.endSequence = 1;\r
-                                       LoadSaveProgress(true);\r
-                                       \r
-                                       new Fader(-1, -5, 0.3);\r
-                               }\r
-       #endif\r
-                       }\r
-                       if ((currentLevelInfo || noMouse) && isMap && isRenderMap && !activeMenu && isFadeRendering<=0)\r
-                       {\r
-                               Pos p;\r
-                               if (noMouse)\r
-                                       p = keyboardp;\r
-                               else\r
-                                       p = mousep;\r
-                               int pad = SCREEN_W/80;\r
-       //                      SDL_Rect src = {0, 0, uiGraphics->w, uiGraphics->h};\r
-                               SDL_Rect dst = {pad, SCREEN_H-TILE_H2-pad, 0, 0};\r
-               //              dst.x = p.getScreenX() + TILE_W3/2 - scrollX;\r
-               //              dst.y = p.getScreenY() - src.h/2 - scrollY;\r
-                               dst.x = p.getScreenX() - scrollX;\r
-                               dst.y = p.getScreenY() - scrollY - FONT_SPACING*3 - FONT_SPACING/2;\r
-               //              if (dst.x > SCREEN_W*2/3) dst.x -= TILE_W3 + src.w;\r
-               //              if (dst.y+src.h > screen->h-pad) dst.y = screen->h-pad - src.h;\r
-\r
-                               RenderTile(false, 0, p.getScreenX(), p.getScreenY());\r
-                       //      SDL_BlitSurface(uiGraphics, &src, screen, &dst);\r
-\r
-               //              dst.x += src.w/2;\r
-\r
-                               if (currentLevelInfo)\r
-                               {\r
-                                       keyboardp = p;\r
-\r
-                                       PrintC(true, dst.x, dst.y - FONT_SPACING/4, currentLevelInfo->name);\r
-\r
-                                       if (currentLevelInfo->file[0]!=0)\r
-                                       {\r
-                                               if (player_score > 0)\r
-                                               {\r
-                                                       if (progress.general.scoringOn)\r
-                                                       {\r
-                                                               PrintC(false, dst.x, dst.y + FONT_SPACING*4 - FONT_SPACING/4, _("Best:% 3d"), player_score);\r
-                                                               PrintC(false, dst.x, dst.y + FONT_SPACING*5 - FONT_SPACING/4, _("Par:% 3d"), levelPar);\r
-                                                       }\r
-                                                       else\r
-                                                               PrintC(false, dst.x, dst.y + FONT_SPACING*4 - FONT_SPACING/4, _("Completed"), player_score);\r
-                                               }\r
-                                               else\r
-                                                       PrintC(false, dst.x, dst.y + FONT_SPACING*4 - FONT_SPACING/4, _("Incomplete"), player_score);\r
-                                       }\r
-                               }\r
-                       }\r
-\r
-                       // "Win"\r
-                       if (win && numUndo > 0 && time > undo[numUndo-1].endTime + 2)\r
-                       {\r
-                               if (currentFile[0] && winFinal==0)\r
-                               {\r
-                                       LevelSave* l = progress.GetLevel(currentFile, true);\r
-\r
-                                       new WinLoseScreen(true, player_score, showScoring ? levelPar : 0, l && showScoring && l->Completed() ? l->GetScore() : 0);\r
-\r
-                                       if (l->IsNewCompletionBetter(player_score))\r
-                                       {\r
-                                               l->SetScore(player_score);\r
-\r
-                                               l->SetSolution(numUndo);\r
-\r
-                                               for (int i=0; i<numUndo; i++)\r
-                                                       l->SetSolutionStep(i, undo[i].playerMovement);\r
-                                       }\r
-\r
-                                       SaveProgress();\r
-                               }\r
-\r
-                               winFinal = 1;\r
-                       }\r
-                       else\r
-                               winFinal = 0;           \r
-\r
-                       // Move up "level complete" writing so it doesn't feel like everything's ground to a halt...\r
-                       if (win && numUndo > 0 && time > undo[numUndo-1].endTime && !winFinal)\r
-                       {\r
-                               double t = (time - undo[numUndo-1].endTime) / 2;\r
-                               t=1-t;\r
-                               t*=t*t;\r
-                               t=1-t;\r
-                               int y = SCREEN_H/3 - FONT_SPACING + 1;\r
-                               y = SCREEN_H + int((y-SCREEN_H)*t);\r
-                               PrintC(true, SCREEN_W/2, y, _("Level Complete!"));\r
-                       }\r
-               }\r
-\r
-               if (activeMenu)\r
-                       activeMenu->Render();\r
-\r
-               if (!noMouse)\r
-               {\r
-                       // Edit cursor\r
-                       if (editMode)\r
-                       {\r
-                               RenderTile(false, editTile, mousex+scrollX, mousey+scrollY);\r
-                       }\r
-               }\r
-       }\r
-\r
-       int Count(Tile t)\r
-       {\r
-               return tileCount[t];\r
-       }\r
-       int Swap(Tile t, Tile t2)\r
-       {\r
-               const int num = Count(t) + Count(t2);\r
-               if (t==t2 || num==0)\r
-                       return Count(t);        // Nothing to do...\r
-               \r
-               int count=0;\r
-               for (int x=0; x<MAP_SIZE; x++)\r
-                       for (int y=0; y<MAP_SIZE; y++)\r
-                       {\r
-                               if (GetTile(Pos(x,y))==t)\r
-                               {\r
-                                       count++;\r
-                                       SetTile(Pos(x,y), t2);\r
-                               }\r
-                               else if (GetTile(Pos(x,y))==t2)\r
-                               {\r
-                                       count++;\r
-                                       SetTile(Pos(x,y), t);\r
-                               }\r
-                               if (count==num)\r
-                                       return count;\r
-                       }\r
-               return count;\r
-       }\r
-       int Replace(Tile t, Tile t2)\r
-       {\r
-               const int num = Count(t);\r
-               if (t==t2 || num==0)\r
-                       return num;     // Nothing to do...\r
-\r
-               int count=0;\r
-               for (int x=0; x<MAP_SIZE; x++)\r
-                       for (int y=0; y<MAP_SIZE; y++)\r
-                       {\r
-                               Pos p(x,y);\r
-                               if (GetTile(p)==t)\r
-                               {\r
-                                       count++;\r
-\r
-                                       SetTile(p, t2, false);\r
-\r
-                                       if (t==COLLAPSE_DOOR && t2==COLLAPSABLE)\r
-                                               renderer(p).Add(new BuildRender(p, -1, 1, 1), time + (rand() & 255)*0.001);\r
-                                       else if (t==COLLAPSE_DOOR2 && t2==COLLAPSABLE2)\r
-                                               renderer(p).Add(new BuildRender(p, -1, 1, 1, 1), time + (rand() & 255)*0.001);\r
-                                       else\r
-                                               SetTile(p, t2);\r
-\r
-                                       if (count==num)\r
-                                               return count;\r
-                               }\r
-                       }\r
-               return count;\r
-       }\r
-\r
-       Tile editTile;\r
-       bool editMode;\r
-       void ResetUndo()\r
-       {\r
-               UndoDone();\r
-               undoTime = -1;\r
-               numUndo = 0;\r
-               win = false;\r
-       }\r
-\r
-       void UpdateCursor(Pos const & s)\r
-       {\r
-               static Pos _s;\r
-               if (s.x!=_s.x || s.y!=_s.y)\r
-               {\r
-                       _s = s;\r
-               \r
-                       char* sp = GetSpecial(s);\r
-                       char tmp[1000];\r
-                       tmp[0]='\0';\r
-                       if (sp)\r
-                       {\r
-                               if (isMap)\r
-                               {\r
-                                       currentLevelInfo = 0;\r
-                                       levelPar = player_score = -1;\r
-                                       if (GetLevelState(s)>=2)\r
-                                       {\r
-                                               LevelSave* l = progress.GetLevel(sp, true);\r
-                                               if (l)\r
-                                               {\r
-                                                       currentLevelInfo = GetLevelInfo(sp);\r
-                                                       levelPar = GetPar(sp);\r
-                                                       player_score = l->GetScore();\r
-                                               }\r
-                                       }\r
-                               }\r
-\r
-#ifdef EDIT\r
-                               sprintf(tmp, _("Special(%d,%d): %s (%d)"), s.x, s.y, sp ? sp : _("<None>"), GetPar(sp));\r
-                               SDL_WM_SetCaption(tmp, NULL);\r
-#endif\r
-                       }\r
-                       else if (currentFile[0])\r
-                       {\r
-#ifdef EDIT\r
-                               SDL_WM_SetCaption(currentFile, NULL);\r
-#endif\r
-                               if (isMap)\r
-                                       currentLevelInfo = 0;\r
-                       }\r
-               }\r
-       }\r
-\r
-       virtual void Mouse(int x, int y, int dx, int dy, int button_pressed, int button_released, int button_held) \r
-       {\r
-               if (activeMenu)\r
-               {\r
-                       activeMenu->Mouse(x,y,dx,dy,button_pressed,button_released,button_held);\r
-                       return; \r
-               }\r
-\r
-               if (isFadeRendering)\r
-                       return;\r
-\r
-\r
-#ifndef EDIT\r
-               if (button_pressed==2 || button_pressed==4 && isMap)\r
-               {\r
-                       KeyPressed(SDLK_ESCAPE, 0);\r
-                       keyState[SDLK_ESCAPE] = 0;\r
-                       return;\r
-               }\r
-#endif\r
-\r
-               x += scrollX;\r
-               y += scrollY;\r
-\r
-               Pos s = Pos::GetFromWorld(x,y);\r
-               if (tileSolid[GetTile(Pos::GetFromWorld(x,y+TILE_HUP))] == 1)\r
-                       s = Pos::GetFromWorld(x,y+TILE_HUP);\r
-\r
-               mousep = s;\r
-\r
-               UpdateCursor(s);\r
-\r
-#ifdef EDIT\r
-               if (button_held & ~button_pressed & 4)\r
-               {\r
-                       scrollX -= dx;\r
-                       scrollY -= dy;\r
-               }\r
-#endif\r
-\r
-               if (!editMode)\r
-               {\r
-                       if (isMap && (button_pressed & 1))\r
-                       {\r
-                               ActivateSpecial(s, 1);\r
-                               return;\r
-                       }\r
-                       if (!isMap && win && winFinal)\r
-                       {\r
-                               if (button_pressed & 1)\r
-                               {\r
-                                       LoadMap();\r
-                                       return;\r
-                               }\r
-                       }\r
-                       if(!isMap)\r
-                       {\r
-                               if((button_pressed & 1) || (button_held & 1) && (numUndo==0 || time>=undo[numUndo-1].endTime))\r
-                               {\r
-                                       if(s.x==player.x && s.y==player.y)\r
-                                       {\r
-                                               // Don't activate jump powerup without a new click\r
-                                               if (button_pressed & 1)\r
-                                                       Input(-1);\r
-                                       }\r
-                                       else if(s.x==player.x && s.y<player.y)\r
-                                               Input(0);\r
-                                       else if(s.x==player.x && s.y>player.y)\r
-                                               Input(3);\r
-                                       else if(s.y==player.y && s.x<player.x)\r
-                                               Input(5);\r
-                                       else if(s.y==player.y && s.x>player.x)\r
-                                               Input(2);\r
-                                       else if(s.y+s.x==player.y+player.x && s.x>player.x)\r
-                                               Input(1);\r
-                                       else if(s.y+s.x==player.y+player.x && s.x<player.x)\r
-                                               Input(4);\r
-                               }\r
-                               if ((button_pressed & 4) || (button_held & 4) && (undoTime < 0))\r
-                                       Undo();\r
-                       }\r
-                       return;\r
-               }\r
-\r
-#ifdef EDIT\r
-               if (!button_pressed && !button_held)\r
-                       return;\r
-\r
-               if (button_pressed==1)\r
-                       if (editTile<0)\r
-                               editTile = GetItem(s)==1 ? -3 : GetItem(s)==2 ? -2 : -1;\r
-\r
-               if (button_held==1 || button_pressed==1)\r
-               {\r
-                       ResetUndo();\r
-                       if (editTile>=0)\r
-                               SetTile(s, editTile, true, false);\r
-                       else\r
-                               SetItem(s, editTile==-2 ? 0 : editTile==-1 ? 1 : 2, true, false);\r
-               }\r
-\r
-               if (button_pressed==2)\r
-               {\r
-                       editTile = GetTile(s);\r
-               }\r
-\r
-               if (button_pressed==8)\r
-               {\r
-                       editTile=editTile-1;\r
-                       if (editTile<=0) editTile=NumTileTypes-1;\r
-               }\r
-\r
-               if (button_pressed==16)\r
-               {\r
-                       editTile=editTile+1;\r
-                       if (editTile<=0) editTile=1;\r
-                       if (editTile==NumTileTypes) editTile=0;\r
-               }\r
-\r
-               if (button_pressed==64)\r
-               {\r
-                       ResetUndo();\r
-                       player = s;\r
-                       dead = false;\r
-                       renderer.player.Reset(-1);\r
-                       renderer.player.Add(new PlayerRender(player, GetHeight(player), dead), 0);\r
-               }\r
-\r
-               if (button_pressed==256)\r
-               {\r
-                       char* fn = LoadSaveDialog(false, true, _("Select level"));\r
-                       if (fn)\r
-                       {\r
-                               char * l = strstr(fn, "Levels");\r
-                               if(l)\r
-                               {\r
-                                       FILE * f = file_open(l,"rb");\r
-                                       if (f) \r
-                                               fclose(f);\r
-                                       if (f)\r
-                                               SetSpecial(s, l);\r
-                                       else if (l[6]!=0 && l[7]=='_')\r
-                                               SetSpecial(s, l+7);\r
-                               }\r
-                               UpdateCursor(Pos(-1,-1));\r
-                       }\r
-               }\r
-               if (button_pressed==512)\r
-               {\r
-                       SetSpecial(s, NULL);\r
-                       UpdateCursor(Pos(-1,-1));\r
-               }\r
-               if (button_pressed==1024)\r
-               {\r
-                       static char x[1000] = "";\r
-                       if (!(s.x<0 || s.x>=MAP_SIZE || s.y<0 || s.y>=MAP_SIZE))\r
-                       {\r
-                               char tmp[1000];\r
-                               strcpy(tmp, x);\r
-                               if (GetSpecial(s))\r
-                                       strcpy(x, GetSpecial(s));\r
-                               else\r
-                                       x[0] = 0;\r
-                               SetSpecial(s, tmp[0] ? tmp : 0);\r
-                               if (!tmp[0])\r
-                                       SetTile(s, EMPTY, true, false);\r
-                       }\r
-               }\r
-\r
-               if (button_pressed==32)\r
-               {\r
-                       editTile = editTile<0 ? 1 : -1;\r
-               }\r
-#endif // EDIT\r
-       }\r
-\r
-       void CheckFinished()\r
-       {\r
-               bool slow = false;\r
-               if (Count(COLLAPSABLE)==0)\r
-               {\r
-                       if (Replace(COLLAPSE_DOOR, COLLAPSABLE) == 0)\r
-                               win = true;\r
-                       else\r
-                               slow = true;\r
-                       Replace(SWITCH, NORMAL);\r
-               }\r
-               else\r
-                       win = false;\r
-\r
-               if (Count(COLLAPSABLE2)==0)\r
-                       if (Replace(COLLAPSE_DOOR2, COLLAPSABLE2))\r
-                               slow = true;\r
-\r
-               if (slow) \r
-                       time += BUILD_TIME;\r
-       }\r
-       bool Collide(Pos p, bool high)\r
-       {\r
-               Tile t = GetTile(p);\r
-//             switch(t)\r
-//             {\r
-//             default:\r
-                       if (!high)\r
-                               return tileSolid[t]==1;\r
-                       else\r
-                               return false;\r
-//             }\r
-       }\r
-       void Undo()\r
-       {\r
-               if (numUndo==0) return;\r
-               \r
-               UndoDone(); // Complete previous undo...\r
-\r
-               numUndo--;\r
-\r
-               if (time > undo[numUndo].endTime)\r
-                       time = undo[numUndo].endTime;\r
-               undoTime = undo[numUndo].time;\r
-               \r
-               undo[numUndo].Restore(this);\r
-       }\r
-       void UndoDone()\r
-       {\r
-               if (undoTime < 0) \r
-                       return;\r
-               renderer.Reset(undoTime);\r
-               time = undoTime;\r
-               undoTime = -1;\r
-       }\r
-       void ScoreDestroy(Pos p)\r
-       {\r
-               Tile t = GetTile(p);\r
-               if (t==COLLAPSABLE || t==COLLAPSE_DOOR)\r
-               {}\r
-               else if (t != EMPTY)\r
-               {\r
-                       player_score += 10;\r
-               }\r
-       }\r
-\r
-       bool LaserTile(Pos p, int mask, double fireTime)\r
-       {\r
-               if (&renderer(p) == &renderer(Pos(-1,-1)))\r
-                       return false;\r
-               //if (!renderer.Visible(p))\r
-               //      return false;\r
-\r
-               TileRender* tr = 0;\r
-               if (time <= renderer(p).GetLastTime())\r
-                       if (fireTime < renderer(p).GetLastTime())\r
-                       {\r
-                               renderer(p).Add(tr = new TileRender(GetTile(p), p, mask), fireTime);\r
-                               ((TileRender*)renderer(p).GetStage(time+10/*HACKY!*/))->special |= mask;\r
-                       }\r
-                       else\r
-                       {\r
-                               tr = new TileRender(GetTile(p), p, mask | ((TileRender*)renderer(p).GetStage(time+10/*HACKY!*/))->special);\r
-                               renderer(p).Add(tr, fireTime);\r
-                       }\r
-               else\r
-                       renderer(p).Add(tr = new TileRender(GetTile(p), p, mask), fireTime);\r
-\r
-               if (tr)\r
-               {\r
-                       tr->specialDuration = time + LASER_LINE_TIME - fireTime + LASER_FADE_TIME;\r
-               }\r
-               return true;\r
-       }\r
-       void FireGun(Pos newpos, Dir d, bool recurse, double fireTime)\r
-       {\r
-               static Pos hits[100];\r
-               static Dir hitDir[100];\r
-               static unsigned int numHits=0;\r
-               if (!recurse)\r
-                       numHits = 0;\r
-\r
-               double starttime = fireTime;\r
-               for (Dir fd=((d<0)?0:d); fd<((d<0)?MAX_DIR:d+1); fd++)\r
-               {\r
-                       fireTime = starttime;\r
-               //      starttime += 0.03;\r
-\r
-                       Pos p = newpos + fd;\r
-                       int range = 0;\r
-                       for (; range<MAP_SIZE; range++, p=p+fd)\r
-                       {\r
-                               Tile t = GetTile(p);\r
-                               if (tileSolid[t]!=-1)\r
-                               {\r
-                                       if (t!=TRAP)\r
-                                               renderer(p).Add(new TileRender(tileSolid[t]==1 ? TILE_WHITE_WALL : TILE_WHITE_TILE, p), fireTime+0.1);\r
-\r
-                                       unsigned int i;\r
-                                       for (i=0; i<numHits; i++)\r
-                                               if (hits[i]==p)\r
-                                                       break;\r
-                                       if (i==numHits || \r
-                                               t==TRAP && (hitDir[i]&(1<<fd))==0\r
-                                          )\r
-                                       {\r
-                                               if (i==numHits)\r
-                                               {\r
-                                                       if (i >= sizeof(hits)/sizeof(hits[0]))\r
-                                                               return;\r
-                                                       hitDir[i] = 1 << fd;\r
-                                                       hits[i] = p;\r
-                                                       numHits++;\r
-                                               }\r
-                                               else\r
-                                               {\r
-                                                       hitDir[i] |= 1 << fd;\r
-                                               }\r
-                                               if (t==TRAP)\r
-                                               {\r
-                                                       int dirmask = \r
-                                                                 1<<((fd+2) % MAX_DIR)\r
-                                                               | 1<<((fd+MAX_DIR-2) % MAX_DIR);\r
-\r
-                                                       if (LaserTile(p, dirmask, fireTime))\r
-                                                               fireTime += (time+LASER_LINE_TIME - fireTime) / 40;\r
-//                                                     fireTime += LASER_SEGMENT_TIME;\r
-\r
-                                                       FireGun(p, (fd+1) % MAX_DIR, true, fireTime);\r
-                                                       FireGun(p, (fd+MAX_DIR-1) % MAX_DIR, true, fireTime);\r
-                                               }\r
-                                       }\r
-                                       break;\r
-                               }\r
-                               else\r
-                               {\r
-                                       LaserTile(p, 1<<(fd%3), fireTime);\r
-\r
-                                       fireTime += (time+LASER_LINE_TIME - fireTime) / 40;\r
-//                                     fireTime += LASER_SEGMENT_TIME;\r
-                               }\r
-                       }\r
-                       \r
-//                     renderer().Add(new LaserRender(newpos, fd, range), time);\r
-               }\r
-\r
-               if (!recurse)\r
-               {\r
-                       //double _time = time;\r
-                       time += LASER_LINE_TIME;\r
-                       for (unsigned int i=0; i<numHits; i++)\r
-                       {\r
-                               Pos p = hits[i];\r
-                               Tile t = GetTile(p);\r
-\r
-                               if (t==TRAP)\r
-                                       continue;\r
-\r
-                               ScoreDestroy(p);\r
-\r
-                               renderer(p).Add(new ExplosionRender(p, t==GUN), time);\r
-                               //renderer(p).Add(new TileRender(EMPTY, p), time+2);\r
-                               SetTile(p, EMPTY, false);\r
-\r
-                               if (GetItem(p))\r
-                                       renderer(p,true).Add(new ItemRender(GetItem(p), 1, p), time);\r
-\r
-                               if (t==GUN)\r
-                               {\r
-                                       for (Dir j=0; j<MAX_DIR; j++)\r
-                                       {\r
-                                               if (GetTile(p+j)!=EMPTY)\r
-                                               {\r
-                                                       renderer(p+j).Add(new TileRender(tileSolid[GetTile(p+j)]==1 ? TILE_WHITE_WALL : TILE_WHITE_TILE, p+j), time+0.05);\r
-                                                       renderer(p+j).Add(new ExplosionRender(p+j), time+0.2);\r
-\r
-                                                       if (GetItem(p+j))\r
-                                                               renderer(p+j,true).Add(new ItemRender(GetItem(p+j), 1, p), time);\r
-\r
-                                                       //renderer(p+j).Add(new TileRender(EMPTY, p+j), time+2.2);\r
-                                               }\r
-                                               ScoreDestroy(p + j);\r
-                                               SetTile(p + j, EMPTY, false);\r
-                                       }\r
-                               }\r
-                       }\r
-\r
-                       time += MAX(LASER_FADE_TIME, 0.15);\r
-                       //time = _time;\r
-                       CheckFinished();\r
-               }\r
-       }\r
-       int GetLastPlayerRot()\r
-       {\r
-               RenderStage* rs = renderer.player.GetStage(-1);\r
-               if (!rs) return 3;\r
-               return ((PlayerRender*)rs)->r;\r
-       }\r
-       bool Input(Dir d)\r
-       {\r
-               if (dead || win || isMap)\r
-                       return false;\r
-\r
-               // Complete undo\r
-               UndoDone();\r
-\r
-               // Jump forwards in time to last move finishing\r
-               if (numUndo > 0 && time < undo[numUndo-1].endTime)\r
-                       time = undo[numUndo-1].endTime;\r
-\r
-               double realTime = time;\r
-               double endAnimTime = time;\r
-               bool high = (tileSolid[GetTile(player)] == 1);\r
-               Pos playerStartPos = player;\r
-               Pos oldpos = player;\r
-               int oldPlayerHeight = GetHeight(oldpos);\r
-               Pos newpos = player + d;\r
-\r
-               int playerRot = GetLastPlayerRot();\r
-               if (d!=-1 && d!=playerRot)\r
-               {\r
-                       while (d!=playerRot)\r
-                       {\r
-                               if ((d+6-playerRot) % MAX_DIR < MAX_DIR/2)\r
-                                       playerRot = (playerRot+1) % MAX_DIR;\r
-                               else\r
-                                       playerRot = (playerRot+MAX_DIR-1) % MAX_DIR;\r
-\r
-                               time += 0.03;\r
-\r
-                               if (GetTile(oldpos) == FLOATING_BALL)\r
-                               {\r
-                                       TileRender* t = new TileRender(FLOATING_BALL, oldpos);\r
-                                       t->special = playerRot + 256;\r
-                                       renderer(oldpos).Add(t, time);\r
-\r
-                                       renderer.player.Add(new PlayerRender(playerRot, Pos(-20,-20), oldPlayerHeight, Pos(-20,-20), oldPlayerHeight, dead), time);\r
-                               }\r
-                               else\r
-                               {\r
-                                       PlayerRender *p = new PlayerRender(playerRot, player, oldPlayerHeight, player, oldPlayerHeight, dead);\r
-                                       p->speed = 0;\r
-                                       renderer.player.Add(p, time);\r
-                               }\r
-                       }\r
-\r
-                       time += 0.03;\r
-               }\r
-\r
-               if (d<0 && player_items[1]==0)\r
-                       return false;\r
-\r
-               if (d >= 0)\r
-               {\r
-                       if (tileSolid[GetTile(newpos)] == -1)\r
-                       {\r
-                               time = realTime;\r
-                               return false;\r
-                       }\r
-                       if (Collide(newpos, high))\r
-                       {\r
-                               time = realTime;\r
-                               return false;\r
-                       }\r
-               }\r
-\r
-               // Don't change any real state before this point!\r
-               if (numUndo >= MAX_UNDO)\r
-               {\r
-                       numUndo--;\r
-                       for(int i=0; i<MAX_UNDO-1; i++)\r
-                               undo[i] = undo[i+1];\r
-               }\r
-               undo[numUndo].New(d, player, player_items, time, player_score);\r
-\r
-               if (d<0)\r
-               {\r
-                       player_items[1]--;\r
-               }\r
-\r
-               double time0 = time;\r
-               time += 0.15;   //Time for leave-tile fx\r
-\r
-               switch (GetTile(oldpos))\r
-               {\r
-                       case COLLAPSABLE:\r
-                               SetTile(oldpos, EMPTY);\r
-                               renderer(oldpos).Add(new DisintegrateRender(oldpos), time);\r
-                               CheckFinished();\r
-                               break;\r
-\r
-                       case COLLAPSE_DOOR:\r
-                               // Don't need to CheckFinished - can't be collapse doors around\r
-                               //  unless there're still collapsable tiles around.\r
-                               SetTile(oldpos, EMPTY);\r
-                               renderer(oldpos).Add(new DisintegrateRender(oldpos, 1), time);\r
-                               break;\r
-\r
-                       case COLLAPSABLE2:\r
-                               SetTile(oldpos, COLLAPSABLE, false);\r
-                               renderer(oldpos).Add(new DisintegrateRender(oldpos, 0, 1), time);\r
-                               player_score += 10; \r
-                               CheckFinished();\r
-                               break;\r
-\r
-                       case COLLAPSE_DOOR2:\r
-                               SetTile(oldpos, COLLAPSE_DOOR, false);\r
-                               renderer(oldpos).Add(new DisintegrateRender(oldpos, 1, 1), time);\r
-                               player_score += 10; \r
-                               break;\r
-\r
-                       case COLLAPSABLE3:\r
-                               SetTile(oldpos, COLLAPSABLE2);\r
-                               break;\r
-               }\r
-\r
-               time = time0;   //End of leave-tile fx\r
-\r
-               int retry_pos_count=0;\r
-retry_pos:\r
-               retry_pos_count++;\r
-               \r
-               if (GetItem(newpos)==1)\r
-               {\r
-                       renderer(newpos, true).Add(new ItemCollectRender(GetItem(newpos), newpos), time + JUMP_TIME/2);\r
-                       SetItem(newpos, 0, false);\r
-                       player_items[0]++;\r
-               }\r
-               if (GetItem(newpos)==2)\r
-               {\r
-                       renderer(newpos, true).Add(new ItemCollectRender(GetItem(newpos), newpos), time + JUMP_TIME/2);\r
-                       SetItem(newpos, 0, false);\r
-                       player_items[1]++;\r
-               }\r
-\r
-               if (GetTile(player) == FLOATING_BALL)\r
-               {\r
-                       TileRender* t = new TileRender(FLOATING_BALL, player);\r
-                       t->special = 0;\r
-                       renderer(oldpos).Add(t, time);\r
-               }\r
-\r
-               PlayerRender *p = new PlayerRender(playerRot, player, oldPlayerHeight, newpos, GetHeight(newpos), dead);\r
-\r
-               // alternate leg (hacky!)\r
-               if (1)\r
-               {\r
-                       static int l=0;\r
-                       l++;\r
-                       p->type = l & 1;\r
-               }\r
-\r
-               if (retry_pos_count!=0 && GetTile(player)==TRAP)\r
-               {\r
-                       p->speed /= 1.5;\r
-                       p->type = 2;\r