Add support for wheel mouse
[hex-a-hop.git] / hex_puzzzle.cpp
blob21a796863381ae9694dfc0cb64dab3b8af9e6f93
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
17 */
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 typedef int Tile;
448 typedef int Dir;
449 struct Pos{
450 int x,y;
451 Pos() : x(0), y(0) {}
452 Pos(int a, int b) : x(a), y(b) {}
453 bool operator == (Pos const & p) const
455 return x==p.x && y==p.y;
457 Pos operator + (Dir const d) const
459 return Pos(
460 x + ((d==1 || d==2) ? 1 : (d==4 || d==5) ? -1 : 0),
461 y + ((d==0 || d==1) ? -1 : (d==3 || d==4) ? 1 : 0)
464 int getScreenX() const {
465 return x*TILE_W2;
467 int getScreenY() const {
468 return x*TILE_H1 + y*TILE_H2;
470 static Pos GetFromWorld(double x, double y)
472 x += TILE_W3/2;
473 y += TILE_H1;
474 int tx, ty;
475 tx = (int)floor(x/TILE_W2);
476 y -= tx*TILE_H1;
477 ty = (int)floor(y/TILE_H2);
479 y -= ty * TILE_H2;
480 x -= tx * TILE_W2;
482 if (x < TILE_W1 && y < TILE_H1)
483 if (x*TILE_H1 + y * TILE_W1 < TILE_H1*TILE_W1)
484 tx--;
485 if (x < TILE_W1 && y > TILE_H1)
486 if (x*TILE_H1 + (TILE_H2-y) * TILE_W1 < TILE_H1*TILE_W1)
487 tx--, ty++;
489 return Pos(tx, ty);
492 Pos mousep(0,0), keyboardp(4,20);
494 class RenderObject;
496 struct RenderStage
498 virtual void Render(RenderObject* r, double time, bool reflect) = 0;
499 virtual int GetDepth(double time) { return 1; }
502 class RenderObject
504 RenderStage** stage;
505 double* time;
506 int numStages;
507 int maxStages;
508 int currentStage;
509 public:
510 double seed;
511 double currentTime;
512 private:
514 void Reserve()
516 if (maxStages <= numStages)
518 maxStages = maxStages ? maxStages*2 : 4;
519 stage = (RenderStage**) realloc(stage, sizeof(stage[0])*maxStages);
520 time = (double*) realloc(time, sizeof(time[0])*maxStages);
523 public:
524 RenderObject() : stage(0), time(0), numStages(0), maxStages(0), currentStage(0)
526 // TODO: use a random number with better range
527 // or maybe make seed an int or float...
528 seed = rand() / (double)RAND_MAX;
530 ~RenderObject()
532 free(stage); free(time);
534 bool Active(double t)
536 if (numStages==0) return false;
537 if (t < time[0]) return false;
538 return true;
540 void UpdateCurrent(double t)
542 if (currentStage >= numStages) currentStage = numStages-1;
543 if (currentStage < 0) currentStage = 0;
545 while (currentStage>0 && time[currentStage]>t)
546 currentStage--;
547 while (currentStage<numStages-1 && time[currentStage+1]<=t)
548 currentStage++;
550 currentTime = t;
552 RenderStage* GetStage(double t)
554 if (t==-1 && numStages>0)
555 return stage[numStages-1];
557 if (!Active(t)) return 0;
558 UpdateCurrent(t);
559 return stage[currentStage];
561 double GetLastTime()
563 return numStages>0 ? time[numStages-1] : -1;
565 void Render(double t, bool reflect)
567 if (!Active(t))
568 return;
569 UpdateCurrent(t);
570 stage[currentStage]->Render(this, t - time[currentStage], reflect);
572 int GetDepth(double t)
574 if (!Active(t))
575 return -1;
576 UpdateCurrent(t);
577 return stage[currentStage]->GetDepth(t - time[currentStage]);
579 void Reset(double t)
581 if (t<0)
582 numStages = currentStage = 0;
583 else
585 while (numStages > 0 && time[numStages-1] >= t)
586 numStages--;
589 void Wipe()
591 if (currentStage > 0 && numStages > 0)
593 memmove(&time[0], &time[currentStage], sizeof(time[0]) * numStages-currentStage);
594 memmove(&stage[0], &stage[currentStage], sizeof(stage[0]) * numStages-currentStage);
595 numStages -= currentStage;
596 currentStage = 0;
599 void Add(RenderStage* s, double t)
601 int i=0;
603 if (currentStage<numStages && time[currentStage]<=t)
604 i = currentStage;
606 while (i<numStages && time[i]<t)
607 i++;
609 if (i<numStages && time[i]==t)
610 stage[i]=s;
611 else
613 Reserve();
615 if (i<numStages)
617 memmove(&time[i+1], &time[i], (numStages-i) * sizeof(time[0]));
618 memmove(&stage[i+1], &stage[i], (numStages-i) * sizeof(stage[0]));
621 numStages++;
622 time[i] = t;
623 stage[i] = s;
628 class WorldRenderer
630 #define SIZE 30
631 #define FX 10
632 RenderObject tile[SIZE][SIZE][2];
633 RenderObject fx[FX];
634 int fxPos;
636 public:
637 RenderObject player;
638 RenderObject dummy;
640 WorldRenderer()
642 Reset();
645 void Reset(double t = -1)
647 fxPos = 0;
648 player.Reset(t);
649 dummy.Reset(-1);
651 for (int i=0; i<SIZE; i++)
652 for (int j=0; j<SIZE; j++)
653 for (int q=0; q<2; q++)
654 tile[i][j][q].Reset(t);
656 for (int j=0; j<FX; j++)
657 fx[j].Reset(t);
660 void Wipe()
662 player.Wipe();
663 dummy.Reset(-1);
665 for (int i=0; i<SIZE; i++)
666 for (int j=0; j<SIZE; j++)
667 for (int q=0; q<2; q++)
668 tile[i][j][q].Wipe();
670 for (int j=0; j<FX; j++)
671 fx[j].Wipe();
674 bool Visible(Pos p)
676 int x0 = (scrollX+TILE_W2) / TILE_W2;
677 int x1 = (scrollX+SCREEN_W+TILE_W3+TILE_W1) / TILE_W2;
678 if (p.x<0 || p.y<0 || p.x>=SIZE || p.y>=SIZE) return false;
679 if (p.x<x0) return false;
680 if (p.x>=x1-1) return false;
681 for (int j0=0; j0<SIZE*3; j0++)
683 if (j0 * TILE_H1 < scrollY-TILE_H1) continue;
684 if (j0 * TILE_H1 > scrollY+SCREEN_H+TILE_H1) break;
685 int i = j0&1;
686 int j = j0>>1;
687 j -= (x0-i)/2;
688 i += (x0-i)/2*2;
689 if (j>=SIZE) i+=(j+1-SIZE)*2, j=SIZE-1;
690 for (; i<x1 && j>=0; i+=2, j--)
692 if (Pos(i,j)==p)
693 return true;
696 return false;
699 void Render(double t, bool reflect)
701 dummy.Reset(-1);
703 int playerDepth = player.GetDepth(t);
704 if (reflect) playerDepth-=4;
705 if (playerDepth<0)
706 player.Render(t, reflect);
708 int x0 = (scrollX+TILE_W2) / TILE_W2;
709 int x1 = (scrollX+SCREEN_W+TILE_W3+TILE_W1) / TILE_W2;
710 x0 = MAX(x0, 0);
711 x1 = MIN(x1, SIZE);
712 for (int j0=0; j0<SIZE*3; j0++)
714 if (j0 * TILE_H1 < scrollY-TILE_H1) continue;
715 if (j0 * TILE_H1 > scrollY+SCREEN_H+TILE_H1) break;
716 int i = j0&1;
717 int j = j0>>1;
718 j -= (x0-i)/2;
719 i += (x0-i)/2*2;
720 if (j>=SIZE) i+=(j+1-SIZE)*2, j=SIZE-1;
721 for (; i<x1 && j>=0; i+=2, j--)
723 for (int q=reflect?1:0; q!=2 && q!=-1; q += (reflect ? -1 : 1))
724 if (tile[i][j][q].Active(t))
726 tile[i][j][q].Render(t, reflect);
730 if (playerDepth==j0 || j0==SIZE*3 && playerDepth>j0)
731 player.Render(t, reflect);
734 for (int j=0; j<FX; j++)
735 if(fx[j].Active(t))
737 fx[j].Render(t, reflect);
741 RenderObject & operator () ()
743 fxPos++;
744 if (fxPos==FX) fxPos = 0;
745 return fx[fxPos];
747 RenderObject & operator () (Pos const & p, bool item=false)
749 if (p.x<0 || p.y<0 || p.x>=SIZE || p.y>=SIZE)
750 return dummy;
751 return tile[p.x][p.y][item ? 1 : 0];
755 void RenderTile(bool reflect, int t, int x, int y, int cliplift)
757 SDL_Rect src = tile[reflect][t];
758 SDL_Rect dst = {x-scrollX-GFX_SIZE/2, y-scrollY-GFX_SIZE+TILE_H1};
759 dst.x += tileOffset[reflect][t][0];
760 dst.y += tileOffset[reflect][t][1];
761 if (reflect)
762 dst.y += TILE_H_REFLECT_OFFSET;
763 if (cliplift==-1 || reflect)
765 // dst.w=src.w; dst.h=src.h;
766 // SDL_FillRect(screen, &dst, rand());
767 SDL_BlitSurface(reflect ? tileGraphicsR : tileGraphics, &src, screen, &dst);
769 else
771 src.h -= cliplift;
772 if (src.h > TILE_W1)
774 src.h -= TILE_W1/2;
775 SDL_BlitSurface(tileGraphics, &src, screen, &dst);
776 src.y += src.h;
777 dst.y += src.h;
778 src.h = TILE_W1/2;
780 if (src.h > 0)
782 src.w -= TILE_W1*2, src.x += TILE_W1;
783 dst.x += TILE_W1;
784 SDL_BlitSurface(tileGraphics, &src, screen, &dst);
788 void RenderGirl(bool reflect, int r, int frame, int x, int y, int h)
790 int sx = r * 64;
791 int sy = frame * 80*2;
792 if (reflect)
793 y += TILE_H_REFLECT_OFFSET+20+h, sy += 80;
794 else
795 y -= h;
796 SDL_Rect src = {sx, sy, 64, 80};
797 SDL_Rect dst = {x-scrollX-32, y-scrollY-65};
798 SDL_BlitSurface(girlGraphics, &src, screen, &dst);
801 struct ItemRender : public RenderStage
803 int item;
804 Pos p;
805 int water;
807 ItemRender(int i2, int _water, Pos const & _p) : item(i2), p(_p), water(_water)
810 double Translate(double seed, double time)
812 double bob = time*2 + seed*PI2;
813 return sin(bob)*4;
816 void Render(RenderObject* r, double time, bool reflect)
818 if (item==0)
819 return;
821 int y = -5 + (int)Translate(r->seed, r->currentTime + time);
822 if (reflect)
823 y=-y;
824 if (!reflect && !water)
825 RenderTile( false, TILE_SPHERE, p.getScreenX(), p.getScreenY());
826 RenderTile(
827 reflect,
828 item==1 ? TILE_ITEM1 : TILE_ITEM2,
829 p.getScreenX(), p.getScreenY()+y
834 void RenderFade(double time, int dir, int seed)
836 int ys=0;
837 srand(seed);
838 for(int x=rand()%22-11; x<SCREEN_W+22; x+=32, ys ^= 1)
840 for (int y=ys*20; y<SCREEN_H+30; y+=40)
842 double a = (rand()&0xff)*dir;
843 double b = (time * 0x400 + (y - SCREEN_H) * 0x140/SCREEN_H)*dir;
844 if (a >= b)
846 RenderTile(false, TILE_BLACK_TILE, x+scrollX, y+scrollY);
852 struct FadeRender : public RenderStage
854 int seed;
855 int dir;
856 FadeRender(int d=-1) : seed(rand()), dir(d)
858 isFadeRendering = d;
861 void Render(RenderObject* r, double time, bool reflect)
863 if (reflect) return;
864 if (time > 0.5)
866 if (dir==1) dir=0, isFadeRendering=0;
867 return;
869 RenderFade(time, dir, seed);
873 struct ScrollRender : public RenderStage
875 int x,y;
876 bool done;
877 ScrollRender(int a,int b) : x(a), y(b), done(false) {}
879 void Render(RenderObject* r, double time, bool reflect)
881 if (done) return;
882 scrollX = x, scrollY = y;
883 isRenderMap = isMap;
884 done = true;
888 struct LevelSelectRender : public RenderStage
890 Pos p;
891 int item;
892 int adj;
893 #ifdef MAP_EDIT_HACKS
894 int magic;
895 #endif
897 LevelSelectRender(Pos const & _p, int i2, int adj) : p(_p), item(i2), adj(adj)
900 void Render(RenderObject* r, double time, bool reflect)
902 if (item==0)
903 return;
905 #ifndef MAP_LOCKED_VISIBLE
906 if (item==1) return;
907 #endif
909 if (!reflect && adj)
910 for (int i=0; i<MAX_DIR; i++)
911 if (adj & (1 << i))
912 RenderTile( false, TILE_LINK_0+i, p.getScreenX(), p.getScreenY());
914 if (item < 0)
915 return;
917 if (!reflect)
919 RenderTile(
920 reflect,
921 TILE_SPHERE + item-1,
922 p.getScreenX(), p.getScreenY()
925 #ifdef MAP_EDIT_HACKS
926 int x = p.getScreenX()-scrollX, y = p.getScreenY()-scrollY;
927 Print(x+5,y-25,"%d",magic);
928 #endif
933 struct ItemCollectRender : public ItemRender
935 ItemCollectRender(int i2, Pos const & p) : ItemRender(i2, 0, p)
938 void Render(RenderObject* r, double time, bool reflect)
943 int GetLiftHeight(double time, int t)
945 if (t==LIFT_UP)
946 time = LIFT_TIME-time;
947 time = time / LIFT_TIME;
948 if (time > 1)
949 time = 1;
950 if (time < 0)
951 time = 0;
952 time = (3 - 2*time)*time*time;
953 if (t==LIFT_UP)
954 time = (3 - 2*time)*time*time;
955 if (t==LIFT_UP)
956 return (int)((TILE_H_LIFT_UP+4) * time);
957 else
958 return (int)((TILE_H_LIFT_UP-4) * time) + 4;
961 struct TileRender : public RenderStage
963 int special;
964 int t;
965 Pos p;
966 double specialDuration;
968 TileRender(int i, Pos const & _p, int _special=0) : t(i), p(_p), special(_special), specialDuration(LASER_LINE_TIME)
971 void Render(RenderObject* r, double time, bool reflect)
973 if (t==0 && special==0)
974 return;
976 if (special && (t==LIFT_UP || t==LIFT_DOWN) && time<LIFT_TIME)
978 int y = GetLiftHeight(time, t);
979 if (!reflect)
981 RenderTile(reflect, TILE_LIFT_BACK, p.getScreenX(), p.getScreenY());
982 RenderTile(reflect, TILE_LIFT_SHAFT, p.getScreenX(), p.getScreenY()+y, y-8);
983 RenderTile(reflect, TILE_LIFT_FRONT, p.getScreenX(), p.getScreenY());
985 else
987 RenderTile(reflect, TILE_LIFT_SHAFT, p.getScreenX(), p.getScreenY()-y, y);
988 RenderTile(reflect, LIFT_DOWN, p.getScreenX(), p.getScreenY());
991 else if (special && (t==EMPTY || t==TRAP) && !reflect && time < specialDuration)
993 if (t == TRAP)
994 if (time < specialDuration-LASER_FADE_TIME)
995 RenderTile(reflect, TILE_ICE_LASER_REFRACT, p.getScreenX(), p.getScreenY());
996 else
997 RenderTile(reflect, t, p.getScreenX(), p.getScreenY());
998 int base = ((t==EMPTY) ? TILE_LASER_0 : TILE_LASER_REFRACT);
999 if (t==EMPTY && time >= specialDuration-LASER_FADE_TIME)
1000 base = TILE_LASER_FADE_0;
1002 int foo=special;
1003 for(int i=0; foo; foo>>=1, i++)
1004 if (foo & 1)
1005 RenderTile(reflect, base+i, p.getScreenX(), p.getScreenY());
1007 else if (t==FLOATING_BALL)
1009 int y = int(1.8 * sin(r->seed*PI + time*4));
1010 if (special==512)
1012 if (time > 2) return;
1013 if (reflect) return;
1014 srand(int(r->seed * 0xfff));
1015 for (int i=0; i<20 - int(time*10); i++)
1017 int x = int((((rand() & 0xfff) - 0x800) / 10) * time);
1018 int y = int((((rand() & 0xfff) - 0x800) / 10) * time);
1019 RenderTile(true, 19 + ((i+int(time*5))&1)*10, p.getScreenX() + x, p.getScreenY() - 14 + y);
1022 if (time < 0.05)
1023 RenderTile(true, 18, p.getScreenX(), p.getScreenY() - 14);
1025 else if (special)
1026 RenderBoat(reflect, int(special)&255, p.getScreenX(), p.getScreenY(), y);
1027 else
1028 RenderTile(reflect, t, p.getScreenX(), p.getScreenY() + (reflect ? -y : y));
1030 else if (t != EMPTY)
1031 RenderTile(reflect, t, p.getScreenX(), p.getScreenY());
1033 static void RenderBoat(bool reflect, int d, int x, int y, int yo)
1035 if (reflect)
1036 RenderGirl(reflect, d, 0, x, y, -yo);
1037 RenderTile(reflect, FLOATING_BALL, x, y+yo);
1038 if (!reflect)
1040 RenderGirl(reflect, d, 0, x, y, -yo);
1041 RenderTile(true, 17, x, y+yo-TILE_H_REFLECT_OFFSET);
1046 struct TileRotateRender : public TileRender
1048 Dir d;
1049 // int range;
1050 int mode;
1051 TileRotateRender(int i, Pos const & p, Dir _d, int m) : TileRender(i, p), d(_d), mode(m)
1053 void Render(RenderObject* r, double time, bool reflect)
1055 if (t==0)
1056 return;
1057 double f = time / ROTATION_TIME;
1059 if (mode & 1) f += 0.5;
1060 if (f<1 && f>0)
1062 if (mode & 2)
1064 else
1065 f = (3-2*f)*f*f;
1068 if (mode & 1) f=1-f; else f=f;
1069 if (f<0) f=0;
1071 if (f >= 1)
1072 TileRender::Render(r, time, reflect);
1073 else
1075 Pos dd = (Pos(0,0)+d);
1076 int x = p.getScreenX() + int(dd.getScreenX()*(f));
1077 int y = p.getScreenY() + int(dd.getScreenY()*(f));
1079 if (mode & 2)
1080 RenderBoat(reflect, (mode&1) ? (d+MAX_DIR/2)%MAX_DIR : d, x, y, 2);
1081 else
1082 RenderTile(reflect, t, x, y);
1087 struct LaserRender : public RenderStage
1089 Pos p;
1090 Dir d;
1091 int range;
1093 LaserRender(Pos _p, int dir, int r) : p(_p), d(dir), range(r)
1096 void Render(RenderObject* r, double time)
1101 struct ExplosionRender : public RenderStage
1103 Pos p;
1104 int seed;
1105 int power;
1106 int type;
1108 ExplosionRender(Pos _p, int _pow=0, int t=0) : p(_p), power(_pow), type(t)
1110 seed = rand();
1113 virtual int GetDepth(double time)
1115 return p.x + p.y*2;
1118 void Render(RenderObject* r, double time, bool reflect)
1120 if (type==1 && time > 2.5)
1121 type = -1, new WinLoseScreen(false);
1123 // if (reflect) return;
1124 if (time > 3) return;
1125 srand(seed);
1126 int q = 50 - int(time * 35);
1127 if (power) q*=2;
1128 if (type) q = 50;
1129 for (int i=0; i<q; i++)
1131 int x = p.getScreenX();
1132 int y = p.getScreenY() + (rand() & 31)-16;
1133 int xs = ((rand() & 63) - 32);
1134 int ys = (-10 - (rand() & 127)) * (1+power);
1135 if (type) ys*=2, xs/=2;
1136 x += int(xs * (1+time*(2+power)));
1137 int yo = int(time*time*128 + ys*time);
1138 //if (yo > 0) yo=-yo;//continue;
1139 if (type)
1142 if (yo > 0)
1144 if (!reflect && ys<-60)
1146 const double T = 0.06;
1147 double ct = -ys / 128.0;
1148 if (time < ct+T*4)
1150 x = p.getScreenX() + int(xs * (1+ct*(2+power)));
1151 RenderTile(
1152 reflect,
1153 time > ct+3*T ? TILE_SPLASH_3 : time > ct+2*T ? TILE_SPLASH_2 : time > ct+T ? TILE_SPLASH_1 : TILE_WATER_PARTICLE+1,
1154 x, y);
1158 else
1159 RenderTile(
1160 reflect,
1161 time - i*0.003 < 0.2 ? TILE_WATER_PARTICLE+1 : TILE_WATER_PARTICLE,
1162 x, y+(reflect?-1:1)*yo);
1164 else
1166 if (yo > 0)
1168 else
1169 RenderTile(
1170 reflect,
1171 i<q-20 || time<0.3 ? TILE_LASER_HEAD : i<q-10 || time<0.6 ? TILE_FIRE_PARTICLE_1 : TILE_FIRE_PARTICLE_2,
1172 x, y+(reflect?-1:1)*yo);
1177 struct DisintegrateRender : public RenderStage
1179 Pos p;
1180 int seed;
1181 int height;
1182 int type;
1184 DisintegrateRender(Pos _p, int _pow=0, int _t=0) : p(_p), height(_pow), type(_t)
1186 seed = rand();
1189 void Render(RenderObject* r, double time, bool reflect)
1191 if (type)
1192 RenderTile(reflect, height ? COLLAPSE_DOOR : COLLAPSABLE, p.getScreenX(), p.getScreenY());
1194 if (time > 50.0/70.0) return;
1195 if (reflect) return;
1196 srand(seed);
1197 int q = 50 - int(time * 70);
1198 if (height) q*=2;
1199 for (int i=0; i<q; i++)
1201 int x = (rand() % (TILE_W3-8))-TILE_W3/2+4;
1202 int y = (rand() % (TILE_H2-8))-TILE_H1+4;
1203 if (x<-TILE_WL/2 && ABS(y)<-TILE_WL/2-x) continue;
1204 if (x>TILE_WL/2 && ABS(y)>x-TILE_WL/2) continue;
1205 int yo=0;
1206 if (height) yo -= rand() % TILE_HUP;
1207 x += p.getScreenX();
1208 y += p.getScreenY() + 4;
1209 int xs = 0;//((rand() & 63) - 32);
1210 int ys = (- (rand() & 31));
1211 x += int(xs * (1+time*(2)));
1212 if (type) yo = -yo;
1213 yo += int(time*time*128 + ys*time);
1214 if (type) yo = -yo*2;
1215 //if (yo > 0) yo=-yo;//continue;
1216 int t = type ? TILE_BLUE_FRAGMENT : TILE_GREEN_FRAGMENT;
1217 if (i>q-20) t++;
1218 if (i>q-10) t++;
1219 if (yo > 5) yo = 5;
1220 RenderTile(false, t, x, y+(reflect?-yo:yo));
1224 struct BuildRender : public RenderStage
1226 Pos p;
1227 Dir dir;
1228 int reverse;
1229 int height;
1230 int type;
1232 BuildRender(Pos _p, Dir _d, int _h, int _r=0, int _type=0) : p(_p), dir(_d), height(_h), reverse(_r), type(_type)
1236 void Render(RenderObject* r, double time, bool reflect)
1238 if (time >= BUILD_TIME)
1239 RenderTile(reflect, height ^ reverse ? (type ? COLLAPSE_DOOR2 : COLLAPSE_DOOR) : (type ? COLLAPSABLE2 : COLLAPSABLE), p.getScreenX(), p.getScreenY());
1240 else
1242 if (height)
1243 RenderTile(reflect, type ? COLLAPSABLE2 : COLLAPSABLE, p.getScreenX(), p.getScreenY());
1245 double dist = time * 2 / BUILD_TIME;
1246 if (dir>-1)
1248 Pos from = p + ((dir+MAX_DIR/2)%MAX_DIR);
1249 if (dist <= 1)
1250 //if (dist > 1)
1252 double offset = (dist*0.7) + 0.3;
1253 int x = from.getScreenX() + int((p.getScreenX()-from.getScreenX()) * offset);
1254 int y = from.getScreenY() + int((p.getScreenY()-from.getScreenY()) * offset - dist*(1-dist)*(TILE_HUP*4));
1255 RenderTile(reflect, TILE_GREEN_FRAGMENT, x, y);
1257 dist -= 1;
1259 else
1261 if (reverse) dist = 1-dist;
1263 if (dist > 0 && !height)
1265 if (!reflect)
1266 for (int i=0; i<=int(dist*15); i++)
1268 int x = p.getScreenX(), y = p.getScreenY();
1269 double d = (i + fmod(dist*15, 1))/10.0;
1270 int x1 = int(sin(d*5+time)*MIN(d,1)*TILE_W2/2);
1271 int y1 = int(cos(d*5+time)*MIN(d,1)*TILE_H1*0.7);
1272 RenderTile(reflect, TILE_GREEN_FRAGMENT, x+x1, y+y1+4);
1273 RenderTile(reflect, TILE_GREEN_FRAGMENT, x-x1, y-y1+4);
1276 if (dist > 0 && height)
1278 int yo = int((1-dist)*(TILE_HUP*1.3));
1279 if (yo > TILE_HUP*1.1)
1280 RenderTile(reflect, TILE_WHITE_TILE, p.getScreenX(), p.getScreenY());
1281 else if (!reflect)
1283 RenderTile(reflect, type ? COLLAPSABLE2 : COLLAPSABLE, p.getScreenX(), p.getScreenY());
1284 RenderTile(reflect, type ? COLLAPSE_DOOR2 : COLLAPSE_DOOR, p.getScreenX(), p.getScreenY()+(reflect ? -yo : yo), yo+6);
1285 RenderTile(reflect, type ? TILE_BLUE_FRONT : TILE_GREEN_FRONT, p.getScreenX(), p.getScreenY());
1287 else
1289 if (yo < TILE_HUP/2)
1291 RenderTile(reflect, type ? COLLAPSE_DOOR2 : COLLAPSE_DOOR, p.getScreenX(), p.getScreenY()+(reflect ? -yo : yo), yo);
1294 RenderTile(reflect, type ? COLLAPSABLE2 : COLLAPSABLE, p.getScreenX(), p.getScreenY());
1302 struct PlayerRender : public RenderStage
1304 Pos p;
1305 Pos target;
1306 int p_h, target_h;
1307 int r;
1308 int type;
1309 double speed;
1310 bool dead;
1312 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)
1314 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)
1316 int dist = MAX(ABS(p.x-target.x), ABS(p.y-target.y));
1317 if (dist > 1)
1318 speed *= 1.5;
1319 if(dist==0)
1320 speed = 0;
1323 virtual int GetDepth(double time)
1325 double f = speed ? time / speed : 1;
1326 if (f>1) f=1;
1327 if (f==1) dead = this->dead;
1329 double x = p.x + (target.x - p.x) * f;
1330 double y = p.y + (target.y - p.y) * f;
1332 if (f==1 || f>0.5 && p_h>target_h)
1333 return target.x+target.y*2;
1334 return MAX(target.x+target.y*2 , p.x+p.y*2);
1337 void Render(RenderObject* ro, double time, bool reflect)
1339 bool dead = false;
1340 double f = speed ? time / speed : 1;
1341 if (f>1) f=1;
1342 if (f==1) dead = this->dead;
1344 int x = p.getScreenX();
1345 int y = p.getScreenY();
1346 int x2 = target.getScreenX();
1347 int y2 = target.getScreenY();
1348 int h = 0;
1349 int shadow_h = (int)((p_h+(target_h-p_h)*f)*TILE_HUP2);
1351 if (x==x2 && y==y2 && p_h!=target_h)
1353 h = TILE_H_LIFT_UP - GetLiftHeight(time, p_h ? LIFT_DOWN : LIFT_UP);
1355 else
1358 int dist = MAX(ABS(p.x-target.x), ABS(p.y-target.y));
1359 int arc = dist*dist;
1360 int h1 = p_h * TILE_HUP2;;
1361 int h2 = target_h * TILE_HUP2;
1362 if (dist==2 && h1!=0)
1364 arc += h2 ? 1 : 3;
1365 h1 = 0;
1366 shadow_h = f>=0.7 ? int(shadow_h*(f-0.7)/0.3) : 0;
1368 if (dist==0)
1369 arc = speed > JUMP_TIME ? 7 : 2;
1371 h = (int)(h1+(h2-h1)*f);
1372 // if (x==x2 && y==y2)
1373 // ;
1374 // else
1376 //h += int(TILE_H_LIFT_UP/3 * (1-f));
1377 h += (int)(f*(1-f)*TILE_HUP2*arc);
1380 if (type==2)
1381 h=0;
1384 if (!dead)
1386 int frame = 0;
1387 if (type==2 && f<1)
1389 //frame = ((int)(f*4) % 4);
1390 //if (frame==2) frame=0; else if (frame==3) frame=2;
1391 frame = 0;
1393 else if (f==1 || x==x2 && y==y2) // stationary
1394 frame = 0;
1395 else if (f > 0.7)
1396 frame = 0;
1397 else
1399 frame = type ? 2 : 1;
1400 if (f<0.1 || f>0.6)
1401 frame += 2;
1404 if (!reflect)
1405 RenderTile( false, TILE_SPHERE,
1406 (int)(x+(x2-x)*f),
1407 (int)(y+(y2-y)*f) - shadow_h
1410 RenderGirl(
1411 reflect,
1412 r, frame,
1413 (int)(x+(x2-x)*f),
1414 (int)(y+(y2-y)*f),
1419 /* RenderTile(
1420 dead ? TILE_SPHERE_OPEN : TILE_SPHERE_DONE,
1421 (int)(x+(x2-x)*f),
1422 (int)(y+(y2-y)*f),
1423 true
1424 );*/
1429 struct HexPuzzle : public State
1431 struct Undo
1433 #define MAX_TILECHANGE 64 // TODO: don't have a magic upper limit
1434 struct TileChange
1436 Pos p;
1437 Tile t;
1438 int item;
1440 TileChange()
1442 TileChange(Pos _p, Tile _t, int _i) : p(_p), t(_t), item(_i)
1444 void Restore(HexPuzzle* w)
1446 w->SetTile(p,t,false,false);
1447 w->SetItem(p,item,false,false);
1451 TileChange t[MAX_TILECHANGE];
1452 Pos playerPos;
1453 Dir playerMovement;
1454 int numT;
1455 int numItems[2];
1456 int score;
1457 double time;
1458 double endTime;
1460 void Add(TileChange const & tc)
1462 for (int i=0; i<numT; i++)
1463 if (t[i].p==tc.p)
1464 return;
1465 if (numT>=MAX_TILECHANGE)
1466 FATAL("numT>=MAX_TILECHANGE");
1467 else
1468 t[numT++] = tc;
1470 void New(Dir pmove, Pos & pp, int* items, double t, int sc)
1472 numItems[0] = items[0];
1473 numItems[1] = items[1];
1474 playerPos = pp;
1475 playerMovement = pmove;
1476 score = sc;
1477 time = t;
1478 numT = 0;
1480 void Restore(HexPuzzle* w)
1482 for (int i=numT-1; i>=0; i--)
1483 t[i].Restore(w);
1484 w->dead = false;
1485 w->win = false;
1486 w->player = playerPos;
1487 w->player_items[0] = numItems[0];
1488 w->player_items[1] = numItems[1];
1489 w->player_score = score;
1491 //w->renderer.player.Add(new PlayerRender(playerPos, w->GetHeight(playerPos), false), w->time);
1495 #define MAP_SIZE 30
1496 char* special[MAP_SIZE][MAP_SIZE];
1497 Tile map[MAP_SIZE][MAP_SIZE];
1498 int map_item[MAP_SIZE][MAP_SIZE];
1499 int tileCount[NumTileTypes];
1500 int levelPar, levelDiff;
1501 int turboAnim;
1502 Pos player;
1503 int player_items[2];
1504 int player_score;
1505 int numComplete, numLevels, numMastered, numLevelsFound;
1506 bool dead;
1507 bool win;
1508 int winFinal;
1510 SaveState progress;
1512 WorldRenderer renderer;
1513 double time;
1514 double undoTime;
1516 #define MAX_UNDO 6
1517 Undo undo[MAX_UNDO];
1518 int numUndo;
1519 LevelInfo* currentLevelInfo;
1521 char currentFile[1000];
1523 ~HexPuzzle()
1525 FreeGraphics();
1528 LevelInfo* GetLevelInfo(const char* f)
1530 if (strstr(f, "Levels\\") == f)
1531 f += 7;
1532 if (currentLevelInfo!=0 && strcmp(currentLevelInfo->file, f)==0)
1533 return currentLevelInfo;
1535 if (f[0]=='_')
1537 int t = atoi(f+1);
1538 if (t <= numComplete)
1539 return 0;
1541 static char tmp1[100];
1542 static LevelInfo tmp = {0, "", tmp1};
1543 sprintf(tmp1, "Complete %d more %s to unlock!", t-numComplete, t-numComplete==1?"level":"levels");
1544 return &tmp;
1547 for (int i=0; i<sizeof(levelNames)/sizeof(levelNames[0]); i++)
1548 if (strcmp(f, levelNames[i].file)==0)
1549 return &levelNames[i];
1550 static LevelInfo tmp = {0, "", "<<NO NAME>>"};
1551 return &tmp;
1554 #ifdef MAP_EDIT_HACKS
1555 int GetAutoTile(const char * level, bool tiletype)
1557 FILE* f = file_open(filename, "rb");
1558 int tile = EMPTY;
1559 int version;
1561 if (f && fscanf(f, "%d", &version)==1 && (version==3 || version==4))
1563 if (strstr(level,"mk"))
1564 level+=0;
1566 fgetc(f); // Remove '\n' character
1568 int par, diff;
1569 unsigned char bounds[4];
1570 Pos playerStart;
1571 fread(&par, sizeof(par), 1, f);
1572 if (version >= 4)
1573 fread(&diff, sizeof(diff), 1, f);
1574 fread(bounds, sizeof(bounds), 1, f);
1575 fread(&playerStart, sizeof(player), 1, f);
1577 int highval=0;
1579 for (int i=bounds[0]; i<=bounds[1]; i++)
1580 for (int j=bounds[2]; j<=bounds[3]; j++)
1582 unsigned char comp = map[i][j] | (map_item[i][j]<<5);
1583 fread(&comp, sizeof(comp), 1, f);
1584 int t = comp & 0x1f;
1585 int item = (comp >> 5) & 3;
1586 for (int i=highval+1; i<sizeof(value_order)/sizeof(value_order[0]); i++)
1587 if (t!=0 && t==value_order[i]
1588 || item!=0 && item==(value_order[i]>>8))
1589 highval = i;
1592 if (tiletype)
1594 tile = value_order[highval];
1595 if (tile==0x100) tile = COLLAPSABLE3;
1596 if (tile==0x200) tile = SWITCH;
1597 if (tile==LIFT_UP) tile = LIFT_DOWN;
1599 else
1601 if (value_order[highval] == LIFT_UP)
1602 tile = highval-1;
1603 else
1604 tile = highval;
1607 else
1609 level+=0;
1611 if (f)
1612 fclose(f);
1613 return tile;
1615 #endif
1617 void InitSpecials()
1619 numComplete = numLevels = numMastered = numLevelsFound = 0;
1620 for (int i=0; i<MAP_SIZE; i++)
1621 for (int j=0; j<MAP_SIZE; j++)
1622 ActivateSpecial(Pos(i,j), 0);
1623 for (int i=0; i<MAP_SIZE; i++)
1624 for (int j=0; j<MAP_SIZE; j++)
1625 ActivateSpecial(Pos(i,j), 2);
1626 numComplete = numLevels = numMastered = numLevelsFound = 0;
1627 for (int i=0; i<MAP_SIZE; i++)
1628 for (int j=0; j<MAP_SIZE; j++)
1629 ActivateSpecial(Pos(i,j), 0);
1632 void DoHints()
1634 #ifndef EDIT
1635 if (strcmp(mapname, currentFile)==0)
1637 // for (int i=0; i<32; i++)
1638 // HintMessage::FlagTile(i);
1639 if (numComplete >= UNLOCK_SCORING && !progress.general.scoringOn)
1641 HintMessage::FlagTile(26);
1642 progress.general.scoringOn = 1;
1643 InitSpecials(); // Re-initialise with gold ones available
1645 HintMessage::FlagTile(25);
1647 else
1649 for (int i=0; i<MAP_SIZE; i++)
1650 for (int j=0; j<MAP_SIZE; j++)
1652 int t = GetTile(Pos(i,j));
1653 int item = GetItem(Pos(i,j));
1654 if (t)
1655 HintMessage::FlagTile(t);
1656 if (item)
1657 HintMessage::FlagTile(item+20);
1659 HintMessage::FlagTile(EMPTY);
1661 #endif
1662 hintsDone = true;
1664 void ResetLevel()
1666 hintsDone = false;
1668 UpdateCursor(Pos(-1,-1));
1670 isMap = false;
1672 player_score = 0;
1674 numUndo = 0;
1675 undoTime = -1;
1677 dead = false;
1678 win = false;
1679 winFinal = false;
1680 player_items[0] = player_items[1] = 0;
1681 // time = 0;
1682 if (strlen(currentSlot) == 0)
1684 new TitleMenu();
1685 new Fader(1, -3);
1687 else
1689 if (!isFadeRendering && time!=0)
1691 renderer().Add(new FadeRender(-1), time);
1692 time += 0.5;
1696 // Reset renderer
1697 renderer.Reset(time);
1698 renderer.Wipe();
1700 for (int t=0; t<NumTileTypes; t++)
1701 tileCount[t] = 0;
1703 for (int i=0; i<MAP_SIZE; i++)
1704 for (int j=0; j<MAP_SIZE; j++)
1706 Pos p(i,j);
1707 int item = GetItem(p);
1708 //if (item)
1709 renderer(p,true).Add(new ItemRender(item, GetTile(p)==EMPTY, p), time);
1712 InitSpecials();
1714 for (int i=0; i<MAP_SIZE; i++)
1715 for (int j=0; j<MAP_SIZE; j++)
1717 Pos p(i,j);
1718 int t = GetTile(p);
1719 tileCount[t]++;
1721 if (isMap)
1722 t = EMPTY;
1724 //if (t)
1725 renderer(p).Add(new TileRender(t, p), time);
1728 if (!isMap)
1729 renderer.player.Add(new PlayerRender(player, GetHeight(player), dead), time);
1730 else
1731 renderer.player.Add(new PlayerRender(Pos(-100,-100), 0, true), time);
1734 int bounds[4] = {player.getScreenX(),player.getScreenX(),player.getScreenY(),player.getScreenY()};
1735 for (int i=0; i<MAP_SIZE; i++)
1736 for (int j=0; j<MAP_SIZE; j++)
1738 Pos p(i,j);
1739 if (map[i][j] !=0 || map_item[i][j]!=0)
1741 int x1 = p.getScreenX();
1742 int y1 = p.getScreenY();
1743 int x2 = x1 + TILE_W3;
1744 int y2 = y1 + TILE_H2;
1745 y1 -= TILE_H2; // Make sure objects/player will be properly visible
1747 if (x1<bounds[0]) bounds[0] = x1;
1748 if (x2>bounds[1]) bounds[1] = x2;
1749 if (y1<bounds[2]) bounds[2] = y1;
1750 if (y2>bounds[3]) bounds[3] = y2;
1754 int sx, sy;
1755 if (isMap)
1757 sx = bounds[0] - int(TILE_W2*6.35);
1758 sy = (bounds[3] + bounds[2] - SCREEN_H) / 2 - TILE_H2/2;
1760 else
1762 sx = (bounds[1] + bounds[0] - SCREEN_W) / 2 - TILE_W3/2;
1763 sy = (bounds[3] + bounds[2] - SCREEN_H) / 2 - TILE_H2/2;
1765 if (isMap)
1767 initScrollX = sx;
1768 initScrollY = sy;
1769 if (mapScrollX==0)
1770 mapScrollX = sx;
1771 else
1772 sx = mapScrollX;
1775 // time = 1; // Guarantee we can't try and do things at time=0
1777 renderer().Add(new ScrollRender(sx, sy), time);
1778 renderer().Add(new FadeRender(1), time);
1779 if (time != 0)
1780 time -= 0.5;
1783 char* ReadAll(FILE* f)
1785 int size;
1786 fseek(f, 0, SEEK_END);
1787 size = ftell(f);
1788 fseek(f, 0, SEEK_SET);
1789 char* c = loadPtr = new char [size];
1790 endLoad = loadPtr + size;
1791 fread(c, 1, size, f);
1792 return c;
1795 static char *loadPtr, *endLoad;
1796 static unsigned int fread_replace(void* d, unsigned int size, unsigned int num, FILE*)
1798 unsigned int remain = (endLoad - loadPtr) / size;
1799 if (remain < num) num = remain;
1800 memcpy(d, loadPtr, size*num);
1801 loadPtr += size*num;
1802 return num;
1805 int GetPar(const char * level, bool getdiff=false)
1807 if (strcmp(level, currentFile)==0)
1808 return getdiff ? levelDiff : levelPar;
1810 #ifdef USE_LEVEL_PACKFILE
1811 PackFile1::Entry* e = levelFiles.Find(level);
1812 if (!e) return 999;
1813 loadPtr = (char*)e->Data();
1814 endLoad = loadPtr + e->DataLen();
1815 FILE* f = 0;
1816 #else
1817 loadPtr = 0;
1818 FILE* f = file_open(level, "rb");
1819 #endif
1821 typedef unsigned int _fn(void*, unsigned int, unsigned int, FILE*);
1822 _fn * fn = (loadPtr ? (_fn*)fread_replace : (_fn*)fread);
1824 int par = 99999, diff = 0;
1825 int version;
1827 if (!f && !loadPtr)
1828 return getdiff ? diff : par;
1830 fn(&version, 2, 1, f); // skip to relevant point
1832 if (fn(&par, sizeof(par), 1, f) != 1)
1833 par = 99999;
1834 if (fn(&diff, sizeof(diff), 1, f) != 1 || diff<0 || diff>10)
1835 diff = 0;
1837 #ifdef USE_LEVEL_PACKFILE
1838 loadPtr = endLoad = 0;
1839 #else
1840 if (f)
1841 fclose(f);
1842 #endif
1844 return getdiff ? diff : par;
1847 bool LoadSave(const char * filename, bool save)
1849 if (!filename)
1850 return false;
1852 if (!save)
1854 showScoring = false;
1855 LevelSave* l = progress.GetLevel(filename, true);
1856 if (progress.general.scoringOn && l && l->Completed() )
1857 showScoring = true;
1860 #ifdef USE_LEVEL_PACKFILE
1861 if (!save)
1863 PackFile1::Entry* e = levelFiles.Find(filename);
1864 if (!e) return false;
1866 strcpy(currentFile, filename);
1867 currentLevelInfo = GetLevelInfo(currentFile);
1869 loadPtr = (char*)e->Data();
1870 endLoad = loadPtr + e->DataLen();
1871 _LoadSave(NULL, save);
1872 loadPtr = endLoad = 0;
1874 return true;
1876 #else
1877 loadPtr = 0;
1878 FILE* f = file_open(filename, save ? "wb" : "rb");
1879 if (f)
1881 strcpy(currentFile, filename);
1882 if (!save)
1883 currentLevelInfo = GetLevelInfo(currentFile);
1885 if (!save)
1887 char* data = ReadAll(f);
1888 _LoadSave(f, save);
1889 delete [] data;
1890 loadPtr = endLoad = 0;
1892 else
1894 _LoadSave(f, save);
1896 fclose(f);
1898 return true;
1900 #endif
1902 return false;
1905 void _LoadSave(FILE* f, bool save)
1907 typedef unsigned int _fn(void*, unsigned int, unsigned int, FILE*);
1908 _fn * fn = save ? (_fn*)fwrite : (loadPtr ? (_fn*)fread_replace : (_fn*)fread);
1910 #define VERSION 4
1911 int version = VERSION;
1912 if (save)
1913 fprintf(f, "%d\n", version);
1914 else
1916 char c;
1917 if (fn(&c, 1, 1, f) != 1)
1918 return;
1919 version = c-'0';
1921 // Remove '\n' character
1922 fn(&c, 1, 1, f);
1925 if (!save)
1927 for (int i=0; i<MAP_SIZE; i++)
1928 for (int j=0; j<MAP_SIZE; j++)
1930 delete [] special[i][j];
1931 special[i][j] = 0;
1935 if (version==1)
1937 for (int i=0; i<MAP_SIZE; i++)
1938 for (int j=0; j<MAP_SIZE; j++)
1939 fn(&map[i][j], sizeof(map[i][j]), 1, f);
1941 fn(&player, sizeof(player), 1, f);
1943 if (fn(map_item, sizeof(map_item), 1, f) == 0)
1944 memset(map_item, 0, sizeof(map_item));
1946 else if (version>=2 && version<=4)
1948 unsigned char bounds[4];
1949 if (save)
1951 bounds[0]=bounds[1]=player.x;
1952 bounds[2]=bounds[3]=player.y;
1953 for (int i=0; i<MAP_SIZE; i++)
1954 for (int j=0; j<MAP_SIZE; j++)
1955 if (map[i][j] !=0 || map_item[i][j]!=0 || special[i][j]!=0)
1957 if (i<bounds[0]) bounds[0] = i;
1958 if (i>bounds[1]) bounds[1] = i;
1959 if (j<bounds[2]) bounds[2] = j;
1960 if (j>bounds[3]) bounds[3] = j;
1963 else
1965 memset(map, 0, sizeof(map));
1966 memset(map_item, 0, sizeof(map_item));
1969 if (version>=3)
1970 fn(&levelPar, 1, sizeof(levelPar), f);
1971 else if (!save)
1972 levelPar = 0;
1974 if (version>=4)
1975 fn(&levelDiff, 1, sizeof(levelDiff), f);
1976 else if (!save)
1977 levelDiff = 0;
1979 fn(bounds, sizeof(bounds), 1, f);
1980 fn(&player, sizeof(player), 1, f);
1982 int offsetx=0, offsety=0;
1984 if (!save && bounds[1]-bounds[0]<15) // Hacky - don't recenter map...
1986 // Re-position map to top left (but leave a bit of space)
1987 // (This ensures the laser/boat effects don't clip prematurely against the edges of the screen)
1988 offsetx = SCREEN_W/2/TILE_W2 + 1 - (bounds[0]+bounds[1]/2);
1989 offsety = SCREEN_H/2/TILE_H2 + SCREEN_W/2/TILE_W2 - (bounds[2]+bounds[3]/2);
1990 offsetx = MAX(0, offsetx);
1991 offsety = MAX(0, offsety);
1992 // if (bounds[0] > 2)
1993 // offsetx = 2 - bounds[0];
1994 // if (bounds[2] > 2)
1995 // offsety = 2 - bounds[2];
1997 bounds[0] += offsetx;
1998 bounds[1] += offsetx;
1999 bounds[2] += offsety;
2000 bounds[3] += offsety;
2001 player.x += offsetx;
2002 player.y += offsety;
2004 for (int i=bounds[0]; i<=bounds[1]; i++)
2005 for (int j=bounds[2]; j<=bounds[3]; j++)
2007 unsigned char comp = map[i][j] | (map_item[i][j]<<5);
2008 fn(&comp, sizeof(comp), 1, f);
2009 map[i][j] = comp & 0x1f;
2010 map_item[i][j] = (comp >> 5) & 3;
2013 if (save)
2015 for (int i=bounds[0]; i<=bounds[1]; i++)
2016 for (int j=bounds[2]; j<=bounds[3]; j++)
2017 if (special[i][j])
2019 short len = strlen(special[i][j]);
2020 unsigned char x=i, y=j;
2021 fn(&x, sizeof(x), 1, f);
2022 fn(&y, sizeof(y), 1, f);
2023 fn(&len, sizeof(len), 1, f);
2024 fn(special[i][j], 1, len, f);
2027 else
2029 while(1){
2030 short len;
2031 unsigned char x, y;
2032 if (!fn(&x, sizeof(x), 1, f))
2033 break;
2034 fn(&y, sizeof(y), 1, f);
2035 x += offsetx; y += offsety;
2036 fn(&len, sizeof(len), 1, f);
2037 if (len<0) break;
2038 char* tmp = new char[len+1];
2039 tmp[len] = 0;
2040 fn(tmp, 1, len, f);
2042 SetSpecial(Pos(x,y), tmp, true, false);
2046 else
2047 return; // Unsupported version!
2049 ResetLevel();
2051 // Save when returning to map!
2052 if (isMap)
2054 progress.general.completionPercentage = numComplete*100/numLevels;
2055 progress.general.masteredPercentage = numMastered*100/numLevels;
2056 LoadSaveProgress(true);
2060 void SetTile(Pos const & p, Tile t, bool updateRenderer=true, bool undoBuffer=true)
2062 if (p.x<0 || p.x>MAP_SIZE)
2063 return;
2064 if (p.y<0 || p.y>MAP_SIZE)
2065 return;
2066 if (map[p.x][p.y] == t)
2067 return;
2068 if (map[p.x][p.y] == t)
2069 return;
2071 tileCount[map[p.x][p.y]]--;
2072 tileCount[t]++;
2074 if (undoBuffer)
2075 undo[numUndo].Add(Undo::TileChange(p,GetTile(p),GetItem(p)));
2077 map[p.x][p.y] = t;
2079 if (updateRenderer)
2080 renderer(p).Add(new TileRender(t, p), time);
2083 Tile GetTile(Pos const & p)
2085 if (p.x<0 || p.x>=MAP_SIZE)
2086 return EMPTY;
2087 if (p.y<0 || p.y>=MAP_SIZE)
2088 return EMPTY;
2089 return map[p.x][p.y];
2092 int GetHeight(Pos const & p)
2094 return tileSolid[GetTile(p)]==1;
2097 char* GetSpecial(Pos const & p)
2099 if (p.x<0 || p.x>=MAP_SIZE)
2100 return NULL;
2101 if (p.y<0 || p.y>=MAP_SIZE)
2102 return NULL;
2103 return special[p.x][p.y];
2106 void SetSpecial(Pos const & p, char * d, bool use_pointer=false, bool auto_activate=true)
2108 if (p.x<0 || p.x>=MAP_SIZE || p.y<0 || p.y>=MAP_SIZE)
2110 if (use_pointer)
2111 delete [] d;
2112 return;
2115 delete [] special[p.x][p.y];
2116 if (!use_pointer && d)
2119 special[p.x][p.y] = new char [strlen(d) + 1];
2120 strcpy(special[p.x][p.y], d);
2122 else
2123 special[p.x][p.y] = d;
2125 if (special[p.x][p.y]==0)
2126 renderer(p,true).Add(new ItemRender(GetItem(p), GetTile(p)==EMPTY, p), time);
2127 else if (auto_activate)
2128 ActivateSpecial(p, 0);
2131 int GetLevelState(Pos const & p, int recurse=0)
2133 char* x = GetSpecial(p);
2134 if (!x) return 0;
2136 LevelSave* l = progress.GetLevel(x, false);
2138 int t = 1;
2140 if (strcmp(x, STARTING_LEVEL)==0)
2141 t = 2;
2142 if (x[0]=='_' && l && l->unlocked)
2143 t=3;
2145 if (l && l->Completed())
2147 t = 3;
2149 if (recurse)
2150 return t;
2152 int par = GetPar(x);
2153 if (progress.general.scoringOn && l->PassesPar( par ))
2154 t = 4;
2156 if (recurse)
2157 return t;
2159 int adj=0;
2160 for (Dir d=0; d<MAX_DIR; d++)
2162 int i = GetLevelState(p+d, 1);
2163 // if (i>1 || i==1 && t>1)
2164 if (i>=1 && t>2 || t>=1 && i>2)
2166 adj |= 1<<d;
2167 if (t==1)
2168 t = 2;
2172 return t | adj<<8;
2175 void ActivateSpecial(Pos const & p, int type)
2177 if (p.x<0 || p.x>=MAP_SIZE || p.y<0 || p.y>=MAP_SIZE)
2178 return;
2180 char * x = special[p.x][p.y];
2182 if (x==0 || x[0]==0)
2183 return;
2185 if (type==2 && x[0]=='_') // Phase2 init - unlock
2187 int t = GetLevelState(p);
2188 int target = atoi(x+1), targetM = 0;
2189 if (target>1000) targetM=target=target-100;
2190 if (t > 1 && numComplete >= target && numMastered >= targetM)
2192 LevelSave* l = progress.GetLevel(x, true);
2193 if (!l->unlocked)
2195 l->unlocked = true;
2197 renderer(p, true).Add(new LevelSelectRender(p, 5, GetLevelState(p)>>8), time+0.01);
2198 renderer().Add(new ExplosionRender(p, 0), time + 0.6);
2199 renderer().Add(new ExplosionRender(p, 1), time + 1.1);
2200 renderer(p, true).Add(new LevelSelectRender(p, -1, GetLevelState(p)>>8), time + 1.1);
2205 if (type==0) // Init & count levels
2207 if (x[0]=='_')
2209 int t = GetLevelState(p);
2210 int unlock = progress.GetLevel(x, true)->unlocked;
2211 LevelSelectRender* lsr = new LevelSelectRender( p, unlock ? -1 : (t>>8) ? 5 : 1, t>>8 );
2212 if ((t>>8) && p.x > mapRightBound) mapRightBound = p.x;
2213 #ifdef MAP_EDIT_HACKS
2214 lsr->magic = -atoi(x+1);
2215 SetTile(p, LIFT_DOWN, true, false);
2216 #else
2217 SetTile(p, EMPTY, true, false);
2218 #endif
2219 renderer(p,true).Add(lsr, time);
2221 else
2223 //printf("Level: %s\n", x);
2225 int t = GetLevelState(p);
2226 numLevels++;
2227 if (t && !GetItem(p))
2229 if (!isMap)
2231 isMap = true;
2232 mapRightBound = 0;
2234 currentLevelInfo = 0;
2236 if ((t&0xff)>=2)
2238 LevelSave* l = progress.GetLevel(x, true);
2239 if (!l->unlocked)
2241 l->unlocked = true;
2243 renderer(p, true).Add(new LevelSelectRender(p, -1, 0), time+0.01);
2244 renderer().Add(new ExplosionRender(p, 0), time + 0.6);
2245 renderer(p, true).Add(new LevelSelectRender(p, t & 0xff, t>>8), time + 0.6);
2248 numLevelsFound++;
2249 if (p.x > mapRightBound) mapRightBound = p.x;
2251 if ((t&0xff)>=3)
2252 numComplete++;
2253 if ((t&0xff)>=4)
2254 numMastered++;
2256 LevelSelectRender* lsr = new LevelSelectRender( p, t & 0xff, t>>8 );
2258 #ifdef MAP_EDIT_HACKS
2259 lsr->magic = 0;
2260 int t = GetAutoTile(x, true);
2261 int v = GetAutoTile(x, false);
2262 if (MAP_EDIT_HACKS_DISPLAY_UNLOCK)
2263 lsr->magic = v;
2264 else
2265 lsr->magic = GetPar(x, true);
2266 t = 1;
2267 SetTile(p, t, true, false);
2268 #else
2269 SetTile(p, EMPTY, true, false);
2270 #endif
2272 renderer(p,true).Add(lsr, time);
2277 if (type==1 && x[0]!='_') // Clicked on
2279 int t = GetLevelState(p);
2280 if (t>1)
2282 LoadSave(x, false);
2287 void SetItem(Pos const & p, int t, bool updateRenderer=true, bool undoBuffer=true)
2289 if (p.x<0 || p.x>MAP_SIZE)
2290 return;
2291 if (p.y<0 || p.y>MAP_SIZE)
2292 return;
2293 if (map_item[p.x][p.y] == t)
2294 return;
2296 if (undoBuffer)
2297 undo[numUndo].Add(Undo::TileChange(p,GetTile(p),GetItem(p)));
2299 map_item[p.x][p.y] = t;
2301 if (updateRenderer)
2302 renderer(p,true).Add(new ItemRender(t, GetTile(p)==EMPTY, p), time);
2305 Tile GetItem(Pos const & p)
2307 if (p.x<0 || p.x>=MAP_SIZE)
2308 return EMPTY;
2309 if (p.y<0 || p.y>=MAP_SIZE)
2310 return EMPTY;
2311 return map_item[p.x][p.y];
2314 void LoadSaveProgress(bool save)
2316 FILE* f = file_open(currentSlot, save ? "wb" : "rb");
2317 if (f)
2319 progress.LoadSave(f, save);
2320 fclose(f);
2322 else
2324 if (!save)
2325 progress.Clear();
2328 void LoadProgress()
2330 LoadSaveProgress(false);
2332 void SaveProgress()
2334 LoadSaveProgress(true);
2337 SDL_Surface* Load(const char * bmp, bool colourKey=true)
2339 typedef unsigned int uint32;
2340 uint32* tmp = 0;
2342 SDL_Surface * g = 0;
2344 #ifdef EDIT
2345 if (strstr(bmp, ".bmp"))
2347 g = SDL_LoadBMP(bmp);
2349 char out[1024];
2350 strcpy(out, bmp);
2351 strcpy(strstr(out, ".bmp"), ".dat");
2353 // SDL_PixelFormat p;
2354 // p.sf = 1;
2355 // SDL_Surface* tmp = SDL_ConvertSurface(g, &p, SDL_SWSURFACE);
2357 short w=g->w, h=g->h;
2358 char* buf = (char*) g->pixels;
2359 if (colourKey)
2361 while (IsEmpty(g, w-1, 0, 1, h) && w>1)
2362 w--;
2363 while (IsEmpty(g, 0, h-1, w, 1) && h>1)
2364 h--;
2367 FILE* f = file_open(out, "wb");
2368 fwrite(&w, sizeof(w), 1, f);
2369 fwrite(&h, sizeof(h), 1, f);
2371 uint32 mask = IMAGE_DAT_OR_MASK;
2372 for (int i=0; i<(int)w*h; )
2374 uint32 c = (*(uint32*)&buf[(i%w)*3 + (i/w)*g->pitch] | mask);
2375 int i0 = i;
2376 while (i < (int)w*h && c == (*(uint32*)&buf[(i%w)*3 + (i/w)*g->pitch] | mask))
2377 i++;
2378 c &= 0xffffff;
2379 i0 = i-i0-1;
2380 if (i0 < 0xff)
2381 c |= i0 << 24;
2382 else
2383 c |= 0xff000000;
2385 fwrite(&c, sizeof(c), 1, f);
2387 if (i0 >= 0xff)
2388 fwrite(&i0, sizeof(i0), 1, f);
2390 fclose(f);
2392 SDL_FreeSurface(g);
2394 bmp = out;
2396 #endif
2398 FILE* f = file_open(bmp, "rb");
2399 if (!f) FATAL("Unable to open file", bmp);
2401 short w,h;
2402 fread(&w, sizeof(w), 1, f);
2403 fread(&h, sizeof(h), 1, f);
2404 if (w>1500 || h>1500) FATAL("Invalid file", bmp);
2406 tmp = new uint32[(int)w*h];
2408 uint32 c = 0;
2409 uint32 cnt = 0;
2410 for (int p=0; p<(int)w*h; p++)
2412 if (cnt)
2413 cnt -= 0x1;
2414 else
2416 fread(&c, sizeof(c), 1, f);
2417 cnt = c >> 24;
2418 if (cnt==255)
2419 fread(&cnt, sizeof(cnt), 1, f);
2421 tmp[p] = c | 0xff000000;
2424 g = SDL_CreateRGBSurfaceFrom(tmp, w, h, 32, w*4,
2425 0xff0000,
2426 0xff00,
2427 0xff,
2428 0xff000000 );
2430 fclose(f);
2433 if (!g) FATAL("Unable to create SDL surface");
2434 if (colourKey)
2435 SDL_SetColorKey(g, SDL_SRCCOLORKEY, SDL_MapRGB(g->format, WATER_COLOUR));
2436 SDL_Surface * out = SDL_DisplayFormat(g);
2437 SDL_FreeSurface(g);
2438 delete [] tmp;
2439 if (!out) FATAL("Unable to create SDL surface (SDL_DisplayFormat)");
2440 return out;
2443 #ifdef USE_LEVEL_PACKFILE
2444 PackFile1 levelFiles;
2445 #endif
2446 HexPuzzle()
2448 SDL_WM_SetCaption(GAMENAME, 0);
2450 time = 0;
2452 #ifdef USE_LEVEL_PACKFILE
2453 FILE* f = file_open("levels.dat", "rb");
2454 if (!f)
2455 FATAL("Unable to open file", "levels.dat");
2456 levelFiles.Read(f);
2457 fclose(f);
2458 #endif
2460 LoadGraphics();
2462 isMap = false;
2463 editMode = false;
2465 currentLevelInfo = 0;
2467 editTile = 0;
2468 levelPar = 0;
2469 levelDiff = 5;
2470 turboAnim = 0;
2472 memset(map, 0, sizeof(map));
2473 memset(map_item, 0, sizeof(map_item));
2474 memset(special, 0, sizeof(special));
2476 LoadProgress();
2478 // player = Pos(1,11);
2480 // ResetLevel();
2482 LoadMap();
2485 void LoadMap()
2487 #ifndef EDIT
2488 progress.GetLevel(STARTING_LEVEL, true)->unlocked = 1;
2489 if (!progress.GetLevel(STARTING_LEVEL, true)->Completed())
2491 LoadSave(STARTING_LEVEL, false);
2492 return;
2494 #endif
2496 //editMode = false;
2497 LoadSave(mapname, false);
2500 void Render()
2502 if (!activeMenu || activeMenu->renderBG)
2504 SDL_Rect src = {0,0,screen->w,screen->h};
2505 SDL_Rect dst = {0,0,screen->w,screen->h};
2506 if (isRenderMap)
2508 int boundW = mapBG->w;
2509 #ifndef EDIT
2510 boundW = MIN(boundW, (mapRightBound+4) * TILE_W2 - TILE_W1);
2511 #endif
2512 src.x = scrollX - initScrollX;
2513 if (src.x+src.w > boundW)
2515 int diff = src.x+src.w - boundW;
2516 src.x -= diff;
2517 if (isMap)
2518 scrollX -= diff;
2520 if (src.x < 0)
2522 if (isMap)
2523 scrollX -= src.x;
2524 src.x = 0;
2526 //scrollY = initScrollY;
2528 if (isMap)
2529 mapScrollX = scrollX;
2531 SDL_BlitSurface(mapBG, &src, screen, &dst);
2533 else
2534 SDL_BlitSurface(gradient, &src, screen, &dst);
2536 renderer.Render(time, true);
2538 if (!hintsDone && !isFadeRendering)
2540 DoHints();
2543 if (1)
2545 SDL_Rect src = {0,SCREEN_H-1,SCREEN_W,1};
2546 SDL_Rect dst = {0,SCREEN_H-1,SCREEN_W,1};
2547 for (int i=0; i<SCREEN_H; i++)
2549 dst.x = src.x = 0;
2550 dst.y = src.y = SCREEN_H-1-i;
2551 src.w = SCREEN_W;
2552 src.h = 1;
2554 const bool farView = false;
2555 if (isRenderMap)
2557 src.x += (int)( sin(i*0.9 + time*3.7) * sin(i*0.3 + time*0.7)*4 );
2558 src.y += (int)( (sin(i*0.3 - time*2.2) * sin(i*0.48 + time*0.47) - 1) * 1.99 );
2560 else
2562 src.x += (int)( sin(i*0.5 + time*6.2) * sin(i*0.3 + time*1.05) * 5 );
2563 src.y += (int)( (sin(i*0.4 - time*4.3) * sin(i*0.08 + time*1.9) - 1) * 2.5 );
2565 SDL_BlitSurface(screen, &src, screen, &dst);
2569 if(isRenderMap)
2570 SDL_BlitSurface(mapBG2, &src, screen, &dst);
2572 renderer.Render(time, false);
2574 if (!isRenderMap && !isMap && !isFadeRendering)
2576 int v[3] = {player_items[0], player_items[1], player_score};
2577 if (numUndo > 1 && time < undo[numUndo-2].endTime)
2579 int i = numUndo-1;
2580 while (i>1 && time<undo[i-1].time)
2581 i--;
2582 v[0] = undo[i].numItems[0];
2583 v[1] = undo[i].numItems[1];
2584 v[2] = undo[i].score;
2586 if (numUndo>1 && time < undo[0].time)
2587 v[0]=v[1]=v[2]=0;
2588 #ifdef EDIT
2589 Print(0,0,"Anti-Ice: %d", v[0]);
2590 Print(0,FONT_SPACING,"Jumps: %d", v[1]);
2591 Print(0,FONT_SPACING*2,"Score: %d (%d)", v[2], player_score);
2592 Print(0,FONT_SPACING*3,"Par: %d", levelPar);
2593 Print(0,FONT_SPACING*4,"Diff: %d", levelDiff);
2594 #else
2595 if (showScoring)
2596 Print(0, SCREEN_H-FONT_SPACING, " Par: %d Current: %d", levelPar, v[2]);
2598 if (v[0])
2599 Print(0,0," Anti-Ice: %d", v[0]);
2600 else if (v[1])
2601 Print(0,0," Jumps: %d", v[1]);
2602 #endif
2604 if (isRenderMap && isMap && !isFadeRendering)
2606 #if 0//def EDIT
2607 Print(0,0,"Points: %d", numComplete+numMastered);
2608 Print(0,FONT_SPACING,"Discovered: %d%% (%d/%d)", numLevelsFound*100/numLevels, numLevelsFound, numLevels);
2609 Print(0,FONT_SPACING*2,"Complete: %d%% (%d)", numComplete*100/numLevels, numComplete);
2610 Print(0,FONT_SPACING*3,"Mastered: %d%% (%d)", numMastered*100/numLevels, numMastered);
2611 #else
2612 if (numComplete==numLevels && progress.general.endSequence>0)
2613 Print(0, SCREEN_H-FONT_SPACING, " %d%% Mastered", numMastered*100/numLevels);
2614 else
2615 Print(0, SCREEN_H-FONT_SPACING, " %d%% Complete", numComplete*100/numLevels);
2617 if (numMastered >= numLevels && progress.general.endSequence < 2)
2619 progress.general.endSequence = 2;
2620 LoadSaveProgress(true);
2622 new Fader(-1, -7, 0.3);
2624 if (numComplete >= numLevels && progress.general.endSequence < 1)
2626 progress.general.endSequence = 1;
2627 LoadSaveProgress(true);
2629 new Fader(-1, -5, 0.3);
2631 #endif
2633 if ((currentLevelInfo || noMouse) && isMap && isRenderMap && !activeMenu && isFadeRendering<=0)
2635 Pos p;
2636 if (noMouse)
2637 p = keyboardp;
2638 else
2639 p = mousep;
2640 int pad = SCREEN_W/80;
2641 // SDL_Rect src = {0, 0, uiGraphics->w, uiGraphics->h};
2642 SDL_Rect dst = {pad, SCREEN_H-TILE_H2-pad, 0, 0};
2643 // dst.x = p.getScreenX() + TILE_W3/2 - scrollX;
2644 // dst.y = p.getScreenY() - src.h/2 - scrollY;
2645 dst.x = p.getScreenX() - scrollX;
2646 dst.y = p.getScreenY() - scrollY - FONT_SPACING*3 - FONT_SPACING/2;
2647 // if (dst.x > SCREEN_W*2/3) dst.x -= TILE_W3 + src.w;
2648 // if (dst.y+src.h > screen->h-pad) dst.y = screen->h-pad - src.h;
2650 RenderTile(false, 0, p.getScreenX(), p.getScreenY());
2651 // SDL_BlitSurface(uiGraphics, &src, screen, &dst);
2653 // dst.x += src.w/2;
2655 if (currentLevelInfo)
2657 keyboardp = p;
2659 PrintC(true, dst.x, dst.y - FONT_SPACING/4, currentLevelInfo->name);
2661 if (currentLevelInfo->file[0]!=0)
2663 if (player_score > 0)
2665 if (progress.general.scoringOn)
2667 PrintC(false, dst.x, dst.y + FONT_SPACING*4 - FONT_SPACING/4, "Best:% 3d", player_score);
2668 PrintC(false, dst.x, dst.y + FONT_SPACING*5 - FONT_SPACING/4, "Par:% 3d", levelPar);
2670 else
2671 PrintC(false, dst.x, dst.y + FONT_SPACING*4 - FONT_SPACING/4, "Completed", player_score);
2673 else
2674 PrintC(false, dst.x, dst.y + FONT_SPACING*4 - FONT_SPACING/4, "Incomplete", player_score);
2679 // "Win"
2680 if (win && numUndo > 0 && time > undo[numUndo-1].endTime + 2)
2682 if (currentFile[0] && winFinal==0)
2684 LevelSave* l = progress.GetLevel(currentFile, true);
2686 new WinLoseScreen(true, player_score, showScoring ? levelPar : 0, l && showScoring && l->Completed() ? l->GetScore() : 0);
2688 if (l->IsNewCompletionBetter(player_score))
2690 l->SetScore(player_score);
2692 l->SetSolution(numUndo);
2694 for (int i=0; i<numUndo; i++)
2695 l->SetSolutionStep(i, undo[i].playerMovement);
2698 SaveProgress();
2701 winFinal = 1;
2703 else
2704 winFinal = 0;
2706 // Move up "level complete" writing so it doesn't feel like everything's ground to a halt...
2707 if (win && numUndo > 0 && time > undo[numUndo-1].endTime && !winFinal)
2709 double t = (time - undo[numUndo-1].endTime) / 2;
2710 t=1-t;
2711 t*=t*t;
2712 t=1-t;
2713 int y = SCREEN_H/3 - FONT_SPACING + 1;
2714 y = SCREEN_H + int((y-SCREEN_H)*t);
2715 PrintC(true, SCREEN_W/2, y, "Level Complete!");
2719 if (activeMenu)
2720 activeMenu->Render();
2722 if (!noMouse)
2724 // Edit cursor
2725 if (editMode)
2727 RenderTile(false, editTile, mousex+scrollX, mousey+scrollY);
2729 else
2731 Print(mousex, mousey-2, "\x7f");
2736 int Count(Tile t)
2738 return tileCount[t];
2740 int Swap(Tile t, Tile t2)
2742 const int num = Count(t) + Count(t2);
2743 if (t==t2 || num==0)
2744 return Count(t); // Nothing to do...
2746 int count=0;
2747 for (int x=0; x<MAP_SIZE; x++)
2748 for (int y=0; y<MAP_SIZE; y++)
2750 if (GetTile(Pos(x,y))==t)
2752 count++;
2753 SetTile(Pos(x,y), t2);
2755 else if (GetTile(Pos(x,y))==t2)
2757 count++;
2758 SetTile(Pos(x,y), t);
2760 if (count==num)
2761 return count;
2763 return count;
2765 int Replace(Tile t, Tile t2)
2767 const int num = Count(t);
2768 if (t==t2 || num==0)
2769 return num; // Nothing to do...
2771 int count=0;
2772 for (int x=0; x<MAP_SIZE; x++)
2773 for (int y=0; y<MAP_SIZE; y++)
2775 Pos p(x,y);
2776 if (GetTile(p)==t)
2778 count++;
2780 SetTile(p, t2, false);
2782 if (t==COLLAPSE_DOOR && t2==COLLAPSABLE)
2783 renderer(p).Add(new BuildRender(p, -1, 1, 1), time + (rand() & 255)*0.001);
2784 else if (t==COLLAPSE_DOOR2 && t2==COLLAPSABLE2)
2785 renderer(p).Add(new BuildRender(p, -1, 1, 1, 1), time + (rand() & 255)*0.001);
2786 else
2787 SetTile(p, t2);
2789 if (count==num)
2790 return count;
2793 return count;
2796 Tile editTile;
2797 bool editMode;
2798 void ResetUndo()
2800 UndoDone();
2801 undoTime = -1;
2802 numUndo = 0;
2803 win = false;
2806 void UpdateCursor(Pos const & s)
2808 static Pos _s;
2809 if (s.x!=_s.x || s.y!=_s.y)
2811 _s = s;
2813 char* sp = GetSpecial(s);
2814 char tmp[1000];
2815 tmp[0]='\0';
2816 if (sp)
2818 if (isMap)
2820 currentLevelInfo = 0;
2821 levelPar = player_score = -1;
2822 if (GetLevelState(s)>=2)
2824 LevelSave* l = progress.GetLevel(sp, true);
2825 if (l)
2827 currentLevelInfo = GetLevelInfo(sp);
2828 levelPar = GetPar(sp);
2829 player_score = l->GetScore();
2834 #ifdef EDIT
2835 sprintf(tmp, "Special(%d,%d): %s (%d)", s.x, s.y, sp ? sp : "<None>", GetPar(sp));
2836 SDL_WM_SetCaption(tmp, NULL);
2837 #endif
2839 else if (currentFile[0])
2841 #ifdef EDIT
2842 SDL_WM_SetCaption(currentFile, NULL);
2843 #endif
2844 if (isMap)
2845 currentLevelInfo = 0;
2850 virtual void Mouse(int x, int y, int dx, int dy, int button_pressed, int button_released, int button_held)
2852 if (activeMenu)
2854 activeMenu->Mouse(x,y,dx,dy,button_pressed,button_released,button_held);
2855 return;
2858 if (isFadeRendering)
2859 return;
2862 #ifndef EDIT
2863 if (button_pressed==2 || button_pressed==4 && isMap)
2865 KeyPressed(SDLK_ESCAPE, 0);
2866 keyState[SDLK_ESCAPE] = 0;
2867 return;
2869 #endif
2871 x += scrollX;
2872 y += scrollY;
2874 Pos s = Pos::GetFromWorld(x,y);
2875 if (tileSolid[GetTile(Pos::GetFromWorld(x,y+TILE_HUP))] == 1)
2876 s = Pos::GetFromWorld(x,y+TILE_HUP);
2878 mousep = s;
2880 UpdateCursor(s);
2882 #ifdef EDIT
2883 if (button_held & ~button_pressed & 4)
2885 scrollX -= dx;
2886 scrollY -= dy;
2888 #endif
2890 if (!editMode)
2892 if (isMap && (button_pressed & 1))
2894 ActivateSpecial(s, 1);
2895 return;
2897 if (!isMap && win && winFinal)
2899 if (button_pressed & 1)
2901 LoadMap();
2902 return;
2905 if(!isMap)
2907 if((button_pressed & 1) || (button_held & 1) && (numUndo==0 || time>=undo[numUndo-1].endTime))
2909 if(s.x==player.x && s.y==player.y)
2911 // Don't activate jump powerup without a new click
2912 if (button_pressed & 1)
2913 Input(-1);
2915 else if(s.x==player.x && s.y<player.y)
2916 Input(0);
2917 else if(s.x==player.x && s.y>player.y)
2918 Input(3);
2919 else if(s.y==player.y && s.x<player.x)
2920 Input(5);
2921 else if(s.y==player.y && s.x>player.x)
2922 Input(2);
2923 else if(s.y+s.x==player.y+player.x && s.x>player.x)
2924 Input(1);
2925 else if(s.y+s.x==player.y+player.x && s.x<player.x)
2926 Input(4);
2928 if ((button_pressed & 4) || (button_held & 4) && (undoTime < 0))
2929 Undo();
2931 return;
2934 #ifdef EDIT
2935 if (!button_pressed && !button_held)
2936 return;
2938 if (button_pressed==1)
2939 if (editTile<0)
2940 editTile = GetItem(s)==1 ? -3 : GetItem(s)==2 ? -2 : -1;
2942 if (button_held==1 || button_pressed==1)
2944 ResetUndo();
2945 if (editTile>=0)
2946 SetTile(s, editTile, true, false);
2947 else
2948 SetItem(s, editTile==-2 ? 0 : editTile==-1 ? 1 : 2, true, false);
2951 if (button_pressed==2)
2953 editTile = GetTile(s);
2956 if (button_pressed==8)
2958 editTile=editTile-1;
2959 if (editTile<=0) editTile=NumTileTypes-1;
2962 if (button_pressed==16)
2964 editTile=editTile+1;
2965 if (editTile<=0) editTile=1;
2966 if (editTile==NumTileTypes) editTile=0;
2969 if (button_pressed==64)
2971 ResetUndo();
2972 player = s;
2973 dead = false;
2974 renderer.player.Reset(-1);
2975 renderer.player.Add(new PlayerRender(player, GetHeight(player), dead), 0);
2978 if (button_pressed==256)
2980 char* fn = LoadSaveDialog(false, true, "Select level");
2981 if (fn)
2983 char * l = strstr(fn, "Levels");
2984 if(l)
2986 FILE * f = file_open(l,"rb");
2987 if (f)
2988 fclose(f);
2989 if (f)
2990 SetSpecial(s, l);
2991 else if (l[6]!=0 && l[7]=='_')
2992 SetSpecial(s, l+7);
2994 UpdateCursor(Pos(-1,-1));
2997 if (button_pressed==512)
2999 SetSpecial(s, NULL);
3000 UpdateCursor(Pos(-1,-1));
3002 if (button_pressed==1024)
3004 static char x[1000] = "";
3005 if (!(s.x<0 || s.x>=MAP_SIZE || s.y<0 || s.y>=MAP_SIZE))
3007 char tmp[1000];
3008 strcpy(tmp, x);
3009 if (GetSpecial(s))
3010 strcpy(x, GetSpecial(s));
3011 else
3012 x[0] = 0;
3013 SetSpecial(s, tmp[0] ? tmp : 0);
3014 if (!tmp[0])
3015 SetTile(s, EMPTY, true, false);
3019 if (button_pressed==32)
3021 editTile = editTile<0 ? 1 : -1;
3023 #endif // EDIT
3026 void CheckFinished()
3028 bool slow = false;
3029 if (Count(COLLAPSABLE)==0)
3031 if (Replace(COLLAPSE_DOOR, COLLAPSABLE) == 0)
3032 win = true;
3033 else
3034 slow = true;
3035 Replace(SWITCH, NORMAL);
3037 else
3038 win = false;
3040 if (Count(COLLAPSABLE2)==0)
3041 if (Replace(COLLAPSE_DOOR2, COLLAPSABLE2))
3042 slow = true;
3044 if (slow)
3045 time += BUILD_TIME;
3047 bool Collide(Pos p, bool high)
3049 Tile t = GetTile(p);
3050 // switch(t)
3051 // {
3052 // default:
3053 if (!high)
3054 return tileSolid[t]==1;
3055 else
3056 return false;
3057 // }
3059 void Undo()
3061 if (numUndo==0) return;
3063 UndoDone(); // Complete previous undo...
3065 numUndo--;
3067 if (time > undo[numUndo].endTime)
3068 time = undo[numUndo].endTime;
3069 undoTime = undo[numUndo].time;
3071 undo[numUndo].Restore(this);
3073 void UndoDone()
3075 if (undoTime < 0)
3076 return;
3077 renderer.Reset(undoTime);
3078 time = undoTime;
3079 undoTime = -1;
3081 void ScoreDestroy(Pos p)
3083 Tile t = GetTile(p);
3084 if (t==COLLAPSABLE || t==COLLAPSE_DOOR)
3086 else if (t != EMPTY)
3088 player_score += 10;
3092 bool LaserTile(Pos p, int mask, double fireTime)
3094 if (&renderer(p) == &renderer(Pos(-1,-1)))
3095 return false;
3096 //if (!renderer.Visible(p))
3097 // return false;
3099 TileRender* tr = 0;
3100 if (time <= renderer(p).GetLastTime())
3101 if (fireTime < renderer(p).GetLastTime())
3103 renderer(p).Add(tr = new TileRender(GetTile(p), p, mask), fireTime);
3104 ((TileRender*)renderer(p).GetStage(time+10/*HACKY!*/))->special |= mask;
3106 else
3108 tr = new TileRender(GetTile(p), p, mask | ((TileRender*)renderer(p).GetStage(time+10/*HACKY!*/))->special);
3109 renderer(p).Add(tr, fireTime);
3111 else
3112 renderer(p).Add(tr = new TileRender(GetTile(p), p, mask), fireTime);
3114 if (tr)
3116 tr->specialDuration = time + LASER_LINE_TIME - fireTime + LASER_FADE_TIME;
3118 return true;
3120 void FireGun(Pos newpos, Dir d, bool recurse, double fireTime)
3122 static Pos hits[100];
3123 static Dir hitDir[100];
3124 static int numHits=0;
3125 if (!recurse)
3126 numHits = 0;
3128 double starttime = fireTime;
3129 for (Dir fd=((d<0)?0:d); fd<((d<0)?MAX_DIR:d+1); fd++)
3131 fireTime = starttime;
3132 // starttime += 0.03;
3134 Pos p = newpos + fd;
3135 int range = 0;
3136 for (range; range<MAP_SIZE; range++, p=p+fd)
3138 Tile t = GetTile(p);
3139 if (tileSolid[t]!=-1)
3141 if (t!=TRAP)
3142 renderer(p).Add(new TileRender(tileSolid[t]==1 ? TILE_WHITE_WALL : TILE_WHITE_TILE, p), fireTime+0.1);
3144 int i;
3145 for (i=0; i<numHits; i++)
3146 if (hits[i]==p)
3147 break;
3148 if (i==numHits ||
3149 t==TRAP && (hitDir[i]&(1<<fd))==0
3152 if (i==numHits)
3154 if (i >= sizeof(hits)/sizeof(hits[0]))
3155 return;
3156 hitDir[i] = 1 << fd;
3157 hits[i] = p;
3158 numHits++;
3160 else
3162 hitDir[i] |= 1 << fd;
3164 if (t==TRAP)
3166 int dirmask =
3167 1<<((fd+2) % MAX_DIR)
3168 | 1<<((fd+MAX_DIR-2) % MAX_DIR);
3170 if (LaserTile(p, dirmask, fireTime))
3171 fireTime += (time+LASER_LINE_TIME - fireTime) / 40;
3172 // fireTime += LASER_SEGMENT_TIME;
3174 FireGun(p, (fd+1) % MAX_DIR, true, fireTime);
3175 FireGun(p, (fd+MAX_DIR-1) % MAX_DIR, true, fireTime);
3178 break;
3180 else
3182 LaserTile(p, 1<<(fd%3), fireTime);
3184 fireTime += (time+LASER_LINE_TIME - fireTime) / 40;
3185 // fireTime += LASER_SEGMENT_TIME;
3189 // renderer().Add(new LaserRender(newpos, fd, range), time);
3192 if (!recurse)
3194 double _time = time;
3195 time += LASER_LINE_TIME;
3196 for (int i=0; i<numHits; i++)
3198 Pos p = hits[i];
3199 Tile t = GetTile(p);
3201 if (t==TRAP)
3202 continue;
3204 ScoreDestroy(p);
3206 renderer(p).Add(new ExplosionRender(p, t==GUN), time);
3207 //renderer(p).Add(new TileRender(EMPTY, p), time+2);
3208 SetTile(p, EMPTY, false);
3210 if (GetItem(p))
3211 renderer(p,true).Add(new ItemRender(GetItem(p), 1, p), time);
3213 if (t==GUN)
3215 for (Dir j=0; j<MAX_DIR; j++)
3217 if (GetTile(p+j)!=EMPTY)
3219 renderer(p+j).Add(new TileRender(tileSolid[GetTile(p+j)]==1 ? TILE_WHITE_WALL : TILE_WHITE_TILE, p+j), time+0.05);
3220 renderer(p+j).Add(new ExplosionRender(p+j), time+0.2);
3222 if (GetItem(p+j))
3223 renderer(p+j,true).Add(new ItemRender(GetItem(p+j), 1, p), time);
3225 //renderer(p+j).Add(new TileRender(EMPTY, p+j), time+2.2);
3227 ScoreDestroy(p + j);
3228 SetTile(p + j, EMPTY, false);
3233 time += MAX(LASER_FADE_TIME, 0.15);
3234 //time = _time;
3235 CheckFinished();
3238 int GetLastPlayerRot()
3240 RenderStage* rs = renderer.player.GetStage(-1);
3241 if (!rs) return 3;
3242 return ((PlayerRender*)rs)->r;
3244 bool Input(Dir d)
3246 if (dead || win || isMap)
3247 return false;
3249 // Complete undo
3250 UndoDone();
3252 // Jump forwards in time to last move finishing
3253 if (numUndo > 0 && time < undo[numUndo-1].endTime)
3254 time = undo[numUndo-1].endTime;
3256 double realTime = time;
3257 double endAnimTime = time;
3258 bool high = (tileSolid[GetTile(player)] == 1);
3259 Pos playerStartPos = player;
3260 Pos oldpos = player;
3261 int oldPlayerHeight = GetHeight(oldpos);
3262 Pos newpos = player + d;
3264 int playerRot = GetLastPlayerRot();
3265 if (d!=-1 && d!=playerRot)
3267 while (d!=playerRot)
3269 if ((d+6-playerRot) % MAX_DIR < MAX_DIR/2)
3270 playerRot = (playerRot+1) % MAX_DIR;
3271 else
3272 playerRot = (playerRot+MAX_DIR-1) % MAX_DIR;
3274 time += 0.03;
3276 if (GetTile(oldpos) == FLOATING_BALL)
3278 TileRender* t = new TileRender(FLOATING_BALL, oldpos);
3279 t->special = playerRot + 256;
3280 renderer(oldpos).Add(t, time);
3282 renderer.player.Add(new PlayerRender(playerRot, Pos(-20,-20), oldPlayerHeight, Pos(-20,-20), oldPlayerHeight, dead), time);
3284 else
3286 PlayerRender *p = new PlayerRender(playerRot, player, oldPlayerHeight, player, oldPlayerHeight, dead);
3287 p->speed = 0;
3288 renderer.player.Add(p, time);
3292 time += 0.03;
3295 if (d<0 && player_items[1]==0)
3296 return false;
3298 if (d >= 0)
3300 if (tileSolid[GetTile(newpos)] == -1)
3302 time = realTime;
3303 return false;
3305 if (Collide(newpos, high))
3307 time = realTime;
3308 return false;
3312 // Don't change any real state before this point!
3313 if (numUndo >= MAX_UNDO)
3315 numUndo--;
3316 for(int i=0; i<MAX_UNDO-1; i++)
3317 undo[i] = undo[i+1];
3319 undo[numUndo].New(d, player, player_items, time, player_score);
3321 if (d<0)
3323 player_items[1]--;
3326 int old_score=player_score;
3328 double time0 = time;
3329 time += 0.15; //Time for leave-tile fx
3331 switch (GetTile(oldpos))
3333 case COLLAPSABLE:
3334 SetTile(oldpos, EMPTY);
3335 renderer(oldpos).Add(new DisintegrateRender(oldpos), time);
3336 CheckFinished();
3337 break;
3339 case COLLAPSE_DOOR:
3340 // Don't need to CheckFinished - can't be collapse doors around
3341 // unless there're still collapsable tiles around.
3342 SetTile(oldpos, EMPTY);
3343 renderer(oldpos).Add(new DisintegrateRender(oldpos, 1), time);
3344 break;
3346 case COLLAPSABLE2:
3347 SetTile(oldpos, COLLAPSABLE, false);
3348 renderer(oldpos).Add(new DisintegrateRender(oldpos, 0, 1), time);
3349 player_score += 10;
3350 CheckFinished();
3351 break;
3353 case COLLAPSE_DOOR2:
3354 SetTile(oldpos, COLLAPSE_DOOR, false);
3355 renderer(oldpos).Add(new DisintegrateRender(oldpos, 1, 1), time);
3356 player_score += 10;
3357 break;
3359 case COLLAPSABLE3:
3360 SetTile(oldpos, COLLAPSABLE2);
3361 break;
3364 time = time0; //End of leave-tile fx
3366 int retry_pos_count=0;
3367 retry_pos:
3368 retry_pos_count++;
3370 if (GetItem(newpos)==1)
3372 renderer(newpos, true).Add(new ItemCollectRender(GetItem(newpos), newpos), time + JUMP_TIME/2);
3373 SetItem(newpos, 0, false);
3374 player_items[0]++;
3376 if (GetItem(newpos)==2)
3378 renderer(newpos, true).Add(new ItemCollectRender(GetItem(newpos), newpos), time + JUMP_TIME/2);
3379 SetItem(newpos, 0, false);
3380 player_items[1]++;
3383 if (GetTile(player) == FLOATING_BALL)
3385 TileRender* t = new TileRender(FLOATING_BALL, player);
3386 t->special = 0;
3387 renderer(oldpos).Add(t, time);
3390 PlayerRender *p = new PlayerRender(playerRot, player, oldPlayerHeight, newpos, GetHeight(newpos), dead);
3392 // alternate leg (hacky!)
3393 if (1)
3395 static int l=0;
3396 l++;
3397 p->type = l & 1;
3400 if (retry_pos_count!=0 && GetTile(player)==TRAP)
3402 p->speed /= 1.5;
3403 p->type = 2;
3405 if (d==-1)
3406 p->speed = JUMP_TIME * 1.5;
3407 renderer.player.Add(p, time);
3408 endAnimTime = MAX(endAnimTime, time + p->speed+0.001);
3409 time += p->speed;
3411 player = newpos;
3413 switch (GetTile(newpos))
3415 case COLLAPSABLE:
3416 renderer(newpos).Add(new TileRender(TILE_GREEN_CRACKED, newpos), time);
3417 break;
3418 case COLLAPSE_DOOR:
3419 renderer(newpos).Add(new TileRender(TILE_GREEN_CRACKED_WALL, newpos), time);
3420 break;
3421 case COLLAPSABLE2:
3422 renderer(newpos).Add(new TileRender(TILE_BLUE_CRACKED, newpos), time);
3423 break;
3424 case COLLAPSE_DOOR2:
3425 renderer(newpos).Add(new TileRender(TILE_BLUE_CRACKED_WALL, newpos), time);
3426 break;
3428 case EMPTY:
3429 dead = true;
3430 break;
3432 case BUILDER:
3434 double pretime = time;
3435 bool done = false;
3436 time += 0.15;
3437 for (Dir fd=0; fd<MAX_DIR; fd++)
3439 Tile t2 = GetTile(newpos + fd);
3440 if (t2==EMPTY)
3442 done = true;
3443 SetTile(newpos+fd, COLLAPSABLE, false);
3444 renderer(newpos+fd).Add(new BuildRender(newpos+fd, fd, 0), time);
3446 else if (t2==COLLAPSABLE)
3448 done = true;
3449 SetTile(newpos+fd, COLLAPSE_DOOR, false);
3450 renderer(newpos+fd).Add(new BuildRender(newpos+fd, fd, 1), time);
3453 if (done) time += BUILD_TIME;
3454 else time = pretime;
3455 CheckFinished();
3456 endAnimTime = MAX(endAnimTime, time + 0.1);
3458 break;
3460 case SWITCH:
3461 Swap(COLLAPSE_DOOR, COLLAPSABLE);
3462 break;
3464 case FLOATING_BALL:
3466 int step=0;
3467 renderer.player.Add(new PlayerRender(playerRot, Pos(-30,-30), 0, Pos(-30,-30), 0, dead), time);
3468 while (tileSolid[GetTile(newpos+d)]==-1)
3470 step++;
3472 if (!renderer.Visible(newpos+d))
3474 TileRender* r = new TileRender(FLOATING_BALL, newpos);
3475 r->special = 512;
3476 renderer(newpos).Add(r, time);
3478 PlayerRender* pr = new PlayerRender(playerRot, newpos, 0, newpos, 0, dead);
3479 pr->speed = JUMP_TIME*1;
3480 renderer.player.Add(pr, time);
3482 time += pr->speed;
3484 dead = 1;
3485 break;