Remove array subscript warnings by replacing direct chars by SDLK_* definitions
[hex-a-hop.git] / hex_puzzzle.cpp
blob91e9c49d153010f6fae1f38073fce3c55dcfb058
1 /*
2 Copyright (C) 2005-2007 Tom Beaumont
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 //////////////////////////////////////////////////////
21 // Config
24 #ifdef _DEBUG
25 #define EDIT
26 #endif
28 //#define MAP_LOCKED_VISIBLE
30 #ifndef GAME_NAME
31 #define GAME_NAME "Hex-a-hop"
32 #endif
34 #ifndef DATA_DIR
35 #define DATA_DIR "."
36 #endif
38 #ifdef EDIT
39 // #define MAP_EDIT_HACKS
40 #define MAP_EDIT_HACKS_DISPLAY_UNLOCK 0
41 #define CHEAT
42 #define BMP_SUFFIX ".bmp"
43 #else
44 #define USE_LEVEL_PACKFILE
45 #define BMP_SUFFIX ".dat"
46 #endif
50 #ifdef EDIT
51 #define GAMENAME GAME_NAME " (EDIT MODE)"
52 #endif
53 #ifndef GAMENAME
54 #define GAMENAME GAME_NAME
55 #endif
57 #define IMAGE_DAT_OR_MASK 0xff030303 // Reduce colour depth of images slightly for better compression (and remove useless top 8 bits!)
58 #define STARTING_LEVEL "Levels\\0_green\\triangular.lev"
59 #define UNLOCK_SCORING 75
60 const char * mapname = "Levels\\map_maybe\\map.lev";
62 //////////////////////////////////////////////////////
66 #ifndef USE_OPENGL
68 #include "state.h"
70 #include "tiletypes.h"
72 #ifdef USE_LEVEL_PACKFILE
73 #include "packfile.h"
74 #endif
76 #include <unistd.h>
77 #include <limits.h>
78 #include <sys/stat.h>
79 #include <sys/types.h>
81 #ifndef PATH_MAX
82 #define PATH_MAX 4096
83 #endif
85 void RenderTile(bool reflect, int t, int x, int y, int cliplift=-1);
87 int keyState[SDLK_LAST] = {0};
89 FILE *file_open( const char *file, const char *flags )
91 // printf("file_open( \"%s\", \"%s\" )\n", file, flags );
92 extern String base_path;
93 static String filename; // static to reduce memory alloc/free calls.
94 if (file[0]=='/') //If a full path is specified, don't prepend base_path
95 filename = "";
96 else
98 if (strncmp(file, "save", 4) == 0)
100 const char *home = getenv("HOME");
101 if (home)
103 char save_path[PATH_MAX];
104 snprintf(save_path, sizeof(save_path), "%s/.hex-a-hop", home);
105 if (!strchr(flags, 'r'))
106 if (mkdir(save_path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != -1)
107 printf("Creating directory \"%s\"\n", (const char *)save_path);
108 strncat(save_path, "/", sizeof(save_path));
109 filename = save_path;
111 else filename = "/tmp/";
113 else filename = base_path;
115 filename += file;
116 // printf(" -> \"%s\"\n", filename );
118 filename.fix_backslashes();
119 FILE* f = fopen( filename, flags );
121 if (!f && strncmp(file, "save", 4) != 0)
123 printf("Warning: unable to open file \"%s\" for %s\n", (const char*)filename, strchr(flags, 'r') ? "reading" : "writing");
126 return f;
130 #ifdef MAP_EDIT_HACKS
131 static const short value_order[]={
132 //WALL,
133 //COLLAPSE_DOOR2,
134 //COLLAPSABLE3
135 //SWITCH
136 //EMPTY, NORMAL,
138 COLLAPSABLE,
139 TRAMPOLINE,
140 COLLAPSE_DOOR, COLLAPSABLE2,
141 GUN,
142 FLOATING_BALL,
143 SPINNER,
144 TRAP,
145 0x100,
146 LIFT_DOWN, LIFT_UP,
147 BUILDER,
148 0x200,
150 #endif
152 //#define PROGRESS_FILE "progress.dat"
154 #define PI (3.1415926535897931)
155 #define PI2 (PI*2)
156 #define MAX(a,b) ((a)>(b) ? (a) : (b))
157 #define MIN(a,b) ((a)<(b) ? (a) : (b))
158 #define ABS(a) ((a)<0 ? -(a) : (a))
160 #define WATER_COLOUR 31 | (IMAGE_DAT_OR_MASK>>16)&255, 37 | (IMAGE_DAT_OR_MASK>>8)&255, 135 | (IMAGE_DAT_OR_MASK>>0)&255
162 #define ROTATION_TIME 0.25
163 #define BUILD_TIME 1
164 #define LASER_LINE_TIME 0.7
165 #define LASER_FADE_TIME 0.1
166 #define LASER_SEGMENT_TIME 0.01
167 #define LIFT_TIME 0.5
168 #define JUMP_TIME 0.4
170 #define X(NAME,FILE,ALPHA) SDL_Surface* NAME = 0;
171 #include "gfx_list.h"
172 int scrollX=0, scrollY=0, initScrollX=0, initScrollY=0;
173 int mapRightBound = 0;
174 int mapScrollX = 0;
175 bool showScoring = false;
176 bool hintsDone = false;
178 enum {
179 TILE_SPLASH_1 = 17,
180 TILE_SPLASH_2,
181 TILE_SPLASH_3,
183 TILE_SPHERE = 20,
184 TILE_SPHERE_OPEN,
185 TILE_SPHERE_DONE,
186 TILE_SPHERE_PERFECT,
187 TILE_LOCK,
189 TILE_LIFT_BACK,
190 TILE_LIFT_FRONT,
191 TILE_LIFT_SHAFT,
192 TILE_BLUE_FRONT,
193 TILE_GREEN_FRONT,
195 TILE_LINK_0 = 30,
196 TILE_LINK_1,
197 TILE_LINK_2,
198 TILE_LINK_3,
199 TILE_LINK_4,
200 TILE_LINK_5,
201 TILE_GREEN_FRAGMENT,
202 TILE_GREEN_FRAGMENT_1,
203 TILE_GREEN_FRAGMENT_2,
204 TILE_ITEM2,
206 TILE_WATER_MAP = 40,
207 TILE_GREEN_CRACKED,
208 TILE_GREEN_CRACKED_WALL,
209 TILE_BLUE_CRACKED,
210 TILE_BLUE_CRACKED_WALL,
211 TILE_LASER_HEAD,
212 TILE_FIRE_PARTICLE_1,
213 TILE_FIRE_PARTICLE_2,
214 TILE_WATER_PARTICLE,
216 TILE_LASER_0 = 50,
217 TILE_LASER_FADE_0 = 53,
218 TILE_BLUE_FRAGMENT = 56,
219 TILE_BLUE_FRAGMENT_1,
220 TILE_BLUE_FRAGMENT_2,
221 TILE_ITEM1,
222 TILE_LASER_REFRACT = 60,
223 TILE_ICE_LASER_REFRACT = TILE_LASER_REFRACT+6,
224 TILE_WHITE_TILE,
225 TILE_WHITE_WALL,
226 TILE_BLACK_TILE,
230 const int colours[] = {
231 #define X(n,col, solid) col,
232 #include "tiletypes.h"
235 const int tileSolid[] = {
236 #define X(n,col, solid) solid,
237 #include "tiletypes.h"
240 void ChangeSuffix(char* filename, char* newsuffix)
242 int len = strlen(filename);
243 int i = len-1;
244 while (i>=0 && filename[i]!='\\' && filename[i]!='.' && filename[i]!='/')
245 i--;
246 if (filename[i]=='.')
247 strcpy(filename+i+1, newsuffix);
248 else
250 strcat(filename, ".");
251 strcat(filename, newsuffix);
255 bool isMap=false, isRenderMap=false;
256 int isFadeRendering=0;
259 |--| |--| TILE_W1
260 |--------| TILE_W2
261 |-----| TILE_WL
262 |-----------| TILE_W3
264 *-----* - -
265 / \ |TILE_H1 |TILE_H2
266 / \ | |
267 * * - |
268 \ / |
269 \ / |
270 *-----* -
272 WL = sqrt(h1*h1 + w1*w1)
273 wl**2 = h1**2 + w1**2
275 w1 = sin60.wL
279 #if 1
280 #define TILE_W1 18
281 #define TILE_W3 64
282 #define GFX_SIZE TILE_W3
283 #define TILE_W2 (TILE_W3-TILE_W1)
284 #define TILE_H1 TILE_W1
285 #define TILE_HUP 22 //extra visible height of wall (used for determining whether a wall was clicked on)
286 #define TILE_H2 (TILE_H1*2)
287 #define TILE_WL (TILE_W2-TILE_W1)
288 #define TILE_H_LIFT_UP 26
289 #define TILE_H_REFLECT_OFFSET 24
290 #define TILE_HUP2 TILE_H_LIFT_UP // Displacement of object on top of wall
291 #define FONT_SPACING 25
292 #define FONT_X_SPACING (-1) // -1 in order to try and overlap the black borders of adjacent characters
293 #else
294 #define TILE_WL 30
295 #define TILE_W1 (TILE_WL/2)
296 #define TILE_W2 (TILE_W1+TILE_WL)
297 #define TILE_W3 (TILE_W1+TILE_W2)
298 #define TILE_H1 (TILE_WL*0.8660254037844386)
299 #define TILE_H2 (TILE_H1*2)
300 #endif
302 #define MAX_DIR 6
304 SDL_Rect font[256];
305 SDL_Rect tile[2][70];
306 short tileOffset[2][70][2];
307 int Peek(SDL_Surface* i, int x, int y)
309 if (x<0 || y<0 || x>=i->w || y>=i->h)
310 return 0;
311 unsigned int p=0;
312 const int BytesPerPixel = i->format->BytesPerPixel;
313 const int BitsPerPixel = i->format->BitsPerPixel;
314 if (BitsPerPixel==8)
315 p = ((unsigned char*)i->pixels)[i->pitch*y + x*BytesPerPixel];
316 else if (BitsPerPixel==15 || BitsPerPixel==16)
317 p = *(short*)(((char*)i->pixels) + (i->pitch*y + x*BytesPerPixel));
318 else if (BitsPerPixel==32)
319 p = *(unsigned int*)(((char*)i->pixels) + (i->pitch*y + x*BytesPerPixel));
320 else if (BitsPerPixel==24)
321 p = (int)((unsigned char*)i->pixels)[i->pitch*y + x*BytesPerPixel]
322 | (int)((unsigned char*)i->pixels)[i->pitch*y + x*BytesPerPixel] << 8
323 | (int)((unsigned char*)i->pixels)[i->pitch*y + x*BytesPerPixel] << 16;
325 return p;
327 bool IsEmpty(SDL_Surface* im, int x, int y, int w, int h)
329 for (int i=x; i<x+w; i++)
330 for (int j=y; j<y+h; j++)
331 if (Peek(im,i,j) != Peek(im,0,im->h-1))
332 return false;
333 return true;
335 void MakeFont()
337 memset(font, 0, sizeof(font));
339 int h = FONT_SPACING;
340 int x=-1, y=0;
341 for (int i=33; i<=127; i++)
345 x++;
346 if (x>=fontImage->w)
347 x=0, y+=h;
348 if (y >= fontImage->h)
349 return;
350 if (y+h > fontImage->h)
351 h = fontImage->h - y;
352 } while(IsEmpty(fontImage, x, y, 1, h));
354 int w=1;
355 while(!IsEmpty(fontImage, x+w, y, 1, h) && x+w<fontImage->w)
356 w++;
357 int h1=h;
358 while (h1>1 && IsEmpty(fontImage, x, y+h1-1, w, 1))
359 h1--;
360 font[i].x = x;
361 font[i].y = y;
362 font[i].w = w;
363 font[i].h = h1;
364 //printf("character %c: % 4d % 4d % 4d % 4d\n", i, x, y, w, h1);
365 x+=w;
368 int i=' ';
369 font[i].x = x;
370 font[i].y = y;
371 font[i].w = font['j'].w;
372 font[i].h = 0;
374 void MakeTileInfo()
376 for (int i=0; i<140; i++)
378 SDL_Rect r = {(i%10)*GFX_SIZE, ((i/10)%7)*GFX_SIZE, GFX_SIZE, GFX_SIZE};
379 short * outOffset = tileOffset[i/70][i%70];
380 SDL_Surface * im = (i/70) ? tileGraphicsR : tileGraphics;
382 outOffset[0] = outOffset[1] = 0;
384 while (r.h>1 && IsEmpty(im, r.x, r.y, r.w, 1)) r.h--, r.y++, outOffset[1]++;
385 while (r.h>1 && IsEmpty(im, r.x, r.y+r.h-1, r.w, 1)) r.h--;
386 while (r.w>1 && IsEmpty(im, r.x, r.y, 1, r.h)) r.w--, r.x++, outOffset[0]++;
387 while (r.w>1 && IsEmpty(im, r.x+r.w-1, r.y, 1, r.h)) r.w--;
389 tile[i/70][i%70] = r;
393 void PrintRaw(int x, int y, const char * tmp)
395 for (int i=0; tmp[i]; i++)
397 SDL_Rect dst = {x, y, 1, 1};
398 SDL_BlitSurface(fontImage, &font[tmp[i]], screen, &dst);
399 x += font[tmp[i]].w + FONT_X_SPACING;
403 void Print(int x, int y, const char * string, ...)
405 va_list marker;
406 va_start( marker, string ); /* Initialize variable arguments. */
408 char tmp[1000];
409 vsprintf((char*)tmp, string, marker);
411 PrintRaw(x, y, tmp);
413 va_end( marker ); /* Reset variable arguments. */
416 int FontWidth(const char * string)
418 int w = 0;
419 for (int i=0; string[i]; i++)
420 w += font[string[i]].w + FONT_X_SPACING;
421 return w;
424 void PrintR(int x, int y, const char * string, ...)
426 va_list marker;
427 va_start( marker, string ); /* Initialize variable arguments. */
429 char tmp[1000];
430 vsprintf((char*)tmp, string, marker);
432 PrintRaw(x-FontWidth(tmp), y, tmp);
434 va_end( marker ); /* Reset variable arguments. */
437 void PrintC(bool split, int x, int y, const char * string, ...)
439 va_list marker;
440 va_start( marker, string ); /* Initialize variable arguments. */
442 char tmp[1000];
443 vsprintf((char*)tmp, string, marker);
445 char* scan = tmp;
446 while (1)
448 char * end = split ? strstr(scan," ") : 0;
449 if (!end)
451 PrintRaw(x - FontWidth(scan)/2, y, scan);
452 break;
454 else
456 *end = '\0';
457 PrintRaw(x - FontWidth(scan)/2, y, scan);
458 scan = end+2;
459 y += FONT_SPACING;
463 va_end( marker ); /* Reset variable arguments. */
467 #include "savestate.h"
468 #include "menus.h"
469 #include "level_list.h"
471 void SaveState::GetStuff()
473 general.hintFlags = HintMessage::flags;
475 void SaveState::ApplyStuff()
477 HintMessage::flags = general.hintFlags;
481 // somewhere else Tile map[][] is assigned to an unsigned char not int32_t
482 // but the data file format expects it to be 32 bit wide!??
483 typedef int32_t Tile;
484 typedef int Dir;
485 struct Pos{
486 int32_t x,y;
487 Pos() : x(0), y(0) {}
488 Pos(int a, int b) : x(a), y(b) {}
489 bool operator == (Pos const & p) const
491 return x==p.x && y==p.y;
493 Pos operator + (Dir const d) const
495 return Pos(
496 x + ((d==1 || d==2) ? 1 : (d==4 || d==5) ? -1 : 0),
497 y + ((d==0 || d==1) ? -1 : (d==3 || d==4) ? 1 : 0)
500 int getScreenX() const {
501 return x*TILE_W2;
503 int getScreenY() const {
504 return x*TILE_H1 + y*TILE_H2;
506 static Pos GetFromWorld(double x, double y)
508 x += TILE_W3/2;
509 y += TILE_H1;
510 int tx, ty;
511 tx = (int)floor(x/TILE_W2);
512 y -= tx*TILE_H1;
513 ty = (int)floor(y/TILE_H2);
515 y -= ty * TILE_H2;
516 x -= tx * TILE_W2;
518 if (x < TILE_W1 && y < TILE_H1)
519 if (x*TILE_H1 + y * TILE_W1 < TILE_H1*TILE_W1)
520 tx--;
521 if (x < TILE_W1 && y > TILE_H1)
522 if (x*TILE_H1 + (TILE_H2-y) * TILE_W1 < TILE_H1*TILE_W1)
523 tx--, ty++;
525 return Pos(tx, ty);
528 Pos mousep(0,0), keyboardp(4,20);
530 class RenderObject;
532 struct RenderStage
534 virtual ~RenderStage() {}
535 virtual void Render(RenderObject* r, double time, bool reflect) = 0;
536 virtual int GetDepth(double /*time*/) { return 1; }
539 class RenderObject
541 RenderStage** stage;
542 double* time;
543 int numStages;
544 int maxStages;
545 int currentStage;
546 public:
547 double seed;
548 double currentTime;
549 private:
551 void Reserve()
553 if (maxStages <= numStages)
555 maxStages = maxStages ? maxStages*2 : 4;
556 stage = (RenderStage**) realloc(stage, sizeof(stage[0])*maxStages);
557 time = (double*) realloc(time, sizeof(time[0])*maxStages);
560 public:
561 RenderObject() : stage(0), time(0), numStages(0), maxStages(0), currentStage(0)
563 // TODO: use a random number with better range
564 // or maybe make seed an int or float...
565 seed = rand() / (double)RAND_MAX;
567 ~RenderObject()
569 free(stage); free(time);
571 bool Active(double t)
573 if (numStages==0) return false;
574 if (t < time[0]) return false;
575 return true;
577 void UpdateCurrent(double t)
579 if (currentStage >= numStages) currentStage = numStages-1;
580 if (currentStage < 0) currentStage = 0;
582 while (currentStage>0 && time[currentStage]>t)
583 currentStage--;
584 while (currentStage<numStages-1 && time[currentStage+1]<=t)
585 currentStage++;
587 currentTime = t;
589 RenderStage* GetStage(double t)
591 if (t==-1 && numStages>0)
592 return stage[numStages-1];
594 if (!Active(t)) return 0;
595 UpdateCurrent(t);
596 return stage[currentStage];
598 double GetLastTime()
600 return numStages>0 ? time[numStages-1] : -1;
602 void Render(double t, bool reflect)
604 if (!Active(t))
605 return;
606 UpdateCurrent(t);
607 stage[currentStage]->Render(this, t - time[currentStage], reflect);
609 int GetDepth(double t)
611 if (!Active(t))
612 return -1;
613 UpdateCurrent(t);
614 return stage[currentStage]->GetDepth(t - time[currentStage]);
616 void Reset(double t)
618 if (t<0)
619 numStages = currentStage = 0;
620 else
622 while (numStages > 0 && time[numStages-1] >= t)
623 numStages--;
626 void Wipe()
628 if (currentStage > 0 && numStages > 0)
630 memmove(&time[0], &time[currentStage], sizeof(time[0]) * numStages-currentStage);
631 memmove(&stage[0], &stage[currentStage], sizeof(stage[0]) * numStages-currentStage);
632 numStages -= currentStage;
633 currentStage = 0;
636 void Add(RenderStage* s, double t)
638 int i=0;
640 if (currentStage<numStages && time[currentStage]<=t)
641 i = currentStage;
643 while (i<numStages && time[i]<t)
644 i++;
646 if (i<numStages && time[i]==t)
647 stage[i]=s;
648 else
650 Reserve();
652 if (i<numStages)
654 memmove(&time[i+1], &time[i], (numStages-i) * sizeof(time[0]));
655 memmove(&stage[i+1], &stage[i], (numStages-i) * sizeof(stage[0]));
658 numStages++;
659 time[i] = t;
660 stage[i] = s;
665 class WorldRenderer
667 #define SIZE 30
668 #define FX 10
669 RenderObject tile[SIZE][SIZE][2];
670 RenderObject fx[FX];
671 int fxPos;
673 public:
674 RenderObject player;
675 RenderObject dummy;
677 WorldRenderer()
679 Reset();
682 void Reset(double t = -1)
684 fxPos = 0;
685 player.Reset(t);
686 dummy.Reset(-1);
688 for (int i=0; i<SIZE; i++)
689 for (int j=0; j<SIZE; j++)
690 for (int q=0; q<2; q++)
691 tile[i][j][q].Reset(t);
693 for (int j=0; j<FX; j++)
694 fx[j].Reset(t);
697 void Wipe()
699 player.Wipe();
700 dummy.Reset(-1);
702 for (int i=0; i<SIZE; i++)
703 for (int j=0; j<SIZE; j++)
704 for (int q=0; q<2; q++)
705 tile[i][j][q].Wipe();
707 for (int j=0; j<FX; j++)
708 fx[j].Wipe();
711 bool Visible(Pos p)
713 int x0 = (scrollX+TILE_W2) / TILE_W2;
714 int x1 = (scrollX+SCREEN_W+TILE_W3+TILE_W1) / TILE_W2;
715 if (p.x<0 || p.y<0 || p.x>=SIZE || p.y>=SIZE) return false;
716 if (p.x<x0) return false;
717 if (p.x>=x1-1) return false;
718 for (int j0=0; j0<SIZE*3; j0++)
720 if (j0 * TILE_H1 < scrollY-TILE_H1) continue;
721 if (j0 * TILE_H1 > scrollY+SCREEN_H+TILE_H1) break;
722 int i = j0&1;
723 int j = j0>>1;
724 j -= (x0-i)/2;
725 i += (x0-i)/2*2;
726 if (j>=SIZE) i+=(j+1-SIZE)*2, j=SIZE-1;
727 for (; i<x1 && j>=0; i+=2, j--)
729 if (Pos(i,j)==p)
730 return true;
733 return false;
736 void Render(double t, bool reflect)
738 dummy.Reset(-1);
740 int playerDepth = player.GetDepth(t);
741 if (reflect) playerDepth-=4;
742 if (playerDepth<0)
743 player.Render(t, reflect);
745 int x0 = (scrollX+TILE_W2) / TILE_W2;
746 int x1 = (scrollX+SCREEN_W+TILE_W3+TILE_W1) / TILE_W2;
747 x0 = MAX(x0, 0);
748 x1 = MIN(x1, SIZE);
749 for (int j0=0; j0<SIZE*3; j0++)
751 if (j0 * TILE_H1 < scrollY-TILE_H1) continue;
752 if (j0 * TILE_H1 > scrollY+SCREEN_H+TILE_H1) break;
753 int i = j0&1;
754 int j = j0>>1;
755 j -= (x0-i)/2;
756 i += (x0-i)/2*2;
757 if (j>=SIZE) i+=(j+1-SIZE)*2, j=SIZE-1;
758 for (; i<x1 && j>=0; i+=2, j--)
760 for (int q=reflect?1:0; q!=2 && q!=-1; q += (reflect ? -1 : 1))
761 if (tile[i][j][q].Active(t))
763 tile[i][j][q].Render(t, reflect);
767 if (playerDepth==j0 || j0==SIZE*3 && playerDepth>j0)
768 player.Render(t, reflect);
771 for (int j=0; j<FX; j++)
772 if(fx[j].Active(t))
774 fx[j].Render(t, reflect);
778 RenderObject & operator () ()
780 fxPos++;
781 if (fxPos==FX) fxPos = 0;
782 return fx[fxPos];
784 RenderObject & operator () (Pos const & p, bool item=false)
786 if (p.x<0 || p.y<0 || p.x>=SIZE || p.y>=SIZE)
787 return dummy;
788 return tile[p.x][p.y][item ? 1 : 0];
792 void RenderTile(bool reflect, int t, int x, int y, int cliplift)
794 SDL_Rect src = tile[reflect][t];
795 SDL_Rect dst = {x-scrollX-GFX_SIZE/2, y-scrollY-GFX_SIZE+TILE_H1, 0, 0};
796 dst.x += tileOffset[reflect][t][0];
797 dst.y += tileOffset[reflect][t][1];
798 if (reflect)
799 dst.y += TILE_H_REFLECT_OFFSET;
800 if (cliplift==-1 || reflect)
802 // dst.w=src.w; dst.h=src.h;
803 // SDL_FillRect(screen, &dst, rand());
804 SDL_BlitSurface(reflect ? tileGraphicsR : tileGraphics, &src, screen, &dst);
806 else
808 src.h -= cliplift;
809 if (src.h > TILE_W1)
811 src.h -= TILE_W1/2;
812 SDL_BlitSurface(tileGraphics, &src, screen, &dst);
813 src.y += src.h;
814 dst.y += src.h;
815 src.h = TILE_W1/2;
817 if (src.h > 0)
819 src.w -= TILE_W1*2, src.x += TILE_W1;
820 dst.x += TILE_W1;
821 SDL_BlitSurface(tileGraphics, &src, screen, &dst);
825 void RenderGirl(bool reflect, int r, int frame, int x, int y, int h)
827 int sx = r * 64;
828 int sy = frame * 80*2;
829 if (reflect)
830 y += TILE_H_REFLECT_OFFSET+20+h, sy += 80;
831 else
832 y -= h;
833 SDL_Rect src = {sx, sy, 64, 80};
834 SDL_Rect dst = {x-scrollX-32, y-scrollY-65, 0, 0};
835 SDL_BlitSurface(girlGraphics, &src, screen, &dst);
838 struct ItemRender : public RenderStage
840 int item;
841 Pos p;
842 int water;
844 ItemRender(int i2, int _water, Pos const & _p) : item(i2), p(_p), water(_water)
847 double Translate(double seed, double time)
849 double bob = time*2 + seed*PI2;
850 return sin(bob)*4;
853 void Render(RenderObject* r, double time, bool reflect)
855 if (item==0)
856 return;
858 int y = -5 + (int)Translate(r->seed, r->currentTime + time);
859 if (reflect)
860 y=-y;
861 if (!reflect && !water)
862 RenderTile( false, TILE_SPHERE, p.getScreenX(), p.getScreenY());
863 RenderTile(
864 reflect,
865 item==1 ? TILE_ITEM1 : TILE_ITEM2,
866 p.getScreenX(), p.getScreenY()+y
871 void RenderFade(double time, int dir, int seed)
873 int ys=0;
874 srand(seed);
875 for(int x=rand()%22-11; x<SCREEN_W+22; x+=32, ys ^= 1)
877 for (int y=ys*20; y<SCREEN_H+30; y+=40)
879 double a = (rand()&0xff)*dir;
880 double b = (time * 0x400 + (y - SCREEN_H) * 0x140/SCREEN_H)*dir;
881 if (a >= b)
883 RenderTile(false, TILE_BLACK_TILE, x+scrollX, y+scrollY);
889 struct FadeRender : public RenderStage
891 int seed;
892 int dir;
893 FadeRender(int d=-1) : seed(rand()), dir(d)
895 isFadeRendering = d;
898 void Render(RenderObject* /*r*/, double time, bool reflect)
900 if (reflect) return;
901 if (time > 0.5)
903 if (dir==1) dir=0, isFadeRendering=0;
904 return;
906 RenderFade(time, dir, seed);
910 struct ScrollRender : public RenderStage
912 int x,y;
913 bool done;
914 ScrollRender(int a,int b) : x(a), y(b), done(false) {}
916 void Render(RenderObject* /*r*/, double /*time*/, bool /*reflect*/)
918 if (done) return;
919 scrollX = x, scrollY = y;
920 isRenderMap = isMap;
921 done = true;
925 struct LevelSelectRender : public RenderStage
927 Pos p;
928 int item;
929 int adj;
930 #ifdef MAP_EDIT_HACKS
931 int magic;
932 #endif
934 LevelSelectRender(Pos const & _p, int i2, int adj) : p(_p), item(i2), adj(adj)
937 void Render(RenderObject* /*r*/, double /*time*/, bool reflect)
939 if (item==0)
940 return;
942 #ifndef MAP_LOCKED_VISIBLE
943 if (item==1) return;
944 #endif
946 if (!reflect && adj)
947 for (int i=0; i<MAX_DIR; i++)
948 if (adj & (1 << i))
949 RenderTile( false, TILE_LINK_0+i, p.getScreenX(), p.getScreenY());
951 if (item < 0)
952 return;
954 if (!reflect)
956 RenderTile(
957 reflect,
958 TILE_SPHERE + item-1,
959 p.getScreenX(), p.getScreenY()
962 #ifdef MAP_EDIT_HACKS
963 int x = p.getScreenX()-scrollX, y = p.getScreenY()-scrollY;
964 Print(x+5,y-25,"%d",magic);
965 #endif
970 struct ItemCollectRender : public ItemRender
972 ItemCollectRender(int i2, Pos const & p) : ItemRender(i2, 0, p)
975 void Render(RenderObject* /*r*/, double /*time*/, bool /*reflect*/)
980 int GetLiftHeight(double time, int t)
982 if (t==LIFT_UP)
983 time = LIFT_TIME-time;
984 time = time / LIFT_TIME;
985 if (time > 1)
986 time = 1;
987 if (time < 0)
988 time = 0;
989 time = (3 - 2*time)*time*time;
990 if (t==LIFT_UP)
991 time = (3 - 2*time)*time*time;
992 if (t==LIFT_UP)
993 return (int)((TILE_H_LIFT_UP+4) * time);
994 else
995 return (int)((TILE_H_LIFT_UP-4) * time) + 4;
998 struct TileRender : public RenderStage
1000 int special;
1001 int t;
1002 Pos p;
1003 double specialDuration;
1005 TileRender(int i, Pos const & _p, int _special=0) : special(_special), t(i), p(_p), specialDuration(LASER_LINE_TIME)
1008 void Render(RenderObject* r, double time, bool reflect)
1010 if (t==0 && special==0)
1011 return;
1013 if (special && (t==LIFT_UP || t==LIFT_DOWN) && time<LIFT_TIME)
1015 int y = GetLiftHeight(time, t);
1016 if (!reflect)
1018 RenderTile(reflect, TILE_LIFT_BACK, p.getScreenX(), p.getScreenY());
1019 RenderTile(reflect, TILE_LIFT_SHAFT, p.getScreenX(), p.getScreenY()+y, y-8);
1020 RenderTile(reflect, TILE_LIFT_FRONT, p.getScreenX(), p.getScreenY());
1022 else
1024 RenderTile(reflect, TILE_LIFT_SHAFT, p.getScreenX(), p.getScreenY()-y, y);
1025 RenderTile(reflect, LIFT_DOWN, p.getScreenX(), p.getScreenY());
1028 else if (special && (t==EMPTY || t==TRAP) && !reflect && time < specialDuration)
1030 if (t == TRAP)
1031 if (time < specialDuration-LASER_FADE_TIME)
1032 RenderTile(reflect, TILE_ICE_LASER_REFRACT, p.getScreenX(), p.getScreenY());
1033 else
1034 RenderTile(reflect, t, p.getScreenX(), p.getScreenY());
1035 int base = ((t==EMPTY) ? TILE_LASER_0 : TILE_LASER_REFRACT);
1036 if (t==EMPTY && time >= specialDuration-LASER_FADE_TIME)
1037 base = TILE_LASER_FADE_0;
1039 int foo=special;
1040 for(int i=0; foo; foo>>=1, i++)
1041 if (foo & 1)
1042 RenderTile(reflect, base+i, p.getScreenX(), p.getScreenY());
1044 else if (t==FLOATING_BALL)
1046 int y = int(1.8 * sin(r->seed*PI + time*4));
1047 if (special==512)
1049 if (time > 2) return;
1050 if (reflect) return;
1051 srand(int(r->seed * 0xfff));
1052 for (int i=0; i<20 - int(time*10); i++)
1054 int x = int((((rand() & 0xfff) - 0x800) / 10) * time);
1055 int y = int((((rand() & 0xfff) - 0x800) / 10) * time);
1056 RenderTile(true, 19 + ((i+int(time*5))&1)*10, p.getScreenX() + x, p.getScreenY() - 14 + y);
1059 if (time < 0.05)
1060 RenderTile(true, 18, p.getScreenX(), p.getScreenY() - 14);
1062 else if (special)
1063 RenderBoat(reflect, int(special)&255, p.getScreenX(), p.getScreenY(), y);
1064 else
1065 RenderTile(reflect, t, p.getScreenX(), p.getScreenY() + (reflect ? -y : y));
1067 else if (t != EMPTY)
1068 RenderTile(reflect, t, p.getScreenX(), p.getScreenY());
1070 static void RenderBoat(bool reflect, int d, int x, int y, int yo)
1072 if (reflect)
1073 RenderGirl(reflect, d, 0, x, y, -yo);
1074 RenderTile(reflect, FLOATING_BALL, x, y+yo);
1075 if (!reflect)
1077 RenderGirl(reflect, d, 0, x, y, -yo);
1078 RenderTile(true, 17, x, y+yo-TILE_H_REFLECT_OFFSET);
1083 struct TileRotateRender : public TileRender
1085 Dir d;
1086 // int range;
1087 int mode;
1088 TileRotateRender(int i, Pos const & p, Dir _d, int m) : TileRender(i, p), d(_d), mode(m)
1090 void Render(RenderObject* r, double time, bool reflect)
1092 if (t==0)
1093 return;
1094 double f = time / ROTATION_TIME;
1096 if (mode & 1) f += 0.5;
1097 if (f<1 && f>0)
1099 if (mode & 2)
1101 else
1102 f = (3-2*f)*f*f;
1105 if (mode & 1) f=1-f; else f=f;
1106 if (f<0) f=0;
1108 if (f >= 1)
1109 TileRender::Render(r, time, reflect);
1110 else
1112 Pos dd = (Pos(0,0)+d);
1113 int x = p.getScreenX() + int(dd.getScreenX()*(f));
1114 int y = p.getScreenY() + int(dd.getScreenY()*(f));
1116 if (mode & 2)
1117 RenderBoat(reflect, (mode&1) ? (d+MAX_DIR/2)%MAX_DIR : d, x, y, 2);
1118 else
1119 RenderTile(reflect, t, x, y);
1124 struct LaserRender : public RenderStage
1126 Pos p;
1127 Dir d;
1128 int range;
1130 LaserRender(Pos _p, int dir, int r) : p(_p), d(dir), range(r)
1133 void Render(RenderObject* /*r*/, double /*time*/)
1138 struct ExplosionRender : public RenderStage
1140 Pos p;
1141 int seed;
1142 int power;
1143 int type;
1145 ExplosionRender(Pos _p, int _pow=0, int t=0) : p(_p), power(_pow), type(t)
1147 seed = rand();
1150 virtual int GetDepth(double /*time*/)
1152 return p.x + p.y*2;
1155 void Render(RenderObject* /*r*/, double time, bool reflect)
1157 if (type==1 && time > 2.5)
1158 type = -1, new WinLoseScreen(false);
1160 // if (reflect) return;
1161 if (time > 3) return;
1162 srand(seed);
1163 int q = 50 - int(time * 35);
1164 if (power) q*=2;
1165 if (type) q = 50;
1166 for (int i=0; i<q; i++)
1168 int x = p.getScreenX();
1169 int y = p.getScreenY() + (rand() & 31)-16;
1170 int xs = ((rand() & 63) - 32);
1171 int ys = (-10 - (rand() & 127)) * (1+power);
1172 if (type) ys*=2, xs/=2;
1173 x += int(xs * (1+time*(2+power)));
1174 int yo = int(time*time*128 + ys*time);
1175 //if (yo > 0) yo=-yo;//continue;
1176 if (type)
1179 if (yo > 0)
1181 if (!reflect && ys<-60)
1183 const double T = 0.06;
1184 double ct = -ys / 128.0;
1185 if (time < ct+T*4)
1187 x = p.getScreenX() + int(xs * (1+ct*(2+power)));
1188 RenderTile(
1189 reflect,
1190 time > ct+3*T ? TILE_SPLASH_3 : time > ct+2*T ? TILE_SPLASH_2 : time > ct+T ? TILE_SPLASH_1 : TILE_WATER_PARTICLE+1,
1191 x, y);
1195 else
1196 RenderTile(
1197 reflect,
1198 time - i*0.003 < 0.2 ? TILE_WATER_PARTICLE+1 : TILE_WATER_PARTICLE,
1199 x, y+(reflect?-1:1)*yo);
1201 else
1203 if (yo > 0)
1205 else
1206 RenderTile(
1207 reflect,
1208 i<q-20 || time<0.3 ? TILE_LASER_HEAD : i<q-10 || time<0.6 ? TILE_FIRE_PARTICLE_1 : TILE_FIRE_PARTICLE_2,
1209 x, y+(reflect?-1:1)*yo);
1214 struct DisintegrateRender : public RenderStage
1216 Pos p;
1217 int seed;
1218 int height;
1219 int type;
1221 DisintegrateRender(Pos _p, int _pow=0, int _t=0) : p(_p), height(_pow), type(_t)
1223 seed = rand();
1226 void Render(RenderObject* /*r*/, double time, bool reflect)
1228 if (type)
1229 RenderTile(reflect, height ? COLLAPSE_DOOR : COLLAPSABLE, p.getScreenX(), p.getScreenY());
1231 if (time > 50.0/70.0) return;
1232 if (reflect) return;
1233 srand(seed);
1234 int q = 50 - int(time * 70);
1235 if (height) q*=2;
1236 for (int i=0; i<q; i++)
1238 int x = (rand() % (TILE_W3-8))-TILE_W3/2+4;
1239 int y = (rand() % (TILE_H2-8))-TILE_H1+4;
1240 if (x<-TILE_WL/2 && ABS(y)<-TILE_WL/2-x) continue;
1241 if (x>TILE_WL/2 && ABS(y)>x-TILE_WL/2) continue;
1242 int yo=0;
1243 if (height) yo -= rand() % TILE_HUP;
1244 x += p.getScreenX();
1245 y += p.getScreenY() + 4;
1246 int xs = 0;//((rand() & 63) - 32);
1247 int ys = (- (rand() & 31));
1248 x += int(xs * (1+time*(2)));
1249 if (type) yo = -yo;
1250 yo += int(time*time*128 + ys*time);
1251 if (type) yo = -yo*2;
1252 //if (yo > 0) yo=-yo;//continue;
1253 int t = type ? TILE_BLUE_FRAGMENT : TILE_GREEN_FRAGMENT;
1254 if (i>q-20) t++;
1255 if (i>q-10) t++;
1256 if (yo > 5) yo = 5;
1257 RenderTile(false, t, x, y+(reflect?-yo:yo));
1261 struct BuildRender : public RenderStage
1263 Pos p;
1264 Dir dir;
1265 int reverse;
1266 int height;
1267 int type;
1269 BuildRender(Pos _p, Dir _d, int _h, int _r=0, int _type=0) : p(_p), dir(_d), reverse(_r), height(_h), type(_type)
1273 void Render(RenderObject* /*r*/, double time, bool reflect)
1275 if (time >= BUILD_TIME)
1276 RenderTile(reflect, height ^ reverse ? (type ? COLLAPSE_DOOR2 : COLLAPSE_DOOR) : (type ? COLLAPSABLE2 : COLLAPSABLE), p.getScreenX(), p.getScreenY());
1277 else
1279 if (height)
1280 RenderTile(reflect, type ? COLLAPSABLE2 : COLLAPSABLE, p.getScreenX(), p.getScreenY());
1282 double dist = time * 2 / BUILD_TIME;
1283 if (dir>-1)
1285 Pos from = p + ((dir+MAX_DIR/2)%MAX_DIR);
1286 if (dist <= 1)
1287 //if (dist > 1)
1289 double offset = (dist*0.7) + 0.3;
1290 int x = from.getScreenX() + int((p.getScreenX()-from.getScreenX()) * offset);
1291 int y = from.getScreenY() + int((p.getScreenY()-from.getScreenY()) * offset - dist*(1-dist)*(TILE_HUP*4));
1292 RenderTile(reflect, TILE_GREEN_FRAGMENT, x, y);
1294 dist -= 1;
1296 else
1298 if (reverse) dist = 1-dist;
1300 if (dist > 0 && !height)
1302 if (!reflect)
1303 for (int i=0; i<=int(dist*15); i++)
1305 int x = p.getScreenX(), y = p.getScreenY();
1306 double d = (i + fmod(dist*15, 1))/10.0;
1307 int x1 = int(sin(d*5+time)*MIN(d,1)*TILE_W2/2);
1308 int y1 = int(cos(d*5+time)*MIN(d,1)*TILE_H1*0.7);
1309 RenderTile(reflect, TILE_GREEN_FRAGMENT, x+x1, y+y1+4);
1310 RenderTile(reflect, TILE_GREEN_FRAGMENT, x-x1, y-y1+4);
1313 if (dist > 0 && height)
1315 int yo = int((1-dist)*(TILE_HUP*1.3));
1316 if (yo > TILE_HUP*1.1)
1317 RenderTile(reflect, TILE_WHITE_TILE, p.getScreenX(), p.getScreenY());
1318 else if (!reflect)
1320 RenderTile(reflect, type ? COLLAPSABLE2 : COLLAPSABLE, p.getScreenX(), p.getScreenY());
1321 RenderTile(reflect, type ? COLLAPSE_DOOR2 : COLLAPSE_DOOR, p.getScreenX(), p.getScreenY()+(reflect ? -yo : yo), yo+6);
1322 RenderTile(reflect, type ? TILE_BLUE_FRONT : TILE_GREEN_FRONT, p.getScreenX(), p.getScreenY());
1324 else
1326 if (yo < TILE_HUP/2)
1328 RenderTile(reflect, type ? COLLAPSE_DOOR2 : COLLAPSE_DOOR, p.getScreenX(), p.getScreenY()+(reflect ? -yo : yo), yo);
1331 RenderTile(reflect, type ? COLLAPSABLE2 : COLLAPSABLE, p.getScreenX(), p.getScreenY());
1339 struct PlayerRender : public RenderStage
1341 Pos p;
1342 Pos target;
1343 int p_h, target_h;
1344 int r;
1345 int type;
1346 double speed;
1347 bool dead;
1349 PlayerRender(Pos a, int h, bool d) : p(a), target(a), p_h(h), target_h(h), r(3), type(0), speed(0), dead(d)
1351 PlayerRender(int _r, Pos a, int h1, Pos t, int h2, bool d) : p(a), target(t), p_h(h1), target_h(h2), r(_r), type(0), speed(JUMP_TIME), dead(d)
1353 int dist = MAX(ABS(p.x-target.x), ABS(p.y-target.y));
1354 if (dist > 1)
1355 speed *= 1.5;
1356 if(dist==0)
1357 speed = 0;
1360 virtual int GetDepth(double time)
1362 double f = speed ? time / speed : 1;
1363 if (f>1) f=1;
1364 if (f==1) dead = this->dead;
1366 if (f==1 || f>0.5 && p_h>target_h)
1367 return target.x+target.y*2;
1368 return MAX(target.x+target.y*2 , p.x+p.y*2);
1371 void Render(RenderObject* /*ro*/, double time, bool reflect)
1373 bool dead = false;
1374 double f = speed ? time / speed : 1;
1375 if (f>1) f=1;
1376 if (f==1) dead = this->dead;
1378 int x = p.getScreenX();
1379 int y = p.getScreenY();
1380 int x2 = target.getScreenX();
1381 int y2 = target.getScreenY();
1382 int h = 0;
1383 int shadow_h = (int)((p_h+(target_h-p_h)*f)*TILE_HUP2);
1385 if (x==x2 && y==y2 && p_h!=target_h)
1387 h = TILE_H_LIFT_UP - GetLiftHeight(time, p_h ? LIFT_DOWN : LIFT_UP);
1389 else
1392 int dist = MAX(ABS(p.x-target.x), ABS(p.y-target.y));
1393 int arc = dist*dist;
1394 int h1 = p_h * TILE_HUP2;;
1395 int h2 = target_h * TILE_HUP2;
1396 if (dist==2 && h1!=0)
1398 arc += h2 ? 1 : 3;
1399 h1 = 0;
1400 shadow_h = f>=0.7 ? int(shadow_h*(f-0.7)/0.3) : 0;
1402 if (dist==0)
1403 arc = speed > JUMP_TIME ? 7 : 2;
1405 h = (int)(h1+(h2-h1)*f);
1406 // if (x==x2 && y==y2)
1407 // ;
1408 // else
1410 //h += int(TILE_H_LIFT_UP/3 * (1-f));
1411 h += (int)(f*(1-f)*TILE_HUP2*arc);
1414 if (type==2)
1415 h=0;
1418 if (!dead)
1420 int frame = 0;
1421 if (type==2 && f<1)
1423 //frame = ((int)(f*4) % 4);
1424 //if (frame==2) frame=0; else if (frame==3) frame=2;
1425 frame = 0;
1427 else if (f==1 || x==x2 && y==y2) // stationary
1428 frame = 0;
1429 else if (f > 0.7)
1430 frame = 0;
1431 else
1433 frame = type ? 2 : 1;
1434 if (f<0.1 || f>0.6)
1435 frame += 2;
1438 if (!reflect)
1439 RenderTile( false, TILE_SPHERE,
1440 (int)(x+(x2-x)*f),
1441 (int)(y+(y2-y)*f) - shadow_h
1444 RenderGirl(
1445 reflect,
1446 r, frame,
1447 (int)(x+(x2-x)*f),
1448 (int)(y+(y2-y)*f),
1453 /* RenderTile(
1454 dead ? TILE_SPHERE_OPEN : TILE_SPHERE_DONE,
1455 (int)(x+(x2-x)*f),
1456 (int)(y+(y2-y)*f),
1457 true
1458 );*/
1463 struct HexPuzzle : public State
1465 struct Undo
1467 #define MAX_TILECHANGE 64 // TODO: don't have a magic upper limit
1468 struct TileChange
1470 Pos p;
1471 Tile t;
1472 int item;
1474 TileChange()
1476 TileChange(Pos _p, Tile _t, int _i) : p(_p), t(_t), item(_i)
1478 void Restore(HexPuzzle* w)
1480 w->SetTile(p,t,false,false);
1481 w->SetItem(p,item,false,false);
1485 TileChange t[MAX_TILECHANGE];
1486 Pos playerPos;
1487 Dir playerMovement;
1488 int numT;
1489 int numItems[2];
1490 int score;
1491 double time;
1492 double endTime;
1494 void Add(TileChange const & tc)
1496 for (int i=0; i<numT; i++)
1497 if (t[i].p==tc.p)
1498 return;
1499 if (numT>=MAX_TILECHANGE)
1500 FATAL("numT>=MAX_TILECHANGE");
1501 else
1502 t[numT++] = tc;
1504 void New(Dir pmove, Pos & pp, int* items, double t, int sc)
1506 numItems[0] = items[0];
1507 numItems[1] = items[1];
1508 playerPos = pp;
1509 playerMovement = pmove;
1510 score = sc;
1511 time = t;
1512 numT = 0;
1514 void Restore(HexPuzzle* w)
1516 for (int i=numT-1; i>=0; i--)
1517 t[i].Restore(w);
1518 w->dead = false;
1519 w->win = false;
1520 w->player = playerPos;
1521 w->player_items[0] = numItems[0];
1522 w->player_items[1] = numItems[1];
1523 w->player_score = score;
1525 //w->renderer.player.Add(new PlayerRender(playerPos, w->GetHeight(playerPos), false), w->time);
1529 #define MAP_SIZE 30
1530 char* special[MAP_SIZE][MAP_SIZE];
1531 Tile map[MAP_SIZE][MAP_SIZE];
1532 int32_t map_item[MAP_SIZE][MAP_SIZE];
1533 int tileCount[NumTileTypes];
1534 int32_t levelPar, levelDiff;
1535 int turboAnim;
1536 Pos player;
1537 int player_items[2];
1538 int player_score;
1539 int numComplete, numLevels, numMastered, numLevelsFound;
1540 bool dead;
1541 bool win;
1542 int winFinal;
1544 SaveState progress;
1546 WorldRenderer renderer;
1547 double time;
1548 double undoTime;
1550 #define MAX_UNDO 6
1551 Undo undo[MAX_UNDO];
1552 int numUndo;
1553 LevelInfo* currentLevelInfo;
1555 char currentFile[1000];
1557 ~HexPuzzle()
1559 FreeGraphics();
1562 LevelInfo* GetLevelInfo(const char* f)
1564 if (strstr(f, "Levels\\") == f)
1565 f += 7;
1566 if (currentLevelInfo!=0 && strcmp(currentLevelInfo->file, f)==0)
1567 return currentLevelInfo;
1569 if (f[0]=='_')
1571 int t = atoi(f+1);
1572 if (t <= numComplete)
1573 return 0;
1575 static char tmp1[100];
1576 static LevelInfo tmp = {0, "", tmp1};
1577 sprintf(tmp1, "Complete %d more %s to unlock!", t-numComplete, t-numComplete==1?"level":"levels");
1578 return &tmp;
1581 for (unsigned int i=0; i<sizeof(levelNames)/sizeof(levelNames[0]); i++)
1582 if (strcmp(f, levelNames[i].file)==0)
1583 return &levelNames[i];
1584 static LevelInfo tmp = {0, "", "<<NO NAME>>"};
1585 return &tmp;
1588 #ifdef MAP_EDIT_HACKS
1589 int GetAutoTile(const char * level, bool tiletype)
1591 FILE* f = file_open(filename, "rb");
1592 int tile = EMPTY;
1593 int version;
1595 if (f && fscanf(f, "%d", &version)==1 && (version==3 || version==4))
1597 if (strstr(level,"mk"))
1598 level+=0;
1600 fgetc(f); // Remove '\n' character
1602 int32_t par, diff;
1603 unsigned char bounds[4];
1604 Pos playerStart;
1605 fread(&par, sizeof(par), 1, f);
1606 par = SWAP32(par);
1608 if (version >= 4) {
1609 fread(&diff, sizeof(diff), 1, f);
1610 diff = SWAP32(diff);
1612 fread(bounds, sizeof(bounds), 1, f);
1613 fread(&playerStart, sizeof(playerStart), 1, f);
1614 playerStart.x = SWAP32(playerStart.x);
1615 playerStart.y = SWAP32(playerStart.y);
1617 int highval=0;
1619 for (int i=bounds[0]; i<=bounds[1]; i++)
1620 for (int j=bounds[2]; j<=bounds[3]; j++)
1622 unsigned char comp = map[i][j] | (map_item[i][j]<<5);
1623 fread(&comp, sizeof(comp), 1, f);
1624 int t = comp & 0x1f;
1625 int item = (comp >> 5) & 3;
1626 for (int i=highval+1; i<sizeof(value_order)/sizeof(value_order[0]); i++)
1627 if (t!=0 && t==value_order[i]
1628 || item!=0 && item==(value_order[i]>>8))
1629 highval = i;
1632 if (tiletype)
1634 tile = value_order[highval];
1635 if (tile==0x100) tile = COLLAPSABLE3;
1636 if (tile==0x200) tile = SWITCH;
1637 if (tile==LIFT_UP) tile = LIFT_DOWN;
1639 else
1641 if (value_order[highval] == LIFT_UP)
1642 tile = highval-1;
1643 else
1644 tile = highval;
1647 else
1649 level+=0;
1651 if (f)
1652 fclose(f);
1653 return tile;
1655 #endif
1657 void InitSpecials()
1659 numComplete = numLevels = numMastered = numLevelsFound = 0;
1660 for (int i=0; i<MAP_SIZE; i++)
1661 for (int j=0; j<MAP_SIZE; j++)
1662 ActivateSpecial(Pos(i,j), 0);
1663 for (int i=0; i<MAP_SIZE; i++)
1664 for (int j=0; j<MAP_SIZE; j++)
1665 ActivateSpecial(Pos(i,j), 2);
1666 numComplete = numLevels = numMastered = numLevelsFound = 0;
1667 for (int i=0; i<MAP_SIZE; i++)
1668 for (int j=0; j<MAP_SIZE; j++)
1669 ActivateSpecial(Pos(i,j), 0);
1672 void DoHints()
1674 #ifndef EDIT
1675 if (strcmp(mapname, currentFile)==0)
1677 // for (int i=0; i<32; i++)
1678 // HintMessage::FlagTile(i);
1679 if (numComplete >= UNLOCK_SCORING && !progress.general.scoringOn)
1681 HintMessage::FlagTile(26);
1682 progress.general.scoringOn = 1;
1683 InitSpecials(); // Re-initialise with gold ones available
1685 HintMessage::FlagTile(25);
1687 else
1689 for (int i=0; i<MAP_SIZE; i++)
1690 for (int j=0; j<MAP_SIZE; j++)
1692 int t = GetTile(Pos(i,j));
1693 int item = GetItem(Pos(i,j));
1694 if (t)
1695 HintMessage::FlagTile(t);
1696 if (item)
1697 HintMessage::FlagTile(item+20);
1699 HintMessage::FlagTile(EMPTY);
1701 #endif
1702 hintsDone = true;
1704 void ResetLevel()
1706 hintsDone = false;
1708 UpdateCursor(Pos(-1,-1));
1710 isMap = false;
1712 player_score = 0;
1714 numUndo = 0;
1715 undoTime = -1;
1717 dead = false;
1718 win = false;
1719 winFinal = false;
1720 player_items[0] = player_items[1] = 0;
1721 // time = 0;
1722 if (strlen(currentSlot) == 0)
1724 new TitleMenu();
1725 new Fader(1, -3);
1727 else
1729 if (!isFadeRendering && time!=0)
1731 renderer().Add(new FadeRender(-1), time);
1732 time += 0.5;
1736 // Reset renderer
1737 renderer.Reset(time);
1738 renderer.Wipe();
1740 for (int t=0; t<NumTileTypes; t++)
1741 tileCount[t] = 0;
1743 for (int i=0; i<MAP_SIZE; i++)
1744 for (int j=0; j<MAP_SIZE; j++)
1746 Pos p(i,j);
1747 int item = GetItem(p);
1748 //if (item)
1749 renderer(p,true).Add(new ItemRender(item, GetTile(p)==EMPTY, p), time);
1752 InitSpecials();
1754 for (int i=0; i<MAP_SIZE; i++)
1755 for (int j=0; j<MAP_SIZE; j++)
1757 Pos p(i,j);
1758 int t = GetTile(p);
1759 tileCount[t]++;
1761 if (isMap)
1762 t = EMPTY;
1764 //if (t)
1765 renderer(p).Add(new TileRender(t, p), time);
1768 if (!isMap)
1769 renderer.player.Add(new PlayerRender(player, GetHeight(player), dead), time);
1770 else
1771 renderer.player.Add(new PlayerRender(Pos(-100,-100), 0, true), time);
1774 int bounds[4] = {player.getScreenX(),player.getScreenX(),player.getScreenY(),player.getScreenY()};
1775 for (int i=0; i<MAP_SIZE; i++)
1776 for (int j=0; j<MAP_SIZE; j++)
1778 Pos p(i,j);
1779 if (map[i][j] !=0 || map_item[i][j]!=0)
1781 int x1 = p.getScreenX();
1782 int y1 = p.getScreenY();
1783 int x2 = x1 + TILE_W3;
1784 int y2 = y1 + TILE_H2;
1785 y1 -= TILE_H2; // Make sure objects/player will be properly visible
1787 if (x1<bounds[0]) bounds[0] = x1;
1788 if (x2>bounds[1]) bounds[1] = x2;
1789 if (y1<bounds[2]) bounds[2] = y1;
1790 if (y2>bounds[3]) bounds[3] = y2;
1794 int sx, sy;
1795 if (isMap)
1797 sx = bounds[0] - int(TILE_W2*6.35);
1798 sy = (bounds[3] + bounds[2] - SCREEN_H) / 2 - TILE_H2/2;
1800 else
1802 sx = (bounds[1] + bounds[0] - SCREEN_W) / 2 - TILE_W3/2;
1803 sy = (bounds[3] + bounds[2] - SCREEN_H) / 2 - TILE_H2/2;
1805 if (isMap)
1807 initScrollX = sx;
1808 initScrollY = sy;
1809 if (mapScrollX==0)
1810 mapScrollX = sx;
1811 else
1812 sx = mapScrollX;
1815 // time = 1; // Guarantee we can't try and do things at time=0
1817 renderer().Add(new ScrollRender(sx, sy), time);
1818 renderer().Add(new FadeRender(1), time);
1819 if (time != 0)
1820 time -= 0.5;
1823 char* ReadAll(FILE* f)
1825 int size;
1826 fseek(f, 0, SEEK_END);
1827 size = ftell(f);
1828 fseek(f, 0, SEEK_SET);
1829 char* c = loadPtr = new char [size];
1830 endLoad = loadPtr + size;
1831 fread(c, 1, size, f);
1832 return c;
1835 static char *loadPtr, *endLoad;
1836 static unsigned int fread_replace(void* d, unsigned int size, unsigned int num, FILE*)
1838 unsigned int remain = (endLoad - loadPtr) / size;
1839 if (remain < num) num = remain;
1840 memcpy(d, loadPtr, size*num);
1841 loadPtr += size*num;
1842 return num;
1845 int GetPar(const char * level, bool getdiff=false)
1847 if (strcmp(level, currentFile)==0)
1848 return getdiff ? levelDiff : levelPar;
1850 #ifdef USE_LEVEL_PACKFILE
1851 PackFile1::Entry* e = levelFiles.Find(level);
1852 if (!e) return 999;
1853 loadPtr = (char*)e->Data();
1854 endLoad = loadPtr + e->DataLen();
1855 FILE* f = 0;
1856 #else
1857 loadPtr = 0;
1858 FILE* f = file_open(level, "rb");
1859 #endif
1861 typedef unsigned int _fn(void*, unsigned int, unsigned int, FILE*);
1862 _fn * fn = (loadPtr ? (_fn*)fread_replace : (_fn*)fread);
1864 int32_t par = 99999, diff = 0;
1865 int16_t version;
1867 if (!f && !loadPtr)
1868 return getdiff ? diff : par;
1870 fn(&version, 2, 1, f); // skip to relevant point
1872 if (fn(&par, sizeof(par), 1, f) != 1)
1873 par = 99999;
1874 else
1875 par = SWAP32(par);
1876 size_t ret = fn(&diff, sizeof(diff), 1, f);
1877 diff = SWAP32(diff);
1878 if (ret != 1 || diff<0 || diff>10)
1879 diff = 0;
1881 #ifdef USE_LEVEL_PACKFILE
1882 loadPtr = endLoad = 0;
1883 #else
1884 if (f)
1885 fclose(f);
1886 #endif
1888 return getdiff ? diff : par;
1891 bool LoadSave(const char * filename, bool save)
1893 if (!filename)
1894 return false;
1896 if (!save)
1898 showScoring = false;
1899 LevelSave* l = progress.GetLevel(filename, true);
1900 if (progress.general.scoringOn && l && l->Completed() )
1901 showScoring = true;
1904 #ifdef USE_LEVEL_PACKFILE
1905 if (!save)
1907 PackFile1::Entry* e = levelFiles.Find(filename);
1908 if (!e) return false;
1910 strcpy(currentFile, filename);
1911 currentLevelInfo = GetLevelInfo(currentFile);
1913 loadPtr = (char*)e->Data();
1914 endLoad = loadPtr + e->DataLen();
1915 _LoadSave(NULL, save);
1916 loadPtr = endLoad = 0;
1918 return true;
1920 #else
1921 loadPtr = 0;
1922 FILE* f = file_open(filename, save ? "wb" : "rb");
1923 if (f)
1925 strcpy(currentFile, filename);
1926 if (!save)
1927 currentLevelInfo = GetLevelInfo(currentFile);
1929 if (!save)
1931 char* data = ReadAll(f);
1932 _LoadSave(f, save);
1933 delete [] data;
1934 loadPtr = endLoad = 0;
1936 else
1938 _LoadSave(f, save);
1940 fclose(f);
1942 return true;
1944 #endif
1946 return false;
1949 /** \brief Writes/reads game status to/from a file
1951 * The game data file is written in little endian so it can be shared
1952 * across different machines.
1954 void _LoadSave(FILE* f, bool save)
1956 typedef unsigned int _fn(void*, unsigned int, unsigned int, FILE*);
1957 _fn * fn = save ? (_fn*)fwrite : (loadPtr ? (_fn*)fread_replace : (_fn*)fread);
1959 #define VERSION 4
1960 int version = VERSION; // 1--9
1961 if (save)
1962 fprintf(f, "%d\n", version);
1963 else
1965 char c;
1966 if (fn(&c, 1, 1, f) != 1)
1967 return;
1968 version = c-'0';
1970 // Remove '\n' character
1971 fn(&c, 1, 1, f);
1974 if (!save)
1976 for (int i=0; i<MAP_SIZE; i++)
1977 for (int j=0; j<MAP_SIZE; j++)
1979 delete [] special[i][j];
1980 special[i][j] = 0;
1984 if (version==1)
1986 for (int i=0; i<MAP_SIZE; i++)
1987 for (int j=0; j<MAP_SIZE; j++) {
1988 map[i][j] = SWAP32(map[i][j]);
1989 fn(&map[i][j], sizeof(map[i][j]), 1, f);
1990 map[i][j] = SWAP32(map[i][j]);
1993 player.x = SWAP32(player.x);
1994 player.y = SWAP32(player.y);
1995 fn(&player, sizeof(player), 1, f);
1996 player.x = SWAP32(player.x);
1997 player.y = SWAP32(player.y);
1999 for (int i=0; i<MAP_SIZE; ++i)
2000 for (int j=0; j<MAP_SIZE; ++j)
2001 map_item[i][j] = SWAP32(map_item[i][j]);
2002 if (fn(map_item, sizeof(map_item), 1, f) == 0)
2003 memset(map_item, 0, sizeof(map_item));
2004 for (int i=0; i<MAP_SIZE; ++i)
2005 for (int j=0; j<MAP_SIZE; ++j)
2006 map_item[i][j] = SWAP32(map_item[i][j]);
2008 else if (version>=2 && version<=4)
2010 unsigned char bounds[4];
2011 if (save)
2013 bounds[0]=bounds[1]=player.x;
2014 bounds[2]=bounds[3]=player.y;
2015 for (int i=0; i<MAP_SIZE; i++)
2016 for (int j=0; j<MAP_SIZE; j++)
2017 if (map[i][j] !=0 || map_item[i][j]!=0 || special[i][j]!=0)
2019 if (i<bounds[0]) bounds[0] = i;
2020 if (i>bounds[1]) bounds[1] = i;
2021 if (j<bounds[2]) bounds[2] = j;
2022 if (j>bounds[3]) bounds[3] = j;
2025 else
2027 memset(map, 0, sizeof(map));
2028 memset(map_item, 0, sizeof(map_item));
2031 if (version>=3) {
2032 levelPar = SWAP32(levelPar);
2033 fn(&levelPar, 1, sizeof(levelPar), f);
2034 levelPar = SWAP32(levelPar);
2036 else if (!save)
2037 levelPar = 0;
2039 if (version>=4) {
2040 levelDiff = SWAP32(levelDiff);
2041 fn(&levelDiff, 1, sizeof(levelDiff), f);
2042 levelDiff = SWAP32(levelDiff);
2044 else if (!save)
2045 levelDiff = 0;
2047 fn(bounds, sizeof(bounds), 1, f);
2048 player.x = SWAP32(player.x);
2049 player.y = SWAP32(player.y);
2050 fn(&player, sizeof(player), 1, f);
2051 player.x = SWAP32(player.x);
2052 player.y = SWAP32(player.y);
2054 int offsetx=0, offsety=0;
2056 if (!save && bounds[1]-bounds[0]<15) // Hacky - don't recenter map...
2058 // Re-position map to top left (but leave a bit of space)
2059 // (This ensures the laser/boat effects don't clip prematurely against the edges of the screen)
2060 offsetx = SCREEN_W/2/TILE_W2 + 1 - (bounds[0]+bounds[1]/2);
2061 offsety = SCREEN_H/2/TILE_H2 + SCREEN_W/2/TILE_W2 - (bounds[2]+bounds[3]/2);
2062 offsetx = MAX(0, offsetx);
2063 offsety = MAX(0, offsety);
2064 // if (bounds[0] > 2)
2065 // offsetx = 2 - bounds[0];
2066 // if (bounds[2] > 2)
2067 // offsety = 2 - bounds[2];
2069 bounds[0] += offsetx;
2070 bounds[1] += offsetx;
2071 bounds[2] += offsety;
2072 bounds[3] += offsety;
2073 player.x += offsetx;
2074 player.y += offsety;
2076 for (int i=bounds[0]; i<=bounds[1]; i++)
2077 for (int j=bounds[2]; j<=bounds[3]; j++)
2079 unsigned char comp = map[i][j] | (map_item[i][j]<<5);
2080 fn(&comp, sizeof(comp), 1, f);
2081 map[i][j] = comp & 0x1f;
2082 map_item[i][j] = (comp >> 5) & 3;
2085 if (save)
2087 for (int i=bounds[0]; i<=bounds[1]; i++)
2088 for (int j=bounds[2]; j<=bounds[3]; j++)
2089 if (special[i][j])
2091 int16_t len = strlen(special[i][j]);
2092 unsigned char x=i, y=j;
2093 fn(&x, sizeof(x), 1, f);
2094 fn(&y, sizeof(y), 1, f);
2095 len = SWAP16(len);
2096 fn(&len, sizeof(len), 1, f);
2097 len = SWAP16(len);
2098 fn(special[i][j], 1, len, f);
2101 else
2103 while(1){
2104 int16_t len;
2105 unsigned char x, y;
2106 if (!fn(&x, sizeof(x), 1, f))
2107 break;
2108 fn(&y, sizeof(y), 1, f);
2109 x += offsetx; y += offsety;
2110 fn(&len, sizeof(len), 1, f);
2111 len = SWAP16(len);
2112 if (len<0) break;
2113 char* tmp = new char[len+1];
2114 tmp[len] = 0;
2115 fn(tmp, 1, len, f);
2117 SetSpecial(Pos(x,y), tmp, true, false);
2121 else
2122 return; // Unsupported version!
2124 ResetLevel();
2126 // Save when returning to map!
2127 if (isMap)
2129 progress.general.completionPercentage = numComplete*100/numLevels;
2130 progress.general.masteredPercentage = numMastered*100/numLevels;
2131 LoadSaveProgress(true);
2135 void SetTile(Pos const & p, Tile t, bool updateRenderer=true, bool undoBuffer=true)
2137 if (p.x<0 || p.x>MAP_SIZE)
2138 return;
2139 if (p.y<0 || p.y>MAP_SIZE)
2140 return;
2141 if (map[p.x][p.y] == t)
2142 return;
2143 if (map[p.x][p.y] == t)
2144 return;
2146 tileCount[map[p.x][p.y]]--;
2147 tileCount[t]++;
2149 if (undoBuffer)
2150 undo[numUndo].Add(Undo::TileChange(p,GetTile(p),GetItem(p)));
2152 map[p.x][p.y] = t;
2154 if (updateRenderer)
2155 renderer(p).Add(new TileRender(t, p), time);
2158 Tile GetTile(Pos const & p)
2160 if (p.x<0 || p.x>=MAP_SIZE)
2161 return EMPTY;
2162 if (p.y<0 || p.y>=MAP_SIZE)
2163 return EMPTY;
2164 return map[p.x][p.y];
2167 int GetHeight(Pos const & p)
2169 return tileSolid[GetTile(p)]==1;
2172 char* GetSpecial(Pos const & p)
2174 if (p.x<0 || p.x>=MAP_SIZE)
2175 return NULL;
2176 if (p.y<0 || p.y>=MAP_SIZE)
2177 return NULL;
2178 return special[p.x][p.y];
2181 void SetSpecial(Pos const & p, char * d, bool use_pointer=false, bool auto_activate=true)
2183 if (p.x<0 || p.x>=MAP_SIZE || p.y<0 || p.y>=MAP_SIZE)
2185 if (use_pointer)
2186 delete [] d;
2187 return;
2190 delete [] special[p.x][p.y];
2191 if (!use_pointer && d)
2194 special[p.x][p.y] = new char [strlen(d) + 1];
2195 strcpy(special[p.x][p.y], d);
2197 else
2198 special[p.x][p.y] = d;
2200 if (special[p.x][p.y]==0)
2201 renderer(p,true).Add(new ItemRender(GetItem(p), GetTile(p)==EMPTY, p), time);
2202 else if (auto_activate)
2203 ActivateSpecial(p, 0);
2206 int GetLevelState(Pos const & p, int recurse=0)
2208 char* x = GetSpecial(p);
2209 if (!x) return 0;
2211 LevelSave* l = progress.GetLevel(x, false);
2213 int t = 1;
2215 if (strcmp(x, STARTING_LEVEL)==0)
2216 t = 2;
2217 if (x[0]=='_' && l && l->unlocked)
2218 t=3;
2220 if (l && l->Completed())
2222 t = 3;
2224 if (recurse)
2225 return t;
2227 int par = GetPar(x);
2228 if (progress.general.scoringOn && l->PassesPar( par ))
2229 t = 4;
2231 if (recurse)
2232 return t;
2234 int adj=0;
2235 for (Dir d=0; d<MAX_DIR; d++)
2237 int i = GetLevelState(p+d, 1);
2238 // if (i>1 || i==1 && t>1)
2239 if (i>=1 && t>2 || t>=1 && i>2)
2241 adj |= 1<<d;
2242 if (t==1)
2243 t = 2;
2247 return t | adj<<8;
2250 void ActivateSpecial(Pos const & p, int type)
2252 if (p.x<0 || p.x>=MAP_SIZE || p.y<0 || p.y>=MAP_SIZE)
2253 return;
2255 char * x = special[p.x][p.y];
2257 if (x==0 || x[0]==0)
2258 return;
2260 if (type==2 && x[0]=='_') // Phase2 init - unlock
2262 int t = GetLevelState(p);
2263 int target = atoi(x+1), targetM = 0;
2264 if (target>1000) targetM=target=target-100;
2265 if (t > 1 && numComplete >= target && numMastered >= targetM)
2267 LevelSave* l = progress.GetLevel(x, true);
2268 if (!l->unlocked)
2270 l->unlocked = true;
2272 renderer(p, true).Add(new LevelSelectRender(p, 5, GetLevelState(p)>>8), time+0.01);
2273 renderer().Add(new ExplosionRender(p, 0), time + 0.6);
2274 renderer().Add(new ExplosionRender(p, 1), time + 1.1);
2275 renderer(p, true).Add(new LevelSelectRender(p, -1, GetLevelState(p)>>8), time + 1.1);
2280 if (type==0) // Init & count levels
2282 if (x[0]=='_')
2284 int t = GetLevelState(p);
2285 int unlock = progress.GetLevel(x, true)->unlocked;
2286 LevelSelectRender* lsr = new LevelSelectRender( p, unlock ? -1 : (t>>8) ? 5 : 1, t>>8 );
2287 if ((t>>8) && p.x > mapRightBound) mapRightBound = p.x;
2288 #ifdef MAP_EDIT_HACKS
2289 lsr->magic = -atoi(x+1);
2290 SetTile(p, LIFT_DOWN, true, false);
2291 #else
2292 SetTile(p, EMPTY, true, false);
2293 #endif
2294 renderer(p,true).Add(lsr, time);
2296 else
2298 //printf("Level: %s\n", x);
2300 int t = GetLevelState(p);
2301 numLevels++;
2302 if (t && !GetItem(p))
2304 if (!isMap)
2306 isMap = true;
2307 mapRightBound = 0;
2309 currentLevelInfo = 0;
2311 if ((t&0xff)>=2)
2313 LevelSave* l = progress.GetLevel(x, true);
2314 if (!l->unlocked)
2316 l->unlocked = true;
2318 renderer(p, true).Add(new LevelSelectRender(p, -1, 0), time+0.01);
2319 renderer().Add(new ExplosionRender(p, 0), time + 0.6);
2320 renderer(p, true).Add(new LevelSelectRender(p, t & 0xff, t>>8), time + 0.6);
2323 numLevelsFound++;
2324 if (p.x > mapRightBound) mapRightBound = p.x;
2326 if ((t&0xff)>=3)
2327 numComplete++;
2328 if ((t&0xff)>=4)
2329 numMastered++;
2331 LevelSelectRender* lsr = new LevelSelectRender( p, t & 0xff, t>>8 );
2333 #ifdef MAP_EDIT_HACKS
2334 lsr->magic = 0;
2335 int t = GetAutoTile(x, true);
2336 int v = GetAutoTile(x, false);
2337 if (MAP_EDIT_HACKS_DISPLAY_UNLOCK)
2338 lsr->magic = v;
2339 else
2340 lsr->magic = GetPar(x, true);
2341 t = 1;
2342 SetTile(p, t, true, false);
2343 #else
2344 SetTile(p, EMPTY, true, false);
2345 #endif
2347 renderer(p,true).Add(lsr, time);
2352 if (type==1 && x[0]!='_') // Clicked on
2354 int t = GetLevelState(p);
2355 if (t>1)
2357 LoadSave(x, false);
2362 void SetItem(Pos const & p, int t, bool updateRenderer=true, bool undoBuffer=true)
2364 if (p.x<0 || p.x>MAP_SIZE)
2365 return;
2366 if (p.y<0 || p.y>MAP_SIZE)
2367 return;
2368 if (map_item[p.x][p.y] == t)
2369 return;
2371 if (undoBuffer)
2372 undo[numUndo].Add(Undo::TileChange(p,GetTile(p),GetItem(p)));
2374 map_item[p.x][p.y] = t;
2376 if (updateRenderer)
2377 renderer(p,true).Add(new ItemRender(t, GetTile(p)==EMPTY, p), time);
2380 Tile GetItem(Pos const & p)
2382 if (p.x<0 || p.x>=MAP_SIZE)
2383 return EMPTY;
2384 if (p.y<0 || p.y>=MAP_SIZE)
2385 return EMPTY;
2386 return map_item[p.x][p.y];
2389 void LoadSaveProgress(bool save)
2391 FILE* f = file_open(currentSlot, save ? "wb" : "rb");
2392 if (f)
2394 progress.LoadSave(f, save);
2395 fclose(f);
2397 else
2399 if (!save)
2400 progress.Clear();
2403 void LoadProgress()
2405 LoadSaveProgress(false);
2407 void SaveProgress()
2409 LoadSaveProgress(true);
2412 SDL_Surface* Load(const char * bmp, bool colourKey=true)
2414 typedef unsigned int uint32;
2415 uint32* tmp = 0;
2417 SDL_Surface * g = 0;
2419 #ifdef EDIT
2420 if (strstr(bmp, ".bmp"))
2422 g = SDL_LoadBMP(bmp);
2424 char out[1024];
2425 strcpy(out, bmp);
2426 strcpy(strstr(out, ".bmp"), ".dat");
2428 // SDL_PixelFormat p;
2429 // p.sf = 1;
2430 // SDL_Surface* tmp = SDL_ConvertSurface(g, &p, SDL_SWSURFACE);
2432 short w=g->w, h=g->h;
2433 char* buf = (char*) g->pixels;
2434 if (colourKey)
2436 while (IsEmpty(g, w-1, 0, 1, h) && w>1)
2437 w--;
2438 while (IsEmpty(g, 0, h-1, w, 1) && h>1)
2439 h--;
2442 FILE* f = file_open(out, "wb");
2443 fwrite(&w, sizeof(w), 1, f);
2444 fwrite(&h, sizeof(h), 1, f);
2446 uint32 mask = IMAGE_DAT_OR_MASK;
2447 for (int i=0; i<(int)w*h; )
2449 uint32 c = (*(uint32*)&buf[(i%w)*3 + (i/w)*g->pitch] | mask);
2450 int i0 = i;
2451 while (i < (int)w*h && c == (*(uint32*)&buf[(i%w)*3 + (i/w)*g->pitch] | mask))
2452 i++;
2453 c &= 0xffffff;
2454 i0 = i-i0-1;
2455 if (i0 < 0xff)
2456 c |= i0 << 24;
2457 else
2458 c |= 0xff000000;
2460 fwrite(&c, sizeof(c), 1, f);
2462 if (i0 >= 0xff)
2463 fwrite(&i0, sizeof(i0), 1, f);
2465 fclose(f);
2467 SDL_FreeSurface(g);
2469 bmp = out;
2471 #endif
2473 FILE* f = file_open(bmp, "rb");
2474 if (!f) FATAL("Unable to open file", bmp);
2476 int16_t w,h;
2477 fread(&w, sizeof(w), 1, f);
2478 fread(&h, sizeof(h), 1, f);
2479 w = SWAP16(w);
2480 h = SWAP16(h);
2481 if (w>1500 || h>1500 || w<=0 || h<=0) FATAL("Invalid file", bmp);
2483 tmp = new uint32[(int)w*h];
2485 uint32 c = 0;
2486 uint32 cnt = 0;
2487 for (int p=0; p<(int)w*h; p++)
2489 if (cnt)
2490 cnt -= 0x1;
2491 else
2493 fread(&c, sizeof(c), 1, f);
2494 c = SWAP32(c);
2495 cnt = c >> 24;
2496 if (cnt==255) {
2497 fread(&cnt, sizeof(cnt), 1, f);
2498 cnt = SWAP32(cnt);
2501 tmp[p] = c | 0xff000000;
2504 g = SDL_CreateRGBSurfaceFrom(tmp, w, h, 32, w*4,
2505 0xff0000,
2506 0xff00,
2507 0xff,
2508 0xff000000 );
2510 fclose(f);
2513 if (!g) FATAL("Unable to create SDL surface");
2514 if (colourKey)
2515 SDL_SetColorKey(g, SDL_SRCCOLORKEY, SDL_MapRGB(g->format, WATER_COLOUR));
2516 SDL_Surface * out = SDL_DisplayFormat(g);
2517 SDL_FreeSurface(g);
2518 delete [] tmp;
2519 if (!out) FATAL("Unable to create SDL surface (SDL_DisplayFormat)");
2520 return out;
2523 #ifdef USE_LEVEL_PACKFILE
2524 PackFile1 levelFiles;
2525 #endif
2526 HexPuzzle()
2528 SDL_WM_SetCaption(GAMENAME, 0);
2530 time = 0;
2532 #ifdef USE_LEVEL_PACKFILE
2533 FILE* f = file_open("levels.dat", "rb");
2534 if (!f)
2535 FATAL("Unable to open file", "levels.dat");
2536 levelFiles.Read(f);
2537 fclose(f);
2538 #endif
2540 LoadGraphics();
2542 isMap = false;
2543 editMode = false;
2545 currentLevelInfo = 0;
2547 editTile = 0;
2548 levelPar = 0;
2549 levelDiff = 5;
2550 turboAnim = 0;
2552 memset(map, 0, sizeof(map));
2553 memset(map_item, 0, sizeof(map_item));
2554 memset(special, 0, sizeof(special));
2556 LoadProgress();
2558 // player = Pos(1,11);
2560 // ResetLevel();
2562 LoadMap();
2565 void LoadMap()
2567 #ifndef EDIT
2568 progress.GetLevel(STARTING_LEVEL, true)->unlocked = 1;
2569 if (!progress.GetLevel(STARTING_LEVEL, true)->Completed())
2571 LoadSave(STARTING_LEVEL, false);
2572 return;
2574 #endif
2576 //editMode = false;
2577 LoadSave(mapname, false);
2580 void Render()
2582 if (!activeMenu || activeMenu->renderBG)
2584 SDL_Rect src = {0,0,screen->w,screen->h};
2585 SDL_Rect dst = {0,0,screen->w,screen->h};
2586 if (isRenderMap)
2588 int boundW = mapBG->w;
2589 #ifndef EDIT
2590 boundW = MIN(boundW, (mapRightBound+4) * TILE_W2 - TILE_W1);
2591 #endif
2592 src.x = scrollX - initScrollX;
2593 if (src.x+src.w > boundW)
2595 int diff = src.x+src.w - boundW;
2596 src.x -= diff;
2597 if (isMap)
2598 scrollX -= diff;
2600 if (src.x < 0)
2602 if (isMap)
2603 scrollX -= src.x;
2604 src.x = 0;
2606 //scrollY = initScrollY;
2608 if (isMap)
2609 mapScrollX = scrollX;
2611 SDL_BlitSurface(mapBG, &src, screen, &dst);
2613 else
2614 SDL_BlitSurface(gradient, &src, screen, &dst);
2616 renderer.Render(time, true);
2618 if (!hintsDone && !isFadeRendering)
2620 DoHints();
2623 if (1)
2625 SDL_Rect src = {0,SCREEN_H-1,SCREEN_W,1};
2626 SDL_Rect dst = {0,SCREEN_H-1,SCREEN_W,1};
2627 for (int i=0; i<SCREEN_H; i++)
2629 dst.x = src.x = 0;
2630 dst.y = src.y = SCREEN_H-1-i;
2631 src.w = SCREEN_W;
2632 src.h = 1;
2634 if (isRenderMap)
2636 src.x += (int)( sin(i*0.9 + time*3.7) * sin(i*0.3 + time*0.7)*4 );
2637 src.y += (int)( (sin(i*0.3 - time*2.2) * sin(i*0.48 + time*0.47) - 1) * 1.99 );
2639 else
2641 src.x += (int)( sin(i*0.5 + time*6.2) * sin(i*0.3 + time*1.05) * 5 );
2642 src.y += (int)( (sin(i*0.4 - time*4.3) * sin(i*0.08 + time*1.9) - 1) * 2.5 );
2644 SDL_BlitSurface(screen, &src, screen, &dst);
2648 if(isRenderMap)
2649 SDL_BlitSurface(mapBG2, &src, screen, &dst);
2651 renderer.Render(time, false);
2653 if (!isRenderMap && !isMap && !isFadeRendering)
2655 int v[3] = {player_items[0], player_items[1], player_score};
2656 if (numUndo > 1 && time < undo[numUndo-2].endTime)
2658 int i = numUndo-1;
2659 while (i>1 && time<undo[i-1].time)
2660 i--;
2661 v[0] = undo[i].numItems[0];
2662 v[1] = undo[i].numItems[1];
2663 v[2] = undo[i].score;
2665 if (numUndo>1 && time < undo[0].time)
2666 v[0]=v[1]=v[2]=0;
2667 #ifdef EDIT
2668 Print(0,0,"Anti-Ice: %d", v[0]);
2669 Print(0,FONT_SPACING,"Jumps: %d", v[1]);
2670 Print(0,FONT_SPACING*2,"Score: %d (%d)", v[2], player_score);
2671 Print(0,FONT_SPACING*3,"Par: %d", levelPar);
2672 Print(0,FONT_SPACING*4,"Diff: %d", levelDiff);
2673 #else
2674 if (showScoring)
2675 Print(0, SCREEN_H-FONT_SPACING, " Par: %d Current: %d", levelPar, v[2]);
2677 if (v[0])
2678 Print(0,0," Anti-Ice: %d", v[0]);
2679 else if (v[1])
2680 Print(0,0," Jumps: %d", v[1]);
2681 #endif
2683 if (isRenderMap && isMap && !isFadeRendering)
2685 #if 0//def EDIT
2686 Print(0,0,"Points: %d", numComplete+numMastered);
2687 Print(0,FONT_SPACING,"Discovered: %d%% (%d/%d)", numLevelsFound*100/numLevels, numLevelsFound, numLevels);
2688 Print(0,FONT_SPACING*2,"Complete: %d%% (%d)", numComplete*100/numLevels, numComplete);
2689 Print(0,FONT_SPACING*3,"Mastered: %d%% (%d)", numMastered*100/numLevels, numMastered);
2690 #else
2691 if (numComplete==numLevels && progress.general.endSequence>0)
2692 Print(0, SCREEN_H-FONT_SPACING, " %d%% Mastered", numMastered*100/numLevels);
2693 else
2694 Print(0, SCREEN_H-FONT_SPACING, " %d%% Complete", numComplete*100/numLevels);
2696 if (numMastered >= numLevels && progress.general.endSequence < 2)
2698 progress.general.endSequence = 2;
2699 LoadSaveProgress(true);
2701 new Fader(-1, -7, 0.3);
2703 if (numComplete >= numLevels && progress.general.endSequence < 1)
2705 progress.general.endSequence = 1;
2706 LoadSaveProgress(true);
2708 new Fader(-1, -5, 0.3);
2710 #endif
2712 if ((currentLevelInfo || noMouse) && isMap && isRenderMap && !activeMenu && isFadeRendering<=0)
2714 Pos p;
2715 if (noMouse)
2716 p = keyboardp;
2717 else
2718 p = mousep;
2719 int pad = SCREEN_W/80;
2720 // SDL_Rect src = {0, 0, uiGraphics->w, uiGraphics->h};
2721 SDL_Rect dst = {pad, SCREEN_H-TILE_H2-pad, 0, 0};
2722 // dst.x = p.getScreenX() + TILE_W3/2 - scrollX;
2723 // dst.y = p.getScreenY() - src.h/2 - scrollY;
2724 dst.x = p.getScreenX() - scrollX;
2725 dst.y = p.getScreenY() - scrollY - FONT_SPACING*3 - FONT_SPACING/2;
2726 // if (dst.x > SCREEN_W*2/3) dst.x -= TILE_W3 + src.w;
2727 // if (dst.y+src.h > screen->h-pad) dst.y = screen->h-pad - src.h;
2729 RenderTile(false, 0, p.getScreenX(), p.getScreenY());
2730 // SDL_BlitSurface(uiGraphics, &src, screen, &dst);
2732 // dst.x += src.w/2;
2734 if (currentLevelInfo)
2736 keyboardp = p;
2738 PrintC(true, dst.x, dst.y - FONT_SPACING/4, currentLevelInfo->name);
2740 if (currentLevelInfo->file[0]!=0)
2742 if (player_score > 0)
2744 if (progress.general.scoringOn)
2746 PrintC(false, dst.x, dst.y + FONT_SPACING*4 - FONT_SPACING/4, "Best:% 3d", player_score);
2747 PrintC(false, dst.x, dst.y + FONT_SPACING*5 - FONT_SPACING/4, "Par:% 3d", levelPar);
2749 else
2750 PrintC(false, dst.x, dst.y + FONT_SPACING*4 - FONT_SPACING/4, "Completed", player_score);
2752 else
2753 PrintC(false, dst.x, dst.y + FONT_SPACING*4 - FONT_SPACING/4, "Incomplete", player_score);
2758 // "Win"
2759 if (win && numUndo > 0 && time > undo[numUndo-1].endTime + 2)
2761 if (currentFile[0] && winFinal==0)
2763 LevelSave* l = progress.GetLevel(currentFile, true);
2765 new WinLoseScreen(true, player_score, showScoring ? levelPar : 0, l && showScoring && l->Completed() ? l->GetScore() : 0);
2767 if (l->IsNewCompletionBetter(player_score))
2769 l->SetScore(player_score);
2771 l->SetSolution(numUndo);
2773 for (int i=0; i<numUndo; i++)
2774 l->SetSolutionStep(i, undo[i].playerMovement);
2777 SaveProgress();
2780 winFinal = 1;
2782 else
2783 winFinal = 0;
2785 // Move up "level complete" writing so it doesn't feel like everything's ground to a halt...
2786 if (win && numUndo > 0 && time > undo[numUndo-1].endTime && !winFinal)
2788 double t = (time - undo[numUndo-1].endTime) / 2;
2789 t=1-t;
2790 t*=t*t;
2791 t=1-t;
2792 int y = SCREEN_H/3 - FONT_SPACING + 1;
2793 y = SCREEN_H + int((y-SCREEN_H)*t);
2794 PrintC(true, SCREEN_W/2, y, "Level Complete!");
2798 if (activeMenu)
2799 activeMenu->Render();
2801 if (!noMouse)
2803 // Edit cursor
2804 if (editMode)
2806 RenderTile(false, editTile, mousex+scrollX, mousey+scrollY);
2808 else
2810 Print(mousex, mousey-2, "\x7f");
2815 int Count(Tile t)
2817 return tileCount[t];
2819 int Swap(Tile t, Tile t2)
2821 const int num = Count(t) + Count(t2);
2822 if (t==t2 || num==0)
2823 return Count(t); // Nothing to do...
2825 int count=0;
2826 for (int x=0; x<MAP_SIZE; x++)
2827 for (int y=0; y<MAP_SIZE; y++)
2829 if (GetTile(Pos(x,y))==t)
2831 count++;
2832 SetTile(Pos(x,y), t2);
2834 else if (GetTile(Pos(x,y))==t2)
2836 count++;
2837 SetTile(Pos(x,y), t);
2839 if (count==num)
2840 return count;
2842 return count;
2844 int Replace(Tile t, Tile t2)
2846 const int num = Count(t);
2847 if (t==t2 || num==0)
2848 return num; // Nothing to do...
2850 int count=0;
2851 for (int x=0; x<MAP_SIZE; x++)
2852 for (int y=0; y<MAP_SIZE; y++)
2854 Pos p(x,y);
2855 if (GetTile(p)==t)
2857 count++;
2859 SetTile(p, t2, false);
2861 if (t==COLLAPSE_DOOR && t2==COLLAPSABLE)
2862 renderer(p).Add(new BuildRender(p, -1, 1, 1), time + (rand() & 255)*0.001);
2863 else if (t==COLLAPSE_DOOR2 && t2==COLLAPSABLE2)
2864 renderer(p).Add(new BuildRender(p, -1, 1, 1, 1), time + (rand() & 255)*0.001);
2865 else
2866 SetTile(p, t2);
2868 if (count==num)
2869 return count;
2872 return count;
2875 Tile editTile;
2876 bool editMode;
2877 void ResetUndo()
2879 UndoDone();
2880 undoTime = -1;
2881 numUndo = 0;
2882 win = false;
2885 void UpdateCursor(Pos const & s)
2887 static Pos _s;
2888 if (s.x!=_s.x || s.y!=_s.y)
2890 _s = s;
2892 char* sp = GetSpecial(s);
2893 char tmp[1000];
2894 tmp[0]='\0';
2895 if (sp)
2897 if (isMap)
2899 currentLevelInfo = 0;
2900 levelPar = player_score = -1;
2901 if (GetLevelState(s)>=2)
2903 LevelSave* l = progress.GetLevel(sp, true);
2904 if (l)
2906 currentLevelInfo = GetLevelInfo(sp);
2907 levelPar = GetPar(sp);
2908 player_score = l->GetScore();
2913 #ifdef EDIT
2914 sprintf(tmp, "Special(%d,%d): %s (%d)", s.x, s.y, sp ? sp : "<None>", GetPar(sp));
2915 SDL_WM_SetCaption(tmp, NULL);
2916 #endif
2918 else if (currentFile[0])
2920 #ifdef EDIT
2921 SDL_WM_SetCaption(currentFile, NULL);
2922 #endif
2923 if (isMap)
2924 currentLevelInfo = 0;
2929 virtual void Mouse(int x, int y, int dx, int dy, int button_pressed, int button_released, int button_held)
2931 if (activeMenu)
2933 activeMenu->Mouse(x,y,dx,dy,button_pressed,button_released,button_held);
2934 return;
2937 if (isFadeRendering)
2938 return;
2941 #ifndef EDIT
2942 if (button_pressed==2 || button_pressed==4 && isMap)
2944 KeyPressed(SDLK_ESCAPE, 0);
2945 keyState[SDLK_ESCAPE] = 0;
2946 return;
2948 #endif
2950 x += scrollX;
2951 y += scrollY;
2953 Pos s = Pos::GetFromWorld(x,y);
2954 if (tileSolid[GetTile(Pos::GetFromWorld(x,y+TILE_HUP))] == 1)
2955 s = Pos::GetFromWorld(x,y+TILE_HUP);
2957 mousep = s;
2959 UpdateCursor(s);
2961 #ifdef EDIT
2962 if (button_held & ~button_pressed & 4)
2964 scrollX -= dx;
2965 scrollY -= dy;
2967 #endif
2969 if (!editMode)
2971 if (isMap && (button_pressed & 1))
2973 ActivateSpecial(s, 1);
2974 return;
2976 if (!isMap && win && winFinal)
2978 if (button_pressed & 1)
2980 LoadMap();
2981 return;
2984 if(!isMap)
2986 if((button_pressed & 1) || (button_held & 1) && (numUndo==0 || time>=undo[numUndo-1].endTime))
2988 if(s.x==player.x && s.y==player.y)
2990 // Don't activate jump powerup without a new click
2991 if (button_pressed & 1)
2992 Input(-1);
2994 else if(s.x==player.x && s.y<player.y)
2995 Input(0);
2996 else if(s.x==player.x && s.y>player.y)
2997 Input(3);
2998 else if(s.y==player.y && s.x<player.x)
2999 Input(5);
3000 else if(s.y==player.y && s.x>player.x)
3001 Input(2);
3002 else if(s.y+s.x==player.y+player.x && s.x>player.x)
3003 Input(1);
3004 else if(s.y+s.x==player.y+player.x && s.x<player.x)
3005 Input(4);
3007 if ((button_pressed & 4) || (button_held & 4) && (undoTime < 0))
3008 Undo();
3010 return;
3013 #ifdef EDIT
3014 if (!button_pressed && !button_held)
3015 return;
3017 if (button_pressed==1)
3018 if (editTile<0)
3019 editTile = GetItem(s)==1 ? -3 : GetItem(s)==2 ? -2 : -1;
3021 if (button_held==1 || button_pressed==1)
3023 ResetUndo();
3024 if (editTile>=0)
3025 SetTile(s, editTile, true, false);
3026 else
3027 SetItem(s, editTile==-2 ? 0 : editTile==-1 ? 1 : 2, true, false);
3030 if (button_pressed==2)
3032 editTile = GetTile(s);
3035 if (button_pressed==8)
3037 editTile=editTile-1;
3038 if (editTile<=0) editTile=NumTileTypes-1;
3041 if (button_pressed==16)
3043 editTile=editTile+1;
3044 if (editTile<=0) editTile=1;
3045 if (editTile==NumTileTypes) editTile=0;
3048 if (button_pressed==64)
3050 ResetUndo();
3051 player = s;
3052 dead = false;
3053 renderer.player.Reset(-1);
3054 renderer.player.Add(new PlayerRender(player, GetHeight(player), dead), 0);
3057 if (button_pressed==256)
3059 char* fn = LoadSaveDialog(false, true, "Select level");
3060 if (fn)
3062 char * l = strstr(fn, "Levels");
3063 if(l)
3065 FILE * f = file_open(l,"rb");
3066 if (f)
3067 fclose(f);
3068 if (f)
3069 SetSpecial(s, l);
3070 else if (l[6]!=0 && l[7]=='_')
3071 SetSpecial(s, l+7);
3073 UpdateCursor(Pos(-1,-1));
3076 if (button_pressed==512)
3078 SetSpecial(s, NULL);
3079 UpdateCursor(Pos(-1,-1));
3081 if (button_pressed==1024)
3083 static char x[1000] = "";
3084 if (!(s.x<0 || s.x>=MAP_SIZE || s.y<0 || s.y>=MAP_SIZE))
3086 char tmp[1000];
3087 strcpy(tmp, x);
3088 if (GetSpecial(s))
3089 strcpy(x, GetSpecial(s));
3090 else
3091 x[0] = 0;
3092 SetSpecial(s, tmp[0] ? tmp : 0);
3093 if (!tmp[0])
3094 SetTile(s, EMPTY, true, false);
3098 if (button_pressed==32)
3100 editTile = editTile<0 ? 1 : -1;
3102 #endif // EDIT
3105 void CheckFinished()
3107 bool slow = false;
3108 if (Count(COLLAPSABLE)==0)
3110 if (Replace(COLLAPSE_DOOR, COLLAPSABLE) == 0)
3111 win = true;
3112 else
3113 slow = true;
3114 Replace(SWITCH, NORMAL);
3116 else
3117 win = false;
3119 if (Count(COLLAPSABLE2)==0)
3120 if (Replace(COLLAPSE_DOOR2, COLLAPSABLE2))
3121 slow = true;
3123 if (slow)
3124 time += BUILD_TIME;
3126 bool Collide(Pos p, bool high)
3128 Tile t = GetTile(p);
3129 // switch(t)
3130 // {
3131 // default:
3132 if (!high)
3133 return tileSolid[t]==1;
3134 else
3135 return false;
3136 // }
3138 void Undo()
3140 if (numUndo==0) return;
3142 UndoDone(); // Complete previous undo...
3144 numUndo--;
3146 if (time > undo[numUndo].endTime)
3147 time = undo[numUndo].endTime;
3148 undoTime = undo[numUndo].time;
3150 undo[numUndo].Restore(this);
3152 void UndoDone()
3154 if (undoTime < 0)
3155 return;
3156 renderer.Reset(undoTime);
3157 time = undoTime;
3158 undoTime = -1;
3160 void ScoreDestroy(Pos p)
3162 Tile t = GetTile(p);
3163 if (t==COLLAPSABLE || t==COLLAPSE_DOOR)
3165 else if (t != EMPTY)
3167 player_score += 10;
3171 bool LaserTile(Pos p, int mask, double fireTime)
3173 if (&renderer(p) == &renderer(Pos(-1,-1)))
3174 return false;
3175 //if (!renderer.Visible(p))
3176 // return false;
3178 TileRender* tr = 0;
3179 if (time <= renderer(p).GetLastTime())
3180 if (fireTime < renderer(p).GetLastTime())
3182 renderer(p).Add(tr = new TileRender(GetTile(p), p, mask), fireTime);
3183 ((TileRender*)renderer(p).GetStage(time+10/*HACKY!*/))->special |= mask;
3185 else
3187 tr = new TileRender(GetTile(p), p, mask | ((TileRender*)renderer(p).GetStage(time+10/*HACKY!*/))->special);
3188 renderer(p).Add(tr, fireTime);
3190 else
3191 renderer(p).Add(tr = new TileRender(GetTile(p), p, mask), fireTime);
3193 if (tr)
3195 tr->specialDuration = time + LASER_LINE_TIME - fireTime + LASER_FADE_TIME;
3197 return true;
3199 void FireGun(Pos newpos, Dir d, bool recurse, double fireTime)
3201 static Pos hits[100];
3202 static Dir hitDir[100];
3203 static unsigned int numHits=0;
3204 if (!recurse)
3205 numHits = 0;
3207 double starttime = fireTime;
3208 for (Dir fd=((d<0)?0:d); fd<((d<0)?MAX_DIR:d+1); fd++)
3210 fireTime = starttime;
3211 // starttime += 0.03;
3213 Pos p = newpos + fd;
3214 int range = 0;
3215 for (; range<MAP_SIZE; range++, p=p+fd)
3217 Tile t = GetTile(p);
3218 if (tileSolid[t]!=-1)
3220 if (t!=TRAP)
3221 renderer(p).Add(new TileRender(tileSolid[t]==1 ? TILE_WHITE_WALL : TILE_WHITE_TILE, p), fireTime+0.1);
3223 unsigned int i;
3224 for (i=0; i<numHits; i++)
3225 if (hits[i]==p)
3226 break;
3227 if (i==numHits ||
3228 t==TRAP && (hitDir[i]&(1<<fd))==0
3231 if (i==numHits)
3233 if (i >= sizeof(hits)/sizeof(hits[0]))
3234 return;
3235 hitDir[i] = 1 << fd;
3236 hits[i] = p;
3237 numHits++;
3239 else
3241 hitDir[i] |= 1 << fd;
3243 if (t==TRAP)
3245 int dirmask =
3246 1<<((fd+2) % MAX_DIR)
3247 | 1<<((fd+MAX_DIR-2) % MAX_DIR);
3249 if (LaserTile(p, dirmask, fireTime))
3250 fireTime += (time+LASER_LINE_TIME - fireTime) / 40;
3251 // fireTime += LASER_SEGMENT_TIME;
3253 FireGun(p, (fd+1) % MAX_DIR, true, fireTime);
3254 FireGun(p, (fd+MAX_DIR-1) % MAX_DIR, true, fireTime);
3257 break;
3259 else
3261 LaserTile(p, 1<<(fd%3), fireTime);
3263 fireTime += (time+LASER_LINE_TIME - fireTime) / 40;
3264 // fireTime += LASER_SEGMENT_TIME;
3268 // renderer().Add(new LaserRender(newpos, fd, range), time);
3271 if (!recurse)
3273 //double _time = time;
3274 time += LASER_LINE_TIME;
3275 for (unsigned int i=0; i<numHits; i++)
3277 Pos p = hits[i];
3278 Tile t = GetTile(p);
3280 if (t==TRAP)
3281 continue;
3283 ScoreDestroy(p);
3285 renderer(p).Add(new ExplosionRender(p, t==GUN), time);
3286 //renderer(p).Add(new TileRender(EMPTY, p), time+2);
3287 SetTile(p, EMPTY, false);
3289 if (GetItem(p))
3290 renderer(p,true).Add(new ItemRender(GetItem(p), 1, p), time);
3292 if (t==GUN)
3294 for (Dir j=0; j<MAX_DIR; j++)
3296 if (GetTile(p+j)!=EMPTY)
3298 renderer(p+j).Add(new TileRender(tileSolid[GetTile(p+j)]==1 ? TILE_WHITE_WALL : TILE_WHITE_TILE, p+j), time+0.05);
3299 renderer(p+j).Add(new ExplosionRender(p+j), time+0.2);
3301 if (GetItem(p+j))
3302 renderer(p+j,true).Add(new ItemRender(GetItem(p+j), 1, p), time);
3304 //renderer(p+j).Add(new TileRender(EMPTY, p+j), time+2.2);
3306 ScoreDestroy(p + j);
3307 SetTile(p + j, EMPTY, false);
3312 time += MAX(LASER_FADE_TIME, 0.15);
3313 //time = _time;
3314 CheckFinished();
3317 int GetLastPlayerRot()
3319 RenderStage* rs = renderer.player.GetStage(-1);
3320 if (!rs) return 3;
3321 return ((PlayerRender*)rs)->r;
3323 bool Input(Dir d)
3325 if (dead || win || isMap)
3326 return false;
3328 // Complete undo
3329 UndoDone();
3331 // Jump forwards in time to last move finishing
3332 if (numUndo > 0 && time < undo[numUndo-1].endTime)
3333 time = undo[numUndo-1].endTime;
3335 double realTime = time;
3336 double endAnimTime = time;
3337 bool high = (tileSolid[GetTile(player)] == 1);
3338 Pos playerStartPos = player;
3339 Pos oldpos = player;
3340 int oldPlayerHeight = GetHeight(oldpos);
3341 Pos newpos = player + d;
3343 int playerRot = GetLastPlayerRot();
3344 if (d!=-1 && d!=playerRot)
3346 while (d!=playerRot)
3348 if ((d+6-playerRot) % MAX_DIR < MAX_DIR/2)
3349 playerRot = (playerRot+1) % MAX_DIR;
3350 else
3351 playerRot = (playerRot+MAX_DIR-1) % MAX_DIR;
3353 time += 0.03;
3355 if (GetTile(oldpos) == FLOATING_BALL)
3357 TileRender* t = new TileRender(FLOATING_BALL, oldpos);
3358 t->special = playerRot + 256;
3359 renderer(oldpos).Add(t, time);
3361 renderer.player.Add(new PlayerRender(playerRot, Pos(-20,-20), oldPlayerHeight, Pos(-20,-20), oldPlayerHeight, dead), time);
3363 else
3365 PlayerRender *p = new PlayerRender(playerRot, player, oldPlayerHeight, player, oldPlayerHeight, dead);
3366 p->speed = 0;
3367 renderer.player.Add(p, time);
3371 time += 0.03;
3374 if (d<0 && player_items[1]==0)
3375 return false;
3377 if (d >= 0)
3379 if (tileSolid[GetTile(newpos)] == -1)
3381 time = realTime;
3382 return false;
3384 if (Collide(newpos, high))
3386 time = realTime;
3387 return false;
3391 // Don't change any real state before this point!
3392 if (numUndo >= MAX_UNDO)
3394 numUndo--;
3395 for(int i=0; i<MAX_UNDO-1; i++)
3396 undo[i] = undo[i+1];
3398 undo[numUndo].New(d, player, player_items, time, player_score);
3400 if (d<0)
3402 player_items[1]--;
3405 double time0 = time;
3406 time += 0.15; //Time for leave-tile fx
3408 switch (GetTile(oldpos))
3410 case COLLAPSABLE:
3411 SetTile(oldpos, EMPTY);
3412 renderer(oldpos).Add(new DisintegrateRender(oldpos), time);
3413 CheckFinished();
3414 break;
3416 case COLLAPSE_DOOR:
3417 // Don't need to CheckFinished - can't be collapse doors around
3418 // unless there're still collapsable tiles around.
3419 SetTile(oldpos, EMPTY);
3420 renderer(oldpos).Add(new DisintegrateRender(oldpos, 1), time);
3421 break;
3423 case COLLAPSABLE2:
3424 SetTile(oldpos, COLLAPSABLE, false);
3425 renderer(oldpos).Add(new DisintegrateRender(oldpos, 0, 1), time);
3426 player_score += 10;
3427 CheckFinished();
3428 break;
3430 case COLLAPSE_DOOR2:
3431 SetTile(oldpos, COLLAPSE_DOOR, false);
3432 renderer(oldpos).Add(new DisintegrateRender(oldpos, 1, 1), time);
3433 player_score += 10;
3434 break;
3436 case COLLAPSABLE3:
3437 SetTile(oldpos, COLLAPSABLE2);
3438 break;
3441 time = time0; //End of leave-tile fx
3443 int retry_pos_count=0;
3444 retry_pos:
3445 retry_pos_count++;
3447 if (GetItem(newpos)==1)
3449 renderer(newpos, true).Add(new ItemCollectRender(GetItem(newpos), newpos), time + JUMP_TIME/2);
3450 SetItem(newpos, 0, false);
3451 player_items[0]++;
3453 if (GetItem(newpos)==2)
3455 renderer(newpos, true).Add(new ItemCollectRender(GetItem(newpos), newpos), time + JUMP_TIME/2);
3456 SetItem(newpos, 0, false);
3457 player_items[1]++;
3460 if (GetTile(player) == FLOATING_BALL)
3462 TileRender* t = new TileRender(FLOATING_BALL, player);
3463 t->special = 0;
3464 renderer(oldpos).Add(t, time);
3467 PlayerRender *p = new PlayerRender(playerRot, player, oldPlayerHeight, newpos, GetHeight(newpos), dead);
3469 // alternate leg (hacky!)
3470 if (1)
3472 static int l=0;
3473 l++;
3474 p->type = l & 1;
3477 if (retry_pos_count!=0 && GetTile(player)==TRAP)
3479 p->speed /= 1.5;
3480 p->type = 2;
3482 if (d==-1)
3483 p->speed = JUMP_TIME * 1.5;
3484 renderer.player.Add(p, time);
3485 endAnimTime = MAX(endAnimTime, time + p->speed+0.001);
3486 time += p->speed;
3488 player = newpos;
3490 switch (GetTile(newpos))
3492 case COLLAPSABLE:
3493 renderer(newpos).Add(new TileRender(TILE_GREEN_CRACKED, newpos), time);
3494 break;
3495 case COLLAPSE_DOOR:
3496 renderer(newpos).Add(new TileRender(TILE_GREEN_CRACKED_WALL, newpos), time);
3497 break;
3498 case COLLAPSABLE2:
3499 renderer(newpos).Add(new TileRender(TILE_BLUE_CRACKED, newpos), time);
3500 break;
3501 case COLLAPSE_DOOR2:
3502 renderer(newpos).Add(new TileRender(TILE_BLUE_CRACKED_WALL, newpos), time);
3503 break;
3505 case EMPTY:
3506 dead = true;
3507 break;
3509 case BUILDER:
3511 double pretime = time;
3512 bool done = false;
3513 time += 0.15;
3514 for (Dir fd=0; fd<MAX_DIR; fd++)
3516 Tile t2 = GetTile(newpos + fd);
3517 if (t2==EMPTY)
3519 done = true;
3520 SetTile(newpos+fd, COLLAPSABLE, false);
3521 renderer(newpos+fd).Add(new BuildRender(newpos+fd, fd, 0), time);
3523 else if (t2==COLLAPSABLE)
3525 done = true;
3526 SetTile(newpos+fd, COLLAPSE_DOOR, false);
3527 renderer(newpos+fd).Add(new BuildRender(newpos+fd, fd, 1), time);
3530 if (done) time += BUILD_TIME;
3531 else time = pretime;
3532 CheckFinished();
3533 endAnimTime = MAX(endAnimTime, time + 0.1);
3535 break;
3537 case SWITCH:
3538 Swap(COLLAPSE_DOOR, COLLAPSABLE);
3539 break;
3541 case FLOATING_BALL:
3543 int step=0;
3544 renderer.player.Add(new PlayerRender(playerRot, Pos(-30,-30), 0, Pos(-30,-30), 0, dead), time);
3545 while (tileSolid[GetTile(newpos+d)]==-1)
3547 step++;
3549 if (!renderer.Visible(newpos+d))
3551 TileRender* r = new TileRender(FLOATING_BALL, newpos);
3552 r->special = 512;
3553 renderer(newpos).Add(r, time);
3555 PlayerRender* pr = new PlayerRender(playerRot, newpos, 0, newpos, 0, dead);
3556 pr->speed = JUMP_TIME*1;
3557 renderer.player.Add(pr, time);
3559 time += pr->speed;
3561 dead = 1;
3562 break;
3564 oldpos = newpos;
3565 newpos = oldpos + d;
3567 SetTile(oldpos, EMPTY, false);
3568 SetTile(newpos, FLOATING_BALL, false);
3570 renderer(oldpos).Add(new TileRotateRender(FLOATING_BALL, oldpos, d, 2), time);
3571 renderer(oldpos).Add(new TileRender(EMPTY, oldpos), time + ROTATION_TIME/2);
3572 renderer(newpos).Add(new TileRotateRender(FLOATING_BALL, newpos, (d+3)%MAX_DIR, 3), time + ROTATION_TIME/2);
3574 // PlayerRender *p = new PlayerRender(playerRot, oldpos, 0, newpos, 0, dead);
3575 // p->speed = ROTATION_TIME*0.9;
3576 // renderer.player.Add(p, time);
3578 endAnimTime = MAX(endAnimTime, time + ROTATION_TIME + ROTATION_TIME/2);
3579 time += ROTATION_TIME;
3581 player = newpos;
3582 // renderer.player.Add(new PlayerRender(playerRot, player, 0, player, 0, 0), time);
3583 if (dead)
3586 else
3588 TileRender* r = new TileRender(FLOATING_BALL, newpos);
3589 r->special = playerRot + 256;
3590 renderer(newpos).Add(r, time);
3593 break;
3595 case LIFT_DOWN:
3596 case LIFT_UP:
3598 SetTile(newpos, GetTile(newpos)==LIFT_UP ? LIFT_DOWN : LIFT_UP, false);
3599 renderer(newpos).Add(new TileRender(GetTile(newpos), newpos, 1), time);
3601 PlayerRender *p = new PlayerRender(playerRot, newpos, 1-GetHeight(newpos), newpos, GetHeight(newpos), dead);
3602 renderer.player.Add(p, time);
3603 endAnimTime = MAX(endAnimTime, time + JUMP_TIME);
3605 break;
3607 case TRAMPOLINE:
3608 if (d<0) break;
3610 oldpos = newpos;
3611 if (Collide(newpos + d, high))
3612 break;
3613 if (Collide((newpos + d) + d, high) == 1)
3614 newpos = (newpos + d);
3615 else
3616 newpos = (newpos + d) + d;
3617 if (tileSolid[GetTile(newpos)] == -1)
3618 dead=1;
3619 //player = newpos;
3620 goto retry_pos;
3622 case SPINNER:
3624 for (Dir d=0; d<MAX_DIR; d++)
3626 Tile tmp = GetTile(newpos + d);
3627 renderer(newpos + d).Add(new TileRotateRender(tmp, newpos + d, (d+2)%MAX_DIR, false), time);
3629 Tile tmp = GetTile(newpos + Dir(MAX_DIR-1));
3630 for (Dir d=0; d<MAX_DIR; d++)
3632 Tile t2 = GetTile(newpos + d);
3633 SetTile(newpos + d, tmp, false);
3634 renderer(newpos + d).Add(new TileRotateRender(tmp, newpos + d, (d+4)%MAX_DIR, true), time + ROTATION_TIME/2);
3635 if (GetItem(newpos + d))
3636 renderer(newpos + d,true).Add(new ItemRender(GetItem(newpos + d), GetTile(newpos + d)==EMPTY, newpos+d), time + ROTATION_TIME/2);
3638 tmp = t2;
3640 endAnimTime = MAX(endAnimTime, time+ROTATION_TIME);
3641 // renderer(newpos).Add(new TileRotateRender(SPINNER, Dir(0), 0), time);
3643 break;
3645 case TRAP:
3647 if (d<0) break;
3649 if (player_items[0]==0)
3651 if (tileSolid[GetTile(newpos + d)] == 1)
3652 break;
3653 newpos = newpos + d;
3654 if (tileSolid[GetTile(newpos)] == -1)
3655 dead=1;
3656 //player = newpos;
3657 goto retry_pos;
3659 else
3661 SetTile(newpos, COLLAPSABLE3);
3662 player_items[0]--;
3665 break;
3667 case GUN:
3669 FireGun(newpos, d, false, time);
3671 endAnimTime = MAX(endAnimTime, time);
3673 if (GetTile(newpos)==EMPTY)
3675 PlayerRender* pr = new PlayerRender(playerRot, player, 0, player, 0, dead);
3676 pr->speed = JUMP_TIME*1;
3677 renderer.player.Add(pr, time);
3679 time += pr->speed;
3680 dead = 1;
3684 Pos hits[MAX_DIR];
3685 int numHits=0;
3687 for (Dir fd=((d<0)?0:d); fd<((d<0)?MAX_DIR:d+1); fd++)
3689 Pos p = newpos + fd;
3690 int range = 0;
3691 for (range; range<MAP_SIZE; range++, p=p+fd)
3693 Tile t = GetTile(p);
3694 if (tileSolid[t]!=-1)
3696 hits[numHits++] = p;
3697 break;
3701 renderer().Add(new LaserRender(newpos, fd, range), time);
3704 double _time = time;
3705 time += 0.25;
3706 for (int i=0; i<numHits; i++)
3708 Pos p = hits[i];
3709 Tile t = GetTile(p);
3711 renderer().Add(new ExplosionRender(p), time);
3712 ScoreDestroy(p);
3713 SetTile(p, EMPTY);
3715 if (t==GUN)
3717 for (Dir j=0; j<MAX_DIR; j++)
3719 ScoreDestroy(p + j);
3720 SetTile(p + j, EMPTY);
3722 if (GetTile(newpos)==EMPTY)
3723 dead = 1;
3726 endAnimTime = MAX(endAnimTime, time);
3728 time = _time;
3730 CheckFinished();
3732 break;
3736 endAnimTime = MAX(endAnimTime, time);
3738 if (dead)
3740 win = false;
3742 PlayerRender* pr = new PlayerRender(player, 0, dead);
3743 pr->speed = 0; // Don't sit around before disappearing!
3744 renderer.player.Add(pr, time);
3746 // If the tile we're drowning on isn't visible, give the ownership of the splash effect to the player, rather than a tile.
3747 if (renderer.Visible(player))
3748 renderer(player).Add(new ExplosionRender(player, 0, 1), time);
3749 else
3750 renderer.player.Add(new ExplosionRender(player, 0, 1), time);
3752 endAnimTime = MAX(endAnimTime, time+2);
3755 time = realTime;
3757 player_score += 1;
3759 undo[numUndo].endTime = endAnimTime;
3760 numUndo++;
3762 return true;
3764 void Update(double timedelta)
3766 while(deadMenu)
3767 delete deadMenu;
3769 if (activeMenu)
3771 activeMenu->Update(timedelta);
3773 else
3774 UpdateKeys();
3776 for (int i=0; i<SDLK_LAST; i++)
3777 if (keyState[i])
3778 keyState[i] = 1;
3780 if (activeMenu)
3781 return;
3783 if (isMap && isRenderMap)
3785 double min = 50;
3786 static double scrollHi = 0;
3787 double x = 0;
3788 #ifndef EDIT
3789 // if (!noMouse)
3791 int xx = noMouse ? keyboardp.getScreenX()-scrollX : mousex;
3792 if (xx > SCREEN_W) xx = SCREEN_W;
3793 int w = TILE_W2*4;
3794 if (xx < w)
3795 x = (double)xx / (w) - 1;
3796 if (xx > SCREEN_W - w)
3797 x = 1 - (double)(SCREEN_W-xx) / (w);
3798 x *= 500;
3799 if (x<-min || x>min)
3801 scrollHi += timedelta * x;
3802 scrollX += (int)scrollHi;
3803 scrollHi -= (int)scrollHi;
3806 #endif
3808 if (undoTime>=0 && undoTime < time)
3810 double acc = (time - undoTime) / 2;
3811 if (acc < 3) acc = 3;
3812 time -= timedelta * acc;
3813 if (undoTime >= time)
3814 UndoDone();
3816 else
3818 time += timedelta;
3819 if (turboAnim)
3820 time += timedelta * 20;
3823 void FileDrop(const char* filename)
3825 LoadSave(filename, false);
3827 void UpdateKeys()
3829 #ifdef EDIT
3830 if (keyState[SDLK_LALT] || keyState[SDLK_LCTRL])
3831 return;
3832 #endif
3834 if (!isMap && !editMode && undoTime < 0)
3836 if (keyState[SDLK_z] || keyState[SDLK_BACKSPACE] || keyState[SDLK_u])
3838 Undo();
3839 return;
3842 if (isMap && !editMode)
3845 if ((keyState[SDLK_q] | keyState[SDLK_KP7]) & 2) keyboardp.x--;
3846 else if ((keyState[SDLK_d] | keyState[SDLK_KP3]) & 2) keyboardp.x++;
3847 else if ((keyState[SDLK_e] | keyState[SDLK_KP9]) & 2) keyboardp.x++, keyboardp.y--;
3848 else if ((keyState[SDLK_a] | keyState[SDLK_KP1]) & 2) keyboardp.x--, keyboardp.y++;
3849 else if ((keyState[SDLK_w] | keyState[SDLK_KP8] | keyState[SDLK_UP]) & 2) keyboardp.y--;
3850 else if ((keyState[SDLK_s] | keyState[SDLK_KP2] | keyState[SDLK_DOWN]) & 2) keyboardp.y++;
3851 else if ((keyState[SDLK_LEFT]) & 2) keyboardp.x--, keyboardp.y+=keyboardp.x&1;
3852 else if (((keyState[SDLK_RIGHT]) & 2)) { if (keyboardp.x < mapRightBound) keyboardp.y-=keyboardp.x&1, keyboardp.x++; }
3853 else if ((keyState[SDLK_RETURN] | keyState[SDLK_KP5] | keyState[SDLK_SPACE] | keyState[SDLK_KP_ENTER]) & 2)
3855 // Simulate user clicking on it...
3856 Mouse(keyboardp.getScreenX()-scrollX, keyboardp.getScreenY()-scrollY, 0, 0, 1, 0, 0);
3857 noMouse = 1;
3858 return;
3860 else
3862 if (noMouse)
3863 UpdateCursor(keyboardp);
3864 return;
3866 int min[21] = { 17, 16, 15, 14, 13, 13, 13, 13, 13, 13, 12, 11, 11, 13, 12, 11, 8, 8, 7, 6, 7 };
3867 int max[21] = { 20, 20, 19, 19, 19, 19, 18, 21, 20, 20, 19, 19, 18, 18, 17, 16, 16, 16, 15, 15, 14 };
3868 if (keyboardp.x < 3) keyboardp.x = 3;
3869 if (keyboardp.x > mapRightBound) keyboardp.x = mapRightBound;
3871 if (keyboardp.y < min[keyboardp.x-3]) keyboardp.y = min[keyboardp.x-3];
3872 if (keyboardp.y > max[keyboardp.x-3]) keyboardp.y = max[keyboardp.x-3];
3873 noMouse = 1;
3874 UpdateCursor(keyboardp);
3876 else if (!editMode && (numUndo==0 || time>=undo[numUndo-1].endTime))
3878 static int usedDiag = 0;
3880 if (keyState[SDLK_q] || keyState[SDLK_KP7]) HandleKey('q', 0);
3881 else if (keyState[SDLK_w] || keyState[SDLK_KP8]) HandleKey('w', 0);
3882 else if (keyState[SDLK_e] || keyState[SDLK_KP9]) HandleKey('e', 0);
3883 else if (keyState[SDLK_a] || keyState[SDLK_KP1]) HandleKey('a', 0);
3884 else if (keyState[SDLK_s] || keyState[SDLK_KP2]) HandleKey('s', 0);
3885 else if (keyState[SDLK_d] || keyState[SDLK_KP3]) HandleKey('d', 0);
3887 else if (keyState[SDLK_UP] && keyState[SDLK_LEFT]) HandleKey('q', 0), usedDiag=1;
3888 else if (keyState[SDLK_UP] && keyState[SDLK_RIGHT]) HandleKey('e', 0), usedDiag=1;
3889 else if (keyState[SDLK_DOWN] && keyState[SDLK_LEFT]) HandleKey('a', 0), usedDiag=1;
3890 else if (keyState[SDLK_DOWN] && keyState[SDLK_RIGHT]) HandleKey('d', 0), usedDiag=1;
3891 else if (keyState[SDLK_UP] && !usedDiag) HandleKey('w', 0);
3892 else if (keyState[SDLK_DOWN] && !usedDiag) HandleKey('s', 0);
3894 else usedDiag = 0;
3897 void KeyReleased(int key)
3899 keyState[key] = 0;
3901 bool KeyPressed(int key, int mod)
3903 keyState[key] = 2;
3905 if (activeMenu)
3907 bool eat = activeMenu->KeyPressed(key, mod);
3908 if (!activeMenu)
3909 memset(keyState, 0, sizeof(keyState));
3910 return eat;
3912 else
3914 if ((key==SDLK_ESCAPE && (mod & KMOD_CTRL)))
3916 if (mod & KMOD_SHIFT)
3918 time = 0;
3919 renderer.Reset();
3920 LoadSaveProgress(false);
3923 LoadMap();
3926 if (isFadeRendering)
3927 return false;
3929 return HandleKey(key, mod);
3932 bool HandleKey(int key, int mod)
3934 turboAnim = 0;
3936 #ifdef CHEAT
3937 if (isMap && key=='r' && (mod & KMOD_ALT))
3939 progress.Clear();
3940 LoadMap();
3942 #endif
3944 if (0) {}
3946 else if ((key=='p' && !editMode || key==SDLK_PAUSE || key==SDLK_ESCAPE))
3948 noMouse = 1;
3949 new PauseMenu(isMap, progress.GetLevel(STARTING_LEVEL, true)->Completed(), progress.general.endSequence>=1, progress.general.endSequence>=2);
3952 #ifdef EDIT
3953 else if (key=='e' && (mod & KMOD_ALT))
3954 editMode = !editMode;
3956 else if (key=='p' && (mod & KMOD_ALT) && numUndo>0
3957 || key>='0' && key<='9' && (mod & KMOD_SHIFT) && !isMap)
3959 if (key>='0' && key<='9')
3960 levelDiff = (key=='0') ? 10 : key-'0';
3962 if (key=='p' && levelPar==0)
3963 levelPar = player_score;
3965 if (numUndo)
3968 undo[numUndo-1].Restore(this);
3969 while (--numUndo);
3971 time = 0;
3972 if (LoadSave(currentFile, true))
3974 if (key>='0' && key<='9')
3975 LoadMap();
3978 #endif
3980 /////////////////////////////////////////////////////////////////////////
3981 if (isMap && !editMode)
3982 return false;
3984 else if (key==SDLK_KP9 || key=='e') Input(1), noMouse=1;
3985 else if (key==SDLK_KP3 || key=='d') Input(2), noMouse=1;
3986 else if (key==SDLK_KP1 || key=='a') Input(4), noMouse=1;
3987 else if (key==SDLK_KP7 || key=='q') Input(5), noMouse=1;
3988 else if (key==SDLK_KP8 || key=='w') Input(0), noMouse=1;
3989 else if (key==SDLK_KP2 || (key=='s' && (((mod & (KMOD_CTRL|KMOD_ALT))==0)||!editMode))) Input(3), noMouse=1;
3990 else if (key==SDLK_KP5 || key==SDLK_SPACE || key==SDLK_RETURN || key==SDLK_KP_ENTER)
3992 noMouse=1;
3993 if (win && winFinal)
3994 LoadMap(), memset(keyState, 0, sizeof(keyState));
3995 else
3996 Input(-1);
3999 else if (key=='r' && (mod & KMOD_CTRL))
4000 LoadSave(currentFile, false);
4002 #ifdef EDIT
4003 else if (key=='z' && (mod & KMOD_ALT))
4005 if (numUndo>0 && !isMap)
4007 time = undo[numUndo-1].endTime;
4008 undoTime = undo[0].time;
4011 undo[numUndo-1].Restore(this);
4012 while (--numUndo);
4015 #endif
4016 else if (key=='z' || key==SDLK_BACKSPACE || key==SDLK_DELETE || key=='u')
4018 if (!isMap)
4019 Undo();
4022 #ifdef EDIT
4023 else if (key=='s' && (mod & KMOD_ALT)){
4024 if (win && strlen(currentFile)>0 && !isMap)
4026 char tmp[1000];
4027 strcpy(tmp, currentFile);
4028 ChangeSuffix(tmp, "sol");
4029 FILE* f = file_open(tmp, "wb");
4030 if (f)
4032 for (int i=0; i<numUndo; i++)
4034 fputc(undo[i].playerMovement, f);
4036 fclose(f);
4040 #endif
4042 #ifdef CHEAT
4043 else if (key=='/' && (mod & KMOD_ALT)){
4044 turboAnim = 1;
4045 if (!isMap)
4047 while (numUndo)
4048 Undo();
4049 ResetLevel();
4051 if (mod & KMOD_SHIFT)
4053 LevelSave* l = progress.GetLevel(currentFile, false);
4054 if (l && l->Completed())
4056 for (int i=0; i<l->bestSolutionLength; i++)
4057 Input(l->bestSolution[i]);
4058 time = 0;
4060 if (!win && l)
4061 l->Clear();
4063 else
4065 char tmp[1000];
4066 strcpy(tmp, currentFile);
4067 ChangeSuffix(tmp, "sol");
4068 FILE* f = file_open(tmp, "rb");
4069 if (f)
4071 int dir;
4072 while ((dir = fgetc(f)) != -1)
4074 if (dir==0xff)
4075 dir = -1;
4076 Input(dir);
4078 time = 0;
4079 fclose(f);
4081 if (!win)
4082 remove(tmp);
4087 #endif
4089 #ifdef EDIT
4090 else if (!editMode)
4091 return false;
4093 else if (key>='0' && key<='9' && (mod & KMOD_ALT) && !isMap)
4094 levelPar = levelPar*10 + key-'0';
4095 else if (key==SDLK_BACKSPACE && (mod & KMOD_ALT) && !isMap)
4096 levelPar /= 10;
4098 else if (key=='i')
4099 Mouse(mousex, mousey, 0, 0, 32, 0, mouse_buttons);
4100 else if (key=='p' && !(mod & KMOD_ALT))
4101 Mouse(mousex, mousey, 0, 0, 64, 0, mouse_buttons);
4102 else if (key=='x')
4103 Mouse(mousex, mousey, 0, 0, 128, 0, mouse_buttons);
4104 else if (key==SDLK_RETURN)
4105 Mouse(mousex, mousey, 0, 0, 256, 0, mouse_buttons);
4106 else if (key==SDLK_BACKSPACE)
4107 Mouse(mousex, mousey, 0, 0, 512, 0, mouse_buttons);
4108 else if (key=='c')
4109 Mouse(mousex, mousey, 0, 0, 1024, 0, mouse_buttons);
4111 else if (key=='s' && (mod & KMOD_CTRL)){
4112 char *fn = LoadSaveDialog(true, true, "Save level");
4113 LoadSave(fn, true);
4114 SDL_WM_SetCaption(currentFile, NULL);
4117 else if (key=='o' && (mod & KMOD_CTRL)){
4118 char* fn = LoadSaveDialog(false, true, "Open level");
4119 LoadSave(fn, false);
4120 SDL_WM_SetCaption(currentFile, NULL);
4122 #endif
4124 else
4125 return false;
4127 return true;
4129 void LoadGraphics()
4131 #define X(NAME,FILE,ALPHA) NAME = Load(DATA_DIR "/graphics/" FILE BMP_SUFFIX, ALPHA);
4132 #include "gfx_list.h"
4134 static int first = 1;
4135 if (first)
4137 first = false;
4138 MakeFont();
4139 MakeTileInfo();
4142 // unsigned int d = {
4144 // };
4145 // static SDL_Cursor * c = SDL_CreateCursor(data, mask, 32, 32, 1, 1);
4146 // SDL_SetCursor(c);
4147 SDL_ShowCursor(0);
4149 void FreeGraphics()
4151 #define X(NAME,FILE,ALPHA) if (NAME) SDL_FreeSurface(NAME), NAME=0;
4152 #include "gfx_list.h"
4154 virtual void ScreenModeChanged()
4156 // FreeGraphics();
4157 // LoadGraphics();
4161 MAKE_STATE(HexPuzzle, SDLK_F1, false);
4163 char * HexPuzzle::loadPtr = 0;
4164 char * HexPuzzle::endLoad = 0;
4166 #endif //USE_OPENGL