--- /dev/null
+/*
+ sdldisplay.c
+
+ Copyright (C) 2010-2019 Amf
+
+ 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <SDL/SDL.h>
+#include <SDL/SDL_image.h>
+#include <libintl.h>
+#include <locale.h>
+
+#include "chroma.h"
+#include "menu.h"
+#include "level.h"
+#include "display.h"
+#include "graphics.h"
+#include "colours.h"
+#include "sdlfont.h"
+#include "sdlscreen.h"
+#include "util.h"
+#include "actions.h"
+#include "xmlparser.h"
+
+#define MOUSE_TIMEOUT_CLICK 200
+#define MOUSE_TIMEOUT_MOVE 1000
+
+char options_colours[FILENAME_MAX] = COLOURS_DEFAULT;
+char options_graphics[FILENAME_MAX] = GRAPHICS_DEFAULT;
+int options_graphic_level = 0;
+int options_sdl_fullscreen = 1;
+int options_sdl_width = 0;
+int options_sdl_height = 0;
+int options_sdl_delay = 100;
+int options_sdl_player_delay = 200;
+int options_sdl_replay_delay = 200;
+int options_sdl_undo_delay = 200;
+int options_sdl_size_x = 0;
+int options_sdl_size_y = 0;
+int options_debug = 0;
+#ifdef XOR_COMPATIBILITY
+int options_xor_options = 0;
+int options_xor_mode = 1;
+int options_xor_display = 0;
+
+int xor_map_scale = 4;
+int xor_map_x_offset = 0;
+int xor_map_y_offset = 0;
+#endif
+
+#ifdef ENIGMA_COMPATIBILITY
+int options_enigma_options = 0;
+int options_enigma_mode = 1;
+#endif
+
+int actions[SDLK_LAST];
+int actions_mouse[3][MOUSE_BUTTONS_MAX];
+
+int display_offset_x;
+int display_offset_y;
+int display_offset_pixels_x = 0;
+int display_offset_pixels_y = 0;
+int display_start_x;
+int display_start_y;
+int display_end_x;
+int display_end_y;
+int display_pieces_x;
+int display_pieces_y;
+int display_focus_x;
+int display_focus_y;
+int display_bar_pixels = 0;
+float display_animation;
+int display_animation_x;
+int display_animation_y;
+int display_border_x = 3;
+int display_border_y = 3;
+
+struct SDL_Surface* psurfacelogo = NULL;
+struct SDL_Surface* psurfacelogosmall = NULL;
+
+extern int font_height;
+
+extern SDL_Surface *screen_surface;
+
+extern int screen_width;
+extern int screen_height;
+extern int screen_fullscreen;
+
+extern int font_height;
+extern int font_width;
+extern int font_border;
+extern int font_padding;
+
+extern int font_size_game;
+
+extern char *piece_name[];
+extern char *action_name[];
+extern char *action_shortname[];
+
+extern int move_x[];
+extern int move_y[];
+
+extern struct graphics* pdisplaygraphics;
+extern struct colours* pdisplaycolours;
+
+extern char options_graphics[];
+extern char options_colours[];
+
+extern int *editor_piece_maps[];
+
+void display_movers(struct level* plevel, int redraw);
+void displayshadowed_movers(struct level* plevel, int redraw);
+
+void displayshadowed_level(struct level* plevel);
+
+void display_clip(struct level* plevel, int clip);
+void display_screensizemenu();
+
+void display_initactions();
+char *display_keyname(SDLKey key);
+void display_addkeytomenu(struct menu* pmenu, int action, char *text);
+void display_options_keys();
+void display_options_mouse();
+void display_options_size();
+void display_options_debug();
+
+#ifdef XOR_COMPATIBILITY
+void xor_focus(struct level* plevel);
+#endif
+
+void display_options_othergames();
+
+#define SCREENREDRAW_LEVEL 1
+#define SCREENREDRAW_BAR 2
+#define SCREENREDRAW_ALL (SCREENREDRAW_BAR | SCREENREDRAW_LEVEL)
+
+void display_init()
+{
+ char buffer[256];
+ struct SDL_Surface* psurfaceicon;
+
+ if(SDL_Init(SDL_INIT_VIDEO) < 0)
+ {
+ snprintf(buffer, 256, gettext("Unable to initalise SDL: %s"), SDL_GetError());
+ fatal(buffer);
+ }
+
+ display_options_load();
+
+ atexit(display_quit);
+
+ SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
+ SDL_EnableUNICODE(1);
+
+ psurfaceicon = graphics_loadimage("icon.png");
+ if(psurfaceicon != NULL)
+ SDL_WM_SetIcon(psurfaceicon, NULL);
+
+ screen_size(options_sdl_width, options_sdl_height, options_sdl_fullscreen);
+ screen_clear(255, 255, 255);
+
+ font_init();
+ font_resize();
+ colours_init();
+ graphics_init();
+
+}
+
+void display_quit()
+{
+ SDL_Quit();
+}
+
+void display_piece(struct level* plevel, int p, int x, int y, int d)
+{
+ SDL_Surface *image;
+
+ SDL_Rect srect;
+ SDL_Rect drect;
+ int alpha;
+ int px, py;
+ int b;
+ int bimage[4];
+ int i;
+ int bsizex, bsizey, boffset;
+ int op;
+
+ int xstart = 0, xend = 0, xsize = 0, xpos = 0;
+
+ if(p < PIECE_SPACE || p >= PIECE_MAX)
+ return;
+
+#ifdef XOR_COMPATIBILITY
+ if(plevel->switched && (p == PIECE_WALL || p == PIECE_SPACE))
+ p = PIECE_DARKNESS;
+#endif
+
+ op = p;
+
+ px = x * pdisplaygraphics->size_x + display_offset_pixels_x;
+ py = y * pdisplaygraphics->size_y + display_offset_pixels_y;
+ if(d != MOVE_NONE
+ && p != PIECE_SPACE && !isexplosion(p)
+#ifdef XOR_COMPATIBILITY
+ && p != PIECE_TELEPORT
+#endif
+ )
+ {
+ if(d == MOVE_LEFT)
+ px = px - display_animation_x;
+ if(d == MOVE_RIGHT)
+ px = px + display_animation_x;
+ if(d == MOVE_UP)
+ py = py - display_animation_y;
+ if(d == MOVE_DOWN)
+ py = py + display_animation_y;
+ }
+
+ if(isexplosion(p) && !(pdisplaygraphics->image_flags[p] & GRAPHICS_KEY))
+ {
+ alpha = 255 * (1 - display_animation);
+ SDL_SetAlpha(pdisplaygraphics->image[p][IMAGE_PIECE], SDL_SRCALPHA, alpha);
+ }
+ if(isnewexplosion(p))
+ {
+ p += PIECE_EXPLOSION_FIRST - PIECE_EXPLOSION_NEW_FIRST;
+ if(!(pdisplaygraphics->image_flags[p] & GRAPHICS_KEY))
+ {
+ alpha = 255 * display_animation;
+ SDL_SetAlpha(pdisplaygraphics->image[p][IMAGE_PIECE], SDL_SRCALPHA, alpha);
+ }
+ }
+
+ image = pdisplaygraphics->image[p][IMAGE_PIECE];
+
+ srect.x = 0;
+ srect.y = 0;
+ srect.w = pdisplaygraphics->size_x;
+ srect.h = pdisplaygraphics->size_y;
+
+ if(image->w > pdisplaygraphics->size_x)
+ {
+ xstart = 0;
+ xend = image->w / pdisplaygraphics->size_x;
+ xsize = 1;
+ xpos = 0;
+
+ if(pdisplaygraphics->image_flags[p] & GRAPHICS_BEVEL)
+ xend -= 4;
+
+ if(pdisplaygraphics->image_flags[p] & GRAPHICS_BEVEL16)
+ xsize = 16;
+
+ if(pdisplaygraphics->image_flags[p] & GRAPHICS_MOVER)
+ {
+ xsize = 5;
+
+ if(d == MOVE_LEFT)
+ xpos += 1;
+ if(d == MOVE_UP)
+ xpos += 2;
+ if(d == MOVE_RIGHT)
+ xpos += 3;
+ if(d == MOVE_DOWN)
+ xpos += 4;
+ }
+
+ /* If we're plotting the players */
+ if(p == PIECE_PLAYER_ONE || p == PIECE_PLAYER_TWO)
+ {
+ /* and there's an image for the swapped player */
+ if(xend > xstart + xsize)
+ {
+ /* then use it if the player is swapped out */
+ if(plevel->player != (p & 1) && plevel->player != 2)
+ xpos += xsize;
+ }
+ }
+
+ if(pdisplaygraphics->image_flags[p] & GRAPHICS_BEVEL16)
+ {
+ i = 15;
+ b = level_data(plevel, x, y) & BEVEL_ALL;
+
+ if(b & BEVEL_U)
+ i -= 1;
+ if(b & BEVEL_R)
+ i -= 2;
+ if(b & BEVEL_D)
+ i -= 4;
+ if(b & BEVEL_L)
+ i -= 8;
+
+ xpos += i;
+ }
+
+ if(pdisplaygraphics->image_flags[p] & GRAPHICS_ANIMATE)
+ {
+ b = (xend - xstart) / xsize;
+
+ if(!isexplosion(p))
+ b = b * display_animation;
+ else
+ b = b * ((display_animation + (isnewexplosion(op) ? 0 : 1)) * 0.5);
+
+ xpos += b * xsize;
+ }
+ else if(pdisplaygraphics->image_flags[p] & GRAPHICS_RANDOM)
+ {
+ b = (xend - xstart) / xsize;
+
+ if(p == PIECE_SPACE)
+ b = (level_data(plevel, x, y) & 0xff) % b;
+ else
+ b = ((level_data(plevel, x, y) & 0xff00) / 0x100) % b;
+
+ xpos += b * xsize;
+ }
+ else if(pdisplaygraphics->image_flags[p] & GRAPHICS_TILE)
+ {
+ b = x % ((xend - xstart) / xsize);
+ if(b < 0)
+ b += (xend - xstart) / xsize;
+ xpos += b * xsize;
+
+ b = y % (image->h / pdisplaygraphics->size_y);
+ if(b < 0)
+ b += image->h / pdisplaygraphics->size_y;
+ srect.y = b * pdisplaygraphics->size_y;
+ }
+
+ srect.x = (xstart + xpos) * pdisplaygraphics->size_x;
+ }
+
+ drect.x = px;
+ drect.y = py;
+ drect.w = pdisplaygraphics->size_x;
+ drect.h = pdisplaygraphics->size_y;
+
+ /* Plot piece */
+ SDL_BlitSurface(image, &srect, screen_surface, &drect);
+
+ /* Plot bevelling */
+ if(pdisplaygraphics->image_flags[p] & GRAPHICS_BEVEL)
+ {
+ b = level_data(plevel, x, y) & BEVEL_ALL;
+ if(b != 0)
+ {
+ bsizex = pdisplaygraphics->size_x / 2;
+ bsizey = pdisplaygraphics->size_y / 2;
+ boffset = (xend - 1) * pdisplaygraphics->size_x;
+
+ for(i = 0; i < 4; i ++)
+ bimage[i] = 0;
+
+ if(b & BEVEL_L)
+ {
+ if(b & BEVEL_U)
+ bimage[0] = 3 * pdisplaygraphics->size_x;
+ else
+ bimage[0] = 1 * pdisplaygraphics->size_x;
+
+ if(b & BEVEL_D)
+ bimage[2] = 3 * pdisplaygraphics->size_x;
+ else
+ bimage[2] = 1 * pdisplaygraphics->size_x;
+ }
+ else
+ {
+ if(b & BEVEL_U)
+ bimage[0] = 2 * pdisplaygraphics->size_x;
+ if(b & BEVEL_D)
+ bimage[2] = 2 * pdisplaygraphics->size_x;
+ }
+
+ if(b & BEVEL_R)
+ {
+ if(b & BEVEL_U)
+ bimage[1] = 3 * pdisplaygraphics->size_x;
+ else
+ bimage[1] = 1 * pdisplaygraphics->size_x;
+
+ if(b & BEVEL_D)
+ bimage[3] = 3 * pdisplaygraphics->size_x;
+ else
+ bimage[3] = 1 * pdisplaygraphics->size_x;
+ }
+ else
+ {
+ if(b & BEVEL_U)
+ bimage[1] = 2 * pdisplaygraphics->size_x;
+ if(b & BEVEL_D)
+ bimage[3] = 2 * pdisplaygraphics->size_x;
+ }
+
+ if(b & BEVEL_TL)
+ bimage[0] = 4 * pdisplaygraphics->size_x;
+ if(b & BEVEL_TR)
+ bimage[1] = 4 * pdisplaygraphics->size_x;
+ if(b & BEVEL_BL)
+ bimage[2] = 4 * pdisplaygraphics->size_x;
+ if(b & BEVEL_BR)
+ bimage[3] = 4 * pdisplaygraphics->size_x;
+
+ for(i = 0; i < 4; i ++)
+ {
+ if(bimage[i] != 0)
+ {
+ srect.x = boffset + bimage[i] + ((i & 1) ? bsizex : 0);
+ srect.y = (i & 2) ? bsizey : 0;
+ srect.w = bsizex;
+ srect.h = bsizey;
+
+ drect.x = px + ((i & 1) ? bsizex : 0);
+ drect.y = py + ((i & 2) ? bsizey : 0);
+ drect.w = bsizex;
+ drect.h = bsizey;
+
+ SDL_BlitSurface(image, &srect, screen_surface, &drect);
+ }
+ }
+ }
+ }
+}
+
+void display_redrawpiece(int p, int x, int y, int d)
+{
+ int dx, dy;
+
+ dx = x * pdisplaygraphics->size_x + display_offset_pixels_x;
+ dy = y * pdisplaygraphics->size_y + display_offset_pixels_y;
+ if(d != MOVE_NONE && p != PIECE_SPACE && !isexplosion(p))
+ {
+ if(d == MOVE_LEFT) { dx = dx - display_animation_x; }
+ if(d == MOVE_RIGHT) { dx = dx + display_animation_x; }
+ if(d == MOVE_UP) { dy = dy - display_animation_y; }
+ if(d == MOVE_DOWN) { dy = dy + display_animation_y; }
+ }
+
+ screen_redraw(dx, dy, pdisplaygraphics->size_x, pdisplaygraphics->size_y);
+}
+
+void display_pieceabsolute(int p, int x, int y, int redraw)
+{
+ SDL_Rect srect;
+ SDL_Rect drect;
+
+ srect.x = 0;
+ srect.y = 0;
+ srect.w = pdisplaygraphics->size_x;
+ srect.h = pdisplaygraphics->size_y;
+
+ drect.x = x;
+ drect.y = y;
+ drect.w = pdisplaygraphics->size_x;
+ drect.h = pdisplaygraphics->size_y;
+
+ if(p != PIECE_SPACE && p!= PIECE_CURSOR)
+ SDL_BlitSurface(pdisplaygraphics->image[PIECE_SPACE][IMAGE_PIECE], &srect, screen_surface, &drect);
+
+ SDL_BlitSurface(pdisplaygraphics->image[p][IMAGE_PIECE], &srect, screen_surface, &drect);
+
+ if(redraw)
+ screen_redraw(x, y, pdisplaygraphics->size_x, pdisplaygraphics->size_y);
+}
+
+int display_focus(struct level* plevel, int refocus)
+{
+ int ox, oy;
+ int px, py;
+
+ int maxx, maxy;
+
+#ifdef XOR_COMPATIBILITY
+ if(plevel->mode == MODE_XOR && options_xor_display)
+ {
+ ox = display_offset_pixels_x;
+ oy = display_offset_pixels_y;
+
+ display_start_x = plevel->view_x[plevel->player];
+ display_start_y = plevel->view_y[plevel->player];
+
+ display_end_x = display_start_x + 8;
+ display_end_y = display_start_y + 8;
+
+ display_offset_pixels_x = (screen_width - pdisplaygraphics->size_x * 8) / 2;
+ display_offset_pixels_y = (screen_height - display_bar_pixels - pdisplaygraphics->size_y * 8) / 2;
+
+ display_offset_pixels_x -= display_start_x * pdisplaygraphics->size_x;
+ display_offset_pixels_y -= display_start_y * pdisplaygraphics->size_y;
+
+ if(display_offset_pixels_x != ox || display_offset_pixels_y != oy)
+ return 1;
+ else
+ return 0;
+ }
+#endif
+
+ px = plevel->player_x[plevel->player] * pdisplaygraphics->size_x;
+ py = plevel->player_y[plevel->player] * pdisplaygraphics->size_y;
+ ox = display_offset_pixels_x;
+ oy = display_offset_pixels_y;
+
+ display_border_x = pdisplaygraphics->size_x * 3;
+ display_border_y = pdisplaygraphics->size_y * 3;
+
+ maxx = (plevel->size_x * pdisplaygraphics->size_x - screen_width);
+ maxy = (plevel->size_y * pdisplaygraphics->size_y - screen_height + display_bar_pixels);
+
+ if((plevel->size_x - 1) * pdisplaygraphics->size_x < screen_width)
+ {
+ display_offset_pixels_x = (screen_width - (plevel->size_x * pdisplaygraphics->size_x)) / 2;
+ }
+ else
+ {
+ if(refocus)
+ {
+ if(px < -(display_offset_pixels_x - display_border_x))
+ display_offset_pixels_x = -(px - display_border_x);
+ if(px >= -(display_offset_pixels_x - screen_width + display_border_x + pdisplaygraphics->size_x))
+ display_offset_pixels_x = -(px - screen_width + display_border_x + pdisplaygraphics->size_x);
+ }
+
+ if(display_offset_pixels_x > 0)
+ display_offset_pixels_x = 0;
+ if(display_offset_pixels_x < -maxx)
+ display_offset_pixels_x = -maxx;
+
+ }
+
+ if((plevel->size_y - 1) * pdisplaygraphics->size_y < screen_height)
+ {
+ display_offset_pixels_y = (screen_height - display_bar_pixels - (plevel->size_y * pdisplaygraphics->size_y)) / 2;
+ }
+ else
+ {
+ if(refocus)
+ {
+ if(py < -(display_offset_pixels_y - display_border_y))
+ display_offset_pixels_y = -(py - display_border_y);
+ if(py >= -(display_offset_pixels_y - screen_height + display_bar_pixels + display_border_y + pdisplaygraphics->size_y))
+ display_offset_pixels_y = -(py - screen_height + display_bar_pixels + display_border_y + pdisplaygraphics->size_y);
+ }
+
+ if(display_offset_pixels_y > 0)
+ display_offset_pixels_y = 0;
+ if(display_offset_pixels_y < -maxy)
+ display_offset_pixels_y = -maxy;
+ }
+
+ /* Calculate start and end points */
+ display_start_x = -display_offset_pixels_x / pdisplaygraphics->size_x;
+ display_end_x = (-display_offset_pixels_x + screen_width + pdisplaygraphics->size_x - 1) / pdisplaygraphics->size_x;
+ if(display_offset_pixels_x > 0)
+ display_start_x --;
+
+ display_start_y = -display_offset_pixels_y / pdisplaygraphics->size_y;
+ display_end_y = (-display_offset_pixels_y + screen_height + pdisplaygraphics->size_y - display_bar_pixels - 1) / pdisplaygraphics->size_y;
+ if(display_offset_pixels_y > 0)
+ display_start_y --;
+
+ if(pdisplaygraphics->flags & GRAPHICS_BACKGROUND)
+ {
+ if(display_start_x < 0)
+ display_start_x = 0;
+ if(display_end_x > plevel->size_x)
+ display_end_x = plevel->size_x;
+ if(display_start_y < 0)
+ display_start_y = 0;
+ if(display_end_y > plevel->size_y)
+ display_end_y = plevel->size_y;
+ }
+
+ if(display_offset_pixels_x != ox || display_offset_pixels_y != oy)
+ return 1;
+ else
+ return 0;
+}
+
+#ifdef XOR_COMPATIBILITY
+void display_map_piece(struct level* plevel, int p, int x, int y, int redraw)
+{
+ SDL_Rect rect;
+
+ rect.x = xor_map_x_offset + x * xor_map_scale;
+ rect.y = xor_map_x_offset + y * xor_map_scale;
+ rect.w = xor_map_scale;
+ rect.h = xor_map_scale;
+
+ if(plevel->player != 2)
+ {
+ if(x < plevel->size_x / 2 && y < plevel->size_y / 2 && !(plevel->mapped & MAPPED_TOP_LEFT))
+ p = PIECE_UNKNOWN;
+ if(x >= plevel->size_x / 2 && y < plevel->size_y / 2 && !(plevel->mapped & MAPPED_TOP_RIGHT))
+ p = PIECE_UNKNOWN;
+ if(x < plevel->size_x / 2 && y >= plevel->size_y / 2 && !(plevel->mapped & MAPPED_BOTTOM_LEFT))
+ p = PIECE_UNKNOWN;
+ if(x >= plevel->size_x / 2 && y >= plevel->size_y / 2 && !(plevel->mapped & MAPPED_BOTTOM_RIGHT))
+ p = PIECE_UNKNOWN;
+ }
+
+ switch(p)
+ {
+ case PIECE_UNKNOWN:
+ SDL_FillRect(screen_surface, &rect, SDL_MapRGB(screen_surface->format, 0x00, 0x00, 0x00));
+ break;
+
+ case PIECE_WALL:
+ SDL_FillRect(screen_surface, &rect, SDL_MapRGB(screen_surface->format, 0x7f, 0x7f, 0x7f));
+ break;
+
+ case PIECE_STAR:
+ SDL_FillRect(screen_surface, &rect, SDL_MapRGB(screen_surface->format, 0xff, 0xa0, 0x00));
+ if(xor_map_scale > 2)
+ {
+ rect.x ++;
+ rect.y ++;
+ rect.w -= 2;
+ rect.h -= 2;
+ SDL_FillRect(screen_surface, &rect, SDL_MapRGB(screen_surface->format, 0xff, 0xff, 0x33));
+ }
+ break;
+
+ case PIECE_DOOR:
+ SDL_FillRect(screen_surface, &rect, SDL_MapRGB(screen_surface->format, 0x00, 0x80, 0xff));
+ if(xor_map_scale > 2)
+ {
+ rect.x ++;
+ rect.y ++;
+ rect.w -= 2;
+ rect.h -= 2;
+ SDL_FillRect(screen_surface, &rect, SDL_MapRGB(screen_surface->format, 0x66, 0xb3, 0xff));
+ }
+ break;
+
+ default:
+ SDL_FillRect(screen_surface, &rect, SDL_MapRGB(screen_surface->format, 0xff, 0xff, 0xff));
+ break;
+ }
+
+ if(redraw)
+ {
+
+ rect.x = xor_map_x_offset + x * xor_map_scale;
+ rect.y = xor_map_x_offset + y * xor_map_scale;
+ rect.w = xor_map_scale;
+ rect.h = xor_map_scale;
+ screen_redraw(rect.x, rect.y, rect.w, rect.h);
+ }
+}
+
+void display_map(struct level* plevel)
+{
+ int i, j;
+
+ xor_map_scale = screen_width / 256;
+ if(xor_map_scale < 1)
+ xor_map_scale = 1;
+
+ display_clip(plevel, 0);
+
+ for(j = 0; j < plevel->size_y; j ++)
+ {
+ for(i = 0; i < plevel->size_y; i ++)
+ {
+ display_map_piece(plevel, level_piece(plevel, i, j), i, j, 0);
+ }
+ }
+
+ /* Redraw map */
+ screen_redraw(xor_map_x_offset, xor_map_y_offset, plevel->size_x * xor_map_scale, plevel->size_y * xor_map_scale);
+}
+#endif
+
+void display_level(struct level* plevel, int redraw)
+{
+ int x, y;
+ int p;
+ SDL_Rect rect;
+
+
+ display_focus(plevel, 0);
+
+ if(redraw == SCREENREDRAW_ALL && (
+ (pdisplaygraphics->flags & GRAPHICS_BACKGROUND)
+#ifdef XOR_COMPATIBILITY
+ || (plevel->mode == MODE_XOR && options_xor_display)
+#endif
+ ))
+ {
+ rect.x = 0;
+ rect.y = 0;
+ rect.w = screen_width;
+ rect.h = screen_height - display_bar_pixels;
+
+#ifdef XOR_COMPATIBILITY
+ if(plevel->switched)
+ SDL_FillRect(screen_surface, &rect, SDL_MapRGB(screen_surface->format, 0, 0, 0));
+ else
+#endif
+ SDL_FillRect(screen_surface, &rect, SDL_MapRGB(screen_surface->format, pdisplaygraphics->background[0], pdisplaygraphics->background[1], pdisplaygraphics->background[2]));
+ }
+
+ if(pdisplaygraphics->shadows != NULL)
+ {
+ displayshadowed_level(plevel);
+#ifdef XOR_COMPATIBILITY
+ if(plevel->mode == MODE_XOR && options_xor_display)
+ display_map(plevel);
+#endif
+ return;
+ }
+
+ display_clip(plevel, 1);
+
+ for(y = display_start_y; y < display_end_y; y ++)
+ {
+ for(x = display_start_x; x < display_end_x; x ++)
+ {
+ p = level_piece(plevel, x, y);
+
+ /* Moving pieces will be redrawn shortly */
+ if(level_moving(plevel, x, y) != MOVE_NONE)
+ p = level_previous(plevel, x, y);
+
+#ifdef XOR_COMPATIBILITY
+ if(plevel->switched && (p == PIECE_WALL || p == PIECE_SPACE))
+ p = PIECE_DARKNESS;
+#endif
+
+ /* If the piece is transparent, plot a background */
+ if(pdisplaygraphics->image[p][IMAGE_PIECE]->flags & SDL_SRCALPHA)
+ {
+#ifdef XOR_COMPATIBILITY
+ if(plevel->switched)
+ display_piece(plevel, PIECE_DARKNESS, x, y, MOVE_NONE);
+ else
+#endif
+ display_piece(plevel, PIECE_SPACE, x, y, MOVE_NONE);
+ }
+
+ /* Plot the piece itself */
+ display_piece(plevel, p, x, y, MOVE_NONE);
+ }
+ }
+
+ display_clip(plevel, 0);
+
+ if(plevel->mover_first != NULL)
+ display_movers(plevel, 0);
+
+#ifdef XOR_COMPATIBILITY
+ if(plevel->mode == MODE_XOR && options_xor_display)
+ display_map(plevel);
+#endif
+
+ screen_redraw(0, 0, screen_width, screen_height);
+
+}
+
+void display_title(struct level* plevel)
+{
+ SDL_Surface *psurface;
+ SDL_Surface *psurfacetest;
+ SDL_Rect drect;
+ int w;
+
+ if(options_debug & DEBUG_SPEED)
+ return;
+
+ if(plevel->title != NULL)
+ {
+ if((strncmp(gettext(plevel->title), "chroma", 6) == 0))
+ psurface = font_render(gettext(plevel->title), -8);
+ else
+ psurface = font_render(plevel->title, COLOUR_WHITE);
+
+ if(plevel->flags & LEVELFLAG_TESTING)
+ {
+ psurfacetest = font_render(gettext("testing: "), COLOUR_CYAN);
+ w = psurfacetest->w;
+
+ drect.x = (screen_width - psurface->w - w) / 2;
+ drect.y = screen_height - font_height;;
+ drect.w = psurfacetest->w;;
+ drect.h = psurfacetest->h;
+ SDL_FillRect(screen_surface, &drect, SDL_MapRGB(screen_surface->format, 0, 0, 0));
+ SDL_BlitSurface(psurfacetest, NULL, screen_surface, &drect);
+ SDL_UpdateRects(screen_surface, 1, &drect);
+ SDL_FreeSurface(psurfacetest);
+ }
+ else
+ w = 0;
+
+ drect.x = ((screen_width - psurface->w - w) / 2) + w;
+ drect.y = screen_height - font_height;
+ drect.w = psurface->w;
+ drect.h = psurface->h;
+ SDL_FillRect(screen_surface, &drect, SDL_MapRGB(screen_surface->format, 0, 0, 0));
+ SDL_BlitSurface(psurface, NULL, screen_surface, &drect);
+ SDL_UpdateRects(screen_surface, 1, &drect);
+ SDL_FreeSurface(psurface);
+ }
+}
+
+void display_moves(struct level* plevel, struct level* plevelreplay)
+{
+ static int length = 0;
+ char buffer[256];
+ SDL_Surface *psurface;
+ SDL_Surface *pimage;
+ SDL_Rect srect, drect;
+ int w;
+ int moves, moves2;
+
+ moves = 0;
+ if(plevel->move_current != NULL)
+ moves = plevel->move_current->count;
+
+ moves2 = -1;
+ if(plevelreplay != NULL)
+ {
+ moves2 = 0;
+ if(plevelreplay->move_last != NULL)
+ moves2 = plevelreplay->move_last->count;
+ }
+ else if(plevel->move_current != plevel->move_last)
+ {
+ if(plevel->move_last != NULL)
+ moves2 = plevel->move_last->count;
+ }
+
+ if(moves2 != -1)
+ sprintf(buffer, "%s%d/%d",
+ plevel->flags & LEVELFLAG_PAUSED ? gettext("paused ") :
+ plevelreplay != NULL ? gettext("replay ") : "",
+ moves, moves2);
+ else
+ sprintf(buffer, "%s%d",
+ plevel->flags & LEVELFLAG_PAUSED ? gettext("paused ") : "",
+ moves);
+
+ if(plevel->flags & LEVELFLAG_FAILED)
+ sprintf(buffer, gettext("failed"));
+
+ psurface = font_render(buffer, COLOUR_CYAN);
+
+ pimage = pdisplaygraphics->image[PIECE_PLAYER_ONE + plevel->player][IMAGE_SMALL];
+ if(pimage != NULL)
+ w = pdisplaygraphics->small_size_x;
+ else
+ w = 0;
+
+ drect.w = length > (psurface->w + w) ? length : (psurface->w + w);
+ drect.h = psurface->h;
+ drect.x = screen_width - drect.w;
+ drect.y = screen_height - font_height;
+ SDL_FillRect(screen_surface, &drect, SDL_MapRGB(screen_surface->format, 0, 0, 0));
+ drect.x = screen_width - w - psurface->w;
+ SDL_BlitSurface(psurface, NULL, screen_surface, &drect);
+
+ if(pimage != NULL)
+ {
+ srect.x = 0;
+ srect.y = 0;
+ srect.w = pdisplaygraphics->small_size_x;
+ srect.h = pdisplaygraphics->small_size_y;
+
+ /* If there is a second small image, use it for a dead player */
+ if(plevel->alive[plevel->player] == 0 && pimage->w > pdisplaygraphics->small_size_x)
+ srect.x += pdisplaygraphics->small_size_x;
+
+ drect.x = screen_width - w;
+ if(pimage->h < font_height)
+ drect.y += (font_height - pimage->h) / 2;
+
+ SDL_BlitSurface(pimage, &srect, screen_surface, &drect);
+ }
+
+ drect.w = (length > psurface->w ? length : psurface->w) + w;
+ drect.h = psurface->h;
+ drect.x = screen_width - drect.w;
+ drect.y = screen_height - font_height;
+ SDL_UpdateRects(screen_surface, 1, &drect);
+ length = psurface->w + w;
+ SDL_FreeSurface(psurface);
+}
+
+void display_stars(struct level* plevel)
+{
+ static int length = 0;
+ char buffer[256];
+
+ SDL_Surface *psurface;
+ SDL_Rect drect;
+ SDL_Surface *pimage;
+ int w;
+ int p;
+
+ sprintf(buffer, "%d/%d", plevel->stars_caught, plevel->stars_total);
+
+ if(plevel->stars_exploded != 0)
+ sprintf(buffer, gettext("%d lost"), plevel->stars_exploded);
+
+ if(plevel->flags & LEVELFLAG_SOLVED && !(plevel->flags & LEVELFLAG_FAILED))
+ sprintf(buffer, gettext("solved"));
+
+ psurface = font_render(buffer, COLOUR_YELLOW);
+
+ /* If solved, and there is a small door, use that */
+ if(plevel->flags & LEVELFLAG_SOLVED && !(plevel->flags & LEVELFLAG_FAILED) && pdisplaygraphics->image[PIECE_DOOR][IMAGE_SMALL] != NULL)
+ p = PIECE_DOOR;
+ /* otherwise use a small star */
+ else
+ p = PIECE_STAR;
+
+ pimage = pdisplaygraphics->image[p][IMAGE_SMALL];
+
+ if(pimage != NULL)
+ w = pdisplaygraphics->small_size_x;
+ else
+ w = 0;
+
+ drect.w = length > psurface->w ? length : psurface->w + w;
+ drect.h = psurface->h;
+ drect.x = 0;
+ drect.y = screen_height - font_height;
+
+ SDL_FillRect(screen_surface, &drect, SDL_MapRGB(screen_surface->format, 0, 0, 0));
+ drect.x = w;
+ SDL_BlitSurface(psurface, NULL, screen_surface, &drect);
+
+ if(pimage != NULL)
+ {
+ drect.x = 0;
+ if(pimage->h < font_height)
+ drect.y += (font_height - pimage->h) / 2;
+
+ SDL_BlitSurface(pimage, NULL, screen_surface, &drect);
+ }
+
+ drect.w = (length > psurface->w ? length : psurface->w) + w;
+ drect.h = psurface->h;
+ drect.x = 0;
+ drect.y = screen_height - font_height;
+ SDL_UpdateRects(screen_surface, 1, &drect);
+ length = psurface->w + w;
+ SDL_FreeSurface(psurface);
+}
+
+int display_bevelsquare(struct level* plevel, int x, int y)
+{
+ int bevel;
+
+ bevel = 0;
+
+ if(level_piece(plevel, x, y) == PIECE_WALL)
+ {
+ if(level_piece(plevel, x - 1, y) != PIECE_WALL)
+ bevel |= BEVEL_L;
+ if(level_piece(plevel, x + 1, y) != PIECE_WALL)
+ bevel |= BEVEL_R;
+ if(level_piece(plevel, x, y - 1) != PIECE_WALL)
+ bevel |= BEVEL_U;
+ if(level_piece(plevel, x, y + 1) != PIECE_WALL)
+ bevel |= BEVEL_D;
+
+ if(((bevel & (BEVEL_L | BEVEL_U)) == 0) && level_piece(plevel, x - 1, y - 1) != PIECE_WALL)
+ bevel |= BEVEL_TL;
+ if(((bevel & (BEVEL_R | BEVEL_U)) == 0) && level_piece(plevel, x + 1, y - 1) != PIECE_WALL)
+ bevel |= BEVEL_TR;
+ if(((bevel & (BEVEL_L | BEVEL_D)) == 0) && level_piece(plevel, x - 1, y + 1) != PIECE_WALL)
+ bevel |= BEVEL_BL;
+ if(((bevel & (BEVEL_R | BEVEL_D)) == 0) && level_piece(plevel, x + 1, y + 1) != PIECE_WALL)
+ bevel |= BEVEL_BR;
+ }
+ else
+ {
+ if(level_piece(plevel, x - 1, y) == PIECE_WALL)
+ bevel |= BEVEL_L;
+ if(level_piece(plevel, x + 1, y) == PIECE_WALL)
+ bevel |= BEVEL_R;
+ if(level_piece(plevel, x, y - 1) == PIECE_WALL)
+ bevel |= BEVEL_U;
+ if(level_piece(plevel, x, y + 1) == PIECE_WALL)
+ bevel |= BEVEL_D;
+
+ if(((bevel & (BEVEL_L | BEVEL_U)) == 0) && level_piece(plevel, x - 1, y - 1) == PIECE_WALL)
+ bevel |= BEVEL_TL;
+ if(((bevel & (BEVEL_R | BEVEL_U)) == 0) && level_piece(plevel, x + 1, y - 1) == PIECE_WALL)
+ bevel |= BEVEL_TR;
+ if(((bevel & (BEVEL_L | BEVEL_D)) == 0) && level_piece(plevel, x - 1, y + 1) == PIECE_WALL)
+ bevel |= BEVEL_BL;
+ if(((bevel & (BEVEL_R | BEVEL_D)) == 0) && level_piece(plevel, x + 1, y + 1) == PIECE_WALL)
+ bevel |= BEVEL_BR;
+ }
+
+ return bevel;
+}
+
+void display_bevellevel(struct level* plevel)
+{
+ int x, y;
+ int bevel;
+
+ for(x = 0; x < plevel->size_x; x ++)
+ {
+ for(y = 0; y < plevel->size_y; y ++)
+ {
+ bevel = level_data(plevel, x, y) & ~BEVEL_ALL;
+ bevel = bevel | display_bevelsquare(plevel, x, y);
+ level_setdata(plevel, x, y, bevel);
+ }
+ }
+}
+
+
+void display_movers(struct level* plevel, int redraw)
+{
+ struct mover* pmover;
+ int x, y, p, pm;
+ int i, j;
+ char buffer[16];
+ int bevel;
+ struct SDL_Surface *psurface;
+ SDL_Rect srect, drect;
+
+ if(pdisplaygraphics->shadows != NULL)
+ {
+ displayshadowed_movers(plevel, redraw);
+ return;
+ }
+
+ display_clip(plevel, 1);
+
+ display_animation_x = - pdisplaygraphics->size_x + (int)((float) pdisplaygraphics->size_x * display_animation);
+ display_animation_y = - pdisplaygraphics->size_y + (int)((float) pdisplaygraphics->size_y * display_animation);
+
+ /* First, plot spaces for all moving pieces */
+ pmover = plevel->mover_first;
+ while(pmover != NULL)
+ {
+#ifdef XOR_COMPATIBILITY
+ if(plevel->switched)
+ display_piece(plevel, PIECE_DARKNESS, pmover->x, pmover->y, MOVE_NONE);
+ else
+#endif
+ display_piece(plevel, PIECE_SPACE, pmover->x, pmover->y, MOVE_NONE);
+
+ pmover = pmover->next;
+ }
+
+ /* Plot moving piece */
+ pmover = plevel->mover_first;
+ while(pmover != NULL)
+ {
+ x = pmover->x;
+ y = pmover->y;
+
+ if(isexplosion(pmover->piece))
+ {
+ /* Plot any piece destroyed by the explosion, or the bomb itself */
+ p = level_previous(plevel, x, y);
+ pm = level_previousmoving(plevel, x, y);
+ if(p != PIECE_SPACE)
+ display_piece(plevel, p, x, y, pm);
+
+ /* Plot the detonator */
+ p = level_detonator(plevel, x, y);
+ pm = level_detonatormoving(plevel, x, y);
+ if(p != PIECE_SPACE)
+ display_piece(plevel, p, x, y, pm);
+ }
+ /* Spaces have already been covered */
+ else if(pmover->piece != PIECE_SPACE && pmover->piece != PIECE_GONE)
+ {
+ if(display_animation < 1)
+ {
+ /* Pieces being collected, earth being eaten */
+ p = level_previous(plevel, x, y);
+ pm = level_previousmoving(plevel, x, y);
+ if((p != PIECE_SPACE && !isexplosion(p) && pm == MOVE_NONE)
+#ifdef XOR_COMPATIBILITY
+ || pmover->piece == PIECE_TELEPORT
+#endif
+ )
+ display_piece(plevel, p, x, y, pm);
+ }
+
+ display_piece(plevel, pmover->piece, x, y, pmover->direction);
+ }
+
+ pmover = pmover->next;
+ }
+
+ /* Plot explosions */
+ pmover = plevel->mover_first;
+ while(pmover != NULL)
+ {
+ x = pmover->x;
+ y = pmover->y;
+ /* Plot growing explosion */
+ if(isexplosion(pmover->piece))
+ display_piece(plevel, pmover->piece + PIECE_EXPLOSION_NEW_FIRST - PIECE_EXPLOSION_FIRST, x, y, MOVE_NONE);
+
+ /* Plot dying explosion */
+ p = level_previous(plevel, x, y);
+ if(isexplosion(p) && display_animation < 1)
+ display_piece(plevel, p, x, y, MOVE_NONE);
+
+ pmover = pmover->next;
+ }
+
+ /* Plot order of movers if debugging (but not if editing) */
+ if(options_debug & DEBUG_ORDER && display_animation < 1 && plevel->player != 2)
+ {
+ pmover = plevel->mover_first;
+ i = 0;
+ while(pmover != NULL)
+ {
+ if(pmover->piece != PIECE_SPACE && pmover->piece != PIECE_GONE)
+ {
+ pm = pmover->direction;
+ if(isexplosion(pmover->piece) || isnewexplosion(pmover->piece))
+ pm = MOVE_NONE;
+
+ x = pmover->x * pdisplaygraphics->size_x + display_offset_pixels_x + ((-1 + display_animation) * move_x[pm] * pdisplaygraphics->size_x);
+ y = pmover->y * pdisplaygraphics->size_y + display_offset_pixels_y + ((-1 + display_animation) * move_y[pm] * pdisplaygraphics->size_y);
+
+ sprintf(buffer, "%X", i++);
+ switch(pmover->direction)
+ {
+ case MOVE_UP:
+ strcat(buffer, ARROW_UP);
+ break;
+ case MOVE_DOWN:
+ strcat(buffer, ARROW_DOWN);
+ break;
+ case MOVE_LEFT:
+ strcat(buffer, ARROW_LEFT);
+ break;
+ case MOVE_RIGHT:
+ strcat(buffer, ARROW_RIGHT);
+ break;
+ default:
+ break;
+ }
+ psurface = font_render(buffer, COLOUR_WHITE | COLOUR_BOLD);
+ srect.w = psurface->w > pdisplaygraphics->size_x ? pdisplaygraphics->size_x : psurface->w;
+ srect.h = psurface->h > pdisplaygraphics->size_y ? pdisplaygraphics->size_y : psurface->h;
+ srect.x = psurface->w - srect.w;
+ srect.y = 0;
+ drect.x = x + pdisplaygraphics->size_x - srect.w;
+ drect.y = y;
+ SDL_BlitSurface(psurface, &srect, screen_surface, &drect);
+ SDL_FreeSurface(psurface);
+ }
+ else
+ i ++;
+
+ pmover = pmover->next;
+ }
+ }
+
+ if(redraw == 0)
+ return;
+
+ /* Redraw screen */
+ pmover = plevel->mover_first;
+
+ while(pmover != NULL)
+ {
+ x = pmover->x;
+ y = pmover->y;
+ display_redrawpiece(pmover->piece, x, y, pmover->direction);
+
+ if(isexplosion(pmover->piece))
+ {
+ p = level_previous(plevel, x, y);
+ pm = level_previousmoving(plevel, x, y);
+ if(pm != MOVE_NONE && p != PIECE_SPACE)
+ display_redrawpiece(p, x, y, pm);
+
+ p = level_detonator(plevel, x, y);
+ pm = level_detonatormoving(plevel, x, y);
+ if(pm != MOVE_NONE && p != PIECE_SPACE)
+ display_redrawpiece(p, x, y, pm);
+ }
+
+ p = level_previous(plevel, x, y);
+ if(isexplosion(p))
+ display_redrawpiece(p, x, y, MOVE_NONE);
+
+ pmover = pmover->next;
+ }
+
+ /* At the peak of the explosion, rebevel any walls that have been
+ destroyed. When undoing, rebevel any walls that have been recreated. */
+ if(display_animation == 0 || display_animation == 1)
+ {
+ /* When undoing, we have to create the wall prior to rebevelling, as it
+ wouldn't otherwise exist until after the end of the animation. */
+ pmover = plevel->mover_first;
+ while(pmover != NULL)
+ {
+ if(pmover->piece == PIECE_WALL)
+ level_setpiece(plevel, pmover->x, pmover->y, pmover->piece);
+ pmover = pmover->next;
+ }
+
+ pmover = plevel->mover_first;
+ while(pmover != NULL)
+ {
+ x = pmover->x;
+ y = pmover->y;
+ if(pmover->piece == PIECE_WALL ||
+ (isexplosion(pmover->piece) && display_animation == 1))
+ {
+ for(i = -1; i < 2; i ++)
+ {
+ for(j = - 1; j < 2; j ++)
+ {
+ bevel = display_bevelsquare(plevel, x + i, y + j);
+ if(bevel != (level_data(plevel, x + i, y + j) & BEVEL_ALL))
+ {
+ level_setdata(plevel, x + i, y + j, bevel | (level_data(plevel, x + i, y + j) & ~BEVEL_ALL));
+ p = level_piece(plevel, x + i, y + j);
+ if(p == PIECE_WALL)
+ {
+#ifdef XOR_COMPATIBILITY
+ if(plevel->switched)
+ display_piece(plevel, PIECE_DARKNESS, x + i, y + j, MOVE_NONE);
+ else
+#endif
+ display_piece(plevel, PIECE_WALL, x + i, y + j, MOVE_NONE);
+ }
+ else
+ {
+#ifdef XOR_COMPATIBILITY
+ if(plevel->switched)
+ display_piece(plevel, PIECE_DARKNESS, x + i, y + j, MOVE_NONE);
+ else
+#endif
+ display_piece(plevel, PIECE_SPACE, x + i, y + j, MOVE_NONE);
+ /* Moving pieces will be replotted when they next move. */
+ if(p != PIECE_SPACE && level_moving(plevel, x + i, y + j) == MOVE_NONE)
+ display_piece(plevel, p, x + i, y + j, MOVE_NONE);
+ }
+ display_redrawpiece(p, x + i, y + j, MOVE_NONE);
+ }
+ }
+ }
+ }
+ pmover = pmover->next;
+ }
+ }
+
+ display_clip(plevel, 0);
+}
+
+void display_play(struct level* plevel, struct level* plevelreplay)
+{
+ SDL_Event event;
+ SDL_Surface *psurface;
+ SDL_Rect drect;
+ int quit;
+ int redraw;
+ int playermove;
+ struct mover* pmover;
+ Uint32 basetime;
+ Uint32 nowtime;
+ Uint32 pausetime = 0;
+ int delay, delayold;
+ int keymod;
+ int frames = 0;
+ int events;
+ char buffer[256];
+ int action;
+ int fast;
+
+ int mouse_x = 0;
+ int mouse_y = 0;
+ int mouse_destination_x = 0;
+ int mouse_destination_y = 0;
+ int mouse_button = 0;
+ int mouse_time;
+
+ int swap = 0;
+
+ graphics_reload();
+
+ display_bevellevel(plevel);
+
+ /* Force full redraw */
+ redraw = SCREENREDRAW_ALL;
+
+ playermove = MOVE_NONE;
+ action = ACTION_NONE;
+ delayold = options_sdl_delay;
+
+ mouse_time = SDL_GetTicks();
+
+ /* Force all animation to end */
+ basetime = SDL_GetTicks() - delayold;
+
+ keymod = 0;
+ fast = 0;
+
+ plevel->flags &= ~LEVELFLAG_PAUSED;
+
+ font_set_size(font_size_game);
+ display_bar_pixels = font_height;
+ display_focus(plevel, 1);
+
+ quit = 0;
+ while(!quit)
+ {
+ //////// Screen redraw ////////
+
+ if(redraw & SCREENREDRAW_ALL)
+ {
+ font_set_size(font_size_game);
+ display_bar_pixels = font_height;
+ }
+ if(redraw & SCREENREDRAW_BAR)
+ {
+ /* Clear bar */
+ drect.x = 0;
+ drect.y = screen_height - display_bar_pixels;
+ drect.w = screen_width;
+ drect.h = display_bar_pixels;
+ SDL_FillRect(screen_surface, &drect, SDL_MapRGB(screen_surface->format, 0, 0, 0));
+
+ display_title(plevel);
+ display_stars(plevel);
+ display_moves(plevel, plevelreplay);
+ }
+ if(redraw & SCREENREDRAW_LEVEL)
+ display_level(plevel, redraw);
+
+ redraw = 0;
+
+ //////// Delay calculation ////////
+
+ /* Calculate what the delay should be, defaulting to the Move Speed. */
+ delay = options_sdl_delay;
+ /* If we're replaying, use the Replay Speed */
+ if(plevelreplay != NULL && plevelreplay->moves != -1)
+ delay = options_sdl_replay_delay;
+ else
+ {
+ /* Otherwise */
+ pmover = plevel->mover_first;
+ while(pmover != NULL)
+ {
+ /* Use the Player Speed if the player is still moving */
+ if(pmover->piece == PIECE_PLAYER_ONE || pmover->piece == PIECE_PLAYER_TWO)
+ delay = options_sdl_player_delay;
+ /* unless there's a piece following in their trail */
+ else if(pmover->piece != PIECE_SPACE && pmover->fast == 1)
+ delay = options_sdl_delay;
+
+ pmover = pmover->next;
+ }
+ /* If we're undoing, use the Undo Speed */
+ if(plevel->flags & LEVELFLAG_UNDO)
+ delay = options_sdl_undo_delay;
+ }
+ /* If SHIFT is pressed, speed things up. */
+ if(keymod & 1)
+ delay = delay / 10;
+ /* If CTRL is pressed, slow things down. */
+ if(keymod & 2)
+ delay = delay * 4;
+ /* If the delay has changed, preserve our position in the animation */
+ if(delay != delayold)
+ {
+ nowtime = SDL_GetTicks();
+ if((plevel->flags & LEVELFLAG_PAUSED))
+ nowtime = pausetime;
+ if(delayold != 0)
+ basetime = nowtime - (((nowtime - basetime) * delay) / delayold );
+ delayold = delay;
+ }
+ if(fast && !(plevel->flags & LEVELFLAG_PAUSED))
+ basetime = 0;
+
+ //////// Movers ////////
+
+ /* If there are movers, plot and then evolve them */
+ if(plevel->mover_first != NULL)
+ {
+ nowtime = SDL_GetTicks();
+ if((plevel->flags & LEVELFLAG_PAUSED))
+ nowtime = pausetime;
+ display_animation = (float)(nowtime - basetime) / delay;
+ if(display_animation > 1)
+ display_animation = 1;
+ frames ++;
+ display_movers(plevel, 1);
+ }
+
+ if(plevel->mover_first != NULL)
+ {
+ /* Is it time for the next stage of this move? */
+ if(nowtime > basetime + delay && !(plevel->flags & LEVELFLAG_PAUSED))
+ {
+#ifdef XOR_COMPATIBILITY
+ if(plevel->mode == MODE_XOR && options_xor_display)
+ {
+ pmover = plevel->mover_first;
+ while(pmover != NULL)
+ {
+ display_map_piece(plevel, pmover->piece, pmover->x, pmover->y, 1);
+ pmover = pmover->next;
+ }
+ }
+#endif
+
+ /* Evolve movers */
+ if(!(plevel->flags & LEVELFLAG_UNDO))
+ {
+ if(level_evolve(plevel))
+ redraw |= display_focus(plevel, 1) * SCREENREDRAW_LEVEL;
+ level_storemovers(plevel);
+
+ if(options_debug & DEBUG_SPEED)
+ {
+ sprintf(buffer, " %4dfps (%d frames / %d ms) ", 1000 * frames / (nowtime - basetime), frames, nowtime - basetime);
+ psurface = font_render(buffer, COLOUR_WHITE);
+ drect.x = (screen_width - psurface->w) / 2;
+ drect.y = screen_height - font_height;
+ drect.w = psurface->w;
+ drect.h = psurface->h;
+ SDL_FillRect(screen_surface, &drect, SDL_MapRGB(screen_surface->format, 0, 0, 0));
+ SDL_BlitSurface(psurface, NULL, screen_surface, &drect);
+ SDL_UpdateRects(screen_surface, 1, &drect);
+ SDL_FreeSurface(psurface);
+
+ printf("%s\n", buffer);
+ }
+ frames = 0;
+ }
+ else
+ {
+ if(level_undo(plevel))
+ plevel->flags |= LEVELFLAG_UNDO;
+ else
+ plevel->flags &= ~LEVELFLAG_UNDO;
+
+ /* Refocus in case we've moved offscreen */
+ redraw |= display_focus(plevel, 1) * SCREENREDRAW_LEVEL;
+ }
+
+ basetime = SDL_GetTicks();
+
+ /* Reset animation in case we redraw before it is next calculated */
+ display_animation = 0;
+ }
+ }
+
+ //////// Events ////////
+
+ /* Hide the mouse if not used whilst in full screen mode */
+ if(SDL_GetTicks() > mouse_time + MOUSE_TIMEOUT_MOVE)
+ screen_cursor(0);
+
+ /* Poll if there are movers, otherwise wait so as to reduce load */
+ if(plevel->mover_first != NULL || SDL_GetTicks() < basetime + delay)
+ events = SDL_PollEvent(&event);
+ else
+ events = SDL_WaitEvent(&event);
+
+ while(events)
+ {
+ switch(event.type)
+ {
+ case SDL_KEYDOWN:
+
+ switch(actions[event.key.keysym.sym])
+ {
+ case ACTION_FASTER:
+ keymod |= 1;
+ break;
+
+ case ACTION_SLOWER:
+ keymod |= 2;
+ break;
+
+ case ACTION_QUIT:
+ quit = 1;
+ break;
+
+ case ACTION_FAST:
+ fast = 1;
+ break;
+
+ case ACTION_PAUSE:
+ if(plevel->flags & LEVELFLAG_PAUSED)
+ {
+ plevel->flags &= ~LEVELFLAG_PAUSED;
+ basetime = SDL_GetTicks() - (pausetime - basetime);
+ }
+ else
+ {
+ plevel->flags |= LEVELFLAG_PAUSED;
+ pausetime = SDL_GetTicks();
+ }
+ redraw |= SCREENREDRAW_BAR;
+ break;
+
+ case ACTION_REDRAW:
+ redraw |= SCREENREDRAW_ALL;
+ break;
+
+ case ACTION_HIDE:
+ SDL_WM_IconifyWindow();
+ break;
+
+ case ACTION_UNDO:
+ if(plevelreplay != NULL)
+ {
+ plevelreplay->flags |= LEVELFLAG_UNDO;
+ plevelreplay->flags &= ~LEVELFLAG_PAUSED;
+ }
+ else
+ action = ACTION_UNDO;
+ break;
+
+
+ case ACTION_REDO:
+ if(plevelreplay!= NULL)
+ {
+ plevelreplay->flags &= ~LEVELFLAG_UNDO;
+ plevelreplay->flags &= ~LEVELFLAG_PAUSED;
+ }
+ else
+ action = ACTION_REDO;
+ break;
+
+ case ACTION_LEFT:
+ if(plevelreplay != NULL)
+ {
+ plevelreplay->flags |= LEVELFLAG_UNDO;
+ plevelreplay->flags &= ~LEVELFLAG_PAUSED;
+ }
+ else
+ action = ACTION_LEFT;
+ break;
+
+ case ACTION_RIGHT:
+ if(plevelreplay != NULL)
+ {
+ plevelreplay->flags &= ~LEVELFLAG_UNDO;
+ plevelreplay->flags &= ~LEVELFLAG_PAUSED;
+ }
+ else
+ action = ACTION_RIGHT;
+ break;
+
+ case ACTION_UP:
+ if(plevelreplay != NULL)
+ plevelreplay->flags |= LEVELFLAG_PAUSED;
+ else
+ action = ACTION_UP;
+ break;
+
+ case ACTION_DOWN:
+ if(plevelreplay != NULL)
+ plevelreplay->flags |= LEVELFLAG_PAUSED;
+ else
+ action = ACTION_DOWN;
+ break;
+
+ case ACTION_SWAP:
+ action = ACTION_SWAP;
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case SDL_KEYUP:
+ switch(actions[event.key.keysym.sym])
+ {
+ case ACTION_SWAP:
+ swap = 0;
+ case ACTION_UP:
+ case ACTION_DOWN:
+ case ACTION_LEFT:
+ case ACTION_RIGHT:
+ case ACTION_UNDO:
+ case ACTION_REDO:
+ action = ACTION_NONE;
+ break;
+
+ case ACTION_FASTER:
+ keymod &= ~1;
+ break;
+
+ case ACTION_SLOWER:
+ keymod &= ~2;
+ break;
+
+ case ACTION_FAST:
+ fast = 0;
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case SDL_QUIT:
+ exit(0);
+
+ case SDL_VIDEORESIZE:
+ screen_resizeevent(&event);
+ redraw = SCREENREDRAW_ALL;
+ break;
+
+ case SDL_ACTIVEEVENT:
+ if((event.active.state & SDL_APPACTIVE) && event.active.gain == 1)
+ redraw = SCREENREDRAW_ALL;
+ break;
+
+ case SDL_MOUSEBUTTONDOWN:
+ mouse_x = event.button.x;
+ mouse_y = event.button.y;
+ mouse_button = event.button.button;
+ mouse_time = SDL_GetTicks();
+ screen_cursor(1);
+
+ if(mouse_button > 0 && mouse_button < MOUSE_BUTTONS_MAX)
+ action = actions_mouse[ACTIONS_GAME][mouse_button];
+
+ if(action == ACTION_HIDE)
+ {
+ SDL_WM_IconifyWindow();
+ action = ACTION_NONE;
+ }
+ if(action == ACTION_QUIT)
+ quit = 1;
+ break;
+
+ case SDL_MOUSEBUTTONUP:
+ if(action == ACTION_MOUSE_CLICK ||
+ (action == ACTION_MOUSE_DRAG_OR_CLICK && (SDL_GetTicks() < mouse_time + MOUSE_TIMEOUT_CLICK)))
+ {
+ mouse_destination_x = (mouse_x - display_offset_pixels_x) / pdisplaygraphics->size_x;
+ mouse_destination_y = (mouse_y - display_offset_pixels_y) / pdisplaygraphics->size_y;
+ if(mouse_destination_x == plevel->player_x[plevel->player])
+ {
+ if(mouse_destination_y < plevel->player_y[plevel->player])
+ action = ACTION_UP;
+ if(mouse_destination_y > plevel->player_y[plevel->player])
+ action = ACTION_DOWN;
+ }
+ if(mouse_destination_y == plevel->player_y[plevel->player])
+ {
+ if(mouse_destination_x < plevel->player_x[plevel->player])
+ action = ACTION_LEFT;
+ if(mouse_destination_x > plevel->player_x[plevel->player])
+ action = ACTION_RIGHT;
+ }
+ if(mouse_destination_x == plevel->player_x[1 - plevel->player] && mouse_destination_y == plevel->player_y[1 - plevel->player])
+ action = ACTION_SWAP;
+ }
+ /* Buttons 4 and 5 can't be held down */
+ else if(mouse_button != 4 && mouse_button != 5)
+ {
+ mouse_button = 0;
+ action = ACTION_NONE;
+ }
+ break;
+ case SDL_MOUSEMOTION:
+ /* Are we dragging? */
+ if(action == ACTION_MOUSE_DRAG || action == ACTION_MOUSE_DRAG_OR_CLICK)
+ {
+ display_offset_pixels_y -= (mouse_y - event.motion.y);
+ display_offset_pixels_x -= (mouse_x - event.motion.x);
+
+ mouse_x = event.motion.x;
+ mouse_y = event.motion.y;
+
+ redraw |= SCREENREDRAW_LEVEL;
+ }
+ else
+ {
+ mouse_time = SDL_GetTicks();
+ screen_cursor(1);
+ }
+ break;
+
+ default:
+ break;
+ }
+ events = SDL_PollEvent(&event);
+ }
+
+ //////// Actions ////////
+
+ /* Are we replaying the level? */
+ if(plevelreplay != NULL)
+ {
+ /* Prevent the user from moving during the replay */
+ playermove = MOVE_NONE;
+
+ /* Is it time for another move? */
+ if(plevel->mover_first == NULL && !(plevelreplay->flags & LEVELFLAG_PAUSED))
+ {
+ /* Moving backwards through replay */
+ if(plevelreplay->flags & LEVELFLAG_UNDO)
+ {
+ if(level_undo(plevel))
+ {
+ plevel->flags |= LEVELFLAG_UNDO;
+ if(plevelreplay->move_current != NULL)
+ plevelreplay->move_current = plevelreplay->move_current->previous;
+ else
+ plevelreplay->move_current = plevelreplay->move_last;
+ }
+ else
+ plevel->flags &= ~LEVELFLAG_UNDO;
+ }
+ /* Moving forwards through replay */
+ else
+ {
+ if(plevelreplay->move_current != NULL)
+ {
+ playermove = plevelreplay->move_current->direction;
+ plevelreplay->move_current = plevelreplay->move_current->next;
+ }
+ }
+ }
+ }
+ /* otherwise, see what action the user has asked for */
+ else
+ {
+ playermove = MOVE_NONE;
+
+ switch(action)
+ {
+ case ACTION_LEFT:
+ playermove = MOVE_LEFT;
+ break;
+ case ACTION_RIGHT:
+ playermove = MOVE_RIGHT;
+ break;
+ case ACTION_UP:
+ playermove = MOVE_UP;
+ break;
+ case ACTION_DOWN:
+ playermove = MOVE_DOWN;
+ break;
+ case ACTION_SWAP:
+ /* Swap only once per keypress */
+ if(plevel->mover_first == NULL && !(plevel->flags & LEVELFLAG_PAUSED) && !swap)
+ {
+ playermove = MOVE_SWAP;
+ swap = 1;
+ }
+ break;
+ case ACTION_UNDO:
+ if(plevel->mover_first == NULL && !(plevel->flags & LEVELFLAG_UNDO))
+ {
+ if(level_undo(plevel))
+ plevel->flags |= LEVELFLAG_UNDO;
+ else
+ plevel->flags &= ~LEVELFLAG_UNDO;
+ playermove = MOVE_NONE;
+ basetime = SDL_GetTicks();
+
+ /* Refocus in case we've moved offscreen */
+ redraw |= display_focus(plevel, 1) * SCREENREDRAW_LEVEL;
+ }
+ break;
+ case ACTION_REDO:
+ playermove = MOVE_REDO;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if(mouse_button == 4 || mouse_button == 5)
+ {
+ action = ACTION_NONE;
+ mouse_button = 0;
+ }
+
+ /* Can't move if we've failed or solved the level */
+ if(plevel->flags & (LEVELFLAG_FAILED | LEVELFLAG_SOLVED))
+ playermove = MOVE_NONE;
+
+ /* If we can move, make the move */
+ if(playermove != MOVE_NONE && plevel->mover_first == NULL && !(plevel->flags & LEVELFLAG_PAUSED))
+ {
+ level_move(plevel, playermove);
+ basetime = SDL_GetTicks();
+ redraw |= display_focus(plevel, 1) * SCREENREDRAW_LEVEL;
+
+ if(mouse_destination_x != 0 || mouse_destination_y != 0)
+ {
+ /* Have we reached our destination, or been blocked? */
+ if((plevel->player_x[plevel->player] == mouse_destination_x && plevel->player_y[plevel->player] == mouse_destination_y) || plevel->mover_first == NULL)
+ {
+ action = ACTION_NONE;
+ mouse_destination_x = 0;
+ mouse_destination_y = 0;
+ }
+ }
+ }
+
+ //////// Display changes ////////
+
+ if(plevel->flags & LEVELFLAG_MOVES)
+ {
+ display_moves(plevel, plevelreplay);
+ plevel->flags &= ~LEVELFLAG_MOVES;
+ }
+
+ if(plevel->flags & LEVELFLAG_STARS)
+ {
+ display_stars(plevel);
+ plevel->flags &= ~LEVELFLAG_STARS;
+ }
+
+#ifdef XOR_COMPATIBILITY
+ if(plevel->flags & LEVELFLAG_SWITCH)
+ {
+ redraw |= SCREENREDRAW_ALL;
+ plevel->flags &= ~LEVELFLAG_SWITCH;
+ }
+
+ if(plevel->flags & LEVELFLAG_MAP)
+ {
+ plevel->flags &= ~LEVELFLAG_MAP;
+ if(plevel->mode == MODE_XOR && options_xor_display)
+ display_map(plevel);
+ }
+#endif
+
+
+ if(!(plevel->flags & LEVELFLAG_SOLVED) && plevel->flags & LEVELFLAG_EXIT)
+ {
+ redraw |= SCREENREDRAW_BAR;
+ plevel->flags |= LEVELFLAG_SOLVED;
+ }
+
+ if(!(plevel->flags & LEVELFLAG_FAILED) && plevel->alive[0] == 0 && plevel->alive[1] ==0)
+ {
+ redraw |= SCREENREDRAW_BAR;
+ plevel->flags |= LEVELFLAG_FAILED;
+ }
+ }
+
+ screen_cursor(1);
+}
+
+void display_edit(struct level* plevel)
+{
+ int quit;
+
+ struct mover* pmover;
+ struct mover* pmovertmp;
+
+ static int editor_piece = 0;
+ int redraw = 0, predraw = 0, moved = 0, pmoved = 0;
+ int i, j;
+ int player;
+
+ int ex, ey, ep;
+ int x, y;
+ int bevel;
+ int piece_start = 0;
+ int piece_end = 0;
+ int piece_width = 0;
+ int piece_count = 0;
+ int action;
+ int effect;
+
+ SDL_Event event;
+ SDL_Rect drect;
+ SDL_Surface *psurface;
+
+ int mouse_x, mouse_y;
+ int dx, dy;
+
+ int mouse_button;
+ int mouse_time;
+
+ font_set_size(font_size_game);
+ graphics_reload();
+
+ display_bevellevel(plevel);
+
+ piece_count = 0;
+ while(editor_piece_maps[plevel->mode][piece_count] != PIECE_GONE)
+ piece_count ++;
+
+ if(editor_piece > piece_count)
+ editor_piece = 0;
+
+ /* The editor uses player 2, so store the player value */
+ player = plevel->player;
+
+ ex = plevel->player_x[2];
+ ey = plevel->player_y[2];
+ ep = editor_piece;
+
+ mouse_x = 0;
+ mouse_y = 0;
+ mouse_button = 0;
+ mouse_time = SDL_GetTicks();
+ action = 0;
+ effect = 0;
+
+ /* Force a complete redraw */
+ redraw = 2;
+
+ display_focus(plevel, 1);
+
+ quit = 0;
+ while(!quit)
+ {
+ plevel->player_x[2] = plevel->player_x[2];
+ plevel->player_y[2] = plevel->player_y[2];
+ plevel->player = 2;
+
+#ifdef XOR_COMPATIBILITY
+ if(plevel->mode == MODE_XOR)
+ xor_focus(plevel);
+#endif
+
+ /* Clear screen, and calculate size of bar */
+ if(redraw == 2)
+ {
+ screen_clear(pdisplaygraphics->background[0], pdisplaygraphics->background[1], pdisplaygraphics->background[2]);
+
+ piece_width = (screen_width - pdisplaygraphics->size_x - 2) / pdisplaygraphics->size_x;
+
+ display_bar_pixels = pdisplaygraphics->size_y + 1;
+
+ if(piece_width < piece_count)
+ {
+ if(display_bar_pixels < font_height)
+ display_bar_pixels = font_height;
+ piece_width = (screen_width - pdisplaygraphics->size_x - 2) / pdisplaygraphics->size_x;
+ }
+ predraw = 2;
+ }
+
+ if(moved == 1)
+ redraw |= display_focus(plevel, 1);
+
+ /* Draw level */
+ if(redraw)
+ {
+ display_level(plevel, SCREENREDRAW_ALL);
+
+ redraw = 0;
+ moved = 1;
+ }
+
+ /* Recalculate piece bar */
+ if(pmoved || predraw)
+ {
+ if(piece_width >= piece_count)
+ {
+ piece_start =0;
+ piece_end = piece_count;
+ }
+ else
+ {
+ while(editor_piece < piece_start + 3 && piece_start > 0)
+ {
+ piece_start --;
+ piece_end = piece_start + piece_width;
+
+ predraw = 1;
+ }
+
+ while(editor_piece >= piece_start + piece_width - 3 && piece_end < piece_count)
+ {
+ piece_start ++;
+ piece_end = piece_start + piece_width;
+
+ predraw = 1;
+ }
+
+ piece_end = piece_start + piece_width;
+ if(piece_end > piece_count)
+ piece_end = piece_count;
+ }
+ }
+
+ /* Redraw all of piece bar */
+ if(predraw)
+ {
+ drect.x = 0;
+ drect.w = screen_width;
+ drect.y = screen_height - display_bar_pixels;
+ drect.h = display_bar_pixels;
+ SDL_FillRect(screen_surface, &drect, SDL_MapRGB(screen_surface->format, 0, 0, 0));
+
+ drect.x = 0;
+ drect.w = screen_width - pdisplaygraphics->size_x - 2;
+ drect.y = screen_height - pdisplaygraphics->size_y;
+ drect.h = pdisplaygraphics->size_y;
+ SDL_SetClipRect(screen_surface, &drect);
+ for(i = piece_start; i < piece_end; i ++)
+ {
+ display_pieceabsolute(editor_piece_maps[plevel->mode][i], (i - piece_start) * pdisplaygraphics->size_x, screen_height - pdisplaygraphics->size_y, 0);
+ }
+ if(piece_start != 0)
+ {
+ psurface = font_render(ARROW_LEFT, COLOUR_WHITE | COLOUR_BOLD);
+ drect.x = 0;
+ drect.y = screen_height - (pdisplaygraphics->size_y + font_height) / 2;
+ SDL_BlitSurface(psurface, NULL, screen_surface, &drect);
+ SDL_FreeSurface(psurface);
+ }
+ if(piece_end != piece_count)
+ {
+ psurface = font_render(ARROW_RIGHT, COLOUR_WHITE | COLOUR_BOLD);
+ drect.x = piece_width * pdisplaygraphics->size_x - 2 - psurface->w;
+ drect.y = screen_height - (pdisplaygraphics->size_y + font_height) / 2;
+ SDL_BlitSurface(psurface, NULL, screen_surface, &drect);
+ SDL_FreeSurface(psurface);
+ }
+ SDL_SetClipRect(screen_surface, NULL);
+
+ drect.x = 0;
+ drect.w = screen_width;
+ drect.y = screen_height - pdisplaygraphics->size_y;
+ drect.h = pdisplaygraphics->size_y;
+ SDL_UpdateRects(screen_surface, 1, &drect);
+
+ pmoved = 1;
+ }
+
+ /* Redraw piece bar cursor */
+ if(pmoved)
+ {
+ pmoved = 0;
+
+ /* Remove cursor from previous piece */
+ if(!predraw)
+ display_pieceabsolute(editor_piece_maps[plevel->mode][ep], (ep - piece_start) * pdisplaygraphics->size_x, screen_height - pdisplaygraphics->size_y, 1);
+
+ /* Plot cursor */
+ display_pieceabsolute(PIECE_CURSOR, (editor_piece - piece_start) * pdisplaygraphics->size_x, screen_height - pdisplaygraphics->size_y, predraw ? 0 : 1);
+
+ /* Plot current piece in far right corner */
+ display_pieceabsolute(editor_piece_maps[plevel->mode][editor_piece], screen_width - pdisplaygraphics->size_x, screen_height - pdisplaygraphics->size_y, predraw ? 0 : 1);
+ }
+
+ if(predraw)
+ {
+ drect.x = 0;
+ drect.w = screen_width;
+ drect.y = screen_height - display_bar_pixels;
+ drect.h = display_bar_pixels;
+ SDL_UpdateRects(screen_surface, 1, &drect);
+ predraw = 0;
+ }
+
+ /* Redraw level cursor */
+ if(moved)
+ {
+ moved = 0;
+
+ /* Create cosmetic movers */
+ level_setprevious(plevel, plevel->player_x[2], plevel->player_y[2], level_piece(plevel, plevel->player_x[2], plevel->player_y[2]));
+ if(pdisplaygraphics->shadows != NULL)
+ mover_newundo(plevel, ex, ey, MOVE_NONE, PIECE_SPACE, PIECE_SPACE, MOVER_UNDO);
+ else
+ mover_newundo(plevel, ex, ey, MOVE_NONE, level_piece(plevel, ex, ey), PIECE_SPACE, MOVER_UNDO);
+ mover_newundo(plevel, plevel->player_x[2], plevel->player_y[2], MOVE_NONE, PIECE_CURSOR, PIECE_SPACE, MOVER_UNDO);
+ display_animation = 0.5;
+ display_movers(plevel, 1);
+ level_setmoving(plevel, ex, ey, MOVE_NONE);
+ level_setmoving(plevel, plevel->player_x[2], plevel->player_y[2], MOVE_NONE);
+ level_setprevious(plevel, plevel->player_x[2], plevel->player_y[2], PIECE_SPACE);
+
+ /* Delete cosmetic movers */
+ pmover = plevel->mover_first;
+ while(pmover != NULL)
+ {
+#ifdef XOR_COMPATIBILITY
+ /* Update map if present */
+ if(options_xor_display && pmover->piece != PIECE_CURSOR)
+ display_map_piece(plevel, pmover->piece, pmover->x, pmover->y, 1);
+#endif
+ pmovertmp = pmover;
+ pmover = pmover->next;
+ free(pmovertmp);
+ }
+ plevel->mover_first = NULL;
+ plevel->mover_last = NULL;
+ }
+
+ SDL_WaitEvent(&event);
+
+ /* Hide the mouse if not used whilst in full screen mode */
+ if(SDL_GetTicks() > mouse_time + MOUSE_TIMEOUT_MOVE)
+ screen_cursor(0);
+
+ /* Store previous cursor location for redrawing damaged areas */
+ ex = plevel->player_x[2];
+ ey = plevel->player_y[2];
+ ep = editor_piece;
+
+ switch(event.type)
+ {
+ case SDL_KEYDOWN:
+ switch(actions[event.key.keysym.sym])
+ {
+ case ACTION_QUIT:
+ quit = 1;
+ break;
+
+ case ACTION_HIDE:
+ SDL_WM_IconifyWindow();
+ break;
+
+ case ACTION_LEFT:
+ if(plevel->player_x[2] > 0)
+ {
+ plevel->player_x[2] --; moved = 1;
+ }
+ break;
+
+ case ACTION_RIGHT:
+ if(plevel->player_x[2] < plevel->size_x - 1)
+ {
+ plevel->player_x[2] ++; moved = 1;
+ }
+ break;
+
+ case ACTION_UP:
+ if(plevel->player_y[2] > 0)
+ {
+ plevel->player_y[2] --; moved = 1;
+ }
+ break;
+
+ case ACTION_DOWN:
+ if(plevel->player_y[2] < plevel->size_y -1)
+ {
+ plevel->player_y[2] ++; moved = 1;
+ }
+ break;
+
+ case ACTION_PIECE_LEFT:
+ effect = ACTION_PIECE_LEFT;
+ break;
+
+ case ACTION_PIECE_RIGHT:
+ effect = ACTION_PIECE_RIGHT;
+ break;
+
+ case ACTION_SWAP:
+ effect = ACTION_SWAP;
+ moved = 1;
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+
+ case SDL_MOUSEBUTTONDOWN:
+ mouse_x = event.button.x;
+ mouse_y = event.button.y;
+ mouse_button = event.button.button;
+ mouse_time = SDL_GetTicks();
+ screen_cursor(1);
+
+ if(mouse_button > 0 && mouse_button < MOUSE_BUTTONS_MAX)
+ action = actions_mouse[ACTIONS_EDIT][mouse_button];
+
+ switch(action)
+ {
+ case ACTION_HIDE:
+ SDL_WM_IconifyWindow();
+ action = ACTION_NONE;
+ break;
+
+ case ACTION_QUIT:
+ quit = 1;
+ break;
+
+ case ACTION_PIECE_LEFT:
+ effect = ACTION_PIECE_LEFT;
+ break;
+
+ case ACTION_PIECE_RIGHT:
+ effect = ACTION_PIECE_RIGHT;
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case SDL_MOUSEBUTTONUP:
+ if(action == ACTION_MOUSE_CLICK ||
+ (action == ACTION_MOUSE_DRAG_OR_CLICK && ((SDL_GetTicks() < mouse_time + MOUSE_TIMEOUT_CLICK) || (mouse_y > (screen_height - display_bar_pixels)))))
+ {
+ /* Have they clicked within the piece bar? */
+ if(mouse_y > (screen_height - display_bar_pixels))
+ {
+ editor_piece = piece_start + ((mouse_x) / pdisplaygraphics->size_x);
+ pmoved = 1;
+ }
+ else
+ {
+ plevel->player_x[2] = (mouse_x - display_offset_pixels_x) / pdisplaygraphics->size_x;
+ plevel->player_y[2] = (mouse_y - display_offset_pixels_y) / pdisplaygraphics->size_y;
+ moved = 2;
+ effect = ACTION_SWAP;
+ }
+ }
+
+ /* Buttons 4 and 5 can't be held down */
+ if(mouse_button != 4 && mouse_button != 5)
+ {
+ mouse_button = 0;
+ action = ACTION_NONE;
+ }
+ break;
+
+ case SDL_MOUSEMOTION:
+ mouse_time = SDL_GetTicks();
+ screen_cursor(1);
+
+ if(action == ACTION_MOUSE_DRAG || action == ACTION_MOUSE_DRAG_OR_CLICK)
+ {
+ dx = mouse_x - event.motion.x;
+ dy = mouse_y - event.motion.y;
+
+ mouse_x = event.motion.x;
+ mouse_y = event.motion.y;
+
+ display_offset_pixels_y -= dy;
+ display_offset_pixels_x -= dx;
+
+ redraw |= 1;
+ }
+ else if(action == ACTION_MOUSE_CLICK)
+ {
+
+ mouse_x = event.motion.x;
+ mouse_y = event.motion.y;
+ /* Have they clicked within the piece bar? */
+ if(mouse_y > (screen_height - display_bar_pixels))
+ {
+ editor_piece = piece_start + ((mouse_x) / pdisplaygraphics->size_x);
+ pmoved = 1;
+ }
+ else
+ {
+ plevel->player_x[2] = (mouse_x - display_offset_pixels_x) / pdisplaygraphics->size_x;
+ plevel->player_y[2] = (mouse_y - display_offset_pixels_y) / pdisplaygraphics->size_y;
+ moved = 2;
+ effect = ACTION_SWAP;
+ }
+ }
+ break;
+
+ case SDL_QUIT:
+ exit(0);
+
+ case SDL_VIDEORESIZE:
+ screen_resizeevent(&event);
+ redraw = 2;
+ break;
+
+ case SDL_ACTIVEEVENT:
+ if((event.active.state & SDL_APPACTIVE) && event.active.gain == 1)
+ redraw = 2;
+ break;
+ }
+
+ switch(effect)
+ {
+ case ACTION_PIECE_LEFT:
+ editor_piece --;
+ if(editor_piece < 0)
+ editor_piece = piece_count - 1;
+ pmoved = 1;
+ break;
+
+ case ACTION_PIECE_RIGHT:
+ editor_piece ++;
+ if(editor_piece >= piece_count)
+ editor_piece = 0;
+ pmoved = 1;
+ break;
+
+ case ACTION_SWAP:
+ bevel = 0;
+ if(editor_piece_maps[plevel->mode][editor_piece] == PIECE_WALL || level_piece(plevel, plevel->player_x[2], plevel->player_y[2]) == PIECE_WALL)
+ bevel = 1;
+ /* Don't allow the edges to be changed */
+ if(!(plevel->player_x[2] < 1 || plevel->player_x[2] > plevel->size_x - 2 || plevel->player_y[2] < 1 || plevel->player_y[2] > plevel->size_y - 2))
+ level_setpiece(plevel, plevel->player_x[2], plevel->player_y[2], editor_piece_maps[plevel->mode][editor_piece]);
+
+ /* Rebevel if necessary */
+ if(bevel == 1)
+ {
+ x = plevel->player_x[2]; y = plevel->player_y[2];
+ for(i = -1; i < 2; i ++)
+ {
+ for(j = - 1; j < 2; j ++)
+ {
+ bevel = display_bevelsquare(plevel, x + i, y + j);
+ if(bevel != (level_data(plevel, x + i, y + j) & BEVEL_ALL))
+ {
+ level_setdata(plevel, x + i, y + j, bevel | (level_data(plevel, x + i, y + j) & ~BEVEL_ALL));
+ /* Redraw changed piece */
+ /* The mover will get deleted when next redrawn */
+ if(pdisplaygraphics->shadows != NULL)
+ mover_newundo(plevel, x + i, y + j, MOVE_NONE, PIECE_SPACE, PIECE_SPACE, MOVER_UNDO);
+ else
+ mover_newundo(plevel, x + i, y + j, MOVE_NONE, level_piece(plevel, x + i, y + j), PIECE_SPACE, MOVER_UNDO);
+ }
+ }
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ effect = ACTION_NONE;
+ }
+
+ /* Restore real player value */
+ plevel->player = player;
+ screen_cursor(1);
+}
+
+int display_type()
+{
+ return DISPLAY_SDL;
+}
+
+int scale_delay(int delay, int change)
+{
+ int tmp;
+ int magnitude;
+
+ if(delay < 1)
+ delay = 1;
+
+ if(change == 1)
+ {
+ tmp = delay;
+
+ while(tmp >= 100)
+ tmp = tmp / 10;
+
+ magnitude = delay / tmp;
+
+ if(tmp < 20)
+ tmp = tmp + 1;
+ else if(tmp < 50)
+ tmp = tmp + 2;
+ else
+ tmp = tmp + 5;
+
+ delay = tmp * magnitude;
+
+ if(delay > 1000)
+ delay = 1;
+ }
+
+ if(change == -1)
+ {
+ tmp = delay;
+
+ while(tmp > 100)
+ tmp = tmp / 10;
+
+ magnitude = delay / tmp;
+
+ if(tmp > 50)
+ tmp = tmp - 5;
+ else if(tmp > 20)
+ tmp = tmp - 2;
+ else
+ tmp = tmp - 1;
+
+ delay = tmp * magnitude;
+
+ if(delay < 1)
+ delay = 1000;
+ }
+
+ return delay;
+}
+
+void display_options()
+{
+ struct menu* pmenu;
+ struct menu* pgraphicsmenu;
+ struct menu* pcoloursmenu;
+ struct menuentry* pentryscreensize;
+ struct menuentry* pentryfullscreen;
+ struct menuentry* pentrygraphics;
+ struct menuentry* pentrysize;
+ struct menuentry* pentrylevel;
+ struct menuentry* pentrycolours;
+ struct menuentry* pentryspeed;
+ struct menuentry* pentryfirstspeed;
+ struct menuentry* pentryreplayspeed;
+ struct menuentry* pentryundospeed;
+ int result;
+ int ok;
+
+ char buffer[4096];
+
+ pmenu = menu_new(gettext("Display Options"));
+
+ menuentry_new(pmenu, gettext("Return to previous menu"), 'Q', 0);
+ menuentry_new(pmenu, gettext("Save Options"), 'S', 0);
+
+ menuentry_new(pmenu, "", 0, MENU_SPACE);
+
+ pentrygraphics = menuentry_new(pmenu, gettext("Graphics Scheme"), 'G', 0);
+ pentrysize = menuentry_new(pmenu, gettext("Graphics Size"), 'I', 0);
+ pentrylevel = menuentry_new(pmenu, gettext("Graphics Level"), 'L', 0);
+ pentrycolours = menuentry_new(pmenu, gettext("Colour Scheme"), 'C', 0);
+
+ menuentry_new(pmenu, "", 0, MENU_SPACE);
+
+ pentryscreensize = menuentry_new(pmenu, gettext("Screen size"), 'Z', 0);
+ pentryfullscreen = menuentry_new(pmenu, gettext("Fullscreen"), 'F', 0);
+
+ menuentry_new(pmenu, "", 0, MENU_SPACE);
+
+ pentryfirstspeed = menuentry_new(pmenu, gettext("Player Speed"), 'P', MENU_SCROLLABLE);
+ pentryspeed = menuentry_new(pmenu, gettext("Move Speed"), 'M', MENU_SCROLLABLE);
+ pentryreplayspeed = menuentry_new(pmenu, gettext("Replay Speed"), 'R', MENU_SCROLLABLE);
+ pentryundospeed = menuentry_new(pmenu, gettext("Undo Speed"), 'U', MENU_SCROLLABLE);
+
+ menuentry_new(pmenu, "", 0, MENU_SPACE);
+
+ menuentry_new(pmenu, gettext("Change Keys"), 'K', 0);
+ menuentry_new(pmenu, gettext("Change Mouse"), 'O', 0);
+
+ /* XOR and Enigma options are only visible once an appropriate level has
+ * been seen so as not to confuse those simply playing Chroma levels */
+ if(0
+#ifdef XOR_COMPATIBILITY
+ || options_xor_options
+#endif
+#ifdef ENIGMA_COMPATIBILITY
+ || options_enigma_options
+#endif
+ )
+ {
+ menuentry_new(pmenu, "", 0, MENU_SPACE);
+ menuentry_new(pmenu, gettext("Other Games Options"), 'X', 0);
+ }
+
+ if(options_debug & DEBUG_MENU)
+ {
+ menuentry_new(pmenu, "", 0, MENU_SPACE);
+ menuentry_new(pmenu, gettext("Debug Options"), 'D', 0);
+ }
+
+ ok = 0;
+ while(!ok)
+ {
+ if(options_sdl_width == 0 && options_sdl_height == 0)
+ sprintf(buffer, gettext("Auto (%d x %d)"), screen_width, screen_height);
+ else
+ sprintf(buffer, "%d x %d", screen_width, screen_height);
+ menuentry_extratext(pentryscreensize, buffer, NULL, NULL);
+
+ menuentry_extratext(pentryfullscreen, screen_fullscreen ? gettext("Yes") : gettext("No"), NULL, NULL);
+
+ sprintf(buffer, gettext("%d milliseconds"), options_sdl_delay);
+ menuentry_extratext(pentryspeed, buffer, NULL, NULL);
+
+ sprintf(buffer, gettext("%d milliseconds"), options_sdl_player_delay);
+ menuentry_extratext(pentryfirstspeed, buffer, NULL, NULL);
+
+ sprintf(buffer, gettext("%d milliseconds"), options_sdl_replay_delay);
+ menuentry_extratext(pentryreplayspeed, buffer, NULL, NULL);
+
+ sprintf(buffer, gettext("%d milliseconds"), options_sdl_undo_delay);
+ menuentry_extratext(pentryundospeed, buffer, NULL, NULL);
+
+ if(pdisplaygraphics != NULL)
+ {
+ if(options_sdl_size_x != pdisplaygraphics->size_x || options_sdl_size_y != pdisplaygraphics->size_y)
+ sprintf(buffer, gettext("Auto (%d x %d)"), pdisplaygraphics->size_x, pdisplaygraphics->size_y);
+
+ else
+ sprintf(buffer, "%d x %d", pdisplaygraphics->size_x, pdisplaygraphics->size_y);
+
+ menuentry_extratext(pentrysize, buffer, NULL, NULL);
+
+ menuentry_extratext(pentrygraphics, pdisplaygraphics->title != NULL ? pdisplaygraphics->title : gettext("[untitled graphics]"), NULL, NULL);
+
+ if(pdisplaygraphics->title == NULL)
+ menuentry_extratext(pentrygraphics, gettext("[untitled graphics]"), NULL, NULL);
+ else if(pdisplaygraphics->flags & GRAPHICS_TRANSLATE)
+ menuentry_extratext(pentrygraphics, gettext(pdisplaygraphics->title), NULL, NULL);
+ else
+ menuentry_extratext(pentrygraphics, pdisplaygraphics->title, NULL, NULL);
+
+ if(options_graphic_level != 0)
+ sprintf(buffer, "%d / %d", options_graphic_level, pdisplaygraphics->levels);
+ else
+ sprintf(buffer, gettext("Auto (%d)"), pdisplaygraphics->levels);
+
+ menuentry_extratext(pentrylevel, buffer, NULL, NULL);
+ }
+ else
+ menuentry_extratext(pentrygraphics, gettext("** NONE **"), NULL, NULL);
+
+ if(pdisplaygraphics != NULL && (pdisplaygraphics->flags & GRAPHICS_CURSES))
+ pentrycolours->flags = 0;
+ else
+ pentrycolours->flags = MENU_INVISIBLE | MENU_GREY;
+
+ if(pdisplaygraphics != NULL && pdisplaygraphics->sizes != NULL)
+ pentrysize->flags = 0;
+ else
+ pentrysize->flags = MENU_INVISIBLE | MENU_GREY;
+
+ if(pdisplaygraphics != NULL && pdisplaygraphics->levels > 0)
+ pentrylevel->flags = MENU_SCROLLABLE;
+ else
+ pentrylevel->flags = MENU_INVISIBLE | MENU_GREY;
+
+ if(pdisplaycolours == NULL)
+ menuentry_extratext(pentrycolours, gettext("** NONE **"), NULL, NULL);
+ else if(pdisplaycolours->title == NULL)
+ menuentry_extratext(pentrycolours, gettext("[untitled colours]"), NULL, NULL);
+ else if(pdisplaycolours->flags & COLOURS_TRANSLATE)
+ menuentry_extratext(pentrycolours, gettext(pdisplaycolours->title), NULL, NULL);
+ else
+ menuentry_extratext(pentrycolours, pdisplaycolours->title, NULL, NULL);
+
+ result = menu_process(pmenu);
+ if(result == MENU_QUIT)
+ ok = 1;
+
+ if(result == MENU_SELECT && pmenu->entry_selected != NULL)
+ {
+ switch(pmenu->entry_selected->key)
+ {
+ case 'Q':
+ ok = 1;
+ break;
+
+ case 'Z':
+ display_screensizemenu();
+ break;
+
+ case 'F':
+ screen_fullscreen = 1 - screen_fullscreen;
+ screen_resize(screen_width, screen_height, screen_fullscreen);
+ options_sdl_fullscreen = screen_fullscreen;
+ break;
+
+ case 'G':
+ pgraphicsmenu = graphics_menu();
+ if(menu_process(pgraphicsmenu) == MENU_SELECT)
+ {
+ if(pgraphicsmenu->entry_selected != NULL && pgraphicsmenu->entry_selected->value != NULL)
+ {
+ strcpy(options_graphics, pgraphicsmenu->entry_selected->value);
+ graphics_init();
+ }
+ }
+ menu_delete(pgraphicsmenu);
+ break;
+
+ case 'C':
+ pcoloursmenu = colours_menu();
+ if(menu_process(pcoloursmenu) == MENU_SELECT)
+ {
+ if(pcoloursmenu->entry_selected != NULL && pcoloursmenu->entry_selected->value != NULL)
+ {
+ strcpy(options_colours, pcoloursmenu->entry_selected->value);
+ colours_init();
+ graphics_init();
+ }
+ }
+ menu_delete(pcoloursmenu);
+ break;
+
+ case 'I':
+ display_options_size();
+ break;
+
+ case 'S':
+ display_options_save();
+ ok = 1;
+ break;
+
+ case 'K':
+ display_options_keys();
+ break;
+
+ case 'O':
+ display_options_mouse();
+ break;
+
+ case 'X':
+ display_options_othergames();
+ break;
+
+ case 'D':
+ display_options_debug();
+ break;
+ }
+ }
+
+ if(result == MENU_SCROLLLEFT && pmenu->entry_selected != NULL)
+ {
+ switch(pmenu->entry_selected->key)
+ {
+ case 'M':
+ options_sdl_delay = scale_delay(options_sdl_delay, -1);
+ break;
+ case 'P':
+ options_sdl_player_delay = scale_delay(options_sdl_player_delay, -1);
+ break;
+ case 'R':
+ options_sdl_replay_delay = scale_delay(options_sdl_replay_delay, -1);
+ break;
+ case 'U':
+ options_sdl_undo_delay = scale_delay(options_sdl_undo_delay, -1);
+ break;
+ case 'L':
+ options_graphic_level --;
+ if(options_graphic_level < 0)
+ options_graphic_level = pdisplaygraphics->levels;
+ graphics_reload();
+ break;
+ }
+ }
+
+ if(result == MENU_SCROLLRIGHT && pmenu->entry_selected != NULL)
+ {
+ switch(pmenu->entry_selected->key)
+ {
+ case 'M':
+ options_sdl_delay = scale_delay(options_sdl_delay, 1);
+ break;
+ case 'P':
+ options_sdl_player_delay = scale_delay(options_sdl_player_delay, 1);
+ break;
+ case 'R':
+ options_sdl_replay_delay = scale_delay(options_sdl_replay_delay, 1);
+ break;
+ case 'U':
+ options_sdl_undo_delay = scale_delay(options_sdl_undo_delay, 1);
+ break;
+ case 'L':
+ options_graphic_level ++;
+ if(options_graphic_level > pdisplaygraphics->levels)
+ options_graphic_level = 0;
+ graphics_reload();
+ break;
+ }
+ }
+
+
+ }
+
+ menu_delete(pmenu);
+}
+
+void display_clip(struct level* plevel, int clip)
+{
+ SDL_Rect crect;
+
+ if(clip)
+ {
+ crect.x = 0;
+ crect.y = 0;
+ crect.w = screen_width;
+ crect.h = screen_height - display_bar_pixels;
+
+#ifdef XOR_COMPATIBILITY
+ if(plevel->mode == MODE_XOR && options_xor_display)
+ {
+ crect.w = pdisplaygraphics->size_x * 8;
+ crect.h = pdisplaygraphics->size_y * 8;
+ crect.x = (screen_width - crect.w) / 2;
+ crect.y = (screen_height - display_bar_pixels - crect.h) / 2;
+
+ /* Ensure bar is always visible */
+ if(crect.y + crect.h > screen_height - display_bar_pixels)
+ crect.h = screen_height - display_bar_pixels - crect.y;
+ }
+#endif
+
+ SDL_SetClipRect(screen_surface, &crect);
+ }
+ else
+ {
+ SDL_SetClipRect(screen_surface, NULL);
+ }
+}
+
+
+void display_screensizemenu()
+{
+ struct menu* pmenu;
+ struct menuentry *pentry;
+
+ int sizes[6][2] = { {640, 480}, {800, 600}, {1024, 768}, {1280, 1024}, {1600, 1200}, {0, 0} };
+ int i;
+ char buffer[256], tmp[256];
+ int custom;
+ SDL_Rect **modes;
+ int w, h;
+ int ok;
+
+ custom = 1;
+
+ pmenu = menu_new(gettext("Screen Size"));
+
+ menuentry_new(pmenu, gettext("Quit and return to previous menu"), 'Q', 0);
+ menuentry_new(pmenu, "", 0, MENU_SPACE);
+
+ pentry = menuentry_new(pmenu, gettext("Automatic sizing"), 0, 0);
+ menuentry_extratext(pentry, NULL, "0", "0");
+ if(options_sdl_width == 0 && options_sdl_height == 0)
+ {
+ pmenu->entry_selected = pentry;
+ custom = 0;
+ }
+
+ /* First, add modes that we know the screen can do */
+ modes = SDL_ListModes(NULL, SDL_FULLSCREEN);
+ if(modes != (SDL_Rect **)0 && modes != (SDL_Rect **)-1)
+ {
+ for(i = 0; modes[i]; i ++)
+ {
+ w = modes[i]->w; h = modes[i]->h;
+ sprintf(buffer, "%d x %d", w, h);
+
+ pentry = pmenu->entry_first; ok = 1;
+ while(pentry != NULL)
+ {
+ if(pentry->text != NULL && strcmp(pentry->text, buffer) == 0)
+ {
+ ok = 0;
+ }
+ pentry = pentry->next;
+ }
+
+ if(ok)
+ {
+ sprintf(tmp, "%04dx%04d", w, h);
+ pentry = menuentry_newwithvalue(pmenu, buffer, 0, MENU_SORT, tmp);
+ sprintf(buffer, "%d", w);
+ sprintf(tmp, "%d", h);
+ menuentry_extratext(pentry, NULL, buffer, tmp);
+ if(options_sdl_width == w && options_sdl_height == h)
+ pmenu->entry_selected = pentry;
+ if(screen_width == w && screen_height == h)
+ custom = 0;
+ }
+ }
+ }
+
+ /* Then add any of the default modes that haven't been already added */
+ i = 0;
+ while(sizes[i][0] != 0)
+ {
+ sprintf(buffer, "%d x %d", sizes[i][0], sizes[i][1]);
+ pentry = pmenu->entry_first; ok = 1;
+ while(pentry != NULL)
+ {
+ if(pentry->text != NULL && strcmp(pentry->text, buffer) == 0)
+ {
+ ok = 0;
+ }
+ pentry = pentry->next;
+ }
+
+ if(ok)
+ {
+ sprintf(tmp, "%04dx%04d", sizes[i][0], sizes[i][1]);
+ pentry = menuentry_newwithvalue(pmenu, buffer, 0, MENU_SORT, tmp);
+ sprintf(buffer, "%d", sizes[i][0]);
+ sprintf(tmp, "%d", sizes[i][1]);
+ menuentry_extratext(pentry, NULL, buffer, tmp);
+ if(options_sdl_width == sizes[i][0] && options_sdl_height == sizes[i][1])
+ pmenu->entry_selected = pentry;
+ if(screen_width == sizes[i][0] && screen_height == sizes[i][1])
+ custom = 0;
+ }
+
+ i ++;
+ }
+ if(custom)
+ {
+ sprintf(buffer, gettext("Custom (%d x %d)"), screen_width, screen_height);
+ pentry = menuentry_new(pmenu, buffer, 0, 0);
+ sprintf(buffer, "%d", screen_width);
+ sprintf(buffer + 16, "%d", screen_height);
+ menuentry_extratext(pentry, NULL, buffer, buffer + 16);
+ pmenu->entry_selected = pentry;
+ }
+
+ pentry = menuentry_new(pmenu, gettext("Current screen size:"), 0, MENU_NOTE);
+ sprintf(buffer, gettext("%d x %d (%d bits per pixel)"), screen_width, screen_height, screen_surface->format->BitsPerPixel);
+ pentry = menuentry_new(pmenu, buffer, 0, MENU_NOTE | MENU_RIGHT);
+
+ menu_assignletters(pmenu);
+
+ if(menu_process(pmenu) == MENU_SELECT)
+ {
+ if(pmenu->entry_selected->text3 != NULL)
+ {
+ options_sdl_width = atoi(pmenu->entry_selected->text3);
+ options_sdl_height = atoi(pmenu->entry_selected->text4);
+ screen_resize(options_sdl_width, options_sdl_height, screen_fullscreen);
+ }
+ }
+
+ menu_delete(pmenu);
+}
+
+int display_keyfixed(SDLKey key)
+{
+ if(key == SDLK_ESCAPE || key == SDLK_q || key == SDLK_RETURN || key == SDLK_UP || key == SDLK_DOWN || key == SDLK_LEFT || key == SDLK_RIGHT)
+ return 1;
+
+ return 0;
+}
+
+char *display_keyname(SDLKey key)
+{
+ return SDL_GetKeyName(key);
+}
+
+void display_addkeytomenu(struct menu* pmenu, int action, char *text)
+{
+ struct menuentry *pentry;
+ char buffer[256];
+ SDLKey key;
+
+ sprintf(buffer, "%d", action);
+ pentry = menuentry_newwithvalue(pmenu, text, 0, MENU_DOUBLE, buffer);
+
+ strcpy(buffer, "");
+ for(key = SDLK_FIRST; key < SDLK_LAST; key ++)
+ {
+ if(actions[key] == action)
+ {
+ if(strlen(buffer) != 0)
+ strcat(buffer,", ");
+ strcat(buffer, "[");
+ strcat(buffer, display_keyname(key));
+ strcat(buffer, "]");
+ }
+ }
+
+ if(strcmp(buffer, "") == 0)
+ strcpy(buffer, gettext("(none)"));
+
+ menuentry_extratext(pentry, NULL, NULL, buffer);
+}
+
+void display_options_keys()
+{
+ struct menu *pmenu;
+ struct menu *psubmenu;
+ struct menuentry *pentry;
+ int redraw;
+ int action;
+ int result;
+ int ok;
+ int subok;
+ char buffer[256];
+ SDLKey key;
+ SDL_Event event;
+
+ ok = 0;
+ while(!ok)
+ {
+ pmenu = menu_new(gettext("Keys"));
+
+ menuentry_new(pmenu, gettext("Quit and return to previous menu"), 'Q', 0);
+ menuentry_new(pmenu, "", 0, MENU_SPACE);
+
+ display_addkeytomenu(pmenu, ACTION_LEFT, gettext(action_name[ACTION_LEFT]));
+ display_addkeytomenu(pmenu, ACTION_RIGHT, gettext(action_name[ACTION_RIGHT]));
+ display_addkeytomenu(pmenu, ACTION_UP, gettext(action_name[ACTION_UP]));
+ display_addkeytomenu(pmenu, ACTION_DOWN, gettext(action_name[ACTION_DOWN]));
+ display_addkeytomenu(pmenu, ACTION_SWAP, gettext(action_name[ACTION_SWAP]));
+ display_addkeytomenu(pmenu, ACTION_UNDO, gettext(action_name[ACTION_UNDO]));
+ display_addkeytomenu(pmenu, ACTION_REDO, gettext(action_name[ACTION_REDO]));
+ display_addkeytomenu(pmenu, ACTION_FAST, gettext(action_name[ACTION_FAST]));
+ display_addkeytomenu(pmenu, ACTION_FASTER, gettext(action_name[ACTION_FASTER]));
+ display_addkeytomenu(pmenu, ACTION_SLOWER, gettext(action_name[ACTION_SLOWER]));
+ display_addkeytomenu(pmenu, ACTION_PAUSE, gettext(action_name[ACTION_PAUSE]));
+ display_addkeytomenu(pmenu, ACTION_QUIT, gettext(action_name[ACTION_QUIT]));
+ display_addkeytomenu(pmenu, ACTION_REDRAW, gettext(action_name[ACTION_REDRAW]));
+ display_addkeytomenu(pmenu, ACTION_HIDE, gettext(action_name[ACTION_HIDE]));
+ display_addkeytomenu(pmenu, ACTION_PIECE_LEFT, gettext(action_name[ACTION_PIECE_LEFT]));
+ display_addkeytomenu(pmenu, ACTION_PIECE_RIGHT, gettext(action_name[ACTION_PIECE_RIGHT]));
+
+ menu_assignletters(pmenu);
+
+ result = menu_process(pmenu);
+
+ if(result == MENU_QUIT)
+ ok = 1;
+
+ if(result == MENU_SELECT)
+ {
+ if(pmenu->entry_selected->key == 'Q')
+ ok = 1;
+ else if(pmenu->entry_selected->value != NULL)
+ {
+ subok = 0;
+ redraw = MENUREDRAW_ALL;
+ while(!subok)
+ {
+ action = atoi(pmenu->entry_selected->value);
+
+ sprintf(buffer, gettext("Set keys for '%s'"), gettext(action_name[action]));
+ psubmenu = menu_new(buffer);
+
+ menuentry_new(psubmenu, gettext("Quit and return to previous menu"), 'Q', 0);
+ menuentry_new(psubmenu, "", 0, MENU_SPACE);
+
+ for(key = SDLK_FIRST; key < SDLK_LAST; key ++)
+ {
+ if(actions[key] == action)
+ {
+ sprintf(buffer, "[%s]", display_keyname(key));
+ pentry = menuentry_new(psubmenu, buffer, 0, MENU_GREY);
+ if(display_keyfixed(key))
+ menuentry_extratext(pentry, gettext("(fixed)"), NULL, NULL);
+ }
+ }
+
+ menuentry_new(psubmenu, gettext("Press a key to add or remove it from this list."), 0, MENU_NOTE | MENU_CENTRE);
+
+ menu_display(psubmenu, redraw);
+ redraw = MENUREDRAW_ENTRIES;
+
+ menu_delete(psubmenu);
+
+ subok = 0;
+ while(!subok)
+ {
+ SDL_WaitEvent(&event);
+
+ switch(event.type)
+ {
+ case SDL_KEYDOWN:
+ key = event.key.keysym.sym;
+ if(key == SDLK_q || key == SDLK_ESCAPE)
+ subok = 1;
+ else if(!display_keyfixed(key))
+ {
+ if(actions[key] == action)
+ actions[key] = ACTION_NONE;
+ else
+ actions[key] = action;
+
+ subok = 2;
+ }
+ break;
+
+ case SDL_QUIT:
+ exit(0);
+
+ case SDL_VIDEORESIZE:
+ screen_resizeevent(&event);
+ subok = 2;
+ redraw = MENUREDRAW_ALL;
+ break;
+
+ case SDL_ACTIVEEVENT:
+ if((event.active.state & SDL_APPACTIVE) && event.active.gain == 1)
+ redraw = MENUREDRAW_ALL;
+ break;
+ }
+ }
+ if(subok == 2)
+ subok = 0;
+ }
+ }
+ }
+
+ menu_delete(pmenu);
+ }
+}
+
+void display_options_mouse()
+{
+ struct menu *pmenu;
+ struct menu *psubmenu;
+ struct menuentry *pentry;
+ int result;
+ int ok;
+ int subok;
+ int i, j, k;
+ int button, where;
+ char buffer[256];
+
+ char *locations[] = {"Game", "Editor", "Menu"};
+
+ int actions_game[] = {
+ ACTION_NONE,
+ ACTION_MOUSE_CLICK,
+ ACTION_MOUSE_DRAG,
+ ACTION_MOUSE_DRAG_OR_CLICK,
+ ACTION_SWAP,
+ ACTION_UNDO,
+ ACTION_REDO,
+ ACTION_HIDE,
+ ACTION_QUIT,
+ ACTION_MAX
+ };
+
+ int actions_edit[] = {
+ ACTION_NONE,
+ ACTION_MOUSE_CLICK,
+ ACTION_MOUSE_DRAG,
+ ACTION_MOUSE_DRAG_OR_CLICK,
+ ACTION_PIECE_LEFT,
+ ACTION_PIECE_RIGHT,
+ ACTION_HIDE,
+ ACTION_QUIT,
+ ACTION_MAX
+ };
+
+ int actions_menu[] = {
+ ACTION_NONE,
+ ACTION_UP,
+ ACTION_DOWN,
+ ACTION_PAGE_UP,
+ ACTION_PAGE_DOWN,
+ ACTION_HIDE,
+ ACTION_MOUSE_CLICK,
+ ACTION_QUIT,
+ ACTION_MAX
+ };
+
+ int *actions_available[] = {
+ actions_game,
+ actions_edit,
+ actions_menu,
+ };
+
+ char *actions_names_game[] = {
+ "Do nothing",
+ "Click to move player",
+ "Drag to scroll screen",
+ "Click to move player, drag to scroll screen",
+ "Click to swap player",
+ "Undo move",
+ "Redo move",
+ "Hide screen",
+ "Return to previous menu",
+ ""
+ };
+
+ char *actions_names_edit[] = {
+ "Do nothing",
+ "Click to set piece",
+ "Drag to scroll screen",
+ "Click to set piece, drag to scroll screen",
+ "Piece left",
+ "Piece right",
+ "Hide screen",
+ "Return to previous menu",
+ ""
+ };
+
+ char *actions_names_menu[] = {
+ "Do nothing",
+ "Move up",
+ "Move down",
+ "Page up",
+ "Page down",
+ "Hide screen",
+ "Select entry",
+ "Return to previous menu",
+ ""
+ };
+
+ char **actions_names[] = {
+ actions_names_game,
+ actions_names_edit,
+ actions_names_menu,
+ };
+
+ ok = 0;
+ pmenu = menu_new(gettext("Mouse"));
+
+ menuentry_new(pmenu, gettext("Quit and return to previous menu"), 'Q', 0);
+
+ for(i = 0; i < 3; i ++)
+ {
+ menuentry_new(pmenu, "", 0, MENU_SPACE);
+ menuentry_new(pmenu, locations[i], 0, MENU_GREY);
+ for(j = 1; j < MOUSE_BUTTONS_MAX; j ++)
+ {
+ sprintf(buffer, gettext("Button %d"), j);
+ pentry = menuentry_new(pmenu, buffer, 0, 0);
+
+ sprintf(buffer, "%d%d", i, j);
+ menuentry_value(pentry, buffer);
+
+ /* Should be overwritten shortly - this is a fallback if not */
+ sprintf(buffer, "? %s", gettext(action_name[actions_mouse[i][j]]));
+ menuentry_extratext(pentry, buffer, NULL, NULL);
+
+ /* Perhaps not the neatest way of doing this here, but saves
+ having to have redundant actions merely to give them different
+ names. */
+ k = 0;
+ while(actions_available[i][k] != ACTION_MAX)
+ {
+ if(actions_available[i][k] == actions_mouse[i][j])
+ menuentry_extratext(pentry, gettext(actions_names[i][k]), NULL, NULL);
+ k ++;
+ }
+ }
+ }
+
+ menu_assignletters(pmenu);
+
+ ok = 0;
+ while(!ok)
+ {
+ result = menu_process(pmenu);
+
+ if(result == MENU_QUIT)
+ ok = 1;
+
+ if(result == MENU_SELECT)
+ {
+ if(pmenu->entry_selected->key == 'Q')
+ ok = 1;
+ else if(pmenu->entry_selected->value != NULL)
+ {
+ where = pmenu->entry_selected->value[0] - '0';
+ button = pmenu->entry_selected->value[1] - '0';
+ sprintf(buffer, gettext("Set action for button %d in %s"), button, locations[where]);
+ psubmenu = menu_new(buffer);
+
+ menuentry_new(psubmenu, gettext("Quit and return to previous menu"), 'Q', 0);
+ menuentry_new(psubmenu, "", 0, MENU_SPACE);
+
+ i = 0;
+ while(actions_available[where][i] != ACTION_MAX)
+ {
+ pentry = menuentry_new(psubmenu, gettext(actions_names[where][i]), 0, 0);
+ sprintf(buffer, "%d", i);
+ menuentry_value(pentry, buffer);
+ if(actions_mouse[where][button] == actions_available[where][i])
+ {
+ psubmenu->entry_selected = pentry;
+
+ menuentry_new(psubmenu, gettext("Current action is:"), 0, MENU_NOTE);
+ menuentry_new(psubmenu, gettext(actions_names[where][i]), 0, MENU_NOTE | MENU_RIGHT);
+ }
+
+ i ++;
+ }
+
+ menu_assignletters(psubmenu);
+
+ subok = 0;
+ while(!subok)
+ {
+ result = menu_process(psubmenu);
+
+ if(result == MENU_QUIT)
+ subok = 1;
+
+ if(result == MENU_SELECT)
+ {
+ if(psubmenu->entry_selected->key == 'Q')
+ subok = 1;
+ else if(psubmenu->entry_selected->value != NULL)
+ {
+ actions_mouse[where][button] = actions_available[where][atoi(psubmenu->entry_selected->value)];
+ menuentry_extratext(pmenu->entry_selected, gettext(actions_names[where][atoi(psubmenu->entry_selected->value)]), NULL, NULL);
+ subok = 1;
+ }
+ }
+
+ }
+ menu_delete(psubmenu);
+ }
+ }
+
+ }
+
+ menu_delete(pmenu);
+}
+
+void display_options_size()
+{
+ struct menu* pmenu;
+ struct menuentry* pentry;
+ struct graphicssize* psize;
+ char buffer[4096];
+
+ pmenu = menu_new(gettext("Graphics Size"));
+
+ menuentry_new(pmenu, gettext("Quit and return to previous menu"), 'Q', 0);
+ menuentry_new(pmenu, "", 0, MENU_SPACE);
+
+ menuentry_new(pmenu, gettext("Current graphics size:"), 0, MENU_NOTE);
+ if(pdisplaygraphics != NULL)
+ {
+ if(options_sdl_size_x != pdisplaygraphics->size_x || options_sdl_size_y != pdisplaygraphics->size_y)
+ sprintf(buffer, gettext("Automatic (%d x %d)"), pdisplaygraphics->size_x, pdisplaygraphics->size_y);
+ else
+ sprintf(buffer, "%d x %d", pdisplaygraphics->size_x, pdisplaygraphics->size_y);
+ menuentry_new(pmenu, buffer, 0, MENU_NOTE | MENU_RIGHT);
+ }
+ else
+ menuentry_new(pmenu, gettext("** NONE **"), 0, MENU_NOTE | MENU_RIGHT);
+
+ pentry = menuentry_new(pmenu, gettext("Automatic sizing"), 0, 0);
+ menuentry_extratext(pentry, NULL, "0", "0");
+ if(options_sdl_size_x != pdisplaygraphics->size_x || options_sdl_size_y != pdisplaygraphics->size_y)
+ pmenu->entry_selected = pentry;
+
+ psize = pdisplaygraphics->sizes;
+ while(psize != NULL)
+ {
+ if(psize->flags & SIZE_PIECES)
+ {
+ sprintf(buffer, "%d x %d", psize->x, psize->y);
+ pentry = menuentry_new(pmenu, buffer, 0, 0);
+ if(psize->x == options_sdl_size_x && psize->y == options_sdl_size_y)
+ pmenu->entry_selected = pentry;
+ sprintf(buffer, "%d", psize->x);
+ sprintf(buffer + 16, "%d", psize->y);
+ menuentry_extratext(pentry, NULL, buffer, buffer + 16);
+ }
+ psize = psize->next;
+ }
+ menu_assignletters(pmenu);
+
+ if(menu_process(pmenu) == MENU_SELECT)
+ {
+ if(pmenu->entry_selected->text3 != NULL)
+ {
+ options_sdl_size_x = atoi(pmenu->entry_selected->text3);
+ options_sdl_size_y = atoi(pmenu->entry_selected->text4);
+
+ graphics_init();
+ }
+ }
+
+ menu_delete(pmenu);
+}
+
+void display_options_debug()
+{
+ struct menu* pmenu;
+ struct menuentry* pentryorder;
+ struct menuentry* pentrymovers;
+ struct menuentry* pentryspeed;
+ struct menuentry* pentryhidden;
+
+ int ok;
+ int result;
+
+ pmenu = menu_new(gettext("Debug Options"));
+
+ menuentry_new(pmenu, gettext("Return to previous menu"), 'Q', 0);
+
+ menuentry_new(pmenu, "", 0, MENU_SPACE);
+
+ pentryorder = menuentry_new(pmenu, gettext("Display order of movers"), 'O', 0);
+ pentrymovers = menuentry_new(pmenu, gettext("List movers on stderr"), 'M', 0);
+ pentryspeed = menuentry_new(pmenu, gettext("Show frames per second"), 'F', 0);
+ pentryhidden = menuentry_new(pmenu, gettext("Show hidden items"), 'H', 0);
+
+ ok = 0;
+ while(!ok)
+ {
+ menuentry_extratext(pentryorder, options_debug & DEBUG_ORDER ? gettext("yes") : gettext("no"), NULL, NULL);
+ menuentry_extratext(pentrymovers, options_debug & DEBUG_MOVERS ? gettext("yes") : gettext("no"), NULL, NULL);
+ menuentry_extratext(pentryspeed, options_debug & DEBUG_SPEED ? gettext("yes") : gettext("no"), NULL, NULL);
+ menuentry_extratext(pentryhidden, options_debug & DEBUG_HIDDEN ? gettext("yes") : gettext("no"), NULL, NULL);
+
+ result = menu_process(pmenu);
+ if(result == MENU_QUIT)
+ ok = 1;
+
+ if(result == MENU_SELECT && pmenu->entry_selected != NULL)
+ {
+ switch(pmenu->entry_selected->key)
+ {
+ case 'Q':
+ ok = 1;
+ break;
+
+ case 'O':
+ options_debug ^= DEBUG_ORDER;
+ break;
+
+ case 'M':
+ options_debug ^= DEBUG_MOVERS;
+ break;
+
+ case 'F':
+ options_debug ^= DEBUG_SPEED;
+ break;
+
+ case 'H':
+ options_debug ^= DEBUG_HIDDEN;
+ break;
+ }
+
+ pmenu->redraw = MENUREDRAW_CHANGED;
+ pmenu->entry_selected->redraw = 1;
+ }
+
+ }
+
+ menu_delete(pmenu);
+}
+
+void display_options_othergames()
+{
+ struct menu* pmenu;
+#ifdef XOR_COMPATIBILITY
+ struct menuentry* pentryxormode;
+ struct menuentry* pentryxordisplay;
+#endif
+#ifdef ENIGMA_COMPATIBILITY
+ struct menuentry* pentryenigmamode;
+#endif
+
+ int ok;
+ int result;
+
+ pmenu = menu_new(gettext("Other Games Options"));
+
+ menuentry_new(pmenu, gettext("Return to previous menu"), 'Q', 0);
+
+ menuentry_new(pmenu, "", 0, MENU_SPACE);
+
+#ifdef XOR_COMPATIBILITY
+ pentryxormode = menuentry_new(pmenu, gettext("XOR Engine"), 'X', options_xor_options ? 0 : MENU_INVISIBLE | MENU_GREY);
+ pentryxordisplay = menuentry_new(pmenu, gettext("XOR Display"), 'D', options_xor_options ? 0 : MENU_INVISIBLE | MENU_GREY);
+ if(options_xor_options)
+ menuentry_new(pmenu, "", 0, MENU_SPACE);
+#endif
+
+#ifdef ENIGMA_COMPATIBILITY
+ pentryenigmamode = menuentry_new(pmenu, gettext("Enigma Engine"), 'E', options_enigma_options ? 0 : MENU_INVISIBLE | MENU_GREY);
+#endif
+
+ ok = 0;
+ while(!ok)
+ {
+#ifdef XOR_COMPATIBILITY
+ menuentry_extratext(pentryxormode, options_xor_mode ? gettext("exact") : gettext("approximate"), NULL, NULL);
+ menuentry_extratext(pentryxordisplay, options_xor_display ? gettext("partial") : gettext("full"), NULL, NULL);
+#endif
+#ifdef ENIGMA_COMPATIBILITY
+ menuentry_extratext(pentryenigmamode, options_enigma_mode ? gettext("exact") : gettext("approximate"), NULL, NULL);
+#endif
+
+ result = menu_process(pmenu);
+ if(result == MENU_QUIT)
+ ok = 1;
+
+ if(result == MENU_SELECT && pmenu->entry_selected != NULL)
+ {
+ switch(pmenu->entry_selected->key)
+ {
+ case 'Q':
+ ok = 1;
+ break;
+
+#ifdef XOR_COMPATIBILITY
+ case 'X':
+ options_xor_mode = 1 - options_xor_mode;
+ break;
+
+ case 'D':
+ options_xor_display = 1 - options_xor_display;
+ break;
+#endif
+
+#ifdef ENIGMA_COMPATIBILITY
+ case 'E':
+ options_enigma_mode = 1 - options_enigma_mode;
+ break;
+#endif
+ }
+
+ pmenu->redraw = MENUREDRAW_CHANGED;
+ pmenu->entry_selected->redraw = 1;
+ }
+
+ }
+
+ menu_delete(pmenu);
+}
+
+void display_options_save()
+{
+ FILE *file;
+ char filename[FILENAME_MAX];
+ SDLKey key;
+ int i, j;
+ char *locations[] = {"game", "editor", "menu"};
+
+ getfilename("sdl.chroma", filename, 1, LOCATION_LOCAL);
+
+ file = fopen(filename, "w");
+ if(file == NULL)
+ {
+ warning("Unable to save options");
+ return;
+ }
+
+ fprintf(file, "<!-- Chroma SDL options \n"
+ " This file is automatically generated. -->\n"
+ "\n"
+ "<chroma type=\"options\">\n");
+
+ fprintf(file, " <screen ");
+ if(options_sdl_width == 0 && options_sdl_height == 0)
+ fprintf(file, "width=\"auto\" height=\"auto\" ");
+ else
+ fprintf(file, "width=\"%d\" height=\"%d\" ", options_sdl_width, options_sdl_height);
+ fprintf(file, "fullscreen=\"%s\" />\n", options_sdl_fullscreen == 1 ? "yes" : "no");
+
+ fprintf(file, " <graphics scheme=\"%s\" ", options_graphics);
+ if(options_graphic_level != 0)
+ fprintf(file, "level=\"%d\" ", options_graphic_level);
+ if(options_sdl_size_x == 0)
+ fprintf(file, "width=\"auto\" height=\"auto\" />\n");
+ else
+ fprintf(file, "width=\"%d\" height=\"%d\" />\n", options_sdl_size_x, options_sdl_size_y);
+
+ fprintf(file, " <colour scheme=\"%s\" />\n", options_colours);
+
+ fprintf(file, " <move speed=\"%d\" />\n", options_sdl_delay);
+ fprintf(file, " <player speed=\"%d\" />\n", options_sdl_player_delay);
+ fprintf(file, " <replay speed=\"%d\" />\n", options_sdl_replay_delay);
+ fprintf(file, " <undo speed=\"%d\" />\n", options_sdl_undo_delay);
+
+#ifdef XOR_COMPATIBILITY
+ if(options_xor_options)
+ fprintf(file, " <xor mode=\"%s\" display=\"%s\" />\n", options_xor_mode ? "exact" : "approximate", options_xor_display ? "partial" : "full");
+#endif
+#ifdef ENIGMA_COMPATIBILITY
+ if(options_enigma_options)
+ fprintf(file, " <enigma mode=\"%s\" />\n", options_enigma_mode ? "exact" : "approximate");
+#endif
+
+ fprintf(file, " <!-- Set <debug menu=\"yes\" /> to change debug options within Chroma -->\n");
+ fprintf(file, " <debug ");
+ fprintf(file, "menu=\"%s\" ", options_debug & DEBUG_MENU ? "yes" : "no");
+ fprintf(file, "order=\"%s\" ", options_debug & DEBUG_ORDER ? "yes" : "no");
+ fprintf(file, "speed=\"%s\" ", options_debug & DEBUG_SPEED ? "yes" : "no");
+ fprintf(file, "movers=\"%s\" ", options_debug & DEBUG_MOVERS ? "yes" : "no");
+ fprintf(file, "hidden=\"%s\" ", options_debug & DEBUG_HIDDEN ? "yes" : "no");
+ fprintf(file, "/>\n");
+
+ fprintf(file, " <keys>\n");
+
+ for(key = SDLK_FIRST; key < SDLK_LAST; key ++)
+ {
+ if(actions[key] != ACTION_NONE)
+ fprintf(file, " <key name=\"%s\" action=\"%s\" />\n", display_keyname(key), action_shortname[actions[key]]);
+ }
+
+ fprintf(file, " </keys>\n");
+
+ for(i = 0; i < 3; i ++)
+ {
+ fprintf(file, " <mouse location=\"%s\">\n", locations[i]);
+ for(j = 1; j < MOUSE_BUTTONS_MAX; j ++)
+ {
+ fprintf(file, " <button type=\"%d\" action=\"%s\" />\n", j, action_shortname[actions_mouse[i][j]]);
+ }
+ fprintf(file, " </mouse>\n");
+ }
+
+ fprintf(file, "</chroma>\n");
+
+ fclose(file);
+}
+
+void display_options_load()
+{
+ struct parser* pparser;
+ char filename[FILENAME_MAX];
+ int state;
+ SDLKey k;
+ int i, key, action, location, button;
+
+ /* Sensible defaults */
+ options_sdl_fullscreen = 1;
+ options_sdl_width = 0;
+ options_sdl_height = 0;
+ options_sdl_delay = 100;
+ options_sdl_player_delay = 200;
+ options_sdl_replay_delay = 200;
+ options_sdl_undo_delay = 200;
+ options_sdl_size_x = 0;
+ options_sdl_size_y = 0;
+ options_graphic_level = 0;
+#ifdef XOR_COMPATIBILITY
+ options_xor_options = 0;
+ options_xor_mode = 1;
+ options_xor_display = 0;
+#endif
+#ifdef ENIGMA_COMPATIBILITY
+ options_enigma_options = 0;
+ options_enigma_mode = 1;
+#endif
+ options_debug = 0;
+
+ getfilename("colours", filename, 0, LOCATION_SYSTEM);
+ sprintf(options_colours, "%s/%s", filename, COLOURS_DEFAULT);
+ getfilename("graphics", filename, 0, LOCATION_SYSTEM);
+ sprintf(options_graphics, "%s/%s", filename, GRAPHICS_DEFAULT);
+
+ getfilename("sdl.chroma", filename, 0, LOCATION_LOCAL);
+
+ for(k = SDLK_FIRST; k < SDLK_LAST; k ++)
+ {
+ actions[k] = ACTION_NONE;
+ }
+
+ for(action = 0; action < MOUSE_BUTTONS_MAX; action ++)
+ {
+ actions_mouse[ACTIONS_GAME][action] = ACTION_NONE;
+ actions_mouse[ACTIONS_MENU][action] = ACTION_NONE;
+ actions_mouse[ACTIONS_EDIT][action] = ACTION_NONE;
+ }
+
+ /* Fixed keys */
+ actions[SDLK_UP] = ACTION_UP;
+ actions[SDLK_DOWN] = ACTION_DOWN;
+ actions[SDLK_LEFT] = ACTION_LEFT;
+ actions[SDLK_RIGHT] = ACTION_RIGHT;
+ actions[SDLK_RETURN] = ACTION_SWAP;
+ actions[SDLK_q] = ACTION_QUIT;
+ actions[SDLK_ESCAPE] = ACTION_QUIT;
+
+ actions_mouse[ACTIONS_GAME][1] = ACTION_MOUSE_DRAG_OR_CLICK;
+ actions_mouse[ACTIONS_GAME][2] = ACTION_SWAP;
+ actions_mouse[ACTIONS_GAME][3] = ACTION_UNDO;
+ actions_mouse[ACTIONS_GAME][4] = ACTION_UNDO;
+ actions_mouse[ACTIONS_GAME][5] = ACTION_REDO;
+
+ actions_mouse[ACTIONS_MENU][1] = ACTION_MOUSE_CLICK;
+ actions_mouse[ACTIONS_MENU][2] = ACTION_MOUSE_CLICK;
+ actions_mouse[ACTIONS_MENU][3] = ACTION_QUIT;
+ actions_mouse[ACTIONS_MENU][4] = ACTION_PAGE_UP;
+ actions_mouse[ACTIONS_MENU][5] = ACTION_PAGE_DOWN;
+
+ actions_mouse[ACTIONS_EDIT][1] = ACTION_MOUSE_CLICK;
+ actions_mouse[ACTIONS_EDIT][2] = ACTION_MOUSE_DRAG;
+ actions_mouse[ACTIONS_EDIT][3] = ACTION_QUIT;
+ actions_mouse[ACTIONS_EDIT][4] = ACTION_PIECE_LEFT;
+ actions_mouse[ACTIONS_EDIT][5] = ACTION_PIECE_RIGHT;
+
+ /* Sensible default keys */
+ if(!isfile(filename))
+ {
+ actions[SDLK_SPACE] = ACTION_SWAP;
+ actions[SDLK_f] = ACTION_FAST;
+ actions[SDLK_BACKSPACE] = ACTION_UNDO;
+ actions[SDLK_DELETE] = ACTION_UNDO;
+ actions[SDLK_u] = ACTION_UNDO;
+ actions[SDLK_INSERT] = ACTION_REDO;
+ actions[SDLK_y] = ACTION_REDO;
+ actions[SDLK_p] = ACTION_PAUSE;
+ actions[SDLK_LSHIFT] = ACTION_FASTER;
+ actions[SDLK_RSHIFT] = ACTION_FASTER;
+ actions[SDLK_LCTRL] = ACTION_SLOWER;
+ actions[SDLK_RCTRL] = ACTION_SLOWER;
+ actions[SDLK_z] = ACTION_PIECE_LEFT;
+ actions[SDLK_x] = ACTION_PIECE_RIGHT;
+ actions[SDLK_PAGEUP] = ACTION_PIECE_LEFT;;
+ actions[SDLK_PAGEDOWN] = ACTION_PIECE_RIGHT;
+ actions[SDLK_KP8] = ACTION_UP;
+ actions[SDLK_KP2] = ACTION_DOWN;
+ actions[SDLK_KP4] = ACTION_LEFT;
+ actions[SDLK_KP6] = ACTION_RIGHT;
+ actions[SDLK_KP_ENTER] = ACTION_SWAP;
+ actions[SDLK_KP_MINUS] = ACTION_UNDO;
+ actions[SDLK_KP_PLUS] = ACTION_REDO;
+ actions[SDLK_KP_DIVIDE] = ACTION_PIECE_LEFT;
+ actions[SDLK_KP_MULTIPLY] = ACTION_PIECE_RIGHT;
+
+ return;
+ }
+
+ /* Parse XML file */
+ /*
+ <chroma type="options">
+ <colour scheme="filename" />
+ <move speed="speed" />
+ <replay speed="speed" />
+ <xor mode="mode" />
+ <debug movers="yes/no" />
+ <keys>
+ <key name="name" action="action" />
+ </keys>
+ </chroma>
+ */
+
+ pparser = parser_new(filename);
+
+ enum {
+ OPTIONSPARSER_END, /* End of file */
+ OPTIONSPARSER_OUTSIDE, /* Outside of <chroma> */
+ OPTIONSPARSER_CHROMA, /* Inside <chroma> */
+ OPTIONSPARSER_KEYS, /* Inside <keys> */
+ OPTIONSPARSER_MOUSE /* Inside <mouse> */
+ };
+
+ state = OPTIONSPARSER_OUTSIDE;
+ key = 0;
+ action = 0;
+ location = -1;
+ button = -1;
+
+ while(state != OPTIONSPARSER_END)
+ {
+ switch(parser_parse(pparser))
+ {
+ case PARSER_END:
+ state = OPTIONSPARSER_END;
+ break;
+
+ case PARSER_ELEMENT_START:
+ switch(state)
+ {
+ case OPTIONSPARSER_CHROMA:
+ if(parser_match(pparser, 0, "keys"))
+ state = OPTIONSPARSER_KEYS;
+ if(parser_match(pparser, 0, "mouse"))
+ {
+ location = -1;
+ state = OPTIONSPARSER_MOUSE;
+ }
+ break;
+
+ case OPTIONSPARSER_KEYS:
+ if(parser_match(pparser, 0, "key"))
+ {
+ key = 0;
+ action = ACTION_NONE;
+ }
+ break;
+
+ case OPTIONSPARSER_MOUSE:
+ if(parser_match(pparser, 0, "button"))
+ {
+ button = -1;
+ action = ACTION_NONE;
+ }
+ break;
+ }
+ break;
+
+ case PARSER_ELEMENT_END:
+ switch(state)
+ {
+ case OPTIONSPARSER_KEYS:
+ if(parser_match(pparser, 0, "keys"))
+ state = OPTIONSPARSER_CHROMA;
+ if(parser_match(pparser, 0, "key"))
+ {
+ if(key != 0 && !display_keyfixed(key))
+ actions[key] = action;
+ }
+ break;
+
+ case OPTIONSPARSER_MOUSE:
+ if(parser_match(pparser, 0, "mouse"))
+ state = OPTIONSPARSER_CHROMA;
+ if(parser_match(pparser, 0, "button"))
+ {
+ if(location != -1 && button != -1)
+ actions_mouse[location][button] = action;
+ }
+ break;
+ }
+ break;
+
+ case PARSER_CONTENT:
+ break;
+
+ case PARSER_ATTRIBUTE:
+ switch(state)
+ {
+ case OPTIONSPARSER_OUTSIDE:
+ if(parser_match(pparser, 2, "chroma") && parser_match(pparser, 1, "type"))
+ {
+ if(parser_match(pparser, 0, "options"))
+ state = OPTIONSPARSER_CHROMA;
+ }
+ break;
+
+ case OPTIONSPARSER_CHROMA:
+ if(parser_match(pparser, 2, "screen") && parser_match(pparser, 1, "width"))
+ {
+ options_sdl_width = atoi(parser_text(pparser, 0));
+ }
+ if(parser_match(pparser, 2, "screen") && parser_match(pparser, 1, "height"))
+ {
+ options_sdl_height = atoi(parser_text(pparser, 0));
+ }
+ if(parser_match(pparser, 2, "screen") && parser_match(pparser, 1, "fullscreen"))
+ {
+ if(parser_match(pparser, 0, "yes"))
+ options_sdl_fullscreen = 1;
+ if(parser_match(pparser, 0, "no"))
+ options_sdl_fullscreen = 0;
+ }
+ if(parser_match(pparser, 2, "colour") && parser_match(pparser, 1, "scheme"))
+ {
+ strncpy(options_colours, parser_text(pparser, 0), FILENAME_MAX);
+ }
+ if(parser_match(pparser, 2, "graphics") && parser_match(pparser, 1, "scheme"))
+ {
+ strncpy(options_graphics, parser_text(pparser, 0), FILENAME_MAX);
+ }
+ if(parser_match(pparser, 2, "graphics") && parser_match(pparser, 1, "width"))
+ {
+ options_sdl_size_x = atoi(parser_text(pparser, 0));
+ }
+ if(parser_match(pparser, 2, "graphics") && parser_match(pparser, 1, "height"))
+ {
+ options_sdl_size_y = atoi(parser_text(pparser, 0));
+ }
+ if(parser_match(pparser, 2, "graphics") && parser_match(pparser, 1, "level"))
+ {
+ options_graphic_level = atoi(parser_text(pparser, 0));
+ }
+ if(parser_match(pparser, 2, "move") && parser_match(pparser, 1, "speed"))
+ {
+ options_sdl_delay = atoi(parser_text(pparser, 0));
+ }
+ if(parser_match(pparser, 2, "player") && parser_match(pparser, 1, "speed"))
+ {
+ options_sdl_player_delay = atoi(parser_text(pparser, 0));
+ }
+ if(parser_match(pparser, 2, "replay") && parser_match(pparser, 1, "speed"))
+ {
+ options_sdl_replay_delay = atoi(parser_text(pparser, 0));
+ }
+ if(parser_match(pparser, 2, "undo") && parser_match(pparser, 1, "speed"))
+ {
+ options_sdl_undo_delay = atoi(parser_text(pparser, 0));
+ }
+#ifdef XOR_COMPATIBILITY
+ if(parser_match(pparser, 2, "xor") && parser_match(pparser, 1, "mode"))
+ {
+ options_xor_options = 1;
+
+ if(parser_match(pparser, 0, "approximate"))
+ options_xor_mode = 0;
+ if(parser_match(pparser, 0, "exact"))
+ options_xor_mode = 1;
+ }
+ if(parser_match(pparser, 2, "xor") && parser_match(pparser, 1, "display"))
+ {
+ options_xor_options = 1;
+
+ if(parser_match(pparser, 0, "full"))
+ options_xor_display = 0;
+ if(parser_match(pparser, 0, "partial"))
+ options_xor_display = 1;
+ }
+#endif
+#ifdef ENIGMA_COMPATIBILITY
+ if(parser_match(pparser, 2, "enigma") && parser_match(pparser, 1, "mode"))
+ {
+ options_enigma_options = 1;
+
+ if(parser_match(pparser, 0, "approximate"))
+ options_enigma_mode = 0;
+ if(parser_match(pparser, 0, "exact"))
+ options_enigma_mode = 1;
+ }
+#endif
+ if(parser_match(pparser, 2, "debug") && parser_match(pparser, 1, "menu"))
+ {
+ if(parser_match(pparser, 0, "yes"))
+ options_debug |= DEBUG_MENU;
+ }
+ if(parser_match(pparser, 2, "debug") && parser_match(pparser, 1, "order"))
+ {
+ if(parser_match(pparser, 0, "yes"))
+ options_debug |= DEBUG_ORDER;
+ }
+ if(parser_match(pparser, 2, "debug") && parser_match(pparser, 1, "speed"))
+ {
+ if(parser_match(pparser, 0, "yes"))
+ options_debug |= DEBUG_SPEED;
+ }
+ if(parser_match(pparser, 2, "debug") && parser_match(pparser, 1, "movers"))
+ {
+ if(parser_match(pparser, 0, "yes"))
+ options_debug |= DEBUG_MOVERS;
+ }
+ if(parser_match(pparser, 2, "debug") && parser_match(pparser, 1, "hidden"))
+ {
+ if(parser_match(pparser, 0, "yes"))
+ options_debug |= DEBUG_HIDDEN;
+ }
+ break;
+
+ case OPTIONSPARSER_KEYS:
+ if(parser_match(pparser, 2, "key") && parser_match(pparser, 1, "name"))
+ {
+ for(i = SDLK_FIRST; i < SDLK_LAST; i ++)
+ {
+ if(parser_match(pparser, 0, display_keyname(i)))
+ {
+ key = i;
+ i = SDLK_LAST;
+ }
+ }
+ }
+ if(parser_match(pparser, 2, "key") && parser_match(pparser, 1, "action"))
+ {
+ for(i = ACTION_KEY_MIN; i < ACTION_KEY_MAX; i ++)
+ {
+ if(parser_match(pparser, 0, action_shortname[i]))
+ {
+ action = i;
+ i = ACTION_KEY_MAX;
+ }
+ }
+ }
+ break;
+
+ case OPTIONSPARSER_MOUSE:
+ if(parser_match(pparser, 2, "button") && parser_match(pparser, 1, "type"))
+ {
+ button = atoi(parser_text(pparser, 0));
+ if(button < 1 || button >= MOUSE_BUTTONS_MAX)
+ button = -1;
+ }
+ if(parser_match(pparser, 2, "mouse") && parser_match(pparser, 1, "location"))
+ {
+ if(parser_match(pparser, 0, "game"))
+ location = ACTIONS_GAME;
+ if(parser_match(pparser, 0, "menu"))
+ location = ACTIONS_MENU;
+ if(parser_match(pparser, 0, "editor"))
+ location = ACTIONS_EDIT;
+ }
+ if(parser_match(pparser, 2, "button") && parser_match(pparser, 1, "action"))
+ {
+ for(i = 0; i < ACTION_MAX; i ++)
+ {
+ if(parser_match(pparser, 0, action_shortname[i]))
+ {
+ action = i;
+ i = ACTION_MAX;
+ }
+ }
+ }
+ break;
+ }
+ break;
+
+ case PARSER_ERROR:
+ state = OPTIONSPARSER_END;
+ break;
+ }
+ }
+
+ parser_delete(pparser);
+}