Merge branch 'reduce-cpu'
[hex-a-hop.git] / hex_puzzzle.cpp
blobee4eef08e89e426e4151755f779b8fababa3ebb8
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--;
624 if (currentStage > 0 && currentStage >= numStages)
625 currentStage = numStages - 1;
627 if (currentStage < 0) currentStage = 0;
629 void Wipe()
631 if (currentStage > 0 && numStages > 0)
633 memmove(&time[0], &time[currentStage], sizeof(time[0]) * (numStages-currentStage));
634 memmove(&stage[0], &stage[currentStage], sizeof(stage[0]) * (numStages-currentStage));
635 numStages -= currentStage;
636 currentStage = 0;
639 void Add(RenderStage* s, double t)
641 int i=0;
643 if (currentStage<numStages && time[currentStage]<=t)
644 i = currentStage;
646 while (i<numStages && time[i]<t)
647 i++;
649 if (i<numStages && time[i]==t)
650 stage[i]=s;
651 else
653 Reserve();
655 if (i<numStages)
657 memmove(&time[i+1], &time[i], (numStages-i) * sizeof(time[0]));
658 memmove(&stage[i+1], &stage[i], (numStages-i) * sizeof(stage[0]));
661 numStages++;
662 time[i] = t;
663 stage[i] = s;
668 class WorldRenderer
670 #define SIZE 30
671 #define FX 10
672 RenderObject tile[SIZE][SIZE][2];
673 RenderObject fx[FX];
674 int fxPos;
676 public:
677 RenderObject player;
678 RenderObject dummy;
680 WorldRenderer()
682 Reset();
685 void Reset(double t = -1)
687 fxPos = 0;
688 player.Reset(t);
689 dummy.Reset(-1);
691 for (int i=0; i<SIZE; i++)
692 for (int j=0; j<SIZE; j++)
693 for (int q=0; q<2; q++)
694 tile[i][j][q].Reset(t);
696 for (int j=0; j<FX; j++)
697 fx[j].Reset(t);
700 void Wipe()
702 player.Wipe();
703 dummy.Reset(-1);
705 for (int i=0; i<SIZE; i++)
706 for (int j=0; j<SIZE; j++)
707 for (int q=0; q<2; q++)
708 tile[i][j][q].Wipe();
710 for (int j=0; j<FX; j++)
711 fx[j].Wipe();
714 bool Visible(Pos p)
716 int x0 = (scrollX+TILE_W2) / TILE_W2;
717 int x1 = (scrollX+SCREEN_W+TILE_W3+TILE_W1) / TILE_W2;
718 if (p.x<0 || p.y<0 || p.x>=SIZE || p.y>=SIZE) return false;
719 if (p.x<x0) return false;
720 if (p.x>=x1-1) return false;
721 for (int j0=0; j0<SIZE*3; j0++)
723 if (j0 * TILE_H1 < scrollY-TILE_H1) continue;
724 if (j0 * TILE_H1 > scrollY+SCREEN_H+TILE_H1) break;
725 int i = j0&1;
726 int j = j0>>1;
727 j -= (x0-i)/2;
728 i += (x0-i)/2*2;
729 if (j>=SIZE) i+=(j+1-SIZE)*2, j=SIZE-1;
730 for (; i<x1 && j>=0; i+=2, j--)
732 if (Pos(i,j)==p)
733 return true;
736 return false;
739 void Render(double t, bool reflect)
741 dummy.Reset(-1);
743 int playerDepth = player.GetDepth(t);
744 if (reflect) playerDepth-=4;
745 if (playerDepth<0)
746 player.Render(t, reflect);
748 int x0 = (scrollX+TILE_W2) / TILE_W2;
749 int x1 = (scrollX+SCREEN_W+TILE_W3+TILE_W1) / TILE_W2;
750 x0 = MAX(x0, 0);
751 x1 = MIN(x1, SIZE);
752 for (int j0=0; j0<SIZE*3; j0++)
754 if (j0 * TILE_H1 < scrollY-TILE_H1) continue;
755 if (j0 * TILE_H1 > scrollY+SCREEN_H+TILE_H1) break;
756 int i = j0&1;
757 int j = j0>>1;
758 j -= (x0-i)/2;
759 i += (x0-i)/2*2;
760 if (j>=SIZE) i+=(j+1-SIZE)*2, j=SIZE-1;
761 for (; i<x1 && j>=0; i+=2, j--)
763 for (int q=reflect?1:0; q!=2 && q!=-1; q += (reflect ? -1 : 1))
764 if (tile[i][j][q].Active(t))
766 tile[i][j][q].Render(t, reflect);
770 if (playerDepth==j0 || j0==SIZE*3 && playerDepth>j0)
771 player.Render(t, reflect);
774 for (int j=0; j<FX; j++)
775 if(fx[j].Active(t))
777 fx[j].Render(t, reflect);
781 RenderObject & operator () ()
783 fxPos++;
784 if (fxPos==FX) fxPos = 0;
785 return fx[fxPos];
787 RenderObject & operator () (Pos const & p, bool item=false)
789 if (p.x<0 || p.y<0 || p.x>=SIZE || p.y>=SIZE)
790 return dummy;
791 return tile[p.x][p.y][item ? 1 : 0];
795 void RenderTile(bool reflect, int t, int x, int y, int cliplift)
797 SDL_Rect src = tile[reflect][t];
798 SDL_Rect dst = {x-scrollX-GFX_SIZE/2, y-scrollY-GFX_SIZE+TILE_H1, 0, 0};
799 dst.x += tileOffset[reflect][t][0];
800 dst.y += tileOffset[reflect][t][1];
801 if (reflect)
802 dst.y += TILE_H_REFLECT_OFFSET;
803 if (cliplift==-1 || reflect)
805 // dst.w=src.w; dst.h=src.h;
806 // SDL_FillRect(screen, &dst, rand());
807 SDL_BlitSurface(reflect ? tileGraphicsR : tileGraphics, &src, screen, &dst);
809 else
811 src.h -= cliplift;
812 if (src.h > TILE_W1)
814 src.h -= TILE_W1/2;
815 SDL_BlitSurface(tileGraphics, &src, screen, &dst);
816 src.y += src.h;
817 dst.y += src.h;
818 src.h = TILE_W1/2;
820 if (src.h > 0)
822 src.w -= TILE_W1*2, src.x += TILE_W1;
823 dst.x += TILE_W1;
824 SDL_BlitSurface(tileGraphics, &src, screen, &dst);
828 void RenderGirl(bool reflect, int r, int frame, int x, int y, int h)
830 int sx = r * 64;
831 int sy = frame * 80*2;
832 if (reflect)
833 y += TILE_H_REFLECT_OFFSET+20+h, sy += 80;
834 else
835 y -= h;
836 SDL_Rect src = {sx, sy, 64, 80};
837 SDL_Rect dst = {x-scrollX-32, y-scrollY-65, 0, 0};
838 SDL_BlitSurface(girlGraphics, &src, screen, &dst);
841 struct ItemRender : public RenderStage
843 int item;
844 Pos p;
845 int water;
847 ItemRender(int i2, int _water, Pos const & _p) : item(i2), p(_p), water(_water)
850 double Translate(double seed, double time)
852 double bob = time*2 + seed*PI2;
853 return sin(bob)*4;
856 void Render(RenderObject* r, double time, bool reflect)
858 if (item==0)
859 return;
861 int y = -5 + (int)Translate(r->seed, r->currentTime + time);
862 if (reflect)
863 y=-y;
864 if (!reflect && !water)
865 RenderTile( false, TILE_SPHERE, p.getScreenX(), p.getScreenY());
866 RenderTile(
867 reflect,
868 item==1 ? TILE_ITEM1 : TILE_ITEM2,
869 p.getScreenX(), p.getScreenY()+y
874 void RenderFade(double time, int dir, int seed)
876 int ys=0;
877 srand(seed);
878 for(int x=rand()%22-11; x<SCREEN_W+22; x+=32, ys ^= 1)
880 for (int y=ys*20; y<SCREEN_H+30; y+=40)
882 double a = (rand()&0xff)*dir;
883 double b = (time * 0x400 + (y - SCREEN_H) * 0x140/SCREEN_H)*dir;
884 if (a >= b)
886 RenderTile(false, TILE_BLACK_TILE, x+scrollX, y+scrollY);
892 struct FadeRender : public RenderStage
894 int seed;
895 int dir;
896 FadeRender(int d=-1) : seed(rand()), dir(d)
898 isFadeRendering = d;
901 void Render(RenderObject* /*r*/, double time, bool reflect)
903 if (reflect) return;
904 if (time > 0.5)
906 if (dir==1) dir=0, isFadeRendering=0;
907 return;
909 RenderFade(time, dir, seed);
913 struct ScrollRender : public RenderStage
915 int x,y;
916 bool done;
917 ScrollRender(int a,int b) : x(a), y(b), done(false) {}
919 void Render(RenderObject* /*r*/, double /*time*/, bool /*reflect*/)
921 if (done) return;
922 scrollX = x, scrollY = y;
923 isRenderMap = isMap;
924 done = true;
928 struct LevelSelectRender : public RenderStage
930 Pos p;
931 int item;
932 int adj;
933 #ifdef MAP_EDIT_HACKS
934 int magic;
935 #endif
937 LevelSelectRender(Pos const & _p, int i2, int adj) : p(_p), item(i2), adj(adj)
940 void Render(RenderObject* /*r*/, double /*time*/, bool reflect)
942 if (item==0)
943 return;
945 #ifndef MAP_LOCKED_VISIBLE
946 if (item==1) return;
947 #endif
949 if (!reflect && adj)
950 for (int i=0; i<MAX_DIR; i++)
951 if (adj & (1 << i))
952 RenderTile( false, TILE_LINK_0+i, p.getScreenX(), p.getScreenY());
954 if (item < 0)
955 return;
957 if (!reflect)
959 RenderTile(
960 reflect,
961 TILE_SPHERE + item-1,
962 p.getScreenX(), p.getScreenY()
965 #ifdef MAP_EDIT_HACKS
966 int x = p.getScreenX()-scrollX, y = p.getScreenY()-scrollY;
967 Print(x+5,y-25,"%d",magic);
968 #endif
973 struct ItemCollectRender : public ItemRender
975 ItemCollectRender(int i2, Pos const & p) : ItemRender(i2, 0, p)
978 void Render(RenderObject* /*r*/, double /*time*/, bool /*reflect*/)
983 int GetLiftHeight(double time, int t)
985 if (t==LIFT_UP)
986 time = LIFT_TIME-time;
987 time = time / LIFT_TIME;
988 if (time > 1)
989 time = 1;
990 if (time < 0)
991 time = 0;
992 time = (3 - 2*time)*time*time;
993 if (t==LIFT_UP)
994 time = (3 - 2*time)*time*time;
995 if (t==LIFT_UP)
996 return (int)((TILE_H_LIFT_UP+4) * time);
997 else
998 return (int)((TILE_H_LIFT_UP-4) * time) + 4;
1001 struct TileRender : public RenderStage
1003 int special;
1004 int t;
1005 Pos p;
1006 double specialDuration;
1008 TileRender(int i, Pos const & _p, int _special=0) : special(_special), t(i), p(_p), specialDuration(LASER_LINE_TIME)
1011 void Render(RenderObject* r, double time, bool reflect)
1013 if (t==0 && special==0)
1014 return;
1016 if (special && (t==LIFT_UP || t==LIFT_DOWN) && time<LIFT_TIME)
1018 int y = GetLiftHeight(time, t);
1019 if (!reflect)
1021 RenderTile(reflect, TILE_LIFT_BACK, p.getScreenX(), p.getScreenY());
1022 RenderTile(reflect, TILE_LIFT_SHAFT, p.getScreenX(), p.getScreenY()+y, y-8);
1023 RenderTile(reflect, TILE_LIFT_FRONT, p.getScreenX(), p.getScreenY());
1025 else
1027 RenderTile(reflect, TILE_LIFT_SHAFT, p.getScreenX(), p.getScreenY()-y, y);
1028 RenderTile(reflect, LIFT_DOWN, p.getScreenX(), p.getScreenY());
1031 else if (special && (t==EMPTY || t==TRAP) && !reflect && time < specialDuration)
1033 if (t == TRAP)
1034 if (time < specialDuration-LASER_FADE_TIME)
1035 RenderTile(reflect, TILE_ICE_LASER_REFRACT, p.getScreenX(), p.getScreenY());
1036 else
1037 RenderTile(reflect, t, p.getScreenX(), p.getScreenY());
1038 int base = ((t==EMPTY) ? TILE_LASER_0 : TILE_LASER_REFRACT);
1039 if (t==EMPTY && time >= specialDuration-LASER_FADE_TIME)
1040 base = TILE_LASER_FADE_0;
1042 int foo=special;
1043 for(int i=0; foo; foo>>=1, i++)
1044 if (foo & 1)
1045 RenderTile(reflect, base+i, p.getScreenX(), p.getScreenY());
1047 else if (t==FLOATING_BALL)
1049 int y = int(1.8 * sin(r->seed*PI + time*4));
1050 if (special==512)
1052 if (time > 2) return;
1053 if (reflect) return;
1054 srand(int(r->seed * 0xfff));
1055 for (int i=0; i<20 - int(time*10); i++)
1057 int x = int((((rand() & 0xfff) - 0x800) / 10) * time);
1058 int y = int((((rand() & 0xfff) - 0x800) / 10) * time);
1059 RenderTile(true, 19 + ((i+int(time*5))&1)*10, p.getScreenX() + x, p.getScreenY() - 14 + y);
1062 if (time < 0.05)
1063 RenderTile(true, 18, p.getScreenX(), p.getScreenY() - 14);
1065 else if (special)
1066 RenderBoat(reflect, int(special)&255, p.getScreenX(), p.getScreenY(), y);
1067 else
1068 RenderTile(reflect, t, p.getScreenX(), p.getScreenY() + (reflect ? -y : y));
1070 else if (t != EMPTY)
1071 RenderTile(reflect, t, p.getScreenX(), p.getScreenY());
1073 static void RenderBoat(bool reflect, int d, int x, int y, int yo)
1075 if (reflect)
1076 RenderGirl(reflect, d, 0, x, y, -yo);
1077 RenderTile(reflect, FLOATING_BALL, x, y+yo);
1078 if (!reflect)
1080 RenderGirl(reflect, d, 0, x, y, -yo);
1081 RenderTile(true, 17, x, y+yo-TILE_H_REFLECT_OFFSET);
1086 struct TileRotateRender : public TileRender
1088 Dir d;
1089 // int range;
1090 int mode;
1091 TileRotateRender(int i, Pos const & p, Dir _d, int m) : TileRender(i, p), d(_d), mode(m)
1093 void Render(RenderObject* r, double time, bool reflect)
1095 if (t==0)
1096 return;
1097 double f = time / ROTATION_TIME;
1099 if (mode & 1) f += 0.5;
1100 if (f<1 && f>0)
1102 if (mode & 2)
1104 else
1105 f = (3-2*f)*f*f;
1108 if (mode & 1) f=1-f; else f=f;
1109 if (f<0) f=0;
1111 if (f >= 1)
1112 TileRender::Render(r, time, reflect);
1113 else
1115 Pos dd = (Pos(0,0)+d);
1116 int x = p.getScreenX() + int(dd.getScreenX()*(f));
1117 int y = p.getScreenY() + int(dd.getScreenY()*(f));
1119 if (mode & 2)
1120 RenderBoat(reflect, (mode&1) ? (d+MAX_DIR/2)%MAX_DIR : d, x, y, 2);
1121 else
1122 RenderTile(reflect, t, x, y);
1127 struct LaserRender : public RenderStage
1129 Pos p;
1130 Dir d;
1131 int range;
1133 LaserRender(Pos _p, int dir, int r) : p(_p), d(dir), range(r)
1136 void Render(RenderObject* /*r*/, double /*time*/)
1141 struct ExplosionRender : public RenderStage
1143 Pos p;
1144 int seed;
1145 int power;
1146 int type;
1148 ExplosionRender(Pos _p, int _pow=0, int t=0) : p(_p), power(_pow), type(t)
1150 seed = rand();
1153 virtual int GetDepth(double /*time*/)
1155 return p.x + p.y*2;
1158 void Render(RenderObject* /*r*/, double time, bool reflect)
1160 if (type==1 && time > 2.5)
1161 type = -1, new WinLoseScreen(false);
1163 // if (reflect) return;
1164 if (time > 3) return;
1165 srand(seed);
1166 int q = 50 - int(time * 35);
1167 if (power) q*=2;
1168 if (type) q = 50;
1169 for (int i=0; i<q; i++)
1171 int x = p.getScreenX();
1172 int y = p.getScreenY() + (rand() & 31)-16;
1173 int xs = ((rand() & 63) - 32);
1174 int ys = (-10 - (rand() & 127)) * (1+power);
1175 if (type) ys*=2, xs/=2;
1176 x += int(xs * (1+time*(2+power)));
1177 int yo = int(time*time*128 + ys*time);
1178 //if (yo > 0) yo=-yo;//continue;
1179 if (type)
1182 if (yo > 0)
1184 if (!reflect && ys<-60)
1186 const double T = 0.06;
1187 double ct = -ys / 128.0;
1188 if (time < ct+T*4)
1190 x = p.getScreenX() + int(xs * (1+ct*(2+power)));
1191 RenderTile(
1192 reflect,
1193 time > ct+3*T ? TILE_SPLASH_3 : time > ct+2*T ? TILE_SPLASH_2 : time > ct+T ? TILE_SPLASH_1 : TILE_WATER_PARTICLE+1,
1194 x, y);
1198 else
1199 RenderTile(
1200 reflect,
1201 time - i*0.003 < 0.2 ? TILE_WATER_PARTICLE+1 : TILE_WATER_PARTICLE,
1202 x, y+(reflect?-1:1)*yo);
1204 else
1206 if (yo > 0)
1208 else
1209 RenderTile(
1210 reflect,
1211 i<q-20 || time<0.3 ? TILE_LASER_HEAD : i<q-10 || time<0.6 ? TILE_FIRE_PARTICLE_1 : TILE_FIRE_PARTICLE_2,
1212 x, y+(reflect?-1:1)*yo);
1217 struct DisintegrateRender : public RenderStage
1219 Pos p;
1220 int seed;
1221 int height;
1222 int type;
1224 DisintegrateRender(Pos _p, int _pow=0, int _t=0) : p(_p), height(_pow), type(_t)
1226 seed = rand();
1229 void Render(RenderObject* /*r*/, double time, bool reflect)
1231 if (type)
1232 RenderTile(reflect, height ? COLLAPSE_DOOR : COLLAPSABLE, p.getScreenX(), p.getScreenY());
1234 if (time > 50.0/70.0) return;
1235 if (reflect) return;
1236 srand(seed);
1237 int q = 50 - int(time * 70);
1238 if (height) q*=2;
1239 for (int i=0; i<q; i++)
1241 int x = (rand() % (TILE_W3-8))-TILE_W3/2+4;
1242 int y = (rand() % (TILE_H2-8))-TILE_H1+4;
1243 if (x<-TILE_WL/2 && ABS(y)<-TILE_WL/2-x) continue;
1244 if (x>TILE_WL/2 && ABS(y)>x-TILE_WL/2) continue;
1245 int yo=0;
1246 if (height) yo -= rand() % TILE_HUP;
1247 x += p.getScreenX();
1248 y += p.getScreenY() + 4;
1249 int xs = 0;//((rand() & 63) - 32);
1250 int ys = (- (rand() & 31));
1251 x += int(xs * (1+time*(2)));
1252 if (type) yo = -yo;
1253 yo += int(time*time*128 + ys*time);
1254 if (type) yo = -yo*2;
1255 //if (yo > 0) yo=-yo;//continue;
1256 int t = type ? TILE_BLUE_FRAGMENT : TILE_GREEN_FRAGMENT;
1257 if (i>q-20) t++;
1258 if (i>q-10) t++;
1259 if (yo > 5) yo = 5;
1260 RenderTile(false, t, x, y+(reflect?-yo:yo));
1264 struct BuildRender : public RenderStage
1266 Pos p;
1267 Dir dir;
1268 int reverse;
1269 int height;
1270 int type;
1272 BuildRender(Pos _p, Dir _d, int _h, int _r=0, int _type=0) : p(_p), dir(_d), reverse(_r), height(_h), type(_type)
1276 void Render(RenderObject* /*r*/, double time, bool reflect)
1278 if (time >= BUILD_TIME)
1279 RenderTile(reflect, height ^ reverse ? (type ? COLLAPSE_DOOR2 : COLLAPSE_DOOR) : (type ? COLLAPSABLE2 : COLLAPSABLE), p.getScreenX(), p.getScreenY());
1280 else
1282 if (height)
1283 RenderTile(reflect, type ? COLLAPSABLE2 : COLLAPSABLE, p.getScreenX(), p.getScreenY());
1285 double dist = time * 2 / BUILD_TIME;
1286 if (dir>-1)
1288 Pos from = p + ((dir+MAX_DIR/2)%MAX_DIR);
1289 if (dist <= 1)
1290 //if (dist > 1)
1292 double offset = (dist*0.7) + 0.3;
1293 int x = from.getScreenX() + int((p.getScreenX()-from.getScreenX()) * offset);
1294 int y = from.getScreenY() + int((p.getScreenY()-from.getScreenY()) * offset - dist*(1-dist)*(TILE_HUP*4));
1295 RenderTile(reflect, TILE_GREEN_FRAGMENT, x, y);
1297 dist -= 1;
1299 else
1301 if (reverse) dist = 1-dist;
1303 if (dist > 0 && !height)
1305 if (!reflect)
1306 for (int i=0; i<=int(dist*15); i++)
1308 int x = p.getScreenX(), y = p.getScreenY();
1309 double d = (i + fmod(dist*15, 1))/10.0;
1310 int x1 = int(sin(d*5+time)*MIN(d,1)*TILE_W2/2);
1311 int y1 = int(cos(d*5+time)*MIN(d,1)*TILE_H1*0.7);
1312 RenderTile(reflect, TILE_GREEN_FRAGMENT, x+x1, y+y1+4);
1313 RenderTile(reflect, TILE_GREEN_FRAGMENT, x-x1, y-y1+4);
1316 if (dist > 0 && height)
1318 int yo = int((1-dist)*(TILE_HUP*1.3));
1319 if (yo > TILE_HUP*1.1)
1320 RenderTile(reflect, TILE_WHITE_TILE, p.getScreenX(), p.getScreenY());
1321 else if (!reflect)
1323 RenderTile(reflect, type ? COLLAPSABLE2 : COLLAPSABLE, p.getScreenX(), p.getScreenY());
1324 RenderTile(reflect, type ? COLLAPSE_DOOR2 : COLLAPSE_DOOR, p.getScreenX(), p.getScreenY()+(reflect ? -yo : yo), yo+6);
1325 RenderTile(reflect, type ? TILE_BLUE_FRONT : TILE_GREEN_FRONT, p.getScreenX(), p.getScreenY());
1327 else
1329 if (yo < TILE_HUP/2)
1331 RenderTile(reflect, type ? COLLAPSE_DOOR2 : COLLAPSE_DOOR, p.getScreenX(), p.getScreenY()+(reflect ? -yo : yo), yo);
1334 RenderTile(reflect, type ? COLLAPSABLE2 : COLLAPSABLE, p.getScreenX(), p.getScreenY());
1342 struct PlayerRender : public RenderStage
1344 Pos p;
1345 Pos target;
1346 int p_h, target_h;
1347 int r;
1348 int type;
1349 double speed;
1350 bool dead;
1352 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)
1354 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)
1356 int dist = MAX(ABS(p.x-target.x), ABS(p.y-target.y));
1357 if (dist > 1)
1358 speed *= 1.5;
1359 if(dist==0)
1360 speed = 0;
1363 virtual int GetDepth(double time)
1365 double f = speed ? time / speed : 1;
1366 if (f>1) f=1;
1367 if (f==1) dead = this->dead;
1369 if (f==1 || f>0.5 && p_h>target_h)
1370 return target.x+target.y*2;
1371 return MAX(target.x+target.y*2 , p.x+p.y*2);
1374 void Render(RenderObject* /*ro*/, double time, bool reflect)
1376 bool dead = false;
1377 double f = speed ? time / speed : 1;
1378 if (f>1) f=1;
1379 if (f==1) dead = this->dead;
1381 int x = p.getScreenX();
1382 int y = p.getScreenY();
1383 int x2 = target.getScreenX();
1384 int y2 = target.getScreenY();
1385 int h = 0;
1386 int shadow_h = (int)((p_h+(target_h-p_h)*f)*TILE_HUP2);
1388 if (x==x2 && y==y2 && p_h!=target_h)
1390 h = TILE_H_LIFT_UP - GetLiftHeight(time, p_h ? LIFT_DOWN : LIFT_UP);
1392 else
1395 int dist = MAX(ABS(p.x-target.x), ABS(p.y-target.y));
1396 int arc = dist*dist;
1397 int h1 = p_h * TILE_HUP2;;
1398 int h2 = target_h * TILE_HUP2;
1399 if (dist==2 && h1!=0)
1401 arc += h2 ? 1 : 3;
1402 h1 = 0;
1403 shadow_h = f>=0.7 ? int(shadow_h*(f-0.7)/0.3) : 0;
1405 if (dist==0)
1406 arc = speed > JUMP_TIME ? 7 : 2;
1408 h = (int)(h1+(h2-h1)*f);
1409 // if (x==x2 && y==y2)
1410 // ;
1411 // else
1413 //h += int(TILE_H_LIFT_UP/3 * (1-f));
1414 h += (int)(f*(1-f)*TILE_HUP2*arc);
1417 if (type==2)
1418 h=0;
1421 if (!dead)
1423 int frame = 0;
1424 if (type==2 && f<1)
1426 //frame = ((int)(f*4) % 4);
1427 //if (frame==2) frame=0; else if (frame==3) frame=2;
1428 frame = 0;
1430 else if (f==1 || x==x2 && y==y2) // stationary
1431 frame = 0;
1432 else if (f > 0.7)
1433 frame = 0;
1434 else
1436 frame = type ? 2 : 1;
1437 if (f<0.1 || f>0.6)
1438 frame += 2;
1441 if (!reflect)
1442 RenderTile( false, TILE_SPHERE,
1443 (int)(x+(x2-x)*f),
1444 (int)(y+(y2-y)*f) - shadow_h
1447 RenderGirl(
1448 reflect,
1449 r, frame,
1450 (int)(x+(x2-x)*f),
1451 (int)(y+(y2-y)*f),
1456 /* RenderTile(
1457 dead ? TILE_SPHERE_OPEN : TILE_SPHERE_DONE,
1458 (int)(x+(x2-x)*f),
1459 (int)(y+(y2-y)*f),
1460 true
1461 );*/
1466 struct HexPuzzle : public State
1468 struct Undo
1470 #define MAX_TILECHANGE 64 // TODO: don't have a magic upper limit
1471 struct TileChange
1473 Pos p;
1474 Tile t;
1475 int item;
1477 TileChange()
1479 TileChange(Pos _p, Tile _t, int _i) : p(_p), t(_t), item(_i)
1481 void Restore(HexPuzzle* w)
1483 w->SetTile(p,t,false,false);
1484 w->SetItem(p,item,false,false);
1488 TileChange t[MAX_TILECHANGE];
1489 Pos playerPos;
1490 Dir playerMovement;
1491 int numT;
1492 int numItems[2];
1493 int score;
1494 double time;
1495 double endTime;
1497 void Add(TileChange const & tc)
1499 for (int i=0; i<numT; i++)
1500 if (t[i].p==tc.p)
1501 return;
1502 if (numT>=MAX_TILECHANGE)
1503 FATAL("numT>=MAX_TILECHANGE");
1504 else
1505 t[numT++] = tc;
1507 void New(Dir pmove, Pos & pp, int* items, double t, int sc)
1509 numItems[0] = items[0];
1510 numItems[1] = items[1];
1511 playerPos = pp;
1512 playerMovement = pmove;
1513 score = sc;
1514 time = t;
1515 numT = 0;
1517 void Restore(HexPuzzle* w)
1519 for (int i=numT-1; i>=0; i--)
1520 t[i].Restore(w);
1521 w->dead = false;
1522 w->win = false;
1523 w->player = playerPos;
1524 w->player_items[0] = numItems[0];
1525 w->player_items[1] = numItems[1];
1526 w->player_score = score;
1528 //w->renderer.player.Add(new PlayerRender(playerPos, w->GetHeight(playerPos), false), w->time);
1532 #define MAP_SIZE 30
1533 char* special[MAP_SIZE][MAP_SIZE];
1534 Tile map[MAP_SIZE][MAP_SIZE];
1535 int32_t map_item[MAP_SIZE][MAP_SIZE];
1536 int tileCount[NumTileTypes];
1537 int32_t levelPar, levelDiff;
1538 int turboAnim;
1539 Pos player;
1540 int player_items[2];
1541 int player_score;
1542 int numComplete, numLevels, numMastered, numLevelsFound;
1543 bool dead;
1544 bool win;
1545 int winFinal;
1547 SaveState progress;
1549 WorldRenderer renderer;
1550 double time;
1551 double undoTime;
1553 #define MAX_UNDO 6
1554 Undo undo[MAX_UNDO];
1555 int numUndo;
1556 LevelInfo* currentLevelInfo;
1558 char currentFile[1000];
1560 ~HexPuzzle()
1562 FreeGraphics();
1565 LevelInfo* GetLevelInfo(const char* f)
1567 if (strstr(f, "Levels\\") == f)
1568 f += 7;
1569 if (currentLevelInfo!=0 && strcmp(currentLevelInfo->file, f)==0)
1570 return currentLevelInfo;
1572 if (f[0]=='_')
1574 int t = atoi(f+1);
1575 if (t <= numComplete)
1576 return 0;
1578 static char tmp1[100];
1579 static LevelInfo tmp = {0, "", tmp1};
1580 sprintf(tmp1, "Complete %d more %s to unlock!", t-numComplete, t-numComplete==1?"level":"levels");
1581 return &tmp;
1584 for (unsigned int i=0; i<sizeof(levelNames)/sizeof(levelNames[0]); i++)
1585 if (strcmp(f, levelNames[i].file)==0)
1586 return &levelNames[i];
1587 static LevelInfo tmp = {0, "", "<<NO NAME>>"};
1588 return &tmp;
1591 #ifdef MAP_EDIT_HACKS
1592 int GetAutoTile(const char * level, bool tiletype)
1594 FILE* f = file_open(filename, "rb");
1595 int tile = EMPTY;
1596 int version;
1598 if (f && fscanf(f, "%d", &version)==1 && (version==3 || version==4))
1600 if (strstr(level,"mk"))
1601 level+=0;
1603 fgetc(f); // Remove '\n' character
1605 int32_t par, diff;
1606 unsigned char bounds[4];
1607 Pos playerStart;
1608 fread(&par, sizeof(par), 1, f);
1609 par = SWAP32(par);
1611 if (version >= 4) {
1612 fread(&diff, sizeof(diff), 1, f);
1613 diff = SWAP32(diff);
1615 fread(bounds, sizeof(bounds), 1, f);
1616 fread(&playerStart, sizeof(playerStart), 1, f);
1617 playerStart.x = SWAP32(playerStart.x);
1618 playerStart.y = SWAP32(playerStart.y);
1620 int highval=0;
1622 for (int i=bounds[0]; i<=bounds[1]; i++)
1623 for (int j=bounds[2]; j<=bounds[3]; j++)
1625 unsigned char comp = map[i][j] | (map_item[i][j]<<5);
1626 fread(&comp, sizeof(comp), 1, f);
1627 int t = comp & 0x1f;
1628 int item = (comp >> 5) & 3;
1629 for (int i=highval+1; i<sizeof(value_order)/sizeof(value_order[0]); i++)
1630 if (t!=0 && t==value_order[i]
1631 || item!=0 && item==(value_order[i]>>8))
1632 highval = i;
1635 if (tiletype)
1637 tile = value_order[highval];
1638 if (tile==0x100) tile = COLLAPSABLE3;
1639 if (tile==0x200) tile = SWITCH;
1640 if (tile==LIFT_UP) tile = LIFT_DOWN;
1642 else
1644 if (value_order[highval] == LIFT_UP)
1645 tile = highval-1;
1646 else
1647 tile = highval;
1650 else
1652 level+=0;
1654 if (f)
1655 fclose(f);
1656 return tile;
1658 #endif
1660 void InitSpecials()
1662 numComplete = numLevels = numMastered = numLevelsFound = 0;
1663 for (int i=0; i<MAP_SIZE; i++)
1664 for (int j=0; j<MAP_SIZE; j++)
1665 ActivateSpecial(Pos(i,j), 0);
1666 for (int i=0; i<MAP_SIZE; i++)
1667 for (int j=0; j<MAP_SIZE; j++)
1668 ActivateSpecial(Pos(i,j), 2);
1669 numComplete = numLevels = numMastered = numLevelsFound = 0;
1670 for (int i=0; i<MAP_SIZE; i++)
1671 for (int j=0; j<MAP_SIZE; j++)
1672 ActivateSpecial(Pos(i,j), 0);
1675 void DoHints()
1677 #ifndef EDIT
1678 if (strcmp(mapname, currentFile)==0)
1680 // for (int i=0; i<32; i++)
1681 // HintMessage::FlagTile(i);
1682 if (numComplete >= UNLOCK_SCORING && !progress.general.scoringOn)
1684 HintMessage::FlagTile(26);
1685 progress.general.scoringOn = 1;
1686 InitSpecials(); // Re-initialise with gold ones available
1688 HintMessage::FlagTile(25);
1690 else
1692 for (int i=0; i<MAP_SIZE; i++)
1693 for (int j=0; j<MAP_SIZE; j++)
1695 int t = GetTile(Pos(i,j));
1696 int item = GetItem(Pos(i,j));
1697 if (t)
1698 HintMessage::FlagTile(t);
1699 if (item)
1700 HintMessage::FlagTile(item+20);
1702 HintMessage::FlagTile(EMPTY);
1704 #endif
1705 hintsDone = true;
1707 void ResetLevel()
1709 hintsDone = false;
1711 UpdateCursor(Pos(-1,-1));
1713 isMap = false;
1715 player_score = 0;
1717 numUndo = 0;
1718 undoTime = -1;
1720 dead = false;
1721 win = false;
1722 winFinal = false;
1723 player_items[0] = player_items[1] = 0;
1724 // time = 0;
1725 if (strlen(currentSlot) == 0)
1727 new TitleMenu();
1728 new Fader(1, -3);
1730 else
1732 if (!isFadeRendering && time!=0)
1734 renderer().Add(new FadeRender(-1), time);
1735 time += 0.5;
1739 // Reset renderer
1740 renderer.Reset(time);
1741 renderer.Wipe();
1743 for (int t=0; t<NumTileTypes; t++)
1744 tileCount[t] = 0;
1746 for (int i=0; i<MAP_SIZE; i++)
1747 for (int j=0; j<MAP_SIZE; j++)
1749 Pos p(i,j);
1750 int item = GetItem(p);
1751 //if (item)
1752 renderer(p,true).Add(new ItemRender(item, GetTile(p)==EMPTY, p), time);
1755 InitSpecials();
1757 for (int i=0; i<MAP_SIZE; i++)
1758 for (int j=0; j<MAP_SIZE; j++)
1760 Pos p(i,j);
1761 int t = GetTile(p);
1762 tileCount[t]++;
1764 if (isMap)
1765 t = EMPTY;
1767 //if (t)
1768 renderer(p).Add(new TileRender(t, p), time);
1771 if (!isMap)
1772 renderer.player.Add(new PlayerRender(player, GetHeight(player), dead), time);
1773 else
1774 renderer.player.Add(new PlayerRender(Pos(-100,-100), 0, true), time);
1777 int bounds[4] = {player.getScreenX(),player.getScreenX(),player.getScreenY(),player.getScreenY()};
1778 for (int i=0; i<MAP_SIZE; i++)
1779 for (int j=0; j<MAP_SIZE; j++)
1781 Pos p(i,j);
1782 if (map[i][j] !=0 || map_item[i][j]!=0)
1784 int x1 = p.getScreenX();
1785 int y1 = p.getScreenY();
1786 int x2 = x1 + TILE_W3;
1787 int y2 = y1 + TILE_H2;
1788 y1 -= TILE_H2; // Make sure objects/player will be properly visible
1790 if (x1<bounds[0]) bounds[0] = x1;
1791 if (x2>bounds[1]) bounds[1] = x2;
1792 if (y1<bounds[2]) bounds[2] = y1;
1793 if (y2>bounds[3]) bounds[3] = y2;
1797 int sx, sy;
1798 if (isMap)
1800 sx = bounds[0] - int(TILE_W2*6.35);
1801 sy = (bounds[3] + bounds[2] - SCREEN_H) / 2 - TILE_H2/2;
1803 else
1805 sx = (bounds[1] + bounds[0] - SCREEN_W) / 2 - TILE_W3/2;
1806 sy = (bounds[3] + bounds[2] - SCREEN_H) / 2 - TILE_H2/2;
1808 if (isMap)
1810 initScrollX = sx;
1811 initScrollY = sy;
1812 if (mapScrollX==0)
1813 mapScrollX = sx;
1814 else
1815 sx = mapScrollX;
1818 // time = 1; // Guarantee we can't try and do things at time=0
1820 renderer().Add(new ScrollRender(sx, sy), time);
1821 renderer().Add(new FadeRender(1), time);
1822 if (time != 0)
1823 time -= 0.5;
1826 char* ReadAll(FILE* f)
1828 int size;
1829 // FIXME: According to http://userpage.fu-berlin.de/~ram/pub/pub_jf47ht20Ht/c_faq_de
1830 // undefined for binary streams! (POSIX does not differ between ascii and binary, so
1831 // we are on the safe side in Linux)
1832 fseek(f, 0, SEEK_END);
1833 size = ftell(f);
1834 fseek(f, 0, SEEK_SET);
1835 char* c = loadPtr = new char [size];
1836 endLoad = loadPtr + size;
1837 fread(c, 1, size, f);
1838 return c;
1841 static char *loadPtr, *endLoad;
1842 static unsigned int fread_replace(void* d, unsigned int size, unsigned int num, FILE*)
1844 unsigned int remain = (endLoad - loadPtr) / size;
1845 if (remain < num) num = remain;
1846 memcpy(d, loadPtr, size*num);
1847 loadPtr += size*num;
1848 return num;
1851 int GetPar(const char * level, bool getdiff=false)
1853 if (strcmp(level, currentFile)==0)
1854 return getdiff ? levelDiff : levelPar;
1856 #ifdef USE_LEVEL_PACKFILE
1857 PackFile1::Entry* e = levelFiles.Find(level);
1858 if (!e) return 999;
1859 loadPtr = (char*)e->Data();
1860 endLoad = loadPtr + e->DataLen();
1861 FILE* f = 0;
1862 #else
1863 loadPtr = 0;
1864 FILE* f = file_open(level, "rb");
1865 #endif
1867 typedef unsigned int _fn(void*, unsigned int, unsigned int, FILE*);
1868 _fn * fn = (loadPtr ? (_fn*)fread_replace : (_fn*)fread);
1870 int32_t par = 99999, diff = 0;
1871 int16_t version;
1873 if (!f && !loadPtr)
1874 return getdiff ? diff : par;
1876 fn(&version, 2, 1, f); // skip to relevant point
1878 if (fn(&par, sizeof(par), 1, f) != 1)
1879 par = 99999;
1880 else
1881 par = SWAP32(par);
1882 size_t ret = fn(&diff, sizeof(diff), 1, f);
1883 diff = SWAP32(diff);
1884 if (ret != 1 || diff<0 || diff>10)
1885 diff = 0;
1887 #ifdef USE_LEVEL_PACKFILE
1888 loadPtr = endLoad = 0;
1889 #else
1890 if (f)
1891 fclose(f);
1892 #endif
1894 return getdiff ? diff : par;
1897 bool LoadSave(const char * filename, bool save)
1899 if (!filename)
1900 return false;
1902 if (!save)
1904 showScoring = false;
1905 LevelSave* l = progress.GetLevel(filename, true);
1906 if (progress.general.scoringOn && l && l->Completed() )
1907 showScoring = true;
1910 #ifdef USE_LEVEL_PACKFILE
1911 if (!save)
1913 PackFile1::Entry* e = levelFiles.Find(filename);
1914 if (!e) return false;
1916 if (currentFile != filename) // equal (overlapping) strings are forbidden
1917 strcpy(currentFile, filename);
1918 currentLevelInfo = GetLevelInfo(currentFile);
1920 loadPtr = (char*)e->Data();
1921 endLoad = loadPtr + e->DataLen();
1922 _LoadSave(NULL, save);
1923 loadPtr = endLoad = 0;
1925 return true;
1927 #else
1928 loadPtr = 0;
1929 FILE* f = file_open(filename, save ? "wb" : "rb");
1930 if (f)
1932 strcpy(currentFile, filename);
1933 if (!save)
1934 currentLevelInfo = GetLevelInfo(currentFile);
1936 if (!save)
1938 char* data = ReadAll(f);
1939 _LoadSave(f, save);
1940 delete [] data;
1941 loadPtr = endLoad = 0;
1943 else
1945 _LoadSave(f, save);
1947 fclose(f);
1949 return true;
1951 #endif
1953 return false;
1956 /** \brief Writes/reads game status to/from a file
1958 * The game data file is written in little endian so it can be shared
1959 * across different machines.
1961 void _LoadSave(FILE* f, bool save)
1963 typedef unsigned int _fn(void*, unsigned int, unsigned int, FILE*);
1964 _fn * fn = save ? (_fn*)fwrite : (loadPtr ? (_fn*)fread_replace : (_fn*)fread);
1966 #define VERSION 4
1967 int version = VERSION; // 1--9
1968 if (save)
1969 fprintf(f, "%d\n", version);
1970 else
1972 char c;
1973 if (fn(&c, 1, 1, f) != 1)
1974 return;
1975 version = c-'0';
1977 // Remove '\n' character
1978 fn(&c, 1, 1, f);
1981 if (!save)
1983 for (int i=0; i<MAP_SIZE; i++)
1984 for (int j=0; j<MAP_SIZE; j++)
1986 delete [] special[i][j];
1987 special[i][j] = 0;
1991 if (version==1)
1993 for (int i=0; i<MAP_SIZE; i++)
1994 for (int j=0; j<MAP_SIZE; j++) {
1995 map[i][j] = SWAP32(map[i][j]);
1996 fn(&map[i][j], sizeof(map[i][j]), 1, f);
1997 map[i][j] = SWAP32(map[i][j]);
2000 player.x = SWAP32(player.x);
2001 player.y = SWAP32(player.y);
2002 fn(&player, sizeof(player), 1, f);
2003 player.x = SWAP32(player.x);
2004 player.y = SWAP32(player.y);
2006 for (int i=0; i<MAP_SIZE; ++i)
2007 for (int j=0; j<MAP_SIZE; ++j)
2008 map_item[i][j] = SWAP32(map_item[i][j]);
2009 if (fn(map_item, sizeof(map_item), 1, f) == 0)
2010 memset(map_item, 0, sizeof(map_item));
2011 for (int i=0; i<MAP_SIZE; ++i)
2012 for (int j=0; j<MAP_SIZE; ++j)
2013 map_item[i][j] = SWAP32(map_item[i][j]);
2015 else if (version>=2 && version<=4)
2017 unsigned char bounds[4];
2018 if (save)
2020 bounds[0]=bounds[1]=player.x;
2021 bounds[2]=bounds[3]=player.y;
2022 for (int i=0; i<MAP_SIZE; i++)
2023 for (int j=0; j<MAP_SIZE; j++)
2024 if (map[i][j] !=0 || map_item[i][j]!=0 || special[i][j]!=0)
2026 if (i<bounds[0]) bounds[0] = i;
2027 if (i>bounds[1]) bounds[1] = i;
2028 if (j<bounds[2]) bounds[2] = j;
2029 if (j>bounds[3]) bounds[3] = j;
2032 else
2034 memset(map, 0, sizeof(map));
2035 memset(map_item, 0, sizeof(map_item));
2038 if (version>=3) {
2039 levelPar = SWAP32(levelPar);
2040 fn(&levelPar, 1, sizeof(levelPar), f);
2041 levelPar = SWAP32(levelPar);
2043 else if (!save)
2044 levelPar = 0;
2046 if (version>=4) {
2047 levelDiff = SWAP32(levelDiff);
2048 fn(&levelDiff, 1, sizeof(levelDiff), f);
2049 levelDiff = SWAP32(levelDiff);
2051 else if (!save)
2052 levelDiff = 0;
2054 fn(bounds, sizeof(bounds), 1, f);
2055 player.x = SWAP32(player.x);
2056 player.y = SWAP32(player.y);
2057 fn(&player, sizeof(player), 1, f);
2058 player.x = SWAP32(player.x);
2059 player.y = SWAP32(player.y);
2061 int offsetx=0, offsety=0;
2063 if (!save && bounds[1]-bounds[0]<15) // Hacky - don't recenter map...
2065 // Re-position map to top left (but leave a bit of space)
2066 // (This ensures the laser/boat effects don't clip prematurely against the edges of the screen)
2067 offsetx = SCREEN_W/2/TILE_W2 + 1 - (bounds[0]+bounds[1]/2);
2068 offsety = SCREEN_H/2/TILE_H2 + SCREEN_W/2/TILE_W2 - (bounds[2]+bounds[3]/2);
2069 offsetx = MAX(0, offsetx);
2070 offsety = MAX(0, offsety);
2071 // if (bounds[0] > 2)
2072 // offsetx = 2 - bounds[0];
2073 // if (bounds[2] > 2)
2074 // offsety = 2 - bounds[2];
2076 bounds[0] += offsetx;
2077 bounds[1] += offsetx;
2078 bounds[2] += offsety;
2079 bounds[3] += offsety;
2080 player.x += offsetx;
2081 player.y += offsety;
2083 for (int i=bounds[0]; i<=bounds[1]; i++)
2084 for (int j=bounds[2]; j<=bounds[3]; j++)
2086 unsigned char comp = map[i][j] | (map_item[i][j]<<5);
2087 fn(&comp, sizeof(comp), 1, f);
2088 map[i][j] = comp & 0x1f;
2089 map_item[i][j] = (comp >> 5) & 3;
2092 if (save)
2094 for (int i=bounds[0]; i<=bounds[1]; i++)
2095 for (int j=bounds[2]; j<=bounds[3]; j++)
2096 if (special[i][j])
2098 int16_t len = strlen(special[i][j]);
2099 unsigned char x=i, y=j;
2100 fn(&x, sizeof(x), 1, f);
2101 fn(&y, sizeof(y), 1, f);
2102 len = SWAP16(len);
2103 fn(&len, sizeof(len), 1, f);
2104 len = SWAP16(len);
2105 fn(special[i][j], 1, len, f);
2108 else
2110 while(1){
2111 int16_t len;
2112 unsigned char x, y;
2113 if (!fn(&x, sizeof(x), 1, f))
2114 break;
2115 fn(&y, sizeof(y), 1, f);
2116 x += offsetx; y += offsety;
2117 fn(&len, sizeof(len), 1, f);
2118 len = SWAP16(len);
2119 if (len<0) break;
2120 char* tmp = new char[len+1];
2121 tmp[len] = 0;
2122 fn(tmp, 1, len, f);
2124 SetSpecial(Pos(x,y), tmp, true, false);
2128 else
2129 return; // Unsupported version!
2131 ResetLevel();
2133 // Save when returning to map!
2134 if (isMap)
2136 progress.general.completionPercentage = numComplete*100/numLevels;
2137 progress.general.masteredPercentage = numMastered*100/numLevels;
2138 LoadSaveProgress(true);
2142 void SetTile(Pos const & p, Tile t, bool updateRenderer=true, bool undoBuffer=true)
2144 if (p.x<0 || p.x>MAP_SIZE)
2145 return;
2146 if (p.y<0 || p.y>MAP_SIZE)
2147 return;
2148 if (map[p.x][p.y] == t)
2149 return;
2150 if (map[p.x][p.y] == t)
2151 return;
2153 tileCount[map[p.x][p.y]]--;
2154 tileCount[t]++;
2156 if (undoBuffer)
2157 undo[numUndo].Add(Undo::TileChange(p,GetTile(p),GetItem(p)));
2159 map[p.x][p.y] = t;
2161 if (updateRenderer)
2162 renderer(p).Add(new TileRender(t, p), time);
2165 Tile GetTile(Pos const & p)
2167 if (p.x<0 || p.x>=MAP_SIZE)
2168 return EMPTY;
2169 if (p.y<0 || p.y>=MAP_SIZE)
2170 return EMPTY;
2171 return map[p.x][p.y];
2174 int GetHeight(Pos const & p)
2176 return tileSolid[GetTile(p)]==1;
2179 char* GetSpecial(Pos const & p)
2181 if (p.x<0 || p.x>=MAP_SIZE)
2182 return NULL;
2183 if (p.y<0 || p.y>=MAP_SIZE)
2184 return NULL;
2185 return special[p.x][p.y];
2188 void SetSpecial(Pos const & p, char * d, bool use_pointer=false, bool auto_activate=true)
2190 if (p.x<0 || p.x>=MAP_SIZE || p.y<0 || p.y>=MAP_SIZE)
2192 if (use_pointer)
2193 delete [] d;
2194 return;
2197 delete [] special[p.x][p.y];
2198 if (!use_pointer && d)
2201 special[p.x][p.y] = new char [strlen(d) + 1];
2202 strcpy(special[p.x][p.y], d);
2204 else
2205 special[p.x][p.y] = d;
2207 if (special[p.x][p.y]==0)
2208 renderer(p,true).Add(new ItemRender(GetItem(p), GetTile(p)==EMPTY, p), time);
2209 else if (auto_activate)
2210 ActivateSpecial(p, 0);
2213 int GetLevelState(Pos const & p, int recurse=0)
2215 char* x = GetSpecial(p);
2216 if (!x) return 0;
2218 LevelSave* l = progress.GetLevel(x, false);
2220 int t = 1;
2222 if (strcmp(x, STARTING_LEVEL)==0)
2223 t = 2;
2224 if (x[0]=='_' && l && l->unlocked)
2225 t=3;
2227 if (l && l->Completed())
2229 t = 3;
2231 if (recurse)
2232 return t;
2234 int par = GetPar(x);
2235 if (progress.general.scoringOn && l->PassesPar( par ))
2236 t = 4;
2238 if (recurse)
2239 return t;
2241 int adj=0;
2242 for (Dir d=0; d<MAX_DIR; d++)
2244 int i = GetLevelState(p+d, 1);
2245 // if (i>1 || i==1 && t>1)
2246 if (i>=1 && t>2 || t>=1 && i>2)
2248 adj |= 1<<d;
2249 if (t==1)
2250 t = 2;
2254 return t | adj<<8;
2257 void ActivateSpecial(Pos const & p, int type)
2259 if (p.x<0 || p.x>=MAP_SIZE || p.y<0 || p.y>=MAP_SIZE)
2260 return;
2262 char * x = special[p.x][p.y];
2264 if (x==0 || x[0]==0)
2265 return;
2267 if (type==2 && x[0]=='_') // Phase2 init - unlock
2269 int t = GetLevelState(p);
2270 int target = atoi(x+1), targetM = 0;
2271 if (target>1000) targetM=target=target-100;
2272 if (t > 1 && numComplete >= target && numMastered >= targetM)
2274 LevelSave* l = progress.GetLevel(x, true);
2275 if (!l->unlocked)
2277 l->unlocked = true;
2279 renderer(p, true).Add(new LevelSelectRender(p, 5, GetLevelState(p)>>8), time+0.01);
2280 renderer().Add(new ExplosionRender(p, 0), time + 0.6);
2281 renderer().Add(new ExplosionRender(p, 1), time + 1.1);
2282 renderer(p, true).Add(new LevelSelectRender(p, -1, GetLevelState(p)>>8), time + 1.1);
2287 if (type==0) // Init & count levels
2289 if (x[0]=='_')
2291 int t = GetLevelState(p);
2292 int unlock = progress.GetLevel(x, true)->unlocked;
2293 LevelSelectRender* lsr = new LevelSelectRender( p, unlock ? -1 : (t>>8) ? 5 : 1, t>>8 );
2294 if ((t>>8) && p.x > mapRightBound) mapRightBound = p.x;
2295 #ifdef MAP_EDIT_HACKS
2296 lsr->magic = -atoi(x+1);
2297 SetTile(p, LIFT_DOWN, true, false);
2298 #else
2299 SetTile(p, EMPTY, true, false);
2300 #endif
2301 renderer(p,true).Add(lsr, time);
2303 else
2305 //printf("Level: %s\n", x);
2307 int t = GetLevelState(p);
2308 numLevels++;
2309 if (t && !GetItem(p))
2311 if (!isMap)
2313 isMap = true;
2314 mapRightBound = 0;
2316 currentLevelInfo = 0;
2318 if ((t&0xff)>=2)
2320 LevelSave* l = progress.GetLevel(x, true);
2321 if (!l->unlocked)
2323 l->unlocked = true;
2325 renderer(p, true).Add(new LevelSelectRender(p, -1, 0), time+0.01);
2326 renderer().Add(new ExplosionRender(p, 0), time + 0.6);
2327 renderer(p, true).Add(new LevelSelectRender(p, t & 0xff, t>>8), time + 0.6);
2330 numLevelsFound++;
2331 if (p.x > mapRightBound) mapRightBound = p.x;
2333 if ((t&0xff)>=3)
2334 numComplete++;
2335 if ((t&0xff)>=4)
2336 numMastered++;
2338 LevelSelectRender* lsr = new LevelSelectRender( p, t & 0xff, t>>8 );
2340 #ifdef MAP_EDIT_HACKS
2341 lsr->magic = 0;
2342 int t = GetAutoTile(x, true);
2343 int v = GetAutoTile(x, false);
2344 if (MAP_EDIT_HACKS_DISPLAY_UNLOCK)
2345 lsr->magic = v;
2346 else
2347 lsr->magic = GetPar(x, true);
2348 t = 1;
2349 SetTile(p, t, true, false);
2350 #else
2351 SetTile(p, EMPTY, true, false);
2352 #endif
2354 renderer(p,true).Add(lsr, time);
2359 if (type==1 && x[0]!='_') // Clicked on
2361 int t = GetLevelState(p);
2362 if (t>1)
2364 LoadSave(x, false);
2369 void SetItem(Pos const & p, int t, bool updateRenderer=true, bool undoBuffer=true)
2371 if (p.x<0 || p.x>MAP_SIZE)
2372 return;
2373 if (p.y<0 || p.y>MAP_SIZE)
2374 return;
2375 if (map_item[p.x][p.y] == t)
2376 return;
2378 if (undoBuffer)
2379 undo[numUndo].Add(Undo::TileChange(p,GetTile(p),GetItem(p)));
2381 map_item[p.x][p.y] = t;
2383 if (updateRenderer)
2384 renderer(p,true).Add(new ItemRender(t, GetTile(p)==EMPTY, p), time);
2387 Tile GetItem(Pos const & p)
2389 if (p.x<0 || p.x>=MAP_SIZE)
2390 return EMPTY;
2391 if (p.y<0 || p.y>=MAP_SIZE)
2392 return EMPTY;
2393 return map_item[p.x][p.y];
2396 void LoadSaveProgress(bool save)
2398 FILE* f = file_open(currentSlot, save ? "wb" : "rb");
2399 if (f)
2401 progress.LoadSave(f, save);
2402 fclose(f);
2404 else
2406 if (!save)
2407 progress.Clear();
2410 void LoadProgress()
2412 LoadSaveProgress(false);
2414 void SaveProgress()
2416 LoadSaveProgress(true);
2419 SDL_Surface* Load(const char * bmp, bool colourKey=true)
2421 typedef unsigned int uint32;
2422 uint32* tmp = 0;
2424 SDL_Surface * g = 0;
2426 #ifdef EDIT
2427 if (strstr(bmp, ".bmp"))
2429 g = SDL_LoadBMP(bmp);
2431 char out[1024];
2432 strcpy(out, bmp);
2433 strcpy(strstr(out, ".bmp"), ".dat");
2435 // SDL_PixelFormat p;
2436 // p.sf = 1;
2437 // SDL_Surface* tmp = SDL_ConvertSurface(g, &p, SDL_SWSURFACE);
2439 short w=g->w, h=g->h;
2440 char* buf = (char*) g->pixels;
2441 if (colourKey)
2443 while (IsEmpty(g, w-1, 0, 1, h) && w>1)
2444 w--;
2445 while (IsEmpty(g, 0, h-1, w, 1) && h>1)
2446 h--;
2449 FILE* f = file_open(out, "wb");
2450 fwrite(&w, sizeof(w), 1, f);
2451 fwrite(&h, sizeof(h), 1, f);
2453 uint32 mask = IMAGE_DAT_OR_MASK;
2454 for (int i=0; i<(int)w*h; )
2456 uint32 c = (*(uint32*)&buf[(i%w)*3 + (i/w)*g->pitch] | mask);
2457 int i0 = i;
2458 while (i < (int)w*h && c == (*(uint32*)&buf[(i%w)*3 + (i/w)*g->pitch] | mask))
2459 i++;
2460 c &= 0xffffff;
2461 i0 = i-i0-1;
2462 if (i0 < 0xff)
2463 c |= i0 << 24;
2464 else
2465 c |= 0xff000000;
2467 fwrite(&c, sizeof(c), 1, f);
2469 if (i0 >= 0xff)
2470 fwrite(&i0, sizeof(i0), 1, f);
2472 fclose(f);
2474 SDL_FreeSurface(g);
2476 bmp = out;
2478 #endif
2480 FILE* f = file_open(bmp, "rb");
2481 if (!f) FATAL("Unable to open file", bmp);
2483 int16_t w,h;
2484 fread(&w, sizeof(w), 1, f);
2485 fread(&h, sizeof(h), 1, f);
2486 w = SWAP16(w);
2487 h = SWAP16(h);
2488 if (w>1500 || h>1500 || w<=0 || h<=0) FATAL("Invalid file", bmp);
2490 tmp = new uint32[(int)w*h];
2492 uint32 c = 0;
2493 uint32 cnt = 0;
2494 for (int p=0; p<(int)w*h; p++)
2496 if (cnt)
2497 cnt -= 0x1;
2498 else
2500 fread(&c, sizeof(c), 1, f);
2501 c = SWAP32(c);
2502 cnt = c >> 24;
2503 if (cnt==255) {
2504 fread(&cnt, sizeof(cnt), 1, f);
2505 cnt = SWAP32(cnt);
2508 tmp[p] = c | 0xff000000;
2511 g = SDL_CreateRGBSurfaceFrom(tmp, w, h, 32, w*4,
2512 0xff0000,
2513 0xff00,
2514 0xff,
2515 0xff000000 );
2517 fclose(f);
2520 if (!g) FATAL("Unable to create SDL surface");
2521 if (colourKey)
2522 SDL_SetColorKey(g, SDL_SRCCOLORKEY, SDL_MapRGB(g->format, WATER_COLOUR));
2523 SDL_Surface * out = SDL_DisplayFormat(g);
2524 SDL_FreeSurface(g);
2525 delete [] tmp;
2526 if (!out) FATAL("Unable to create SDL surface (SDL_DisplayFormat)");
2527 return out;
2530 #ifdef USE_LEVEL_PACKFILE
2531 PackFile1 levelFiles;
2532 #endif
2533 HexPuzzle()
2535 SDL_WM_SetCaption(GAMENAME, 0);
2537 time = 0;
2539 #ifdef USE_LEVEL_PACKFILE
2540 FILE* f = file_open("levels.dat", "rb");
2541 if (!f)
2542 FATAL("Unable to open file", "levels.dat");
2543 levelFiles.Read(f);
2544 fclose(f);
2545 #endif
2547 LoadGraphics();
2549 isMap = false;
2550 editMode = false;
2552 currentLevelInfo = 0;
2554 editTile = 0;
2555 levelPar = 0;
2556 levelDiff = 5;
2557 turboAnim = 0;
2559 memset(map, 0, sizeof(map));
2560 memset(map_item, 0, sizeof(map_item));
2561 memset(special, 0, sizeof(special));
2563 LoadProgress();
2565 // player = Pos(1,11);
2567 // ResetLevel();
2569 LoadMap();
2572 void LoadMap()
2574 #ifndef EDIT
2575 progress.GetLevel(STARTING_LEVEL, true)->unlocked = 1;
2576 if (!progress.GetLevel(STARTING_LEVEL, true)->Completed())
2578 LoadSave(STARTING_LEVEL, false);
2579 return;
2581 #endif
2583 //editMode = false;
2584 LoadSave(mapname, false);
2587 void Render()
2589 if (!activeMenu || activeMenu->renderBG)
2591 SDL_Rect src = {0,0,screen->w,screen->h};
2592 SDL_Rect dst = {0,0,screen->w,screen->h};
2593 if (isRenderMap)
2595 int boundW = mapBG->w;
2596 #ifndef EDIT
2597 boundW = MIN(boundW, (mapRightBound+4) * TILE_W2 - TILE_W1);
2598 #endif
2599 src.x = scrollX - initScrollX;
2600 if (src.x+src.w > boundW)
2602 int diff = src.x+src.w - boundW;
2603 src.x -= diff;
2604 if (isMap)
2605 scrollX -= diff;
2607 if (src.x < 0)
2609 if (isMap)
2610 scrollX -= src.x;
2611 src.x = 0;
2613 //scrollY = initScrollY;
2615 if (isMap)
2616 mapScrollX = scrollX;
2618 SDL_BlitSurface(mapBG, &src, screen, &dst);
2620 else
2621 SDL_BlitSurface(gradient, &src, screen, &dst);
2623 renderer.Render(time, true);
2625 if (!hintsDone && !isFadeRendering)
2627 DoHints();
2630 if (1)
2632 SDL_Rect src = {0,SCREEN_H-1,SCREEN_W,1};
2633 SDL_Rect dst = {0,SCREEN_H-1,SCREEN_W,1};
2634 for (int i=0; i<SCREEN_H; i++)
2636 dst.x = src.x = 0;
2637 dst.y = src.y = SCREEN_H-1-i;
2638 src.w = SCREEN_W;
2639 src.h = 1;
2641 if (isRenderMap)
2643 src.x += (int)( sin(i*0.9 + time*3.7) * sin(i*0.3 + time*0.7)*4 );
2644 src.y += (int)( (sin(i*0.3 - time*2.2) * sin(i*0.48 + time*0.47) - 1) * 1.99 );
2646 else
2648 src.x += (int)( sin(i*0.5 + time*6.2) * sin(i*0.3 + time*1.05) * 5 );
2649 src.y += (int)( (sin(i*0.4 - time*4.3) * sin(i*0.08 + time*1.9) - 1) * 2.5 );
2651 SDL_BlitSurface(screen, &src, screen, &dst);
2655 if(isRenderMap)
2656 SDL_BlitSurface(mapBG2, &src, screen, &dst);
2658 renderer.Render(time, false);
2660 if (!isRenderMap && !isMap && !isFadeRendering)
2662 int v[3] = {player_items[0], player_items[1], player_score};
2663 if (numUndo > 1 && time < undo[numUndo-2].endTime)
2665 int i = numUndo-1;
2666 while (i>1 && time<undo[i-1].time)
2667 i--;
2668 v[0] = undo[i].numItems[0];
2669 v[1] = undo[i].numItems[1];
2670 v[2] = undo[i].score;
2672 if (numUndo>1 && time < undo[0].time)
2673 v[0]=v[1]=v[2]=0;
2674 #ifdef EDIT
2675 Print(0,0,"Anti-Ice: %d", v[0]);
2676 Print(0,FONT_SPACING,"Jumps: %d", v[1]);
2677 Print(0,FONT_SPACING*2,"Score: %d (%d)", v[2], player_score);
2678 Print(0,FONT_SPACING*3,"Par: %d", levelPar);
2679 Print(0,FONT_SPACING*4,"Diff: %d", levelDiff);
2680 #else
2681 if (showScoring)
2682 Print(0, SCREEN_H-FONT_SPACING, " Par: %d Current: %d", levelPar, v[2]);
2684 if (v[0])
2685 Print(0,0," Anti-Ice: %d", v[0]);
2686 else if (v[1])
2687 Print(0,0," Jumps: %d", v[1]);
2688 #endif
2690 if (isRenderMap && isMap && !isFadeRendering)
2692 #if 0//def EDIT
2693 Print(0,0,"Points: %d", numComplete+numMastered);
2694 Print(0,FONT_SPACING,"Discovered: %d%% (%d/%d)", numLevelsFound*100/numLevels, numLevelsFound, numLevels);
2695 Print(0,FONT_SPACING*2,"Complete: %d%% (%d)", numComplete*100/numLevels, numComplete);
2696 Print(0,FONT_SPACING*3,"Mastered: %d%% (%d)", numMastered*100/numLevels, numMastered);
2697 #else
2698 if (numComplete==numLevels && progress.general.endSequence>0)
2699 Print(0, SCREEN_H-FONT_SPACING, " %d%% Mastered", numMastered*100/numLevels);
2700 else
2701 Print(0, SCREEN_H-FONT_SPACING, " %d%% Complete", numComplete*100/numLevels);
2703 if (numMastered >= numLevels && progress.general.endSequence < 2)
2705 progress.general.endSequence = 2;
2706 LoadSaveProgress(true);
2708 new Fader(-1, -7, 0.3);
2710 if (numComplete >= numLevels && progress.general.endSequence < 1)
2712 progress.general.endSequence = 1;
2713 LoadSaveProgress(true);
2715 new Fader(-1, -5, 0.3);
2717 #endif
2719 if ((currentLevelInfo || noMouse) && isMap && isRenderMap && !activeMenu && isFadeRendering<=0)
2721 Pos p;
2722 if (noMouse)
2723 p = keyboardp;
2724 else
2725 p = mousep;
2726 int pad = SCREEN_W/80;
2727 // SDL_Rect src = {0, 0, uiGraphics->w, uiGraphics->h};
2728 SDL_Rect dst = {pad, SCREEN_H-TILE_H2-pad, 0, 0};
2729 // dst.x = p.getScreenX() + TILE_W3/2 - scrollX;
2730 // dst.y = p.getScreenY() - src.h/2 - scrollY;
2731 dst.x = p.getScreenX() - scrollX;
2732 dst.y = p.getScreenY() - scrollY - FONT_SPACING*3 - FONT_SPACING/2;
2733 // if (dst.x > SCREEN_W*2/3) dst.x -= TILE_W3 + src.w;
2734 // if (dst.y+src.h > screen->h-pad) dst.y = screen->h-pad - src.h;
2736 RenderTile(false, 0, p.getScreenX(), p.getScreenY());
2737 // SDL_BlitSurface(uiGraphics, &src, screen, &dst);
2739 // dst.x += src.w/2;
2741 if (currentLevelInfo)
2743 keyboardp = p;
2745 PrintC(true, dst.x, dst.y - FONT_SPACING/4, currentLevelInfo->name);
2747 if (currentLevelInfo->file[0]!=0)
2749 if (player_score > 0)
2751 if (progress.general.scoringOn)
2753 PrintC(false, dst.x, dst.y + FONT_SPACING*4 - FONT_SPACING/4, "Best:% 3d", player_score);
2754 PrintC(false, dst.x, dst.y + FONT_SPACING*5 - FONT_SPACING/4, "Par:% 3d", levelPar);
2756 else
2757 PrintC(false, dst.x, dst.y + FONT_SPACING*4 - FONT_SPACING/4, "Completed", player_score);
2759 else
2760 PrintC(false, dst.x, dst.y + FONT_SPACING*4 - FONT_SPACING/4, "Incomplete", player_score);
2765 // "Win"
2766 if (win && numUndo > 0 && time > undo[numUndo-1].endTime + 2)
2768 if (currentFile[0] && winFinal==0)
2770 LevelSave* l = progress.GetLevel(currentFile, true);
2772 new WinLoseScreen(true, player_score, showScoring ? levelPar : 0, l && showScoring && l->Completed() ? l->GetScore() : 0);
2774 if (l->IsNewCompletionBetter(player_score))
2776 l->SetScore(player_score);
2778 l->SetSolution(numUndo);
2780 for (int i=0; i<numUndo; i++)
2781 l->SetSolutionStep(i, undo[i].playerMovement);
2784 SaveProgress();
2787 winFinal = 1;
2789 else
2790 winFinal = 0;
2792 // Move up "level complete" writing so it doesn't feel like everything's ground to a halt...
2793 if (win && numUndo > 0 && time > undo[numUndo-1].endTime && !winFinal)
2795 double t = (time - undo[numUndo-1].endTime) / 2;
2796 t=1-t;
2797 t*=t*t;
2798 t=1-t;
2799 int y = SCREEN_H/3 - FONT_SPACING + 1;
2800 y = SCREEN_H + int((y-SCREEN_H)*t);
2801 PrintC(true, SCREEN_W/2, y, "Level Complete!");
2805 if (activeMenu)
2806 activeMenu->Render();
2808 if (!noMouse)
2810 // Edit cursor
2811 if (editMode)
2813 RenderTile(false, editTile, mousex+scrollX, mousey+scrollY);
2815 else
2817 Print(mousex, mousey-2, "\x7f");
2822 int Count(Tile t)
2824 return tileCount[t];
2826 int Swap(Tile t, Tile t2)
2828 const int num = Count(t) + Count(t2);
2829 if (t==t2 || num==0)
2830 return Count(t); // Nothing to do...
2832 int count=0;
2833 for (int x=0; x<MAP_SIZE; x++)
2834 for (int y=0; y<MAP_SIZE; y++)
2836 if (GetTile(Pos(x,y))==t)
2838 count++;
2839 SetTile(Pos(x,y), t2);
2841 else if (GetTile(Pos(x,y))==t2)
2843 count++;
2844 SetTile(Pos(x,y), t);
2846 if (count==num)
2847 return count;
2849 return count;
2851 int Replace(Tile t, Tile t2)
2853 const int num = Count(t);
2854 if (t==t2 || num==0)
2855 return num; // Nothing to do...
2857 int count=0;
2858 for (int x=0; x<MAP_SIZE; x++)
2859 for (int y=0; y<MAP_SIZE; y++)
2861 Pos p(x,y);
2862 if (GetTile(p)==t)
2864 count++;
2866 SetTile(p, t2, false);
2868 if (t==COLLAPSE_DOOR && t2==COLLAPSABLE)
2869 renderer(p).Add(new BuildRender(p, -1, 1, 1), time + (rand() & 255)*0.001);
2870 else if (t==COLLAPSE_DOOR2 && t2==COLLAPSABLE2)
2871 renderer(p).Add(new BuildRender(p, -1, 1, 1, 1), time + (rand() & 255)*0.001);
2872 else
2873 SetTile(p, t2);
2875 if (count==num)
2876 return count;
2879 return count;
2882 Tile editTile;
2883 bool editMode;
2884 void ResetUndo()
2886 UndoDone();
2887 undoTime = -1;
2888 numUndo = 0;
2889 win = false;
2892 void UpdateCursor(Pos const & s)
2894 static Pos _s;
2895 if (s.x!=_s.x || s.y!=_s.y)
2897 _s = s;
2899 char* sp = GetSpecial(s);
2900 char tmp[1000];
2901 tmp[0]='\0';
2902 if (sp)
2904 if (isMap)
2906 currentLevelInfo = 0;
2907 levelPar = player_score = -1;
2908 if (GetLevelState(s)>=2)
2910 LevelSave* l = progress.GetLevel(sp, true);
2911 if (l)
2913 currentLevelInfo = GetLevelInfo(sp);
2914 levelPar = GetPar(sp);
2915 player_score = l->GetScore();
2920 #ifdef EDIT
2921 sprintf(tmp, "Special(%d,%d): %s (%d)", s.x, s.y, sp ? sp : "<None>", GetPar(sp));
2922 SDL_WM_SetCaption(tmp, NULL);
2923 #endif
2925 else if (currentFile[0])
2927 #ifdef EDIT
2928 SDL_WM_SetCaption(currentFile, NULL);
2929 #endif
2930 if (isMap)
2931 currentLevelInfo = 0;
2936 virtual void Mouse(int x, int y, int dx, int dy, int button_pressed, int button_released, int button_held)
2938 if (activeMenu)
2940 activeMenu->Mouse(x,y,dx,dy,button_pressed,button_released,button_held);
2941 return;
2944 if (isFadeRendering)
2945 return;
2948 #ifndef EDIT
2949 if (button_pressed==2 || button_pressed==4 && isMap)
2951 KeyPressed(SDLK_ESCAPE, 0);
2952 keyState[SDLK_ESCAPE] = 0;
2953 return;
2955 #endif
2957 x += scrollX;
2958 y += scrollY;
2960 Pos s = Pos::GetFromWorld(x,y);
2961 if (tileSolid[GetTile(Pos::GetFromWorld(x,y+TILE_HUP))] == 1)
2962 s = Pos::GetFromWorld(x,y+TILE_HUP);
2964 mousep = s;
2966 UpdateCursor(s);
2968 #ifdef EDIT
2969 if (button_held & ~button_pressed & 4)
2971 scrollX -= dx;
2972 scrollY -= dy;
2974 #endif
2976 if (!editMode)
2978 if (isMap && (button_pressed & 1))
2980 ActivateSpecial(s, 1);
2981 return;
2983 if (!isMap && win && winFinal)
2985 if (button_pressed & 1)
2987 LoadMap();
2988 return;
2991 if(!isMap)
2993 if((button_pressed & 1) || (button_held & 1) && (numUndo==0 || time>=undo[numUndo-1].endTime))
2995 if(s.x==player.x && s.y==player.y)
2997 // Don't activate jump powerup without a new click
2998 if (button_pressed & 1)
2999 Input(-1);
3001 else if(s.x==player.x && s.y<player.y)
3002 Input(0);
3003 else if(s.x==player.x && s.y>player.y)
3004 Input(3);
3005 else if(s.y==player.y && s.x<player.x)
3006 Input(5);
3007 else if(s.y==player.y && s.x>player.x)
3008 Input(2);
3009 else if(s.y+s.x==player.y+player.x && s.x>player.x)
3010 Input(1);
3011 else if(s.y+s.x==player.y+player.x && s.x<player.x)
3012 Input(4);
3014 if ((button_pressed & 4) || (button_held & 4) && (undoTime < 0))
3015 Undo();
3017 return;
3020 #ifdef EDIT
3021 if (!button_pressed && !button_held)
3022 return;
3024 if (button_pressed==1)
3025 if (editTile<0)
3026 editTile = GetItem(s)==1 ? -3 : GetItem(s)==2 ? -2 : -1;
3028 if (button_held==1 || button_pressed==1)
3030 ResetUndo();
3031 if (editTile>=0)
3032 SetTile(s, editTile, true, false);
3033 else
3034 SetItem(s, editTile==-2 ? 0 : editTile==-1 ? 1 : 2, true, false);
3037 if (button_pressed==2)
3039 editTile = GetTile(s);
3042 if (button_pressed==8)
3044 editTile=editTile-1;
3045 if (editTile<=0) editTile=NumTileTypes-1;
3048 if (button_pressed==16)
3050 editTile=editTile+1;
3051 if (editTile<=0) editTile=1;
3052 if (editTile==NumTileTypes) editTile=0;
3055 if (button_pressed==64)
3057 ResetUndo();
3058 player = s;
3059 dead = false;
3060 renderer.player.Reset(-1);
3061 renderer.player.Add(new PlayerRender(player, GetHeight(player), dead), 0);
3064 if (button_pressed==256)
3066 char* fn = LoadSaveDialog(false, true, "Select level");
3067 if (fn)
3069 char * l = strstr(fn, "Levels");
3070 if(l)
3072 FILE * f = file_open(l,"rb");
3073 if (f)
3074 fclose(f);
3075 if (f)
3076 SetSpecial(s, l);
3077 else if (l[6]!=0 && l[7]=='_')
3078 SetSpecial(s, l+7);
3080 UpdateCursor(Pos(-1,-1));
3083 if (button_pressed==512)
3085 SetSpecial(s, NULL);
3086 UpdateCursor(Pos(-1,-1));
3088 if (button_pressed==1024)
3090 static char x[1000] = "";
3091 if (!(s.x<0 || s.x>=MAP_SIZE || s.y<0 || s.y>=MAP_SIZE))
3093 char tmp[1000];
3094 strcpy(tmp, x);
3095 if (GetSpecial(s))
3096 strcpy(x, GetSpecial(s));
3097 else
3098 x[0] = 0;
3099 SetSpecial(s, tmp[0] ? tmp : 0);
3100 if (!tmp[0])
3101 SetTile(s, EMPTY, true, false);
3105 if (button_pressed==32)
3107 editTile = editTile<0 ? 1 : -1;
3109 #endif // EDIT
3112 void CheckFinished()
3114 bool slow = false;
3115 if (Count(COLLAPSABLE)==0)
3117 if (Replace(COLLAPSE_DOOR, COLLAPSABLE) == 0)
3118 win = true;
3119 else
3120 slow = true;
3121 Replace(SWITCH, NORMAL);
3123 else
3124 win = false;
3126 if (Count(COLLAPSABLE2)==0)
3127 if (Replace(COLLAPSE_DOOR2, COLLAPSABLE2))
3128 slow = true;
3130 if (slow)
3131 time += BUILD_TIME;
3133 bool Collide(Pos p, bool high)
3135 Tile t = GetTile(p);
3136 // switch(t)
3137 // {
3138 // default:
3139 if (!high)
3140 return tileSolid[t]==1;
3141 else
3142 return false;
3143 // }
3145 void Undo()
3147 if (numUndo==0) return;
3149 UndoDone(); // Complete previous undo...
3151 numUndo--;
3153 if (time > undo[numUndo].endTime)
3154 time = undo[numUndo].endTime;
3155 undoTime = undo[numUndo].time;
3157 undo[numUndo].Restore(this);
3159 void UndoDone()
3161 if (undoTime < 0)
3162 return;
3163 renderer.Reset(undoTime);
3164 time = undoTime;
3165 undoTime = -1;
3167 void ScoreDestroy(Pos p)
3169 Tile t = GetTile(p);
3170 if (t==COLLAPSABLE || t==COLLAPSE_DOOR)
3172 else if (t != EMPTY)
3174 player_score += 10;
3178 bool LaserTile(Pos p, int mask, double fireTime)
3180 if (&renderer(p) == &renderer(Pos(-1,-1)))
3181 return false;
3182 //if (!renderer.Visible(p))
3183 // return false;
3185 TileRender* tr = 0;
3186 if (time <= renderer(p).GetLastTime())
3187 if (fireTime < renderer(p).GetLastTime())
3189 renderer(p).Add(tr = new TileRender(GetTile(p), p, mask), fireTime);
3190 ((TileRender*)renderer(p).GetStage(time+10/*HACKY!*/))->special |= mask;
3192 else
3194 tr = new TileRender(GetTile(p), p, mask | ((TileRender*)renderer(p).GetStage(time+10/*HACKY!*/))->special);
3195 renderer(p).Add(tr, fireTime);
3197 else
3198 renderer(p).Add(tr = new TileRender(GetTile(p), p, mask), fireTime);
3200 if (tr)
3202 tr->specialDuration = time + LASER_LINE_TIME - fireTime + LASER_FADE_TIME;
3204 return true;
3206 void FireGun(Pos newpos, Dir d, bool recurse, double fireTime)
3208 static Pos hits[100];
3209 static Dir hitDir[100];
3210 static unsigned int numHits=0;
3211 if (!recurse)
3212 numHits = 0;
3214 double starttime = fireTime;
3215 for (Dir fd=((d<0)?0:d); fd<((d<0)?MAX_DIR:d+1); fd++)
3217 fireTime = starttime;
3218 // starttime += 0.03;
3220 Pos p = newpos + fd;
3221 int range = 0;
3222 for (; range<MAP_SIZE; range++, p=p+fd)
3224 Tile t = GetTile(p);
3225 if (tileSolid[t]!=-1)
3227 if (t!=TRAP)
3228 renderer(p).Add(new TileRender(tileSolid[t]==1 ? TILE_WHITE_WALL : TILE_WHITE_TILE, p), fireTime+0.1);
3230 unsigned int i;
3231 for (i=0; i<numHits; i++)
3232 if (hits[i]==p)
3233 break;
3234 if (i==numHits ||
3235 t==TRAP && (hitDir[i]&(1<<fd))==0
3238 if (i==numHits)
3240 if (i >= sizeof(hits)/sizeof(hits[0]))
3241 return;
3242 hitDir[i] = 1 << fd;
3243 hits[i] = p;
3244 numHits++;
3246 else
3248 hitDir[i] |= 1 << fd;
3250 if (t==TRAP)
3252 int dirmask =
3253 1<<((fd+2) % MAX_DIR)
3254 | 1<<((fd+MAX_DIR-2) % MAX_DIR);
3256 if (LaserTile(p, dirmask, fireTime))
3257 fireTime += (time+LASER_LINE_TIME - fireTime) / 40;
3258 // fireTime += LASER_SEGMENT_TIME;
3260 FireGun(p, (fd+1) % MAX_DIR, true, fireTime);
3261 FireGun(p, (fd+MAX_DIR-1) % MAX_DIR, true, fireTime);
3264 break;
3266 else
3268 LaserTile(p, 1<<(fd%3), fireTime);
3270 fireTime += (time+LASER_LINE_TIME - fireTime) / 40;
3271 // fireTime += LASER_SEGMENT_TIME;
3275 // renderer().Add(new LaserRender(newpos, fd, range), time);
3278 if (!recurse)
3280 //double _time = time;
3281 time += LASER_LINE_TIME;
3282 for (unsigned int i=0; i<numHits; i++)
3284 Pos p = hits[i];
3285 Tile t = GetTile(p);
3287 if (t==TRAP)
3288 continue;
3290 ScoreDestroy(p);
3292 renderer(p).Add(new ExplosionRender(p, t==GUN), time);
3293 //renderer(p).Add(new TileRender(EMPTY, p), time+2);
3294 SetTile(p, EMPTY, false);
3296 if (GetItem(p))
3297 renderer(p,true).Add(new ItemRender(GetItem(p), 1, p), time);
3299 if (t==GUN)
3301 for (Dir j=0; j<MAX_DIR; j++)
3303 if (GetTile(p+j)!=EMPTY)
3305 renderer(p+j).Add(new TileRender(tileSolid[GetTile(p+j)]==1 ? TILE_WHITE_WALL : TILE_WHITE_TILE, p+j), time+0.05);
3306 renderer(p+j).Add(new ExplosionRender(p+j), time+0.2);
3308 if (GetItem(p+j))
3309 renderer(p+j,true).Add(new ItemRender(GetItem(p+j), 1, p), time);
3311 //renderer(p+j).Add(new TileRender(EMPTY, p+j), time+2.2);
3313 ScoreDestroy(p + j);
3314 SetTile(p + j, EMPTY, false);
3319 time += MAX(LASER_FADE_TIME, 0.15);
3320 //time = _time;
3321 CheckFinished();
3324 int GetLastPlayerRot()
3326 RenderStage* rs = renderer.player.GetStage(-1);
3327 if (!rs) return 3;
3328 return ((PlayerRender*)rs)->r;
3330 bool Input(Dir d)
3332 if (dead || win || isMap)
3333 return false;
3335 // Complete undo
3336 UndoDone();
3338 // Jump forwards in time to last move finishing
3339 if (numUndo > 0 && time < undo[numUndo-1].endTime)
3340 time = undo[numUndo-1].endTime;
3342 double realTime = time;
3343 double endAnimTime = time;
3344 bool high = (tileSolid[GetTile(player)] == 1);
3345 Pos playerStartPos = player;
3346 Pos oldpos = player;
3347 int oldPlayerHeight = GetHeight(oldpos);
3348 Pos newpos = player + d;
3350 int playerRot = GetLastPlayerRot();
3351 if (d!=-1 && d!=playerRot)
3353 while (d!=playerRot)
3355 if ((d+6-playerRot) % MAX_DIR < MAX_DIR/2)
3356 playerRot = (playerRot+1) % MAX_DIR;
3357 else
3358 playerRot = (playerRot+MAX_DIR-1) % MAX_DIR;
3360 time += 0.03;
3362 if (GetTile(oldpos) == FLOATING_BALL)
3364 TileRender* t = new TileRender(FLOATING_BALL, oldpos);
3365 t->special = playerRot + 256;
3366 renderer(oldpos).Add(t, time);
3368 renderer.player.Add(new PlayerRender(playerRot, Pos(-20,-20), oldPlayerHeight, Pos(-20,-20), oldPlayerHeight, dead), time);
3370 else
3372 PlayerRender *p = new PlayerRender(playerRot, player, oldPlayerHeight, player, oldPlayerHeight, dead);
3373 p->speed = 0;
3374 renderer.player.Add(p, time);
3378 time += 0.03;
3381 if (d<0 && player_items[1]==0)
3382 return false;
3384 if (d >= 0)
3386 if (tileSolid[GetTile(newpos)] == -1)
3388 time = realTime;
3389 return false;
3391 if (Collide(newpos, high))
3393 time = realTime;
3394 return false;
3398 // Don't change any real state before this point!
3399 if (numUndo >= MAX_UNDO)
3401 numUndo--;
3402 for(int i=0; i<MAX_UNDO-1; i++)
3403 undo[i] = undo[i+1];
3405 undo[numUndo].New(d, player, player_items, time, player_score);
3407 if (d<0)
3409 player_items[1]--;
3412 double time0 = time;
3413 time += 0.15; //Time for leave-tile fx
3415 switch (GetTile(oldpos))
3417 case COLLAPSABLE:
3418 SetTile(oldpos, EMPTY);
3419 renderer(oldpos).Add(new DisintegrateRender(oldpos), time);
3420 CheckFinished();
3421 break;
3423 case COLLAPSE_DOOR:
3424 // Don't need to CheckFinished - can't be collapse doors around
3425 // unless there're still collapsable tiles around.
3426 SetTile(oldpos, EMPTY);
3427 renderer(oldpos).Add(new DisintegrateRender(oldpos, 1), time);
3428 break;
3430 case COLLAPSABLE2:
3431 SetTile(oldpos, COLLAPSABLE, false);
3432 renderer(oldpos).Add(new DisintegrateRender(oldpos, 0, 1), time);
3433 player_score += 10;
3434 CheckFinished();
3435 break;
3437 case COLLAPSE_DOOR2:
3438 SetTile(oldpos, COLLAPSE_DOOR, false);
3439 renderer(oldpos).Add(new DisintegrateRender(oldpos, 1, 1), time);
3440 player_score += 10;
3441 break;
3443 case COLLAPSABLE3:
3444 SetTile(oldpos, COLLAPSABLE2);
3445 break;
3448 time = time0; //End of leave-tile fx
3450 int retry_pos_count=0;
3451 retry_pos:
3452 retry_pos_count++;
3454 if (GetItem(newpos)==1)
3456 renderer(newpos, true).Add(new ItemCollectRender(GetItem(newpos), newpos), time + JUMP_TIME/2);
3457 SetItem(newpos, 0, false);
3458 player_items[0]++;
3460 if (GetItem(newpos)==2)
3462 renderer(newpos, true).Add(new ItemCollectRender(GetItem(newpos), newpos), time + JUMP_TIME/2);
3463 SetItem(newpos, 0, false);
3464 player_items[1]++;
3467 if (GetTile(player) == FLOATING_BALL)
3469 TileRender* t = new TileRender(FLOATING_BALL, player);
3470 t->special = 0;
3471 renderer(oldpos).Add(t, time);
3474 PlayerRender *p = new PlayerRender(playerRot, player, oldPlayerHeight, newpos, GetHeight(newpos), dead);
3476 // alternate leg (hacky!)
3477 if (1)
3479 static int l=0;
3480 l++;
3481 p->type = l & 1;
3484 if (retry_pos_count!=0 && GetTile(player)==TRAP)
3486 p->speed /= 1.5;
3487 p->type = 2;
3489 if (d==-1)
3490 p->speed = JUMP_TIME * 1.5;
3491 renderer.player.Add(p, time);
3492 endAnimTime = MAX(endAnimTime, time + p->speed+0.001);
3493 time += p->speed;
3495 player = newpos;
3497 switch (GetTile(newpos))
3499 case COLLAPSABLE:
3500 renderer(newpos).Add(new TileRender(TILE_GREEN_CRACKED, newpos), time);
3501 break;
3502 case COLLAPSE_DOOR:
3503 renderer(newpos).Add(new TileRender(TILE_GREEN_CRACKED_WALL, newpos), time);
3504 break;
3505 case COLLAPSABLE2:
3506 renderer(newpos).Add(new TileRender(TILE_BLUE_CRACKED, newpos), time);
3507 break;
3508 case COLLAPSE_DOOR2:
3509 renderer(newpos).Add(new TileRender(TILE_BLUE_CRACKED_WALL, newpos), time);
3510 break;
3512 case EMPTY:
3513 dead = true;
3514 break;
3516 case BUILDER:
3518 double pretime = time;
3519 bool done = false;
3520 time += 0.15;
3521 for (Dir fd=0; fd<MAX_DIR; fd++)
3523 Tile t2 = GetTile(newpos + fd);
3524 if (t2==EMPTY)
3526 done = true;
3527 SetTile(newpos+fd, COLLAPSABLE, false);
3528 renderer(newpos+fd).Add(new BuildRender(newpos+fd, fd, 0), time);
3530 else if (t2==COLLAPSABLE)
3532 done = true;
3533 SetTile(newpos+fd, COLLAPSE_DOOR, false);
3534 renderer(newpos+fd).Add(new BuildRender(newpos+fd, fd, 1), time);
3537 if (done) time += BUILD_TIME;
3538 else time = pretime;
3539 CheckFinished();
3540 endAnimTime = MAX(endAnimTime, time + 0.1);
3542 break;
3544 case SWITCH:
3545 Swap(COLLAPSE_DOOR, COLLAPSABLE);
3546 break;
3548 case FLOATING_BALL:
3550 int step=0;
3551 renderer.player.Add(new PlayerRender(playerRot, Pos(-30,-30), 0, Pos(-30,-30), 0, dead), time);
3552 while (tileSolid[GetTile(newpos+d)]==-1)
3554 step++;
3556 if (!renderer.Visible(newpos+d))
3558 TileRender* r = new TileRender(FLOATING_BALL, newpos);
3559 r->special = 512;
3560 renderer(newpos).Add(r, time);
3562 PlayerRender* pr = new PlayerRender(playerRot, newpos, 0, newpos, 0, dead);
3563 pr->speed = JUMP_TIME*1;
3564 renderer.player.Add(pr, time);
3566 time += pr->speed;
3568 dead = 1;
3569 break;
3571 oldpos = newpos;
3572 newpos = oldpos + d;
3574 SetTile(oldpos, EMPTY, false);
3575 SetTile(newpos, FLOATING_BALL, false);
3577 renderer(oldpos).Add(new TileRotateRender(FLOATING_BALL, oldpos, d, 2), time);
3578 renderer(oldpos).Add(new TileRender(EMPTY, oldpos), time + ROTATION_TIME/2);
3579 renderer(newpos).Add(new TileRotateRender(FLOATING_BALL, newpos, (d+3)%MAX_DIR, 3), time + ROTATION_TIME/2);
3581 // PlayerRender *p = new PlayerRender(playerRot, oldpos, 0, newpos, 0, dead);
3582 // p->speed = ROTATION_TIME*0.9;
3583 // renderer.player.Add(p, time);
3585 endAnimTime = MAX(endAnimTime, time + ROTATION_TIME + ROTATION_TIME/2);
3586 time += ROTATION_TIME;
3588 player = newpos;
3589 // renderer.player.Add(new PlayerRender(playerRot, player, 0, player, 0, 0), time);
3590 if (dead)
3593 else
3595 TileRender* r = new TileRender(FLOATING_BALL, newpos);
3596 r->special = playerRot + 256;
3597 renderer(newpos).Add(r, time);
3600 break;
3602 case LIFT_DOWN:
3603 case LIFT_UP:
3605 SetTile(newpos, GetTile(newpos)==LIFT_UP ? LIFT_DOWN : LIFT_UP, false);
3606 renderer(newpos).Add(new TileRender(GetTile(newpos), newpos, 1), time);
3608 PlayerRender *p = new PlayerRender(playerRot, newpos, 1-GetHeight(newpos), newpos, GetHeight(newpos), dead);
3609 renderer.player.Add(p, time);
3610 endAnimTime = MAX(endAnimTime, time + JUMP_TIME);
3612 break;
3614 case TRAMPOLINE:
3615 if (d<0) break;
3617 oldpos = newpos;
3618 if (Collide(newpos + d, high))
3619 break;
3620 if (Collide((newpos + d) + d, high) == 1)
3621 newpos = (newpos + d);
3622 else
3623 newpos = (newpos + d) + d;
3624 if (tileSolid[GetTile(newpos)] == -1)
3625 dead=1;
3626 //player = newpos;
3627 goto retry_pos;
3629 case SPINNER:
3631 for (Dir d=0; d<MAX_DIR; d++)
3633 Tile tmp = GetTile(newpos + d);
3634 renderer(newpos + d).Add(new TileRotateRender(tmp, newpos + d, (d+2)%MAX_DIR, false), time);
3636 Tile tmp = GetTile(newpos + Dir(MAX_DIR-1));
3637 for (Dir d=0; d<MAX_DIR; d++)
3639 Tile t2 = GetTile(newpos + d);
3640 SetTile(newpos + d, tmp, false);
3641 renderer(newpos + d).Add(new TileRotateRender(tmp, newpos + d, (d+4)%MAX_DIR, true), time + ROTATION_TIME/2);
3642 if (GetItem(newpos + d))
3643 renderer(newpos + d,true).Add(new ItemRender(GetItem(newpos + d), GetTile(newpos + d)==EMPTY, newpos+d), time + ROTATION_TIME/2);
3645 tmp = t2;
3647 endAnimTime = MAX(endAnimTime, time+ROTATION_TIME);
3648 // renderer(newpos).Add(new TileRotateRender(SPINNER, Dir(0), 0), time);
3650 break;
3652 case TRAP:
3654 if (d<0) break;
3656 if (player_items[0]==0)
3658 if (tileSolid[GetTile(newpos + d)] == 1)
3659 break;
3660 newpos = newpos + d;
3661 if (tileSolid[GetTile(newpos)] == -1)
3662 dead=1;
3663 //player = newpos;
3664 goto retry_pos;
3666 else
3668 SetTile(newpos, COLLAPSABLE3);
3669 player_items[0]--;
3672 break;
3674 case GUN:
3676 FireGun(newpos, d, false, time);
3678 endAnimTime = MAX(endAnimTime, time);
3680 if (GetTile(newpos)==EMPTY)
3682 PlayerRender* pr = new PlayerRender(playerRot, player, 0, player, 0, dead);
3683 pr->speed = JUMP_TIME*1;
3684 renderer.player.Add(pr, time);
3686 time += pr->speed;
3687 dead = 1;
3691 Pos hits[MAX_DIR];
3692 int numHits=0;
3694 for (Dir fd=((d<0)?0:d); fd<((d<0)?MAX_DIR:d+1); fd++)
3696 Pos p = newpos + fd;
3697 int range = 0;
3698 for (range; range<MAP_SIZE; range++, p=p+fd)
3700 Tile t = GetTile(p);
3701 if (tileSolid[t]!=-1)
3703 hits[numHits++] = p;
3704 break;
3708 renderer().Add(new LaserRender(newpos, fd, range), time);
3711 double _time = time;
3712 time += 0.25;
3713 for (int i=0; i<numHits; i++)
3715 Pos p = hits[i];
3716 Tile t = GetTile(p);
3718 renderer().Add(new ExplosionRender(p), time);
3719 ScoreDestroy(p);
3720 SetTile(p, EMPTY);
3722 if (t==GUN)
3724 for (Dir j=0; j<MAX_DIR; j++)
3726 ScoreDestroy(p + j);
3727 SetTile(p + j, EMPTY);
3729 if (GetTile(newpos)==EMPTY)
3730 dead = 1;
3733 endAnimTime = MAX(endAnimTime, time);
3735 time = _time;
3737 CheckFinished();
3739 break;
3743 endAnimTime = MAX(endAnimTime, time);
3745 if (dead)
3747 win = false;
3749 PlayerRender* pr = new PlayerRender(player, 0, dead);
3750 pr->speed = 0; // Don't sit around before disappearing!
3751 renderer.player.Add(pr, time);
3753 // If the tile we're drowning on isn't visible, give the ownership of the splash effect to the player, rather than a tile.
3754 if (renderer.Visible(player))
3755 renderer(player).Add(new ExplosionRender(player, 0, 1), time);
3756 else
3757 renderer.player.Add(new ExplosionRender(player, 0, 1), time);
3759 endAnimTime = MAX(endAnimTime, time+2);
3762 time = realTime;
3764 player_score += 1;
3766 undo[numUndo].endTime = endAnimTime;
3767 numUndo++;
3769 return true;
3771 void Update(double timedelta)
3773 while(deadMenu)
3774 delete deadMenu;
3776 if (activeMenu)
3778 activeMenu->Update(timedelta);
3780 else
3781 UpdateKeys();
3783 for (int i=0; i<SDLK_LAST; i++)
3784 if (keyState[i])
3785 keyState[i] = 1;
3787 if (activeMenu)
3788 return;
3790 if (isMap && isRenderMap)
3792 double min = 50;
3793 static double scrollHi = 0;
3794 double x = 0;
3795 #ifndef EDIT
3796 // if (!noMouse)
3798 int xx = noMouse ? keyboardp.getScreenX()-scrollX : mousex;
3799 if (xx > SCREEN_W) xx = SCREEN_W;
3800 int w = TILE_W2*4;
3801 if (xx < w)
3802 x = (double)xx / (w) - 1;
3803 if (xx > SCREEN_W - w)
3804 x = 1 - (double)(SCREEN_W-xx) / (w);
3805 x *= 500;
3806 if (x<-min || x>min)
3808 scrollHi += timedelta * x;
3809 scrollX += (int)scrollHi;
3810 scrollHi -= (int)scrollHi;
3813 #endif
3815 if (undoTime>=0 && undoTime < time)
3817 double acc = (time - undoTime) / 2;
3818 if (acc < 3) acc = 3;
3819 time -= timedelta * acc;
3820 if (undoTime >= time)
3821 UndoDone();
3823 else
3825 time += timedelta;
3826 if (turboAnim)
3827 time += timedelta * 20;
3830 void FileDrop(const char* filename)
3832 LoadSave(filename, false);
3834 void UpdateKeys()
3836 #ifdef EDIT
3837 if (keyState[SDLK_LALT] || keyState[SDLK_LCTRL])
3838 return;
3839 #endif
3841 if (!isMap && !editMode && undoTime < 0)
3843 if (keyState[SDLK_z] || keyState[SDLK_BACKSPACE] || keyState[SDLK_u])
3845 Undo();
3846 return;
3849 if (isMap && !editMode)
3852 if ((keyState[SDLK_q] | keyState[SDLK_KP7]) & 2) keyboardp.x--;
3853 else if ((keyState[SDLK_d] | keyState[SDLK_KP3]) & 2) keyboardp.x++;
3854 else if ((keyState[SDLK_e] | keyState[SDLK_KP9]) & 2) keyboardp.x++, keyboardp.y--;
3855 else if ((keyState[SDLK_a] | keyState[SDLK_KP1]) & 2) keyboardp.x--, keyboardp.y++;
3856 else if ((keyState[SDLK_w] | keyState[SDLK_KP8] | keyState[SDLK_UP]) & 2) keyboardp.y--;
3857 else if ((keyState[SDLK_s] | keyState[SDLK_KP2] | keyState[SDLK_DOWN]) & 2) keyboardp.y++;
3858 else if ((keyState[SDLK_LEFT]) & 2) keyboardp.x--, keyboardp.y+=keyboardp.x&1;
3859 else if (((keyState[SDLK_RIGHT]) & 2)) { if (keyboardp.x < mapRightBound) keyboardp.y-=keyboardp.x&1, keyboardp.x++; }
3860 else if ((keyState[SDLK_RETURN] | keyState[SDLK_KP5] | keyState[SDLK_SPACE] | keyState[SDLK_KP_ENTER]) & 2)
3862 // Simulate user clicking on it...
3863 Mouse(keyboardp.getScreenX()-scrollX, keyboardp.getScreenY()-scrollY, 0, 0, 1, 0, 0);
3864 noMouse = 1;
3865 return;
3867 else
3869 if (noMouse)
3870 UpdateCursor(keyboardp);
3871 return;
3873 int min[21] = { 17, 16, 15, 14, 13, 13, 13, 13, 13, 13, 12, 11, 11, 13, 12, 11, 8, 8, 7, 6, 7 };
3874 int max[21] = { 20, 20, 19, 19, 19, 19, 18, 21, 20, 20, 19, 19, 18, 18, 17, 16, 16, 16, 15, 15, 14 };
3875 if (keyboardp.x < 3) keyboardp.x = 3;
3876 if (keyboardp.x > mapRightBound) keyboardp.x = mapRightBound;
3878 if (keyboardp.y < min[keyboardp.x-3]) keyboardp.y = min[keyboardp.x-3];
3879 if (keyboardp.y > max[keyboardp.x-3]) keyboardp.y = max[keyboardp.x-3];
3880 noMouse = 1;
3881 UpdateCursor(keyboardp);
3883 else if (!editMode && (numUndo==0 || time>=undo[numUndo-1].endTime))
3885 static int usedDiag = 0;
3887 if (keyState[SDLK_q] || keyState[SDLK_KP7]) HandleKey('q', 0);
3888 else if (keyState[SDLK_w] || keyState[SDLK_KP8]) HandleKey('w', 0);
3889 else if (keyState[SDLK_e] || keyState[SDLK_KP9]) HandleKey('e', 0);
3890 else if (keyState[SDLK_a] || keyState[SDLK_KP1]) HandleKey('a', 0);
3891 else if (keyState[SDLK_s] || keyState[SDLK_KP2]) HandleKey('s', 0);
3892 else if (keyState[SDLK_d] || keyState[SDLK_KP3]) HandleKey('d', 0);
3894 else if (keyState[SDLK_UP] && keyState[SDLK_LEFT]) HandleKey('q', 0), usedDiag=1;
3895 else if (keyState[SDLK_UP] && keyState[SDLK_RIGHT]) HandleKey('e', 0), usedDiag=1;
3896 else if (keyState[SDLK_DOWN] && keyState[SDLK_LEFT]) HandleKey('a', 0), usedDiag=1;
3897 else if (keyState[SDLK_DOWN] && keyState[SDLK_RIGHT]) HandleKey('d', 0), usedDiag=1;
3898 else if (keyState[SDLK_UP] && !usedDiag) HandleKey('w', 0);
3899 else if (keyState[SDLK_DOWN] && !usedDiag) HandleKey('s', 0);
3901 else usedDiag = 0;
3904 void KeyReleased(int key)
3906 keyState[key] = 0;
3908 bool KeyPressed(int key, int mod)
3910 keyState[key] = 2;
3912 if (activeMenu)
3914 bool eat = activeMenu->KeyPressed(key, mod);
3915 if (!activeMenu)
3916 memset(keyState, 0, sizeof(keyState));
3917 return eat;
3919 else
3921 if ((key==SDLK_ESCAPE && (mod & KMOD_CTRL)))
3923 if (mod & KMOD_SHIFT)
3925 time = 0;
3926 renderer.Reset();
3927 LoadSaveProgress(false);
3930 LoadMap();
3933 if (isFadeRendering)
3934 return false;
3936 return HandleKey(key, mod);
3939 bool HandleKey(int key, int mod)
3941 turboAnim = 0;
3943 #ifdef CHEAT
3944 if (isMap && key=='r' && (mod & KMOD_ALT))
3946 progress.Clear();
3947 LoadMap();
3949 #endif
3951 if (0) {}
3953 else if ((key=='p' && !editMode || key==SDLK_PAUSE || key==SDLK_ESCAPE))
3955 noMouse = 1;
3956 new PauseMenu(isMap, progress.GetLevel(STARTING_LEVEL, true)->Completed(), progress.general.endSequence>=1, progress.general.endSequence>=2);
3959 #ifdef EDIT
3960 else if (key=='e' && (mod & KMOD_ALT))
3961 editMode = !editMode;
3963 else if (key=='p' && (mod & KMOD_ALT) && numUndo>0
3964 || key>='0' && key<='9' && (mod & KMOD_SHIFT) && !isMap)
3966 if (key>='0' && key<='9')
3967 levelDiff = (key=='0') ? 10 : key-'0';
3969 if (key=='p' && levelPar==0)
3970 levelPar = player_score;
3972 if (numUndo)
3975 undo[numUndo-1].Restore(this);
3976 while (--numUndo);
3978 time = 0;
3979 if (LoadSave(currentFile, true))
3981 if (key>='0' && key<='9')
3982 LoadMap();
3985 #endif
3987 /////////////////////////////////////////////////////////////////////////
3988 if (isMap && !editMode)
3989 return false;
3991 else if (key==SDLK_KP9 || key=='e') Input(1), noMouse=1;
3992 else if (key==SDLK_KP3 || key=='d') Input(2), noMouse=1;
3993 else if (key==SDLK_KP1 || key=='a') Input(4), noMouse=1;
3994 else if (key==SDLK_KP7 || key=='q') Input(5), noMouse=1;
3995 else if (key==SDLK_KP8 || key=='w') Input(0), noMouse=1;
3996 else if (key==SDLK_KP2 || (key=='s' && (((mod & (KMOD_CTRL|KMOD_ALT))==0)||!editMode))) Input(3), noMouse=1;
3997 else if (key==SDLK_KP5 || key==SDLK_SPACE || key==SDLK_RETURN || key==SDLK_KP_ENTER)
3999 noMouse=1;
4000 if (win && winFinal)
4001 LoadMap(), memset(keyState, 0, sizeof(keyState));
4002 else
4003 Input(-1);
4006 else if (key=='r' && (mod & KMOD_CTRL))
4007 LoadSave(currentFile, false);
4009 #ifdef EDIT
4010 else if (key=='z' && (mod & KMOD_ALT))
4012 if (numUndo>0 && !isMap)
4014 time = undo[numUndo-1].endTime;
4015 undoTime = undo[0].time;
4018 undo[numUndo-1].Restore(this);
4019 while (--numUndo);
4022 #endif
4023 else if (key=='z' || key==SDLK_BACKSPACE || key==SDLK_DELETE || key=='u')
4025 if (!isMap)
4026 Undo();
4029 #ifdef EDIT
4030 else if (key=='s' && (mod & KMOD_ALT)){
4031 if (win && strlen(currentFile)>0 && !isMap)
4033 char tmp[1000];
4034 strcpy(tmp, currentFile);
4035 ChangeSuffix(tmp, "sol");
4036 FILE* f = file_open(tmp, "wb");
4037 if (f)
4039 for (int i=0; i<numUndo; i++)
4041 fputc(undo[i].playerMovement, f);
4043 fclose(f);
4047 #endif
4049 #ifdef CHEAT
4050 else if (key=='/' && (mod & KMOD_ALT)){
4051 turboAnim = 1;
4052 if (!isMap)
4054 while (numUndo)
4055 Undo();
4056 ResetLevel();
4058 if (mod & KMOD_SHIFT)
4060 LevelSave* l = progress.GetLevel(currentFile, false);
4061 if (l && l->Completed())
4063 for (int i=0; i<l->bestSolutionLength; i++)
4064 Input(l->bestSolution[i]);
4065 time = 0;
4067 if (!win && l)
4068 l->Clear();
4070 else
4072 char tmp[1000];
4073 strcpy(tmp, currentFile);
4074 ChangeSuffix(tmp, "sol");
4075 FILE* f = file_open(tmp, "rb");
4076 if (f)
4078 int dir;
4079 while ((dir = fgetc(f)) != -1)
4081 if (dir==0xff)
4082 dir = -1;
4083 Input(dir);
4085 time = 0;
4086 fclose(f);
4088 if (!win)
4089 remove(tmp);
4094 #endif
4096 #ifdef EDIT
4097 else if (!editMode)
4098 return false;
4100 else if (key>='0' && key<='9' && (mod & KMOD_ALT) && !isMap)
4101 levelPar = levelPar*10 + key-'0';
4102 else if (key==SDLK_BACKSPACE && (mod & KMOD_ALT) && !isMap)
4103 levelPar /= 10;
4105 else if (key=='i')
4106 Mouse(mousex, mousey, 0, 0, 32, 0, mouse_buttons);
4107 else if (key=='p' && !(mod & KMOD_ALT))
4108 Mouse(mousex, mousey, 0, 0, 64, 0, mouse_buttons);
4109 else if (key=='x')
4110 Mouse(mousex, mousey, 0, 0, 128, 0, mouse_buttons);
4111 else if (key==SDLK_RETURN)
4112 Mouse(mousex, mousey, 0, 0, 256, 0, mouse_buttons);
4113 else if (key==SDLK_BACKSPACE)
4114 Mouse(mousex, mousey, 0, 0, 512, 0, mouse_buttons);
4115 else if (key=='c')
4116 Mouse(mousex, mousey, 0, 0, 1024, 0, mouse_buttons);
4118 else if (key=='s' && (mod & KMOD_CTRL)){
4119 char *fn = LoadSaveDialog(true, true, "Save level");
4120 LoadSave(fn, true);
4121 SDL_WM_SetCaption(currentFile, NULL);
4124 else if (key=='o' && (mod & KMOD_CTRL)){
4125 char* fn = LoadSaveDialog(false, true, "Open level");
4126 LoadSave(fn, false);
4127 SDL_WM_SetCaption(currentFile, NULL);
4129 #endif
4131 else
4132 return false;
4134 return true;
4136 void LoadGraphics()
4138 #define X(NAME,FILE,ALPHA) NAME = Load(DATA_DIR "/graphics/" FILE BMP_SUFFIX, ALPHA);
4139 #include "gfx_list.h"
4141 static int first = 1;
4142 if (first)
4144 first = false;
4145 MakeFont();
4146 MakeTileInfo();
4149 // unsigned int d = {
4151 // };
4152 // static SDL_Cursor * c = SDL_CreateCursor(data, mask, 32, 32, 1, 1);
4153 // SDL_SetCursor(c);
4154 SDL_ShowCursor(0);
4156 void FreeGraphics()
4158 #define X(NAME,FILE,ALPHA) if (NAME) SDL_FreeSurface(NAME), NAME=0;
4159 #include "gfx_list.h"
4161 virtual void ScreenModeChanged()
4163 // FreeGraphics();
4164 // LoadGraphics();
4168 MAKE_STATE(HexPuzzle, SDLK_F1, false);
4170 char * HexPuzzle::loadPtr = 0;
4171 char * HexPuzzle::endLoad = 0;
4173 #endif //USE_OPENGL