From aa906cbc4ee7f62c6d5af80fbd32cf968d887aa6 Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Mon, 1 Dec 2008 15:34:36 +0100 Subject: [PATCH] convert \r\n to \n --- gfx.cpp | 1162 ++++---- gfx_list.h | 68 +- hex_puzzzle.cpp | 8306 +++++++++++++++++++++++++++---------------------------- level_list.h | 276 +- levels.dat | Bin 21689 -> 21628 bytes menus.h | 2538 ++++++++--------- packfile.h | 276 +- savestate.h | 612 ++-- state.h | 418 +-- tiletypes.h | 112 +- 10 files changed, 6884 insertions(+), 6884 deletions(-) diff --git a/gfx.cpp b/gfx.cpp index 0c8e5ae..020caba 100644 --- a/gfx.cpp +++ b/gfx.cpp @@ -1,581 +1,581 @@ -/* - 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 - -#ifdef WIN32 - #include - #include // 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 -#include - -#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 - #include - #include - #include - - 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; -} +/* + 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 + +#ifdef WIN32 + #include + #include // 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 +#include + +#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 + #include + #include + #include + + 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; +} diff --git a/gfx_list.h b/gfx_list.h index a0a631e..f60f91f 100644 --- a/gfx_list.h +++ b/gfx_list.h @@ -1,34 +1,34 @@ -/* - 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 - +/* + 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 + diff --git a/hex_puzzzle.cpp b/hex_puzzzle.cpp index 4fe0030..3f341c6 100644 --- a/hex_puzzzle.cpp +++ b/hex_puzzzle.cpp @@ -1,4153 +1,4153 @@ -/* - 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 -#include -#include // TODO: remove it later -#include -#include - -////////////////////////////////////////////////////// -// Config - - -#ifdef _DEBUG -#define EDIT -#endif - -//#define MAP_LOCKED_VISIBLE - -#ifndef GAME_NAME -#define GAME_NAME "Hex-a-hop" -#endif - -#ifndef DATA_DIR -#define DATA_DIR "." -#endif - -#ifdef EDIT -// #define MAP_EDIT_HACKS - #define MAP_EDIT_HACKS_DISPLAY_UNLOCK 0 - #define CHEAT - #define BMP_SUFFIX ".bmp" -#else - #define USE_LEVEL_PACKFILE - #define BMP_SUFFIX ".dat" -#endif - - - -#ifdef EDIT -#define GAMENAME GAME_NAME " (EDIT MODE)" -#endif -#ifndef GAMENAME -#define GAMENAME GAME_NAME -#endif - -#define IMAGE_DAT_OR_MASK 0xff030303 // Reduce colour depth of images slightly for better compression (and remove useless top 8 bits!) -#define STARTING_LEVEL "Levels\\0_green\\triangular.lev" -#define UNLOCK_SCORING 75 -const char * mapname = "Levels\\map_maybe\\map.lev"; - -////////////////////////////////////////////////////// - - - -#ifndef USE_OPENGL - -#include "state.h" - -#include "tiletypes.h" - -#ifdef USE_LEVEL_PACKFILE -#include "packfile.h" -#endif - -#include -#include -#include -#include - -#ifndef PATH_MAX -#define PATH_MAX 4096 -#endif - -void RenderTile(bool reflect, int t, int x, int y, int cliplift=-1); - -int keyState[SDLK_LAST] = {0}; - -FILE *file_open( const char *file, const char *flags ) -{ -// printf("file_open( \"%s\", \"%s\" )\n", file, flags ); - extern String base_path; - static String filename; // static to reduce memory alloc/free calls. - if (file[0]=='/') //If a full path is specified, don't prepend base_path - filename = ""; - else - { - if (strncmp(file, "save", 4) == 0) - { - const char *home = getenv("HOME"); - if (home) - { - char save_path[PATH_MAX]; - snprintf(save_path, sizeof(save_path), "%s/.hex-a-hop", home); - if (!strchr(flags, 'r')) - if (mkdir(save_path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != -1) - printf("Creating directory \"%s\"\n", (const char *)save_path); - strncat(save_path, "/", sizeof(save_path)); - filename = save_path; - } - else filename = "/tmp/"; - } - else filename = base_path; - } - filename += file; -// printf(" -> \"%s\"\n", filename ); - - filename.fix_backslashes(); - FILE* f = fopen( filename, flags ); - - if (!f && strncmp(file, "save", 4) != 0) - { - printf("Warning: unable to open file \"%s\" for %s\n", (const char*)filename, strchr(flags, 'r') ? "reading" : "writing"); - } - - return f; -} - - -#ifdef MAP_EDIT_HACKS - static const short value_order[]={ - //WALL, - //COLLAPSE_DOOR2, - //COLLAPSABLE3 - //SWITCH - //EMPTY, NORMAL, - - COLLAPSABLE, - TRAMPOLINE, - COLLAPSE_DOOR, COLLAPSABLE2, - GUN, - FLOATING_BALL, - SPINNER, - TRAP, - 0x100, - LIFT_DOWN, LIFT_UP, - BUILDER, - 0x200, - }; -#endif - -//#define PROGRESS_FILE "progress.dat" - -#define PI (3.1415926535897931) -#define PI2 (PI*2) -#define MAX(a,b) ((a)>(b) ? (a) : (b)) -#define MIN(a,b) ((a)<(b) ? (a) : (b)) -#define ABS(a) ((a)<0 ? -(a) : (a)) - -#define WATER_COLOUR 31 | (IMAGE_DAT_OR_MASK>>16)&255, 37 | (IMAGE_DAT_OR_MASK>>8)&255, 135 | (IMAGE_DAT_OR_MASK>>0)&255 - -#define ROTATION_TIME 0.25 -#define BUILD_TIME 1 -#define LASER_LINE_TIME 0.7 -#define LASER_FADE_TIME 0.1 -#define LASER_SEGMENT_TIME 0.01 -#define LIFT_TIME 0.5 -#define JUMP_TIME 0.4 - -#define X(NAME,FILE,ALPHA) SDL_Surface* NAME = 0; -#include "gfx_list.h" -int scrollX=0, scrollY=0, initScrollX=0, initScrollY=0; -int mapRightBound = 0; -int mapScrollX = 0; -bool showScoring = false; -bool hintsDone = false; - -enum { - TILE_SPLASH_1 = 17, - TILE_SPLASH_2, - TILE_SPLASH_3, - - TILE_SPHERE = 20, - TILE_SPHERE_OPEN, - TILE_SPHERE_DONE, - TILE_SPHERE_PERFECT, - TILE_LOCK, - - TILE_LIFT_BACK, - TILE_LIFT_FRONT, - TILE_LIFT_SHAFT, - TILE_BLUE_FRONT, - TILE_GREEN_FRONT, - - TILE_LINK_0 = 30, - TILE_LINK_1, - TILE_LINK_2, - TILE_LINK_3, - TILE_LINK_4, - TILE_LINK_5, - TILE_GREEN_FRAGMENT, - TILE_GREEN_FRAGMENT_1, - TILE_GREEN_FRAGMENT_2, - TILE_ITEM2, - - TILE_WATER_MAP = 40, - TILE_GREEN_CRACKED, - TILE_GREEN_CRACKED_WALL, - TILE_BLUE_CRACKED, - TILE_BLUE_CRACKED_WALL, - TILE_LASER_HEAD, - TILE_FIRE_PARTICLE_1, - TILE_FIRE_PARTICLE_2, - TILE_WATER_PARTICLE, - - TILE_LASER_0 = 50, - TILE_LASER_FADE_0 = 53, - TILE_BLUE_FRAGMENT = 56, - TILE_BLUE_FRAGMENT_1, - TILE_BLUE_FRAGMENT_2, - TILE_ITEM1, - TILE_LASER_REFRACT = 60, - TILE_ICE_LASER_REFRACT = TILE_LASER_REFRACT+6, - TILE_WHITE_TILE, - TILE_WHITE_WALL, - TILE_BLACK_TILE, - -}; - -const int colours[] = { - #define X(n,col, solid) col, - #include "tiletypes.h" -}; - -const int tileSolid[] = { - #define X(n,col, solid) solid, - #include "tiletypes.h" -}; - -void ChangeSuffix(char* filename, char* newsuffix) -{ - int len = strlen(filename); - int i = len-1; - while (i>=0 && filename[i]!='\\' && filename[i]!='.' && filename[i]!='/') - i--; - if (filename[i]=='.') - strcpy(filename+i+1, newsuffix); - else - { - strcat(filename, "."); - strcat(filename, newsuffix); - } -} - -bool isMap=false, isRenderMap=false; -int isFadeRendering=0; - -/* - |--| |--| TILE_W1 - |--------| TILE_W2 - |-----| TILE_WL - |-----------| TILE_W3 - - *-----* - - - / \ |TILE_H1 |TILE_H2 - / \ | | - * * - | - \ / | - \ / | - *-----* - - - WL = sqrt(h1*h1 + w1*w1) - wl**2 = h1**2 + w1**2 - - w1 = sin60.wL - -*/ - -#if 1 - #define TILE_W1 18 - #define TILE_W3 64 - #define GFX_SIZE TILE_W3 - #define TILE_W2 (TILE_W3-TILE_W1) - #define TILE_H1 TILE_W1 - #define TILE_HUP 22 //extra visible height of wall (used for determining whether a wall was clicked on) - #define TILE_H2 (TILE_H1*2) - #define TILE_WL (TILE_W2-TILE_W1) - #define TILE_H_LIFT_UP 26 - #define TILE_H_REFLECT_OFFSET 24 - #define TILE_HUP2 TILE_H_LIFT_UP // Displacement of object on top of wall - #define FONT_SPACING 25 - #define FONT_X_SPACING (-1) // -1 in order to try and overlap the black borders of adjacent characters -#else - #define TILE_WL 30 - #define TILE_W1 (TILE_WL/2) - #define TILE_W2 (TILE_W1+TILE_WL) - #define TILE_W3 (TILE_W1+TILE_W2) - #define TILE_H1 (TILE_WL*0.8660254037844386) - #define TILE_H2 (TILE_H1*2) -#endif - -#define MAX_DIR 6 - -SDL_Rect tile[2][70]; -short tileOffset[2][70][2]; -int Peek(SDL_Surface* i, int x, int y) -{ - if (x<0 || y<0 || x>=i->w || y>=i->h) - return 0; - unsigned int p=0; - const int BytesPerPixel = i->format->BytesPerPixel; - const int BitsPerPixel = i->format->BitsPerPixel; - if (BitsPerPixel==8) - p = ((unsigned char*)i->pixels)[i->pitch*y + x*BytesPerPixel]; - else if (BitsPerPixel==15 || BitsPerPixel==16) - p = *(short*)(((char*)i->pixels) + (i->pitch*y + x*BytesPerPixel)); - else if (BitsPerPixel==32) - p = *(unsigned int*)(((char*)i->pixels) + (i->pitch*y + x*BytesPerPixel)); - else if (BitsPerPixel==24) - p = (int)((unsigned char*)i->pixels)[i->pitch*y + x*BytesPerPixel] - | (int)((unsigned char*)i->pixels)[i->pitch*y + x*BytesPerPixel] << 8 - | (int)((unsigned char*)i->pixels)[i->pitch*y + x*BytesPerPixel] << 16; - - return p; -} -bool IsEmpty(SDL_Surface* im, int x, int y, int w, int h) -{ - for (int i=x; ih-1)) - return false; - return true; -} - -void MakeTileInfo() -{ - for (int i=0; i<140; i++) - { - SDL_Rect r = {(i%10)*GFX_SIZE, ((i/10)%7)*GFX_SIZE, GFX_SIZE, GFX_SIZE}; - short * outOffset = tileOffset[i/70][i%70]; - SDL_Surface * im = (i/70) ? tileGraphicsR : tileGraphics; - - outOffset[0] = outOffset[1] = 0; - - while (r.h>1 && IsEmpty(im, r.x, r.y, r.w, 1)) r.h--, r.y++, outOffset[1]++; - while (r.h>1 && IsEmpty(im, r.x, r.y+r.h-1, r.w, 1)) r.h--; - while (r.w>1 && IsEmpty(im, r.x, r.y, 1, r.h)) r.w--, r.x++, outOffset[0]++; - while (r.w>1 && IsEmpty(im, r.x+r.w-1, r.y, 1, r.h)) r.w--; - - tile[i/70][i%70] = r; - } -} - -void ConvertToUTF8(const std::string &text_locally_encoded, char *text_utf8, size_t text_utf8_length) -{ - // Is this portable? - size_t text_length = text_locally_encoded.length()+1; - errno = 0; - static const char *locale_enc = gettext_init.GetEncoding(); - iconv_t cd = iconv_open("UTF-8", locale_enc); - char *in_buf = const_cast(&text_locally_encoded[0]); - char *out_buf = &text_utf8[0]; - iconv(cd, &in_buf, &text_length, &out_buf, &text_utf8_length); - iconv_close(cd); - if (errno != 0) - std::cerr << "An error occurred recoding " << text_locally_encoded << " to UTF8" << std::endl; -} - -int SDLPangoTextWidth(const std::string &text_utf8); -void Print_Pango(int x, int y, const std::string &text_utf8); -void Print_Pango_Aligned(int x, int y, int width, const std::string &text_utf8, int align); - -/// Prints a left aligned string (a single line) beginning at (x,y) -// TODO: Check that the maximal text width is already set -void Print(int x, int y, const char * string, ...) -{ - va_list marker; - va_start( marker, string ); /* Initialize variable arguments. */ - - char tmp[1000], tmp_utf8[5000]; // FIXME: Check this limit - vsprintf((char*)tmp, string, marker); - - ConvertToUTF8(tmp, tmp_utf8, sizeof(tmp_utf8)/sizeof(char)); - Print_Pango(x, y, tmp_utf8); - - va_end( marker ); /* Reset variable arguments. */ -} - -/// Prints a string right aligned so that it ends at (x,y) -// TODO: Check that the maximal text width is already set -void PrintR(int x, int y, const char * string, ...) -{ - va_list marker; - va_start( marker, string ); /* Initialize variable arguments. */ - - char tmp[1000], tmp_utf8[5000]; // FIXME: Check this limit - vsprintf((char*)tmp, string, marker); - - ConvertToUTF8(tmp, tmp_utf8, sizeof(tmp_utf8)/sizeof(char)); - Print_Pango(x-SDLPangoTextWidth(tmp_utf8), y, tmp_utf8); - - va_end( marker ); /* Reset variable arguments. */ -} - -/** \brief Prints a string horizontally centered around (x,y) - * - * " " in the string is interpreted as linebreak -*/ -void Print_Aligned(bool split, int x, int y, int width, const char * string, int align) -{ - char tmp_utf8[5000]; // FIXME: Check this limit - - ConvertToUTF8(string, tmp_utf8, sizeof(tmp_utf8)/sizeof(char)); - - std::string msg(tmp_utf8); - while (split && msg.find(" ") != std::string::npos) - msg.replace(msg.find(" "), 2, "\n"); - - Print_Pango_Aligned(x, y, width, msg, align); -} - -void PrintC(bool split, int x, int y, const char * string, ...) -{ - va_list marker; - va_start( marker, string ); /* Initialize variable arguments. */ - - char tmp[1000]; // FIXME: Check this limit - vsprintf((char*)tmp, string, marker); - - va_end( marker ); /* Reset variable arguments. */ - - static bool print = true; // avoid flickering! - if (print) { - std::cerr << "Warning: don't know window width for message:\n" << tmp << "\n"; - for (unsigned int i=0; i TILE_H1) - if (x*TILE_H1 + (TILE_H2-y) * TILE_W1 < TILE_H1*TILE_W1) - tx--, ty++; - - return Pos(tx, ty); - } -}; -Pos mousep(0,0), keyboardp(4,20); - -class RenderObject; - -struct RenderStage -{ - virtual ~RenderStage() {} - virtual void Render(RenderObject* r, double time, bool reflect) = 0; - virtual int GetDepth(double /*time*/) { return 1; } -}; - -class RenderObject -{ - RenderStage** stage; - double* time; - int numStages; - int maxStages; - int currentStage; -public: - double seed; - double currentTime; -private: - - void Reserve() - { - if (maxStages <= numStages) - { - maxStages = maxStages ? maxStages*2 : 4; - stage = (RenderStage**) realloc(stage, sizeof(stage[0])*maxStages); - time = (double*) realloc(time, sizeof(time[0])*maxStages); - } - } -public: - RenderObject() : stage(0), time(0), numStages(0), maxStages(0), currentStage(0) - { - // TODO: use a random number with better range - // or maybe make seed an int or float... - seed = rand() / (double)RAND_MAX; - } - ~RenderObject() - { - free(stage); free(time); - } - bool Active(double t) - { - if (numStages==0) return false; - if (t < time[0]) return false; - return true; - } - void UpdateCurrent(double t) - { - if (currentStage >= numStages) currentStage = numStages-1; - if (currentStage < 0) currentStage = 0; - - while (currentStage>0 && time[currentStage]>t) - currentStage--; - while (currentStage0) - return stage[numStages-1]; - - if (!Active(t)) return 0; - UpdateCurrent(t); - return stage[currentStage]; - } - double GetLastTime() - { - return numStages>0 ? time[numStages-1] : -1; - } - void Render(double t, bool reflect) - { - if (!Active(t)) - return; - UpdateCurrent(t); - stage[currentStage]->Render(this, t - time[currentStage], reflect); - } - int GetDepth(double t) - { - if (!Active(t)) - return -1; - UpdateCurrent(t); - return stage[currentStage]->GetDepth(t - time[currentStage]); - } - void Reset(double t) - { - if (t<0) - numStages = currentStage = 0; - else - { - while (numStages > 0 && time[numStages-1] >= t) - numStages--; - if (currentStage > 0 && currentStage >= numStages) - currentStage = numStages - 1; - } - if (currentStage < 0) currentStage = 0; - } - void Wipe() - { - if (currentStage > 0 && numStages > 0) - { - memmove(&time[0], &time[currentStage], sizeof(time[0]) * (numStages-currentStage)); - memmove(&stage[0], &stage[currentStage], sizeof(stage[0]) * (numStages-currentStage)); - numStages -= currentStage; - currentStage = 0; - } - } - void Add(RenderStage* s, double t) - { - int i=0; - - if (currentStage=SIZE || p.y>=SIZE) return false; - if (p.x=x1-1) return false; - for (int j0=0; j0 scrollY+SCREEN_H+TILE_H1) break; - int i = j0&1; - int j = j0>>1; - j -= (x0-i)/2; - i += (x0-i)/2*2; - if (j>=SIZE) i+=(j+1-SIZE)*2, j=SIZE-1; - for (; i=0; i+=2, j--) - { - if (Pos(i,j)==p) - return true; - } - } - return false; - } - - void Render(double t, bool reflect) - { - dummy.Reset(-1); - - int playerDepth = player.GetDepth(t); - if (reflect) playerDepth-=4; - if (playerDepth<0) - player.Render(t, reflect); - - int x0 = (scrollX+TILE_W2) / TILE_W2; - int x1 = (scrollX+SCREEN_W+TILE_W3+TILE_W1) / TILE_W2; - x0 = MAX(x0, 0); - x1 = MIN(x1, SIZE); - for (int j0=0; j0 scrollY+SCREEN_H+TILE_H1) break; - int i = j0&1; - int j = j0>>1; - j -= (x0-i)/2; - i += (x0-i)/2*2; - if (j>=SIZE) i+=(j+1-SIZE)*2, j=SIZE-1; - for (; i=0; i+=2, j--) - { - for (int q=reflect?1:0; q!=2 && q!=-1; q += (reflect ? -1 : 1)) - if (tile[i][j][q].Active(t)) - { - tile[i][j][q].Render(t, reflect); - } - } - - if (playerDepth==j0 || j0==SIZE*3 && playerDepth>j0) - player.Render(t, reflect); - } - - for (int j=0; j=SIZE || p.y>=SIZE) - return dummy; - return tile[p.x][p.y][item ? 1 : 0]; - } -}; - -void RenderTile(bool reflect, int t, int x, int y, int cliplift) -{ - SDL_Rect src = tile[reflect][t]; - SDL_Rect dst = {x-scrollX-GFX_SIZE/2, y-scrollY-GFX_SIZE+TILE_H1, 0, 0}; - dst.x += tileOffset[reflect][t][0]; - dst.y += tileOffset[reflect][t][1]; - if (reflect) - dst.y += TILE_H_REFLECT_OFFSET; - if (cliplift==-1 || reflect) - { - // dst.w=src.w; dst.h=src.h; - // SDL_FillRect(screen, &dst, rand()); - SDL_BlitSurface(reflect ? tileGraphicsR : tileGraphics, &src, screen, &dst); - } - else - { - src.h -= cliplift; - if (src.h > TILE_W1) - { - src.h -= TILE_W1/2; - SDL_BlitSurface(tileGraphics, &src, screen, &dst); - src.y += src.h; - dst.y += src.h; - src.h = TILE_W1/2; - } - if (src.h > 0) - { - src.w -= TILE_W1*2, src.x += TILE_W1; - dst.x += TILE_W1; - SDL_BlitSurface(tileGraphics, &src, screen, &dst); - } - } -} -void RenderGirl(bool reflect, int r, int frame, int x, int y, int h) -{ - int sx = r * 64; - int sy = frame * 80*2; - if (reflect) - y += TILE_H_REFLECT_OFFSET+20+h, sy += 80; - else - y -= h; - SDL_Rect src = {sx, sy, 64, 80}; - SDL_Rect dst = {x-scrollX-32, y-scrollY-65, 0, 0}; - SDL_BlitSurface(girlGraphics, &src, screen, &dst); -} - -struct ItemRender : public RenderStage -{ - int item; - Pos p; - int water; - - ItemRender(int i2, int _water, Pos const & _p) : item(i2), p(_p), water(_water) - {} - - double Translate(double seed, double time) - { - double bob = time*2 + seed*PI2; - return sin(bob)*4; - } - - void Render(RenderObject* r, double time, bool reflect) - { - if (item==0) - return; - - int y = -5 + (int)Translate(r->seed, r->currentTime + time); - if (reflect) - y=-y; - if (!reflect && !water) - RenderTile( false, TILE_SPHERE, p.getScreenX(), p.getScreenY()); - RenderTile( - reflect, - item==1 ? TILE_ITEM1 : TILE_ITEM2, - p.getScreenX(), p.getScreenY()+y - ); - } -}; - -void RenderFade(double time, int dir, int seed) -{ - int ys=0; - srand(seed); - for(int x=rand()%22-11; x= b) - { - RenderTile(false, TILE_BLACK_TILE, x+scrollX, y+scrollY); - } - } - } -} - -struct FadeRender : public RenderStage -{ - int seed; - int dir; - FadeRender(int d=-1) : seed(rand()), dir(d) - { - isFadeRendering = d; - } - - void Render(RenderObject* /*r*/, double time, bool reflect) - { - if (reflect) return; - if (time > 0.5) - { - if (dir==1) dir=0, isFadeRendering=0; - return; - } - RenderFade(time, dir, seed); - } -}; - -struct ScrollRender : public RenderStage -{ - int x,y; - bool done; - ScrollRender(int a,int b) : x(a), y(b), done(false) {} - - void Render(RenderObject* /*r*/, double /*time*/, bool /*reflect*/) - { - if (done) return; - scrollX = x, scrollY = y; - isRenderMap = isMap; - done = true; - } -}; - -struct LevelSelectRender : public RenderStage -{ - Pos p; - int item; - int adj; -#ifdef MAP_EDIT_HACKS - int magic; -#endif - - LevelSelectRender(Pos const & _p, int i2, int adj) : p(_p), item(i2), adj(adj) - {} - - void Render(RenderObject* /*r*/, double /*time*/, bool reflect) - { - if (item==0) - return; - - #ifndef MAP_LOCKED_VISIBLE - if (item==1) return; - #endif - - if (!reflect && adj) - for (int i=0; i 1) - time = 1; - if (time < 0) - time = 0; - time = (3 - 2*time)*time*time; - if (t==LIFT_UP) - time = (3 - 2*time)*time*time; - if (t==LIFT_UP) - return (int)((TILE_H_LIFT_UP+4) * time); - else - return (int)((TILE_H_LIFT_UP-4) * time) + 4; -} - -struct TileRender : public RenderStage -{ - int special; - int t; - Pos p; - double specialDuration; - - TileRender(int i, Pos const & _p, int _special=0) : special(_special), t(i), p(_p), specialDuration(LASER_LINE_TIME) - {} - - void Render(RenderObject* r, double time, bool reflect) - { - if (t==0 && special==0) - return; - - if (special && (t==LIFT_UP || t==LIFT_DOWN) && time= specialDuration-LASER_FADE_TIME) - base = TILE_LASER_FADE_0; - - int foo=special; - for(int i=0; foo; foo>>=1, i++) - if (foo & 1) - RenderTile(reflect, base+i, p.getScreenX(), p.getScreenY()); - } - else if (t==FLOATING_BALL) - { - int y = int(1.8 * sin(r->seed*PI + time*4)); - if (special==512) - { - if (time > 2) return; - if (reflect) return; - srand(int(r->seed * 0xfff)); - for (int i=0; i<20 - int(time*10); i++) - { - int x = int((((rand() & 0xfff) - 0x800) / 10) * time); - int y = int((((rand() & 0xfff) - 0x800) / 10) * time); - RenderTile(true, 19 + ((i+int(time*5))&1)*10, p.getScreenX() + x, p.getScreenY() - 14 + y); - } - - if (time < 0.05) - RenderTile(true, 18, p.getScreenX(), p.getScreenY() - 14); - } - else if (special) - RenderBoat(reflect, int(special)&255, p.getScreenX(), p.getScreenY(), y); - else - RenderTile(reflect, t, p.getScreenX(), p.getScreenY() + (reflect ? -y : y)); - } - else if (t != EMPTY) - RenderTile(reflect, t, p.getScreenX(), p.getScreenY()); - } - static void RenderBoat(bool reflect, int d, int x, int y, int yo) - { - if (reflect) - RenderGirl(reflect, d, 0, x, y, -yo); - RenderTile(reflect, FLOATING_BALL, x, y+yo); - if (!reflect) - { - RenderGirl(reflect, d, 0, x, y, -yo); - RenderTile(true, 17, x, y+yo-TILE_H_REFLECT_OFFSET); - } - } -}; - -struct TileRotateRender : public TileRender -{ - Dir d; -// int range; - int mode; - TileRotateRender(int i, Pos const & p, Dir _d, int m) : TileRender(i, p), d(_d), mode(m) - {} - void Render(RenderObject* r, double time, bool reflect) - { - if (t==0) - return; - double f = time / ROTATION_TIME; - - if (mode & 1) f += 0.5; - if (f<1 && f>0) - { - if (mode & 2) - ; - else - f = (3-2*f)*f*f; - } - - if (mode & 1) f=1-f; else f=f; - if (f<0) f=0; - - if (f >= 1) - TileRender::Render(r, time, reflect); - else - { - Pos dd = (Pos(0,0)+d); - int x = p.getScreenX() + int(dd.getScreenX()*(f)); - int y = p.getScreenY() + int(dd.getScreenY()*(f)); - - if (mode & 2) - RenderBoat(reflect, (mode&1) ? (d+MAX_DIR/2)%MAX_DIR : d, x, y, 2); - else - RenderTile(reflect, t, x, y); - } - } -}; - -struct LaserRender : public RenderStage -{ - Pos p; - Dir d; - int range; - - LaserRender(Pos _p, int dir, int r) : p(_p), d(dir), range(r) - {} - - void Render(RenderObject* /*r*/, double /*time*/) - { - } -}; - -struct ExplosionRender : public RenderStage -{ - Pos p; - int seed; - int power; - int type; - - ExplosionRender(Pos _p, int _pow=0, int t=0) : p(_p), power(_pow), type(t) - { - seed = rand(); - } - - virtual int GetDepth(double /*time*/) - { - return p.x + p.y*2; - } - - void Render(RenderObject* /*r*/, double time, bool reflect) - { - if (type==1 && time > 2.5) - type = -1, new WinLoseScreen(false); - - // if (reflect) return; - if (time > 3) return; - srand(seed); - int q = 50 - int(time * 35); - if (power) q*=2; - if (type) q = 50; - for (int i=0; i 0) yo=-yo;//continue; - if (type) - { - - if (yo > 0) - { - if (!reflect && ys<-60) - { - const double T = 0.06; - double ct = -ys / 128.0; - if (time < ct+T*4) - { - x = p.getScreenX() + int(xs * (1+ct*(2+power))); - RenderTile( - reflect, - time > ct+3*T ? TILE_SPLASH_3 : time > ct+2*T ? TILE_SPLASH_2 : time > ct+T ? TILE_SPLASH_1 : TILE_WATER_PARTICLE+1, - x, y); - } - } - } - else - RenderTile( - reflect, - time - i*0.003 < 0.2 ? TILE_WATER_PARTICLE+1 : TILE_WATER_PARTICLE, - x, y+(reflect?-1:1)*yo); - } - else - { - if (yo > 0) - ; - else - RenderTile( - reflect, - i 50.0/70.0) return; - if (reflect) return; - srand(seed); - int q = 50 - int(time * 70); - if (height) q*=2; - for (int i=0; iTILE_WL/2 && ABS(y)>x-TILE_WL/2) continue; - int yo=0; - if (height) yo -= rand() % TILE_HUP; - x += p.getScreenX(); - y += p.getScreenY() + 4; - int xs = 0;//((rand() & 63) - 32); - int ys = (- (rand() & 31)); - x += int(xs * (1+time*(2))); - if (type) yo = -yo; - yo += int(time*time*128 + ys*time); - if (type) yo = -yo*2; - //if (yo > 0) yo=-yo;//continue; - int t = type ? TILE_BLUE_FRAGMENT : TILE_GREEN_FRAGMENT; - if (i>q-20) t++; - if (i>q-10) t++; - if (yo > 5) yo = 5; - RenderTile(false, t, x, y+(reflect?-yo:yo)); - } - } -}; -struct BuildRender : public RenderStage -{ - Pos p; - Dir dir; - int reverse; - int height; - int type; - - BuildRender(Pos _p, Dir _d, int _h, int _r=0, int _type=0) : p(_p), dir(_d), reverse(_r), height(_h), type(_type) - { - } - - void Render(RenderObject* /*r*/, double time, bool reflect) - { - if (time >= BUILD_TIME) - RenderTile(reflect, height ^ reverse ? (type ? COLLAPSE_DOOR2 : COLLAPSE_DOOR) : (type ? COLLAPSABLE2 : COLLAPSABLE), p.getScreenX(), p.getScreenY()); - else - { - if (height) - RenderTile(reflect, type ? COLLAPSABLE2 : COLLAPSABLE, p.getScreenX(), p.getScreenY()); - - double dist = time * 2 / BUILD_TIME; - if (dir>-1) - { - Pos from = p + ((dir+MAX_DIR/2)%MAX_DIR); - if (dist <= 1) - //if (dist > 1) - { - double offset = (dist*0.7) + 0.3; - int x = from.getScreenX() + int((p.getScreenX()-from.getScreenX()) * offset); - int y = from.getScreenY() + int((p.getScreenY()-from.getScreenY()) * offset - dist*(1-dist)*(TILE_HUP*4)); - RenderTile(reflect, TILE_GREEN_FRAGMENT, x, y); - } - dist -= 1; - } - else - { - if (reverse) dist = 1-dist; - } - if (dist > 0 && !height) - { - if (!reflect) - for (int i=0; i<=int(dist*15); i++) - { - int x = p.getScreenX(), y = p.getScreenY(); - double d = (i + fmod(dist*15, 1))/10.0; - int x1 = int(sin(d*5+time)*MIN(d,1)*TILE_W2/2); - int y1 = int(cos(d*5+time)*MIN(d,1)*TILE_H1*0.7); - RenderTile(reflect, TILE_GREEN_FRAGMENT, x+x1, y+y1+4); - RenderTile(reflect, TILE_GREEN_FRAGMENT, x-x1, y-y1+4); - } - } - if (dist > 0 && height) - { - int yo = int((1-dist)*(TILE_HUP*1.3)); - if (yo > TILE_HUP*1.1) - RenderTile(reflect, TILE_WHITE_TILE, p.getScreenX(), p.getScreenY()); - else if (!reflect) - { - RenderTile(reflect, type ? COLLAPSABLE2 : COLLAPSABLE, p.getScreenX(), p.getScreenY()); - RenderTile(reflect, type ? COLLAPSE_DOOR2 : COLLAPSE_DOOR, p.getScreenX(), p.getScreenY()+(reflect ? -yo : yo), yo+6); - RenderTile(reflect, type ? TILE_BLUE_FRONT : TILE_GREEN_FRONT, p.getScreenX(), p.getScreenY()); - } - else - { - if (yo < TILE_HUP/2) - { - RenderTile(reflect, type ? COLLAPSE_DOOR2 : COLLAPSE_DOOR, p.getScreenX(), p.getScreenY()+(reflect ? -yo : yo), yo); - - } - RenderTile(reflect, type ? COLLAPSABLE2 : COLLAPSABLE, p.getScreenX(), p.getScreenY()); - - } - } - } - } -}; - -struct PlayerRender : public RenderStage -{ - Pos p; - Pos target; - int p_h, target_h; - int r; - int type; - double speed; - bool dead; - - 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) - {} - 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) - { - int dist = MAX(ABS(p.x-target.x), ABS(p.y-target.y)); - if (dist > 1) - speed *= 1.5; - if(dist==0) - speed = 0; - } - - virtual int GetDepth(double time) - { - double f = speed ? time / speed : 1; - if (f>1) f=1; - if (f==1) dead = this->dead; - - if (f==1 || f>0.5 && p_h>target_h) - return target.x+target.y*2; - return MAX(target.x+target.y*2 , p.x+p.y*2); - } - - void Render(RenderObject* /*ro*/, double time, bool reflect) - { - bool dead = false; - double f = speed ? time / speed : 1; - if (f>1) f=1; - if (f==1) dead = this->dead; - - int x = p.getScreenX(); - int y = p.getScreenY(); - int x2 = target.getScreenX(); - int y2 = target.getScreenY(); - int h = 0; - int shadow_h = (int)((p_h+(target_h-p_h)*f)*TILE_HUP2); - - if (x==x2 && y==y2 && p_h!=target_h) - { - h = TILE_H_LIFT_UP - GetLiftHeight(time, p_h ? LIFT_DOWN : LIFT_UP); - } - else - { - - int dist = MAX(ABS(p.x-target.x), ABS(p.y-target.y)); - int arc = dist*dist; - int h1 = p_h * TILE_HUP2;; - int h2 = target_h * TILE_HUP2; - if (dist==2 && h1!=0) - { - arc += h2 ? 1 : 3; - h1 = 0; - shadow_h = f>=0.7 ? int(shadow_h*(f-0.7)/0.3) : 0; - } - if (dist==0) - arc = speed > JUMP_TIME ? 7 : 2; - - h = (int)(h1+(h2-h1)*f); - // if (x==x2 && y==y2) - // ; - // else - { - //h += int(TILE_H_LIFT_UP/3 * (1-f)); - h += (int)(f*(1-f)*TILE_HUP2*arc); - } - - if (type==2) - h=0; - } - - if (!dead) - { - int frame = 0; - if (type==2 && f<1) - { - //frame = ((int)(f*4) % 4); - //if (frame==2) frame=0; else if (frame==3) frame=2; - frame = 0; - } - else if (f==1 || x==x2 && y==y2) // stationary - frame = 0; - else if (f > 0.7) - frame = 0; - else - { - frame = type ? 2 : 1; - if (f<0.1 || f>0.6) - frame += 2; - } - - if (!reflect) - RenderTile( false, TILE_SPHERE, - (int)(x+(x2-x)*f), - (int)(y+(y2-y)*f) - shadow_h - ); - - RenderGirl( - reflect, - r, frame, - (int)(x+(x2-x)*f), - (int)(y+(y2-y)*f), - h - ); - - } - /* RenderTile( - dead ? TILE_SPHERE_OPEN : TILE_SPHERE_DONE, - (int)(x+(x2-x)*f), - (int)(y+(y2-y)*f), - true - );*/ - } -}; - - -struct HexPuzzle : public State -{ - struct Undo - { - #define MAX_TILECHANGE 64 // TODO: don't have a magic upper limit - struct TileChange - { - Pos p; - Tile t; - int item; - - TileChange() - {} - TileChange(Pos _p, Tile _t, int _i) : p(_p), t(_t), item(_i) - {} - void Restore(HexPuzzle* w) - { - w->SetTile(p,t,false,false); - w->SetItem(p,item,false,false); - } - }; - - TileChange t[MAX_TILECHANGE]; - Pos playerPos; - Dir playerMovement; - int numT; - int numItems[2]; - int score; - double time; - double endTime; - - void Add(TileChange const & tc) - { - for (int i=0; i=MAX_TILECHANGE) - FATAL("numT>=MAX_TILECHANGE"); - else - t[numT++] = tc; - } - void New(Dir pmove, Pos & pp, int* items, double t, int sc) - { - numItems[0] = items[0]; - numItems[1] = items[1]; - playerPos = pp; - playerMovement = pmove; - score = sc; - time = t; - numT = 0; - } - void Restore(HexPuzzle* w) - { - for (int i=numT-1; i>=0; i--) - t[i].Restore(w); - w->dead = false; - w->win = false; - w->player = playerPos; - w->player_items[0] = numItems[0]; - w->player_items[1] = numItems[1]; - w->player_score = score; - - //w->renderer.player.Add(new PlayerRender(playerPos, w->GetHeight(playerPos), false), w->time); - } - }; - - #define MAP_SIZE 30 - char* special[MAP_SIZE][MAP_SIZE]; - Tile map[MAP_SIZE][MAP_SIZE]; - int32_t map_item[MAP_SIZE][MAP_SIZE]; - int tileCount[NumTileTypes]; - int32_t levelPar, levelDiff; - int turboAnim; - Pos player; - int player_items[2]; - int player_score; - int numComplete, numLevels, numMastered, numLevelsFound; - bool dead; - bool win; - int winFinal; - - SaveState progress; - - WorldRenderer renderer; - double time; - double undoTime; - - #define MAX_UNDO 6 - Undo undo[MAX_UNDO]; - int numUndo; - LevelInfo* currentLevelInfo; - - char currentFile[1000]; - - ~HexPuzzle() - { - FreeGraphics(); - } - - LevelInfo* GetLevelInfo(const char* f) - { - if (strstr(f, "Levels\\") == f) - f += 7; - if (currentLevelInfo!=0 && strcmp(currentLevelInfo->file, f)==0) - return currentLevelInfo; - - if (f[0]=='_') - { - int t = atoi(f+1); - if (t <= numComplete) - return 0; - - static char tmp1[1000]; - static LevelInfo tmp = {0, "", tmp1}; - sprintf(tmp1, ngettext("Complete 1 more level to unlock!", "Complete %d more levels to unlock!", t-numComplete), t-numComplete); - return &tmp; - } - - for (unsigned int i=0; i>")}; - return &tmp; - } - -#ifdef MAP_EDIT_HACKS - int GetAutoTile(const char * level, bool tiletype) - { - FILE* f = file_open(filename, "rb"); - int tile = EMPTY; - int version; - - if (f && fscanf(f, "%d", &version)==1 && (version==3 || version==4)) - { - if (strstr(level,"mk")) - level+=0; - - fgetc(f); // Remove '\n' character - - int32_t par, diff; - unsigned char bounds[4]; - Pos playerStart; - fread(&par, sizeof(par), 1, f); - par = SWAP32(par); - - if (version >= 4) { - fread(&diff, sizeof(diff), 1, f); - diff = SWAP32(diff); - } - fread(bounds, sizeof(bounds), 1, f); - fread(&playerStart, sizeof(playerStart), 1, f); - playerStart.x = SWAP32(playerStart.x); - playerStart.y = SWAP32(playerStart.y); - - int highval=0; - - for (int i=bounds[0]; i<=bounds[1]; i++) - for (int j=bounds[2]; j<=bounds[3]; j++) - { - unsigned char comp = map[i][j] | (map_item[i][j]<<5); - fread(&comp, sizeof(comp), 1, f); - int t = comp & 0x1f; - int item = (comp >> 5) & 3; - for (int i=highval+1; i>8)) - highval = i; - } - - if (tiletype) - { - tile = value_order[highval]; - if (tile==0x100) tile = COLLAPSABLE3; - if (tile==0x200) tile = SWITCH; - if (tile==LIFT_UP) tile = LIFT_DOWN; - } - else - { - if (value_order[highval] == LIFT_UP) - tile = highval-1; - else - tile = highval; - } - } - else - { - level+=0; - } - if (f) - fclose(f); - return tile; - } -#endif - - void InitSpecials() - { - numComplete = numLevels = numMastered = numLevelsFound = 0; - for (int i=0; i= UNLOCK_SCORING && !progress.general.scoringOn) - { - HintMessage::FlagTile(26); - progress.general.scoringOn = 1; - InitSpecials(); // Re-initialise with gold ones available - } - HintMessage::FlagTile(25); - } - else - { - for (int i=0; ibounds[1]) bounds[1] = x2; - if (y1bounds[3]) bounds[3] = y2; - } - } - - int sx, sy; - if (isMap) - { - sx = bounds[0] - int(TILE_W2*6.35); - sy = (bounds[3] + bounds[2] - SCREEN_H) / 2 - TILE_H2/2; - } - else - { - sx = (bounds[1] + bounds[0] - SCREEN_W) / 2 - TILE_W3/2; - sy = (bounds[3] + bounds[2] - SCREEN_H) / 2 - TILE_H2/2; - } - if (isMap) - { - initScrollX = sx; - initScrollY = sy; - if (mapScrollX==0) - mapScrollX = sx; - else - sx = mapScrollX; - } - -// time = 1; // Guarantee we can't try and do things at time=0 - - renderer().Add(new ScrollRender(sx, sy), time); - renderer().Add(new FadeRender(1), time); - if (time != 0) - time -= 0.5; - } - - char* ReadAll(FILE* f) - { - int size; - // FIXME: According to http://userpage.fu-berlin.de/~ram/pub/pub_jf47ht20Ht/c_faq_de - // undefined for binary streams! (POSIX does not differ between ascii and binary, so - // we are on the safe side in Linux) - fseek(f, 0, SEEK_END); - size = ftell(f); - fseek(f, 0, SEEK_SET); - char* c = loadPtr = new char [size]; - endLoad = loadPtr + size; - fread(c, 1, size, f); - return c; - } - - static char *loadPtr, *endLoad; - static unsigned int fread_replace(void* d, unsigned int size, unsigned int num, FILE*) - { - unsigned int remain = (endLoad - loadPtr) / size; - if (remain < num) num = remain; - memcpy(d, loadPtr, size*num); - loadPtr += size*num; - return num; - } - - int GetPar(const char * level, bool getdiff=false) - { - if (strcmp(level, currentFile)==0) - return getdiff ? levelDiff : levelPar; - - #ifdef USE_LEVEL_PACKFILE - PackFile1::Entry* e = levelFiles.Find(level); - if (!e) return 999; - loadPtr = (char*)e->Data(); - endLoad = loadPtr + e->DataLen(); - FILE* f = 0; - #else - loadPtr = 0; - FILE* f = file_open(level, "rb"); - #endif - - typedef unsigned int _fn(void*, unsigned int, unsigned int, FILE*); - _fn * fn = (loadPtr ? (_fn*)fread_replace : (_fn*)fread); - - int32_t par = 99999, diff = 0; - int16_t version; - - if (!f && !loadPtr) - return getdiff ? diff : par; - - fn(&version, 2, 1, f); // skip to relevant point - - if (fn(&par, sizeof(par), 1, f) != 1) - par = 99999; - else - par = SWAP32(par); - size_t ret = fn(&diff, sizeof(diff), 1, f); - diff = SWAP32(diff); - if (ret != 1 || diff<0 || diff>10) - diff = 0; - - #ifdef USE_LEVEL_PACKFILE - loadPtr = endLoad = 0; - #else - if (f) - fclose(f); - #endif - - return getdiff ? diff : par; - } - - bool LoadSave(const char * filename, bool save) - { - if (!filename) - return false; - - if (!save) - { - showScoring = false; - LevelSave* l = progress.GetLevel(filename, true); - if (progress.general.scoringOn && l && l->Completed() ) - showScoring = true; - } - - #ifdef USE_LEVEL_PACKFILE - if (!save) - { - PackFile1::Entry* e = levelFiles.Find(filename); - if (!e) return false; - - if (currentFile != filename) // equal (overlapping) strings are forbidden - strcpy(currentFile, filename); - currentLevelInfo = GetLevelInfo(currentFile); - - loadPtr = (char*)e->Data(); - endLoad = loadPtr + e->DataLen(); - _LoadSave(NULL, save); - loadPtr = endLoad = 0; - - return true; - } - #else - loadPtr = 0; - FILE* f = file_open(filename, save ? "wb" : "rb"); - if (f) - { - strcpy(currentFile, filename); - if (!save) - currentLevelInfo = GetLevelInfo(currentFile); - - if (!save) - { - char* data = ReadAll(f); - _LoadSave(f, save); - delete [] data; - loadPtr = endLoad = 0; - } - else - { - _LoadSave(f, save); - } - fclose(f); - - return true; - } - #endif - - return false; - } - - /** \brief Writes/reads game status to/from a file - * - * The game data file is written in little endian so it can be shared - * across different machines. - */ - void _LoadSave(FILE* f, bool save) - { - typedef unsigned int _fn(void*, unsigned int, unsigned int, FILE*); - _fn * fn = save ? (_fn*)fwrite : (loadPtr ? (_fn*)fread_replace : (_fn*)fread); - - #define VERSION 4 - int version = VERSION; // 1--9 - if (save) - fprintf(f, "%d\n", version); - else - { - char c; - if (fn(&c, 1, 1, f) != 1) - return; - version = c-'0'; - - // Remove '\n' character - fn(&c, 1, 1, f); - } - - if (!save) - { - for (int i=0; i=2 && version<=4) - { - unsigned char bounds[4]; - if (save) - { - bounds[0]=bounds[1]=player.x; - bounds[2]=bounds[3]=player.y; - for (int i=0; ibounds[1]) bounds[1] = i; - if (jbounds[3]) bounds[3] = j; - } - } - else - { - memset(map, 0, sizeof(map)); - memset(map_item, 0, sizeof(map_item)); - } - - if (version>=3) { - levelPar = SWAP32(levelPar); - fn(&levelPar, 1, sizeof(levelPar), f); - levelPar = SWAP32(levelPar); - } - else if (!save) - levelPar = 0; - - if (version>=4) { - levelDiff = SWAP32(levelDiff); - fn(&levelDiff, 1, sizeof(levelDiff), f); - levelDiff = SWAP32(levelDiff); - } - else if (!save) - levelDiff = 0; - - fn(bounds, sizeof(bounds), 1, f); - player.x = SWAP32(player.x); - player.y = SWAP32(player.y); - fn(&player, sizeof(player), 1, f); - player.x = SWAP32(player.x); - player.y = SWAP32(player.y); - - int offsetx=0, offsety=0; - - if (!save && bounds[1]-bounds[0]<15) // Hacky - don't recenter map... - { - // Re-position map to top left (but leave a bit of space) - // (This ensures the laser/boat effects don't clip prematurely against the edges of the screen) - offsetx = SCREEN_W/2/TILE_W2 + 1 - (bounds[0]+bounds[1]/2); - offsety = SCREEN_H/2/TILE_H2 + SCREEN_W/2/TILE_W2 - (bounds[2]+bounds[3]/2); - offsetx = MAX(0, offsetx); - offsety = MAX(0, offsety); -// if (bounds[0] > 2) -// offsetx = 2 - bounds[0]; -// if (bounds[2] > 2) -// offsety = 2 - bounds[2]; - } - bounds[0] += offsetx; - bounds[1] += offsetx; - bounds[2] += offsety; - bounds[3] += offsety; - player.x += offsetx; - player.y += offsety; - - for (int i=bounds[0]; i<=bounds[1]; i++) - for (int j=bounds[2]; j<=bounds[3]; j++) - { - unsigned char comp = map[i][j] | (map_item[i][j]<<5); - fn(&comp, sizeof(comp), 1, f); - map[i][j] = comp & 0x1f; - map_item[i][j] = (comp >> 5) & 3; - } - - if (save) - { - for (int i=bounds[0]; i<=bounds[1]; i++) - for (int j=bounds[2]; j<=bounds[3]; j++) - if (special[i][j]) - { - int16_t len = strlen(special[i][j]); - unsigned char x=i, y=j; - fn(&x, sizeof(x), 1, f); - fn(&y, sizeof(y), 1, f); - len = SWAP16(len); - fn(&len, sizeof(len), 1, f); - len = SWAP16(len); - fn(special[i][j], 1, len, f); - } - } - else - { - while(1){ - int16_t len; - unsigned char x, y; - if (!fn(&x, sizeof(x), 1, f)) - break; - fn(&y, sizeof(y), 1, f); - x += offsetx; y += offsety; - fn(&len, sizeof(len), 1, f); - len = SWAP16(len); - if (len<0) break; - char* tmp = new char[len+1]; - tmp[len] = 0; - fn(tmp, 1, len, f); - - SetSpecial(Pos(x,y), tmp, true, false); - } - } - } - else - return; // Unsupported version! - - ResetLevel(); - - // Save when returning to map! - if (isMap) - { - progress.general.completionPercentage = numComplete*100/numLevels; - progress.general.masteredPercentage = numMastered*100/numLevels; - LoadSaveProgress(true); - } - } - - void SetTile(Pos const & p, Tile t, bool updateRenderer=true, bool undoBuffer=true) - { - if (p.x<0 || p.x>MAP_SIZE) - return; - if (p.y<0 || p.y>MAP_SIZE) - return; - if (map[p.x][p.y] == t) - return; - if (map[p.x][p.y] == t) - return; - - tileCount[map[p.x][p.y]]--; - tileCount[t]++; - - if (undoBuffer) - undo[numUndo].Add(Undo::TileChange(p,GetTile(p),GetItem(p))); - - map[p.x][p.y] = t; - - if (updateRenderer) - renderer(p).Add(new TileRender(t, p), time); - } - - Tile GetTile(Pos const & p) - { - if (p.x<0 || p.x>=MAP_SIZE) - return EMPTY; - if (p.y<0 || p.y>=MAP_SIZE) - return EMPTY; - return map[p.x][p.y]; - } - - int GetHeight(Pos const & p) - { - return tileSolid[GetTile(p)]==1; - } - - char* GetSpecial(Pos const & p) - { - if (p.x<0 || p.x>=MAP_SIZE) - return NULL; - if (p.y<0 || p.y>=MAP_SIZE) - return NULL; - return special[p.x][p.y]; - } - - void SetSpecial(Pos const & p, char * d, bool use_pointer=false, bool auto_activate=true) - { - if (p.x<0 || p.x>=MAP_SIZE || p.y<0 || p.y>=MAP_SIZE) - { - if (use_pointer) - delete [] d; - return; - } - - delete [] special[p.x][p.y]; - if (!use_pointer && d) - { - - special[p.x][p.y] = new char [strlen(d) + 1]; - strcpy(special[p.x][p.y], d); - } - else - special[p.x][p.y] = d; - - if (special[p.x][p.y]==0) - renderer(p,true).Add(new ItemRender(GetItem(p), GetTile(p)==EMPTY, p), time); - else if (auto_activate) - ActivateSpecial(p, 0); - } - - int GetLevelState(Pos const & p, int recurse=0) - { - char* x = GetSpecial(p); - if (!x) return 0; - - LevelSave* l = progress.GetLevel(x, false); - - int t = 1; - - if (strcmp(x, STARTING_LEVEL)==0) - t = 2; - if (x[0]=='_' && l && l->unlocked) - t=3; - - if (l && l->Completed()) - { - t = 3; - - if (recurse) - return t; - - int par = GetPar(x); - if (progress.general.scoringOn && l->PassesPar( par )) - t = l->BeatsPar( par ) ? 40 : 4; - } - if (recurse) - return t; - - int adj=0; - for (Dir d=0; d1 || i==1 && t>1) - if (i>=1 && t>2 || t>=1 && i>2) - { - adj |= 1<=MAP_SIZE || p.y<0 || p.y>=MAP_SIZE) - return; - - char * x = special[p.x][p.y]; - - if (x==0 || x[0]==0) - return; - - if (type==2 && x[0]=='_') // Phase2 init - unlock - { - int t = GetLevelState(p); - int target = atoi(x+1), targetM = 0; - if (target>1000) targetM=target=target-100; - if (t > 1 && numComplete >= target && numMastered >= targetM) - { - LevelSave* l = progress.GetLevel(x, true); - if (!l->unlocked) - { - l->unlocked = true; - - renderer(p, true).Add(new LevelSelectRender(p, 5, GetLevelState(p)>>8), time+0.01); - renderer().Add(new ExplosionRender(p, 0), time + 0.6); - renderer().Add(new ExplosionRender(p, 1), time + 1.1); - renderer(p, true).Add(new LevelSelectRender(p, -1, GetLevelState(p)>>8), time + 1.1); - } - } - } - - if (type==0) // Init & count levels - { - if (x[0]=='_') - { - int t = GetLevelState(p); - int unlock = progress.GetLevel(x, true)->unlocked; - LevelSelectRender* lsr = new LevelSelectRender( p, unlock ? -1 : (t>>8) ? 5 : 1, t>>8 ); - if ((t>>8) && p.x > mapRightBound) mapRightBound = p.x; - #ifdef MAP_EDIT_HACKS - lsr->magic = -atoi(x+1); - SetTile(p, LIFT_DOWN, true, false); - #else - SetTile(p, EMPTY, true, false); - #endif - renderer(p,true).Add(lsr, time); - } - else - { - //printf("Level: %s\n", x); - - int t = GetLevelState(p); - numLevels++; - if (t && !GetItem(p)) - { - if (!isMap) - { - isMap = true; - mapRightBound = 0; - } - currentLevelInfo = 0; - - if ((t&0xff)>=2) - { - LevelSave* l = progress.GetLevel(x, true); - if (!l->unlocked) - { - l->unlocked = true; - - renderer(p, true).Add(new LevelSelectRender(p, -1, 0), time+0.01); - renderer().Add(new ExplosionRender(p, 0), time + 0.6); - renderer(p, true).Add(new LevelSelectRender(p, t & 0xff, t>>8), time + 0.6); - } - - numLevelsFound++; - if (p.x > mapRightBound) mapRightBound = p.x; - } - if ((t&0xff)>=3) - numComplete++; - if ((t&0xff)>=4) - numMastered++; - - LevelSelectRender* lsr = new LevelSelectRender( p, t & 0xff, t>>8 ); - - #ifdef MAP_EDIT_HACKS - lsr->magic = 0; - int t = GetAutoTile(x, true); - int v = GetAutoTile(x, false); - if (MAP_EDIT_HACKS_DISPLAY_UNLOCK) - lsr->magic = v; - else - lsr->magic = GetPar(x, true); - t = 1; - SetTile(p, t, true, false); - #else - SetTile(p, EMPTY, true, false); - #endif - - renderer(p,true).Add(lsr, time); - } - } - } - - if (type==1 && x[0]!='_') // Clicked on - { - int t = GetLevelState(p); - if (t>1) - { - LoadSave(x, false); - } - } - } - - void SetItem(Pos const & p, int t, bool updateRenderer=true, bool undoBuffer=true) - { - if (p.x<0 || p.x>MAP_SIZE) - return; - if (p.y<0 || p.y>MAP_SIZE) - return; - if (map_item[p.x][p.y] == t) - return; - - if (undoBuffer) - undo[numUndo].Add(Undo::TileChange(p,GetTile(p),GetItem(p))); - - map_item[p.x][p.y] = t; - - if (updateRenderer) - renderer(p,true).Add(new ItemRender(t, GetTile(p)==EMPTY, p), time); - } - - Tile GetItem(Pos const & p) - { - if (p.x<0 || p.x>=MAP_SIZE) - return EMPTY; - if (p.y<0 || p.y>=MAP_SIZE) - return EMPTY; - return map_item[p.x][p.y]; - } - - void LoadSaveProgress(bool save) - { - FILE* f = file_open(currentSlot, save ? "wb" : "rb"); - if (f) - { - progress.LoadSave(f, save); - fclose(f); - } - else - { - if (!save) - progress.Clear(); - } - } - void LoadProgress() - { - LoadSaveProgress(false); - } - void SaveProgress() - { - LoadSaveProgress(true); - } - - SDL_Surface* Load(const char * bmp, bool colourKey=true) - { - typedef unsigned int uint32; - uint32* tmp = 0; - - SDL_Surface * g = 0; - -#ifdef EDIT - if (strstr(bmp, ".bmp")) - { - g = SDL_LoadBMP(bmp); - - char out[1024]; - strcpy(out, bmp); - strcpy(strstr(out, ".bmp"), ".dat"); - -// SDL_PixelFormat p; -// p.sf = 1; -// SDL_Surface* tmp = SDL_ConvertSurface(g, &p, SDL_SWSURFACE); - - short w=g->w, h=g->h; - char* buf = (char*) g->pixels; - if (colourKey) - { - while (IsEmpty(g, w-1, 0, 1, h) && w>1) - w--; - while (IsEmpty(g, 0, h-1, w, 1) && h>1) - h--; - } - - FILE* f = file_open(out, "wb"); - fwrite(&w, sizeof(w), 1, f); - fwrite(&h, sizeof(h), 1, f); - - uint32 mask = IMAGE_DAT_OR_MASK; - for (int i=0; i<(int)w*h; ) - { - uint32 c = (*(uint32*)&buf[(i%w)*3 + (i/w)*g->pitch] | mask); - int i0 = i; - while (i < (int)w*h && c == (*(uint32*)&buf[(i%w)*3 + (i/w)*g->pitch] | mask)) - i++; - c &= 0xffffff; - i0 = i-i0-1; - if (i0 < 0xff) - c |= i0 << 24; - else - c |= 0xff000000; - - fwrite(&c, sizeof(c), 1, f); - - if (i0 >= 0xff) - fwrite(&i0, sizeof(i0), 1, f); - } - fclose(f); - - SDL_FreeSurface(g); - - bmp = out; - } -#endif - - FILE* f = file_open(bmp, "rb"); - if (!f) FATAL("Unable to open file", bmp); - - int16_t w,h; - fread(&w, sizeof(w), 1, f); - fread(&h, sizeof(h), 1, f); - w = SWAP16(w); - h = SWAP16(h); - if (w>1500 || h>1500 || w<=0 || h<=0) FATAL("Invalid file", bmp); - - tmp = new uint32[(int)w*h]; - - uint32 c = 0; - uint32 cnt = 0; - for (int p=0; p<(int)w*h; p++) - { - if (cnt) - cnt -= 0x1; - else - { - fread(&c, sizeof(c), 1, f); - c = SWAP32(c); - cnt = c >> 24; - if (cnt==255) { - fread(&cnt, sizeof(cnt), 1, f); - cnt = SWAP32(cnt); - } - } - tmp[p] = c | 0xff000000; - } - - g = SDL_CreateRGBSurfaceFrom(tmp, w, h, 32, w*4, - 0xff0000, - 0xff00, - 0xff, - 0xff000000 ); - - fclose(f); - - - if (!g) FATAL("Unable to create SDL surface"); - if (colourKey) - SDL_SetColorKey(g, SDL_SRCCOLORKEY, SDL_MapRGB(g->format, WATER_COLOUR)); - SDL_Surface * out = SDL_DisplayFormat(g); - SDL_FreeSurface(g); - delete [] tmp; - if (!out) FATAL("Unable to create SDL surface (SDL_DisplayFormat)"); - return out; - } - - #ifdef USE_LEVEL_PACKFILE - PackFile1 levelFiles; - #endif - HexPuzzle() - { - SDL_WM_SetCaption(GAMENAME, 0); - - time = 0; - - #ifdef USE_LEVEL_PACKFILE - FILE* f = file_open("levels.dat", "rb"); - if (!f) - FATAL("Unable to open file", "levels.dat"); - levelFiles.Read(f); - fclose(f); - #endif - - LoadGraphics(); - - isMap = false; - editMode = false; - - currentLevelInfo = 0; - - editTile = 0; - levelPar = 0; - levelDiff = 5; - turboAnim = 0; - - memset(map, 0, sizeof(map)); - memset(map_item, 0, sizeof(map_item)); - memset(special, 0, sizeof(special)); - - LoadProgress(); - -// player = Pos(1,11); - -// ResetLevel(); - - LoadMap(); - } - - void LoadMap() - { - #ifndef EDIT - progress.GetLevel(STARTING_LEVEL, true)->unlocked = 1; - if (!progress.GetLevel(STARTING_LEVEL, true)->Completed()) - { - LoadSave(STARTING_LEVEL, false); - return; - } - #endif - - //editMode = false; - LoadSave(mapname, false); - } - - void Render() - { - if (!activeMenu || activeMenu->renderBG) - { - SDL_Rect src = {0,0,screen->w,screen->h}; - SDL_Rect dst = {0,0,screen->w,screen->h}; - if (isRenderMap) - { - int boundW = mapBG->w; - #ifndef EDIT - boundW = MIN(boundW, (mapRightBound+4) * TILE_W2 - TILE_W1); - #endif - src.x = scrollX - initScrollX; - if (src.x+src.w > boundW) - { - int diff = src.x+src.w - boundW; - src.x -= diff; - if (isMap) - scrollX -= diff; - } - if (src.x < 0) - { - if (isMap) - scrollX -= src.x; - src.x = 0; - } - //scrollY = initScrollY; - - if (isMap) - mapScrollX = scrollX; - - SDL_BlitSurface(mapBG, &src, screen, &dst); - } - else - SDL_BlitSurface(gradient, &src, screen, &dst); - - renderer.Render(time, true); - - if (!hintsDone && !isFadeRendering) - { - DoHints(); - } - - if (1) - { - SDL_Rect src = {0,SCREEN_H-1,SCREEN_W,1}; - SDL_Rect dst = {0,SCREEN_H-1,SCREEN_W,1}; - for (int i=0; i 1 && time < undo[numUndo-2].endTime) - { - int i = numUndo-1; - while (i>1 && time1 && time < undo[0].time) - v[0]=v[1]=v[2]=0; - #ifdef EDIT - /* TRANSLATORS: Anti-Ice are pickups, which turn ice plates into solid - plates once you step on them. Each pickup changes one ice plate */ - Print(0,0,_("Anti-Ice: %d"), v[0]); - Print(0,FONT_SPACING,_("Jumps: %d"), v[1]); - Print(0,FONT_SPACING*2,_("Score: %d (%d)"), v[2], player_score); - /* TRANSLATORS: Par is similar to golf, a pre defined score which you - can attempt to beat */ - Print(0,FONT_SPACING*3,_("Par: %d"), levelPar); - Print(0,FONT_SPACING*4,_("Diff: %d"), levelDiff); - #else - if (showScoring) - Print(0, SCREEN_H-FONT_SPACING, _(" Par: %d Current: %d"), levelPar, v[2]); - - if (v[0]) - Print(0,0,_(" Anti-Ice: %d"), v[0]); - else if (v[1]) - Print(0,0,_(" Jumps: %d"), v[1]); - #endif - } - if (isRenderMap && isMap && !isFadeRendering) - { - #if 0//def EDIT - Print(0,0,_("Points: %d"), numComplete+numMastered); - Print(0,FONT_SPACING,_("Discovered: %d%% (%d/%d)"), numLevelsFound*100/numLevels, numLevelsFound, numLevels); - Print(0,FONT_SPACING*2,_("Complete: %d%% (%d)"), numComplete*100/numLevels, numComplete); - Print(0,FONT_SPACING*3,_("Mastered: %d%% (%d)"), numMastered*100/numLevels, numMastered); - #else - if (numComplete==numLevels && progress.general.endSequence>0) - Print(0, SCREEN_H-FONT_SPACING, _(" %d%% Mastered"), numMastered*100/numLevels); - else - Print(0, SCREEN_H-FONT_SPACING, _(" %d%% Complete"), numComplete*100/numLevels); - - if (numMastered >= numLevels && progress.general.endSequence < 2) - { - progress.general.endSequence = 2; - LoadSaveProgress(true); - - new Fader(-1, -7, 0.3); - } - if (numComplete >= numLevels && progress.general.endSequence < 1) - { - progress.general.endSequence = 1; - LoadSaveProgress(true); - - new Fader(-1, -5, 0.3); - } - #endif - } - if ((currentLevelInfo || noMouse) && isMap && isRenderMap && !activeMenu && isFadeRendering<=0) - { - Pos p; - if (noMouse) - p = keyboardp; - else - p = mousep; - int pad = SCREEN_W/80; - // SDL_Rect src = {0, 0, uiGraphics->w, uiGraphics->h}; - SDL_Rect dst = {pad, SCREEN_H-TILE_H2-pad, 0, 0}; - // dst.x = p.getScreenX() + TILE_W3/2 - scrollX; - // dst.y = p.getScreenY() - src.h/2 - scrollY; - dst.x = p.getScreenX() - scrollX; - dst.y = p.getScreenY() - scrollY - FONT_SPACING*3 - FONT_SPACING/2; - // if (dst.x > SCREEN_W*2/3) dst.x -= TILE_W3 + src.w; - // if (dst.y+src.h > screen->h-pad) dst.y = screen->h-pad - src.h; - - RenderTile(false, 0, p.getScreenX(), p.getScreenY()); - // SDL_BlitSurface(uiGraphics, &src, screen, &dst); - - // dst.x += src.w/2; - - if (currentLevelInfo) - { - keyboardp = p; - - PrintC(true, dst.x, dst.y - FONT_SPACING/4, currentLevelInfo->name); - - if (currentLevelInfo->file[0]!=0) - { - if (player_score > 0) - { - if (progress.general.scoringOn) - { - PrintC(false, dst.x, dst.y + FONT_SPACING*4 - FONT_SPACING/4, _("Best:% 3d"), player_score); - PrintC(false, dst.x, dst.y + FONT_SPACING*5 - FONT_SPACING/4, _("Par:% 3d"), levelPar); - } - else - PrintC(false, dst.x, dst.y + FONT_SPACING*4 - FONT_SPACING/4, _("Completed"), player_score); - } - else - PrintC(false, dst.x, dst.y + FONT_SPACING*4 - FONT_SPACING/4, _("Incomplete"), player_score); - } - } - } - - // "Win" - if (win && numUndo > 0 && time > undo[numUndo-1].endTime + 2) - { - if (currentFile[0] && winFinal==0) - { - LevelSave* l = progress.GetLevel(currentFile, true); - - new WinLoseScreen(true, player_score, showScoring ? levelPar : 0, l && showScoring && l->Completed() ? l->GetScore() : 0); - - if (l->IsNewCompletionBetter(player_score)) - { - l->SetScore(player_score); - - l->SetSolution(numUndo); - - for (int i=0; iSetSolutionStep(i, undo[i].playerMovement); - } - - SaveProgress(); - } - - winFinal = 1; - } - else - winFinal = 0; - - // Move up "level complete" writing so it doesn't feel like everything's ground to a halt... - if (win && numUndo > 0 && time > undo[numUndo-1].endTime && !winFinal) - { - double t = (time - undo[numUndo-1].endTime) / 2; - t=1-t; - t*=t*t; - t=1-t; - int y = SCREEN_H/3 - FONT_SPACING + 1; - y = SCREEN_H + int((y-SCREEN_H)*t); - PrintC(true, SCREEN_W/2, y, _("Level Complete!")); - } - } - - if (activeMenu) - activeMenu->Render(); - - if (!noMouse) - { - // Edit cursor - if (editMode) - { - RenderTile(false, editTile, mousex+scrollX, mousey+scrollY); - } - } - } - - int Count(Tile t) - { - return tileCount[t]; - } - int Swap(Tile t, Tile t2) - { - const int num = Count(t) + Count(t2); - if (t==t2 || num==0) - return Count(t); // Nothing to do... - - int count=0; - for (int x=0; x=2) - { - LevelSave* l = progress.GetLevel(sp, true); - if (l) - { - currentLevelInfo = GetLevelInfo(sp); - levelPar = GetPar(sp); - player_score = l->GetScore(); - } - } - } - -#ifdef EDIT - sprintf(tmp, _("Special(%d,%d): %s (%d)"), s.x, s.y, sp ? sp : _(""), GetPar(sp)); - SDL_WM_SetCaption(tmp, NULL); -#endif - } - else if (currentFile[0]) - { -#ifdef EDIT - SDL_WM_SetCaption(currentFile, NULL); -#endif - if (isMap) - currentLevelInfo = 0; - } - } - } - - virtual void Mouse(int x, int y, int dx, int dy, int button_pressed, int button_released, int button_held) - { - if (activeMenu) - { - activeMenu->Mouse(x,y,dx,dy,button_pressed,button_released,button_held); - return; - } - - if (isFadeRendering) - return; - - -#ifndef EDIT - if (button_pressed==2 || button_pressed==4 && isMap) - { - KeyPressed(SDLK_ESCAPE, 0); - keyState[SDLK_ESCAPE] = 0; - return; - } -#endif - - x += scrollX; - y += scrollY; - - Pos s = Pos::GetFromWorld(x,y); - if (tileSolid[GetTile(Pos::GetFromWorld(x,y+TILE_HUP))] == 1) - s = Pos::GetFromWorld(x,y+TILE_HUP); - - mousep = s; - - UpdateCursor(s); - -#ifdef EDIT - if (button_held & ~button_pressed & 4) - { - scrollX -= dx; - scrollY -= dy; - } -#endif - - if (!editMode) - { - if (isMap && (button_pressed & 1)) - { - ActivateSpecial(s, 1); - return; - } - if (!isMap && win && winFinal) - { - if (button_pressed & 1) - { - LoadMap(); - return; - } - } - if(!isMap) - { - if((button_pressed & 1) || (button_held & 1) && (numUndo==0 || time>=undo[numUndo-1].endTime)) - { - if(s.x==player.x && s.y==player.y) - { - // Don't activate jump powerup without a new click - if (button_pressed & 1) - Input(-1); - } - else if(s.x==player.x && s.yplayer.y) - Input(3); - else if(s.y==player.y && s.xplayer.x) - Input(2); - else if(s.y+s.x==player.y+player.x && s.x>player.x) - Input(1); - else if(s.y+s.x==player.y+player.x && s.x=0) - SetTile(s, editTile, true, false); - else - SetItem(s, editTile==-2 ? 0 : editTile==-1 ? 1 : 2, true, false); - } - - if (button_pressed==2) - { - editTile = GetTile(s); - } - - if (button_pressed==8) - { - editTile=editTile-1; - if (editTile<=0) editTile=NumTileTypes-1; - } - - if (button_pressed==16) - { - editTile=editTile+1; - if (editTile<=0) editTile=1; - if (editTile==NumTileTypes) editTile=0; - } - - if (button_pressed==64) - { - ResetUndo(); - player = s; - dead = false; - renderer.player.Reset(-1); - renderer.player.Add(new PlayerRender(player, GetHeight(player), dead), 0); - } - - if (button_pressed==256) - { - char* fn = LoadSaveDialog(false, true, _("Select level")); - if (fn) - { - char * l = strstr(fn, "Levels"); - if(l) - { - FILE * f = file_open(l,"rb"); - if (f) - fclose(f); - if (f) - SetSpecial(s, l); - else if (l[6]!=0 && l[7]=='_') - SetSpecial(s, l+7); - } - UpdateCursor(Pos(-1,-1)); - } - } - if (button_pressed==512) - { - SetSpecial(s, NULL); - UpdateCursor(Pos(-1,-1)); - } - if (button_pressed==1024) - { - static char x[1000] = ""; - if (!(s.x<0 || s.x>=MAP_SIZE || s.y<0 || s.y>=MAP_SIZE)) - { - char tmp[1000]; - strcpy(tmp, x); - if (GetSpecial(s)) - strcpy(x, GetSpecial(s)); - else - x[0] = 0; - SetSpecial(s, tmp[0] ? tmp : 0); - if (!tmp[0]) - SetTile(s, EMPTY, true, false); - } - } - - if (button_pressed==32) - { - editTile = editTile<0 ? 1 : -1; - } -#endif // EDIT - } - - void CheckFinished() - { - bool slow = false; - if (Count(COLLAPSABLE)==0) - { - if (Replace(COLLAPSE_DOOR, COLLAPSABLE) == 0) - win = true; - else - slow = true; - Replace(SWITCH, NORMAL); - } - else - win = false; - - if (Count(COLLAPSABLE2)==0) - if (Replace(COLLAPSE_DOOR2, COLLAPSABLE2)) - slow = true; - - if (slow) - time += BUILD_TIME; - } - bool Collide(Pos p, bool high) - { - Tile t = GetTile(p); -// switch(t) -// { -// default: - if (!high) - return tileSolid[t]==1; - else - return false; -// } - } - void Undo() - { - if (numUndo==0) return; - - UndoDone(); // Complete previous undo... - - numUndo--; - - if (time > undo[numUndo].endTime) - time = undo[numUndo].endTime; - undoTime = undo[numUndo].time; - - undo[numUndo].Restore(this); - } - void UndoDone() - { - if (undoTime < 0) - return; - renderer.Reset(undoTime); - time = undoTime; - undoTime = -1; - } - void ScoreDestroy(Pos p) - { - Tile t = GetTile(p); - if (t==COLLAPSABLE || t==COLLAPSE_DOOR) - {} - else if (t != EMPTY) - { - player_score += 10; - } - } - - bool LaserTile(Pos p, int mask, double fireTime) - { - if (&renderer(p) == &renderer(Pos(-1,-1))) - return false; - //if (!renderer.Visible(p)) - // return false; - - TileRender* tr = 0; - if (time <= renderer(p).GetLastTime()) - if (fireTime < renderer(p).GetLastTime()) - { - renderer(p).Add(tr = new TileRender(GetTile(p), p, mask), fireTime); - ((TileRender*)renderer(p).GetStage(time+10/*HACKY!*/))->special |= mask; - } - else - { - tr = new TileRender(GetTile(p), p, mask | ((TileRender*)renderer(p).GetStage(time+10/*HACKY!*/))->special); - renderer(p).Add(tr, fireTime); - } - else - renderer(p).Add(tr = new TileRender(GetTile(p), p, mask), fireTime); - - if (tr) - { - tr->specialDuration = time + LASER_LINE_TIME - fireTime + LASER_FADE_TIME; - } - return true; - } - void FireGun(Pos newpos, Dir d, bool recurse, double fireTime) - { - static Pos hits[100]; - static Dir hitDir[100]; - static unsigned int numHits=0; - if (!recurse) - numHits = 0; - - double starttime = fireTime; - for (Dir fd=((d<0)?0:d); fd<((d<0)?MAX_DIR:d+1); fd++) - { - fireTime = starttime; - // starttime += 0.03; - - Pos p = newpos + fd; - int range = 0; - for (; range= sizeof(hits)/sizeof(hits[0])) - return; - hitDir[i] = 1 << fd; - hits[i] = p; - numHits++; - } - else - { - hitDir[i] |= 1 << fd; - } - if (t==TRAP) - { - int dirmask = - 1<<((fd+2) % MAX_DIR) - | 1<<((fd+MAX_DIR-2) % MAX_DIR); - - if (LaserTile(p, dirmask, fireTime)) - fireTime += (time+LASER_LINE_TIME - fireTime) / 40; -// fireTime += LASER_SEGMENT_TIME; - - FireGun(p, (fd+1) % MAX_DIR, true, fireTime); - FireGun(p, (fd+MAX_DIR-1) % MAX_DIR, true, fireTime); - } - } - break; - } - else - { - LaserTile(p, 1<<(fd%3), fireTime); - - fireTime += (time+LASER_LINE_TIME - fireTime) / 40; -// fireTime += LASER_SEGMENT_TIME; - } - } - -// renderer().Add(new LaserRender(newpos, fd, range), time); - } - - if (!recurse) - { - //double _time = time; - time += LASER_LINE_TIME; - for (unsigned int i=0; ir; - } - bool Input(Dir d) - { - if (dead || win || isMap) - return false; - - // Complete undo - UndoDone(); - - // Jump forwards in time to last move finishing - if (numUndo > 0 && time < undo[numUndo-1].endTime) - time = undo[numUndo-1].endTime; - - double realTime = time; - double endAnimTime = time; - bool high = (tileSolid[GetTile(player)] == 1); - Pos playerStartPos = player; - Pos oldpos = player; - int oldPlayerHeight = GetHeight(oldpos); - Pos newpos = player + d; - - int playerRot = GetLastPlayerRot(); - if (d!=-1 && d!=playerRot) - { - while (d!=playerRot) - { - if ((d+6-playerRot) % MAX_DIR < MAX_DIR/2) - playerRot = (playerRot+1) % MAX_DIR; - else - playerRot = (playerRot+MAX_DIR-1) % MAX_DIR; - - time += 0.03; - - if (GetTile(oldpos) == FLOATING_BALL) - { - TileRender* t = new TileRender(FLOATING_BALL, oldpos); - t->special = playerRot + 256; - renderer(oldpos).Add(t, time); - - renderer.player.Add(new PlayerRender(playerRot, Pos(-20,-20), oldPlayerHeight, Pos(-20,-20), oldPlayerHeight, dead), time); - } - else - { - PlayerRender *p = new PlayerRender(playerRot, player, oldPlayerHeight, player, oldPlayerHeight, dead); - p->speed = 0; - renderer.player.Add(p, time); - } - } - - time += 0.03; - } - - if (d<0 && player_items[1]==0) - return false; - - if (d >= 0) - { - if (tileSolid[GetTile(newpos)] == -1) - { - time = realTime; - return false; - } - if (Collide(newpos, high)) - { - time = realTime; - return false; - } - } - - // Don't change any real state before this point! - if (numUndo >= MAX_UNDO) - { - numUndo--; - for(int i=0; ispecial = 0; - renderer(oldpos).Add(t, time); - } - - PlayerRender *p = new PlayerRender(playerRot, player, oldPlayerHeight, newpos, GetHeight(newpos), dead); - - // alternate leg (hacky!) - if (1) - { - static int l=0; - l++; - p->type = l & 1; - } - - if (retry_pos_count!=0 && GetTile(player)==TRAP) - { - p->speed /= 1.5; - p->type = 2; - } - if (d==-1) - p->speed = JUMP_TIME * 1.5; - renderer.player.Add(p, time); - endAnimTime = MAX(endAnimTime, time + p->speed+0.001); - time += p->speed; - - player = newpos; - - switch (GetTile(newpos)) - { - case COLLAPSABLE: - renderer(newpos).Add(new TileRender(TILE_GREEN_CRACKED, newpos), time); - break; - case COLLAPSE_DOOR: - renderer(newpos).Add(new TileRender(TILE_GREEN_CRACKED_WALL, newpos), time); - break; - case COLLAPSABLE2: - renderer(newpos).Add(new TileRender(TILE_BLUE_CRACKED, newpos), time); - break; - case COLLAPSE_DOOR2: - renderer(newpos).Add(new TileRender(TILE_BLUE_CRACKED_WALL, newpos), time); - break; - - case EMPTY: - dead = true; - break; - - case BUILDER: - { - double pretime = time; - bool done = false; - time += 0.15; - for (Dir fd=0; fdspecial = 512; - renderer(newpos).Add(r, time); - - PlayerRender* pr = new PlayerRender(playerRot, newpos, 0, newpos, 0, dead); - pr->speed = JUMP_TIME*1; - renderer.player.Add(pr, time); - - time += pr->speed; - - dead = 1; - break; - } - oldpos = newpos; - newpos = oldpos + d; - - SetTile(oldpos, EMPTY, false); - SetTile(newpos, FLOATING_BALL, false); - - renderer(oldpos).Add(new TileRotateRender(FLOATING_BALL, oldpos, d, 2), time); - renderer(oldpos).Add(new TileRender(EMPTY, oldpos), time + ROTATION_TIME/2); - renderer(newpos).Add(new TileRotateRender(FLOATING_BALL, newpos, (d+3)%MAX_DIR, 3), time + ROTATION_TIME/2); - -// PlayerRender *p = new PlayerRender(playerRot, oldpos, 0, newpos, 0, dead); -// p->speed = ROTATION_TIME*0.9; -// renderer.player.Add(p, time); - - endAnimTime = MAX(endAnimTime, time + ROTATION_TIME + ROTATION_TIME/2); - time += ROTATION_TIME; - } - player = newpos; -// renderer.player.Add(new PlayerRender(playerRot, player, 0, player, 0, 0), time); - if (dead) - { - } - else - { - TileRender* r = new TileRender(FLOATING_BALL, newpos); - r->special = playerRot + 256; - renderer(newpos).Add(r, time); - } - } - break; - - case LIFT_DOWN: - case LIFT_UP: - { - SetTile(newpos, GetTile(newpos)==LIFT_UP ? LIFT_DOWN : LIFT_UP, false); - renderer(newpos).Add(new TileRender(GetTile(newpos), newpos, 1), time); - - PlayerRender *p = new PlayerRender(playerRot, newpos, 1-GetHeight(newpos), newpos, GetHeight(newpos), dead); - renderer.player.Add(p, time); - endAnimTime = MAX(endAnimTime, time + JUMP_TIME); - } - break; - - case TRAMPOLINE: - if (d<0) break; - - oldpos = newpos; - if (Collide(newpos + d, high)) - break; - if (Collide((newpos + d) + d, high) == 1) - newpos = (newpos + d); - else - newpos = (newpos + d) + d; - if (tileSolid[GetTile(newpos)] == -1) - dead=1; - //player = newpos; - goto retry_pos; - - case SPINNER: - { - for (Dir d=0; dspeed = JUMP_TIME*1; - renderer.player.Add(pr, time); - - time += pr->speed; - dead = 1; - } - - /* - Pos hits[MAX_DIR]; - int numHits=0; - - for (Dir fd=((d<0)?0:d); fd<((d<0)?MAX_DIR:d+1); fd++) - { - Pos p = newpos + fd; - int range = 0; - for (range; rangespeed = 0; // Don't sit around before disappearing! - renderer.player.Add(pr, time); - - // If the tile we're drowning on isn't visible, give the ownership of the splash effect to the player, rather than a tile. - if (renderer.Visible(player)) - renderer(player).Add(new ExplosionRender(player, 0, 1), time); - else - renderer.player.Add(new ExplosionRender(player, 0, 1), time); - - endAnimTime = MAX(endAnimTime, time+2); - } - - time = realTime; - - player_score += 1; - - undo[numUndo].endTime = endAnimTime; - numUndo++; - - return true; - } - void Update(double timedelta) - { - while(deadMenu) - delete deadMenu; - - if (activeMenu) - { - activeMenu->Update(timedelta); - } - else - UpdateKeys(); - - for (int i=0; i SCREEN_W) xx = SCREEN_W; - int w = TILE_W2*4; - if (xx < w) - x = (double)xx / (w) - 1; - if (xx > SCREEN_W - w) - x = 1 - (double)(SCREEN_W-xx) / (w); - x *= 500; - if (x<-min || x>min) - { - scrollHi += timedelta * x; - scrollX += (int)scrollHi; - scrollHi -= (int)scrollHi; - } - } -#endif - } - if (undoTime>=0 && undoTime < time) - { - double acc = (time - undoTime) / 2; - if (acc < 3) acc = 3; - time -= timedelta * acc; - if (undoTime >= time) - UndoDone(); - } - else - { - time += timedelta; - if (turboAnim) - time += timedelta * 20; - } - } - void FileDrop(const char* filename) - { - LoadSave(filename, false); - } - void UpdateKeys() - { -#ifdef EDIT - if (keyState[SDLK_LALT] || keyState[SDLK_LCTRL]) - return; -#endif - - if (!isMap && !editMode && undoTime < 0) - { - if (keyState[SDLK_z] || keyState[SDLK_BACKSPACE] || keyState[SDLK_u]) - { - Undo(); - return; - } - } - if (isMap && !editMode) - { - - if ((keyState[SDLK_q] | keyState[SDLK_KP7]) & 2) keyboardp.x--; - else if ((keyState[SDLK_d] | keyState[SDLK_KP3]) & 2) keyboardp.x++; - else if ((keyState[SDLK_e] | keyState[SDLK_KP9]) & 2) keyboardp.x++, keyboardp.y--; - else if ((keyState[SDLK_a] | keyState[SDLK_KP1]) & 2) keyboardp.x--, keyboardp.y++; - else if ((keyState[SDLK_w] | keyState[SDLK_KP8] | keyState[SDLK_UP]) & 2) keyboardp.y--; - else if ((keyState[SDLK_s] | keyState[SDLK_KP2] | keyState[SDLK_DOWN]) & 2) keyboardp.y++; - else if ((keyState[SDLK_LEFT]) & 2) keyboardp.x--, keyboardp.y+=keyboardp.x&1; - else if (((keyState[SDLK_RIGHT]) & 2)) { if (keyboardp.x < mapRightBound) keyboardp.y-=keyboardp.x&1, keyboardp.x++; } - else if ((keyState[SDLK_RETURN] | keyState[SDLK_KP5] | keyState[SDLK_SPACE] | keyState[SDLK_KP_ENTER]) & 2) - { - // Simulate user clicking on it... - Mouse(keyboardp.getScreenX()-scrollX, keyboardp.getScreenY()-scrollY, 0, 0, 1, 0, 0); - noMouse = 1; - return; - } - else - { - if (noMouse) - UpdateCursor(keyboardp); - return; - } - int min[21] = { 17, 16, 15, 14, 13, 13, 13, 13, 13, 13, 12, 11, 11, 13, 12, 11, 8, 8, 7, 6, 7 }; - int max[21] = { 20, 20, 19, 19, 19, 19, 18, 21, 20, 20, 19, 19, 18, 18, 17, 16, 16, 16, 15, 15, 14 }; - if (keyboardp.x < 3) keyboardp.x = 3; - if (keyboardp.x > mapRightBound) keyboardp.x = mapRightBound; - - if (keyboardp.y < min[keyboardp.x-3]) keyboardp.y = min[keyboardp.x-3]; - if (keyboardp.y > max[keyboardp.x-3]) keyboardp.y = max[keyboardp.x-3]; - noMouse = 1; - UpdateCursor(keyboardp); - } - else if (!editMode && (numUndo==0 || time>=undo[numUndo-1].endTime)) - { - static int usedDiag = 0; - - if (keyState[SDLK_q] || keyState[SDLK_KP7]) HandleKey('q', 0); - else if (keyState[SDLK_w] || keyState[SDLK_KP8]) HandleKey('w', 0); - else if (keyState[SDLK_e] || keyState[SDLK_KP9]) HandleKey('e', 0); - else if (keyState[SDLK_a] || keyState[SDLK_KP1]) HandleKey('a', 0); - else if (keyState[SDLK_s] || keyState[SDLK_KP2]) HandleKey('s', 0); - else if (keyState[SDLK_d] || keyState[SDLK_KP3]) HandleKey('d', 0); - - else if (keyState[SDLK_UP] && keyState[SDLK_LEFT]) HandleKey('q', 0), usedDiag=1; - else if (keyState[SDLK_UP] && keyState[SDLK_RIGHT]) HandleKey('e', 0), usedDiag=1; - else if (keyState[SDLK_DOWN] && keyState[SDLK_LEFT]) HandleKey('a', 0), usedDiag=1; - else if (keyState[SDLK_DOWN] && keyState[SDLK_RIGHT]) HandleKey('d', 0), usedDiag=1; - else if (keyState[SDLK_UP] && !usedDiag) HandleKey('w', 0); - else if (keyState[SDLK_DOWN] && !usedDiag) HandleKey('s', 0); - - else usedDiag = 0; - } - } - void KeyReleased(int key) - { - keyState[key] = 0; - } - bool KeyPressed(int key, int mod) - { - keyState[key] = 2; - - if (activeMenu) - { - bool eat = activeMenu->KeyPressed(key, mod); - if (!activeMenu) - memset(keyState, 0, sizeof(keyState)); - return eat; - } - else - { - if ((key==SDLK_ESCAPE && (mod & KMOD_CTRL))) - { - if (mod & KMOD_SHIFT) - { - time = 0; - renderer.Reset(); - LoadSaveProgress(false); - } - - LoadMap(); - } - - if (isFadeRendering) - return false; - - return HandleKey(key, mod); - } - } - bool HandleKey(int key, int mod) - { - turboAnim = 0; - -#ifdef CHEAT - if (isMap && key=='r' && (mod & KMOD_ALT)) - { - progress.Clear(); - LoadMap(); - } -#endif - - if (0) {} - - else if ((key=='p' && !editMode || key==SDLK_PAUSE || key==SDLK_ESCAPE)) - { - noMouse = 1; - new PauseMenu(isMap, progress.GetLevel(STARTING_LEVEL, true)->Completed(), progress.general.endSequence>=1, progress.general.endSequence>=2); - } - -#ifdef EDIT - else if (key=='e' && (mod & KMOD_ALT)) - editMode = !editMode; - - else if (key=='p' && (mod & KMOD_ALT) && numUndo>0 - || key>='0' && key<='9' && (mod & KMOD_SHIFT) && !isMap) - { - if (key>='0' && key<='9') - levelDiff = (key=='0') ? 10 : key-'0'; - - if (key=='p' && levelPar==0) - levelPar = player_score; - - if (numUndo) - { - do - undo[numUndo-1].Restore(this); - while (--numUndo); - } - time = 0; - if (LoadSave(currentFile, true)) - { - if (key>='0' && key<='9') - LoadMap(); - } - } -#endif - - ///////////////////////////////////////////////////////////////////////// - if (isMap && !editMode) - return false; - - else if (key==SDLK_KP9 || key=='e') Input(1), noMouse=1; - else if (key==SDLK_KP3 || key=='d') Input(2), noMouse=1; - else if (key==SDLK_KP1 || key=='a') Input(4), noMouse=1; - else if (key==SDLK_KP7 || key=='q') Input(5), noMouse=1; - else if (key==SDLK_KP8 || key=='w') Input(0), noMouse=1; - else if (key==SDLK_KP2 || (key=='s' && (((mod & (KMOD_CTRL|KMOD_ALT))==0)||!editMode))) Input(3), noMouse=1; - else if (key==SDLK_KP5 || key==SDLK_SPACE || key==SDLK_RETURN || key==SDLK_KP_ENTER) - { - noMouse=1; - if (win && winFinal) - LoadMap(), memset(keyState, 0, sizeof(keyState)); - else - Input(-1); - } - - else if (key=='r' && (mod & KMOD_CTRL)) - LoadSave(currentFile, false); - -#ifdef EDIT - else if (key=='z' && (mod & KMOD_ALT)) - { - if (numUndo>0 && !isMap) - { - time = undo[numUndo-1].endTime; - undoTime = undo[0].time; - - do - undo[numUndo-1].Restore(this); - while (--numUndo); - } - } -#endif - else if (key=='z' || key==SDLK_BACKSPACE || key==SDLK_DELETE || key=='u') - { - if (!isMap) - Undo(); - } - -#ifdef EDIT - else if (key=='s' && (mod & KMOD_ALT)){ - if (win && strlen(currentFile)>0 && !isMap) - { - char tmp[1000]; - strcpy(tmp, currentFile); - ChangeSuffix(tmp, "sol"); - FILE* f = file_open(tmp, "wb"); - if (f) - { - for (int i=0; iCompleted()) - { - for (int i=0; ibestSolutionLength; i++) - Input(l->bestSolution[i]); - time = 0; - } - if (!win && l) - l->Clear(); - } - else - { - char tmp[1000]; - strcpy(tmp, currentFile); - ChangeSuffix(tmp, "sol"); - FILE* f = file_open(tmp, "rb"); - if (f) - { - int dir; - while ((dir = fgetc(f)) != -1) - { - if (dir==0xff) - dir = -1; - Input(dir); - } - time = 0; - fclose(f); - - if (!win) - remove(tmp); - } - } - } - } -#endif - -#ifdef EDIT - else if (!editMode) - return false; - - else if (key>='0' && key<='9' && (mod & KMOD_ALT) && !isMap) - levelPar = levelPar*10 + key-'0'; - else if (key==SDLK_BACKSPACE && (mod & KMOD_ALT) && !isMap) - levelPar /= 10; - - else if (key=='i') - Mouse(mousex, mousey, 0, 0, 32, 0, mouse_buttons); - else if (key=='p' && !(mod & KMOD_ALT)) - Mouse(mousex, mousey, 0, 0, 64, 0, mouse_buttons); - else if (key=='x') - Mouse(mousex, mousey, 0, 0, 128, 0, mouse_buttons); - else if (key==SDLK_RETURN) - Mouse(mousex, mousey, 0, 0, 256, 0, mouse_buttons); - else if (key==SDLK_BACKSPACE) - Mouse(mousex, mousey, 0, 0, 512, 0, mouse_buttons); - else if (key=='c') - Mouse(mousex, mousey, 0, 0, 1024, 0, mouse_buttons); - - else if (key=='s' && (mod & KMOD_CTRL)){ - char *fn = LoadSaveDialog(true, true, _("Save level")); - LoadSave(fn, true); - SDL_WM_SetCaption(currentFile, NULL); - } - - else if (key=='o' && (mod & KMOD_CTRL)){ - char* fn = LoadSaveDialog(false, true, _("Open level")); - LoadSave(fn, false); - SDL_WM_SetCaption(currentFile, NULL); - } -#endif - - else - return false; - - return true; - } - void LoadGraphics() - { - #define X(NAME,FILE,ALPHA) NAME = Load(DATA_DIR "/graphics/" FILE BMP_SUFFIX, ALPHA); - #include "gfx_list.h" - - static int first = 1; - if (first) - { - first = false; - MakeTileInfo(); - } - - // unsigned int d = { - - // }; - // static SDL_Cursor * c = SDL_CreateCursor(data, mask, 32, 32, 1, 1); - // SDL_SetCursor(c); - SDL_ShowCursor(1); - } - void FreeGraphics() - { - #define X(NAME,FILE,ALPHA) if (NAME) SDL_FreeSurface(NAME), NAME=0; - #include "gfx_list.h" - } - virtual void ScreenModeChanged() - { -// FreeGraphics(); -// LoadGraphics(); - } -}; - -MAKE_STATE(HexPuzzle, SDLK_F1, false); - -char * HexPuzzle::loadPtr = 0; -char * HexPuzzle::endLoad = 0; - -#endif //USE_OPENGL +/* + 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 +#include +#include // TODO: remove it later +#include +#include + +////////////////////////////////////////////////////// +// Config + + +#ifdef _DEBUG +#define EDIT +#endif + +//#define MAP_LOCKED_VISIBLE + +#ifndef GAME_NAME +#define GAME_NAME "Hex-a-hop" +#endif + +#ifndef DATA_DIR +#define DATA_DIR "." +#endif + +#ifdef EDIT +// #define MAP_EDIT_HACKS + #define MAP_EDIT_HACKS_DISPLAY_UNLOCK 0 + #define CHEAT + #define BMP_SUFFIX ".bmp" +#else + #define USE_LEVEL_PACKFILE + #define BMP_SUFFIX ".dat" +#endif + + + +#ifdef EDIT +#define GAMENAME GAME_NAME " (EDIT MODE)" +#endif +#ifndef GAMENAME +#define GAMENAME GAME_NAME +#endif + +#define IMAGE_DAT_OR_MASK 0xff030303 // Reduce colour depth of images slightly for better compression (and remove useless top 8 bits!) +#define STARTING_LEVEL "Levels\\0_green\\triangular.lev" +#define UNLOCK_SCORING 75 +const char * mapname = "Levels\\map_maybe\\map.lev"; + +////////////////////////////////////////////////////// + + + +#ifndef USE_OPENGL + +#include "state.h" + +#include "tiletypes.h" + +#ifdef USE_LEVEL_PACKFILE +#include "packfile.h" +#endif + +#include +#include +#include +#include + +#ifndef PATH_MAX +#define PATH_MAX 4096 +#endif + +void RenderTile(bool reflect, int t, int x, int y, int cliplift=-1); + +int keyState[SDLK_LAST] = {0}; + +FILE *file_open( const char *file, const char *flags ) +{ +// printf("file_open( \"%s\", \"%s\" )\n", file, flags ); + extern String base_path; + static String filename; // static to reduce memory alloc/free calls. + if (file[0]=='/') //If a full path is specified, don't prepend base_path + filename = ""; + else + { + if (strncmp(file, "save", 4) == 0) + { + const char *home = getenv("HOME"); + if (home) + { + char save_path[PATH_MAX]; + snprintf(save_path, sizeof(save_path), "%s/.hex-a-hop", home); + if (!strchr(flags, 'r')) + if (mkdir(save_path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != -1) + printf("Creating directory \"%s\"\n", (const char *)save_path); + strncat(save_path, "/", sizeof(save_path)); + filename = save_path; + } + else filename = "/tmp/"; + } + else filename = base_path; + } + filename += file; +// printf(" -> \"%s\"\n", filename ); + + filename.fix_backslashes(); + FILE* f = fopen( filename, flags ); + + if (!f && strncmp(file, "save", 4) != 0) + { + printf("Warning: unable to open file \"%s\" for %s\n", (const char*)filename, strchr(flags, 'r') ? "reading" : "writing"); + } + + return f; +} + + +#ifdef MAP_EDIT_HACKS + static const short value_order[]={ + //WALL, + //COLLAPSE_DOOR2, + //COLLAPSABLE3 + //SWITCH + //EMPTY, NORMAL, + + COLLAPSABLE, + TRAMPOLINE, + COLLAPSE_DOOR, COLLAPSABLE2, + GUN, + FLOATING_BALL, + SPINNER, + TRAP, + 0x100, + LIFT_DOWN, LIFT_UP, + BUILDER, + 0x200, + }; +#endif + +//#define PROGRESS_FILE "progress.dat" + +#define PI (3.1415926535897931) +#define PI2 (PI*2) +#define MAX(a,b) ((a)>(b) ? (a) : (b)) +#define MIN(a,b) ((a)<(b) ? (a) : (b)) +#define ABS(a) ((a)<0 ? -(a) : (a)) + +#define WATER_COLOUR 31 | (IMAGE_DAT_OR_MASK>>16)&255, 37 | (IMAGE_DAT_OR_MASK>>8)&255, 135 | (IMAGE_DAT_OR_MASK>>0)&255 + +#define ROTATION_TIME 0.25 +#define BUILD_TIME 1 +#define LASER_LINE_TIME 0.7 +#define LASER_FADE_TIME 0.1 +#define LASER_SEGMENT_TIME 0.01 +#define LIFT_TIME 0.5 +#define JUMP_TIME 0.4 + +#define X(NAME,FILE,ALPHA) SDL_Surface* NAME = 0; +#include "gfx_list.h" +int scrollX=0, scrollY=0, initScrollX=0, initScrollY=0; +int mapRightBound = 0; +int mapScrollX = 0; +bool showScoring = false; +bool hintsDone = false; + +enum { + TILE_SPLASH_1 = 17, + TILE_SPLASH_2, + TILE_SPLASH_3, + + TILE_SPHERE = 20, + TILE_SPHERE_OPEN, + TILE_SPHERE_DONE, + TILE_SPHERE_PERFECT, + TILE_LOCK, + + TILE_LIFT_BACK, + TILE_LIFT_FRONT, + TILE_LIFT_SHAFT, + TILE_BLUE_FRONT, + TILE_GREEN_FRONT, + + TILE_LINK_0 = 30, + TILE_LINK_1, + TILE_LINK_2, + TILE_LINK_3, + TILE_LINK_4, + TILE_LINK_5, + TILE_GREEN_FRAGMENT, + TILE_GREEN_FRAGMENT_1, + TILE_GREEN_FRAGMENT_2, + TILE_ITEM2, + + TILE_WATER_MAP = 40, + TILE_GREEN_CRACKED, + TILE_GREEN_CRACKED_WALL, + TILE_BLUE_CRACKED, + TILE_BLUE_CRACKED_WALL, + TILE_LASER_HEAD, + TILE_FIRE_PARTICLE_1, + TILE_FIRE_PARTICLE_2, + TILE_WATER_PARTICLE, + + TILE_LASER_0 = 50, + TILE_LASER_FADE_0 = 53, + TILE_BLUE_FRAGMENT = 56, + TILE_BLUE_FRAGMENT_1, + TILE_BLUE_FRAGMENT_2, + TILE_ITEM1, + TILE_LASER_REFRACT = 60, + TILE_ICE_LASER_REFRACT = TILE_LASER_REFRACT+6, + TILE_WHITE_TILE, + TILE_WHITE_WALL, + TILE_BLACK_TILE, + +}; + +const int colours[] = { + #define X(n,col, solid) col, + #include "tiletypes.h" +}; + +const int tileSolid[] = { + #define X(n,col, solid) solid, + #include "tiletypes.h" +}; + +void ChangeSuffix(char* filename, char* newsuffix) +{ + int len = strlen(filename); + int i = len-1; + while (i>=0 && filename[i]!='\\' && filename[i]!='.' && filename[i]!='/') + i--; + if (filename[i]=='.') + strcpy(filename+i+1, newsuffix); + else + { + strcat(filename, "."); + strcat(filename, newsuffix); + } +} + +bool isMap=false, isRenderMap=false; +int isFadeRendering=0; + +/* + |--| |--| TILE_W1 + |--------| TILE_W2 + |-----| TILE_WL + |-----------| TILE_W3 + + *-----* - - + / \ |TILE_H1 |TILE_H2 + / \ | | + * * - | + \ / | + \ / | + *-----* - + + WL = sqrt(h1*h1 + w1*w1) + wl**2 = h1**2 + w1**2 + + w1 = sin60.wL + +*/ + +#if 1 + #define TILE_W1 18 + #define TILE_W3 64 + #define GFX_SIZE TILE_W3 + #define TILE_W2 (TILE_W3-TILE_W1) + #define TILE_H1 TILE_W1 + #define TILE_HUP 22 //extra visible height of wall (used for determining whether a wall was clicked on) + #define TILE_H2 (TILE_H1*2) + #define TILE_WL (TILE_W2-TILE_W1) + #define TILE_H_LIFT_UP 26 + #define TILE_H_REFLECT_OFFSET 24 + #define TILE_HUP2 TILE_H_LIFT_UP // Displacement of object on top of wall + #define FONT_SPACING 25 + #define FONT_X_SPACING (-1) // -1 in order to try and overlap the black borders of adjacent characters +#else + #define TILE_WL 30 + #define TILE_W1 (TILE_WL/2) + #define TILE_W2 (TILE_W1+TILE_WL) + #define TILE_W3 (TILE_W1+TILE_W2) + #define TILE_H1 (TILE_WL*0.8660254037844386) + #define TILE_H2 (TILE_H1*2) +#endif + +#define MAX_DIR 6 + +SDL_Rect tile[2][70]; +short tileOffset[2][70][2]; +int Peek(SDL_Surface* i, int x, int y) +{ + if (x<0 || y<0 || x>=i->w || y>=i->h) + return 0; + unsigned int p=0; + const int BytesPerPixel = i->format->BytesPerPixel; + const int BitsPerPixel = i->format->BitsPerPixel; + if (BitsPerPixel==8) + p = ((unsigned char*)i->pixels)[i->pitch*y + x*BytesPerPixel]; + else if (BitsPerPixel==15 || BitsPerPixel==16) + p = *(short*)(((char*)i->pixels) + (i->pitch*y + x*BytesPerPixel)); + else if (BitsPerPixel==32) + p = *(unsigned int*)(((char*)i->pixels) + (i->pitch*y + x*BytesPerPixel)); + else if (BitsPerPixel==24) + p = (int)((unsigned char*)i->pixels)[i->pitch*y + x*BytesPerPixel] + | (int)((unsigned char*)i->pixels)[i->pitch*y + x*BytesPerPixel] << 8 + | (int)((unsigned char*)i->pixels)[i->pitch*y + x*BytesPerPixel] << 16; + + return p; +} +bool IsEmpty(SDL_Surface* im, int x, int y, int w, int h) +{ + for (int i=x; ih-1)) + return false; + return true; +} + +void MakeTileInfo() +{ + for (int i=0; i<140; i++) + { + SDL_Rect r = {(i%10)*GFX_SIZE, ((i/10)%7)*GFX_SIZE, GFX_SIZE, GFX_SIZE}; + short * outOffset = tileOffset[i/70][i%70]; + SDL_Surface * im = (i/70) ? tileGraphicsR : tileGraphics; + + outOffset[0] = outOffset[1] = 0; + + while (r.h>1 && IsEmpty(im, r.x, r.y, r.w, 1)) r.h--, r.y++, outOffset[1]++; + while (r.h>1 && IsEmpty(im, r.x, r.y+r.h-1, r.w, 1)) r.h--; + while (r.w>1 && IsEmpty(im, r.x, r.y, 1, r.h)) r.w--, r.x++, outOffset[0]++; + while (r.w>1 && IsEmpty(im, r.x+r.w-1, r.y, 1, r.h)) r.w--; + + tile[i/70][i%70] = r; + } +} + +void ConvertToUTF8(const std::string &text_locally_encoded, char *text_utf8, size_t text_utf8_length) +{ + // Is this portable? + size_t text_length = text_locally_encoded.length()+1; + errno = 0; + static const char *locale_enc = gettext_init.GetEncoding(); + iconv_t cd = iconv_open("UTF-8", locale_enc); + char *in_buf = const_cast(&text_locally_encoded[0]); + char *out_buf = &text_utf8[0]; + iconv(cd, &in_buf, &text_length, &out_buf, &text_utf8_length); + iconv_close(cd); + if (errno != 0) + std::cerr << "An error occurred recoding " << text_locally_encoded << " to UTF8" << std::endl; +} + +int SDLPangoTextWidth(const std::string &text_utf8); +void Print_Pango(int x, int y, const std::string &text_utf8); +void Print_Pango_Aligned(int x, int y, int width, const std::string &text_utf8, int align); + +/// Prints a left aligned string (a single line) beginning at (x,y) +// TODO: Check that the maximal text width is already set +void Print(int x, int y, const char * string, ...) +{ + va_list marker; + va_start( marker, string ); /* Initialize variable arguments. */ + + char tmp[1000], tmp_utf8[5000]; // FIXME: Check this limit + vsprintf((char*)tmp, string, marker); + + ConvertToUTF8(tmp, tmp_utf8, sizeof(tmp_utf8)/sizeof(char)); + Print_Pango(x, y, tmp_utf8); + + va_end( marker ); /* Reset variable arguments. */ +} + +/// Prints a string right aligned so that it ends at (x,y) +// TODO: Check that the maximal text width is already set +void PrintR(int x, int y, const char * string, ...) +{ + va_list marker; + va_start( marker, string ); /* Initialize variable arguments. */ + + char tmp[1000], tmp_utf8[5000]; // FIXME: Check this limit + vsprintf((char*)tmp, string, marker); + + ConvertToUTF8(tmp, tmp_utf8, sizeof(tmp_utf8)/sizeof(char)); + Print_Pango(x-SDLPangoTextWidth(tmp_utf8), y, tmp_utf8); + + va_end( marker ); /* Reset variable arguments. */ +} + +/** \brief Prints a string horizontally centered around (x,y) + * + * " " in the string is interpreted as linebreak +*/ +void Print_Aligned(bool split, int x, int y, int width, const char * string, int align) +{ + char tmp_utf8[5000]; // FIXME: Check this limit + + ConvertToUTF8(string, tmp_utf8, sizeof(tmp_utf8)/sizeof(char)); + + std::string msg(tmp_utf8); + while (split && msg.find(" ") != std::string::npos) + msg.replace(msg.find(" "), 2, "\n"); + + Print_Pango_Aligned(x, y, width, msg, align); +} + +void PrintC(bool split, int x, int y, const char * string, ...) +{ + va_list marker; + va_start( marker, string ); /* Initialize variable arguments. */ + + char tmp[1000]; // FIXME: Check this limit + vsprintf((char*)tmp, string, marker); + + va_end( marker ); /* Reset variable arguments. */ + + static bool print = true; // avoid flickering! + if (print) { + std::cerr << "Warning: don't know window width for message:\n" << tmp << "\n"; + for (unsigned int i=0; i TILE_H1) + if (x*TILE_H1 + (TILE_H2-y) * TILE_W1 < TILE_H1*TILE_W1) + tx--, ty++; + + return Pos(tx, ty); + } +}; +Pos mousep(0,0), keyboardp(4,20); + +class RenderObject; + +struct RenderStage +{ + virtual ~RenderStage() {} + virtual void Render(RenderObject* r, double time, bool reflect) = 0; + virtual int GetDepth(double /*time*/) { return 1; } +}; + +class RenderObject +{ + RenderStage** stage; + double* time; + int numStages; + int maxStages; + int currentStage; +public: + double seed; + double currentTime; +private: + + void Reserve() + { + if (maxStages <= numStages) + { + maxStages = maxStages ? maxStages*2 : 4; + stage = (RenderStage**) realloc(stage, sizeof(stage[0])*maxStages); + time = (double*) realloc(time, sizeof(time[0])*maxStages); + } + } +public: + RenderObject() : stage(0), time(0), numStages(0), maxStages(0), currentStage(0) + { + // TODO: use a random number with better range + // or maybe make seed an int or float... + seed = rand() / (double)RAND_MAX; + } + ~RenderObject() + { + free(stage); free(time); + } + bool Active(double t) + { + if (numStages==0) return false; + if (t < time[0]) return false; + return true; + } + void UpdateCurrent(double t) + { + if (currentStage >= numStages) currentStage = numStages-1; + if (currentStage < 0) currentStage = 0; + + while (currentStage>0 && time[currentStage]>t) + currentStage--; + while (currentStage0) + return stage[numStages-1]; + + if (!Active(t)) return 0; + UpdateCurrent(t); + return stage[currentStage]; + } + double GetLastTime() + { + return numStages>0 ? time[numStages-1] : -1; + } + void Render(double t, bool reflect) + { + if (!Active(t)) + return; + UpdateCurrent(t); + stage[currentStage]->Render(this, t - time[currentStage], reflect); + } + int GetDepth(double t) + { + if (!Active(t)) + return -1; + UpdateCurrent(t); + return stage[currentStage]->GetDepth(t - time[currentStage]); + } + void Reset(double t) + { + if (t<0) + numStages = currentStage = 0; + else + { + while (numStages > 0 && time[numStages-1] >= t) + numStages--; + if (currentStage > 0 && currentStage >= numStages) + currentStage = numStages - 1; + } + if (currentStage < 0) currentStage = 0; + } + void Wipe() + { + if (currentStage > 0 && numStages > 0) + { + memmove(&time[0], &time[currentStage], sizeof(time[0]) * (numStages-currentStage)); + memmove(&stage[0], &stage[currentStage], sizeof(stage[0]) * (numStages-currentStage)); + numStages -= currentStage; + currentStage = 0; + } + } + void Add(RenderStage* s, double t) + { + int i=0; + + if (currentStage=SIZE || p.y>=SIZE) return false; + if (p.x=x1-1) return false; + for (int j0=0; j0 scrollY+SCREEN_H+TILE_H1) break; + int i = j0&1; + int j = j0>>1; + j -= (x0-i)/2; + i += (x0-i)/2*2; + if (j>=SIZE) i+=(j+1-SIZE)*2, j=SIZE-1; + for (; i=0; i+=2, j--) + { + if (Pos(i,j)==p) + return true; + } + } + return false; + } + + void Render(double t, bool reflect) + { + dummy.Reset(-1); + + int playerDepth = player.GetDepth(t); + if (reflect) playerDepth-=4; + if (playerDepth<0) + player.Render(t, reflect); + + int x0 = (scrollX+TILE_W2) / TILE_W2; + int x1 = (scrollX+SCREEN_W+TILE_W3+TILE_W1) / TILE_W2; + x0 = MAX(x0, 0); + x1 = MIN(x1, SIZE); + for (int j0=0; j0 scrollY+SCREEN_H+TILE_H1) break; + int i = j0&1; + int j = j0>>1; + j -= (x0-i)/2; + i += (x0-i)/2*2; + if (j>=SIZE) i+=(j+1-SIZE)*2, j=SIZE-1; + for (; i=0; i+=2, j--) + { + for (int q=reflect?1:0; q!=2 && q!=-1; q += (reflect ? -1 : 1)) + if (tile[i][j][q].Active(t)) + { + tile[i][j][q].Render(t, reflect); + } + } + + if (playerDepth==j0 || j0==SIZE*3 && playerDepth>j0) + player.Render(t, reflect); + } + + for (int j=0; j=SIZE || p.y>=SIZE) + return dummy; + return tile[p.x][p.y][item ? 1 : 0]; + } +}; + +void RenderTile(bool reflect, int t, int x, int y, int cliplift) +{ + SDL_Rect src = tile[reflect][t]; + SDL_Rect dst = {x-scrollX-GFX_SIZE/2, y-scrollY-GFX_SIZE+TILE_H1, 0, 0}; + dst.x += tileOffset[reflect][t][0]; + dst.y += tileOffset[reflect][t][1]; + if (reflect) + dst.y += TILE_H_REFLECT_OFFSET; + if (cliplift==-1 || reflect) + { + // dst.w=src.w; dst.h=src.h; + // SDL_FillRect(screen, &dst, rand()); + SDL_BlitSurface(reflect ? tileGraphicsR : tileGraphics, &src, screen, &dst); + } + else + { + src.h -= cliplift; + if (src.h > TILE_W1) + { + src.h -= TILE_W1/2; + SDL_BlitSurface(tileGraphics, &src, screen, &dst); + src.y += src.h; + dst.y += src.h; + src.h = TILE_W1/2; + } + if (src.h > 0) + { + src.w -= TILE_W1*2, src.x += TILE_W1; + dst.x += TILE_W1; + SDL_BlitSurface(tileGraphics, &src, screen, &dst); + } + } +} +void RenderGirl(bool reflect, int r, int frame, int x, int y, int h) +{ + int sx = r * 64; + int sy = frame * 80*2; + if (reflect) + y += TILE_H_REFLECT_OFFSET+20+h, sy += 80; + else + y -= h; + SDL_Rect src = {sx, sy, 64, 80}; + SDL_Rect dst = {x-scrollX-32, y-scrollY-65, 0, 0}; + SDL_BlitSurface(girlGraphics, &src, screen, &dst); +} + +struct ItemRender : public RenderStage +{ + int item; + Pos p; + int water; + + ItemRender(int i2, int _water, Pos const & _p) : item(i2), p(_p), water(_water) + {} + + double Translate(double seed, double time) + { + double bob = time*2 + seed*PI2; + return sin(bob)*4; + } + + void Render(RenderObject* r, double time, bool reflect) + { + if (item==0) + return; + + int y = -5 + (int)Translate(r->seed, r->currentTime + time); + if (reflect) + y=-y; + if (!reflect && !water) + RenderTile( false, TILE_SPHERE, p.getScreenX(), p.getScreenY()); + RenderTile( + reflect, + item==1 ? TILE_ITEM1 : TILE_ITEM2, + p.getScreenX(), p.getScreenY()+y + ); + } +}; + +void RenderFade(double time, int dir, int seed) +{ + int ys=0; + srand(seed); + for(int x=rand()%22-11; x= b) + { + RenderTile(false, TILE_BLACK_TILE, x+scrollX, y+scrollY); + } + } + } +} + +struct FadeRender : public RenderStage +{ + int seed; + int dir; + FadeRender(int d=-1) : seed(rand()), dir(d) + { + isFadeRendering = d; + } + + void Render(RenderObject* /*r*/, double time, bool reflect) + { + if (reflect) return; + if (time > 0.5) + { + if (dir==1) dir=0, isFadeRendering=0; + return; + } + RenderFade(time, dir, seed); + } +}; + +struct ScrollRender : public RenderStage +{ + int x,y; + bool done; + ScrollRender(int a,int b) : x(a), y(b), done(false) {} + + void Render(RenderObject* /*r*/, double /*time*/, bool /*reflect*/) + { + if (done) return; + scrollX = x, scrollY = y; + isRenderMap = isMap; + done = true; + } +}; + +struct LevelSelectRender : public RenderStage +{ + Pos p; + int item; + int adj; +#ifdef MAP_EDIT_HACKS + int magic; +#endif + + LevelSelectRender(Pos const & _p, int i2, int adj) : p(_p), item(i2), adj(adj) + {} + + void Render(RenderObject* /*r*/, double /*time*/, bool reflect) + { + if (item==0) + return; + + #ifndef MAP_LOCKED_VISIBLE + if (item==1) return; + #endif + + if (!reflect && adj) + for (int i=0; i 1) + time = 1; + if (time < 0) + time = 0; + time = (3 - 2*time)*time*time; + if (t==LIFT_UP) + time = (3 - 2*time)*time*time; + if (t==LIFT_UP) + return (int)((TILE_H_LIFT_UP+4) * time); + else + return (int)((TILE_H_LIFT_UP-4) * time) + 4; +} + +struct TileRender : public RenderStage +{ + int special; + int t; + Pos p; + double specialDuration; + + TileRender(int i, Pos const & _p, int _special=0) : special(_special), t(i), p(_p), specialDuration(LASER_LINE_TIME) + {} + + void Render(RenderObject* r, double time, bool reflect) + { + if (t==0 && special==0) + return; + + if (special && (t==LIFT_UP || t==LIFT_DOWN) && time= specialDuration-LASER_FADE_TIME) + base = TILE_LASER_FADE_0; + + int foo=special; + for(int i=0; foo; foo>>=1, i++) + if (foo & 1) + RenderTile(reflect, base+i, p.getScreenX(), p.getScreenY()); + } + else if (t==FLOATING_BALL) + { + int y = int(1.8 * sin(r->seed*PI + time*4)); + if (special==512) + { + if (time > 2) return; + if (reflect) return; + srand(int(r->seed * 0xfff)); + for (int i=0; i<20 - int(time*10); i++) + { + int x = int((((rand() & 0xfff) - 0x800) / 10) * time); + int y = int((((rand() & 0xfff) - 0x800) / 10) * time); + RenderTile(true, 19 + ((i+int(time*5))&1)*10, p.getScreenX() + x, p.getScreenY() - 14 + y); + } + + if (time < 0.05) + RenderTile(true, 18, p.getScreenX(), p.getScreenY() - 14); + } + else if (special) + RenderBoat(reflect, int(special)&255, p.getScreenX(), p.getScreenY(), y); + else + RenderTile(reflect, t, p.getScreenX(), p.getScreenY() + (reflect ? -y : y)); + } + else if (t != EMPTY) + RenderTile(reflect, t, p.getScreenX(), p.getScreenY()); + } + static void RenderBoat(bool reflect, int d, int x, int y, int yo) + { + if (reflect) + RenderGirl(reflect, d, 0, x, y, -yo); + RenderTile(reflect, FLOATING_BALL, x, y+yo); + if (!reflect) + { + RenderGirl(reflect, d, 0, x, y, -yo); + RenderTile(true, 17, x, y+yo-TILE_H_REFLECT_OFFSET); + } + } +}; + +struct TileRotateRender : public TileRender +{ + Dir d; +// int range; + int mode; + TileRotateRender(int i, Pos const & p, Dir _d, int m) : TileRender(i, p), d(_d), mode(m) + {} + void Render(RenderObject* r, double time, bool reflect) + { + if (t==0) + return; + double f = time / ROTATION_TIME; + + if (mode & 1) f += 0.5; + if (f<1 && f>0) + { + if (mode & 2) + ; + else + f = (3-2*f)*f*f; + } + + if (mode & 1) f=1-f; else f=f; + if (f<0) f=0; + + if (f >= 1) + TileRender::Render(r, time, reflect); + else + { + Pos dd = (Pos(0,0)+d); + int x = p.getScreenX() + int(dd.getScreenX()*(f)); + int y = p.getScreenY() + int(dd.getScreenY()*(f)); + + if (mode & 2) + RenderBoat(reflect, (mode&1) ? (d+MAX_DIR/2)%MAX_DIR : d, x, y, 2); + else + RenderTile(reflect, t, x, y); + } + } +}; + +struct LaserRender : public RenderStage +{ + Pos p; + Dir d; + int range; + + LaserRender(Pos _p, int dir, int r) : p(_p), d(dir), range(r) + {} + + void Render(RenderObject* /*r*/, double /*time*/) + { + } +}; + +struct ExplosionRender : public RenderStage +{ + Pos p; + int seed; + int power; + int type; + + ExplosionRender(Pos _p, int _pow=0, int t=0) : p(_p), power(_pow), type(t) + { + seed = rand(); + } + + virtual int GetDepth(double /*time*/) + { + return p.x + p.y*2; + } + + void Render(RenderObject* /*r*/, double time, bool reflect) + { + if (type==1 && time > 2.5) + type = -1, new WinLoseScreen(false); + + // if (reflect) return; + if (time > 3) return; + srand(seed); + int q = 50 - int(time * 35); + if (power) q*=2; + if (type) q = 50; + for (int i=0; i 0) yo=-yo;//continue; + if (type) + { + + if (yo > 0) + { + if (!reflect && ys<-60) + { + const double T = 0.06; + double ct = -ys / 128.0; + if (time < ct+T*4) + { + x = p.getScreenX() + int(xs * (1+ct*(2+power))); + RenderTile( + reflect, + time > ct+3*T ? TILE_SPLASH_3 : time > ct+2*T ? TILE_SPLASH_2 : time > ct+T ? TILE_SPLASH_1 : TILE_WATER_PARTICLE+1, + x, y); + } + } + } + else + RenderTile( + reflect, + time - i*0.003 < 0.2 ? TILE_WATER_PARTICLE+1 : TILE_WATER_PARTICLE, + x, y+(reflect?-1:1)*yo); + } + else + { + if (yo > 0) + ; + else + RenderTile( + reflect, + i 50.0/70.0) return; + if (reflect) return; + srand(seed); + int q = 50 - int(time * 70); + if (height) q*=2; + for (int i=0; iTILE_WL/2 && ABS(y)>x-TILE_WL/2) continue; + int yo=0; + if (height) yo -= rand() % TILE_HUP; + x += p.getScreenX(); + y += p.getScreenY() + 4; + int xs = 0;//((rand() & 63) - 32); + int ys = (- (rand() & 31)); + x += int(xs * (1+time*(2))); + if (type) yo = -yo; + yo += int(time*time*128 + ys*time); + if (type) yo = -yo*2; + //if (yo > 0) yo=-yo;//continue; + int t = type ? TILE_BLUE_FRAGMENT : TILE_GREEN_FRAGMENT; + if (i>q-20) t++; + if (i>q-10) t++; + if (yo > 5) yo = 5; + RenderTile(false, t, x, y+(reflect?-yo:yo)); + } + } +}; +struct BuildRender : public RenderStage +{ + Pos p; + Dir dir; + int reverse; + int height; + int type; + + BuildRender(Pos _p, Dir _d, int _h, int _r=0, int _type=0) : p(_p), dir(_d), reverse(_r), height(_h), type(_type) + { + } + + void Render(RenderObject* /*r*/, double time, bool reflect) + { + if (time >= BUILD_TIME) + RenderTile(reflect, height ^ reverse ? (type ? COLLAPSE_DOOR2 : COLLAPSE_DOOR) : (type ? COLLAPSABLE2 : COLLAPSABLE), p.getScreenX(), p.getScreenY()); + else + { + if (height) + RenderTile(reflect, type ? COLLAPSABLE2 : COLLAPSABLE, p.getScreenX(), p.getScreenY()); + + double dist = time * 2 / BUILD_TIME; + if (dir>-1) + { + Pos from = p + ((dir+MAX_DIR/2)%MAX_DIR); + if (dist <= 1) + //if (dist > 1) + { + double offset = (dist*0.7) + 0.3; + int x = from.getScreenX() + int((p.getScreenX()-from.getScreenX()) * offset); + int y = from.getScreenY() + int((p.getScreenY()-from.getScreenY()) * offset - dist*(1-dist)*(TILE_HUP*4)); + RenderTile(reflect, TILE_GREEN_FRAGMENT, x, y); + } + dist -= 1; + } + else + { + if (reverse) dist = 1-dist; + } + if (dist > 0 && !height) + { + if (!reflect) + for (int i=0; i<=int(dist*15); i++) + { + int x = p.getScreenX(), y = p.getScreenY(); + double d = (i + fmod(dist*15, 1))/10.0; + int x1 = int(sin(d*5+time)*MIN(d,1)*TILE_W2/2); + int y1 = int(cos(d*5+time)*MIN(d,1)*TILE_H1*0.7); + RenderTile(reflect, TILE_GREEN_FRAGMENT, x+x1, y+y1+4); + RenderTile(reflect, TILE_GREEN_FRAGMENT, x-x1, y-y1+4); + } + } + if (dist > 0 && height) + { + int yo = int((1-dist)*(TILE_HUP*1.3)); + if (yo > TILE_HUP*1.1) + RenderTile(reflect, TILE_WHITE_TILE, p.getScreenX(), p.getScreenY()); + else if (!reflect) + { + RenderTile(reflect, type ? COLLAPSABLE2 : COLLAPSABLE, p.getScreenX(), p.getScreenY()); + RenderTile(reflect, type ? COLLAPSE_DOOR2 : COLLAPSE_DOOR, p.getScreenX(), p.getScreenY()+(reflect ? -yo : yo), yo+6); + RenderTile(reflect, type ? TILE_BLUE_FRONT : TILE_GREEN_FRONT, p.getScreenX(), p.getScreenY()); + } + else + { + if (yo < TILE_HUP/2) + { + RenderTile(reflect, type ? COLLAPSE_DOOR2 : COLLAPSE_DOOR, p.getScreenX(), p.getScreenY()+(reflect ? -yo : yo), yo); + + } + RenderTile(reflect, type ? COLLAPSABLE2 : COLLAPSABLE, p.getScreenX(), p.getScreenY()); + + } + } + } + } +}; + +struct PlayerRender : public RenderStage +{ + Pos p; + Pos target; + int p_h, target_h; + int r; + int type; + double speed; + bool dead; + + 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) + {} + 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) + { + int dist = MAX(ABS(p.x-target.x), ABS(p.y-target.y)); + if (dist > 1) + speed *= 1.5; + if(dist==0) + speed = 0; + } + + virtual int GetDepth(double time) + { + double f = speed ? time / speed : 1; + if (f>1) f=1; + if (f==1) dead = this->dead; + + if (f==1 || f>0.5 && p_h>target_h) + return target.x+target.y*2; + return MAX(target.x+target.y*2 , p.x+p.y*2); + } + + void Render(RenderObject* /*ro*/, double time, bool reflect) + { + bool dead = false; + double f = speed ? time / speed : 1; + if (f>1) f=1; + if (f==1) dead = this->dead; + + int x = p.getScreenX(); + int y = p.getScreenY(); + int x2 = target.getScreenX(); + int y2 = target.getScreenY(); + int h = 0; + int shadow_h = (int)((p_h+(target_h-p_h)*f)*TILE_HUP2); + + if (x==x2 && y==y2 && p_h!=target_h) + { + h = TILE_H_LIFT_UP - GetLiftHeight(time, p_h ? LIFT_DOWN : LIFT_UP); + } + else + { + + int dist = MAX(ABS(p.x-target.x), ABS(p.y-target.y)); + int arc = dist*dist; + int h1 = p_h * TILE_HUP2;; + int h2 = target_h * TILE_HUP2; + if (dist==2 && h1!=0) + { + arc += h2 ? 1 : 3; + h1 = 0; + shadow_h = f>=0.7 ? int(shadow_h*(f-0.7)/0.3) : 0; + } + if (dist==0) + arc = speed > JUMP_TIME ? 7 : 2; + + h = (int)(h1+(h2-h1)*f); + // if (x==x2 && y==y2) + // ; + // else + { + //h += int(TILE_H_LIFT_UP/3 * (1-f)); + h += (int)(f*(1-f)*TILE_HUP2*arc); + } + + if (type==2) + h=0; + } + + if (!dead) + { + int frame = 0; + if (type==2 && f<1) + { + //frame = ((int)(f*4) % 4); + //if (frame==2) frame=0; else if (frame==3) frame=2; + frame = 0; + } + else if (f==1 || x==x2 && y==y2) // stationary + frame = 0; + else if (f > 0.7) + frame = 0; + else + { + frame = type ? 2 : 1; + if (f<0.1 || f>0.6) + frame += 2; + } + + if (!reflect) + RenderTile( false, TILE_SPHERE, + (int)(x+(x2-x)*f), + (int)(y+(y2-y)*f) - shadow_h + ); + + RenderGirl( + reflect, + r, frame, + (int)(x+(x2-x)*f), + (int)(y+(y2-y)*f), + h + ); + + } + /* RenderTile( + dead ? TILE_SPHERE_OPEN : TILE_SPHERE_DONE, + (int)(x+(x2-x)*f), + (int)(y+(y2-y)*f), + true + );*/ + } +}; + + +struct HexPuzzle : public State +{ + struct Undo + { + #define MAX_TILECHANGE 64 // TODO: don't have a magic upper limit + struct TileChange + { + Pos p; + Tile t; + int item; + + TileChange() + {} + TileChange(Pos _p, Tile _t, int _i) : p(_p), t(_t), item(_i) + {} + void Restore(HexPuzzle* w) + { + w->SetTile(p,t,false,false); + w->SetItem(p,item,false,false); + } + }; + + TileChange t[MAX_TILECHANGE]; + Pos playerPos; + Dir playerMovement; + int numT; + int numItems[2]; + int score; + double time; + double endTime; + + void Add(TileChange const & tc) + { + for (int i=0; i=MAX_TILECHANGE) + FATAL("numT>=MAX_TILECHANGE"); + else + t[numT++] = tc; + } + void New(Dir pmove, Pos & pp, int* items, double t, int sc) + { + numItems[0] = items[0]; + numItems[1] = items[1]; + playerPos = pp; + playerMovement = pmove; + score = sc; + time = t; + numT = 0; + } + void Restore(HexPuzzle* w) + { + for (int i=numT-1; i>=0; i--) + t[i].Restore(w); + w->dead = false; + w->win = false; + w->player = playerPos; + w->player_items[0] = numItems[0]; + w->player_items[1] = numItems[1]; + w->player_score = score; + + //w->renderer.player.Add(new PlayerRender(playerPos, w->GetHeight(playerPos), false), w->time); + } + }; + + #define MAP_SIZE 30 + char* special[MAP_SIZE][MAP_SIZE]; + Tile map[MAP_SIZE][MAP_SIZE]; + int32_t map_item[MAP_SIZE][MAP_SIZE]; + int tileCount[NumTileTypes]; + int32_t levelPar, levelDiff; + int turboAnim; + Pos player; + int player_items[2]; + int player_score; + int numComplete, numLevels, numMastered, numLevelsFound; + bool dead; + bool win; + int winFinal; + + SaveState progress; + + WorldRenderer renderer; + double time; + double undoTime; + + #define MAX_UNDO 6 + Undo undo[MAX_UNDO]; + int numUndo; + LevelInfo* currentLevelInfo; + + char currentFile[1000]; + + ~HexPuzzle() + { + FreeGraphics(); + } + + LevelInfo* GetLevelInfo(const char* f) + { + if (strstr(f, "Levels\\") == f) + f += 7; + if (currentLevelInfo!=0 && strcmp(currentLevelInfo->file, f)==0) + return currentLevelInfo; + + if (f[0]=='_') + { + int t = atoi(f+1); + if (t <= numComplete) + return 0; + + static char tmp1[1000]; + static LevelInfo tmp = {0, "", tmp1}; + sprintf(tmp1, ngettext("Complete 1 more level to unlock!", "Complete %d more levels to unlock!", t-numComplete), t-numComplete); + return &tmp; + } + + for (unsigned int i=0; i>")}; + return &tmp; + } + +#ifdef MAP_EDIT_HACKS + int GetAutoTile(const char * level, bool tiletype) + { + FILE* f = file_open(filename, "rb"); + int tile = EMPTY; + int version; + + if (f && fscanf(f, "%d", &version)==1 && (version==3 || version==4)) + { + if (strstr(level,"mk")) + level+=0; + + fgetc(f); // Remove '\n' character + + int32_t par, diff; + unsigned char bounds[4]; + Pos playerStart; + fread(&par, sizeof(par), 1, f); + par = SWAP32(par); + + if (version >= 4) { + fread(&diff, sizeof(diff), 1, f); + diff = SWAP32(diff); + } + fread(bounds, sizeof(bounds), 1, f); + fread(&playerStart, sizeof(playerStart), 1, f); + playerStart.x = SWAP32(playerStart.x); + playerStart.y = SWAP32(playerStart.y); + + int highval=0; + + for (int i=bounds[0]; i<=bounds[1]; i++) + for (int j=bounds[2]; j<=bounds[3]; j++) + { + unsigned char comp = map[i][j] | (map_item[i][j]<<5); + fread(&comp, sizeof(comp), 1, f); + int t = comp & 0x1f; + int item = (comp >> 5) & 3; + for (int i=highval+1; i>8)) + highval = i; + } + + if (tiletype) + { + tile = value_order[highval]; + if (tile==0x100) tile = COLLAPSABLE3; + if (tile==0x200) tile = SWITCH; + if (tile==LIFT_UP) tile = LIFT_DOWN; + } + else + { + if (value_order[highval] == LIFT_UP) + tile = highval-1; + else + tile = highval; + } + } + else + { + level+=0; + } + if (f) + fclose(f); + return tile; + } +#endif + + void InitSpecials() + { + numComplete = numLevels = numMastered = numLevelsFound = 0; + for (int i=0; i= UNLOCK_SCORING && !progress.general.scoringOn) + { + HintMessage::FlagTile(26); + progress.general.scoringOn = 1; + InitSpecials(); // Re-initialise with gold ones available + } + HintMessage::FlagTile(25); + } + else + { + for (int i=0; ibounds[1]) bounds[1] = x2; + if (y1bounds[3]) bounds[3] = y2; + } + } + + int sx, sy; + if (isMap) + { + sx = bounds[0] - int(TILE_W2*6.35); + sy = (bounds[3] + bounds[2] - SCREEN_H) / 2 - TILE_H2/2; + } + else + { + sx = (bounds[1] + bounds[0] - SCREEN_W) / 2 - TILE_W3/2; + sy = (bounds[3] + bounds[2] - SCREEN_H) / 2 - TILE_H2/2; + } + if (isMap) + { + initScrollX = sx; + initScrollY = sy; + if (mapScrollX==0) + mapScrollX = sx; + else + sx = mapScrollX; + } + +// time = 1; // Guarantee we can't try and do things at time=0 + + renderer().Add(new ScrollRender(sx, sy), time); + renderer().Add(new FadeRender(1), time); + if (time != 0) + time -= 0.5; + } + + char* ReadAll(FILE* f) + { + int size; + // FIXME: According to http://userpage.fu-berlin.de/~ram/pub/pub_jf47ht20Ht/c_faq_de + // undefined for binary streams! (POSIX does not differ between ascii and binary, so + // we are on the safe side in Linux) + fseek(f, 0, SEEK_END); + size = ftell(f); + fseek(f, 0, SEEK_SET); + char* c = loadPtr = new char [size]; + endLoad = loadPtr + size; + fread(c, 1, size, f); + return c; + } + + static char *loadPtr, *endLoad; + static unsigned int fread_replace(void* d, unsigned int size, unsigned int num, FILE*) + { + unsigned int remain = (endLoad - loadPtr) / size; + if (remain < num) num = remain; + memcpy(d, loadPtr, size*num); + loadPtr += size*num; + return num; + } + + int GetPar(const char * level, bool getdiff=false) + { + if (strcmp(level, currentFile)==0) + return getdiff ? levelDiff : levelPar; + + #ifdef USE_LEVEL_PACKFILE + PackFile1::Entry* e = levelFiles.Find(level); + if (!e) return 999; + loadPtr = (char*)e->Data(); + endLoad = loadPtr + e->DataLen(); + FILE* f = 0; + #else + loadPtr = 0; + FILE* f = file_open(level, "rb"); + #endif + + typedef unsigned int _fn(void*, unsigned int, unsigned int, FILE*); + _fn * fn = (loadPtr ? (_fn*)fread_replace : (_fn*)fread); + + int32_t par = 99999, diff = 0; + int16_t version; + + if (!f && !loadPtr) + return getdiff ? diff : par; + + fn(&version, 2, 1, f); // skip to relevant point + + if (fn(&par, sizeof(par), 1, f) != 1) + par = 99999; + else + par = SWAP32(par); + size_t ret = fn(&diff, sizeof(diff), 1, f); + diff = SWAP32(diff); + if (ret != 1 || diff<0 || diff>10) + diff = 0; + + #ifdef USE_LEVEL_PACKFILE + loadPtr = endLoad = 0; + #else + if (f) + fclose(f); + #endif + + return getdiff ? diff : par; + } + + bool LoadSave(const char * filename, bool save) + { + if (!filename) + return false; + + if (!save) + { + showScoring = false; + LevelSave* l = progress.GetLevel(filename, true); + if (progress.general.scoringOn && l && l->Completed() ) + showScoring = true; + } + + #ifdef USE_LEVEL_PACKFILE + if (!save) + { + PackFile1::Entry* e = levelFiles.Find(filename); + if (!e) return false; + + if (currentFile != filename) // equal (overlapping) strings are forbidden + strcpy(currentFile, filename); + currentLevelInfo = GetLevelInfo(currentFile); + + loadPtr = (char*)e->Data(); + endLoad = loadPtr + e->DataLen(); + _LoadSave(NULL, save); + loadPtr = endLoad = 0; + + return true; + } + #else + loadPtr = 0; + FILE* f = file_open(filename, save ? "wb" : "rb"); + if (f) + { + strcpy(currentFile, filename); + if (!save) + currentLevelInfo = GetLevelInfo(currentFile); + + if (!save) + { + char* data = ReadAll(f); + _LoadSave(f, save); + delete [] data; + loadPtr = endLoad = 0; + } + else + { + _LoadSave(f, save); + } + fclose(f); + + return true; + } + #endif + + return false; + } + + /** \brief Writes/reads game status to/from a file + * + * The game data file is written in little endian so it can be shared + * across different machines. + */ + void _LoadSave(FILE* f, bool save) + { + typedef unsigned int _fn(void*, unsigned int, unsigned int, FILE*); + _fn * fn = save ? (_fn*)fwrite : (loadPtr ? (_fn*)fread_replace : (_fn*)fread); + + #define VERSION 4 + int version = VERSION; // 1--9 + if (save) + fprintf(f, "%d\n", version); + else + { + char c; + if (fn(&c, 1, 1, f) != 1) + return; + version = c-'0'; + + // Remove '\n' character + fn(&c, 1, 1, f); + } + + if (!save) + { + for (int i=0; i=2 && version<=4) + { + unsigned char bounds[4]; + if (save) + { + bounds[0]=bounds[1]=player.x; + bounds[2]=bounds[3]=player.y; + for (int i=0; ibounds[1]) bounds[1] = i; + if (jbounds[3]) bounds[3] = j; + } + } + else + { + memset(map, 0, sizeof(map)); + memset(map_item, 0, sizeof(map_item)); + } + + if (version>=3) { + levelPar = SWAP32(levelPar); + fn(&levelPar, 1, sizeof(levelPar), f); + levelPar = SWAP32(levelPar); + } + else if (!save) + levelPar = 0; + + if (version>=4) { + levelDiff = SWAP32(levelDiff); + fn(&levelDiff, 1, sizeof(levelDiff), f); + levelDiff = SWAP32(levelDiff); + } + else if (!save) + levelDiff = 0; + + fn(bounds, sizeof(bounds), 1, f); + player.x = SWAP32(player.x); + player.y = SWAP32(player.y); + fn(&player, sizeof(player), 1, f); + player.x = SWAP32(player.x); + player.y = SWAP32(player.y); + + int offsetx=0, offsety=0; + + if (!save && bounds[1]-bounds[0]<15) // Hacky - don't recenter map... + { + // Re-position map to top left (but leave a bit of space) + // (This ensures the laser/boat effects don't clip prematurely against the edges of the screen) + offsetx = SCREEN_W/2/TILE_W2 + 1 - (bounds[0]+bounds[1]/2); + offsety = SCREEN_H/2/TILE_H2 + SCREEN_W/2/TILE_W2 - (bounds[2]+bounds[3]/2); + offsetx = MAX(0, offsetx); + offsety = MAX(0, offsety); +// if (bounds[0] > 2) +// offsetx = 2 - bounds[0]; +// if (bounds[2] > 2) +// offsety = 2 - bounds[2]; + } + bounds[0] += offsetx; + bounds[1] += offsetx; + bounds[2] += offsety; + bounds[3] += offsety; + player.x += offsetx; + player.y += offsety; + + for (int i=bounds[0]; i<=bounds[1]; i++) + for (int j=bounds[2]; j<=bounds[3]; j++) + { + unsigned char comp = map[i][j] | (map_item[i][j]<<5); + fn(&comp, sizeof(comp), 1, f); + map[i][j] = comp & 0x1f; + map_item[i][j] = (comp >> 5) & 3; + } + + if (save) + { + for (int i=bounds[0]; i<=bounds[1]; i++) + for (int j=bounds[2]; j<=bounds[3]; j++) + if (special[i][j]) + { + int16_t len = strlen(special[i][j]); + unsigned char x=i, y=j; + fn(&x, sizeof(x), 1, f); + fn(&y, sizeof(y), 1, f); + len = SWAP16(len); + fn(&len, sizeof(len), 1, f); + len = SWAP16(len); + fn(special[i][j], 1, len, f); + } + } + else + { + while(1){ + int16_t len; + unsigned char x, y; + if (!fn(&x, sizeof(x), 1, f)) + break; + fn(&y, sizeof(y), 1, f); + x += offsetx; y += offsety; + fn(&len, sizeof(len), 1, f); + len = SWAP16(len); + if (len<0) break; + char* tmp = new char[len+1]; + tmp[len] = 0; + fn(tmp, 1, len, f); + + SetSpecial(Pos(x,y), tmp, true, false); + } + } + } + else + return; // Unsupported version! + + ResetLevel(); + + // Save when returning to map! + if (isMap) + { + progress.general.completionPercentage = numComplete*100/numLevels; + progress.general.masteredPercentage = numMastered*100/numLevels; + LoadSaveProgress(true); + } + } + + void SetTile(Pos const & p, Tile t, bool updateRenderer=true, bool undoBuffer=true) + { + if (p.x<0 || p.x>MAP_SIZE) + return; + if (p.y<0 || p.y>MAP_SIZE) + return; + if (map[p.x][p.y] == t) + return; + if (map[p.x][p.y] == t) + return; + + tileCount[map[p.x][p.y]]--; + tileCount[t]++; + + if (undoBuffer) + undo[numUndo].Add(Undo::TileChange(p,GetTile(p),GetItem(p))); + + map[p.x][p.y] = t; + + if (updateRenderer) + renderer(p).Add(new TileRender(t, p), time); + } + + Tile GetTile(Pos const & p) + { + if (p.x<0 || p.x>=MAP_SIZE) + return EMPTY; + if (p.y<0 || p.y>=MAP_SIZE) + return EMPTY; + return map[p.x][p.y]; + } + + int GetHeight(Pos const & p) + { + return tileSolid[GetTile(p)]==1; + } + + char* GetSpecial(Pos const & p) + { + if (p.x<0 || p.x>=MAP_SIZE) + return NULL; + if (p.y<0 || p.y>=MAP_SIZE) + return NULL; + return special[p.x][p.y]; + } + + void SetSpecial(Pos const & p, char * d, bool use_pointer=false, bool auto_activate=true) + { + if (p.x<0 || p.x>=MAP_SIZE || p.y<0 || p.y>=MAP_SIZE) + { + if (use_pointer) + delete [] d; + return; + } + + delete [] special[p.x][p.y]; + if (!use_pointer && d) + { + + special[p.x][p.y] = new char [strlen(d) + 1]; + strcpy(special[p.x][p.y], d); + } + else + special[p.x][p.y] = d; + + if (special[p.x][p.y]==0) + renderer(p,true).Add(new ItemRender(GetItem(p), GetTile(p)==EMPTY, p), time); + else if (auto_activate) + ActivateSpecial(p, 0); + } + + int GetLevelState(Pos const & p, int recurse=0) + { + char* x = GetSpecial(p); + if (!x) return 0; + + LevelSave* l = progress.GetLevel(x, false); + + int t = 1; + + if (strcmp(x, STARTING_LEVEL)==0) + t = 2; + if (x[0]=='_' && l && l->unlocked) + t=3; + + if (l && l->Completed()) + { + t = 3; + + if (recurse) + return t; + + int par = GetPar(x); + if (progress.general.scoringOn && l->PassesPar( par )) + t = l->BeatsPar( par ) ? 40 : 4; + } + if (recurse) + return t; + + int adj=0; + for (Dir d=0; d1 || i==1 && t>1) + if (i>=1 && t>2 || t>=1 && i>2) + { + adj |= 1<=MAP_SIZE || p.y<0 || p.y>=MAP_SIZE) + return; + + char * x = special[p.x][p.y]; + + if (x==0 || x[0]==0) + return; + + if (type==2 && x[0]=='_') // Phase2 init - unlock + { + int t = GetLevelState(p); + int target = atoi(x+1), targetM = 0; + if (target>1000) targetM=target=target-100; + if (t > 1 && numComplete >= target && numMastered >= targetM) + { + LevelSave* l = progress.GetLevel(x, true); + if (!l->unlocked) + { + l->unlocked = true; + + renderer(p, true).Add(new LevelSelectRender(p, 5, GetLevelState(p)>>8), time+0.01); + renderer().Add(new ExplosionRender(p, 0), time + 0.6); + renderer().Add(new ExplosionRender(p, 1), time + 1.1); + renderer(p, true).Add(new LevelSelectRender(p, -1, GetLevelState(p)>>8), time + 1.1); + } + } + } + + if (type==0) // Init & count levels + { + if (x[0]=='_') + { + int t = GetLevelState(p); + int unlock = progress.GetLevel(x, true)->unlocked; + LevelSelectRender* lsr = new LevelSelectRender( p, unlock ? -1 : (t>>8) ? 5 : 1, t>>8 ); + if ((t>>8) && p.x > mapRightBound) mapRightBound = p.x; + #ifdef MAP_EDIT_HACKS + lsr->magic = -atoi(x+1); + SetTile(p, LIFT_DOWN, true, false); + #else + SetTile(p, EMPTY, true, false); + #endif + renderer(p,true).Add(lsr, time); + } + else + { + //printf("Level: %s\n", x); + + int t = GetLevelState(p); + numLevels++; + if (t && !GetItem(p)) + { + if (!isMap) + { + isMap = true; + mapRightBound = 0; + } + currentLevelInfo = 0; + + if ((t&0xff)>=2) + { + LevelSave* l = progress.GetLevel(x, true); + if (!l->unlocked) + { + l->unlocked = true; + + renderer(p, true).Add(new LevelSelectRender(p, -1, 0), time+0.01); + renderer().Add(new ExplosionRender(p, 0), time + 0.6); + renderer(p, true).Add(new LevelSelectRender(p, t & 0xff, t>>8), time + 0.6); + } + + numLevelsFound++; + if (p.x > mapRightBound) mapRightBound = p.x; + } + if ((t&0xff)>=3) + numComplete++; + if ((t&0xff)>=4) + numMastered++; + + LevelSelectRender* lsr = new LevelSelectRender( p, t & 0xff, t>>8 ); + + #ifdef MAP_EDIT_HACKS + lsr->magic = 0; + int t = GetAutoTile(x, true); + int v = GetAutoTile(x, false); + if (MAP_EDIT_HACKS_DISPLAY_UNLOCK) + lsr->magic = v; + else + lsr->magic = GetPar(x, true); + t = 1; + SetTile(p, t, true, false); + #else + SetTile(p, EMPTY, true, false); + #endif + + renderer(p,true).Add(lsr, time); + } + } + } + + if (type==1 && x[0]!='_') // Clicked on + { + int t = GetLevelState(p); + if (t>1) + { + LoadSave(x, false); + } + } + } + + void SetItem(Pos const & p, int t, bool updateRenderer=true, bool undoBuffer=true) + { + if (p.x<0 || p.x>MAP_SIZE) + return; + if (p.y<0 || p.y>MAP_SIZE) + return; + if (map_item[p.x][p.y] == t) + return; + + if (undoBuffer) + undo[numUndo].Add(Undo::TileChange(p,GetTile(p),GetItem(p))); + + map_item[p.x][p.y] = t; + + if (updateRenderer) + renderer(p,true).Add(new ItemRender(t, GetTile(p)==EMPTY, p), time); + } + + Tile GetItem(Pos const & p) + { + if (p.x<0 || p.x>=MAP_SIZE) + return EMPTY; + if (p.y<0 || p.y>=MAP_SIZE) + return EMPTY; + return map_item[p.x][p.y]; + } + + void LoadSaveProgress(bool save) + { + FILE* f = file_open(currentSlot, save ? "wb" : "rb"); + if (f) + { + progress.LoadSave(f, save); + fclose(f); + } + else + { + if (!save) + progress.Clear(); + } + } + void LoadProgress() + { + LoadSaveProgress(false); + } + void SaveProgress() + { + LoadSaveProgress(true); + } + + SDL_Surface* Load(const char * bmp, bool colourKey=true) + { + typedef unsigned int uint32; + uint32* tmp = 0; + + SDL_Surface * g = 0; + +#ifdef EDIT + if (strstr(bmp, ".bmp")) + { + g = SDL_LoadBMP(bmp); + + char out[1024]; + strcpy(out, bmp); + strcpy(strstr(out, ".bmp"), ".dat"); + +// SDL_PixelFormat p; +// p.sf = 1; +// SDL_Surface* tmp = SDL_ConvertSurface(g, &p, SDL_SWSURFACE); + + short w=g->w, h=g->h; + char* buf = (char*) g->pixels; + if (colourKey) + { + while (IsEmpty(g, w-1, 0, 1, h) && w>1) + w--; + while (IsEmpty(g, 0, h-1, w, 1) && h>1) + h--; + } + + FILE* f = file_open(out, "wb"); + fwrite(&w, sizeof(w), 1, f); + fwrite(&h, sizeof(h), 1, f); + + uint32 mask = IMAGE_DAT_OR_MASK; + for (int i=0; i<(int)w*h; ) + { + uint32 c = (*(uint32*)&buf[(i%w)*3 + (i/w)*g->pitch] | mask); + int i0 = i; + while (i < (int)w*h && c == (*(uint32*)&buf[(i%w)*3 + (i/w)*g->pitch] | mask)) + i++; + c &= 0xffffff; + i0 = i-i0-1; + if (i0 < 0xff) + c |= i0 << 24; + else + c |= 0xff000000; + + fwrite(&c, sizeof(c), 1, f); + + if (i0 >= 0xff) + fwrite(&i0, sizeof(i0), 1, f); + } + fclose(f); + + SDL_FreeSurface(g); + + bmp = out; + } +#endif + + FILE* f = file_open(bmp, "rb"); + if (!f) FATAL("Unable to open file", bmp); + + int16_t w,h; + fread(&w, sizeof(w), 1, f); + fread(&h, sizeof(h), 1, f); + w = SWAP16(w); + h = SWAP16(h); + if (w>1500 || h>1500 || w<=0 || h<=0) FATAL("Invalid file", bmp); + + tmp = new uint32[(int)w*h]; + + uint32 c = 0; + uint32 cnt = 0; + for (int p=0; p<(int)w*h; p++) + { + if (cnt) + cnt -= 0x1; + else + { + fread(&c, sizeof(c), 1, f); + c = SWAP32(c); + cnt = c >> 24; + if (cnt==255) { + fread(&cnt, sizeof(cnt), 1, f); + cnt = SWAP32(cnt); + } + } + tmp[p] = c | 0xff000000; + } + + g = SDL_CreateRGBSurfaceFrom(tmp, w, h, 32, w*4, + 0xff0000, + 0xff00, + 0xff, + 0xff000000 ); + + fclose(f); + + + if (!g) FATAL("Unable to create SDL surface"); + if (colourKey) + SDL_SetColorKey(g, SDL_SRCCOLORKEY, SDL_MapRGB(g->format, WATER_COLOUR)); + SDL_Surface * out = SDL_DisplayFormat(g); + SDL_FreeSurface(g); + delete [] tmp; + if (!out) FATAL("Unable to create SDL surface (SDL_DisplayFormat)"); + return out; + } + + #ifdef USE_LEVEL_PACKFILE + PackFile1 levelFiles; + #endif + HexPuzzle() + { + SDL_WM_SetCaption(GAMENAME, 0); + + time = 0; + + #ifdef USE_LEVEL_PACKFILE + FILE* f = file_open("levels.dat", "rb"); + if (!f) + FATAL("Unable to open file", "levels.dat"); + levelFiles.Read(f); + fclose(f); + #endif + + LoadGraphics(); + + isMap = false; + editMode = false; + + currentLevelInfo = 0; + + editTile = 0; + levelPar = 0; + levelDiff = 5; + turboAnim = 0; + + memset(map, 0, sizeof(map)); + memset(map_item, 0, sizeof(map_item)); + memset(special, 0, sizeof(special)); + + LoadProgress(); + +// player = Pos(1,11); + +// ResetLevel(); + + LoadMap(); + } + + void LoadMap() + { + #ifndef EDIT + progress.GetLevel(STARTING_LEVEL, true)->unlocked = 1; + if (!progress.GetLevel(STARTING_LEVEL, true)->Completed()) + { + LoadSave(STARTING_LEVEL, false); + return; + } + #endif + + //editMode = false; + LoadSave(mapname, false); + } + + void Render() + { + if (!activeMenu || activeMenu->renderBG) + { + SDL_Rect src = {0,0,screen->w,screen->h}; + SDL_Rect dst = {0,0,screen->w,screen->h}; + if (isRenderMap) + { + int boundW = mapBG->w; + #ifndef EDIT + boundW = MIN(boundW, (mapRightBound+4) * TILE_W2 - TILE_W1); + #endif + src.x = scrollX - initScrollX; + if (src.x+src.w > boundW) + { + int diff = src.x+src.w - boundW; + src.x -= diff; + if (isMap) + scrollX -= diff; + } + if (src.x < 0) + { + if (isMap) + scrollX -= src.x; + src.x = 0; + } + //scrollY = initScrollY; + + if (isMap) + mapScrollX = scrollX; + + SDL_BlitSurface(mapBG, &src, screen, &dst); + } + else + SDL_BlitSurface(gradient, &src, screen, &dst); + + renderer.Render(time, true); + + if (!hintsDone && !isFadeRendering) + { + DoHints(); + } + + if (1) + { + SDL_Rect src = {0,SCREEN_H-1,SCREEN_W,1}; + SDL_Rect dst = {0,SCREEN_H-1,SCREEN_W,1}; + for (int i=0; i 1 && time < undo[numUndo-2].endTime) + { + int i = numUndo-1; + while (i>1 && time1 && time < undo[0].time) + v[0]=v[1]=v[2]=0; + #ifdef EDIT + /* TRANSLATORS: Anti-Ice are pickups, which turn ice plates into solid + plates once you step on them. Each pickup changes one ice plate */ + Print(0,0,_("Anti-Ice: %d"), v[0]); + Print(0,FONT_SPACING,_("Jumps: %d"), v[1]); + Print(0,FONT_SPACING*2,_("Score: %d (%d)"), v[2], player_score); + /* TRANSLATORS: Par is similar to golf, a pre defined score which you + can attempt to beat */ + Print(0,FONT_SPACING*3,_("Par: %d"), levelPar); + Print(0,FONT_SPACING*4,_("Diff: %d"), levelDiff); + #else + if (showScoring) + Print(0, SCREEN_H-FONT_SPACING, _(" Par: %d Current: %d"), levelPar, v[2]); + + if (v[0]) + Print(0,0,_(" Anti-Ice: %d"), v[0]); + else if (v[1]) + Print(0,0,_(" Jumps: %d"), v[1]); + #endif + } + if (isRenderMap && isMap && !isFadeRendering) + { + #if 0//def EDIT + Print(0,0,_("Points: %d"), numComplete+numMastered); + Print(0,FONT_SPACING,_("Discovered: %d%% (%d/%d)"), numLevelsFound*100/numLevels, numLevelsFound, numLevels); + Print(0,FONT_SPACING*2,_("Complete: %d%% (%d)"), numComplete*100/numLevels, numComplete); + Print(0,FONT_SPACING*3,_("Mastered: %d%% (%d)"), numMastered*100/numLevels, numMastered); + #else + if (numComplete==numLevels && progress.general.endSequence>0) + Print(0, SCREEN_H-FONT_SPACING, _(" %d%% Mastered"), numMastered*100/numLevels); + else + Print(0, SCREEN_H-FONT_SPACING, _(" %d%% Complete"), numComplete*100/numLevels); + + if (numMastered >= numLevels && progress.general.endSequence < 2) + { + progress.general.endSequence = 2; + LoadSaveProgress(true); + + new Fader(-1, -7, 0.3); + } + if (numComplete >= numLevels && progress.general.endSequence < 1) + { + progress.general.endSequence = 1; + LoadSaveProgress(true); + + new Fader(-1, -5, 0.3); + } + #endif + } + if ((currentLevelInfo || noMouse) && isMap && isRenderMap && !activeMenu && isFadeRendering<=0) + { + Pos p; + if (noMouse) + p = keyboardp; + else + p = mousep; + int pad = SCREEN_W/80; + // SDL_Rect src = {0, 0, uiGraphics->w, uiGraphics->h}; + SDL_Rect dst = {pad, SCREEN_H-TILE_H2-pad, 0, 0}; + // dst.x = p.getScreenX() + TILE_W3/2 - scrollX; + // dst.y = p.getScreenY() - src.h/2 - scrollY; + dst.x = p.getScreenX() - scrollX; + dst.y = p.getScreenY() - scrollY - FONT_SPACING*3 - FONT_SPACING/2; + // if (dst.x > SCREEN_W*2/3) dst.x -= TILE_W3 + src.w; + // if (dst.y+src.h > screen->h-pad) dst.y = screen->h-pad - src.h; + + RenderTile(false, 0, p.getScreenX(), p.getScreenY()); + // SDL_BlitSurface(uiGraphics, &src, screen, &dst); + + // dst.x += src.w/2; + + if (currentLevelInfo) + { + keyboardp = p; + + PrintC(true, dst.x, dst.y - FONT_SPACING/4, currentLevelInfo->name); + + if (currentLevelInfo->file[0]!=0) + { + if (player_score > 0) + { + if (progress.general.scoringOn) + { + PrintC(false, dst.x, dst.y + FONT_SPACING*4 - FONT_SPACING/4, _("Best:% 3d"), player_score); + PrintC(false, dst.x, dst.y + FONT_SPACING*5 - FONT_SPACING/4, _("Par:% 3d"), levelPar); + } + else + PrintC(false, dst.x, dst.y + FONT_SPACING*4 - FONT_SPACING/4, _("Completed"), player_score); + } + else + PrintC(false, dst.x, dst.y + FONT_SPACING*4 - FONT_SPACING/4, _("Incomplete"), player_score); + } + } + } + + // "Win" + if (win && numUndo > 0 && time > undo[numUndo-1].endTime + 2) + { + if (currentFile[0] && winFinal==0) + { + LevelSave* l = progress.GetLevel(currentFile, true); + + new WinLoseScreen(true, player_score, showScoring ? levelPar : 0, l && showScoring && l->Completed() ? l->GetScore() : 0); + + if (l->IsNewCompletionBetter(player_score)) + { + l->SetScore(player_score); + + l->SetSolution(numUndo); + + for (int i=0; iSetSolutionStep(i, undo[i].playerMovement); + } + + SaveProgress(); + } + + winFinal = 1; + } + else + winFinal = 0; + + // Move up "level complete" writing so it doesn't feel like everything's ground to a halt... + if (win && numUndo > 0 && time > undo[numUndo-1].endTime && !winFinal) + { + double t = (time - undo[numUndo-1].endTime) / 2; + t=1-t; + t*=t*t; + t=1-t; + int y = SCREEN_H/3 - FONT_SPACING + 1; + y = SCREEN_H + int((y-SCREEN_H)*t); + PrintC(true, SCREEN_W/2, y, _("Level Complete!")); + } + } + + if (activeMenu) + activeMenu->Render(); + + if (!noMouse) + { + // Edit cursor + if (editMode) + { + RenderTile(false, editTile, mousex+scrollX, mousey+scrollY); + } + } + } + + int Count(Tile t) + { + return tileCount[t]; + } + int Swap(Tile t, Tile t2) + { + const int num = Count(t) + Count(t2); + if (t==t2 || num==0) + return Count(t); // Nothing to do... + + int count=0; + for (int x=0; x=2) + { + LevelSave* l = progress.GetLevel(sp, true); + if (l) + { + currentLevelInfo = GetLevelInfo(sp); + levelPar = GetPar(sp); + player_score = l->GetScore(); + } + } + } + +#ifdef EDIT + sprintf(tmp, _("Special(%d,%d): %s (%d)"), s.x, s.y, sp ? sp : _(""), GetPar(sp)); + SDL_WM_SetCaption(tmp, NULL); +#endif + } + else if (currentFile[0]) + { +#ifdef EDIT + SDL_WM_SetCaption(currentFile, NULL); +#endif + if (isMap) + currentLevelInfo = 0; + } + } + } + + virtual void Mouse(int x, int y, int dx, int dy, int button_pressed, int button_released, int button_held) + { + if (activeMenu) + { + activeMenu->Mouse(x,y,dx,dy,button_pressed,button_released,button_held); + return; + } + + if (isFadeRendering) + return; + + +#ifndef EDIT + if (button_pressed==2 || button_pressed==4 && isMap) + { + KeyPressed(SDLK_ESCAPE, 0); + keyState[SDLK_ESCAPE] = 0; + return; + } +#endif + + x += scrollX; + y += scrollY; + + Pos s = Pos::GetFromWorld(x,y); + if (tileSolid[GetTile(Pos::GetFromWorld(x,y+TILE_HUP))] == 1) + s = Pos::GetFromWorld(x,y+TILE_HUP); + + mousep = s; + + UpdateCursor(s); + +#ifdef EDIT + if (button_held & ~button_pressed & 4) + { + scrollX -= dx; + scrollY -= dy; + } +#endif + + if (!editMode) + { + if (isMap && (button_pressed & 1)) + { + ActivateSpecial(s, 1); + return; + } + if (!isMap && win && winFinal) + { + if (button_pressed & 1) + { + LoadMap(); + return; + } + } + if(!isMap) + { + if((button_pressed & 1) || (button_held & 1) && (numUndo==0 || time>=undo[numUndo-1].endTime)) + { + if(s.x==player.x && s.y==player.y) + { + // Don't activate jump powerup without a new click + if (button_pressed & 1) + Input(-1); + } + else if(s.x==player.x && s.yplayer.y) + Input(3); + else if(s.y==player.y && s.xplayer.x) + Input(2); + else if(s.y+s.x==player.y+player.x && s.x>player.x) + Input(1); + else if(s.y+s.x==player.y+player.x && s.x=0) + SetTile(s, editTile, true, false); + else + SetItem(s, editTile==-2 ? 0 : editTile==-1 ? 1 : 2, true, false); + } + + if (button_pressed==2) + { + editTile = GetTile(s); + } + + if (button_pressed==8) + { + editTile=editTile-1; + if (editTile<=0) editTile=NumTileTypes-1; + } + + if (button_pressed==16) + { + editTile=editTile+1; + if (editTile<=0) editTile=1; + if (editTile==NumTileTypes) editTile=0; + } + + if (button_pressed==64) + { + ResetUndo(); + player = s; + dead = false; + renderer.player.Reset(-1); + renderer.player.Add(new PlayerRender(player, GetHeight(player), dead), 0); + } + + if (button_pressed==256) + { + char* fn = LoadSaveDialog(false, true, _("Select level")); + if (fn) + { + char * l = strstr(fn, "Levels"); + if(l) + { + FILE * f = file_open(l,"rb"); + if (f) + fclose(f); + if (f) + SetSpecial(s, l); + else if (l[6]!=0 && l[7]=='_') + SetSpecial(s, l+7); + } + UpdateCursor(Pos(-1,-1)); + } + } + if (button_pressed==512) + { + SetSpecial(s, NULL); + UpdateCursor(Pos(-1,-1)); + } + if (button_pressed==1024) + { + static char x[1000] = ""; + if (!(s.x<0 || s.x>=MAP_SIZE || s.y<0 || s.y>=MAP_SIZE)) + { + char tmp[1000]; + strcpy(tmp, x); + if (GetSpecial(s)) + strcpy(x, GetSpecial(s)); + else + x[0] = 0; + SetSpecial(s, tmp[0] ? tmp : 0); + if (!tmp[0]) + SetTile(s, EMPTY, true, false); + } + } + + if (button_pressed==32) + { + editTile = editTile<0 ? 1 : -1; + } +#endif // EDIT + } + + void CheckFinished() + { + bool slow = false; + if (Count(COLLAPSABLE)==0) + { + if (Replace(COLLAPSE_DOOR, COLLAPSABLE) == 0) + win = true; + else + slow = true; + Replace(SWITCH, NORMAL); + } + else + win = false; + + if (Count(COLLAPSABLE2)==0) + if (Replace(COLLAPSE_DOOR2, COLLAPSABLE2)) + slow = true; + + if (slow) + time += BUILD_TIME; + } + bool Collide(Pos p, bool high) + { + Tile t = GetTile(p); +// switch(t) +// { +// default: + if (!high) + return tileSolid[t]==1; + else + return false; +// } + } + void Undo() + { + if (numUndo==0) return; + + UndoDone(); // Complete previous undo... + + numUndo--; + + if (time > undo[numUndo].endTime) + time = undo[numUndo].endTime; + undoTime = undo[numUndo].time; + + undo[numUndo].Restore(this); + } + void UndoDone() + { + if (undoTime < 0) + return; + renderer.Reset(undoTime); + time = undoTime; + undoTime = -1; + } + void ScoreDestroy(Pos p) + { + Tile t = GetTile(p); + if (t==COLLAPSABLE || t==COLLAPSE_DOOR) + {} + else if (t != EMPTY) + { + player_score += 10; + } + } + + bool LaserTile(Pos p, int mask, double fireTime) + { + if (&renderer(p) == &renderer(Pos(-1,-1))) + return false; + //if (!renderer.Visible(p)) + // return false; + + TileRender* tr = 0; + if (time <= renderer(p).GetLastTime()) + if (fireTime < renderer(p).GetLastTime()) + { + renderer(p).Add(tr = new TileRender(GetTile(p), p, mask), fireTime); + ((TileRender*)renderer(p).GetStage(time+10/*HACKY!*/))->special |= mask; + } + else + { + tr = new TileRender(GetTile(p), p, mask | ((TileRender*)renderer(p).GetStage(time+10/*HACKY!*/))->special); + renderer(p).Add(tr, fireTime); + } + else + renderer(p).Add(tr = new TileRender(GetTile(p), p, mask), fireTime); + + if (tr) + { + tr->specialDuration = time + LASER_LINE_TIME - fireTime + LASER_FADE_TIME; + } + return true; + } + void FireGun(Pos newpos, Dir d, bool recurse, double fireTime) + { + static Pos hits[100]; + static Dir hitDir[100]; + static unsigned int numHits=0; + if (!recurse) + numHits = 0; + + double starttime = fireTime; + for (Dir fd=((d<0)?0:d); fd<((d<0)?MAX_DIR:d+1); fd++) + { + fireTime = starttime; + // starttime += 0.03; + + Pos p = newpos + fd; + int range = 0; + for (; range= sizeof(hits)/sizeof(hits[0])) + return; + hitDir[i] = 1 << fd; + hits[i] = p; + numHits++; + } + else + { + hitDir[i] |= 1 << fd; + } + if (t==TRAP) + { + int dirmask = + 1<<((fd+2) % MAX_DIR) + | 1<<((fd+MAX_DIR-2) % MAX_DIR); + + if (LaserTile(p, dirmask, fireTime)) + fireTime += (time+LASER_LINE_TIME - fireTime) / 40; +// fireTime += LASER_SEGMENT_TIME; + + FireGun(p, (fd+1) % MAX_DIR, true, fireTime); + FireGun(p, (fd+MAX_DIR-1) % MAX_DIR, true, fireTime); + } + } + break; + } + else + { + LaserTile(p, 1<<(fd%3), fireTime); + + fireTime += (time+LASER_LINE_TIME - fireTime) / 40; +// fireTime += LASER_SEGMENT_TIME; + } + } + +// renderer().Add(new LaserRender(newpos, fd, range), time); + } + + if (!recurse) + { + //double _time = time; + time += LASER_LINE_TIME; + for (unsigned int i=0; ir; + } + bool Input(Dir d) + { + if (dead || win || isMap) + return false; + + // Complete undo + UndoDone(); + + // Jump forwards in time to last move finishing + if (numUndo > 0 && time < undo[numUndo-1].endTime) + time = undo[numUndo-1].endTime; + + double realTime = time; + double endAnimTime = time; + bool high = (tileSolid[GetTile(player)] == 1); + Pos playerStartPos = player; + Pos oldpos = player; + int oldPlayerHeight = GetHeight(oldpos); + Pos newpos = player + d; + + int playerRot = GetLastPlayerRot(); + if (d!=-1 && d!=playerRot) + { + while (d!=playerRot) + { + if ((d+6-playerRot) % MAX_DIR < MAX_DIR/2) + playerRot = (playerRot+1) % MAX_DIR; + else + playerRot = (playerRot+MAX_DIR-1) % MAX_DIR; + + time += 0.03; + + if (GetTile(oldpos) == FLOATING_BALL) + { + TileRender* t = new TileRender(FLOATING_BALL, oldpos); + t->special = playerRot + 256; + renderer(oldpos).Add(t, time); + + renderer.player.Add(new PlayerRender(playerRot, Pos(-20,-20), oldPlayerHeight, Pos(-20,-20), oldPlayerHeight, dead), time); + } + else + { + PlayerRender *p = new PlayerRender(playerRot, player, oldPlayerHeight, player, oldPlayerHeight, dead); + p->speed = 0; + renderer.player.Add(p, time); + } + } + + time += 0.03; + } + + if (d<0 && player_items[1]==0) + return false; + + if (d >= 0) + { + if (tileSolid[GetTile(newpos)] == -1) + { + time = realTime; + return false; + } + if (Collide(newpos, high)) + { + time = realTime; + return false; + } + } + + // Don't change any real state before this point! + if (numUndo >= MAX_UNDO) + { + numUndo--; + for(int i=0; ispecial = 0; + renderer(oldpos).Add(t, time); + } + + PlayerRender *p = new PlayerRender(playerRot, player, oldPlayerHeight, newpos, GetHeight(newpos), dead); + + // alternate leg (hacky!) + if (1) + { + static int l=0; + l++; + p->type = l & 1; + } + + if (retry_pos_count!=0 && GetTile(player)==TRAP) + { + p->speed /= 1.5; + p->type = 2; + } + if (d==-1) + p->speed = JUMP_TIME * 1.5; + renderer.player.Add(p, time); + endAnimTime = MAX(endAnimTime, time + p->speed+0.001); + time += p->speed; + + player = newpos; + + switch (GetTile(newpos)) + { + case COLLAPSABLE: + renderer(newpos).Add(new TileRender(TILE_GREEN_CRACKED, newpos), time); + break; + case COLLAPSE_DOOR: + renderer(newpos).Add(new TileRender(TILE_GREEN_CRACKED_WALL, newpos), time); + break; + case COLLAPSABLE2: + renderer(newpos).Add(new TileRender(TILE_BLUE_CRACKED, newpos), time); + break; + case COLLAPSE_DOOR2: + renderer(newpos).Add(new TileRender(TILE_BLUE_CRACKED_WALL, newpos), time); + break; + + case EMPTY: + dead = true; + break; + + case BUILDER: + { + double pretime = time; + bool done = false; + time += 0.15; + for (Dir fd=0; fdspecial = 512; + renderer(newpos).Add(r, time); + + PlayerRender* pr = new PlayerRender(playerRot, newpos, 0, newpos, 0, dead); + pr->speed = JUMP_TIME*1; + renderer.player.Add(pr, time); + + time += pr->speed; + + dead = 1; + break; + } + oldpos = newpos; + newpos = oldpos + d; + + SetTile(oldpos, EMPTY, false); + SetTile(newpos, FLOATING_BALL, false); + + renderer(oldpos).Add(new TileRotateRender(FLOATING_BALL, oldpos, d, 2), time); + renderer(oldpos).Add(new TileRender(EMPTY, oldpos), time + ROTATION_TIME/2); + renderer(newpos).Add(new TileRotateRender(FLOATING_BALL, newpos, (d+3)%MAX_DIR, 3), time + ROTATION_TIME/2); + +// PlayerRender *p = new PlayerRender(playerRot, oldpos, 0, newpos, 0, dead); +// p->speed = ROTATION_TIME*0.9; +// renderer.player.Add(p, time); + + endAnimTime = MAX(endAnimTime, time + ROTATION_TIME + ROTATION_TIME/2); + time += ROTATION_TIME; + } + player = newpos; +// renderer.player.Add(new PlayerRender(playerRot, player, 0, player, 0, 0), time); + if (dead) + { + } + else + { + TileRender* r = new TileRender(FLOATING_BALL, newpos); + r->special = playerRot + 256; + renderer(newpos).Add(r, time); + } + } + break; + + case LIFT_DOWN: + case LIFT_UP: + { + SetTile(newpos, GetTile(newpos)==LIFT_UP ? LIFT_DOWN : LIFT_UP, false); + renderer(newpos).Add(new TileRender(GetTile(newpos), newpos, 1), time); + + PlayerRender *p = new PlayerRender(playerRot, newpos, 1-GetHeight(newpos), newpos, GetHeight(newpos), dead); + renderer.player.Add(p, time); + endAnimTime = MAX(endAnimTime, time + JUMP_TIME); + } + break; + + case TRAMPOLINE: + if (d<0) break; + + oldpos = newpos; + if (Collide(newpos + d, high)) + break; + if (Collide((newpos + d) + d, high) == 1) + newpos = (newpos + d); + else + newpos = (newpos + d) + d; + if (tileSolid[GetTile(newpos)] == -1) + dead=1; + //player = newpos; + goto retry_pos; + + case SPINNER: + { + for (Dir d=0; dspeed = JUMP_TIME*1; + renderer.player.Add(pr, time); + + time += pr->speed; + dead = 1; + } + + /* + Pos hits[MAX_DIR]; + int numHits=0; + + for (Dir fd=((d<0)?0:d); fd<((d<0)?MAX_DIR:d+1); fd++) + { + Pos p = newpos + fd; + int range = 0; + for (range; rangespeed = 0; // Don't sit around before disappearing! + renderer.player.Add(pr, time); + + // If the tile we're drowning on isn't visible, give the ownership of the splash effect to the player, rather than a tile. + if (renderer.Visible(player)) + renderer(player).Add(new ExplosionRender(player, 0, 1), time); + else + renderer.player.Add(new ExplosionRender(player, 0, 1), time); + + endAnimTime = MAX(endAnimTime, time+2); + } + + time = realTime; + + player_score += 1; + + undo[numUndo].endTime = endAnimTime; + numUndo++; + + return true; + } + void Update(double timedelta) + { + while(deadMenu) + delete deadMenu; + + if (activeMenu) + { + activeMenu->Update(timedelta); + } + else + UpdateKeys(); + + for (int i=0; i SCREEN_W) xx = SCREEN_W; + int w = TILE_W2*4; + if (xx < w) + x = (double)xx / (w) - 1; + if (xx > SCREEN_W - w) + x = 1 - (double)(SCREEN_W-xx) / (w); + x *= 500; + if (x<-min || x>min) + { + scrollHi += timedelta * x; + scrollX += (int)scrollHi; + scrollHi -= (int)scrollHi; + } + } +#endif + } + if (undoTime>=0 && undoTime < time) + { + double acc = (time - undoTime) / 2; + if (acc < 3) acc = 3; + time -= timedelta * acc; + if (undoTime >= time) + UndoDone(); + } + else + { + time += timedelta; + if (turboAnim) + time += timedelta * 20; + } + } + void FileDrop(const char* filename) + { + LoadSave(filename, false); + } + void UpdateKeys() + { +#ifdef EDIT + if (keyState[SDLK_LALT] || keyState[SDLK_LCTRL]) + return; +#endif + + if (!isMap && !editMode && undoTime < 0) + { + if (keyState[SDLK_z] || keyState[SDLK_BACKSPACE] || keyState[SDLK_u]) + { + Undo(); + return; + } + } + if (isMap && !editMode) + { + + if ((keyState[SDLK_q] | keyState[SDLK_KP7]) & 2) keyboardp.x--; + else if ((keyState[SDLK_d] | keyState[SDLK_KP3]) & 2) keyboardp.x++; + else if ((keyState[SDLK_e] | keyState[SDLK_KP9]) & 2) keyboardp.x++, keyboardp.y--; + else if ((keyState[SDLK_a] | keyState[SDLK_KP1]) & 2) keyboardp.x--, keyboardp.y++; + else if ((keyState[SDLK_w] | keyState[SDLK_KP8] | keyState[SDLK_UP]) & 2) keyboardp.y--; + else if ((keyState[SDLK_s] | keyState[SDLK_KP2] | keyState[SDLK_DOWN]) & 2) keyboardp.y++; + else if ((keyState[SDLK_LEFT]) & 2) keyboardp.x--, keyboardp.y+=keyboardp.x&1; + else if (((keyState[SDLK_RIGHT]) & 2)) { if (keyboardp.x < mapRightBound) keyboardp.y-=keyboardp.x&1, keyboardp.x++; } + else if ((keyState[SDLK_RETURN] | keyState[SDLK_KP5] | keyState[SDLK_SPACE] | keyState[SDLK_KP_ENTER]) & 2) + { + // Simulate user clicking on it... + Mouse(keyboardp.getScreenX()-scrollX, keyboardp.getScreenY()-scrollY, 0, 0, 1, 0, 0); + noMouse = 1; + return; + } + else + { + if (noMouse) + UpdateCursor(keyboardp); + return; + } + int min[21] = { 17, 16, 15, 14, 13, 13, 13, 13, 13, 13, 12, 11, 11, 13, 12, 11, 8, 8, 7, 6, 7 }; + int max[21] = { 20, 20, 19, 19, 19, 19, 18, 21, 20, 20, 19, 19, 18, 18, 17, 16, 16, 16, 15, 15, 14 }; + if (keyboardp.x < 3) keyboardp.x = 3; + if (keyboardp.x > mapRightBound) keyboardp.x = mapRightBound; + + if (keyboardp.y < min[keyboardp.x-3]) keyboardp.y = min[keyboardp.x-3]; + if (keyboardp.y > max[keyboardp.x-3]) keyboardp.y = max[keyboardp.x-3]; + noMouse = 1; + UpdateCursor(keyboardp); + } + else if (!editMode && (numUndo==0 || time>=undo[numUndo-1].endTime)) + { + static int usedDiag = 0; + + if (keyState[SDLK_q] || keyState[SDLK_KP7]) HandleKey('q', 0); + else if (keyState[SDLK_w] || keyState[SDLK_KP8]) HandleKey('w', 0); + else if (keyState[SDLK_e] || keyState[SDLK_KP9]) HandleKey('e', 0); + else if (keyState[SDLK_a] || keyState[SDLK_KP1]) HandleKey('a', 0); + else if (keyState[SDLK_s] || keyState[SDLK_KP2]) HandleKey('s', 0); + else if (keyState[SDLK_d] || keyState[SDLK_KP3]) HandleKey('d', 0); + + else if (keyState[SDLK_UP] && keyState[SDLK_LEFT]) HandleKey('q', 0), usedDiag=1; + else if (keyState[SDLK_UP] && keyState[SDLK_RIGHT]) HandleKey('e', 0), usedDiag=1; + else if (keyState[SDLK_DOWN] && keyState[SDLK_LEFT]) HandleKey('a', 0), usedDiag=1; + else if (keyState[SDLK_DOWN] && keyState[SDLK_RIGHT]) HandleKey('d', 0), usedDiag=1; + else if (keyState[SDLK_UP] && !usedDiag) HandleKey('w', 0); + else if (keyState[SDLK_DOWN] && !usedDiag) HandleKey('s', 0); + + else usedDiag = 0; + } + } + void KeyReleased(int key) + { + keyState[key] = 0; + } + bool KeyPressed(int key, int mod) + { + keyState[key] = 2; + + if (activeMenu) + { + bool eat = activeMenu->KeyPressed(key, mod); + if (!activeMenu) + memset(keyState, 0, sizeof(keyState)); + return eat; + } + else + { + if ((key==SDLK_ESCAPE && (mod & KMOD_CTRL))) + { + if (mod & KMOD_SHIFT) + { + time = 0; + renderer.Reset(); + LoadSaveProgress(false); + } + + LoadMap(); + } + + if (isFadeRendering) + return false; + + return HandleKey(key, mod); + } + } + bool HandleKey(int key, int mod) + { + turboAnim = 0; + +#ifdef CHEAT + if (isMap && key=='r' && (mod & KMOD_ALT)) + { + progress.Clear(); + LoadMap(); + } +#endif + + if (0) {} + + else if ((key=='p' && !editMode || key==SDLK_PAUSE || key==SDLK_ESCAPE)) + { + noMouse = 1; + new PauseMenu(isMap, progress.GetLevel(STARTING_LEVEL, true)->Completed(), progress.general.endSequence>=1, progress.general.endSequence>=2); + } + +#ifdef EDIT + else if (key=='e' && (mod & KMOD_ALT)) + editMode = !editMode; + + else if (key=='p' && (mod & KMOD_ALT) && numUndo>0 + || key>='0' && key<='9' && (mod & KMOD_SHIFT) && !isMap) + { + if (key>='0' && key<='9') + levelDiff = (key=='0') ? 10 : key-'0'; + + if (key=='p' && levelPar==0) + levelPar = player_score; + + if (numUndo) + { + do + undo[numUndo-1].Restore(this); + while (--numUndo); + } + time = 0; + if (LoadSave(currentFile, true)) + { + if (key>='0' && key<='9') + LoadMap(); + } + } +#endif + + ///////////////////////////////////////////////////////////////////////// + if (isMap && !editMode) + return false; + + else if (key==SDLK_KP9 || key=='e') Input(1), noMouse=1; + else if (key==SDLK_KP3 || key=='d') Input(2), noMouse=1; + else if (key==SDLK_KP1 || key=='a') Input(4), noMouse=1; + else if (key==SDLK_KP7 || key=='q') Input(5), noMouse=1; + else if (key==SDLK_KP8 || key=='w') Input(0), noMouse=1; + else if (key==SDLK_KP2 || (key=='s' && (((mod & (KMOD_CTRL|KMOD_ALT))==0)||!editMode))) Input(3), noMouse=1; + else if (key==SDLK_KP5 || key==SDLK_SPACE || key==SDLK_RETURN || key==SDLK_KP_ENTER) + { + noMouse=1; + if (win && winFinal) + LoadMap(), memset(keyState, 0, sizeof(keyState)); + else + Input(-1); + } + + else if (key=='r' && (mod & KMOD_CTRL)) + LoadSave(currentFile, false); + +#ifdef EDIT + else if (key=='z' && (mod & KMOD_ALT)) + { + if (numUndo>0 && !isMap) + { + time = undo[numUndo-1].endTime; + undoTime = undo[0].time; + + do + undo[numUndo-1].Restore(this); + while (--numUndo); + } + } +#endif + else if (key=='z' || key==SDLK_BACKSPACE || key==SDLK_DELETE || key=='u') + { + if (!isMap) + Undo(); + } + +#ifdef EDIT + else if (key=='s' && (mod & KMOD_ALT)){ + if (win && strlen(currentFile)>0 && !isMap) + { + char tmp[1000]; + strcpy(tmp, currentFile); + ChangeSuffix(tmp, "sol"); + FILE* f = file_open(tmp, "wb"); + if (f) + { + for (int i=0; iCompleted()) + { + for (int i=0; ibestSolutionLength; i++) + Input(l->bestSolution[i]); + time = 0; + } + if (!win && l) + l->Clear(); + } + else + { + char tmp[1000]; + strcpy(tmp, currentFile); + ChangeSuffix(tmp, "sol"); + FILE* f = file_open(tmp, "rb"); + if (f) + { + int dir; + while ((dir = fgetc(f)) != -1) + { + if (dir==0xff) + dir = -1; + Input(dir); + } + time = 0; + fclose(f); + + if (!win) + remove(tmp); + } + } + } + } +#endif + +#ifdef EDIT + else if (!editMode) + return false; + + else if (key>='0' && key<='9' && (mod & KMOD_ALT) && !isMap) + levelPar = levelPar*10 + key-'0'; + else if (key==SDLK_BACKSPACE && (mod & KMOD_ALT) && !isMap) + levelPar /= 10; + + else if (key=='i') + Mouse(mousex, mousey, 0, 0, 32, 0, mouse_buttons); + else if (key=='p' && !(mod & KMOD_ALT)) + Mouse(mousex, mousey, 0, 0, 64, 0, mouse_buttons); + else if (key=='x') + Mouse(mousex, mousey, 0, 0, 128, 0, mouse_buttons); + else if (key==SDLK_RETURN) + Mouse(mousex, mousey, 0, 0, 256, 0, mouse_buttons); + else if (key==SDLK_BACKSPACE) + Mouse(mousex, mousey, 0, 0, 512, 0, mouse_buttons); + else if (key=='c') + Mouse(mousex, mousey, 0, 0, 1024, 0, mouse_buttons); + + else if (key=='s' && (mod & KMOD_CTRL)){ + char *fn = LoadSaveDialog(true, true, _("Save level")); + LoadSave(fn, true); + SDL_WM_SetCaption(currentFile, NULL); + } + + else if (key=='o' && (mod & KMOD_CTRL)){ + char* fn = LoadSaveDialog(false, true, _("Open level")); + LoadSave(fn, false); + SDL_WM_SetCaption(currentFile, NULL); + } +#endif + + else + return false; + + return true; + } + void LoadGraphics() + { + #define X(NAME,FILE,ALPHA) NAME = Load(DATA_DIR "/graphics/" FILE BMP_SUFFIX, ALPHA); + #include "gfx_list.h" + + static int first = 1; + if (first) + { + first = false; + MakeTileInfo(); + } + + // unsigned int d = { + + // }; + // static SDL_Cursor * c = SDL_CreateCursor(data, mask, 32, 32, 1, 1); + // SDL_SetCursor(c); + SDL_ShowCursor(1); + } + void FreeGraphics() + { + #define X(NAME,FILE,ALPHA) if (NAME) SDL_FreeSurface(NAME), NAME=0; + #include "gfx_list.h" + } + virtual void ScreenModeChanged() + { +// FreeGraphics(); +// LoadGraphics(); + } +}; + +MAKE_STATE(HexPuzzle, SDLK_F1, false); + +char * HexPuzzle::loadPtr = 0; +char * HexPuzzle::endLoad = 0; + +#endif //USE_OPENGL diff --git a/level_list.h b/level_list.h index 74783fb..5993a3a 100644 --- a/level_list.h +++ b/level_list.h @@ -1,138 +1,138 @@ -/* - 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 -*/ - - -struct LevelInfo{ - int hintID; - const char *file, *name; -} levelNames[] = { - -/* TRANSLATORS: The following strings (in level_list.h) are level names, which - should give (in a humerous way) the player an indication about this level. */ -{0, "map_maybe\\map.lev", _(" Map"),}, -{0, "0_green\\asymmetrix.lev", _(" Orbital"),}, -{0, "0_green\\hive.lev", _(" Hive"),}, -{0, "0_green\\there and back.lev", _(" There and Back"),}, -{0, "0_green\\triangular.lev", _(" Triangular"),}, -{0, "1_trampoline\\01.lev", _(" Mini Island"),}, -{0, "1_trampoline\\01_b.lev", _(" Island Variation"),}, -{0, "1_trampoline\\archipeligo.lev", _(" Archipelago"),}, -{0, "1_trampoline\\arrow.lev", _(" Weathervane"),}, -{0, "1_trampoline\\Bridges.lev", _(" Bridges"),}, -{0, "1_trampoline\\explorer.lev", _(" Explorer"),}, -{0, "1_trampoline\\test_trampoline.lev", _(" Trampolines"),}, -{0, "2_greendoor\\Loopy.lev", _(" Not a Knot"),}, -{0, "2_greendoor\\more mountain.lev", _(" Another Mountain"),}, -{0, "2_greendoor\\Mountain.lev", _(" A Mountain"),}, -{0, "2_greendoor\\test_green_gate.lev", _(" Green Walls"),}, -{0, "2_greendoor\\winding order.lev", _(" Winding Order"),}, -{0, "2005_11_15\\boating.lev", _(" Rental Boat"),}, -{0, "2005_11_15\\ferry.lev", _(" Ferrying"),}, -{0, "2005_11_15\\HUB.LEV", _(" Transport Hub"),}, -{0, "2005_11_15\\learn lift.lev", _(" Lifts"),}, -{0, "2005_11_15\\leftovers.lev", _(" Leftovers"),}, -{0, "2005_11_15\\lumpy.lev", _(" Trampoline Retrieval"),}, -{0, "2005_11_15\\rolling hexagons.lev", _(" Rolling Hexagons"),}, -{0, "2005_11_15\\telephone.lev", _(" Telephone"),}, -{0, "2005_11_16\\breakthrough.lev", _(" Breakthrough"),}, -{0, "2005_11_19\\aa.lev", _(" Laser Safety"),}, -{0, "2005_11_19\\branches.lev", _(" Branching Pathway"),}, -{0, "2005_11_19\\one way up.lev", _(" Only One Way Up"),}, -{0, "2005_11_19\\outposts.lev", _(" Outposts"),}, -{0, "2005_11_19\\turntables.lev", _(" Roundabouts"),}, -{0, "2005_11_19\\two fish.lev", _(" Two Fish"),}, -{0, "3_2hitfloor\\all wound up.lev", _("All Wound Up"),}, -{0, "3_2hitfloor\\collapse2.lev", _(" Toughened Tiles"),}, -{0, "3_2hitfloor\\Island.lev", _(" Island"),}, -{0, "3_2hitfloor\\more stripes.lev", _(" More Stripes"),}, -{0, "3_2hitfloor\\Stripey.lev", _(" Stripes"),}, -{0, "3_2hitfloor\\test_2hit_floor.lev", _(" One Two One Two"),}, -{0, "3_2hitfloor\\Turtle.lev", _(" Turtle"),}, -{0, "3_2hitfloor\\Wand.lev", _(" Wand"),}, -{0, "4_gun\\deathtrap.lev", _(" Deathtrap"),}, -{0, "4_gun\\eagerness.lev", _(" Eagerness"),}, -{0, "4_gun\\gun platform.lev", _(" Gun Platform"),}, -{0, "4_gun\\Nucleus.lev", _(" Nucleus"),}, -{0, "4_gun\\Sniper.lev", _(" Sniper"),}, -{0, "4_gun\\snowflake 2.lev", _(" Deadly Snowflake"),}, -{0, "4_gun\\snowflake.lev", _(" Snowflake"),}, -{0, "4_gun\\Test_gun.lev", _(" Laser Tiles"),}, -{0, "4_gun\\trigger happy.lev", _(" Trigger Happy"),}, -{0, "5_spinner\\lure.lev", _(" Lure"),}, -{0, "5_spinner\\Maxe.lev", _(" Maze"),}, -{0, "5_spinner\\Motion.lev", _(" Motion Sickness"),}, -{0, "5_spinner\\preperation mk 3.lev", _(" All About Preparation"),}, -{0, "5_spinner\\revolver cannon.lev", _(" Revolver Cannon"),}, -{0, "5_spinner\\small cog.lev", _(" Small Cog"),}, -{0, "5_spinner\\Sprocket.lev", _(" Sprocket"),}, -{0, "5_spinner\\switch.lev", _(" Switch"),}, -{0, "5_spinner\\test_spinner.lev", _(" Spinner Tiles"),}, -{0, "5_spinner\\three more ways.lev", _(" Three More Ways"),}, -{0, "5_spinner\\three ways mk 2.lev", _(" Three Ways To Go"),}, -{0, "6_ice\\oo.lev", _("Please Skate Safely"),}, -{0, "6_ice\\refraction.lev", _(" Refraction"),}, -{0, "6_ice\\route finder.lev", _(" Route Finder"),}, -{0, "6_ice\\slippy.lev", _("A Slippery Situation"),}, -{0, "7_item\\crooked.lev", _(" Crooked"),}, -{0, "7_item\\green honey.lev", _(" Green Honey"),}, -{0, "7_item\\kx.lev", _(" Carefully Does It"),}, -{0, "7_item\\radioactive ice.lev", _(" Radioactive Ice"),}, -{0, "7_item\\slider.lev", _(" Pro Skater"),}, -{0, "7_item\\spinners mk2.lev", _(" Spinners II"),}, -{0, "7_item\\spinners.lev", _(" Spinners"),}, -/* TRANSLATORS: There is a special kind of pickup, which will turn ice - plates into "normal" plates (you won't slip on them). So the name - is (probably) related to "Make less slippery". */ -{0, "7_item\\test_ice.lev", _(" Deslippify"),}, -{0, "7_item\\tt.lev", _(" Tri Mesh"),}, -{0, "7_item\\Wheel.lev", _(" Wheel"),}, -{0, "8_item2\\finishing strike.lev", _(" Finishing Strike"),}, -{0, "8_item2\\p2.lev", _(" Big Jumps"),}, -{0, "8_item2\\wave cannon.lev", _(" Wave Cannon"),}, -{0, "9_boat\\clearance.lev", _(" Clearance"),}, -{0, "9_boat\\floating.lev", _(" Floating"),}, -{0, "9_boat\\forced fire.lev", _(" Forced Fire"),}, -{0, "9_boat\\no swimming allowed.lev", _("No Swimming Allowed"),}, -{0, "a.lev", _("A Little Light Lifting"),}, -{0, "A_Lift\\house.lev", _(" House"),}, -{0, "A_Lift\\hunting.lev", _(" Hunting"),}, -{0, "A_Lift\\Lifting.lev", _(" More Lifting"),}, -{0, "A_Lift\\opportunist_mini.lev", _(" Opportunist"),}, -{0, "A_Lift\\test_lift.lev", _(" Demolition"),}, -{0, "A_Lift\\upper.lev", _(" Upper"),}, -{0, "b.lev", _("Beware Feedback Loops"),}, -{0, "B_Builder\\airlock ending.lev", _(" Somewhat Constructive"),}, -{0, "B_Builder\\overbuild.lev", _(" Overbuild"),}, -{0, "B_Builder\\reversing space.lev", _(" Reversing Space"),}, -{0, "B_Builder\\test_builder.lev", _("Burn Your Bridges"),}, -{0, "c.lev", _("A Strange Place"),}, -{0, "commute.lev", _(" Commute"),}, -{0, "d.lev", _(" Bouncing Required"),}, -{0, "de-icing.lev", _(" Fetch Quest"),}, -{0, "e.lev", _(" Laser Surgery"),}, -{0, "ice intro.lev", _(" Icy Tiles"),}, -{0, "icy road.lev", _(" Icy Road"),}, -{0, "invertor.lev", _(" Inversion"),}, - -{0, "_20", _("Complete __ levels to unlock")}, -{0, "_35", _("Complete __ levels to unlock")}, -{0, "_55", _("Complete __ levels to unlock")}, -{0, "_75", _("Complete __ levels to unlock")}, -{0, "_90", _("Complete __ levels to unlock")}, - -}; +/* + 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 +*/ + + +struct LevelInfo{ + int hintID; + const char *file, *name; +} levelNames[] = { + +/* TRANSLATORS: The following strings (in level_list.h) are level names, which + should give (in a humerous way) the player an indication about this level. */ +{0, "map_maybe\\map.lev", _(" Map"),}, +{0, "0_green\\asymmetrix.lev", _(" Orbital"),}, +{0, "0_green\\hive.lev", _(" Hive"),}, +{0, "0_green\\there and back.lev", _(" There and Back"),}, +{0, "0_green\\triangular.lev", _(" Triangular"),}, +{0, "1_trampoline\\01.lev", _(" Mini Island"),}, +{0, "1_trampoline\\01_b.lev", _(" Island Variation"),}, +{0, "1_trampoline\\archipeligo.lev", _(" Archipelago"),}, +{0, "1_trampoline\\arrow.lev", _(" Weathervane"),}, +{0, "1_trampoline\\Bridges.lev", _(" Bridges"),}, +{0, "1_trampoline\\explorer.lev", _(" Explorer"),}, +{0, "1_trampoline\\test_trampoline.lev", _(" Trampolines"),}, +{0, "2_greendoor\\Loopy.lev", _(" Not a Knot"),}, +{0, "2_greendoor\\more mountain.lev", _(" Another Mountain"),}, +{0, "2_greendoor\\Mountain.lev", _(" A Mountain"),}, +{0, "2_greendoor\\test_green_gate.lev", _(" Green Walls"),}, +{0, "2_greendoor\\winding order.lev", _(" Winding Order"),}, +{0, "2005_11_15\\boating.lev", _(" Rental Boat"),}, +{0, "2005_11_15\\ferry.lev", _(" Ferrying"),}, +{0, "2005_11_15\\HUB.LEV", _(" Transport Hub"),}, +{0, "2005_11_15\\learn lift.lev", _(" Lifts"),}, +{0, "2005_11_15\\leftovers.lev", _(" Leftovers"),}, +{0, "2005_11_15\\lumpy.lev", _(" Trampoline Retrieval"),}, +{0, "2005_11_15\\rolling hexagons.lev", _(" Rolling Hexagons"),}, +{0, "2005_11_15\\telephone.lev", _(" Telephone"),}, +{0, "2005_11_16\\breakthrough.lev", _(" Breakthrough"),}, +{0, "2005_11_19\\aa.lev", _(" Laser Safety"),}, +{0, "2005_11_19\\branches.lev", _(" Branching Pathway"),}, +{0, "2005_11_19\\one way up.lev", _(" Only One Way Up"),}, +{0, "2005_11_19\\outposts.lev", _(" Outposts"),}, +{0, "2005_11_19\\turntables.lev", _(" Roundabouts"),}, +{0, "2005_11_19\\two fish.lev", _(" Two Fish"),}, +{0, "3_2hitfloor\\all wound up.lev", _("All Wound Up"),}, +{0, "3_2hitfloor\\collapse2.lev", _(" Toughened Tiles"),}, +{0, "3_2hitfloor\\Island.lev", _(" Island"),}, +{0, "3_2hitfloor\\more stripes.lev", _(" More Stripes"),}, +{0, "3_2hitfloor\\Stripey.lev", _(" Stripes"),}, +{0, "3_2hitfloor\\test_2hit_floor.lev", _(" One Two One Two"),}, +{0, "3_2hitfloor\\Turtle.lev", _(" Turtle"),}, +{0, "3_2hitfloor\\Wand.lev", _(" Wand"),}, +{0, "4_gun\\deathtrap.lev", _(" Deathtrap"),}, +{0, "4_gun\\eagerness.lev", _(" Eagerness"),}, +{0, "4_gun\\gun platform.lev", _(" Gun Platform"),}, +{0, "4_gun\\Nucleus.lev", _(" Nucleus"),}, +{0, "4_gun\\Sniper.lev", _(" Sniper"),}, +{0, "4_gun\\snowflake 2.lev", _(" Deadly Snowflake"),}, +{0, "4_gun\\snowflake.lev", _(" Snowflake"),}, +{0, "4_gun\\Test_gun.lev", _(" Laser Tiles"),}, +{0, "4_gun\\trigger happy.lev", _(" Trigger Happy"),}, +{0, "5_spinner\\lure.lev", _(" Lure"),}, +{0, "5_spinner\\Maxe.lev", _(" Maze"),}, +{0, "5_spinner\\Motion.lev", _(" Motion Sickness"),}, +{0, "5_spinner\\preperation mk 3.lev", _(" All About Preparation"),}, +{0, "5_spinner\\revolver cannon.lev", _(" Revolver Cannon"),}, +{0, "5_spinner\\small cog.lev", _(" Small Cog"),}, +{0, "5_spinner\\Sprocket.lev", _(" Sprocket"),}, +{0, "5_spinner\\switch.lev", _(" Switch"),}, +{0, "5_spinner\\test_spinner.lev", _(" Spinner Tiles"),}, +{0, "5_spinner\\three more ways.lev", _(" Three More Ways"),}, +{0, "5_spinner\\three ways mk 2.lev", _(" Three Ways To Go"),}, +{0, "6_ice\\oo.lev", _("Please Skate Safely"),}, +{0, "6_ice\\refraction.lev", _(" Refraction"),}, +{0, "6_ice\\route finder.lev", _(" Route Finder"),}, +{0, "6_ice\\slippy.lev", _("A Slippery Situation"),}, +{0, "7_item\\crooked.lev", _(" Crooked"),}, +{0, "7_item\\green honey.lev", _(" Green Honey"),}, +{0, "7_item\\kx.lev", _(" Carefully Does It"),}, +{0, "7_item\\radioactive ice.lev", _(" Radioactive Ice"),}, +{0, "7_item\\slider.lev", _(" Pro Skater"),}, +{0, "7_item\\spinners mk2.lev", _(" Spinners II"),}, +{0, "7_item\\spinners.lev", _(" Spinners"),}, +/* TRANSLATORS: There is a special kind of pickup, which will turn ice + plates into "normal" plates (you won't slip on them). So the name + is (probably) related to "Make less slippery". */ +{0, "7_item\\test_ice.lev", _(" Deslippify"),}, +{0, "7_item\\tt.lev", _(" Tri Mesh"),}, +{0, "7_item\\Wheel.lev", _(" Wheel"),}, +{0, "8_item2\\finishing strike.lev", _(" Finishing Strike"),}, +{0, "8_item2\\p2.lev", _(" Big Jumps"),}, +{0, "8_item2\\wave cannon.lev", _(" Wave Cannon"),}, +{0, "9_boat\\clearance.lev", _(" Clearance"),}, +{0, "9_boat\\floating.lev", _(" Floating"),}, +{0, "9_boat\\forced fire.lev", _(" Forced Fire"),}, +{0, "9_boat\\no swimming allowed.lev", _("No Swimming Allowed"),}, +{0, "a.lev", _("A Little Light Lifting"),}, +{0, "A_Lift\\house.lev", _(" House"),}, +{0, "A_Lift\\hunting.lev", _(" Hunting"),}, +{0, "A_Lift\\Lifting.lev", _(" More Lifting"),}, +{0, "A_Lift\\opportunist_mini.lev", _(" Opportunist"),}, +{0, "A_Lift\\test_lift.lev", _(" Demolition"),}, +{0, "A_Lift\\upper.lev", _(" Upper"),}, +{0, "b.lev", _("Beware Feedback Loops"),}, +{0, "B_Builder\\airlock ending.lev", _(" Somewhat Constructive"),}, +{0, "B_Builder\\overbuild.lev", _(" Overbuild"),}, +{0, "B_Builder\\reversing space.lev", _(" Reversing Space"),}, +{0, "B_Builder\\test_builder.lev", _("Burn Your Bridges"),}, +{0, "c.lev", _("A Strange Place"),}, +{0, "commute.lev", _(" Commute"),}, +{0, "d.lev", _(" Bouncing Required"),}, +{0, "de-icing.lev", _(" Fetch Quest"),}, +{0, "e.lev", _(" Laser Surgery"),}, +{0, "ice intro.lev", _(" Icy Tiles"),}, +{0, "icy road.lev", _(" Icy Road"),}, +{0, "invertor.lev", _(" Inversion"),}, + +{0, "_20", _("Complete __ levels to unlock")}, +{0, "_35", _("Complete __ levels to unlock")}, +{0, "_55", _("Complete __ levels to unlock")}, +{0, "_75", _("Complete __ levels to unlock")}, +{0, "_90", _("Complete __ levels to unlock")}, + +}; diff --git a/levels.dat b/levels.dat index 3739a194386cfab367485cbe8b89357e597fe496..5963d11922c2e7551a5bf4550c1cbc7ff369fdcd 100644 GIT binary patch delta 346 zcwQaMO(?^09LMo}e>NLUGn0qLX1461S=+<`Q)p>y{z#G>C_4~}g$qiGn>L@6loJOO z#h3DM=0JIha$6xM5~UQR7Q{$a@(s$5HIIG2ojjiyFq*Y9Xy6h7REl}fdVJ-lIeud zZH}Ybvcb<5FTq%J&F@8SVzVtuylYnwA37EYP0m&RaKN~A=Un&1L+%QJP45x+ zmh^~~^HSoAvT+3mD>Z~p-xDd>Uo|TyB`#`axUKez*yfMO$pB9v6Wf6?*z4nDgrqBg zqhXr#|NSLfg|mi1lm}bMgl=mgc7mJe3gwdpZ`K?vhSEp_Q==}me`Gl9>JN{m>+x&P j6IvZ&_Dp<+YzcjZR`NlTw(uhzV83Yvoz16gZJ8H;^9Xu< delta 449 zcwYOnf^p|c#tFKNyc2b|0*Ob(jJ%se8ILlu@iHA2TuYGVx9>m$?E|;w^iF z18gD>1H{xj&4qf8!0v|lp2*4`xOS^GyEh)eMwx@a_hZ+&&>dGT!F`kkt2; zW#Z+X?BlD(%*!u1d4q}+$T7ZhKyr<*9TTtQO8?YEiDg>Say5m*gBqjjmL3>RA diff --git a/menus.h b/menus.h index 5912977..70e794b 100644 --- a/menus.h +++ b/menus.h @@ -1,1269 +1,1269 @@ -/* - 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 - -struct Menu; -Menu* activeMenu = 0; -Menu* deadMenu = 0; - -int SDLPangoTextHeight(const std::string &text_utf8, int width); - -static void HackKeyPress(int key, int mod) -{ - int k = keyState[key]; - StateMakerBase::current->KeyPressed(key, mod); - keyState[key] = k; -} - -struct Menu { - bool renderBG; - - Menu() : renderBG(true), under(activeMenu), time(0) { activeMenu = this; } - - virtual ~Menu() { - if(this!=deadMenu) FATAL("this!=deadMenu"); - deadMenu=under; - } - - static void Pop() - { - if (!activeMenu) return; - Menu* m = activeMenu; - activeMenu = activeMenu->under; - m->under = deadMenu; - deadMenu = m; - } - - virtual bool KeyPressed(int key, int /*mod*/) - { - if (key=='w' || key==SDLK_UP || key==SDLK_KP8 || key=='q' || key=='e' || key==SDLK_KP7 || key==SDLK_KP9) - Move(-1), noMouse=1; - else if (key=='s' || key==SDLK_DOWN || key==SDLK_KP2 || key=='a' || key=='d' || key==SDLK_KP1 || key==SDLK_KP3) - Move(1), noMouse=1; - else if (key==' ' || key==SDLK_RETURN) - { - Select(); - noMouse=1; - } - else if (key==SDLK_ESCAPE || key==SDLK_BACKSPACE || key==SDLK_KP_PERIOD || key==SDLK_DELETE) - Cancel(); - else - return false; - return true; - } - virtual void Mouse(int /*x*/, int /*y*/, int /*dx*/, int /*dy*/, int buttons_pressed, int /*buttons_released*/, int /*buttons*/) - { - if (buttons_pressed==4 || buttons_pressed==2) - Cancel(); - } - - virtual void Move(int /*dir*/) {} - virtual void Select() {} - virtual void Cancel() {} - - virtual void Update(double timedelta) {time+=timedelta;} - virtual void Render() = 0; - - Menu * under; - double time; -}; - -const char * hint[] = { - -/*EMPTY*/ -_("Basic controls:|Move around with the keys Q,W,E,A,S,D or the numeric keypad. Alternatively, you can use the mouse and click on the tile you'd like to move to. Use 'U', backspace or the right mouse button to undo mistakes. The 'Esc' key (or middle mouse button) brings up a menu from which you can restart if you get stuck."), -/*NORMAL*/ -0, -/*COLLAPSABLE*/ -_("Objective:|Your goal is to break all the green tiles. You mainly do this by jumping on them. They will crack when you land on them, and only disintegrate when you jump off. Try not to trap yourself!"), -/*COLLAPSE_DOOR*/ -_("The coloured walls flatten themselves when there are no matching coloured tiles remaining."), -/*TRAMPOLINE*/ -_("You can bounce on the purple trampoline tiles to get around. But try not to fall in the water. If you do, remember you can undo with 'U', backspace or the right mouse button!"), -/*SPINNER*/ -_("A red spinner tile will rotate the pieces around it when you step on it."), -/*WALL*/ -0, -/*COLLAPSABLE2*/ -_("You don't need to destroy blue tiles to complete the level. But they'll turn green when you step off them, and you know what you have to do to green tiles..."), -/*COLLAPSE_DOOR2*/ -0, -/*GUN*/ -_("Yellow laser tiles fire when you step on them. Shooting other laser tiles is more destructive."), -/*TRAP*/ -_("Ice is slippery! Please be careful!!"), -/*COLLAPSABLE3*/ -0, -/*BUILDER*/ -_("The dark grey tiles with arrows on are builders. Landing on one creates green tiles in any adjacent empty tile, and turns green tiles into walls."), -/*SWITCH*/ -0, -/*FLOATING_BALL*/ -/* TRANSLATORS: pop means vanish and Emy drowns (you loose) */ -_("You can ride on the pink floating boats to get across water. They'll pop if you try and float off the edge of the screen though, so look where you're going."), -/*LIFT_DOWN*/ -_("The blue lifts go up or down when you land on them."), -/*LIFT_UP*/ -0, - -0,0,0,0, -//Item 0 (21) -_("The spiky anti-ice pickups turn icy tiles into blue ones. They get used automatically when you land on ice."), -//Item 1 (22) -/* TRANSLATORS: Normally you jump from one plate to another. The golden jump (a - pickup) allows you to jump and land on the *same* plate */ -_("Collecting the golden jump pickups will allow you to do a big vertical jump. Try it out on different types of tiles. Use the space bar or return key to jump. Or click on the tile you're currently on with the mouse."), - -0,0, - -// Map (25) -_("Map Screen:|You can choose which level to attempt next from the map screen. Silver levels are ones you've cleared. Black levels are ones you haven't completed yet, but are available to play."), - -// Scoring (26) -/* TRANSLATORS: Levels are depicted as black balls. Once you passed them, they - turn silver. If you reached the par, they turn golden (with a crown), and if - you beat the par, they turn their shape/color once more */ -_("New feature unlocked!|Each level has an efficiency target for you to try and beat. Every move you make and each non-green tile you destroy counts against you. Why not try replaying some levels and going for gold?"), - -0,0,0, - -// End of help (30) -_("Thanks for playing this little game. I hope you enjoy it! -- -- All content is Copyright 2005 Tom Beaumont email: tombeaumont@yahoo.com Any constructive criticism gratefully received!"), - -// First help page (31) -/* TRANSLATORS: This string is copied twice into the POT file to workaround a - gettext limitation (no macro expansion). The extracted string "Welcome to " - will not be used. */ -_("Welcome to " GAMENAME "! This is a puzzle game based on hexagonal tiles. There is no time limit and no real-time element, so take as long as you like. Use the cursor keys or click on the arrows to scroll through the help pages. More pages will be added as you progress through the game."), -}; - -struct HintMessage : public Menu -{ - static int flags; - - SDL_Rect InnerTextWindowRect; - SDL_Rect OuterTextWindowRect; - const char * msg; - char title[500]; - int state; - - const SDL_Rect &GetInnerWindowRect() const - { - return InnerTextWindowRect; - } - - const SDL_Rect &GetOuterWindowRect() const - { - return OuterTextWindowRect; - } - - static bool FlagTile(int t, bool newStuff=true) - { - if (t==LIFT_UP) t=LIFT_DOWN; - if (newStuff && (flags & (1<31) return false; - if (!hint[t]) return false; - - flags |= 1<0.2) - PrintR(SCREEN_W-FONT_SPACING, SCREEN_H-FONT_SPACING, _("Press any key")); - } - - void Render(int x, int y) - { - //if (y<0) { - // std::cout << "Error in Render: " << x << " " << y << "\n"; // CHECKME - // y = 0; - //} - InnerTextWindowRect.x = x+TILE_W1; - InnerTextWindowRect.y = y; - OuterTextWindowRect.x = InnerTextWindowRect.x-2; - OuterTextWindowRect.y = InnerTextWindowRect.y-2-FONT_SPACING; - // Height is reduced in SDL_FillRect!!? Why? ==> Use a copy: - SDL_Rect r2 = InnerTextWindowRect; - SDL_Rect r = OuterTextWindowRect; - SDL_FillRect(screen, &r, SDL_MapRGB(screen->format, 60,90,90)); - SDL_FillRect(screen, &r2, SDL_MapRGB(screen->format, 20,50,50)); - Print(OuterTextWindowRect.x+FONT_SPACING/4, y-FONT_SPACING, "%s", title); - /* TRANSLATORS: This specifies how the text in the help dialog should - be aligned. Do *not* translate the text itself but use one of "left", - "center" or "right" (untranslated!). The default is "center". */ - std::string alignment = _("text alignment"); - if (alignment == "right") - Print_Aligned(true, InnerTextWindowRect.x + InnerTextWindowRect.w - FONT_SPACING, y+FONT_SPACING/2, InnerTextWindowRect.w - 2*FONT_SPACING, msg, 2); - else if (alignment == "left") - Print_Aligned(true, InnerTextWindowRect.x + FONT_SPACING, y+FONT_SPACING/2, InnerTextWindowRect.w - 2*FONT_SPACING, msg, 0); - else - Print_Aligned(true, x+SCREEN_W/2, y+FONT_SPACING/2, InnerTextWindowRect.w - 2*FONT_SPACING, msg, 1); - } - - virtual void Mouse(int /*x*/, int /*y*/, int /*dx*/, int /*dy*/, int buttons_pressed, int /*buttons_released*/, int /*buttons*/) - { - if (buttons_pressed && state==0 && time>0.2) - state = 1, time=0; - } - - bool KeyPressed(int /*key*/, int /*mod*/) - { - if (state==0 && time>0.2) - state = 1, time=0; - return true; - } - - virtual void Update(double timedelta) - { - Menu::Update(timedelta); - if(state && time > 0.2) - Pop(); - } -}; - -int HintMessage::flags = 1<<31 | 1<<30; - -struct HintReview : public HintMessage -{ - int page; - int page_dir; - int page_count; - int page_display; - HintReview() : HintMessage(hint[31]), page(31), page_dir(0), page_display(0) - { - page_count=0; - for (int i=0; i<32; i++) - if (flags & (1< HintMessage::GetOuterWindowRect().y+ - HintMessage::GetOuterWindowRect().h) - Move(1); - else - Cancel(); - } - else if (buttons_pressed==8) - Move(-1); - else if (buttons_pressed==16) - Move(1); - else - Menu::Mouse(x,y,dx,dy,buttons_pressed, buttons_released, buttons); - } - - bool KeyPressed(int key, int mod) - { - if (key==SDLK_LEFT) - Move(-1); - else if (key==SDLK_RIGHT) - Move(1); - else - return Menu::KeyPressed(key, mod); - return true; - } - - virtual void Render() - { - const double SPD = 10; - -#ifdef EDIT - sprintf (title, _("Help (Page --)"), page_display+1, page_count); -#else - sprintf (title, _("Help (Page %d/%d)"), page_display+1, page_count); -#endif - - int y=SCREEN_H/4; - if (state==0) - y = SCREEN_H/4+int(MAX(0, time*SPD)*-page_dir*SCREEN_H); - if (state==1) - y = SCREEN_H/4+int(MAX(0, 1-time*SPD)*page_dir*SCREEN_H); - - //if (!noMouse) - { - PrintC(false, SCREEN_W/2, y-FONT_SPACING*2, "^"); - PrintC(false, SCREEN_W/2, HintMessage::GetOuterWindowRect().y+ - HintMessage::GetOuterWindowRect().h, "_"); - } - - HintMessage::Render(0,y); - - if (time > 1.0/SPD && page_dir && state==0) - { - do { - page = (page+page_dir) & 31; -#ifdef EDIT - if (hint[page]) break; -#endif - } while (hint[page]==0 || !(flags&(1<1.0/SPD && state==1) - state=0, page_dir = 0; - } - virtual void Update(double timedelta) - { - Menu::Update(timedelta); - } - void Move(int dir) - { - if (page_dir) - return; - time = 0; - page_dir = dir; - state = 0; - } -}; - -#define MAX_GAMESLOT 4 -enum option { - OPT_GAMESLOT_0, - OPT_GAMESLOT_LAST = OPT_GAMESLOT_0 + MAX_GAMESLOT - 1, - OPT_RESUME, - OPT_RESTART, - OPT_GOTO_MAP, - OPT_GOTO_MAP_CONTINUE, - OPT_FULLSCREEN, - OPT_OPTIONS, - OPT_QUIT, - OPT_QUIT_CONFIRM, - OPT_QUIT_CANCEL, - OPT_QUIT_MENU_CONFIRM, - OPT_HELP, - OPT_GAMESLOT_NEW, - OPT_DELETE_CONFIRM, - OPT_DELETE_CANCEL, - OPT_UNDO, - OPT_BACK, - OPT_END, OPT_END2, -}; - -char optionSlotName[MAX_GAMESLOT][400] = { {0} }; -char currentSlot[800] = ""; -int freeSlot = -1; -char* GetSlotName(int i, char * t) -{ - sprintf(t, "save%d.dat", i+1); - return t; -} - -char * optionString[] = { - optionSlotName[0], - optionSlotName[1], - optionSlotName[2], - optionSlotName[3], - _("Resume"), - _("Restart Level"), - _("Return to Map"), - _("Continue"), - _("Toggle Fullscreen"), - _("Options"), - _("Quit"), - _("Yes"), - _("No"), - _("Return to Title"), - _("Help"), - _("Start New Game"), - _("Yes, really delete it!"), - _("Don't do it!"), - _("Undo Last Move"), - _("OK"), - _("View Credits Sequence"), _("View Credits Sequence"), -}; - - -struct OptMenu : public Menu -{ - int select; - int num_opt; - int opt[10]; - bool left_align; - const char * title; - SDL_Rect r, r2; - - OptMenu(const char * t) : select(0), title(t) - { - left_align = false; - num_opt = 0; - } - - void Init() - { - r.w=SCREEN_W/2; - r.x=(SCREEN_W-r.w)/2; - r.y=SCREEN_H/3; - - r2 = r; - - const int SPACE = int(FONT_SPACING * 1.5); - - r2.h = SPACE*num_opt + FONT_SPACING/2; - r.h = r2.h + (FONT_SPACING+2*2); - r.y -= FONT_SPACING+2; - r.w += 2*2; - r.x -= 2; - } - - void RenderOption(int o, const char * s) - { - int y = r2.y + FONT_SPACING/2 + int(FONT_SPACING * 1.5) * o; - if (left_align) - { - int x = r.x + SDLPangoTextWidth(" "); - int x1 = x + SDLPangoTextWidth("> "); - if (select==o) - { - //x += int( sin(time*9)*2.5 ); - //y += int( sin(time*9 + 1.5)*1.5 ); - Print(x, y, "> %s", s); - } - else - { - Print(x1, y, "%s", s); - } - } - else - { - int x = r.x + r.w/2; - if (select==o) - { - //x += int( sin(time*9)*2.5 ); - //y += int( sin(time*9 + 1.5)*1.5 ); - PrintC(false, x, y, "> %s <", s); - } - else - { - PrintC(false, x, y, "%s", s); - } - } - } - - void Move(int dir) - { - select += dir; - if (select<0) select = num_opt-1; - if (select>=num_opt) select = 0; - } - virtual void Mouse(int x, int y, int dx, int dy, int buttons_pressed, int buttons_released, int buttons) - { - if (1) - { - if (xr2.x+r2.w || y>r2.y+r2.h) - { - if (buttons_pressed!=4 && buttons_pressed!=2) - return; - } - else - { - select = (y-r2.y-FONT_SPACING/3) / int(FONT_SPACING*1.5); - if (select<0) select = 0; - if (select>=num_opt) select = num_opt-1; - } - } - if (buttons_pressed==1) - Select(); - Menu::Mouse(x, y, dx, dy, buttons_pressed, buttons_released, buttons); - } - void Select(); - - void Render() - { - RenderBG(); - RenderTitle(); - RenderOptions(); - } - void RenderBG() - { - SDL_FillRect(screen, &r, SDL_MapRGB(screen->format, 60,90,90)); - SDL_FillRect(screen, &r2, SDL_MapRGB(screen->format, 20,50,50)); - } - void RenderTitle() - { - if (left_align) - Print(r2.x+SDLPangoTextWidth(" "), r.y+4, title); - else - PrintC(false, r2.x+r2.w/2, r.y+4, title); - } - void RenderOptions() - { - for (int i=0; iformat, 10,25,25)); - - - SDL_BlitSurface(titlePage, &a, screen, &a); - - OptMenu::RenderTitle(); - OptMenu::RenderOptions(); - } - - void Init() - { - OptMenu::Init(); - - int xw = SCREEN_W/6; - r.w += xw; r2.w+=xw; - int x = SCREEN_W - r.x - r.w;// - FONT_SPACING; - int y = SCREEN_H - r.y - r.h + FONT_SPACING/4;// - FONT_SPACING; - r.x += x; r2.x += x; - r.y += y; r2.y += y; - r.w+=20; r2.w+=20; r.h+=20; r2.h+=20; - - r.h = r2.h = SCREEN_H; - r2.y = SCREEN_H/2; - r.y = r2.y - FONT_SPACING - 2; - } -}; - -const char *ending[] = { - _(" Very Well Done! "), - "", "", "", "", "", "", - - "", "", "*15,4", "", "", - - _("All Levels Cleared!"), - - "", "", "*5,7", "", "", - - _("Not a single green hexagon is left unbroken."), - "", - _("Truly, you are a master of hexagon hopping!"), - - "", "", "*9,10", "", "", - - "", _("Credits"), "", "", "", - _("Tom Beaumont", "", "", - _("Tom Beaumont", "", "", - _("Tom Beaumont", "", "", - _("Kris Beaumont", "", "", -// "", "Photoshop LE", ">Inno Setup", ">Wings 3D", ">MSVC", ">SDL", "", - _("Copperplate gothic bold", ">Verdana", "", - - "", "", "*12,14", "", "", - - _("Thanks for playing!") -}; - -const char *ending2[] = { - _(" Absolutely Amazing! "), - "", "", "", "", "", - - "", "", "*15,4", "", "", - - _("All Levels Mastered!!"), - - "", "", "*5,7", "", "", - - _("You crushed every last green hexagon with"), - _("breathtaking efficiency!"), - "", - _("You truly are a grand master of hexagon hopping!"), -}; - -const int endingLen = sizeof(ending)/sizeof(ending[0]); -const int endingLen2 = sizeof(ending2)/sizeof(ending2[0]); -const int scrollMax = SCREEN_H + (endingLen+1) * FONT_SPACING; - -struct Ending : public Menu -{ - struct Particle{ - double x,y,xs,ys,xa,ya; - double time; - int type; - - void Update(double td) - { - if (type==EMPTY) return; - - time -= td; - x += xs*td; - y += ys*td; - xs += xa*td; - ys += ya*td; - if (type==TILE_LASER_HEAD && time<0.3) - type=TILE_FIRE_PARTICLE_1; - if (type==TILE_FIRE_PARTICLE_1 && time<0.1) - type=TILE_FIRE_PARTICLE_2; - -// if (type==COLLAPSABLE || type==COLLAPSE_DOOR) -// for (int i=int((time)*40); iSCREEN_H && type==TILE_GREEN_FRAGMENT) - xa=0, ys=-ys/2, y=SCREEN_H, type=TILE_GREEN_FRAGMENT_1, time+=rand()%100*0.01; - if (y>SCREEN_H && type==TILE_GREEN_FRAGMENT_1) - xa=0, ys=-ys/2, y=SCREEN_H, type=TILE_GREEN_FRAGMENT_2, time+=rand()%100*0.01; - if (y>SCREEN_H && type==TILE_GREEN_FRAGMENT_2) - type = EMPTY; - - if (time<=0) - { - if (type < 10) - { - for (int i=0; i<40; i++) - new Particle(TILE_LASER_HEAD, x, y); - for (int i=0; i<40; i++) - new Particle(TILE_GREEN_FRAGMENT + rand() % 3, x+32-rand()%63, y+20-rand()%40); - } - if (type==COLLAPSE_DOOR) - { - type = COLLAPSABLE; - time += 1; - } - else - { - type = EMPTY; - } - } - - if (type==EMPTY) return; - - if (type<0.05 && type<15) - RenderTile(false, tileSolid[type] ? TILE_WHITE_WALL : TILE_WHITE_TILE, int(x)+scrollX, int(y)+scrollY); - else - RenderTile(false, type, int(x)+scrollX, int(y)+scrollY); - } - - Particle() : type(EMPTY) {} - Particle(int t, int _x) : x(_x), type(t) - { - xa=ys=xs=0; ya=400; - //if (t==1) - { - xs = rand()%100-50; - ys=-400-rand()%200; - //x=rand() % SCREEN_W; - //y=rand() % SCREEN_H; - y = SCREEN_H+20; - time = ys/-ya; - } - } - Particle(int t, double _x, double _y) : type(t) - { - x=_x; y=_y; - xa=0; ya=-100; - double r = (rand() % 2000) * PI / 1000; - double d = rand() % 50 + 250; - - xs = sin(r)*d; - ys = cos(r)*d; - time = 1 + (rand() & 255) /255.0; - if (t==TILE_WATER_PARTICLE || t==TILE_GREEN_FRAGMENT || t==TILE_GREEN_FRAGMENT_1) - time = 2; - xa=-xs/time; ya=-ys/time; - if (t==TILE_WATER_PARTICLE || t==TILE_GREEN_FRAGMENT || t==TILE_GREEN_FRAGMENT_1) - ya += 500, ya/=2, xa/=2, xs/=2, ys/=2; - } - ~Particle() { type = EMPTY; } - void* operator new(size_t sz); - }; - - Particle p[1000]; - - double scroll; - double t; - bool goodEnding; - static Ending* ending; - - Ending(bool _goodEnd) : goodEnding(_goodEnd) - { - memset(p, 0, sizeof(p)); - ending = this; - renderBG = false; - scroll = 0; - t=0; - } - - void Render() - { - SDL_Rect a = {0,0,SCREEN_W,SCREEN_H}; - SDL_FillRect(screen, &a, SDL_MapRGB(screen->format, 10,25,25)); - - for (unsigned int i=0; i-FONT_SPACING*2 && y') - PrintR(xr, y, xx+1); - else if (xx[0]=='*') - { - RenderTile(false, atoi(xx+1), (xl+x)/2+scrollX, y+FONT_SPACING/2+scrollY); - RenderTile(false, atoi(strchr(xx, ',')+1), (xr+x)/2+scrollX, y+FONT_SPACING/2+scrollY); - } - else - PrintC(false, x, y, xx); - } - y+=FONT_SPACING; - } - if (scroll > scrollMax + FONT_SPACING*10) - PrintC(true, x, SCREEN_H/2-FONT_SPACING/2, _("The End")); - } - - void Cancel(); - - bool KeyPressed(int key, int mod) - { - if (key=='r' && (mod & KMOD_CTRL)) - { - time = 0; - memset(p, 0, sizeof(p)); - } - else - return Menu::KeyPressed(key, mod); - return true; - } - - void Update(double td) - { - noMouse = 1; - - double old = time; - - t = td; - if (keyState[SDLK_LSHIFT]) - t = td*5; - if (keyState[SDLK_0]) - t = MAX(-td*5, -time); - - Menu::Update(t); - scroll = time * 50; -// if (scroll > scrollMax) -// scroll = fmod(scroll, scrollMax); -// scroll = 0, time = 0; - - if (old>4 && time > 4) - { - if (scroll < scrollMax + FONT_SPACING*17) - { - for (int i=int( old/2.5); i scrollMax + FONT_SPACING*27) - Cancel(); - } - - } -}; - -Ending* Ending::ending = 0; -void* Ending::Particle::operator new(size_t /*sz*/) -{ - static int start = 0; - const int max = sizeof(ending->p)/sizeof(ending->p[0]); - for (int i=0; ip[start].type==EMPTY) - return &ending->p[start]; - } - return &ending->p[rand() % max]; -} - -struct TitleMenu : public OptMenuTitle -{ - TitleMenu() : OptMenuTitle("") - { - //left_align = 1; - - SaveState p; - freeSlot = -1; - for (int i=0; i=num_opt) - return; - if (opt[select] == OPT_DELETE_CONFIRM) - { - GetSlotName(num, tmp); - remove(tmp); - } - Pop(); - Pop(); - new TitleMenu(); - } -}; - -bool TitleMenu::KeyPressed(int key, int mod) -{ - if (key==SDLK_DELETE || key==SDLK_BACKSPACE || key==SDLK_F2) - { - if (select<0 || select>=num_opt || opt[select]OPT_GAMESLOT_LAST) - return true; - int i = opt[select] - OPT_GAMESLOT_0; - - new DeleteConfirmMenu(i); - - return true; - } - return OptMenu::KeyPressed(key, mod); -} - -struct PauseMenu : public OptMenu -{ - PauseMenu(bool isMap, bool allowGotoMap, int allowEnd, int allowEnd2) : OptMenu(_("Paused")) - { - opt[num_opt++] = OPT_RESUME; - if (!isMap) - opt[num_opt++] = OPT_RESTART; - opt[num_opt++] = OPT_OPTIONS; - opt[num_opt++] = OPT_HELP; - if (allowEnd || allowEnd2) - opt[num_opt++] = allowEnd2 ? OPT_END2 : OPT_END; - opt[num_opt++] = (isMap || !allowGotoMap) ? OPT_QUIT_MENU_CONFIRM : OPT_GOTO_MAP; - Init(); - } - virtual bool KeyPressed(int key, int mod) - { - if (key=='p' || key==SDLK_PAUSE) - { - Cancel(); - return true; - } - return Menu::KeyPressed(key, mod); - } - -}; - -struct OptionMenu : public OptMenuTitle -{ - bool title; - OptionMenu(bool _title) : OptMenuTitle(_("Options")), title(_title) - { - opt[num_opt++] = OPT_FULLSCREEN; - - opt[num_opt++] = OPT_BACK; - - if (title) - { - OptMenuTitle::Init(), renderBG=false; - r.y += FONT_SPACING; - r2.y += FONT_SPACING*2; - } - else - OptMenu::Init(), renderBG=true; - } - void Render() - { - if (title) - OptMenuTitle::Render(); - else - OptMenu::Render(); - } -}; - -void RenderFade(double time, int dir, int seed); - -struct Fader : public Menu -{ - int dir; - double speed; - int result; - Fader(int _dir, int _result, double _speed=1) : dir(_dir), speed(_speed), result(_result) - { - renderBG = under ? under->renderBG : true; - } - void Render() - { - if (under) - under->Render(); - - RenderFade(time, dir, (long)this); - } - void Update(double timedelta) - { - Menu::Update(timedelta * speed); - if (result==-2) - { - if (time > 0.7) - quitting = 1; - } - else if (time >= 0.5) - { - Pop(); - if (result==-1) - { - new TitleMenu(); - new Fader(1, -3); - } - if (result==-4) - { - Pop(); // Remove old menu - HackKeyPress(SDLK_ESCAPE, KMOD_CTRL | KMOD_SHIFT); // Reload map combination! - } - if (result==-6) - { - Pop(); // Remove old menu - new Fader(1, 0, speed); - } - if (result==-5 || result==-7) - { - new Ending(result==-7); - new Fader(1, 0, speed); - } - } - } -}; - -void Ending::Cancel() -{ - new Fader(-1, -6, 0.3); -// Pop(); -} - -void ToggleFullscreen(); - -void OptMenu::Select() -{ - if (select<0 || select>=num_opt) - return; - switch(opt[select]) - { - case OPT_RESUME: - Cancel(); - break; - - case OPT_RESTART: - Cancel(); - HackKeyPress('r', KMOD_CTRL); - break; - - case OPT_GOTO_MAP: - case OPT_GOTO_MAP_CONTINUE: - Pop(); - HackKeyPress(SDLK_ESCAPE, KMOD_CTRL); - break; - - case OPT_QUIT: - new QuitConfirmMenu(); - break; - - case OPT_FULLSCREEN: - ToggleFullscreen(); - break; - - case OPT_QUIT_CONFIRM: - new Fader(-1, -2); - break; - - case OPT_QUIT_MENU_CONFIRM: - Pop(); - new Fader(-1, -1); - break; - - case OPT_OPTIONS: - new OptionMenu(!activeMenu->renderBG); - break; - - case OPT_HELP: - new HintReview(); - break; - - case OPT_QUIT_CANCEL: - case OPT_BACK: - Pop(); - break; - - case OPT_END: - new Fader(-1, -5, 0.3); - break; - - case OPT_END2: - new Fader(-1, -7, 0.3); - break; - - case OPT_UNDO: - Pop(); - HackKeyPress('z', 0); - break; - - default: - if (opt[select]>=OPT_GAMESLOT_0 && opt[select]<=OPT_GAMESLOT_LAST - || opt[select]==OPT_GAMESLOT_NEW && freeSlot>=0) - { - if (opt[select]==OPT_GAMESLOT_NEW) - GetSlotName(freeSlot, currentSlot); - else - GetSlotName(opt[select]-OPT_GAMESLOT_0, currentSlot); - new Fader(-1, -4); - } - break; - } -} +/* + 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 + +struct Menu; +Menu* activeMenu = 0; +Menu* deadMenu = 0; + +int SDLPangoTextHeight(const std::string &text_utf8, int width); + +static void HackKeyPress(int key, int mod) +{ + int k = keyState[key]; + StateMakerBase::current->KeyPressed(key, mod); + keyState[key] = k; +} + +struct Menu { + bool renderBG; + + Menu() : renderBG(true), under(activeMenu), time(0) { activeMenu = this; } + + virtual ~Menu() { + if(this!=deadMenu) FATAL("this!=deadMenu"); + deadMenu=under; + } + + static void Pop() + { + if (!activeMenu) return; + Menu* m = activeMenu; + activeMenu = activeMenu->under; + m->under = deadMenu; + deadMenu = m; + } + + virtual bool KeyPressed(int key, int /*mod*/) + { + if (key=='w' || key==SDLK_UP || key==SDLK_KP8 || key=='q' || key=='e' || key==SDLK_KP7 || key==SDLK_KP9) + Move(-1), noMouse=1; + else if (key=='s' || key==SDLK_DOWN || key==SDLK_KP2 || key=='a' || key=='d' || key==SDLK_KP1 || key==SDLK_KP3) + Move(1), noMouse=1; + else if (key==' ' || key==SDLK_RETURN) + { + Select(); + noMouse=1; + } + else if (key==SDLK_ESCAPE || key==SDLK_BACKSPACE || key==SDLK_KP_PERIOD || key==SDLK_DELETE) + Cancel(); + else + return false; + return true; + } + virtual void Mouse(int /*x*/, int /*y*/, int /*dx*/, int /*dy*/, int buttons_pressed, int /*buttons_released*/, int /*buttons*/) + { + if (buttons_pressed==4 || buttons_pressed==2) + Cancel(); + } + + virtual void Move(int /*dir*/) {} + virtual void Select() {} + virtual void Cancel() {} + + virtual void Update(double timedelta) {time+=timedelta;} + virtual void Render() = 0; + + Menu * under; + double time; +}; + +const char * hint[] = { + +/*EMPTY*/ +_("Basic controls:|Move around with the keys Q,W,E,A,S,D or the numeric keypad. Alternatively, you can use the mouse and click on the tile you'd like to move to. Use 'U', backspace or the right mouse button to undo mistakes. The 'Esc' key (or middle mouse button) brings up a menu from which you can restart if you get stuck."), +/*NORMAL*/ +0, +/*COLLAPSABLE*/ +_("Objective:|Your goal is to break all the green tiles. You mainly do this by jumping on them. They will crack when you land on them, and only disintegrate when you jump off. Try not to trap yourself!"), +/*COLLAPSE_DOOR*/ +_("The coloured walls flatten themselves when there are no matching coloured tiles remaining."), +/*TRAMPOLINE*/ +_("You can bounce on the purple trampoline tiles to get around. But try not to fall in the water. If you do, remember you can undo with 'U', backspace or the right mouse button!"), +/*SPINNER*/ +_("A red spinner tile will rotate the pieces around it when you step on it."), +/*WALL*/ +0, +/*COLLAPSABLE2*/ +_("You don't need to destroy blue tiles to complete the level. But they'll turn green when you step off them, and you know what you have to do to green tiles..."), +/*COLLAPSE_DOOR2*/ +0, +/*GUN*/ +_("Yellow laser tiles fire when you step on them. Shooting other laser tiles is more destructive."), +/*TRAP*/ +_("Ice is slippery! Please be careful!!"), +/*COLLAPSABLE3*/ +0, +/*BUILDER*/ +_("The dark grey tiles with arrows on are builders. Landing on one creates green tiles in any adjacent empty tile, and turns green tiles into walls."), +/*SWITCH*/ +0, +/*FLOATING_BALL*/ +/* TRANSLATORS: pop means vanish and Emy drowns (you loose) */ +_("You can ride on the pink floating boats to get across water. They'll pop if you try and float off the edge of the screen though, so look where you're going."), +/*LIFT_DOWN*/ +_("The blue lifts go up or down when you land on them."), +/*LIFT_UP*/ +0, + +0,0,0,0, +//Item 0 (21) +_("The spiky anti-ice pickups turn icy tiles into blue ones. They get used automatically when you land on ice."), +//Item 1 (22) +/* TRANSLATORS: Normally you jump from one plate to another. The golden jump (a + pickup) allows you to jump and land on the *same* plate */ +_("Collecting the golden jump pickups will allow you to do a big vertical jump. Try it out on different types of tiles. Use the space bar or return key to jump. Or click on the tile you're currently on with the mouse."), + +0,0, + +// Map (25) +_("Map Screen:|You can choose which level to attempt next from the map screen. Silver levels are ones you've cleared. Black levels are ones you haven't completed yet, but are available to play."), + +// Scoring (26) +/* TRANSLATORS: Levels are depicted as black balls. Once you passed them, they + turn silver. If you reached the par, they turn golden (with a crown), and if + you beat the par, they turn their shape/color once more */ +_("New feature unlocked!|Each level has an efficiency target for you to try and beat. Every move you make and each non-green tile you destroy counts against you. Why not try replaying some levels and going for gold?"), + +0,0,0, + +// End of help (30) +_("Thanks for playing this little game. I hope you enjoy it! -- -- All content is Copyright 2005 Tom Beaumont email: tombeaumont@yahoo.com Any constructive criticism gratefully received!"), + +// First help page (31) +/* TRANSLATORS: This string is copied twice into the POT file to workaround a + gettext limitation (no macro expansion). The extracted string "Welcome to " + will not be used. */ +_("Welcome to " GAMENAME "! This is a puzzle game based on hexagonal tiles. There is no time limit and no real-time element, so take as long as you like. Use the cursor keys or click on the arrows to scroll through the help pages. More pages will be added as you progress through the game."), +}; + +struct HintMessage : public Menu +{ + static int flags; + + SDL_Rect InnerTextWindowRect; + SDL_Rect OuterTextWindowRect; + const char * msg; + char title[500]; + int state; + + const SDL_Rect &GetInnerWindowRect() const + { + return InnerTextWindowRect; + } + + const SDL_Rect &GetOuterWindowRect() const + { + return OuterTextWindowRect; + } + + static bool FlagTile(int t, bool newStuff=true) + { + if (t==LIFT_UP) t=LIFT_DOWN; + if (newStuff && (flags & (1<31) return false; + if (!hint[t]) return false; + + flags |= 1<0.2) + PrintR(SCREEN_W-FONT_SPACING, SCREEN_H-FONT_SPACING, _("Press any key")); + } + + void Render(int x, int y) + { + //if (y<0) { + // std::cout << "Error in Render: " << x << " " << y << "\n"; // CHECKME + // y = 0; + //} + InnerTextWindowRect.x = x+TILE_W1; + InnerTextWindowRect.y = y; + OuterTextWindowRect.x = InnerTextWindowRect.x-2; + OuterTextWindowRect.y = InnerTextWindowRect.y-2-FONT_SPACING; + // Height is reduced in SDL_FillRect!!? Why? ==> Use a copy: + SDL_Rect r2 = InnerTextWindowRect; + SDL_Rect r = OuterTextWindowRect; + SDL_FillRect(screen, &r, SDL_MapRGB(screen->format, 60,90,90)); + SDL_FillRect(screen, &r2, SDL_MapRGB(screen->format, 20,50,50)); + Print(OuterTextWindowRect.x+FONT_SPACING/4, y-FONT_SPACING, "%s", title); + /* TRANSLATORS: This specifies how the text in the help dialog should + be aligned. Do *not* translate the text itself but use one of "left", + "center" or "right" (untranslated!). The default is "center". */ + std::string alignment = _("text alignment"); + if (alignment == "right") + Print_Aligned(true, InnerTextWindowRect.x + InnerTextWindowRect.w - FONT_SPACING, y+FONT_SPACING/2, InnerTextWindowRect.w - 2*FONT_SPACING, msg, 2); + else if (alignment == "left") + Print_Aligned(true, InnerTextWindowRect.x + FONT_SPACING, y+FONT_SPACING/2, InnerTextWindowRect.w - 2*FONT_SPACING, msg, 0); + else + Print_Aligned(true, x+SCREEN_W/2, y+FONT_SPACING/2, InnerTextWindowRect.w - 2*FONT_SPACING, msg, 1); + } + + virtual void Mouse(int /*x*/, int /*y*/, int /*dx*/, int /*dy*/, int buttons_pressed, int /*buttons_released*/, int /*buttons*/) + { + if (buttons_pressed && state==0 && time>0.2) + state = 1, time=0; + } + + bool KeyPressed(int /*key*/, int /*mod*/) + { + if (state==0 && time>0.2) + state = 1, time=0; + return true; + } + + virtual void Update(double timedelta) + { + Menu::Update(timedelta); + if(state && time > 0.2) + Pop(); + } +}; + +int HintMessage::flags = 1<<31 | 1<<30; + +struct HintReview : public HintMessage +{ + int page; + int page_dir; + int page_count; + int page_display; + HintReview() : HintMessage(hint[31]), page(31), page_dir(0), page_display(0) + { + page_count=0; + for (int i=0; i<32; i++) + if (flags & (1< HintMessage::GetOuterWindowRect().y+ + HintMessage::GetOuterWindowRect().h) + Move(1); + else + Cancel(); + } + else if (buttons_pressed==8) + Move(-1); + else if (buttons_pressed==16) + Move(1); + else + Menu::Mouse(x,y,dx,dy,buttons_pressed, buttons_released, buttons); + } + + bool KeyPressed(int key, int mod) + { + if (key==SDLK_LEFT) + Move(-1); + else if (key==SDLK_RIGHT) + Move(1); + else + return Menu::KeyPressed(key, mod); + return true; + } + + virtual void Render() + { + const double SPD = 10; + +#ifdef EDIT + sprintf (title, _("Help (Page --)"), page_display+1, page_count); +#else + sprintf (title, _("Help (Page %d/%d)"), page_display+1, page_count); +#endif + + int y=SCREEN_H/4; + if (state==0) + y = SCREEN_H/4+int(MAX(0, time*SPD)*-page_dir*SCREEN_H); + if (state==1) + y = SCREEN_H/4+int(MAX(0, 1-time*SPD)*page_dir*SCREEN_H); + + //if (!noMouse) + { + PrintC(false, SCREEN_W/2, y-FONT_SPACING*2, "^"); + PrintC(false, SCREEN_W/2, HintMessage::GetOuterWindowRect().y+ + HintMessage::GetOuterWindowRect().h, "_"); + } + + HintMessage::Render(0,y); + + if (time > 1.0/SPD && page_dir && state==0) + { + do { + page = (page+page_dir) & 31; +#ifdef EDIT + if (hint[page]) break; +#endif + } while (hint[page]==0 || !(flags&(1<1.0/SPD && state==1) + state=0, page_dir = 0; + } + virtual void Update(double timedelta) + { + Menu::Update(timedelta); + } + void Move(int dir) + { + if (page_dir) + return; + time = 0; + page_dir = dir; + state = 0; + } +}; + +#define MAX_GAMESLOT 4 +enum option { + OPT_GAMESLOT_0, + OPT_GAMESLOT_LAST = OPT_GAMESLOT_0 + MAX_GAMESLOT - 1, + OPT_RESUME, + OPT_RESTART, + OPT_GOTO_MAP, + OPT_GOTO_MAP_CONTINUE, + OPT_FULLSCREEN, + OPT_OPTIONS, + OPT_QUIT, + OPT_QUIT_CONFIRM, + OPT_QUIT_CANCEL, + OPT_QUIT_MENU_CONFIRM, + OPT_HELP, + OPT_GAMESLOT_NEW, + OPT_DELETE_CONFIRM, + OPT_DELETE_CANCEL, + OPT_UNDO, + OPT_BACK, + OPT_END, OPT_END2, +}; + +char optionSlotName[MAX_GAMESLOT][400] = { {0} }; +char currentSlot[800] = ""; +int freeSlot = -1; +char* GetSlotName(int i, char * t) +{ + sprintf(t, "save%d.dat", i+1); + return t; +} + +char * optionString[] = { + optionSlotName[0], + optionSlotName[1], + optionSlotName[2], + optionSlotName[3], + _("Resume"), + _("Restart Level"), + _("Return to Map"), + _("Continue"), + _("Toggle Fullscreen"), + _("Options"), + _("Quit"), + _("Yes"), + _("No"), + _("Return to Title"), + _("Help"), + _("Start New Game"), + _("Yes, really delete it!"), + _("Don't do it!"), + _("Undo Last Move"), + _("OK"), + _("View Credits Sequence"), _("View Credits Sequence"), +}; + + +struct OptMenu : public Menu +{ + int select; + int num_opt; + int opt[10]; + bool left_align; + const char * title; + SDL_Rect r, r2; + + OptMenu(const char * t) : select(0), title(t) + { + left_align = false; + num_opt = 0; + } + + void Init() + { + r.w=SCREEN_W/2; + r.x=(SCREEN_W-r.w)/2; + r.y=SCREEN_H/3; + + r2 = r; + + const int SPACE = int(FONT_SPACING * 1.5); + + r2.h = SPACE*num_opt + FONT_SPACING/2; + r.h = r2.h + (FONT_SPACING+2*2); + r.y -= FONT_SPACING+2; + r.w += 2*2; + r.x -= 2; + } + + void RenderOption(int o, const char * s) + { + int y = r2.y + FONT_SPACING/2 + int(FONT_SPACING * 1.5) * o; + if (left_align) + { + int x = r.x + SDLPangoTextWidth(" "); + int x1 = x + SDLPangoTextWidth("> "); + if (select==o) + { + //x += int( sin(time*9)*2.5 ); + //y += int( sin(time*9 + 1.5)*1.5 ); + Print(x, y, "> %s", s); + } + else + { + Print(x1, y, "%s", s); + } + } + else + { + int x = r.x + r.w/2; + if (select==o) + { + //x += int( sin(time*9)*2.5 ); + //y += int( sin(time*9 + 1.5)*1.5 ); + PrintC(false, x, y, "> %s <", s); + } + else + { + PrintC(false, x, y, "%s", s); + } + } + } + + void Move(int dir) + { + select += dir; + if (select<0) select = num_opt-1; + if (select>=num_opt) select = 0; + } + virtual void Mouse(int x, int y, int dx, int dy, int buttons_pressed, int buttons_released, int buttons) + { + if (1) + { + if (xr2.x+r2.w || y>r2.y+r2.h) + { + if (buttons_pressed!=4 && buttons_pressed!=2) + return; + } + else + { + select = (y-r2.y-FONT_SPACING/3) / int(FONT_SPACING*1.5); + if (select<0) select = 0; + if (select>=num_opt) select = num_opt-1; + } + } + if (buttons_pressed==1) + Select(); + Menu::Mouse(x, y, dx, dy, buttons_pressed, buttons_released, buttons); + } + void Select(); + + void Render() + { + RenderBG(); + RenderTitle(); + RenderOptions(); + } + void RenderBG() + { + SDL_FillRect(screen, &r, SDL_MapRGB(screen->format, 60,90,90)); + SDL_FillRect(screen, &r2, SDL_MapRGB(screen->format, 20,50,50)); + } + void RenderTitle() + { + if (left_align) + Print(r2.x+SDLPangoTextWidth(" "), r.y+4, title); + else + PrintC(false, r2.x+r2.w/2, r.y+4, title); + } + void RenderOptions() + { + for (int i=0; iformat, 10,25,25)); + + + SDL_BlitSurface(titlePage, &a, screen, &a); + + OptMenu::RenderTitle(); + OptMenu::RenderOptions(); + } + + void Init() + { + OptMenu::Init(); + + int xw = SCREEN_W/6; + r.w += xw; r2.w+=xw; + int x = SCREEN_W - r.x - r.w;// - FONT_SPACING; + int y = SCREEN_H - r.y - r.h + FONT_SPACING/4;// - FONT_SPACING; + r.x += x; r2.x += x; + r.y += y; r2.y += y; + r.w+=20; r2.w+=20; r.h+=20; r2.h+=20; + + r.h = r2.h = SCREEN_H; + r2.y = SCREEN_H/2; + r.y = r2.y - FONT_SPACING - 2; + } +}; + +const char *ending[] = { + _(" Very Well Done! "), + "", "", "", "", "", "", + + "", "", "*15,4", "", "", + + _("All Levels Cleared!"), + + "", "", "*5,7", "", "", + + _("Not a single green hexagon is left unbroken."), + "", + _("Truly, you are a master of hexagon hopping!"), + + "", "", "*9,10", "", "", + + "", _("Credits"), "", "", "", + _("Tom Beaumont", "", "", + _("Tom Beaumont", "", "", + _("Tom Beaumont", "", "", + _("Kris Beaumont", "", "", +// "", "Photoshop LE", ">Inno Setup", ">Wings 3D", ">MSVC", ">SDL", "", + _("Copperplate gothic bold", ">Verdana", "", + + "", "", "*12,14", "", "", + + _("Thanks for playing!") +}; + +const char *ending2[] = { + _(" Absolutely Amazing! "), + "", "", "", "", "", + + "", "", "*15,4", "", "", + + _("All Levels Mastered!!"), + + "", "", "*5,7", "", "", + + _("You crushed every last green hexagon with"), + _("breathtaking efficiency!"), + "", + _("You truly are a grand master of hexagon hopping!"), +}; + +const int endingLen = sizeof(ending)/sizeof(ending[0]); +const int endingLen2 = sizeof(ending2)/sizeof(ending2[0]); +const int scrollMax = SCREEN_H + (endingLen+1) * FONT_SPACING; + +struct Ending : public Menu +{ + struct Particle{ + double x,y,xs,ys,xa,ya; + double time; + int type; + + void Update(double td) + { + if (type==EMPTY) return; + + time -= td; + x += xs*td; + y += ys*td; + xs += xa*td; + ys += ya*td; + if (type==TILE_LASER_HEAD && time<0.3) + type=TILE_FIRE_PARTICLE_1; + if (type==TILE_FIRE_PARTICLE_1 && time<0.1) + type=TILE_FIRE_PARTICLE_2; + +// if (type==COLLAPSABLE || type==COLLAPSE_DOOR) +// for (int i=int((time)*40); iSCREEN_H && type==TILE_GREEN_FRAGMENT) + xa=0, ys=-ys/2, y=SCREEN_H, type=TILE_GREEN_FRAGMENT_1, time+=rand()%100*0.01; + if (y>SCREEN_H && type==TILE_GREEN_FRAGMENT_1) + xa=0, ys=-ys/2, y=SCREEN_H, type=TILE_GREEN_FRAGMENT_2, time+=rand()%100*0.01; + if (y>SCREEN_H && type==TILE_GREEN_FRAGMENT_2) + type = EMPTY; + + if (time<=0) + { + if (type < 10) + { + for (int i=0; i<40; i++) + new Particle(TILE_LASER_HEAD, x, y); + for (int i=0; i<40; i++) + new Particle(TILE_GREEN_FRAGMENT + rand() % 3, x+32-rand()%63, y+20-rand()%40); + } + if (type==COLLAPSE_DOOR) + { + type = COLLAPSABLE; + time += 1; + } + else + { + type = EMPTY; + } + } + + if (type==EMPTY) return; + + if (type<0.05 && type<15) + RenderTile(false, tileSolid[type] ? TILE_WHITE_WALL : TILE_WHITE_TILE, int(x)+scrollX, int(y)+scrollY); + else + RenderTile(false, type, int(x)+scrollX, int(y)+scrollY); + } + + Particle() : type(EMPTY) {} + Particle(int t, int _x) : x(_x), type(t) + { + xa=ys=xs=0; ya=400; + //if (t==1) + { + xs = rand()%100-50; + ys=-400-rand()%200; + //x=rand() % SCREEN_W; + //y=rand() % SCREEN_H; + y = SCREEN_H+20; + time = ys/-ya; + } + } + Particle(int t, double _x, double _y) : type(t) + { + x=_x; y=_y; + xa=0; ya=-100; + double r = (rand() % 2000) * PI / 1000; + double d = rand() % 50 + 250; + + xs = sin(r)*d; + ys = cos(r)*d; + time = 1 + (rand() & 255) /255.0; + if (t==TILE_WATER_PARTICLE || t==TILE_GREEN_FRAGMENT || t==TILE_GREEN_FRAGMENT_1) + time = 2; + xa=-xs/time; ya=-ys/time; + if (t==TILE_WATER_PARTICLE || t==TILE_GREEN_FRAGMENT || t==TILE_GREEN_FRAGMENT_1) + ya += 500, ya/=2, xa/=2, xs/=2, ys/=2; + } + ~Particle() { type = EMPTY; } + void* operator new(size_t sz); + }; + + Particle p[1000]; + + double scroll; + double t; + bool goodEnding; + static Ending* ending; + + Ending(bool _goodEnd) : goodEnding(_goodEnd) + { + memset(p, 0, sizeof(p)); + ending = this; + renderBG = false; + scroll = 0; + t=0; + } + + void Render() + { + SDL_Rect a = {0,0,SCREEN_W,SCREEN_H}; + SDL_FillRect(screen, &a, SDL_MapRGB(screen->format, 10,25,25)); + + for (unsigned int i=0; i-FONT_SPACING*2 && y') + PrintR(xr, y, xx+1); + else if (xx[0]=='*') + { + RenderTile(false, atoi(xx+1), (xl+x)/2+scrollX, y+FONT_SPACING/2+scrollY); + RenderTile(false, atoi(strchr(xx, ',')+1), (xr+x)/2+scrollX, y+FONT_SPACING/2+scrollY); + } + else + PrintC(false, x, y, xx); + } + y+=FONT_SPACING; + } + if (scroll > scrollMax + FONT_SPACING*10) + PrintC(true, x, SCREEN_H/2-FONT_SPACING/2, _("The End")); + } + + void Cancel(); + + bool KeyPressed(int key, int mod) + { + if (key=='r' && (mod & KMOD_CTRL)) + { + time = 0; + memset(p, 0, sizeof(p)); + } + else + return Menu::KeyPressed(key, mod); + return true; + } + + void Update(double td) + { + noMouse = 1; + + double old = time; + + t = td; + if (keyState[SDLK_LSHIFT]) + t = td*5; + if (keyState[SDLK_0]) + t = MAX(-td*5, -time); + + Menu::Update(t); + scroll = time * 50; +// if (scroll > scrollMax) +// scroll = fmod(scroll, scrollMax); +// scroll = 0, time = 0; + + if (old>4 && time > 4) + { + if (scroll < scrollMax + FONT_SPACING*17) + { + for (int i=int( old/2.5); i scrollMax + FONT_SPACING*27) + Cancel(); + } + + } +}; + +Ending* Ending::ending = 0; +void* Ending::Particle::operator new(size_t /*sz*/) +{ + static int start = 0; + const int max = sizeof(ending->p)/sizeof(ending->p[0]); + for (int i=0; ip[start].type==EMPTY) + return &ending->p[start]; + } + return &ending->p[rand() % max]; +} + +struct TitleMenu : public OptMenuTitle +{ + TitleMenu() : OptMenuTitle("") + { + //left_align = 1; + + SaveState p; + freeSlot = -1; + for (int i=0; i=num_opt) + return; + if (opt[select] == OPT_DELETE_CONFIRM) + { + GetSlotName(num, tmp); + remove(tmp); + } + Pop(); + Pop(); + new TitleMenu(); + } +}; + +bool TitleMenu::KeyPressed(int key, int mod) +{ + if (key==SDLK_DELETE || key==SDLK_BACKSPACE || key==SDLK_F2) + { + if (select<0 || select>=num_opt || opt[select]OPT_GAMESLOT_LAST) + return true; + int i = opt[select] - OPT_GAMESLOT_0; + + new DeleteConfirmMenu(i); + + return true; + } + return OptMenu::KeyPressed(key, mod); +} + +struct PauseMenu : public OptMenu +{ + PauseMenu(bool isMap, bool allowGotoMap, int allowEnd, int allowEnd2) : OptMenu(_("Paused")) + { + opt[num_opt++] = OPT_RESUME; + if (!isMap) + opt[num_opt++] = OPT_RESTART; + opt[num_opt++] = OPT_OPTIONS; + opt[num_opt++] = OPT_HELP; + if (allowEnd || allowEnd2) + opt[num_opt++] = allowEnd2 ? OPT_END2 : OPT_END; + opt[num_opt++] = (isMap || !allowGotoMap) ? OPT_QUIT_MENU_CONFIRM : OPT_GOTO_MAP; + Init(); + } + virtual bool KeyPressed(int key, int mod) + { + if (key=='p' || key==SDLK_PAUSE) + { + Cancel(); + return true; + } + return Menu::KeyPressed(key, mod); + } + +}; + +struct OptionMenu : public OptMenuTitle +{ + bool title; + OptionMenu(bool _title) : OptMenuTitle(_("Options")), title(_title) + { + opt[num_opt++] = OPT_FULLSCREEN; + + opt[num_opt++] = OPT_BACK; + + if (title) + { + OptMenuTitle::Init(), renderBG=false; + r.y += FONT_SPACING; + r2.y += FONT_SPACING*2; + } + else + OptMenu::Init(), renderBG=true; + } + void Render() + { + if (title) + OptMenuTitle::Render(); + else + OptMenu::Render(); + } +}; + +void RenderFade(double time, int dir, int seed); + +struct Fader : public Menu +{ + int dir; + double speed; + int result; + Fader(int _dir, int _result, double _speed=1) : dir(_dir), speed(_speed), result(_result) + { + renderBG = under ? under->renderBG : true; + } + void Render() + { + if (under) + under->Render(); + + RenderFade(time, dir, (long)this); + } + void Update(double timedelta) + { + Menu::Update(timedelta * speed); + if (result==-2) + { + if (time > 0.7) + quitting = 1; + } + else if (time >= 0.5) + { + Pop(); + if (result==-1) + { + new TitleMenu(); + new Fader(1, -3); + } + if (result==-4) + { + Pop(); // Remove old menu + HackKeyPress(SDLK_ESCAPE, KMOD_CTRL | KMOD_SHIFT); // Reload map combination! + } + if (result==-6) + { + Pop(); // Remove old menu + new Fader(1, 0, speed); + } + if (result==-5 || result==-7) + { + new Ending(result==-7); + new Fader(1, 0, speed); + } + } + } +}; + +void Ending::Cancel() +{ + new Fader(-1, -6, 0.3); +// Pop(); +} + +void ToggleFullscreen(); + +void OptMenu::Select() +{ + if (select<0 || select>=num_opt) + return; + switch(opt[select]) + { + case OPT_RESUME: + Cancel(); + break; + + case OPT_RESTART: + Cancel(); + HackKeyPress('r', KMOD_CTRL); + break; + + case OPT_GOTO_MAP: + case OPT_GOTO_MAP_CONTINUE: + Pop(); + HackKeyPress(SDLK_ESCAPE, KMOD_CTRL); + break; + + case OPT_QUIT: + new QuitConfirmMenu(); + break; + + case OPT_FULLSCREEN: + ToggleFullscreen(); + break; + + case OPT_QUIT_CONFIRM: + new Fader(-1, -2); + break; + + case OPT_QUIT_MENU_CONFIRM: + Pop(); + new Fader(-1, -1); + break; + + case OPT_OPTIONS: + new OptionMenu(!activeMenu->renderBG); + break; + + case OPT_HELP: + new HintReview(); + break; + + case OPT_QUIT_CANCEL: + case OPT_BACK: + Pop(); + break; + + case OPT_END: + new Fader(-1, -5, 0.3); + break; + + case OPT_END2: + new Fader(-1, -7, 0.3); + break; + + case OPT_UNDO: + Pop(); + HackKeyPress('z', 0); + break; + + default: + if (opt[select]>=OPT_GAMESLOT_0 && opt[select]<=OPT_GAMESLOT_LAST + || opt[select]==OPT_GAMESLOT_NEW && freeSlot>=0) + { + if (opt[select]==OPT_GAMESLOT_NEW) + GetSlotName(freeSlot, currentSlot); + else + GetSlotName(opt[select]-OPT_GAMESLOT_0, currentSlot); + new Fader(-1, -4); + } + break; + } +} diff --git a/packfile.h b/packfile.h index 9e5d5f9..4534a00 100644 --- a/packfile.h +++ b/packfile.h @@ -1,138 +1,138 @@ -/* - 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 -#include - -#if SDL_BYTEORDER == SDL_LIL_ENDIAN -#define SWAP16(X) (X) -#define SWAP32(X) (X) -#else -#define SWAP16(X) SDL_Swap16(X) -#define SWAP32(X) SDL_Swap32(X) -#endif - -struct PackFile1 -{ - /* Is it *NOT* save to interpret a byte stream as list of Entries! - * The alignment could increase the Entry size on some systems without attribute. - * - * It works on: i386, amd64, mips o32 ABI, powerPC - * Maybe it's also compiler dependent ... - * - * See also http://c-faq.com/struct/padding.html, - * http://c-faq.com/strangeprob/ptralign.html and Debian bug #442854 - * (Need to refer to a C FAQ in a (so called) C++ program, argh ...) - * */ - class Entry { - int32_t len; - public: - // an array of size 1 (no char* pointer!) is saved after len, - // accessing name[0] should (but doesn't always) fit the first byte after len - // See e.g. http://c-faq.com/aryptr/index.html - char name[1]; - - Entry* GetNext() - { - return (Entry*)((char*)name + len); - } - - void* Data() - { - char* pos = name; - while (*pos!=0) - pos++; - return pos+1; - } - - int DataLen() - { - return len - strlen(name) - 1; - } - } -#ifdef __GNUC__ - __attribute__ ((__packed__)); - int static_assert1[sizeof(Entry)==5 ? 0 : -1]; -#else - int static_assert1[sizeof(Entry)<=8 ? 0 : -1]; -#endif - - int numfiles; - Entry** e; - void* data; - - PackFile1() : numfiles(0), e(0), data(0) - {} - - Entry* Find(const char* name) - { - if (numfiles==0) return 0; - int a=0, b=numfiles; - while (b>a) - { - const int mid = (a+b)>>1; - int diff = strcmp(name, e[mid]->name); - if (diff==0) - return e[mid]; - else if (diff < 0) - b=mid; - else - a=mid+1; - } - return 0; - } - - void Read(FILE* f) - { - if (numfiles || e || data) - FATAL("Calling Packfile1::Read when already initialised."); - - int32_t size; - fseek(f, -(int)sizeof(size), SEEK_END); - int end_offset = ftell(f); - fread(&size, sizeof(size), 1, f); - size = SWAP32(size); - fseek(f, end_offset - size, SEEK_SET); - - data = malloc(size); - char* data_end = (char*)data + size; - fread(data, 1, size, f); - - numfiles=0; - Entry* i = (Entry*)data; - while ((void*)i < data_end) - { - numfiles++; - int32_t *data_length = (int32_t*)i; - *data_length = SWAP32(*data_length); - i = i->GetNext(); - } - - e = new Entry* [numfiles]; // CHECKME: where to delete? - - i = (Entry*)data; - for (int j=0; jGetNext()) - e[j] = i; - } - - ~PackFile1() - { - free(data); - } - -}; +/* + 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 +#include + +#if SDL_BYTEORDER == SDL_LIL_ENDIAN +#define SWAP16(X) (X) +#define SWAP32(X) (X) +#else +#define SWAP16(X) SDL_Swap16(X) +#define SWAP32(X) SDL_Swap32(X) +#endif + +struct PackFile1 +{ + /* Is it *NOT* save to interpret a byte stream as list of Entries! + * The alignment could increase the Entry size on some systems without attribute. + * + * It works on: i386, amd64, mips o32 ABI, powerPC + * Maybe it's also compiler dependent ... + * + * See also http://c-faq.com/struct/padding.html, + * http://c-faq.com/strangeprob/ptralign.html and Debian bug #442854 + * (Need to refer to a C FAQ in a (so called) C++ program, argh ...) + * */ + class Entry { + int32_t len; + public: + // an array of size 1 (no char* pointer!) is saved after len, + // accessing name[0] should (but doesn't always) fit the first byte after len + // See e.g. http://c-faq.com/aryptr/index.html + char name[1]; + + Entry* GetNext() + { + return (Entry*)((char*)name + len); + } + + void* Data() + { + char* pos = name; + while (*pos!=0) + pos++; + return pos+1; + } + + int DataLen() + { + return len - strlen(name) - 1; + } + } +#ifdef __GNUC__ + __attribute__ ((__packed__)); + int static_assert1[sizeof(Entry)==5 ? 0 : -1]; +#else + int static_assert1[sizeof(Entry)<=8 ? 0 : -1]; +#endif + + int numfiles; + Entry** e; + void* data; + + PackFile1() : numfiles(0), e(0), data(0) + {} + + Entry* Find(const char* name) + { + if (numfiles==0) return 0; + int a=0, b=numfiles; + while (b>a) + { + const int mid = (a+b)>>1; + int diff = strcmp(name, e[mid]->name); + if (diff==0) + return e[mid]; + else if (diff < 0) + b=mid; + else + a=mid+1; + } + return 0; + } + + void Read(FILE* f) + { + if (numfiles || e || data) + FATAL("Calling Packfile1::Read when already initialised."); + + int32_t size; + fseek(f, -(int)sizeof(size), SEEK_END); + int end_offset = ftell(f); + fread(&size, sizeof(size), 1, f); + size = SWAP32(size); + fseek(f, end_offset - size, SEEK_SET); + + data = malloc(size); + char* data_end = (char*)data + size; + fread(data, 1, size, f); + + numfiles=0; + Entry* i = (Entry*)data; + while ((void*)i < data_end) + { + numfiles++; + int32_t *data_length = (int32_t*)i; + *data_length = SWAP32(*data_length); + i = i->GetNext(); + } + + e = new Entry* [numfiles]; // CHECKME: where to delete? + + i = (Entry*)data; + for (int j=0; jGetNext()) + e[j] = i; + } + + ~PackFile1() + { + free(data); + } + +}; diff --git a/savestate.h b/savestate.h index 32cceec..6c4426d 100644 --- a/savestate.h +++ b/savestate.h @@ -1,306 +1,306 @@ -/* - 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 -*/ - - -struct HexPuzzle; - -class LevelSave -{ - friend struct HexPuzzle; - - // CHECKME: If char is larger than 8 bits (== 1 byte???) - // the code is no longer big endian safe? SWAP16/32 is necessary? - // Or is a byte always of the same size as char, e.g. 16 bits, so - // that int16_t is equally saved on big and little endian systems? - char * bestSolution; - int32_t bestSolutionLength; - int32_t bestScore; - #define NUM_LAST_SCORES 19 - int32_t lastScores[NUM_LAST_SCORES]; - int32_t unlocked; -public: - LevelSave() - { - Clear(); - } - void Clear() - { - unlocked = 0; - bestSolution = 0; - bestScore = 0; - bestSolutionLength = 0; - memset(lastScores, 0, sizeof(lastScores)); - } - void LoadSave(FILE* f, bool save) - { - typedef unsigned int _fn(void*, unsigned int, unsigned int, FILE*); - _fn * fn = save ? (_fn*)fwrite : (_fn*)fread; - - // we write little endian data - bestSolutionLength = SWAP32(bestSolutionLength); - bestScore = SWAP32(bestScore); - for (int i=0; inext; - delete x; - x = nx; - } - first = 0; - - } - -public: - - General general; - - - SaveState() : first(0) - { - Clear(); - } - - ~SaveState() - { - ClearRaw(); - } - - void Clear() - { - ClearRaw(); - ApplyStuff(); - } - - void GetStuff(); - void ApplyStuff(); - - void LoadSave(FILE* f, bool save) - { - if (save) - { - GetStuff(); - - //printf("----\n"); - - fputc('2', f); - general.SwapBytes(); // big==>little endian - fwrite(&general, sizeof(general), 1, f); - general.SwapBytes(); // revert changes - for(X* x=first; x; x=x->next) - { - int16_t len = strlen(x->name); - len = SWAP16(len); - fwrite(&len, sizeof(len), 1, f); - len = SWAP16(len); - fwrite(x->name, 1, len, f); - - x->LoadSave(f,save); - - if (x->Completed()) - { - //printf("% 8d %s\n", x->GetScore(), x->name); - x->Dump(); - } - } - } - else - { - ClearRaw(); - int v = fgetc(f); - if (v=='2') - { - fread(&general, sizeof(general), 1, f); - general.SwapBytes(); - v = '1'; - } - if (v=='1') - { - while(!feof(f)) - { - char temp[1000]; - int16_t len; - fread(&len, sizeof(len), 1, f); - len = SWAP16(len); - if (feof(f)) break; - fread(temp, len, 1, f); - temp[len] = 0; - first = new X(temp, first); - - first->LoadSave(f,save); - } - } - - ApplyStuff(); - } - } - - LevelSave* GetLevel(const char * name, bool create) - { - char * l = strstr(name, "Levels"); - if (l) - name = l; - - X* x = first; - if (x && strcmp(name, x->name)==0) return x; - while (x && x->next) - { - if (strcmp(name, x->next->name)==0) return x->next; - x = x->next; - } - if (create) - { - X* n = new X(name); - if (x) - x->next = n; - else - first = n; - return n; - } - else - { - return 0; - } - } -}; +/* + 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 +*/ + + +struct HexPuzzle; + +class LevelSave +{ + friend struct HexPuzzle; + + // CHECKME: If char is larger than 8 bits (== 1 byte???) + // the code is no longer big endian safe? SWAP16/32 is necessary? + // Or is a byte always of the same size as char, e.g. 16 bits, so + // that int16_t is equally saved on big and little endian systems? + char * bestSolution; + int32_t bestSolutionLength; + int32_t bestScore; + #define NUM_LAST_SCORES 19 + int32_t lastScores[NUM_LAST_SCORES]; + int32_t unlocked; +public: + LevelSave() + { + Clear(); + } + void Clear() + { + unlocked = 0; + bestSolution = 0; + bestScore = 0; + bestSolutionLength = 0; + memset(lastScores, 0, sizeof(lastScores)); + } + void LoadSave(FILE* f, bool save) + { + typedef unsigned int _fn(void*, unsigned int, unsigned int, FILE*); + _fn * fn = save ? (_fn*)fwrite : (_fn*)fread; + + // we write little endian data + bestSolutionLength = SWAP32(bestSolutionLength); + bestScore = SWAP32(bestScore); + for (int i=0; inext; + delete x; + x = nx; + } + first = 0; + + } + +public: + + General general; + + + SaveState() : first(0) + { + Clear(); + } + + ~SaveState() + { + ClearRaw(); + } + + void Clear() + { + ClearRaw(); + ApplyStuff(); + } + + void GetStuff(); + void ApplyStuff(); + + void LoadSave(FILE* f, bool save) + { + if (save) + { + GetStuff(); + + //printf("----\n"); + + fputc('2', f); + general.SwapBytes(); // big==>little endian + fwrite(&general, sizeof(general), 1, f); + general.SwapBytes(); // revert changes + for(X* x=first; x; x=x->next) + { + int16_t len = strlen(x->name); + len = SWAP16(len); + fwrite(&len, sizeof(len), 1, f); + len = SWAP16(len); + fwrite(x->name, 1, len, f); + + x->LoadSave(f,save); + + if (x->Completed()) + { + //printf("% 8d %s\n", x->GetScore(), x->name); + x->Dump(); + } + } + } + else + { + ClearRaw(); + int v = fgetc(f); + if (v=='2') + { + fread(&general, sizeof(general), 1, f); + general.SwapBytes(); + v = '1'; + } + if (v=='1') + { + while(!feof(f)) + { + char temp[1000]; + int16_t len; + fread(&len, sizeof(len), 1, f); + len = SWAP16(len); + if (feof(f)) break; + fread(temp, len, 1, f); + temp[len] = 0; + first = new X(temp, first); + + first->LoadSave(f,save); + } + } + + ApplyStuff(); + } + } + + LevelSave* GetLevel(const char * name, bool create) + { + char * l = strstr(name, "Levels"); + if (l) + name = l; + + X* x = first; + if (x && strcmp(name, x->name)==0) return x; + while (x && x->next) + { + if (strcmp(name, x->next->name)==0) return x->next; + x = x->next; + } + if (create) + { + X* n = new X(name); + if (x) + x->next = n; + else + first = n; + return n; + } + else + { + return 0; + } + } +}; diff --git a/state.h b/state.h index 94f7f33..03a1c70 100644 --- a/state.h +++ b/state.h @@ -1,209 +1,209 @@ -/* - 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 -*/ - - -// -// Config block -// - - -// Uncomment this to check cross-platform compilation compatibility -// #undef WIN32 - -//#define USE_BBTABLET -//#define USE_OPENGL - -#define SCREEN_W 640 -#define SCREEN_H 480 - -// -// End of config block -// - -// Hacky workaround for MSVC's broken for scoping -#define for if (0) ; else for - -#include -#ifdef USE_OPENGL -#include -#endif -#include -#include -#include -#include -#include - -extern SDL_Surface * screen; - -#ifdef WIN32 - // Trigger debugger -// #define FATAL(string, string2) do{__asm{int 3};}while(0) - static inline void FATAL(const char * string="Unknown", const char * string2="") { fprintf(stderr, "Fatal error: %s \"%s\"\n", string, string2); exit(0); } -#else - static inline void FATAL(const char * string="Unknown", const char * string2="") { fprintf(stderr, "Fatal error: %s \"%s\"\n", string, string2); exit(0); } -#endif - -class String -{ - int len; - char* data; -public: - void reserve(int i) { if (i<0) FATAL("-ve string length."); if (i<=len) return; len=i; data=(char*)realloc(data, (len+1)*sizeof(char)); } - String() : len(0), data(NULL) {} - String(String const & s) : len(0), data(NULL) { reserve(s.len); strcpy(data, s.data); } - ~String() { free(data); } - operator const char* () const {return data ? data : "";} - void operator = (String const & a) { *this = (const char*)a; } - void operator = (const char * a) { reserve(strlen(a)); strcpy(data, a); } - void operator += (const char * a) { reserve(strlen(a)+len); strcat(data, a); } - void truncate (int pos) { data[pos] = '\0'; } - void fix_backslashes() { if(data) for (int i=0; data[i]; i++) if (data[i]=='\\') data[i]='/'; } -}; - -class State -{ -public: - virtual ~State() {} - - virtual bool KeyPressed(int key, int mod) = 0; - virtual void KeyReleased(int /*key*/) {}; - virtual void Mouse(int x, int y, int dx, int dy, int buttons_pressed, int buttons_released, int buttons) = 0; - virtual void Update(double timedelta) = 0; - virtual void Render() = 0; - virtual void FileDrop(const char* filename) = 0; - virtual void ScreenModeChanged() {}; -}; - -/************************************************************************ -// TEMPLATE - copy & paste - -#define ClassName NEWSTATE -class ClassName : public State -{ -public: - virtual bool KeyPressed(int key, int mod) - { - return false; - } - virtual void KeyReleased(int key) - { - } - virtual void Mouse(int x, int y, int dx, int dy, int buttons_pressed, int buttons_released, int buttons) - { - } - virtual void FileDrop(const char* filename) - { - } - virtual void Update(double timedelta) - { - // TODO - } - virtual void Render() - { - // TODO - } - virtual void ScreenModeChanged() - { - // TODO - } -}; -MAKE_STATE(ClassName, _KEY_, false); - -************************************************************************/ - -class StateMakerBase -{ - static StateMakerBase* first; - StateMakerBase* next; - int key; - bool start; -protected: - State* state; -public: - static State* current; - -public: - StateMakerBase(int key, bool start) : state(NULL) - { - for (StateMakerBase* s = first; s; s=s->next) - if(key == s->key) - { - FATAL("Duplicate key in StateMakerBase::StateMakerBase"); - return; - } - this->key = key; - this->start = start; - next = first; - first = this; - } - virtual ~StateMakerBase() {} - virtual State* Create() = 0; - void Destroy() - { - delete state; - state = 0; - } - static State* GetNew(int k) - { - for (StateMakerBase* s = first; s; s=s->next) - if(k==s->key) - return current = s->Create(); - return current; - } - static State* GetNew() - { - if (!first) - { - FATAL("StateMakerBase::GetNew - first is NULL"); - return 0; - } - for (StateMakerBase* s = first; s; s=s->next) - if(s->start) - return current = s->Create(); - return current = first->Create(); - } - static void DestroyAll() - { - for (StateMakerBase* s = first; s; s=s->next) - s->Destroy(); - current = 0; - } -}; - -template -class StateMaker : public StateMakerBase -{ -public: - StateMaker(int key, bool start=false) : StateMakerBase(key, start) - {} - State* Create() - { - if (!state) state = new X; - return state; - } -}; - -#define MAKE_STATE(x,key,start) static StateMaker _maker_##x(key, start); - -extern int mouse_buttons, mousex, mousey, noMouse; -extern double stylusx, stylusy; -extern float styluspressure; -extern int stylusok; -extern int quitting; - -char* LoadSaveDialog(bool save, bool levels, const char * title); +/* + 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 +*/ + + +// +// Config block +// + + +// Uncomment this to check cross-platform compilation compatibility +// #undef WIN32 + +//#define USE_BBTABLET +//#define USE_OPENGL + +#define SCREEN_W 640 +#define SCREEN_H 480 + +// +// End of config block +// + +// Hacky workaround for MSVC's broken for scoping +#define for if (0) ; else for + +#include +#ifdef USE_OPENGL +#include +#endif +#include +#include +#include +#include +#include + +extern SDL_Surface * screen; + +#ifdef WIN32 + // Trigger debugger +// #define FATAL(string, string2) do{__asm{int 3};}while(0) + static inline void FATAL(const char * string="Unknown", const char * string2="") { fprintf(stderr, "Fatal error: %s \"%s\"\n", string, string2); exit(0); } +#else + static inline void FATAL(const char * string="Unknown", const char * string2="") { fprintf(stderr, "Fatal error: %s \"%s\"\n", string, string2); exit(0); } +#endif + +class String +{ + int len; + char* data; +public: + void reserve(int i) { if (i<0) FATAL("-ve string length."); if (i<=len) return; len=i; data=(char*)realloc(data, (len+1)*sizeof(char)); } + String() : len(0), data(NULL) {} + String(String const & s) : len(0), data(NULL) { reserve(s.len); strcpy(data, s.data); } + ~String() { free(data); } + operator const char* () const {return data ? data : "";} + void operator = (String const & a) { *this = (const char*)a; } + void operator = (const char * a) { reserve(strlen(a)); strcpy(data, a); } + void operator += (const char * a) { reserve(strlen(a)+len); strcat(data, a); } + void truncate (int pos) { data[pos] = '\0'; } + void fix_backslashes() { if(data) for (int i=0; data[i]; i++) if (data[i]=='\\') data[i]='/'; } +}; + +class State +{ +public: + virtual ~State() {} + + virtual bool KeyPressed(int key, int mod) = 0; + virtual void KeyReleased(int /*key*/) {}; + virtual void Mouse(int x, int y, int dx, int dy, int buttons_pressed, int buttons_released, int buttons) = 0; + virtual void Update(double timedelta) = 0; + virtual void Render() = 0; + virtual void FileDrop(const char* filename) = 0; + virtual void ScreenModeChanged() {}; +}; + +/************************************************************************ +// TEMPLATE - copy & paste + +#define ClassName NEWSTATE +class ClassName : public State +{ +public: + virtual bool KeyPressed(int key, int mod) + { + return false; + } + virtual void KeyReleased(int key) + { + } + virtual void Mouse(int x, int y, int dx, int dy, int buttons_pressed, int buttons_released, int buttons) + { + } + virtual void FileDrop(const char* filename) + { + } + virtual void Update(double timedelta) + { + // TODO + } + virtual void Render() + { + // TODO + } + virtual void ScreenModeChanged() + { + // TODO + } +}; +MAKE_STATE(ClassName, _KEY_, false); + +************************************************************************/ + +class StateMakerBase +{ + static StateMakerBase* first; + StateMakerBase* next; + int key; + bool start; +protected: + State* state; +public: + static State* current; + +public: + StateMakerBase(int key, bool start) : state(NULL) + { + for (StateMakerBase* s = first; s; s=s->next) + if(key == s->key) + { + FATAL("Duplicate key in StateMakerBase::StateMakerBase"); + return; + } + this->key = key; + this->start = start; + next = first; + first = this; + } + virtual ~StateMakerBase() {} + virtual State* Create() = 0; + void Destroy() + { + delete state; + state = 0; + } + static State* GetNew(int k) + { + for (StateMakerBase* s = first; s; s=s->next) + if(k==s->key) + return current = s->Create(); + return current; + } + static State* GetNew() + { + if (!first) + { + FATAL("StateMakerBase::GetNew - first is NULL"); + return 0; + } + for (StateMakerBase* s = first; s; s=s->next) + if(s->start) + return current = s->Create(); + return current = first->Create(); + } + static void DestroyAll() + { + for (StateMakerBase* s = first; s; s=s->next) + s->Destroy(); + current = 0; + } +}; + +template +class StateMaker : public StateMakerBase +{ +public: + StateMaker(int key, bool start=false) : StateMakerBase(key, start) + {} + State* Create() + { + if (!state) state = new X; + return state; + } +}; + +#define MAKE_STATE(x,key,start) static StateMaker _maker_##x(key, start); + +extern int mouse_buttons, mousex, mousey, noMouse; +extern double stylusx, stylusy; +extern float styluspressure; +extern int stylusok; +extern int quitting; + +char* LoadSaveDialog(bool save, bool levels, const char * title); diff --git a/tiletypes.h b/tiletypes.h index 1c77cf2..15df7ba 100644 --- a/tiletypes.h +++ b/tiletypes.h @@ -1,56 +1,56 @@ -/* - 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 -*/ - -#undef X_DEFAULT -#ifndef X - #ifndef TILETYPES_H - #define TILETYPES_H - #define X_DEFAULT - #define X(id, col, solid) id, - enum TileTypes { - #else - #define X(id, col, solid) - #endif -#endif - -X(EMPTY, 0x202060, -1) -X(NORMAL, 0x506070, 0) -X(COLLAPSABLE, 0x408040, 0) -X(COLLAPSE_DOOR,0xa0f0a0, 1) -X(TRAMPOLINE, 0x603060, 0) -X(SPINNER, 0x784040, 0) -X(WALL, 0x000080, 1) -X(COLLAPSABLE2, 0x408080, 0) -X(COLLAPSE_DOOR2,0xa0f0f0, 1) -X(GUN, 0xb0a040, 0) -X(TRAP, 0x000000, 0) -X(COLLAPSABLE3, 0x202020, 0) -X(BUILDER, 0x009000, 0) -X(SWITCH, 0x004000, 0) -X(FLOATING_BALL,0xa00050, 0) -X(LIFT_DOWN, 0x7850a0, 0) -X(LIFT_UP, 0x7850a0, 1) - -#undef X - -#ifdef X_DEFAULT - NumTileTypes - }; -#undef X_DEFAULT -#endif - +/* + 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 +*/ + +#undef X_DEFAULT +#ifndef X + #ifndef TILETYPES_H + #define TILETYPES_H + #define X_DEFAULT + #define X(id, col, solid) id, + enum TileTypes { + #else + #define X(id, col, solid) + #endif +#endif + +X(EMPTY, 0x202060, -1) +X(NORMAL, 0x506070, 0) +X(COLLAPSABLE, 0x408040, 0) +X(COLLAPSE_DOOR,0xa0f0a0, 1) +X(TRAMPOLINE, 0x603060, 0) +X(SPINNER, 0x784040, 0) +X(WALL, 0x000080, 1) +X(COLLAPSABLE2, 0x408080, 0) +X(COLLAPSE_DOOR2,0xa0f0f0, 1) +X(GUN, 0xb0a040, 0) +X(TRAP, 0x000000, 0) +X(COLLAPSABLE3, 0x202020, 0) +X(BUILDER, 0x009000, 0) +X(SWITCH, 0x004000, 0) +X(FLOATING_BALL,0xa00050, 0) +X(LIFT_DOWN, 0x7850a0, 0) +X(LIFT_UP, 0x7850a0, 1) + +#undef X + +#ifdef X_DEFAULT + NumTileTypes + }; +#undef X_DEFAULT +#endif + -- 2.11.4.GIT