Fix endianess problems
[hex-a-hop.git] / hex_puzzzle.cpp
bloba3977462ded15784e24da4663791cc8dc22dfc3b
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 #define GAME_NAME "Hex-a-hop"
32 #ifdef EDIT
33 // #define MAP_EDIT_HACKS
34 #define MAP_EDIT_HACKS_DISPLAY_UNLOCK 0
35 #define CHEAT
36 #define BMP_SUFFIX ".bmp"
37 #else
38 #define USE_LEVEL_PACKFILE
39 #define BMP_SUFFIX ".dat"
40 #endif
44 #ifdef EDIT
45 #define GAMENAME GAME_NAME " (EDIT MODE)"
46 #endif
47 #ifndef GAMENAME
48 #define GAMENAME GAME_NAME
49 #endif
51 #define IMAGE_DAT_OR_MASK 0xff030303 // Reduce colour depth of images slightly for better compression (and remove useless top 8 bits!)
52 #define STARTING_LEVEL "Levels\\0_green\\triangular.lev"
53 #define UNLOCK_SCORING 75
54 const char * mapname = "Levels\\map_maybe\\map.lev";
56 //////////////////////////////////////////////////////
60 #ifndef USE_OPENGL
62 #include "state.h"
64 #include "tiletypes.h"
66 #ifdef USE_LEVEL_PACKFILE
67 #include "packfile.h"
68 #endif
70 void RenderTile(bool reflect, int t, int x, int y, int cliplift=-1);
72 int keyState[SDLK_LAST] = {0};
74 FILE *file_open( const char *file, const char *flags )
76 // printf("file_open( \"%s\", \"%s\" )\n", file, flags );
77 extern String base_path;
78 static String filename; // static to reduce memory alloc/free calls.
79 if (file[0]=='\0' || file[1]!=':') //If a full path is specified, don't prepend base_path
80 filename = base_path;
81 filename += file;
82 // printf(" -> \"%s\"\n", filename );
84 filename.fix_backslashes();
85 FILE* f = fopen( filename, flags );
87 if (!f)
89 printf("Warning: unable to open file \"%s\" for %s\n", (const char*)filename, strchr(flags, 'r') ? "reading" : "writing");
92 return f;
96 #ifdef MAP_EDIT_HACKS
97 static const short value_order[]={
98 //WALL,
99 //COLLAPSE_DOOR2,
100 //COLLAPSABLE3
101 //SWITCH
102 //EMPTY, NORMAL,
104 COLLAPSABLE,
105 TRAMPOLINE,
106 COLLAPSE_DOOR, COLLAPSABLE2,
107 GUN,
108 FLOATING_BALL,
109 SPINNER,
110 TRAP,
111 0x100,
112 LIFT_DOWN, LIFT_UP,
113 BUILDER,
114 0x200,
116 #endif
118 //#define PROGRESS_FILE "progress.dat"
120 #define PI (3.1415926535897931)
121 #define PI2 (PI*2)
122 #define MAX(a,b) ((a)>(b) ? (a) : (b))
123 #define MIN(a,b) ((a)<(b) ? (a) : (b))
124 #define ABS(a) ((a)<0 ? -(a) : (a))
126 #define WATER_COLOUR 31 | (IMAGE_DAT_OR_MASK>>16)&255, 37 | (IMAGE_DAT_OR_MASK>>8)&255, 135 | (IMAGE_DAT_OR_MASK>>0)&255
128 #define ROTATION_TIME 0.25
129 #define BUILD_TIME 1
130 #define LASER_LINE_TIME 0.7
131 #define LASER_FADE_TIME 0.1
132 #define LASER_SEGMENT_TIME 0.01
133 #define LIFT_TIME 0.5
134 #define JUMP_TIME 0.4
136 #define X(NAME,FILE,ALPHA) SDL_Surface* NAME = 0;
137 #include "gfx_list.h"
138 int scrollX=0, scrollY=0, initScrollX=0, initScrollY=0;
139 int mapRightBound = 0;
140 int mapScrollX = 0;
141 bool showScoring = false;
142 bool hintsDone = false;
144 enum {
145 TILE_SPLASH_1 = 17,
146 TILE_SPLASH_2,
147 TILE_SPLASH_3,
149 TILE_SPHERE = 20,
150 TILE_SPHERE_OPEN,
151 TILE_SPHERE_DONE,
152 TILE_SPHERE_PERFECT,
153 TILE_LOCK,
155 TILE_LIFT_BACK,
156 TILE_LIFT_FRONT,
157 TILE_LIFT_SHAFT,
158 TILE_BLUE_FRONT,
159 TILE_GREEN_FRONT,
161 TILE_LINK_0 = 30,
162 TILE_LINK_1,
163 TILE_LINK_2,
164 TILE_LINK_3,
165 TILE_LINK_4,
166 TILE_LINK_5,
167 TILE_GREEN_FRAGMENT,
168 TILE_GREEN_FRAGMENT_1,
169 TILE_GREEN_FRAGMENT_2,
170 TILE_ITEM2,
172 TILE_WATER_MAP = 40,
173 TILE_GREEN_CRACKED,
174 TILE_GREEN_CRACKED_WALL,
175 TILE_BLUE_CRACKED,
176 TILE_BLUE_CRACKED_WALL,
177 TILE_LASER_HEAD,
178 TILE_FIRE_PARTICLE_1,
179 TILE_FIRE_PARTICLE_2,
180 TILE_WATER_PARTICLE,
182 TILE_LASER_0 = 50,
183 TILE_LASER_FADE_0 = 53,
184 TILE_BLUE_FRAGMENT = 56,
185 TILE_BLUE_FRAGMENT_1,
186 TILE_BLUE_FRAGMENT_2,
187 TILE_ITEM1,
188 TILE_LASER_REFRACT = 60,
189 TILE_ICE_LASER_REFRACT = TILE_LASER_REFRACT+6,
190 TILE_WHITE_TILE,
191 TILE_WHITE_WALL,
192 TILE_BLACK_TILE,
196 const int colours[] = {
197 #define X(n,col, solid) col,
198 #include "tiletypes.h"
201 const int tileSolid[] = {
202 #define X(n,col, solid) solid,
203 #include "tiletypes.h"
206 void ChangeSuffix(char* filename, char* newsuffix)
208 int len = strlen(filename);
209 int i = len-1;
210 while (i>=0 && filename[i]!='\\' && filename[i]!='.' && filename[i]!='/')
211 i--;
212 if (filename[i]=='.')
213 strcpy(filename+i+1, newsuffix);
214 else
216 strcat(filename, ".");
217 strcat(filename, newsuffix);
221 bool isMap=false, isRenderMap=false;
222 int isFadeRendering=0;
225 |--| |--| TILE_W1
226 |--------| TILE_W2
227 |-----| TILE_WL
228 |-----------| TILE_W3
230 *-----* - -
231 / \ |TILE_H1 |TILE_H2
232 / \ | |
233 * * - |
234 \ / |
235 \ / |
236 *-----* -
238 WL = sqrt(h1*h1 + w1*w1)
239 wl**2 = h1**2 + w1**2
241 w1 = sin60.wL
245 #if 1
246 #define TILE_W1 18
247 #define TILE_W3 64
248 #define GFX_SIZE TILE_W3
249 #define TILE_W2 (TILE_W3-TILE_W1)
250 #define TILE_H1 TILE_W1
251 #define TILE_HUP 22 //extra visible height of wall (used for determining whether a wall was clicked on)
252 #define TILE_H2 (TILE_H1*2)
253 #define TILE_WL (TILE_W2-TILE_W1)
254 #define TILE_H_LIFT_UP 26
255 #define TILE_H_REFLECT_OFFSET 24
256 #define TILE_HUP2 TILE_H_LIFT_UP // Displacement of object on top of wall
257 #define FONT_SPACING 25
258 #define FONT_X_SPACING (-1) // -1 in order to try and overlap the black borders of adjacent characters
259 #else
260 #define TILE_WL 30
261 #define TILE_W1 (TILE_WL/2)
262 #define TILE_W2 (TILE_W1+TILE_WL)
263 #define TILE_W3 (TILE_W1+TILE_W2)
264 #define TILE_H1 (TILE_WL*0.8660254037844386)
265 #define TILE_H2 (TILE_H1*2)
266 #endif
268 #define MAX_DIR 6
270 SDL_Rect font[256];
271 SDL_Rect tile[2][70];
272 short tileOffset[2][70][2];
273 int Peek(SDL_Surface* i, int x, int y)
275 if (x<0 || y<0 || x>=i->w || y>=i->h)
276 return 0;
277 unsigned int p=0;
278 const int BytesPerPixel = i->format->BytesPerPixel;
279 const int BitsPerPixel = i->format->BitsPerPixel;
280 if (BitsPerPixel==8)
281 p = ((unsigned char*)i->pixels)[i->pitch*y + x*BytesPerPixel];
282 else if (BitsPerPixel==15 || BitsPerPixel==16)
283 p = *(short*)(((char*)i->pixels) + (i->pitch*y + x*BytesPerPixel));
284 else if (BitsPerPixel==32)
285 p = *(unsigned int*)(((char*)i->pixels) + (i->pitch*y + x*BytesPerPixel));
286 else if (BitsPerPixel==24)
287 p = (int)((unsigned char*)i->pixels)[i->pitch*y + x*BytesPerPixel]
288 | (int)((unsigned char*)i->pixels)[i->pitch*y + x*BytesPerPixel] << 8
289 | (int)((unsigned char*)i->pixels)[i->pitch*y + x*BytesPerPixel] << 16;
291 return p;
293 bool IsEmpty(SDL_Surface* im, int x, int y, int w, int h)
295 for (int i=x; i<x+w; i++)
296 for (int j=y; j<y+h; j++)
297 if (Peek(im,i,j) != Peek(im,0,im->h-1))
298 return false;
299 return true;
301 void MakeFont()
303 memset(font, 0, sizeof(font));
305 int h = FONT_SPACING;
306 int x=-1, y=0;
307 for (int i=33; i<=127; i++)
311 x++;
312 if (x>=fontImage->w)
313 x=0, y+=h;
314 if (y >= fontImage->h)
315 return;
316 if (y+h > fontImage->h)
317 h = fontImage->h - y;
318 } while(IsEmpty(fontImage, x, y, 1, h));
320 int w=1;
321 while(!IsEmpty(fontImage, x+w, y, 1, h) && x+w<fontImage->w)
322 w++;
323 int h1=h;
324 while (h1>1 && IsEmpty(fontImage, x, y+h1-1, w, 1))
325 h1--;
326 font[i].x = x;
327 font[i].y = y;
328 font[i].w = w;
329 font[i].h = h1;
330 //printf("character %c: % 4d % 4d % 4d % 4d\n", i, x, y, w, h1);
331 x+=w;
334 int i=' ';
335 font[i].x = x;
336 font[i].y = y;
337 font[i].w = font['j'].w;
338 font[i].h = 0;
340 void MakeTileInfo()
342 for (int i=0; i<140; i++)
344 SDL_Rect r = {(i%10)*GFX_SIZE, ((i/10)%7)*GFX_SIZE, GFX_SIZE, GFX_SIZE};
345 short * outOffset = tileOffset[i/70][i%70];
346 SDL_Surface * im = (i/70) ? tileGraphicsR : tileGraphics;
348 outOffset[0] = outOffset[1] = 0;
350 while (r.h>1 && IsEmpty(im, r.x, r.y, r.w, 1)) r.h--, r.y++, outOffset[1]++;
351 while (r.h>1 && IsEmpty(im, r.x, r.y+r.h-1, r.w, 1)) r.h--;
352 while (r.w>1 && IsEmpty(im, r.x, r.y, 1, r.h)) r.w--, r.x++, outOffset[0]++;
353 while (r.w>1 && IsEmpty(im, r.x+r.w-1, r.y, 1, r.h)) r.w--;
355 tile[i/70][i%70] = r;
359 void PrintRaw(int x, int y, const char * tmp)
361 for (int i=0; tmp[i]; i++)
363 SDL_Rect dst = {x, y, 1, 1};
364 SDL_BlitSurface(fontImage, &font[tmp[i]], screen, &dst);
365 x += font[tmp[i]].w + FONT_X_SPACING;
369 void Print(int x, int y, const char * string, ...)
371 va_list marker;
372 va_start( marker, string ); /* Initialize variable arguments. */
374 char tmp[1000];
375 vsprintf((char*)tmp, string, marker);
377 PrintRaw(x, y, tmp);
379 va_end( marker ); /* Reset variable arguments. */
382 int FontWidth(const char * string)
384 int w = 0;
385 for (int i=0; string[i]; i++)
386 w += font[string[i]].w + FONT_X_SPACING;
387 return w;
390 void PrintR(int x, int y, const char * string, ...)
392 va_list marker;
393 va_start( marker, string ); /* Initialize variable arguments. */
395 char tmp[1000];
396 vsprintf((char*)tmp, string, marker);
398 PrintRaw(x-FontWidth(tmp), y, tmp);
400 va_end( marker ); /* Reset variable arguments. */
403 void PrintC(bool split, 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 char* scan = tmp;
412 while (1)
414 char * end = split ? strstr(scan," ") : 0;
415 if (!end)
417 PrintRaw(x - FontWidth(scan)/2, y, scan);
418 break;
420 else
422 *end = '\0';
423 PrintRaw(x - FontWidth(scan)/2, y, scan);
424 scan = end+2;
425 y += FONT_SPACING;
429 va_end( marker ); /* Reset variable arguments. */
433 #include "savestate.h"
434 #include "menus.h"
435 #include "level_list.h"
437 void SaveState::GetStuff()
439 general.hintFlags = HintMessage::flags;
441 void SaveState::ApplyStuff()
443 HintMessage::flags = general.hintFlags;
447 // somewhere else Tile map[][] is assigned to an unsigned char not int32_t
448 // but the data file format expects it to be 32 bit wide!??
449 typedef int32_t Tile;
450 typedef int Dir;
451 struct Pos{
452 int32_t x,y;
453 Pos() : x(0), y(0) {}
454 Pos(int a, int b) : x(a), y(b) {}
455 bool operator == (Pos const & p) const
457 return x==p.x && y==p.y;
459 Pos operator + (Dir const d) const
461 return Pos(
462 x + ((d==1 || d==2) ? 1 : (d==4 || d==5) ? -1 : 0),
463 y + ((d==0 || d==1) ? -1 : (d==3 || d==4) ? 1 : 0)
466 int getScreenX() const {
467 return x*TILE_W2;
469 int getScreenY() const {
470 return x*TILE_H1 + y*TILE_H2;
472 static Pos GetFromWorld(double x, double y)
474 x += TILE_W3/2;
475 y += TILE_H1;
476 int tx, ty;
477 tx = (int)floor(x/TILE_W2);
478 y -= tx*TILE_H1;
479 ty = (int)floor(y/TILE_H2);
481 y -= ty * TILE_H2;
482 x -= tx * TILE_W2;
484 if (x < TILE_W1 && y < TILE_H1)
485 if (x*TILE_H1 + y * TILE_W1 < TILE_H1*TILE_W1)
486 tx--;
487 if (x < TILE_W1 && y > TILE_H1)
488 if (x*TILE_H1 + (TILE_H2-y) * TILE_W1 < TILE_H1*TILE_W1)
489 tx--, ty++;
491 return Pos(tx, ty);
494 Pos mousep(0,0), keyboardp(4,20);
496 class RenderObject;
498 struct RenderStage
500 virtual void Render(RenderObject* r, double time, bool reflect) = 0;
501 virtual int GetDepth(double time) { return 1; }
504 class RenderObject
506 RenderStage** stage;
507 double* time;
508 int numStages;
509 int maxStages;
510 int currentStage;
511 public:
512 double seed;
513 double currentTime;
514 private:
516 void Reserve()
518 if (maxStages <= numStages)
520 maxStages = maxStages ? maxStages*2 : 4;
521 stage = (RenderStage**) realloc(stage, sizeof(stage[0])*maxStages);
522 time = (double*) realloc(time, sizeof(time[0])*maxStages);
525 public:
526 RenderObject() : stage(0), time(0), numStages(0), maxStages(0), currentStage(0)
528 // TODO: use a random number with better range
529 // or maybe make seed an int or float...
530 seed = rand() / (double)RAND_MAX;
532 ~RenderObject()
534 free(stage); free(time);
536 bool Active(double t)
538 if (numStages==0) return false;
539 if (t < time[0]) return false;
540 return true;
542 void UpdateCurrent(double t)
544 if (currentStage >= numStages) currentStage = numStages-1;
545 if (currentStage < 0) currentStage = 0;
547 while (currentStage>0 && time[currentStage]>t)
548 currentStage--;
549 while (currentStage<numStages-1 && time[currentStage+1]<=t)
550 currentStage++;
552 currentTime = t;
554 RenderStage* GetStage(double t)
556 if (t==-1 && numStages>0)
557 return stage[numStages-1];
559 if (!Active(t)) return 0;
560 UpdateCurrent(t);
561 return stage[currentStage];
563 double GetLastTime()
565 return numStages>0 ? time[numStages-1] : -1;
567 void Render(double t, bool reflect)
569 if (!Active(t))
570 return;
571 UpdateCurrent(t);
572 stage[currentStage]->Render(this, t - time[currentStage], reflect);
574 int GetDepth(double t)
576 if (!Active(t))
577 return -1;
578 UpdateCurrent(t);
579 return stage[currentStage]->GetDepth(t - time[currentStage]);
581 void Reset(double t)
583 if (t<0)
584 numStages = currentStage = 0;
585 else
587 while (numStages > 0 && time[numStages-1] >= t)
588 numStages--;
591 void Wipe()
593 if (currentStage > 0 && numStages > 0)
595 memmove(&time[0], &time[currentStage], sizeof(time[0]) * numStages-currentStage);
596 memmove(&stage[0], &stage[currentStage], sizeof(stage[0]) * numStages-currentStage);
597 numStages -= currentStage;
598 currentStage = 0;
601 void Add(RenderStage* s, double t)
603 int i=0;
605 if (currentStage<numStages && time[currentStage]<=t)
606 i = currentStage;
608 while (i<numStages && time[i]<t)
609 i++;
611 if (i<numStages && time[i]==t)
612 stage[i]=s;
613 else
615 Reserve();
617 if (i<numStages)
619 memmove(&time[i+1], &time[i], (numStages-i) * sizeof(time[0]));
620 memmove(&stage[i+1], &stage[i], (numStages-i) * sizeof(stage[0]));
623 numStages++;
624 time[i] = t;
625 stage[i] = s;
630 class WorldRenderer
632 #define SIZE 30
633 #define FX 10
634 RenderObject tile[SIZE][SIZE][2];
635 RenderObject fx[FX];
636 int fxPos;
638 public:
639 RenderObject player;
640 RenderObject dummy;
642 WorldRenderer()
644 Reset();
647 void Reset(double t = -1)
649 fxPos = 0;
650 player.Reset(t);
651 dummy.Reset(-1);
653 for (int i=0; i<SIZE; i++)
654 for (int j=0; j<SIZE; j++)
655 for (int q=0; q<2; q++)
656 tile[i][j][q].Reset(t);
658 for (int j=0; j<FX; j++)
659 fx[j].Reset(t);
662 void Wipe()
664 player.Wipe();
665 dummy.Reset(-1);
667 for (int i=0; i<SIZE; i++)
668 for (int j=0; j<SIZE; j++)
669 for (int q=0; q<2; q++)
670 tile[i][j][q].Wipe();
672 for (int j=0; j<FX; j++)
673 fx[j].Wipe();
676 bool Visible(Pos p)
678 int x0 = (scrollX+TILE_W2) / TILE_W2;
679 int x1 = (scrollX+SCREEN_W+TILE_W3+TILE_W1) / TILE_W2;
680 if (p.x<0 || p.y<0 || p.x>=SIZE || p.y>=SIZE) return false;
681 if (p.x<x0) return false;
682 if (p.x>=x1-1) return false;
683 for (int j0=0; j0<SIZE*3; j0++)
685 if (j0 * TILE_H1 < scrollY-TILE_H1) continue;
686 if (j0 * TILE_H1 > scrollY+SCREEN_H+TILE_H1) break;
687 int i = j0&1;
688 int j = j0>>1;
689 j -= (x0-i)/2;
690 i += (x0-i)/2*2;
691 if (j>=SIZE) i+=(j+1-SIZE)*2, j=SIZE-1;
692 for (; i<x1 && j>=0; i+=2, j--)
694 if (Pos(i,j)==p)
695 return true;
698 return false;
701 void Render(double t, bool reflect)
703 dummy.Reset(-1);
705 int playerDepth = player.GetDepth(t);
706 if (reflect) playerDepth-=4;
707 if (playerDepth<0)
708 player.Render(t, reflect);
710 int x0 = (scrollX+TILE_W2) / TILE_W2;
711 int x1 = (scrollX+SCREEN_W+TILE_W3+TILE_W1) / TILE_W2;
712 x0 = MAX(x0, 0);
713 x1 = MIN(x1, SIZE);
714 for (int j0=0; j0<SIZE*3; j0++)
716 if (j0 * TILE_H1 < scrollY-TILE_H1) continue;
717 if (j0 * TILE_H1 > scrollY+SCREEN_H+TILE_H1) break;
718 int i = j0&1;
719 int j = j0>>1;
720 j -= (x0-i)/2;
721 i += (x0-i)/2*2;
722 if (j>=SIZE) i+=(j+1-SIZE)*2, j=SIZE-1;
723 for (; i<x1 && j>=0; i+=2, j--)
725 for (int q=reflect?1:0; q!=2 && q!=-1; q += (reflect ? -1 : 1))
726 if (tile[i][j][q].Active(t))
728 tile[i][j][q].Render(t, reflect);
732 if (playerDepth==j0 || j0==SIZE*3 && playerDepth>j0)
733 player.Render(t, reflect);
736 for (int j=0; j<FX; j++)
737 if(fx[j].Active(t))
739 fx[j].Render(t, reflect);
743 RenderObject & operator () ()
745 fxPos++;
746 if (fxPos==FX) fxPos = 0;
747 return fx[fxPos];
749 RenderObject & operator () (Pos const & p, bool item=false)
751 if (p.x<0 || p.y<0 || p.x>=SIZE || p.y>=SIZE)
752 return dummy;
753 return tile[p.x][p.y][item ? 1 : 0];
757 void RenderTile(bool reflect, int t, int x, int y, int cliplift)
759 SDL_Rect src = tile[reflect][t];
760 SDL_Rect dst = {x-scrollX-GFX_SIZE/2, y-scrollY-GFX_SIZE+TILE_H1};
761 dst.x += tileOffset[reflect][t][0];
762 dst.y += tileOffset[reflect][t][1];
763 if (reflect)
764 dst.y += TILE_H_REFLECT_OFFSET;
765 if (cliplift==-1 || reflect)
767 // dst.w=src.w; dst.h=src.h;
768 // SDL_FillRect(screen, &dst, rand());
769 SDL_BlitSurface(reflect ? tileGraphicsR : tileGraphics, &src, screen, &dst);
771 else
773 src.h -= cliplift;
774 if (src.h > TILE_W1)
776 src.h -= TILE_W1/2;
777 SDL_BlitSurface(tileGraphics, &src, screen, &dst);
778 src.y += src.h;
779 dst.y += src.h;
780 src.h = TILE_W1/2;
782 if (src.h > 0)
784 src.w -= TILE_W1*2, src.x += TILE_W1;
785 dst.x += TILE_W1;
786 SDL_BlitSurface(tileGraphics, &src, screen, &dst);
790 void RenderGirl(bool reflect, int r, int frame, int x, int y, int h)
792 int sx = r * 64;
793 int sy = frame * 80*2;
794 if (reflect)
795 y += TILE_H_REFLECT_OFFSET+20+h, sy += 80;
796 else
797 y -= h;
798 SDL_Rect src = {sx, sy, 64, 80};
799 SDL_Rect dst = {x-scrollX-32, y-scrollY-65};
800 SDL_BlitSurface(girlGraphics, &src, screen, &dst);
803 struct ItemRender : public RenderStage
805 int item;
806 Pos p;
807 int water;
809 ItemRender(int i2, int _water, Pos const & _p) : item(i2), p(_p), water(_water)
812 double Translate(double seed, double time)
814 double bob = time*2 + seed*PI2;
815 return sin(bob)*4;
818 void Render(RenderObject* r, double time, bool reflect)
820 if (item==0)
821 return;
823 int y = -5 + (int)Translate(r->seed, r->currentTime + time);
824 if (reflect)
825 y=-y;
826 if (!reflect && !water)
827 RenderTile( false, TILE_SPHERE, p.getScreenX(), p.getScreenY());
828 RenderTile(
829 reflect,
830 item==1 ? TILE_ITEM1 : TILE_ITEM2,
831 p.getScreenX(), p.getScreenY()+y
836 void RenderFade(double time, int dir, int seed)
838 int ys=0;
839 srand(seed);
840 for(int x=rand()%22-11; x<SCREEN_W+22; x+=32, ys ^= 1)
842 for (int y=ys*20; y<SCREEN_H+30; y+=40)
844 double a = (rand()&0xff)*dir;
845 double b = (time * 0x400 + (y - SCREEN_H) * 0x140/SCREEN_H)*dir;
846 if (a >= b)
848 RenderTile(false, TILE_BLACK_TILE, x+scrollX, y+scrollY);
854 struct FadeRender : public RenderStage
856 int seed;
857 int dir;
858 FadeRender(int d=-1) : seed(rand()), dir(d)
860 isFadeRendering = d;
863 void Render(RenderObject* r, double time, bool reflect)
865 if (reflect) return;
866 if (time > 0.5)
868 if (dir==1) dir=0, isFadeRendering=0;
869 return;
871 RenderFade(time, dir, seed);
875 struct ScrollRender : public RenderStage
877 int x,y;
878 bool done;
879 ScrollRender(int a,int b) : x(a), y(b), done(false) {}
881 void Render(RenderObject* r, double time, bool reflect)
883 if (done) return;
884 scrollX = x, scrollY = y;
885 isRenderMap = isMap;
886 done = true;
890 struct LevelSelectRender : public RenderStage
892 Pos p;
893 int item;
894 int adj;
895 #ifdef MAP_EDIT_HACKS
896 int magic;
897 #endif
899 LevelSelectRender(Pos const & _p, int i2, int adj) : p(_p), item(i2), adj(adj)
902 void Render(RenderObject* r, double time, bool reflect)
904 if (item==0)
905 return;
907 #ifndef MAP_LOCKED_VISIBLE
908 if (item==1) return;
909 #endif
911 if (!reflect && adj)
912 for (int i=0; i<MAX_DIR; i++)
913 if (adj & (1 << i))
914 RenderTile( false, TILE_LINK_0+i, p.getScreenX(), p.getScreenY());
916 if (item < 0)
917 return;
919 if (!reflect)
921 RenderTile(
922 reflect,
923 TILE_SPHERE + item-1,
924 p.getScreenX(), p.getScreenY()
927 #ifdef MAP_EDIT_HACKS
928 int x = p.getScreenX()-scrollX, y = p.getScreenY()-scrollY;
929 Print(x+5,y-25,"%d",magic);
930 #endif
935 struct ItemCollectRender : public ItemRender
937 ItemCollectRender(int i2, Pos const & p) : ItemRender(i2, 0, p)
940 void Render(RenderObject* r, double time, bool reflect)
945 int GetLiftHeight(double time, int t)
947 if (t==LIFT_UP)
948 time = LIFT_TIME-time;
949 time = time / LIFT_TIME;
950 if (time > 1)
951 time = 1;
952 if (time < 0)
953 time = 0;
954 time = (3 - 2*time)*time*time;
955 if (t==LIFT_UP)
956 time = (3 - 2*time)*time*time;
957 if (t==LIFT_UP)
958 return (int)((TILE_H_LIFT_UP+4) * time);
959 else
960 return (int)((TILE_H_LIFT_UP-4) * time) + 4;
963 struct TileRender : public RenderStage
965 int special;
966 int t;
967 Pos p;
968 double specialDuration;
970 TileRender(int i, Pos const & _p, int _special=0) : t(i), p(_p), special(_special), specialDuration(LASER_LINE_TIME)
973 void Render(RenderObject* r, double time, bool reflect)
975 if (t==0 && special==0)
976 return;
978 if (special && (t==LIFT_UP || t==LIFT_DOWN) && time<LIFT_TIME)
980 int y = GetLiftHeight(time, t);
981 if (!reflect)
983 RenderTile(reflect, TILE_LIFT_BACK, p.getScreenX(), p.getScreenY());
984 RenderTile(reflect, TILE_LIFT_SHAFT, p.getScreenX(), p.getScreenY()+y, y-8);
985 RenderTile(reflect, TILE_LIFT_FRONT, p.getScreenX(), p.getScreenY());
987 else
989 RenderTile(reflect, TILE_LIFT_SHAFT, p.getScreenX(), p.getScreenY()-y, y);
990 RenderTile(reflect, LIFT_DOWN, p.getScreenX(), p.getScreenY());
993 else if (special && (t==EMPTY || t==TRAP) && !reflect && time < specialDuration)
995 if (t == TRAP)
996 if (time < specialDuration-LASER_FADE_TIME)
997 RenderTile(reflect, TILE_ICE_LASER_REFRACT, p.getScreenX(), p.getScreenY());
998 else
999 RenderTile(reflect, t, p.getScreenX(), p.getScreenY());
1000 int base = ((t==EMPTY) ? TILE_LASER_0 : TILE_LASER_REFRACT);
1001 if (t==EMPTY && time >= specialDuration-LASER_FADE_TIME)
1002 base = TILE_LASER_FADE_0;
1004 int foo=special;
1005 for(int i=0; foo; foo>>=1, i++)
1006 if (foo & 1)
1007 RenderTile(reflect, base+i, p.getScreenX(), p.getScreenY());
1009 else if (t==FLOATING_BALL)
1011 int y = int(1.8 * sin(r->seed*PI + time*4));
1012 if (special==512)
1014 if (time > 2) return;
1015 if (reflect) return;
1016 srand(int(r->seed * 0xfff));
1017 for (int i=0; i<20 - int(time*10); i++)
1019 int x = int((((rand() & 0xfff) - 0x800) / 10) * time);
1020 int y = int((((rand() & 0xfff) - 0x800) / 10) * time);
1021 RenderTile(true, 19 + ((i+int(time*5))&1)*10, p.getScreenX() + x, p.getScreenY() - 14 + y);
1024 if (time < 0.05)
1025 RenderTile(true, 18, p.getScreenX(), p.getScreenY() - 14);
1027 else if (special)
1028 RenderBoat(reflect, int(special)&255, p.getScreenX(), p.getScreenY(), y);
1029 else
1030 RenderTile(reflect, t, p.getScreenX(), p.getScreenY() + (reflect ? -y : y));
1032 else if (t != EMPTY)
1033 RenderTile(reflect, t, p.getScreenX(), p.getScreenY());
1035 static void RenderBoat(bool reflect, int d, int x, int y, int yo)
1037 if (reflect)
1038 RenderGirl(reflect, d, 0, x, y, -yo);
1039 RenderTile(reflect, FLOATING_BALL, x, y+yo);
1040 if (!reflect)
1042 RenderGirl(reflect, d, 0, x, y, -yo);
1043 RenderTile(true, 17, x, y+yo-TILE_H_REFLECT_OFFSET);
1048 struct TileRotateRender : public TileRender
1050 Dir d;
1051 // int range;
1052 int mode;
1053 TileRotateRender(int i, Pos const & p, Dir _d, int m) : TileRender(i, p), d(_d), mode(m)
1055 void Render(RenderObject* r, double time, bool reflect)
1057 if (t==0)
1058 return;
1059 double f = time / ROTATION_TIME;
1061 if (mode & 1) f += 0.5;
1062 if (f<1 && f>0)
1064 if (mode & 2)
1066 else
1067 f = (3-2*f)*f*f;
1070 if (mode & 1) f=1-f; else f=f;
1071 if (f<0) f=0;
1073 if (f >= 1)
1074 TileRender::Render(r, time, reflect);
1075 else
1077 Pos dd = (Pos(0,0)+d);
1078 int x = p.getScreenX() + int(dd.getScreenX()*(f));
1079 int y = p.getScreenY() + int(dd.getScreenY()*(f));
1081 if (mode & 2)
1082 RenderBoat(reflect, (mode&1) ? (d+MAX_DIR/2)%MAX_DIR : d, x, y, 2);
1083 else
1084 RenderTile(reflect, t, x, y);
1089 struct LaserRender : public RenderStage
1091 Pos p;
1092 Dir d;
1093 int range;
1095 LaserRender(Pos _p, int dir, int r) : p(_p), d(dir), range(r)
1098 void Render(RenderObject* r, double time)
1103 struct ExplosionRender : public RenderStage
1105 Pos p;
1106 int seed;
1107 int power;
1108 int type;
1110 ExplosionRender(Pos _p, int _pow=0, int t=0) : p(_p), power(_pow), type(t)
1112 seed = rand();
1115 virtual int GetDepth(double time)
1117 return p.x + p.y*2;
1120 void Render(RenderObject* r, double time, bool reflect)
1122 if (type==1 && time > 2.5)
1123 type = -1, new WinLoseScreen(false);
1125 // if (reflect) return;
1126 if (time > 3) return;
1127 srand(seed);
1128 int q = 50 - int(time * 35);
1129 if (power) q*=2;
1130 if (type) q = 50;
1131 for (int i=0; i<q; i++)
1133 int x = p.getScreenX();
1134 int y = p.getScreenY() + (rand() & 31)-16;
1135 int xs = ((rand() & 63) - 32);
1136 int ys = (-10 - (rand() & 127)) * (1+power);
1137 if (type) ys*=2, xs/=2;
1138 x += int(xs * (1+time*(2+power)));
1139 int yo = int(time*time*128 + ys*time);
1140 //if (yo > 0) yo=-yo;//continue;
1141 if (type)
1144 if (yo > 0)
1146 if (!reflect && ys<-60)
1148 const double T = 0.06;
1149 double ct = -ys / 128.0;
1150 if (time < ct+T*4)
1152 x = p.getScreenX() + int(xs * (1+ct*(2+power)));
1153 RenderTile(
1154 reflect,
1155 time > ct+3*T ? TILE_SPLASH_3 : time > ct+2*T ? TILE_SPLASH_2 : time > ct+T ? TILE_SPLASH_1 : TILE_WATER_PARTICLE+1,
1156 x, y);
1160 else
1161 RenderTile(
1162 reflect,
1163 time - i*0.003 < 0.2 ? TILE_WATER_PARTICLE+1 : TILE_WATER_PARTICLE,
1164 x, y+(reflect?-1:1)*yo);
1166 else
1168 if (yo > 0)
1170 else
1171 RenderTile(
1172 reflect,
1173 i<q-20 || time<0.3 ? TILE_LASER_HEAD : i<q-10 || time<0.6 ? TILE_FIRE_PARTICLE_1 : TILE_FIRE_PARTICLE_2,
1174 x, y+(reflect?-1:1)*yo);
1179 struct DisintegrateRender : public RenderStage
1181 Pos p;
1182 int seed;
1183 int height;
1184 int type;
1186 DisintegrateRender(Pos _p, int _pow=0, int _t=0) : p(_p), height(_pow), type(_t)
1188 seed = rand();
1191 void Render(RenderObject* r, double time, bool reflect)
1193 if (type)
1194 RenderTile(reflect, height ? COLLAPSE_DOOR : COLLAPSABLE, p.getScreenX(), p.getScreenY());
1196 if (time > 50.0/70.0) return;
1197 if (reflect) return;
1198 srand(seed);
1199 int q = 50 - int(time * 70);
1200 if (height) q*=2;
1201 for (int i=0; i<q; i++)
1203 int x = (rand() % (TILE_W3-8))-TILE_W3/2+4;
1204 int y = (rand() % (TILE_H2-8))-TILE_H1+4;
1205 if (x<-TILE_WL/2 && ABS(y)<-TILE_WL/2-x) continue;
1206 if (x>TILE_WL/2 && ABS(y)>x-TILE_WL/2) continue;
1207 int yo=0;
1208 if (height) yo -= rand() % TILE_HUP;
1209 x += p.getScreenX();
1210 y += p.getScreenY() + 4;
1211 int xs = 0;//((rand() & 63) - 32);
1212 int ys = (- (rand() & 31));
1213 x += int(xs * (1+time*(2)));
1214 if (type) yo = -yo;
1215 yo += int(time*time*128 + ys*time);
1216 if (type) yo = -yo*2;
1217 //if (yo > 0) yo=-yo;//continue;
1218 int t = type ? TILE_BLUE_FRAGMENT : TILE_GREEN_FRAGMENT;
1219 if (i>q-20) t++;
1220 if (i>q-10) t++;
1221 if (yo > 5) yo = 5;
1222 RenderTile(false, t, x, y+(reflect?-yo:yo));
1226 struct BuildRender : public RenderStage
1228 Pos p;
1229 Dir dir;
1230 int reverse;
1231 int height;
1232 int type;
1234 BuildRender(Pos _p, Dir _d, int _h, int _r=0, int _type=0) : p(_p), dir(_d), height(_h), reverse(_r), type(_type)
1238 void Render(RenderObject* r, double time, bool reflect)
1240 if (time >= BUILD_TIME)
1241 RenderTile(reflect, height ^ reverse ? (type ? COLLAPSE_DOOR2 : COLLAPSE_DOOR) : (type ? COLLAPSABLE2 : COLLAPSABLE), p.getScreenX(), p.getScreenY());
1242 else
1244 if (height)
1245 RenderTile(reflect, type ? COLLAPSABLE2 : COLLAPSABLE, p.getScreenX(), p.getScreenY());
1247 double dist = time * 2 / BUILD_TIME;
1248 if (dir>-1)
1250 Pos from = p + ((dir+MAX_DIR/2)%MAX_DIR);
1251 if (dist <= 1)
1252 //if (dist > 1)
1254 double offset = (dist*0.7) + 0.3;
1255 int x = from.getScreenX() + int((p.getScreenX()-from.getScreenX()) * offset);
1256 int y = from.getScreenY() + int((p.getScreenY()-from.getScreenY()) * offset - dist*(1-dist)*(TILE_HUP*4));
1257 RenderTile(reflect, TILE_GREEN_FRAGMENT, x, y);
1259 dist -= 1;
1261 else
1263 if (reverse) dist = 1-dist;
1265 if (dist > 0 && !height)
1267 if (!reflect)
1268 for (int i=0; i<=int(dist*15); i++)
1270 int x = p.getScreenX(), y = p.getScreenY();
1271 double d = (i + fmod(dist*15, 1))/10.0;
1272 int x1 = int(sin(d*5+time)*MIN(d,1)*TILE_W2/2);
1273 int y1 = int(cos(d*5+time)*MIN(d,1)*TILE_H1*0.7);
1274 RenderTile(reflect, TILE_GREEN_FRAGMENT, x+x1, y+y1+4);
1275 RenderTile(reflect, TILE_GREEN_FRAGMENT, x-x1, y-y1+4);
1278 if (dist > 0 && height)
1280 int yo = int((1-dist)*(TILE_HUP*1.3));
1281 if (yo > TILE_HUP*1.1)
1282 RenderTile(reflect, TILE_WHITE_TILE, p.getScreenX(), p.getScreenY());
1283 else if (!reflect)
1285 RenderTile(reflect, type ? COLLAPSABLE2 : COLLAPSABLE, p.getScreenX(), p.getScreenY());
1286 RenderTile(reflect, type ? COLLAPSE_DOOR2 : COLLAPSE_DOOR, p.getScreenX(), p.getScreenY()+(reflect ? -yo : yo), yo+6);
1287 RenderTile(reflect, type ? TILE_BLUE_FRONT : TILE_GREEN_FRONT, p.getScreenX(), p.getScreenY());
1289 else
1291 if (yo < TILE_HUP/2)
1293 RenderTile(reflect, type ? COLLAPSE_DOOR2 : COLLAPSE_DOOR, p.getScreenX(), p.getScreenY()+(reflect ? -yo : yo), yo);
1296 RenderTile(reflect, type ? COLLAPSABLE2 : COLLAPSABLE, p.getScreenX(), p.getScreenY());
1304 struct PlayerRender : public RenderStage
1306 Pos p;
1307 Pos target;
1308 int p_h, target_h;
1309 int r;
1310 int type;
1311 double speed;
1312 bool dead;
1314 PlayerRender(Pos a, int h, bool d) : p(a), dead(d), target(a), speed(0), p_h(h), target_h(h), r(3), type(0)
1316 PlayerRender(int _r, Pos a, int h1, Pos t, int h2, bool d) : p(a), dead(d), target(t), speed(JUMP_TIME), p_h(h1), target_h(h2), r(_r), type(0)
1318 int dist = MAX(ABS(p.x-target.x), ABS(p.y-target.y));
1319 if (dist > 1)
1320 speed *= 1.5;
1321 if(dist==0)
1322 speed = 0;
1325 virtual int GetDepth(double time)
1327 double f = speed ? time / speed : 1;
1328 if (f>1) f=1;
1329 if (f==1) dead = this->dead;
1331 double x = p.x + (target.x - p.x) * f;
1332 double y = p.y + (target.y - p.y) * f;
1334 if (f==1 || f>0.5 && p_h>target_h)
1335 return target.x+target.y*2;
1336 return MAX(target.x+target.y*2 , p.x+p.y*2);
1339 void Render(RenderObject* ro, double time, bool reflect)
1341 bool dead = false;
1342 double f = speed ? time / speed : 1;
1343 if (f>1) f=1;
1344 if (f==1) dead = this->dead;
1346 int x = p.getScreenX();
1347 int y = p.getScreenY();
1348 int x2 = target.getScreenX();
1349 int y2 = target.getScreenY();
1350 int h = 0;
1351 int shadow_h = (int)((p_h+(target_h-p_h)*f)*TILE_HUP2);
1353 if (x==x2 && y==y2 && p_h!=target_h)
1355 h = TILE_H_LIFT_UP - GetLiftHeight(time, p_h ? LIFT_DOWN : LIFT_UP);
1357 else
1360 int dist = MAX(ABS(p.x-target.x), ABS(p.y-target.y));
1361 int arc = dist*dist;
1362 int h1 = p_h * TILE_HUP2;;
1363 int h2 = target_h * TILE_HUP2;
1364 if (dist==2 && h1!=0)
1366 arc += h2 ? 1 : 3;
1367 h1 = 0;
1368 shadow_h = f>=0.7 ? int(shadow_h*(f-0.7)/0.3) : 0;
1370 if (dist==0)
1371 arc = speed > JUMP_TIME ? 7 : 2;
1373 h = (int)(h1+(h2-h1)*f);
1374 // if (x==x2 && y==y2)
1375 // ;
1376 // else
1378 //h += int(TILE_H_LIFT_UP/3 * (1-f));
1379 h += (int)(f*(1-f)*TILE_HUP2*arc);
1382 if (type==2)
1383 h=0;
1386 if (!dead)
1388 int frame = 0;
1389 if (type==2 && f<1)
1391 //frame = ((int)(f*4) % 4);
1392 //if (frame==2) frame=0; else if (frame==3) frame=2;
1393 frame = 0;
1395 else if (f==1 || x==x2 && y==y2) // stationary
1396 frame = 0;
1397 else if (f > 0.7)
1398 frame = 0;
1399 else
1401 frame = type ? 2 : 1;
1402 if (f<0.1 || f>0.6)
1403 frame += 2;
1406 if (!reflect)
1407 RenderTile( false, TILE_SPHERE,
1408 (int)(x+(x2-x)*f),
1409 (int)(y+(y2-y)*f) - shadow_h
1412 RenderGirl(
1413 reflect,
1414 r, frame,
1415 (int)(x+(x2-x)*f),
1416 (int)(y+(y2-y)*f),
1421 /* RenderTile(
1422 dead ? TILE_SPHERE_OPEN : TILE_SPHERE_DONE,
1423 (int)(x+(x2-x)*f),
1424 (int)(y+(y2-y)*f),
1425 true
1426 );*/
1431 struct HexPuzzle : public State
1433 struct Undo
1435 #define MAX_TILECHANGE 64 // TODO: don't have a magic upper limit
1436 struct TileChange
1438 Pos p;
1439 Tile t;
1440 int item;
1442 TileChange()
1444 TileChange(Pos _p, Tile _t, int _i) : p(_p), t(_t), item(_i)
1446 void Restore(HexPuzzle* w)
1448 w->SetTile(p,t,false,false);
1449 w->SetItem(p,item,false,false);
1453 TileChange t[MAX_TILECHANGE];
1454 Pos playerPos;
1455 Dir playerMovement;
1456 int numT;
1457 int numItems[2];
1458 int score;
1459 double time;
1460 double endTime;
1462 void Add(TileChange const & tc)
1464 for (int i=0; i<numT; i++)
1465 if (t[i].p==tc.p)
1466 return;
1467 if (numT>=MAX_TILECHANGE)
1468 FATAL("numT>=MAX_TILECHANGE");
1469 else
1470 t[numT++] = tc;
1472 void New(Dir pmove, Pos & pp, int* items, double t, int sc)
1474 numItems[0] = items[0];
1475 numItems[1] = items[1];
1476 playerPos = pp;
1477 playerMovement = pmove;
1478 score = sc;
1479 time = t;
1480 numT = 0;
1482 void Restore(HexPuzzle* w)
1484 for (int i=numT-1; i>=0; i--)
1485 t[i].Restore(w);
1486 w->dead = false;
1487 w->win = false;
1488 w->player = playerPos;
1489 w->player_items[0] = numItems[0];
1490 w->player_items[1] = numItems[1];
1491 w->player_score = score;
1493 //w->renderer.player.Add(new PlayerRender(playerPos, w->GetHeight(playerPos), false), w->time);
1497 #define MAP_SIZE 30
1498 char* special[MAP_SIZE][MAP_SIZE];
1499 Tile map[MAP_SIZE][MAP_SIZE];
1500 int32_t map_item[MAP_SIZE][MAP_SIZE];
1501 int tileCount[NumTileTypes];
1502 int32_t levelPar, levelDiff;
1503 int turboAnim;
1504 Pos player;
1505 int player_items[2];
1506 int player_score;
1507 int numComplete, numLevels, numMastered, numLevelsFound;
1508 bool dead;
1509 bool win;
1510 int winFinal;
1512 SaveState progress;
1514 WorldRenderer renderer;
1515 double time;
1516 double undoTime;
1518 #define MAX_UNDO 6
1519 Undo undo[MAX_UNDO];
1520 int numUndo;
1521 LevelInfo* currentLevelInfo;
1523 char currentFile[1000];
1525 ~HexPuzzle()
1527 FreeGraphics();
1530 LevelInfo* GetLevelInfo(const char* f)
1532 if (strstr(f, "Levels\\") == f)
1533 f += 7;
1534 if (currentLevelInfo!=0 && strcmp(currentLevelInfo->file, f)==0)
1535 return currentLevelInfo;
1537 if (f[0]=='_')
1539 int t = atoi(f+1);
1540 if (t <= numComplete)
1541 return 0;
1543 static char tmp1[100];
1544 static LevelInfo tmp = {0, "", tmp1};
1545 sprintf(tmp1, "Complete %d more %s to unlock!", t-numComplete, t-numComplete==1?"level":"levels");
1546 return &tmp;
1549 for (int i=0; i<sizeof(levelNames)/sizeof(levelNames[0]); i++)
1550 if (strcmp(f, levelNames[i].file)==0)
1551 return &levelNames[i];
1552 static LevelInfo tmp = {0, "", "<<NO NAME>>"};
1553 return &tmp;
1556 #ifdef MAP_EDIT_HACKS
1557 int GetAutoTile(const char * level, bool tiletype)
1559 FILE* f = file_open(filename, "rb");
1560 int tile = EMPTY;
1561 int version;
1563 if (f && fscanf(f, "%d", &version)==1 && (version==3 || version==4))
1565 if (strstr(level,"mk"))
1566 level+=0;
1568 fgetc(f); // Remove '\n' character
1570 int32_t par, diff;
1571 unsigned char bounds[4];
1572 Pos playerStart;
1573 fread(&par, sizeof(par), 1, f);
1574 par = SWAP32(par);
1576 if (version >= 4) {
1577 fread(&diff, sizeof(diff), 1, f);
1578 diff = SWAP32(diff);
1580 fread(bounds, sizeof(bounds), 1, f);
1581 fread(&playerStart, sizeof(playerStart), 1, f);
1582 playerStart.x = SWAP32(playerStart.x);
1583 playerStart.y = SWAP32(playerStart.y);
1585 int highval=0;
1587 for (int i=bounds[0]; i<=bounds[1]; i++)
1588 for (int j=bounds[2]; j<=bounds[3]; j++)
1590 unsigned char comp = map[i][j] | (map_item[i][j]<<5);
1591 fread(&comp, sizeof(comp), 1, f);
1592 int t = comp & 0x1f;
1593 int item = (comp >> 5) & 3;
1594 for (int i=highval+1; i<sizeof(value_order)/sizeof(value_order[0]); i++)
1595 if (t!=0 && t==value_order[i]
1596 || item!=0 && item==(value_order[i]>>8))
1597 highval = i;
1600 if (tiletype)
1602 tile = value_order[highval];
1603 if (tile==0x100) tile = COLLAPSABLE3;
1604 if (tile==0x200) tile = SWITCH;
1605 if (tile==LIFT_UP) tile = LIFT_DOWN;
1607 else
1609 if (value_order[highval] == LIFT_UP)
1610 tile = highval-1;
1611 else
1612 tile = highval;
1615 else
1617 level+=0;
1619 if (f)
1620 fclose(f);
1621 return tile;
1623 #endif
1625 void InitSpecials()
1627 numComplete = numLevels = numMastered = numLevelsFound = 0;
1628 for (int i=0; i<MAP_SIZE; i++)
1629 for (int j=0; j<MAP_SIZE; j++)
1630 ActivateSpecial(Pos(i,j), 0);
1631 for (int i=0; i<MAP_SIZE; i++)
1632 for (int j=0; j<MAP_SIZE; j++)
1633 ActivateSpecial(Pos(i,j), 2);
1634 numComplete = numLevels = numMastered = numLevelsFound = 0;
1635 for (int i=0; i<MAP_SIZE; i++)
1636 for (int j=0; j<MAP_SIZE; j++)
1637 ActivateSpecial(Pos(i,j), 0);
1640 void DoHints()
1642 #ifndef EDIT
1643 if (strcmp(mapname, currentFile)==0)
1645 // for (int i=0; i<32; i++)
1646 // HintMessage::FlagTile(i);
1647 if (numComplete >= UNLOCK_SCORING && !progress.general.scoringOn)
1649 HintMessage::FlagTile(26);
1650 progress.general.scoringOn = 1;
1651 InitSpecials(); // Re-initialise with gold ones available
1653 HintMessage::FlagTile(25);
1655 else
1657 for (int i=0; i<MAP_SIZE; i++)
1658 for (int j=0; j<MAP_SIZE; j++)
1660 int t = GetTile(Pos(i,j));
1661 int item = GetItem(Pos(i,j));
1662 if (t)
1663 HintMessage::FlagTile(t);
1664 if (item)
1665 HintMessage::FlagTile(item+20);
1667 HintMessage::FlagTile(EMPTY);
1669 #endif
1670 hintsDone = true;
1672 void ResetLevel()
1674 hintsDone = false;
1676 UpdateCursor(Pos(-1,-1));
1678 isMap = false;
1680 player_score = 0;
1682 numUndo = 0;
1683 undoTime = -1;
1685 dead = false;
1686 win = false;
1687 winFinal = false;
1688 player_items[0] = player_items[1] = 0;
1689 // time = 0;
1690 if (strlen(currentSlot) == 0)
1692 new TitleMenu();
1693 new Fader(1, -3);
1695 else
1697 if (!isFadeRendering && time!=0)
1699 renderer().Add(new FadeRender(-1), time);
1700 time += 0.5;
1704 // Reset renderer
1705 renderer.Reset(time);
1706 renderer.Wipe();
1708 for (int t=0; t<NumTileTypes; t++)
1709 tileCount[t] = 0;
1711 for (int i=0; i<MAP_SIZE; i++)
1712 for (int j=0; j<MAP_SIZE; j++)
1714 Pos p(i,j);
1715 int item = GetItem(p);
1716 //if (item)
1717 renderer(p,true).Add(new ItemRender(item, GetTile(p)==EMPTY, p), time);
1720 InitSpecials();
1722 for (int i=0; i<MAP_SIZE; i++)
1723 for (int j=0; j<MAP_SIZE; j++)
1725 Pos p(i,j);
1726 int t = GetTile(p);
1727 tileCount[t]++;
1729 if (isMap)
1730 t = EMPTY;
1732 //if (t)
1733 renderer(p).Add(new TileRender(t, p), time);
1736 if (!isMap)
1737 renderer.player.Add(new PlayerRender(player, GetHeight(player), dead), time);
1738 else
1739 renderer.player.Add(new PlayerRender(Pos(-100,-100), 0, true), time);
1742 int bounds[4] = {player.getScreenX(),player.getScreenX(),player.getScreenY(),player.getScreenY()};
1743 for (int i=0; i<MAP_SIZE; i++)
1744 for (int j=0; j<MAP_SIZE; j++)
1746 Pos p(i,j);
1747 if (map[i][j] !=0 || map_item[i][j]!=0)
1749 int x1 = p.getScreenX();
1750 int y1 = p.getScreenY();
1751 int x2 = x1 + TILE_W3;
1752 int y2 = y1 + TILE_H2;
1753 y1 -= TILE_H2; // Make sure objects/player will be properly visible
1755 if (x1<bounds[0]) bounds[0] = x1;
1756 if (x2>bounds[1]) bounds[1] = x2;
1757 if (y1<bounds[2]) bounds[2] = y1;
1758 if (y2>bounds[3]) bounds[3] = y2;
1762 int sx, sy;
1763 if (isMap)
1765 sx = bounds[0] - int(TILE_W2*6.35);
1766 sy = (bounds[3] + bounds[2] - SCREEN_H) / 2 - TILE_H2/2;
1768 else
1770 sx = (bounds[1] + bounds[0] - SCREEN_W) / 2 - TILE_W3/2;
1771 sy = (bounds[3] + bounds[2] - SCREEN_H) / 2 - TILE_H2/2;
1773 if (isMap)
1775 initScrollX = sx;
1776 initScrollY = sy;
1777 if (mapScrollX==0)
1778 mapScrollX = sx;
1779 else
1780 sx = mapScrollX;
1783 // time = 1; // Guarantee we can't try and do things at time=0
1785 renderer().Add(new ScrollRender(sx, sy), time);
1786 renderer().Add(new FadeRender(1), time);
1787 if (time != 0)
1788 time -= 0.5;
1791 char* ReadAll(FILE* f)
1793 int size;
1794 fseek(f, 0, SEEK_END);
1795 size = ftell(f);
1796 fseek(f, 0, SEEK_SET);
1797 char* c = loadPtr = new char [size];
1798 endLoad = loadPtr + size;
1799 fread(c, 1, size, f);
1800 return c;
1803 static char *loadPtr, *endLoad;
1804 static unsigned int fread_replace(void* d, unsigned int size, unsigned int num, FILE*)
1806 unsigned int remain = (endLoad - loadPtr) / size;
1807 if (remain < num) num = remain;
1808 memcpy(d, loadPtr, size*num);
1809 loadPtr += size*num;
1810 return num;
1813 int GetPar(const char * level, bool getdiff=false)
1815 if (strcmp(level, currentFile)==0)
1816 return getdiff ? levelDiff : levelPar;
1818 #ifdef USE_LEVEL_PACKFILE
1819 PackFile1::Entry* e = levelFiles.Find(level);
1820 if (!e) return 999;
1821 loadPtr = (char*)e->Data();
1822 endLoad = loadPtr + e->DataLen();
1823 FILE* f = 0;
1824 #else
1825 loadPtr = 0;
1826 FILE* f = file_open(level, "rb");
1827 #endif
1829 typedef unsigned int _fn(void*, unsigned int, unsigned int, FILE*);
1830 _fn * fn = (loadPtr ? (_fn*)fread_replace : (_fn*)fread);
1832 int32_t par = 99999, diff = 0;
1833 int16_t version;
1835 if (!f && !loadPtr)
1836 return getdiff ? diff : par;
1838 fn(&version, 2, 1, f); // skip to relevant point
1840 if (fn(&par, sizeof(par), 1, f) != 1)
1841 par = 99999;
1842 else
1843 par = SWAP32(par);
1844 size_t ret = fn(&diff, sizeof(diff), 1, f);
1845 diff = SWAP32(diff);
1846 if (ret != 1 || diff<0 || diff>10)
1847 diff = 0;
1849 #ifdef USE_LEVEL_PACKFILE
1850 loadPtr = endLoad = 0;
1851 #else
1852 if (f)
1853 fclose(f);
1854 #endif
1856 return getdiff ? diff : par;
1859 bool LoadSave(const char * filename, bool save)
1861 if (!filename)
1862 return false;
1864 if (!save)
1866 showScoring = false;
1867 LevelSave* l = progress.GetLevel(filename, true);
1868 if (progress.general.scoringOn && l && l->Completed() )
1869 showScoring = true;
1872 #ifdef USE_LEVEL_PACKFILE
1873 if (!save)
1875 PackFile1::Entry* e = levelFiles.Find(filename);
1876 if (!e) return false;
1878 strcpy(currentFile, filename);
1879 currentLevelInfo = GetLevelInfo(currentFile);
1881 loadPtr = (char*)e->Data();
1882 endLoad = loadPtr + e->DataLen();
1883 _LoadSave(NULL, save);
1884 loadPtr = endLoad = 0;
1886 return true;
1888 #else
1889 loadPtr = 0;
1890 FILE* f = file_open(filename, save ? "wb" : "rb");
1891 if (f)
1893 strcpy(currentFile, filename);
1894 if (!save)
1895 currentLevelInfo = GetLevelInfo(currentFile);
1897 if (!save)
1899 char* data = ReadAll(f);
1900 _LoadSave(f, save);
1901 delete [] data;
1902 loadPtr = endLoad = 0;
1904 else
1906 _LoadSave(f, save);
1908 fclose(f);
1910 return true;
1912 #endif
1914 return false;
1917 /** \brief Writes/reads game status to/from a file
1919 * The game data file is written in little endian so it can be shared
1920 * across different machines.
1922 void _LoadSave(FILE* f, bool save)
1924 typedef unsigned int _fn(void*, unsigned int, unsigned int, FILE*);
1925 _fn * fn = save ? (_fn*)fwrite : (loadPtr ? (_fn*)fread_replace : (_fn*)fread);
1927 #define VERSION 4
1928 int version = VERSION; // 1--9
1929 if (save)
1930 fprintf(f, "%d\n", version);
1931 else
1933 char c;
1934 if (fn(&c, 1, 1, f) != 1)
1935 return;
1936 version = c-'0';
1938 // Remove '\n' character
1939 fn(&c, 1, 1, f);
1942 if (!save)
1944 for (int i=0; i<MAP_SIZE; i++)
1945 for (int j=0; j<MAP_SIZE; j++)
1947 delete [] special[i][j];
1948 special[i][j] = 0;
1952 if (version==1)
1954 for (int i=0; i<MAP_SIZE; i++)
1955 for (int j=0; j<MAP_SIZE; j++) {
1956 map[i][j] = SWAP32(map[i][j]);
1957 fn(&map[i][j], sizeof(map[i][j]), 1, f);
1958 map[i][j] = SWAP32(map[i][j]);
1961 player.x = SWAP32(player.x);
1962 player.y = SWAP32(player.y);
1963 fn(&player, sizeof(player), 1, f);
1964 player.x = SWAP32(player.x);
1965 player.y = SWAP32(player.y);
1967 for (int i=0; i<MAP_SIZE; ++i)
1968 for (int j=0; j<MAP_SIZE; ++j)
1969 map_item[i][j] = SWAP32(map_item[i][j]);
1970 if (fn(map_item, sizeof(map_item), 1, f) == 0)
1971 memset(map_item, 0, sizeof(map_item));
1972 for (int i=0; i<MAP_SIZE; ++i)
1973 for (int j=0; j<MAP_SIZE; ++j)
1974 map_item[i][j] = SWAP32(map_item[i][j]);
1976 else if (version>=2 && version<=4)
1978 unsigned char bounds[4];
1979 if (save)
1981 bounds[0]=bounds[1]=player.x;
1982 bounds[2]=bounds[3]=player.y;
1983 for (int i=0; i<MAP_SIZE; i++)
1984 for (int j=0; j<MAP_SIZE; j++)
1985 if (map[i][j] !=0 || map_item[i][j]!=0 || special[i][j]!=0)
1987 if (i<bounds[0]) bounds[0] = i;
1988 if (i>bounds[1]) bounds[1] = i;
1989 if (j<bounds[2]) bounds[2] = j;
1990 if (j>bounds[3]) bounds[3] = j;
1993 else
1995 memset(map, 0, sizeof(map));
1996 memset(map_item, 0, sizeof(map_item));
1999 if (version>=3) {
2000 levelPar = SWAP32(levelPar);
2001 fn(&levelPar, 1, sizeof(levelPar), f);
2002 levelPar = SWAP32(levelPar);
2004 else if (!save)
2005 levelPar = 0;
2007 if (version>=4) {
2008 levelDiff = SWAP32(levelDiff);
2009 fn(&levelDiff, 1, sizeof(levelDiff), f);
2010 levelDiff = SWAP32(levelDiff);
2012 else if (!save)
2013 levelDiff = 0;
2015 fn(bounds, sizeof(bounds), 1, f);
2016 player.x = SWAP32(player.x);
2017 player.y = SWAP32(player.y);
2018 fn(&player, sizeof(player), 1, f);
2019 player.x = SWAP32(player.x);
2020 player.y = SWAP32(player.y);
2022 int offsetx=0, offsety=0;
2024 if (!save && bounds[1]-bounds[0]<15) // Hacky - don't recenter map...
2026 // Re-position map to top left (but leave a bit of space)
2027 // (This ensures the laser/boat effects don't clip prematurely against the edges of the screen)
2028 offsetx = SCREEN_W/2/TILE_W2 + 1 - (bounds[0]+bounds[1]/2);
2029 offsety = SCREEN_H/2/TILE_H2 + SCREEN_W/2/TILE_W2 - (bounds[2]+bounds[3]/2);
2030 offsetx = MAX(0, offsetx);
2031 offsety = MAX(0, offsety);
2032 // if (bounds[0] > 2)
2033 // offsetx = 2 - bounds[0];
2034 // if (bounds[2] > 2)
2035 // offsety = 2 - bounds[2];
2037 bounds[0] += offsetx;
2038 bounds[1] += offsetx;
2039 bounds[2] += offsety;
2040 bounds[3] += offsety;
2041 player.x += offsetx;
2042 player.y += offsety;
2044 for (int i=bounds[0]; i<=bounds[1]; i++)
2045 for (int j=bounds[2]; j<=bounds[3]; j++)
2047 unsigned char comp = map[i][j] | (map_item[i][j]<<5);
2048 fn(&comp, sizeof(comp), 1, f);
2049 map[i][j] = comp & 0x1f;
2050 map_item[i][j] = (comp >> 5) & 3;
2053 if (save)
2055 for (int i=bounds[0]; i<=bounds[1]; i++)
2056 for (int j=bounds[2]; j<=bounds[3]; j++)
2057 if (special[i][j])
2059 int16_t len = strlen(special[i][j]);
2060 unsigned char x=i, y=j;
2061 fn(&x, sizeof(x), 1, f);
2062 fn(&y, sizeof(y), 1, f);
2063 len = SWAP16(len);
2064 fn(&len, sizeof(len), 1, f);
2065 len = SWAP16(len);
2066 fn(special[i][j], 1, len, f);
2069 else
2071 while(1){
2072 int16_t len;
2073 unsigned char x, y;
2074 if (!fn(&x, sizeof(x), 1, f))
2075 break;
2076 fn(&y, sizeof(y), 1, f);
2077 x += offsetx; y += offsety;
2078 fn(&len, sizeof(len), 1, f);
2079 len = SWAP16(len);
2080 if (len<0) break;
2081 char* tmp = new char[len+1];
2082 tmp[len] = 0;
2083 fn(tmp, 1, len, f);
2085 SetSpecial(Pos(x,y), tmp, true, false);
2089 else
2090 return; // Unsupported version!
2092 ResetLevel();
2094 // Save when returning to map!
2095 if (isMap)
2097 progress.general.completionPercentage = numComplete*100/numLevels;
2098 progress.general.masteredPercentage = numMastered*100/numLevels;
2099 LoadSaveProgress(true);
2103 void SetTile(Pos const & p, Tile t, bool updateRenderer=true, bool undoBuffer=true)
2105 if (p.x<0 || p.x>MAP_SIZE)
2106 return;
2107 if (p.y<0 || p.y>MAP_SIZE)
2108 return;
2109 if (map[p.x][p.y] == t)
2110 return;
2111 if (map[p.x][p.y] == t)
2112 return;
2114 tileCount[map[p.x][p.y]]--;
2115 tileCount[t]++;
2117 if (undoBuffer)
2118 undo[numUndo].Add(Undo::TileChange(p,GetTile(p),GetItem(p)));
2120 map[p.x][p.y] = t;
2122 if (updateRenderer)
2123 renderer(p).Add(new TileRender(t, p), time);
2126 Tile GetTile(Pos const & p)
2128 if (p.x<0 || p.x>=MAP_SIZE)
2129 return EMPTY;
2130 if (p.y<0 || p.y>=MAP_SIZE)
2131 return EMPTY;
2132 return map[p.x][p.y];
2135 int GetHeight(Pos const & p)
2137 return tileSolid[GetTile(p)]==1;
2140 char* GetSpecial(Pos const & p)
2142 if (p.x<0 || p.x>=MAP_SIZE)
2143 return NULL;
2144 if (p.y<0 || p.y>=MAP_SIZE)
2145 return NULL;
2146 return special[p.x][p.y];
2149 void SetSpecial(Pos const & p, char * d, bool use_pointer=false, bool auto_activate=true)
2151 if (p.x<0 || p.x>=MAP_SIZE || p.y<0 || p.y>=MAP_SIZE)
2153 if (use_pointer)
2154 delete [] d;
2155 return;
2158 delete [] special[p.x][p.y];
2159 if (!use_pointer && d)
2162 special[p.x][p.y] = new char [strlen(d) + 1];
2163 strcpy(special[p.x][p.y], d);
2165 else
2166 special[p.x][p.y] = d;
2168 if (special[p.x][p.y]==0)
2169 renderer(p,true).Add(new ItemRender(GetItem(p), GetTile(p)==EMPTY, p), time);
2170 else if (auto_activate)
2171 ActivateSpecial(p, 0);
2174 int GetLevelState(Pos const & p, int recurse=0)
2176 char* x = GetSpecial(p);
2177 if (!x) return 0;
2179 LevelSave* l = progress.GetLevel(x, false);
2181 int t = 1;
2183 if (strcmp(x, STARTING_LEVEL)==0)
2184 t = 2;
2185 if (x[0]=='_' && l && l->unlocked)
2186 t=3;
2188 if (l && l->Completed())
2190 t = 3;
2192 if (recurse)
2193 return t;
2195 int par = GetPar(x);
2196 if (progress.general.scoringOn && l->PassesPar( par ))
2197 t = 4;
2199 if (recurse)
2200 return t;
2202 int adj=0;
2203 for (Dir d=0; d<MAX_DIR; d++)
2205 int i = GetLevelState(p+d, 1);
2206 // if (i>1 || i==1 && t>1)
2207 if (i>=1 && t>2 || t>=1 && i>2)
2209 adj |= 1<<d;
2210 if (t==1)
2211 t = 2;
2215 return t | adj<<8;
2218 void ActivateSpecial(Pos const & p, int type)
2220 if (p.x<0 || p.x>=MAP_SIZE || p.y<0 || p.y>=MAP_SIZE)
2221 return;
2223 char * x = special[p.x][p.y];
2225 if (x==0 || x[0]==0)
2226 return;
2228 if (type==2 && x[0]=='_') // Phase2 init - unlock
2230 int t = GetLevelState(p);
2231 int target = atoi(x+1), targetM = 0;
2232 if (target>1000) targetM=target=target-100;
2233 if (t > 1 && numComplete >= target && numMastered >= targetM)
2235 LevelSave* l = progress.GetLevel(x, true);
2236 if (!l->unlocked)
2238 l->unlocked = true;
2240 renderer(p, true).Add(new LevelSelectRender(p, 5, GetLevelState(p)>>8), time+0.01);
2241 renderer().Add(new ExplosionRender(p, 0), time + 0.6);
2242 renderer().Add(new ExplosionRender(p, 1), time + 1.1);
2243 renderer(p, true).Add(new LevelSelectRender(p, -1, GetLevelState(p)>>8), time + 1.1);
2248 if (type==0) // Init & count levels
2250 if (x[0]=='_')
2252 int t = GetLevelState(p);
2253 int unlock = progress.GetLevel(x, true)->unlocked;
2254 LevelSelectRender* lsr = new LevelSelectRender( p, unlock ? -1 : (t>>8) ? 5 : 1, t>>8 );
2255 if ((t>>8) && p.x > mapRightBound) mapRightBound = p.x;
2256 #ifdef MAP_EDIT_HACKS
2257 lsr->magic = -atoi(x+1);
2258 SetTile(p, LIFT_DOWN, true, false);
2259 #else
2260 SetTile(p, EMPTY, true, false);
2261 #endif
2262 renderer(p,true).Add(lsr, time);
2264 else
2266 //printf("Level: %s\n", x);
2268 int t = GetLevelState(p);
2269 numLevels++;
2270 if (t && !GetItem(p))
2272 if (!isMap)
2274 isMap = true;
2275 mapRightBound = 0;
2277 currentLevelInfo = 0;
2279 if ((t&0xff)>=2)
2281 LevelSave* l = progress.GetLevel(x, true);
2282 if (!l->unlocked)
2284 l->unlocked = true;
2286 renderer(p, true).Add(new LevelSelectRender(p, -1, 0), time+0.01);
2287 renderer().Add(new ExplosionRender(p, 0), time + 0.6);
2288 renderer(p, true).Add(new LevelSelectRender(p, t & 0xff, t>>8), time + 0.6);
2291 numLevelsFound++;
2292 if (p.x > mapRightBound) mapRightBound = p.x;
2294 if ((t&0xff)>=3)
2295 numComplete++;
2296 if ((t&0xff)>=4)
2297 numMastered++;
2299 LevelSelectRender* lsr = new LevelSelectRender( p, t & 0xff, t>>8 );
2301 #ifdef MAP_EDIT_HACKS
2302 lsr->magic = 0;
2303 int t = GetAutoTile(x, true);
2304 int v = GetAutoTile(x, false);
2305 if (MAP_EDIT_HACKS_DISPLAY_UNLOCK)
2306 lsr->magic = v;
2307 else
2308 lsr->magic = GetPar(x, true);
2309 t = 1;
2310 SetTile(p, t, true, false);
2311 #else
2312 SetTile(p, EMPTY, true, false);
2313 #endif
2315 renderer(p,true).Add(lsr, time);
2320 if (type==1 && x[0]!='_') // Clicked on
2322 int t = GetLevelState(p);
2323 if (t>1)
2325 LoadSave(x, false);
2330 void SetItem(Pos const & p, int t, bool updateRenderer=true, bool undoBuffer=true)
2332 if (p.x<0 || p.x>MAP_SIZE)
2333 return;
2334 if (p.y<0 || p.y>MAP_SIZE)
2335 return;
2336 if (map_item[p.x][p.y] == t)
2337 return;
2339 if (undoBuffer)
2340 undo[numUndo].Add(Undo::TileChange(p,GetTile(p),GetItem(p)));
2342 map_item[p.x][p.y] = t;
2344 if (updateRenderer)
2345 renderer(p,true).Add(new ItemRender(t, GetTile(p)==EMPTY, p), time);
2348 Tile GetItem(Pos const & p)
2350 if (p.x<0 || p.x>=MAP_SIZE)
2351 return EMPTY;
2352 if (p.y<0 || p.y>=MAP_SIZE)
2353 return EMPTY;
2354 return map_item[p.x][p.y];
2357 void LoadSaveProgress(bool save)
2359 FILE* f = file_open(currentSlot, save ? "wb" : "rb");
2360 if (f)
2362 progress.LoadSave(f, save);
2363 fclose(f);
2365 else
2367 if (!save)
2368 progress.Clear();
2371 void LoadProgress()
2373 LoadSaveProgress(false);
2375 void SaveProgress()
2377 LoadSaveProgress(true);
2380 SDL_Surface* Load(const char * bmp, bool colourKey=true)
2382 typedef unsigned int uint32;
2383 uint32* tmp = 0;
2385 SDL_Surface * g = 0;
2387 #ifdef EDIT
2388 if (strstr(bmp, ".bmp"))
2390 g = SDL_LoadBMP(bmp);
2392 char out[1024];
2393 strcpy(out, bmp);
2394 strcpy(strstr(out, ".bmp"), ".dat");
2396 // SDL_PixelFormat p;
2397 // p.sf = 1;
2398 // SDL_Surface* tmp = SDL_ConvertSurface(g, &p, SDL_SWSURFACE);
2400 short w=g->w, h=g->h;
2401 char* buf = (char*) g->pixels;
2402 if (colourKey)
2404 while (IsEmpty(g, w-1, 0, 1, h) && w>1)
2405 w--;
2406 while (IsEmpty(g, 0, h-1, w, 1) && h>1)
2407 h--;
2410 FILE* f = file_open(out, "wb");
2411 fwrite(&w, sizeof(w), 1, f);
2412 fwrite(&h, sizeof(h), 1, f);
2414 uint32 mask = IMAGE_DAT_OR_MASK;
2415 for (int i=0; i<(int)w*h; )
2417 uint32 c = (*(uint32*)&buf[(i%w)*3 + (i/w)*g->pitch] | mask);
2418 int i0 = i;
2419 while (i < (int)w*h && c == (*(uint32*)&buf[(i%w)*3 + (i/w)*g->pitch] | mask))
2420 i++;
2421 c &= 0xffffff;
2422 i0 = i-i0-1;
2423 if (i0 < 0xff)
2424 c |= i0 << 24;
2425 else
2426 c |= 0xff000000;
2428 fwrite(&c, sizeof(c), 1, f);
2430 if (i0 >= 0xff)
2431 fwrite(&i0, sizeof(i0), 1, f);
2433 fclose(f);
2435 SDL_FreeSurface(g);
2437 bmp = out;
2439 #endif
2441 FILE* f = file_open(bmp, "rb");
2442 if (!f) FATAL("Unable to open file", bmp);
2444 int16_t w,h;
2445 fread(&w, sizeof(w), 1, f);
2446 fread(&h, sizeof(h), 1, f);
2447 w = SWAP16(w);
2448 h = SWAP16(h);
2449 if (w>1500 || h>1500 || w<=0 || h<=0) FATAL("Invalid file", bmp);
2451 tmp = new uint32[(int)w*h];
2453 uint32 c = 0;
2454 uint32 cnt = 0;
2455 for (int p=0; p<(int)w*h; p++)
2457 if (cnt)
2458 cnt -= 0x1;
2459 else
2461 fread(&c, sizeof(c), 1, f);
2462 c = SWAP32(c);
2463 cnt = c >> 24;
2464 if (cnt==255) {
2465 fread(&cnt, sizeof(cnt), 1, f);
2466 cnt = SWAP32(cnt);
2469 tmp[p] = c | 0xff000000;
2472 g = SDL_CreateRGBSurfaceFrom(tmp, w, h, 32, w*4,
2473 0xff0000,
2474 0xff00,
2475 0xff,
2476 0xff000000 );
2478 fclose(f);
2481 if (!g) FATAL("Unable to create SDL surface");
2482 if (colourKey)
2483 SDL_SetColorKey(g, SDL_SRCCOLORKEY, SDL_MapRGB(g->format, WATER_COLOUR));
2484 SDL_Surface * out = SDL_DisplayFormat(g);
2485 SDL_FreeSurface(g);
2486 delete [] tmp;
2487 if (!out) FATAL("Unable to create SDL surface (SDL_DisplayFormat)");
2488 return out;
2491 #ifdef USE_LEVEL_PACKFILE
2492 PackFile1 levelFiles;
2493 #endif
2494 HexPuzzle()
2496 SDL_WM_SetCaption(GAMENAME, 0);
2498 time = 0;
2500 #ifdef USE_LEVEL_PACKFILE
2501 FILE* f = file_open("levels.dat", "rb");
2502 if (!f)
2503 FATAL("Unable to open file", "levels.dat");
2504 levelFiles.Read(f);
2505 fclose(f);
2506 #endif
2508 LoadGraphics();
2510 isMap = false;
2511 editMode = false;
2513 currentLevelInfo = 0;
2515 editTile = 0;
2516 levelPar = 0;
2517 levelDiff = 5;
2518 turboAnim = 0;
2520 memset(map, 0, sizeof(map));
2521 memset(map_item, 0, sizeof(map_item));
2522 memset(special, 0, sizeof(special));
2524 LoadProgress();
2526 // player = Pos(1,11);
2528 // ResetLevel();
2530 LoadMap();
2533 void LoadMap()
2535 #ifndef EDIT
2536 progress.GetLevel(STARTING_LEVEL, true)->unlocked = 1;
2537 if (!progress.GetLevel(STARTING_LEVEL, true)->Completed())
2539 LoadSave(STARTING_LEVEL, false);
2540 return;
2542 #endif
2544 //editMode = false;
2545 LoadSave(mapname, false);
2548 void Render()
2550 if (!activeMenu || activeMenu->renderBG)
2552 SDL_Rect src = {0,0,screen->w,screen->h};
2553 SDL_Rect dst = {0,0,screen->w,screen->h};
2554 if (isRenderMap)
2556 int boundW = mapBG->w;
2557 #ifndef EDIT
2558 boundW = MIN(boundW, (mapRightBound+4) * TILE_W2 - TILE_W1);
2559 #endif
2560 src.x = scrollX - initScrollX;
2561 if (src.x+src.w > boundW)
2563 int diff = src.x+src.w - boundW;
2564 src.x -= diff;
2565 if (isMap)
2566 scrollX -= diff;
2568 if (src.x < 0)
2570 if (isMap)
2571 scrollX -= src.x;
2572 src.x = 0;
2574 //scrollY = initScrollY;
2576 if (isMap)
2577 mapScrollX = scrollX;
2579 SDL_BlitSurface(mapBG, &src, screen, &dst);
2581 else
2582 SDL_BlitSurface(gradient, &src, screen, &dst);
2584 renderer.Render(time, true);
2586 if (!hintsDone && !isFadeRendering)
2588 DoHints();
2591 if (1)
2593 SDL_Rect src = {0,SCREEN_H-1,SCREEN_W,1};
2594 SDL_Rect dst = {0,SCREEN_H-1,SCREEN_W,1};
2595 for (int i=0; i<SCREEN_H; i++)
2597 dst.x = src.x = 0;
2598 dst.y = src.y = SCREEN_H-1-i;
2599 src.w = SCREEN_W;
2600 src.h = 1;
2602 const bool farView = false;
2603 if (isRenderMap)
2605 src.x += (int)( sin(i*0.9 + time*3.7) * sin(i*0.3 + time*0.7)*4 );
2606 src.y += (int)( (sin(i*0.3 - time*2.2) * sin(i*0.48 + time*0.47) - 1) * 1.99 );
2608 else
2610 src.x += (int)( sin(i*0.5 + time*6.2) * sin(i*0.3 + time*1.05) * 5 );
2611 src.y += (int)( (sin(i*0.4 - time*4.3) * sin(i*0.08 + time*1.9) - 1) * 2.5 );
2613 SDL_BlitSurface(screen, &src, screen, &dst);
2617 if(isRenderMap)
2618 SDL_BlitSurface(mapBG2, &src, screen, &dst);
2620 renderer.Render(time, false);
2622 if (!isRenderMap && !isMap && !isFadeRendering)
2624 int v[3] = {player_items[0], player_items[1], player_score};
2625 if (numUndo > 1 && time < undo[numUndo-2].endTime)
2627 int i = numUndo-1;
2628 while (i>1 && time<undo[i-1].time)
2629 i--;
2630 v[0] = undo[i].numItems[0];
2631 v[1] = undo[i].numItems[1];
2632 v[2] = undo[i].score;
2634 if (numUndo>1 && time < undo[0].time)
2635 v[0]=v[1]=v[2]=0;
2636 #ifdef EDIT
2637 Print(0,0,"Anti-Ice: %d", v[0]);
2638 Print(0,FONT_SPACING,"Jumps: %d", v[1]);
2639 Print(0,FONT_SPACING*2,"Score: %d (%d)", v[2], player_score);
2640 Print(0,FONT_SPACING*3,"Par: %d", levelPar);
2641 Print(0,FONT_SPACING*4,"Diff: %d", levelDiff);
2642 #else
2643 if (showScoring)
2644 Print(0, SCREEN_H-FONT_SPACING, " Par: %d Current: %d", levelPar, v[2]);
2646 if (v[0])
2647 Print(0,0," Anti-Ice: %d", v[0]);
2648 else if (v[1])
2649 Print(0,0," Jumps: %d", v[1]);
2650 #endif
2652 if (isRenderMap && isMap && !isFadeRendering)
2654 #if 0//def EDIT
2655 Print(0,0,"Points: %d", numComplete+numMastered);
2656 Print(0,FONT_SPACING,"Discovered: %d%% (%d/%d)", numLevelsFound*100/numLevels, numLevelsFound, numLevels);
2657 Print(0,FONT_SPACING*2,"Complete: %d%% (%d)", numComplete*100/numLevels, numComplete);
2658 Print(0,FONT_SPACING*3,"Mastered: %d%% (%d)", numMastered*100/numLevels, numMastered);
2659 #else
2660 if (numComplete==numLevels && progress.general.endSequence>0)
2661 Print(0, SCREEN_H-FONT_SPACING, " %d%% Mastered", numMastered*100/numLevels);
2662 else
2663 Print(0, SCREEN_H-FONT_SPACING, " %d%% Complete", numComplete*100/numLevels);
2665 if (numMastered >= numLevels && progress.general.endSequence < 2)
2667 progress.general.endSequence = 2;
2668 LoadSaveProgress(true);
2670 new Fader(-1, -7, 0.3);
2672 if (numComplete >= numLevels && progress.general.endSequence < 1)
2674 progress.general.endSequence = 1;
2675 LoadSaveProgress(true);
2677 new Fader(-1, -5, 0.3);
2679 #endif
2681 if ((currentLevelInfo || noMouse) && isMap && isRenderMap && !activeMenu && isFadeRendering<=0)
2683 Pos p;
2684 if (noMouse)
2685 p = keyboardp;
2686 else
2687 p = mousep;
2688 int pad = SCREEN_W/80;
2689 // SDL_Rect src = {0, 0, uiGraphics->w, uiGraphics->h};
2690 SDL_Rect dst = {pad, SCREEN_H-TILE_H2-pad, 0, 0};
2691 // dst.x = p.getScreenX() + TILE_W3/2 - scrollX;
2692 // dst.y = p.getScreenY() - src.h/2 - scrollY;
2693 dst.x = p.getScreenX() - scrollX;
2694 dst.y = p.getScreenY() - scrollY - FONT_SPACING*3 - FONT_SPACING/2;
2695 // if (dst.x > SCREEN_W*2/3) dst.x -= TILE_W3 + src.w;
2696 // if (dst.y+src.h > screen->h-pad) dst.y = screen->h-pad - src.h;
2698 RenderTile(false, 0, p.getScreenX(), p.getScreenY());
2699 // SDL_BlitSurface(uiGraphics, &src, screen, &dst);
2701 // dst.x += src.w/2;
2703 if (currentLevelInfo)
2705 keyboardp = p;
2707 PrintC(true, dst.x, dst.y - FONT_SPACING/4, currentLevelInfo->name);
2709 if (currentLevelInfo->file[0]!=0)
2711 if (player_score > 0)
2713 if (progress.general.scoringOn)
2715 PrintC(false, dst.x, dst.y + FONT_SPACING*4 - FONT_SPACING/4, "Best:% 3d", player_score);
2716 PrintC(false, dst.x, dst.y + FONT_SPACING*5 - FONT_SPACING/4, "Par:% 3d", levelPar);
2718 else
2719 PrintC(false, dst.x, dst.y + FONT_SPACING*4 - FONT_SPACING/4, "Completed", player_score);
2721 else
2722 PrintC(false, dst.x, dst.y + FONT_SPACING*4 - FONT_SPACING/4, "Incomplete", player_score);
2727 // "Win"
2728 if (win && numUndo > 0 && time > undo[numUndo-1].endTime + 2)
2730 if (currentFile[0] && winFinal==0)
2732 LevelSave* l = progress.GetLevel(currentFile, true);
2734 new WinLoseScreen(true, player_score, showScoring ? levelPar : 0, l && showScoring && l->Completed() ? l->GetScore() : 0);
2736 if (l->IsNewCompletionBetter(player_score))
2738 l->SetScore(player_score);
2740 l->SetSolution(numUndo);
2742 for (int i=0; i<numUndo; i++)
2743 l->SetSolutionStep(i, undo[i].playerMovement);
2746 SaveProgress();
2749 winFinal = 1;
2751 else
2752 winFinal = 0;
2754 // Move up "level complete" writing so it doesn't feel like everything's ground to a halt...
2755 if (win && numUndo > 0 && time > undo[numUndo-1].endTime && !winFinal)
2757 double t = (time - undo[numUndo-1].endTime) / 2;
2758 t=1-t;
2759 t*=t*t;
2760 t=1-t;
2761 int y = SCREEN_H/3 - FONT_SPACING + 1;
2762 y = SCREEN_H + int((y-SCREEN_H)*t);
2763 PrintC(true, SCREEN_W/2, y, "Level Complete!");
2767 if (activeMenu)
2768 activeMenu->Render();
2770 if (!noMouse)
2772 // Edit cursor
2773 if (editMode)
2775 RenderTile(false, editTile, mousex+scrollX, mousey+scrollY);
2777 else
2779 Print(mousex, mousey-2, "\x7f");
2784 int Count(Tile t)
2786 return tileCount[t];
2788 int Swap(Tile t, Tile t2)
2790 const int num = Count(t) + Count(t2);
2791 if (t==t2 || num==0)
2792 return Count(t); // Nothing to do...
2794 int count=0;
2795 for (int x=0; x<MAP_SIZE; x++)
2796 for (int y=0; y<MAP_SIZE; y++)
2798 if (GetTile(Pos(x,y))==t)
2800 count++;
2801 SetTile(Pos(x,y), t2);
2803 else if (GetTile(Pos(x,y))==t2)
2805 count++;
2806 SetTile(Pos(x,y), t);
2808 if (count==num)
2809 return count;
2811 return count;
2813 int Replace(Tile t, Tile t2)
2815 const int num = Count(t);
2816 if (t==t2 || num==0)
2817 return num; // Nothing to do...
2819 int count=0;
2820 for (int x=0; x<MAP_SIZE; x++)
2821 for (int y=0; y<MAP_SIZE; y++)
2823 Pos p(x,y);
2824 if (GetTile(p)==t)
2826 count++;
2828 SetTile(p, t2, false);
2830 if (t==COLLAPSE_DOOR && t2==COLLAPSABLE)
2831 renderer(p).Add(new BuildRender(p, -1, 1, 1), time + (rand() & 255)*0.001);
2832 else if (t==COLLAPSE_DOOR2 && t2==COLLAPSABLE2)
2833 renderer(p).Add(new BuildRender(p, -1, 1, 1, 1), time + (rand() & 255)*0.001);
2834 else
2835 SetTile(p, t2);
2837 if (count==num)
2838 return count;
2841 return count;
2844 Tile editTile;
2845 bool editMode;
2846 void ResetUndo()
2848 UndoDone();
2849 undoTime = -1;
2850 numUndo = 0;
2851 win = false;
2854 void UpdateCursor(Pos const & s)
2856 static Pos _s;
2857 if (s.x!=_s.x || s.y!=_s.y)
2859 _s = s;
2861 char* sp = GetSpecial(s);
2862 char tmp[1000];
2863 tmp[0]='\0';
2864 if (sp)
2866 if (isMap)
2868 currentLevelInfo = 0;
2869 levelPar = player_score = -1;
2870 if (GetLevelState(s)>=2)
2872 LevelSave* l = progress.GetLevel(sp, true);
2873 if (l)
2875 currentLevelInfo = GetLevelInfo(sp);
2876 levelPar = GetPar(sp);
2877 player_score = l->GetScore();
2882 #ifdef EDIT
2883 sprintf(tmp, "Special(%d,%d): %s (%d)", s.x, s.y, sp ? sp : "<None>", GetPar(sp));
2884 SDL_WM_SetCaption(tmp, NULL);
2885 #endif
2887 else if (currentFile[0])
2889 #ifdef EDIT
2890 SDL_WM_SetCaption(currentFile, NULL);
2891 #endif
2892 if (isMap)
2893 currentLevelInfo = 0;
2898 virtual void Mouse(int x, int y, int dx, int dy, int button_pressed, int button_released, int button_held)
2900 if (activeMenu)
2902 activeMenu->Mouse(x,y,dx,dy,button_pressed,button_released,button_held);
2903 return;
2906 if (isFadeRendering)
2907 return;
2910 #ifndef EDIT
2911 if (button_pressed==2 || button_pressed==4 && isMap)
2913 KeyPressed(SDLK_ESCAPE, 0);
2914 keyState[SDLK_ESCAPE] = 0;
2915 return;
2917 #endif
2919 x += scrollX;
2920 y += scrollY;
2922 Pos s = Pos::GetFromWorld(x,y);
2923 if (tileSolid[GetTile(Pos::GetFromWorld(x,y+TILE_HUP))] == 1)
2924 s = Pos::GetFromWorld(x,y+TILE_HUP);
2926 mousep = s;
2928 UpdateCursor(s);
2930 #ifdef EDIT
2931 if (button_held & ~button_pressed & 4)
2933 scrollX -= dx;
2934 scrollY -= dy;
2936 #endif
2938 if (!editMode)
2940 if (isMap && (button_pressed & 1))
2942 ActivateSpecial(s, 1);
2943 return;
2945 if (!isMap && win && winFinal)
2947 if (button_pressed & 1)
2949 LoadMap();
2950 return;
2953 if(!isMap)
2955 if((button_pressed & 1) || (button_held & 1) && (numUndo==0 || time>=undo[numUndo-1].endTime))
2957 if(s.x==player.x && s.y==player.y)
2959 // Don't activate jump powerup without a new click
2960 if (button_pressed & 1)
2961 Input(-1);
2963 else if(s.x==player.x && s.y<player.y)
2964 Input(0);
2965 else if(s.x==player.x && s.y>player.y)
2966 Input(3);
2967 else if(s.y==player.y && s.x<player.x)
2968 Input(5);
2969 else if(s.y==player.y && s.x>player.x)
2970 Input(2);
2971 else if(s.y+s.x==player.y+player.x && s.x>player.x)
2972 Input(1);
2973 else if(s.y+s.x==player.y+player.x && s.x<player.x)
2974 Input(4);
2976 if ((button_pressed & 4) || (button_held & 4) && (undoTime < 0))
2977 Undo();
2979 return;
2982 #ifdef EDIT
2983 if (!button_pressed && !button_held)
2984 return;
2986 if (button_pressed==1)
2987 if (editTile<0)
2988 editTile = GetItem(s)==1 ? -3 : GetItem(s)==2 ? -2 : -1;
2990 if (button_held==1 || button_pressed==1)
2992 ResetUndo();
2993 if (editTile>=0)
2994 SetTile(s, editTile, true, false);
2995 else
2996 SetItem(s, editTile==-2 ? 0 : editTile==-1 ? 1 : 2, true, false);
2999 if (button_pressed==2)
3001 editTile = GetTile(s);
3004 if (button_pressed==8)
3006 editTile=editTile-1;
3007 if (editTile<=0) editTile=NumTileTypes-1;
3010 if (button_pressed==16)
3012 editTile=editTile+1;
3013 if (editTile<=0) editTile=1;
3014 if (editTile==NumTileTypes) editTile=0;
3017 if (button_pressed==64)
3019 ResetUndo();
3020 player = s;
3021 dead = false;
3022 renderer.player.Reset(-1);
3023 renderer.player.Add(new PlayerRender(player, GetHeight(player), dead), 0);
3026 if (button_pressed==256)
3028 char* fn = LoadSaveDialog(false, true, "Select level");
3029 if (fn)
3031 char * l = strstr(fn, "Levels");
3032 if(l)
3034 FILE * f = file_open(l,"rb");
3035 if (f)
3036 fclose(f);
3037 if (f)
3038 SetSpecial(s, l);
3039 else if (l[6]!=0 && l[7]=='_')
3040 SetSpecial(s, l+7);
3042 UpdateCursor(Pos(-1,-1));
3045 if (button_pressed==512)
3047 SetSpecial(s, NULL);
3048 UpdateCursor(Pos(-1,-1));
3050 if (button_pressed==1024)
3052 static char x[1000] = "";
3053 if (!(s.x<0 || s.x>=MAP_SIZE || s.y<0 || s.y>=MAP_SIZE))
3055 char tmp[1000];
3056 strcpy(tmp, x);
3057 if (GetSpecial(s))
3058 strcpy(x, GetSpecial(s));
3059 else
3060 x[0] = 0;
3061 SetSpecial(s, tmp[0] ? tmp : 0);
3062 if (!tmp[0])
3063 SetTile(s, EMPTY, true, false);
3067 if (button_pressed==32)
3069 editTile = editTile<0 ? 1 : -1;
3071 #endif // EDIT
3074 void CheckFinished()
3076 bool slow = false;
3077 if (Count(COLLAPSABLE)==0)
3079 if (Replace(COLLAPSE_DOOR, COLLAPSABLE) == 0)
3080 win = true;
3081 else
3082 slow = true;
3083 Replace(SWITCH, NORMAL);
3085 else
3086 win = false;
3088 if (Count(COLLAPSABLE2)==0)
3089 if (Replace(COLLAPSE_DOOR2, COLLAPSABLE2))
3090 slow = true;
3092 if (slow)
3093 time += BUILD_TIME;
3095 bool Collide(Pos p, bool high)
3097 Tile t = GetTile(p);
3098 // switch(t)
3099 // {
3100 // default:
3101 if (!high)
3102 return tileSolid[t]==1;
3103 else
3104 return false;
3105 // }
3107 void Undo()
3109 if (numUndo==0) return;
3111 UndoDone(); // Complete previous undo...
3113 numUndo--;
3115 if (time > undo[numUndo].endTime)
3116 time = undo[numUndo].endTime;
3117 undoTime = undo[numUndo].time;
3119 undo[numUndo].Restore(this);
3121 void UndoDone()
3123 if (undoTime < 0)
3124 return;
3125 renderer.Reset(undoTime);
3126 time = undoTime;
3127 undoTime = -1;
3129 void ScoreDestroy(Pos p)
3131 Tile t = GetTile(p);
3132 if (t==COLLAPSABLE || t==COLLAPSE_DOOR)
3134 else if (t != EMPTY)
3136 player_score += 10;
3140 bool LaserTile(Pos p, int mask, double fireTime)
3142 if (&renderer(p) == &renderer(Pos(-1,-1)))
3143 return false;
3144 //if (!renderer.Visible(p))
3145 // return false;
3147 TileRender* tr = 0;
3148 if (time <= renderer(p).GetLastTime())
3149 if (fireTime < renderer(p).GetLastTime())
3151 renderer(p).Add(tr = new TileRender(GetTile(p), p, mask), fireTime);
3152 ((TileRender*)renderer(p).GetStage(time+10/*HACKY!*/))->special |= mask;
3154 else
3156 tr = new TileRender(GetTile(p), p, mask | ((TileRender*)renderer(p).GetStage(time+10/*HACKY!*/))->special);
3157 renderer(p).Add(tr, fireTime);
3159 else
3160 renderer(p).Add(tr = new TileRender(GetTile(p), p, mask), fireTime);
3162 if (tr)
3164 tr->specialDuration = time + LASER_LINE_TIME - fireTime + LASER_FADE_TIME;
3166 return true;
3168 void FireGun(Pos newpos, Dir d, bool recurse, double fireTime)
3170 static Pos hits[100];
3171 static Dir hitDir[100];
3172 static int numHits=0;
3173 if (!recurse)
3174 numHits = 0;
3176 double starttime = fireTime;
3177 for (Dir fd=((d<0)?0:d); fd<((d<0)?MAX_DIR:d+1); fd++)
3179 fireTime = starttime;
3180 // starttime += 0.03;
3182 Pos p = newpos + fd;
3183 int range = 0;
3184 for (range; range<MAP_SIZE; range++, p=p+fd)
3186 Tile t = GetTile(p);
3187 if (tileSolid[t]!=-1)
3189 if (t!=TRAP)
3190 renderer(p).Add(new TileRender(tileSolid[t]==1 ? TILE_WHITE_WALL : TILE_WHITE_TILE, p), fireTime+0.1);
3192 int i;
3193 for (i=0; i<numHits; i++)
3194 if (hits[i]==p)
3195 break;
3196 if (i==numHits ||
3197 t==TRAP && (hitDir[i]&(1<<fd))==0
3200 if (i==numHits)
3202 if (i >= sizeof(hits)/sizeof(hits[0]))
3203 return;
3204 hitDir[i] = 1 << fd;
3205 hits[i] = p;
3206 numHits++;
3208 else
3210 hitDir[i] |= 1 << fd;
3212 if (t==TRAP)
3214 int dirmask =
3215 1<<((fd+2) % MAX_DIR)
3216 | 1<<((fd+MAX_DIR-2) % MAX_DIR);
3218 if (LaserTile(p, dirmask, fireTime))
3219 fireTime += (time+LASER_LINE_TIME - fireTime) / 40;
3220 // fireTime += LASER_SEGMENT_TIME;
3222 FireGun(p, (fd+1) % MAX_DIR, true, fireTime);
3223 FireGun(p, (fd+MAX_DIR-1) % MAX_DIR, true, fireTime);
3226 break;
3228 else
3230 LaserTile(p, 1<<(fd%3), fireTime);
3232 fireTime += (time+LASER_LINE_TIME - fireTime) / 40;
3233 // fireTime += LASER_SEGMENT_TIME;
3237 // renderer().Add(new LaserRender(newpos, fd, range), time);
3240 if (!recurse)
3242 double _time = time;
3243 time += LASER_LINE_TIME;
3244 for (int i=0; i<numHits; i++)
3246 Pos p = hits[i];
3247 Tile t = GetTile(p);
3249 if (t==TRAP)
3250 continue;
3252 ScoreDestroy(p);
3254 renderer(p).Add(new ExplosionRender(p, t==GUN), time);
3255 //renderer(p).Add(new TileRender(EMPTY, p), time+2);
3256 SetTile(p, EMPTY, false);
3258 if (GetItem(p))
3259 renderer(p,true).Add(new ItemRender(GetItem(p), 1, p), time);
3261 if (t==GUN)
3263 for (Dir j=0; j<MAX_DIR; j++)
3265 if (GetTile(p+j)!=EMPTY)
3267 renderer(p+j).Add(new TileRender(tileSolid[GetTile(p+j)]==1 ? TILE_WHITE_WALL : TILE_WHITE_TILE, p+j), time+0.05);
3268 renderer(p+j).Add(new ExplosionRender(p+j), time+0.2);
3270 if (GetItem(p+j))
3271 renderer(p+j,true).Add(new ItemRender(GetItem(p+j), 1, p), time);
3273 //renderer(p+j).Add(new TileRender(EMPTY, p+j), time+2.2);
3275 ScoreDestroy(p + j);
3276 SetTile(p + j, EMPTY, false);
3281 time += MAX(LASER_FADE_TIME, 0.15);
3282 //time = _time;
3283 CheckFinished();
3286 int GetLastPlayerRot()
3288 RenderStage* rs = renderer.player.GetStage(-1);
3289 if (!rs) return 3;
3290 return ((PlayerRender*)rs)->r;
3292 bool Input(Dir d)
3294 if (dead || win || isMap)
3295 return false;
3297 // Complete undo
3298 UndoDone();
3300 // Jump forwards in time to last move finishing
3301 if (numUndo > 0 && time < undo[numUndo-1].endTime)
3302 time = undo[numUndo-1].endTime;
3304 double realTime = time;
3305 double endAnimTime = time;
3306 bool high = (tileSolid[GetTile(player)] == 1);
3307 Pos playerStartPos = player;
3308 Pos oldpos = player;
3309 int oldPlayerHeight = GetHeight(oldpos);
3310 Pos newpos = player + d;
3312 int playerRot = GetLastPlayerRot();
3313 if (d!=-1 && d!=playerRot)
3315 while (d!=playerRot)
3317 if ((d+6-playerRot) % MAX_DIR < MAX_DIR/2)
3318 playerRot = (playerRot+1) % MAX_DIR;
3319 else
3320 playerRot = (playerRot+MAX_DIR-1) % MAX_DIR;
3322 time += 0.03;
3324 if (GetTile(oldpos) == FLOATING_BALL)
3326 TileRender* t = new TileRender(FLOATING_BALL, oldpos);
3327 t->special = playerRot + 256;
3328 renderer(oldpos).Add(t, time);
3330 renderer.player.Add(new PlayerRender(playerRot, Pos(-20,-20), oldPlayerHeight, Pos(-20,-20), oldPlayerHeight, dead), time);
3332 else
3334 PlayerRender *p = new PlayerRender(playerRot, player, oldPlayerHeight, player, oldPlayerHeight, dead);
3335 p->speed = 0;
3336 renderer.player.Add(p, time);
3340 time += 0.03;
3343 if (d<0 && player_items[1]==0)
3344 return false;
3346 if (d >= 0)
3348 if (tileSolid[GetTile(newpos)] == -1)
3350 time = realTime;
3351 return false;
3353 if (Collide(newpos, high))
3355 time = realTime;
3356 return false;
3360 // Don't change any real state before this point!
3361 if (numUndo >= MAX_UNDO)
3363 numUndo--;
3364 for(int i=0; i<MAX_UNDO-1; i++)
3365 undo[i] = undo[i+1];
3367 undo[numUndo].New(d, player, player_items, time, player_score);
3369 if (d<0)
3371 player_items[1]--;
3374 int old_score=player_score;
3376 double time0 = time;
3377 time += 0.15; //Time for leave-tile fx
3379 switch (GetTile(oldpos))
3381 case COLLAPSABLE:
3382 SetTile(oldpos, EMPTY);
3383 renderer(oldpos).Add(new DisintegrateRender(oldpos), time);
3384 CheckFinished();
3385 break;
3387 case COLLAPSE_DOOR:
3388 // Don't need to CheckFinished - can't be collapse doors around
3389 // unless there're still collapsable tiles around.
3390 SetTile(oldpos, EMPTY);
3391 renderer(oldpos).Add(new DisintegrateRender(oldpos, 1), time);
3392 break;
3394 case COLLAPSABLE2:
3395 SetTile(oldpos, COLLAPSABLE, false);
3396 renderer(oldpos).Add(new DisintegrateRender(oldpos, 0, 1), time);
3397 player_score += 10;
3398 CheckFinished();
3399 break;
3401 case COLLAPSE_DOOR2:
3402 SetTile(oldpos, COLLAPSE_DOOR, false);
3403 renderer(oldpos).Add(new DisintegrateRender(oldpos, 1, 1), time);
3404 player_score += 10;
3405 break;
3407 case COLLAPSABLE3:
3408 SetTile(oldpos, COLLAPSABLE2);
3409 break;
3412 time = time0; //End of leave-tile fx
3414 int retry_pos_count=0;
3415 retry_pos:
3416 retry_pos_count++;
3418 if (GetItem(newpos)==1)
3420 renderer(newpos, true).Add(new ItemCollectRender(GetItem(newpos), newpos), time + JUMP_TIME/2);
3421 SetItem(newpos, 0, false);
3422 player_items[0]++;
3424 if (GetItem(newpos)==2)
3426 renderer(newpos, true).Add(new ItemCollectRender(GetItem(newpos), newpos), time + JUMP_TIME/2);
3427 SetItem(newpos, 0, false);
3428 player_items[1]++;
3431 if (GetTile(player) == FLOATING_BALL)
3433 TileRender* t = new TileRender(FLOATING_BALL, player);
3434 t->special = 0;
3435 renderer(oldpos).Add(t, time);
3438 PlayerRender *p = new PlayerRender(playerRot, player, oldPlayerHeight, newpos, GetHeight(newpos), dead);
3440 // alternate leg (hacky!)
3441 if (1)
3443 static int l=0;
3444 l++;
3445 p->type = l & 1;
3448 if (retry_pos_count!=0 && GetTile(player)==TRAP)
3450 p->speed /= 1.5;
3451 p->type = 2;
3453 if (d==-1)
3454 p->speed = JUMP_TIME * 1.5;
3455 renderer.player.Add(p, time);
3456 endAnimTime = MAX(endAnimTime, time + p->speed+0.001);
3457 time += p->speed;
3459 player = newpos;
3461 switch (GetTile(newpos))
3463 case COLLAPSABLE:
3464 renderer(newpos).Add(new TileRender(TILE_GREEN_CRACKED, newpos), time);
3465 break;
3466 case COLLAPSE_DOOR:
3467 renderer(newpos).Add(new TileRender(TILE_GREEN_CRACKED_WALL, newpos), time);
3468 break;
3469 case COLLAPSABLE2:
3470 renderer(newpos).Add(new TileRender(TILE_BLUE_CRACKED, newpos), time);
3471 break;
3472 case COLLAPSE_DOOR2:
3473 renderer(newpos).Add(new TileRender(TILE_BLUE_CRACKED_WALL, newpos), time);
3474 break;
3476 case EMPTY:
3477 dead = true;
3478 break;
3480 case BUILDER:
3482 double pretime = time;
3483 bool done = false;
3484 time += 0.15;
3485 for (Dir fd=0; fd<MAX_DIR; fd++)
3487 Tile t2 = GetTile(newpos + fd);
3488 if (t2==EMPTY)
3490 done = true;
3491 SetTile(newpos+fd, COLLAPSABLE, false);
3492 renderer(newpos+fd).Add(new BuildRender(newpos+fd, fd, 0), time);
3494 else if (t2==COLLAPSABLE)
3496 done = true;
3497 SetTile(newpos+fd, COLLAPSE_DOOR, false);
3498 renderer(newpos+fd).Add(new BuildRender(newpos+fd, fd, 1), time);
3501 if (done) time += BUILD_TIME;
3502 else time = pretime;
3503 CheckFinished();
3504 endAnimTime = MAX(endAnimTime, time + 0.1);
3506 break;
3508 case SWITCH:
3509 Swap(COLLAPSE_DOOR, COLLAPSABLE);
3510 break;
3512 case FLOATING_BALL:
3514 int step=0;
3515 renderer.player.Add(new PlayerRender(playerRot, Pos(-30,-30), 0, Pos(-30,-30), 0, dead), time);
3516 while (tileSolid[GetTile(newpos+d)]==-1)
3518 step++;
3520 if (!renderer.Visible(newpos+d))
3522 TileRender* r = new TileRender(FLOATING_BALL, newpos);
3523 r->special = 512;
3524 renderer(newpos).Add(r, time);
3526 PlayerRender* pr = new PlayerRender(playerRot, newpos, 0, newpos, 0, dead);
3527 pr->speed = JUMP_TIME*1;
3528 renderer.player.Add(pr, time);
3530 time += pr->speed;
3532 dead = 1;
3533 break;
3535 oldpos = newpos;
3536 newpos = oldpos + d;
3538 SetTile(oldpos, EMPTY, false);
3539 SetTile(newpos, FLOATING_BALL, false);
3541 renderer(oldpos).Add(new TileRotateRender(FLOATING_BALL, oldpos, d, 2), time);
3542 renderer(oldpos).Add(new TileRender(EMPTY, oldpos), time + ROTATION_TIME/2);
3543 renderer(newpos).Add(new TileRotateRender(FLOATING_BALL, newpos, (d+3)%MAX_DIR, 3), time + ROTATION_TIME/2);
3545 // PlayerRender *p = new PlayerRender(playerRot, oldpos, 0, newpos, 0, dead);
3546 // p->speed = ROTATION_TIME*0.9;
3547 // renderer.player.Add(p, time);
3549 endAnimTime = MAX(endAnimTime, time + ROTATION_TIME + ROTATION_TIME/2);
3550 time += ROTATION_TIME;
3552 player = newpos;
3553 // renderer.player.Add(new PlayerRender(playerRot, player, 0, player, 0, 0), time);
3554 if (dead)
3557 else
3559 TileRender* r = new TileRender(FLOATING_BALL, newpos);
3560 r->special = playerRot + 256;
3561 renderer(newpos).Add(r, time);
3564 break;
3566 case LIFT_DOWN:
3567 case LIFT_UP:
3569 SetTile(newpos, GetTile(newpos)==LIFT_UP ? LIFT_DOWN : LIFT_UP, false);
3570 renderer(newpos).Add(new TileRender(GetTile(newpos), newpos, 1), time);
3572 PlayerRender *p = new PlayerRender(playerRot, newpos, 1-GetHeight(newpos), newpos, GetHeight(newpos), dead);
3573 renderer.player.Add(p, time);
3574 endAnimTime = MAX(endAnimTime, time + JUMP_TIME);
3576 break;
3578 case TRAMPOLINE:
3579 if (d<0) break;
3581 oldpos = newpos;
3582 if (Collide(newpos + d, high))
3583 break;
3584 if (Collide((newpos + d) + d, high) == 1)
3585 newpos = (newpos + d);
3586 else
3587 newpos = (newpos + d) + d;
3588 if (tileSolid[GetTile(newpos)] == -1)
3589 dead=1;
3590 //player = newpos;
3591 goto retry_pos;
3593 case SPINNER:
3595 for (Dir d=0; d<MAX_DIR; d++)
3597 Tile tmp = GetTile(newpos + d);
3598 renderer(newpos + d).Add(new TileRotateRender(tmp, newpos + d, (d+2)%MAX_DIR, false), time);
3600 Tile tmp = GetTile(newpos + Dir(MAX_DIR-1));
3601 for (Dir d=0; d<MAX_DIR; d++)
3603 Tile t2 = GetTile(newpos + d);
3604 SetTile(newpos + d, tmp, false);
3605 renderer(newpos + d).Add(new TileRotateRender(tmp, newpos + d, (d+4)%MAX_DIR, true), time + ROTATION_TIME/2);
3606 if (GetItem(newpos + d))
3607 renderer(newpos + d,true).Add(new ItemRender(GetItem(newpos + d), GetTile(newpos + d)==EMPTY, newpos+d), time + ROTATION_TIME/2);
3609 tmp = t2;
3611 endAnimTime = MAX(endAnimTime, time+ROTATION_TIME);
3612 // renderer(newpos).Add(new TileRotateRender(SPINNER, Dir(0), 0), time);
3614 break;
3616 case TRAP:
3618 if (d<0) break;
3620 if (player_items[0]==0)
3622 if (tileSolid[GetTile(newpos + d)] == 1)
3623 break;
3624 newpos = newpos + d;
3625 if (tileSolid[GetTile(newpos)] == -1)
3626 dead=1;
3627 //player = newpos;
3628 goto retry_pos;
3630 else
3632 SetTile(newpos, COLLAPSABLE3);
3633 player_items[0]--;
3636 break;
3638 case GUN:
3640 FireGun(newpos, d, false, time);
3642 endAnimTime = MAX(endAnimTime, time);
3644 if (GetTile(newpos)==EMPTY)
3646 PlayerRender* pr = new PlayerRender(playerRot, player, 0, player, 0, dead);
3647 pr->speed = JUMP_TIME*1;
3648 renderer.player.Add(pr, time);
3650 time += pr->speed;
3651 dead = 1;
3655 Pos hits[MAX_DIR];
3656 int numHits=0;
3658 for (Dir fd=((d<0)?0:d); fd<((d<0)?MAX_DIR:d+1); fd++)
3660 Pos p = newpos + fd;
3661 int range = 0;
3662 for (range; range<MAP_SIZE; range++, p=p+fd)
3664 Tile t = GetTile(p);
3665 if (tileSolid[t]!=-1)
3667 hits[numHits++] = p;
3668 break;
3672 renderer().Add(new LaserRender(newpos, fd, range), time);
3675 double _time = time;
3676 time += 0.25;
3677 for (int i=0; i<numHits; i++)
3679 Pos p = hits[i];
3680 Tile t = GetTile(p);
3682 renderer().Add(new ExplosionRender(p), time);
3683 ScoreDestroy(p);
3684 SetTile(p, EMPTY);
3686 if (t==GUN)
3688 for (Dir j=0; j<MAX_DIR; j++)
3690 ScoreDestroy(p + j);
3691 SetTile(p + j, EMPTY);
3693 if (GetTile(newpos)==EMPTY)
3694 dead = 1;
3697 endAnimTime = MAX(endAnimTime, time);
3699 time = _time;
3701 CheckFinished();
3703 break;
3707 endAnimTime = MAX(endAnimTime, time);
3709 if (dead)
3711 win = false;
3713 PlayerRender* pr = new PlayerRender(player, 0, dead);
3714 pr->speed = 0; // Don't sit around before disappearing!
3715 renderer.player.Add(pr, time);
3717 // If the tile we're drowning on isn't visible, give the ownership of the splash effect to the player, rather than a tile.
3718 if (renderer.Visible(player))
3719 renderer(player).Add(new ExplosionRender(player, 0, 1), time);
3720 else
3721 renderer.player.Add(new ExplosionRender(player, 0, 1), time);
3723 endAnimTime = MAX(endAnimTime, time+2);
3726 time = realTime;
3728 player_score += 1;
3730 undo[numUndo].endTime = endAnimTime;
3731 numUndo++;
3733 return true;
3735 void Update(double timedelta)
3737 while(deadMenu)
3738 delete deadMenu;
3740 if (activeMenu)
3742 activeMenu->Update(timedelta);
3744 else
3745 UpdateKeys();
3747 for (int i=0; i<SDLK_LAST; i++)
3748 if (keyState[i])
3749 keyState[i] = 1;
3751 if (activeMenu)
3752 return;
3754 if (isMap && isRenderMap)
3756 double min = 50;
3757 static double scrollHi = 0;
3758 double x = 0;
3759 #ifndef EDIT
3760 // if (!noMouse)
3762 int xx = noMouse ? keyboardp.getScreenX()-scrollX : mousex;
3763 if (xx > SCREEN_W) xx = SCREEN_W;
3764 int w = TILE_W2*4;
3765 if (xx < w)
3766 x = (double)xx / (w) - 1;
3767 if (xx > SCREEN_W - w)
3768 x = 1 - (double)(SCREEN_W-xx) / (w);
3769 x *= 500;
3770 if (x<-min || x>min)
3772 scrollHi += timedelta * x;
3773 scrollX += (int)scrollHi;
3774 scrollHi -= (int)scrollHi;
3777 #endif
3779 if (undoTime>=0 && undoTime < time)
3781 double acc = (time - undoTime) / 2;
3782 if (acc < 3) acc = 3;
3783 time -= timedelta * acc;
3784 if (undoTime >= time)
3785 UndoDone();
3787 else
3789 time += timedelta;
3790 if (turboAnim)
3791 time += timedelta * 20;
3794 void FileDrop(const char* filename)
3796 LoadSave(filename, false);
3798 void UpdateKeys()
3800 #ifdef EDIT
3801 if (keyState[SDLK_LALT] || keyState[SDLK_LCTRL])
3802 return;
3803 #endif
3805 if (!isMap && !editMode && undoTime < 0)
3807 if (keyState['z'] || keyState[SDLK_BACKSPACE] || keyState['u'])
3809 Undo();
3810 return;
3813 if (isMap && !editMode)
3816 if ((keyState['q'] | keyState[SDLK_KP7]) & 2) keyboardp.x--;
3817 else if ((keyState['d'] | keyState[SDLK_KP3]) & 2) keyboardp.x++;
3818 else if ((keyState['e'] | keyState[SDLK_KP9]) & 2) keyboardp.x++, keyboardp.y--;
3819 else if ((keyState['a'] | keyState[SDLK_KP1]) & 2) keyboardp.x--, keyboardp.y++;
3820 else if ((keyState['w'] | keyState[SDLK_KP8] | keyState[SDLK_UP]) & 2) keyboardp.y--;
3821 else if ((keyState['s'] | keyState[SDLK_KP2] | keyState[SDLK_DOWN]) & 2) keyboardp.y++;
3822 else if ((keyState[SDLK_LEFT]) & 2) keyboardp.x--, keyboardp.y+=keyboardp.x&1;
3823 else if (((keyState[SDLK_RIGHT]) & 2)) { if (keyboardp.x < mapRightBound) keyboardp.y-=keyboardp.x&1, keyboardp.x++; }
3824 else if ((keyState[SDLK_RETURN] | keyState[SDLK_KP5] | keyState[SDLK_SPACE] | keyState[SDLK_KP_ENTER]) & 2)
3826 // Simulate user clicking on it...
3827 Mouse(keyboardp.getScreenX()-scrollX, keyboardp.getScreenY()-scrollY, 0, 0, 1, 0, 0);
3828 noMouse = 1;
3829 return;
3831 else
3833 if (noMouse)
3834 UpdateCursor(keyboardp);
3835 return;
3837 int min[21] = { 17, 16, 15, 14, 13, 13, 13, 13, 13, 13, 12, 11, 11, 13, 12, 11, 8, 8, 7, 6, 7 };
3838 int max[21] = { 20, 20, 19, 19, 19, 19, 18, 21, 20, 20, 19, 19, 18, 18, 17, 16, 16, 16, 15, 15, 14 };
3839 if (keyboardp.x < 3) keyboardp.x = 3;
3840 if (keyboardp.x > mapRightBound) keyboardp.x = mapRightBound;
3842 if (keyboardp.y < min[keyboardp.x-3]) keyboardp.y = min[keyboardp.x-3];
3843 if (keyboardp.y > max[keyboardp.x-3]) keyboardp.y = max[keyboardp.x-3];
3844 noMouse = 1;
3845 UpdateCursor(keyboardp);
3847 else if (!editMode && (numUndo==0 || time>=undo[numUndo-1].endTime))
3849 static int usedDiag = 0;
3851 if (keyState['q'] || keyState[SDLK_KP7]) HandleKey('q', 0);
3852 else if (keyState['w'] || keyState[SDLK_KP8]) HandleKey('w', 0);
3853 else if (keyState['e'] || keyState[SDLK_KP9]) HandleKey('e', 0);
3854 else if (keyState['a'] || keyState[SDLK_KP1]) HandleKey('a', 0);
3855 else if (keyState['s'] || keyState[SDLK_KP2]) HandleKey('s', 0);
3856 else if (keyState['d'] || keyState[SDLK_KP3]) HandleKey('d', 0);
3858 else if (keyState[SDLK_UP] && keyState[SDLK_LEFT]) HandleKey('q', 0), usedDiag=1;
3859 else if (keyState[SDLK_UP] && keyState[SDLK_RIGHT]) HandleKey('e', 0), usedDiag=1;
3860 else if (keyState[SDLK_DOWN] && keyState[SDLK_LEFT]) HandleKey('a', 0), usedDiag=1;
3861 else if (keyState[SDLK_DOWN] && keyState[SDLK_RIGHT]) HandleKey('d', 0), usedDiag=1;
3862 else if (keyState[SDLK_UP] && !usedDiag) HandleKey('w', 0);
3863 else if (keyState[SDLK_DOWN] && !usedDiag) HandleKey('s', 0);
3865 else usedDiag = 0;
3868 void KeyReleased(int key)
3870 keyState[key] = 0;
3872 bool KeyPressed(int key, int mod)
3874 keyState[key] = 2;
3876 if (activeMenu)
3878 bool eat = activeMenu->KeyPressed(key, mod);
3879 if (!activeMenu)
3880 memset(keyState, 0, sizeof(keyState));
3881 return eat;
3883 else
3885 if ((key==SDLK_ESCAPE && (mod & KMOD_CTRL)))
3887 if (mod & KMOD_SHIFT)
3889 time = 0;
3890 renderer.Reset();
3891 LoadSaveProgress(false);
3894 LoadMap();
3897 if (isFadeRendering)
3898 return false;
3900 return HandleKey(key, mod);
3903 bool HandleKey(int key, int mod)
3905 turboAnim = 0;
3907 #ifdef CHEAT
3908 if (isMap && key=='r' && (mod & KMOD_ALT))
3910 progress.Clear();
3911 LoadMap();
3913 #endif
3915 if (0) {}
3917 else if ((key=='p' && !editMode || key==SDLK_PAUSE || key==SDLK_ESCAPE))
3919 noMouse = 1;
3920 new PauseMenu(isMap, progress.GetLevel(STARTING_LEVEL, true)->Completed(), progress.general.endSequence>=1, progress.general.endSequence>=2);
3923 #ifdef EDIT
3924 else if (key=='e' && (mod & KMOD_ALT))
3925 editMode = !editMode;
3927 else if (key=='p' && (mod & KMOD_ALT) && numUndo>0
3928 || key>='0' && key<='9' && (mod & KMOD_SHIFT) && !isMap)
3930 if (key>='0' && key<='9')
3931 levelDiff = (key=='0') ? 10 : key-'0';
3933 if (key=='p' && levelPar==0)
3934 levelPar = player_score;
3936 if (numUndo)
3939 undo[numUndo-1].Restore(this);
3940 while (--numUndo);
3942 time = 0;
3943 if (LoadSave(currentFile, true))
3945 if (key>='0' && key<='9')
3946 LoadMap();
3949 #endif
3951 /////////////////////////////////////////////////////////////////////////
3952 if (isMap && !editMode)
3953 return false;
3955 else if (key==SDLK_KP9 || key=='e') Input(1), noMouse=1;
3956 else if (key==SDLK_KP3 || key=='d') Input(2), noMouse=1;
3957 else if (key==SDLK_KP1 || key=='a') Input(4), noMouse=1;
3958 else if (key==SDLK_KP7 || key=='q') Input(5), noMouse=1;
3959 else if (key==SDLK_KP8 || key=='w') Input(0), noMouse=1;
3960 else if (key==SDLK_KP2 || (key=='s' && (((mod & (KMOD_CTRL|KMOD_ALT))==0)||!editMode))) Input(3), noMouse=1;
3961 else if (key==SDLK_KP5 || key==SDLK_SPACE || key==SDLK_RETURN || key==SDLK_KP_ENTER)
3963 noMouse=1;
3964 if (win && winFinal)
3965 LoadMap(), memset(keyState, 0, sizeof(keyState));
3966 else
3967 Input(-1);
3970 else if (key=='r' && (mod & KMOD_CTRL))
3971 LoadSave(currentFile, false);
3973 #ifdef EDIT
3974 else if (key=='z' && (mod & KMOD_ALT))
3976 if (numUndo>0 && !isMap)
3978 time = undo[numUndo-1].endTime;
3979 undoTime = undo[0].time;
3982 undo[numUndo-1].Restore(this);
3983 while (--numUndo);
3986 #endif
3987 else if (key=='z' || key==SDLK_BACKSPACE || key==SDLK_DELETE || key=='u')
3989 if (!isMap)
3990 Undo();
3993 #ifdef EDIT
3994 else if (key=='s' && (mod & KMOD_ALT)){
3995 if (win && strlen(currentFile)>0 && !isMap)
3997 char tmp[1000];
3998 strcpy(tmp, currentFile);
3999 ChangeSuffix(tmp, "sol");
4000 FILE* f = file_open(tmp, "wb");
4001 if (f)
4003 for (int i=0; i<numUndo; i++)
4005 fputc(undo[i].playerMovement, f);
4007 fclose(f);
4011 #endif
4013 #ifdef CHEAT
4014 else if (key=='/' && (mod & KMOD_ALT)){
4015 turboAnim = 1;
4016 if (!isMap)
4018 while (numUndo)
4019 Undo();
4020 ResetLevel();
4022 if (mod & KMOD_SHIFT)
4024 LevelSave* l = progress.GetLevel(currentFile, false);
4025 if (l && l->Completed())
4027 for (int i=0; i<l->bestSolutionLength; i++)
4028 Input(l->bestSolution[i]);
4029 time = 0;
4031 if (!win && l)
4032 l->Clear();
4034 else
4036 char tmp[1000];
4037 strcpy(tmp, currentFile);
4038 ChangeSuffix(tmp, "sol");
4039 FILE* f = file_open(tmp, "rb");
4040 if (f)
4042 int dir;
4043 while ((dir = fgetc(f)) != -1)
4045 if (dir==0xff)
4046 dir = -1;
4047 Input(dir);
4049 time = 0;
4050 fclose(f);
4052 if (!win)
4053 remove(tmp);
4058 #endif
4060 #ifdef EDIT
4061 else if (!editMode)
4062 return false;
4064 else if (key>='0' && key<='9' && (mod & KMOD_ALT) && !isMap)
4065 levelPar = levelPar*10 + key-'0';
4066 else if (key==SDLK_BACKSPACE && (mod & KMOD_ALT) && !isMap)
4067 levelPar /= 10;
4069 else if (key=='i')
4070 Mouse(mousex, mousey, 0, 0, 32, 0, mouse_buttons);
4071 else if (key=='p' && !(mod & KMOD_ALT))
4072 Mouse(mousex, mousey, 0, 0, 64, 0, mouse_buttons);
4073 else if (key=='x')
4074 Mouse(mousex, mousey, 0, 0, 128, 0, mouse_buttons);
4075 else if (key==SDLK_RETURN)
4076 Mouse(mousex, mousey, 0, 0, 256, 0, mouse_buttons);
4077 else if (key==SDLK_BACKSPACE)
4078 Mouse(mousex, mousey, 0, 0, 512, 0, mouse_buttons);
4079 else if (key=='c')
4080 Mouse(mousex, mousey, 0, 0, 1024, 0, mouse_buttons);
4082 else if (key=='s' && (mod & KMOD_CTRL)){
4083 char *fn = LoadSaveDialog(true, true, "Save level");
4084 LoadSave(fn, true);
4085 SDL_WM_SetCaption(currentFile, NULL);
4088 else if (key=='o' && (mod & KMOD_CTRL)){
4089 char* fn = LoadSaveDialog(false, true, "Open level");
4090 LoadSave(fn, false);
4091 SDL_WM_SetCaption(currentFile, NULL);
4093 #endif
4095 else
4096 return false;
4098 return true;
4100 void LoadGraphics()
4102 #define X(NAME,FILE,ALPHA) NAME = Load("graphics/" FILE BMP_SUFFIX, ALPHA);
4103 #include "gfx_list.h"
4105 static int first = 1;
4106 if (first)
4108 first = false;
4109 MakeFont();
4110 MakeTileInfo();
4113 // unsigned int d = {
4115 // };
4116 // static SDL_Cursor * c = SDL_CreateCursor(data, mask, 32, 32, 1, 1);
4117 // SDL_SetCursor(c);
4118 SDL_ShowCursor(0);
4120 void FreeGraphics()
4122 #define X(NAME,FILE,ALPHA) if (NAME) SDL_FreeSurface(NAME), NAME=0;
4123 #include "gfx_list.h"
4125 virtual void ScreenModeChanged()
4127 // FreeGraphics();
4128 // LoadGraphics();
4132 MAKE_STATE(HexPuzzle, SDLK_F1, false);
4134 char * HexPuzzle::loadPtr = 0;
4135 char * HexPuzzle::endLoad = 0;
4137 #endif //USE_OPENGL