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 //////////////////////////////////////////////////////
28 //#define MAP_LOCKED_VISIBLE
30 #define GAME_NAME "Hex-a-hop"
33 // #define MAP_EDIT_HACKS
34 #define MAP_EDIT_HACKS_DISPLAY_UNLOCK 0
36 #define BMP_SUFFIX ".bmp"
38 #define USE_LEVEL_PACKFILE
39 #define BMP_SUFFIX ".dat"
45 #define GAMENAME GAME_NAME " (EDIT MODE)"
48 #define GAMENAME GAME_NAME
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 //////////////////////////////////////////////////////
64 #include "tiletypes.h"
66 #ifdef USE_LEVEL_PACKFILE
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
82 // printf(" -> \"%s\"\n", filename );
84 filename
.fix_backslashes();
85 FILE* f
= fopen( filename
, flags
);
89 printf("Warning: unable to open file \"%s\" for %s\n", (const char*)filename
, strchr(flags
, 'r') ? "reading" : "writing");
97 static const short value_order
[]={
106 COLLAPSE_DOOR
, COLLAPSABLE2
,
118 //#define PROGRESS_FILE "progress.dat"
120 #define PI (3.1415926535897931)
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
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;
141 bool showScoring
= false;
142 bool hintsDone
= false;
168 TILE_GREEN_FRAGMENT_1
,
169 TILE_GREEN_FRAGMENT_2
,
174 TILE_GREEN_CRACKED_WALL
,
176 TILE_BLUE_CRACKED_WALL
,
178 TILE_FIRE_PARTICLE_1
,
179 TILE_FIRE_PARTICLE_2
,
183 TILE_LASER_FADE_0
= 53,
184 TILE_BLUE_FRAGMENT
= 56,
185 TILE_BLUE_FRAGMENT_1
,
186 TILE_BLUE_FRAGMENT_2
,
188 TILE_LASER_REFRACT
= 60,
189 TILE_ICE_LASER_REFRACT
= TILE_LASER_REFRACT
+6,
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
);
210 while (i
>=0 && filename
[i
]!='\\' && filename
[i
]!='.' && filename
[i
]!='/')
212 if (filename
[i
]=='.')
213 strcpy(filename
+i
+1, newsuffix
);
216 strcat(filename
, ".");
217 strcat(filename
, newsuffix
);
221 bool isMap
=false, isRenderMap
=false;
222 int isFadeRendering
=0;
228 |-----------| TILE_W3
231 / \ |TILE_H1 |TILE_H2
238 WL = sqrt(h1*h1 + w1*w1)
239 wl**2 = h1**2 + w1**2
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
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)
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
)
278 const int BytesPerPixel
= i
->format
->BytesPerPixel
;
279 const int BitsPerPixel
= i
->format
->BitsPerPixel
;
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;
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))
303 memset(font
, 0, sizeof(font
));
305 int h
= FONT_SPACING
;
307 for (int i
=33; i
<=127; i
++)
314 if (y
>= fontImage
->h
)
316 if (y
+h
> fontImage
->h
)
317 h
= fontImage
->h
- y
;
318 } while(IsEmpty(fontImage
, x
, y
, 1, h
));
321 while(!IsEmpty(fontImage
, x
+w
, y
, 1, h
) && x
+w
<fontImage
->w
)
324 while (h1
>1 && IsEmpty(fontImage
, x
, y
+h1
-1, w
, 1))
330 //printf("character %c: % 4d % 4d % 4d % 4d\n", i, x, y, w, h1);
337 font
[i
].w
= font
['j'].w
;
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
, ...)
372 va_start( marker
, string
); /* Initialize variable arguments. */
375 vsprintf((char*)tmp
, string
, marker
);
379 va_end( marker
); /* Reset variable arguments. */
382 int FontWidth(const char * string
)
385 for (int i
=0; string
[i
]; i
++)
386 w
+= font
[string
[i
]].w
+ FONT_X_SPACING
;
390 void PrintR(int x
, int y
, const char * string
, ...)
393 va_start( marker
, string
); /* Initialize variable arguments. */
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
, ...)
406 va_start( marker
, string
); /* Initialize variable arguments. */
409 vsprintf((char*)tmp
, string
, marker
);
414 char * end
= split
? strstr(scan
," ") : 0;
417 PrintRaw(x
- FontWidth(scan
)/2, y
, scan
);
423 PrintRaw(x
- FontWidth(scan
)/2, y
, scan
);
429 va_end( marker
); /* Reset variable arguments. */
433 #include "savestate.h"
435 #include "level_list.h"
437 void SaveState::GetStuff()
439 general
.hintFlags
= HintMessage::flags
;
441 void SaveState::ApplyStuff()
443 HintMessage::flags
= general
.hintFlags
;
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
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 {
467 int getScreenY() const {
468 return x
*TILE_H1
+ y
*TILE_H2
;
470 static Pos
GetFromWorld(double x
, double y
)
475 tx
= (int)floor(x
/TILE_W2
);
477 ty
= (int)floor(y
/TILE_H2
);
482 if (x
< TILE_W1
&& y
< TILE_H1
)
483 if (x
*TILE_H1
+ y
* TILE_W1
< TILE_H1
*TILE_W1
)
485 if (x
< TILE_W1
&& y
> TILE_H1
)
486 if (x
*TILE_H1
+ (TILE_H2
-y
) * TILE_W1
< TILE_H1
*TILE_W1
)
492 Pos
mousep(0,0), keyboardp(4,20);
498 virtual void Render(RenderObject
* r
, double time
, bool reflect
) = 0;
499 virtual int GetDepth(double time
) { return 1; }
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
);
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
;
532 free(stage
); free(time
);
534 bool Active(double t
)
536 if (numStages
==0) return false;
537 if (t
< time
[0]) return false;
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
)
547 while (currentStage
<numStages
-1 && time
[currentStage
+1]<=t
)
552 RenderStage
* GetStage(double t
)
554 if (t
==-1 && numStages
>0)
555 return stage
[numStages
-1];
557 if (!Active(t
)) return 0;
559 return stage
[currentStage
];
563 return numStages
>0 ? time
[numStages
-1] : -1;
565 void Render(double t
, bool reflect
)
570 stage
[currentStage
]->Render(this, t
- time
[currentStage
], reflect
);
572 int GetDepth(double t
)
577 return stage
[currentStage
]->GetDepth(t
- time
[currentStage
]);
582 numStages
= currentStage
= 0;
585 while (numStages
> 0 && time
[numStages
-1] >= t
)
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
;
599 void Add(RenderStage
* s
, double t
)
603 if (currentStage
<numStages
&& time
[currentStage
]<=t
)
606 while (i
<numStages
&& time
[i
]<t
)
609 if (i
<numStages
&& time
[i
]==t
)
617 memmove(&time
[i
+1], &time
[i
], (numStages
-i
) * sizeof(time
[0]));
618 memmove(&stage
[i
+1], &stage
[i
], (numStages
-i
) * sizeof(stage
[0]));
632 RenderObject tile
[SIZE
][SIZE
][2];
645 void Reset(double t
= -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
++)
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
++)
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;
689 if (j
>=SIZE
) i
+=(j
+1-SIZE
)*2, j
=SIZE
-1;
690 for (; i
<x1
&& j
>=0; i
+=2, j
--)
699 void Render(double t
, bool reflect
)
703 int playerDepth
= player
.GetDepth(t
);
704 if (reflect
) playerDepth
-=4;
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
;
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;
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
++)
737 fx
[j
].Render(t
, reflect
);
741 RenderObject
& operator () ()
744 if (fxPos
==FX
) fxPos
= 0;
747 RenderObject
& operator () (Pos
const & p
, bool item
=false)
749 if (p
.x
<0 || p
.y
<0 || p
.x
>=SIZE
|| p
.y
>=SIZE
)
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];
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
);
775 SDL_BlitSurface(tileGraphics
, &src
, screen
, &dst
);
782 src
.w
-= TILE_W1
*2, src
.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
)
791 int sy
= frame
* 80*2;
793 y
+= TILE_H_REFLECT_OFFSET
+20+h
, sy
+= 80;
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
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
;
816 void Render(RenderObject
* r
, double time
, bool reflect
)
821 int y
= -5 + (int)Translate(r
->seed
, r
->currentTime
+ time
);
824 if (!reflect
&& !water
)
825 RenderTile( false, TILE_SPHERE
, p
.getScreenX(), p
.getScreenY());
828 item
==1 ? TILE_ITEM1
: TILE_ITEM2
,
829 p
.getScreenX(), p
.getScreenY()+y
834 void RenderFade(double time
, int dir
, int 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
;
846 RenderTile(false, TILE_BLACK_TILE
, x
+scrollX
, y
+scrollY
);
852 struct FadeRender
: public RenderStage
856 FadeRender(int d
=-1) : seed(rand()), dir(d
)
861 void Render(RenderObject
* r
, double time
, bool reflect
)
866 if (dir
==1) dir
=0, isFadeRendering
=0;
869 RenderFade(time
, dir
, seed
);
873 struct ScrollRender
: public RenderStage
877 ScrollRender(int a
,int b
) : x(a
), y(b
), done(false) {}
879 void Render(RenderObject
* r
, double time
, bool reflect
)
882 scrollX
= x
, scrollY
= y
;
888 struct LevelSelectRender
: public RenderStage
893 #ifdef MAP_EDIT_HACKS
897 LevelSelectRender(Pos
const & _p
, int i2
, int adj
) : p(_p
), item(i2
), adj(adj
)
900 void Render(RenderObject
* r
, double time
, bool reflect
)
905 #ifndef MAP_LOCKED_VISIBLE
910 for (int i
=0; i
<MAX_DIR
; i
++)
912 RenderTile( false, TILE_LINK_0
+i
, p
.getScreenX(), p
.getScreenY());
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
);
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
)
946 time
= LIFT_TIME
-time
;
947 time
= time
/ LIFT_TIME
;
952 time
= (3 - 2*time
)*time
*time
;
954 time
= (3 - 2*time
)*time
*time
;
956 return (int)((TILE_H_LIFT_UP
+4) * time
);
958 return (int)((TILE_H_LIFT_UP
-4) * time
) + 4;
961 struct TileRender
: public RenderStage
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)
976 if (special
&& (t
==LIFT_UP
|| t
==LIFT_DOWN
) && time
<LIFT_TIME
)
978 int y
= GetLiftHeight(time
, t
);
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());
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
)
994 if (time
< specialDuration
-LASER_FADE_TIME
)
995 RenderTile(reflect
, TILE_ICE_LASER_REFRACT
, p
.getScreenX(), p
.getScreenY());
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
;
1003 for(int i
=0; foo
; foo
>>=1, i
++)
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));
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
);
1023 RenderTile(true, 18, p
.getScreenX(), p
.getScreenY() - 14);
1026 RenderBoat(reflect
, int(special
)&255, p
.getScreenX(), p
.getScreenY(), y
);
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
)
1036 RenderGirl(reflect
, d
, 0, x
, y
, -yo
);
1037 RenderTile(reflect
, FLOATING_BALL
, x
, y
+yo
);
1040 RenderGirl(reflect
, d
, 0, x
, y
, -yo
);
1041 RenderTile(true, 17, x
, y
+yo
-TILE_H_REFLECT_OFFSET
);
1046 struct TileRotateRender
: public TileRender
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
)
1057 double f
= time
/ ROTATION_TIME
;
1059 if (mode
& 1) f
+= 0.5;
1068 if (mode
& 1) f
=1-f
; else f
=f
;
1072 TileRender::Render(r
, time
, reflect
);
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
));
1080 RenderBoat(reflect
, (mode
&1) ? (d
+MAX_DIR
/2)%MAX_DIR
: d
, x
, y
, 2);
1082 RenderTile(reflect
, t
, x
, y
);
1087 struct LaserRender
: public RenderStage
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
1108 ExplosionRender(Pos _p
, int _pow
=0, int t
=0) : p(_p
), power(_pow
), type(t
)
1113 virtual int GetDepth(double time
)
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;
1126 int q
= 50 - int(time
* 35);
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;
1144 if (!reflect
&& ys
<-60)
1146 const double T
= 0.06;
1147 double ct
= -ys
/ 128.0;
1150 x
= p
.getScreenX() + int(xs
* (1+ct
*(2+power
)));
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,
1161 time
- i
*0.003 < 0.2 ? TILE_WATER_PARTICLE
+1 : TILE_WATER_PARTICLE
,
1162 x
, y
+(reflect
?-1:1)*yo
);
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
1184 DisintegrateRender(Pos _p
, int _pow
=0, int _t
=0) : p(_p
), height(_pow
), type(_t
)
1189 void Render(RenderObject
* r
, double time
, bool reflect
)
1192 RenderTile(reflect
, height
? COLLAPSE_DOOR
: COLLAPSABLE
, p
.getScreenX(), p
.getScreenY());
1194 if (time
> 50.0/70.0) return;
1195 if (reflect
) return;
1197 int q
= 50 - int(time
* 70);
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;
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)));
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
;
1220 RenderTile(false, t
, x
, y
+(reflect
?-yo
:yo
));
1224 struct BuildRender
: public RenderStage
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());
1243 RenderTile(reflect
, type
? COLLAPSABLE2
: COLLAPSABLE
, p
.getScreenX(), p
.getScreenY());
1245 double dist
= time
* 2 / BUILD_TIME
;
1248 Pos from
= p
+ ((dir
+MAX_DIR
/2)%MAX_DIR
);
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
);
1261 if (reverse
) dist
= 1-dist
;
1263 if (dist
> 0 && !height
)
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());
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());
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
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
));
1323 virtual int GetDepth(double time
)
1325 double f
= speed
? time
/ speed
: 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
)
1340 double f
= speed
? time
/ speed
: 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();
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
);
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)
1366 shadow_h
= f
>=0.7 ? int(shadow_h
*(f
-0.7)/0.3) : 0;
1369 arc
= speed
> JUMP_TIME
? 7 : 2;
1371 h
= (int)(h1
+(h2
-h1
)*f
);
1372 // if (x==x2 && y==y2)
1376 //h += int(TILE_H_LIFT_UP/3 * (1-f));
1377 h
+= (int)(f
*(1-f
)*TILE_HUP2
*arc
);
1389 //frame = ((int)(f*4) % 4);
1390 //if (frame==2) frame=0; else if (frame==3) frame=2;
1393 else if (f
==1 || x
==x2
&& y
==y2
) // stationary
1399 frame
= type
? 2 : 1;
1405 RenderTile( false, TILE_SPHERE
,
1407 (int)(y
+(y2
-y
)*f
) - shadow_h
1420 dead ? TILE_SPHERE_OPEN : TILE_SPHERE_DONE,
1429 struct HexPuzzle
: public State
1433 #define MAX_TILECHANGE 64 // TODO: don't have a magic upper limit
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
];
1460 void Add(TileChange
const & tc
)
1462 for (int i
=0; i
<numT
; i
++)
1465 if (numT
>=MAX_TILECHANGE
)
1466 FATAL("numT>=MAX_TILECHANGE");
1470 void New(Dir pmove
, Pos
& pp
, int* items
, double t
, int sc
)
1472 numItems
[0] = items
[0];
1473 numItems
[1] = items
[1];
1475 playerMovement
= pmove
;
1480 void Restore(HexPuzzle
* w
)
1482 for (int i
=numT
-1; i
>=0; i
--)
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);
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
;
1503 int player_items
[2];
1505 int numComplete
, numLevels
, numMastered
, numLevelsFound
;
1512 WorldRenderer renderer
;
1517 Undo undo
[MAX_UNDO
];
1519 LevelInfo
* currentLevelInfo
;
1521 char currentFile
[1000];
1528 LevelInfo
* GetLevelInfo(const char* f
)
1530 if (strstr(f
, "Levels\\") == f
)
1532 if (currentLevelInfo
!=0 && strcmp(currentLevelInfo
->file
, f
)==0)
1533 return currentLevelInfo
;
1538 if (t
<= numComplete
)
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");
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>>"};
1554 #ifdef MAP_EDIT_HACKS
1555 int GetAutoTile(const char * level
, bool tiletype
)
1557 FILE* f
= file_open(filename
, "rb");
1561 if (f
&& fscanf(f
, "%d", &version
)==1 && (version
==3 || version
==4))
1563 if (strstr(level
,"mk"))
1566 fgetc(f
); // Remove '\n' character
1569 unsigned char bounds
[4];
1571 fread(&par
, sizeof(par
), 1, f
);
1573 fread(&diff
, sizeof(diff
), 1, f
);
1574 fread(bounds
, sizeof(bounds
), 1, f
);
1575 fread(&playerStart
, sizeof(player
), 1, f
);
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))
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
;
1601 if (value_order
[highval
] == LIFT_UP
)
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);
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);
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
));
1655 HintMessage::FlagTile(t
);
1657 HintMessage::FlagTile(item
+20);
1659 HintMessage::FlagTile(EMPTY
);
1668 UpdateCursor(Pos(-1,-1));
1680 player_items
[0] = player_items
[1] = 0;
1682 if (strlen(currentSlot
) == 0)
1689 if (!isFadeRendering
&& time
!=0)
1691 renderer().Add(new FadeRender(-1), time
);
1697 renderer
.Reset(time
);
1700 for (int t
=0; t
<NumTileTypes
; t
++)
1703 for (int i
=0; i
<MAP_SIZE
; i
++)
1704 for (int j
=0; j
<MAP_SIZE
; j
++)
1707 int item
= GetItem(p
);
1709 renderer(p
,true).Add(new ItemRender(item
, GetTile(p
)==EMPTY
, p
), time
);
1714 for (int i
=0; i
<MAP_SIZE
; i
++)
1715 for (int j
=0; j
<MAP_SIZE
; j
++)
1725 renderer(p
).Add(new TileRender(t
, p
), time
);
1729 renderer
.player
.Add(new PlayerRender(player
, GetHeight(player
), dead
), time
);
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
++)
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
;
1757 sx
= bounds
[0] - int(TILE_W2
*6.35);
1758 sy
= (bounds
[3] + bounds
[2] - SCREEN_H
) / 2 - TILE_H2
/2;
1762 sx
= (bounds
[1] + bounds
[0] - SCREEN_W
) / 2 - TILE_W3
/2;
1763 sy
= (bounds
[3] + bounds
[2] - SCREEN_H
) / 2 - TILE_H2
/2;
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
);
1783 char* ReadAll(FILE* f
)
1786 fseek(f
, 0, SEEK_END
);
1788 fseek(f
, 0, SEEK_SET
);
1789 char* c
= loadPtr
= new char [size
];
1790 endLoad
= loadPtr
+ size
;
1791 fread(c
, 1, size
, f
);
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
;
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
);
1813 loadPtr
= (char*)e
->Data();
1814 endLoad
= loadPtr
+ e
->DataLen();
1818 FILE* f
= file_open(level
, "rb");
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;
1828 return getdiff
? diff
: par
;
1830 fn(&version
, 2, 1, f
); // skip to relevant point
1832 if (fn(&par
, sizeof(par
), 1, f
) != 1)
1834 if (fn(&diff
, sizeof(diff
), 1, f
) != 1 || diff
<0 || diff
>10)
1837 #ifdef USE_LEVEL_PACKFILE
1838 loadPtr
= endLoad
= 0;
1844 return getdiff
? diff
: par
;
1847 bool LoadSave(const char * filename
, bool save
)
1854 showScoring
= false;
1855 LevelSave
* l
= progress
.GetLevel(filename
, true);
1856 if (progress
.general
.scoringOn
&& l
&& l
->Completed() )
1860 #ifdef USE_LEVEL_PACKFILE
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;
1878 FILE* f
= file_open(filename
, save
? "wb" : "rb");
1881 strcpy(currentFile
, filename
);
1883 currentLevelInfo
= GetLevelInfo(currentFile
);
1887 char* data
= ReadAll(f
);
1890 loadPtr
= endLoad
= 0;
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
);
1911 int version
= VERSION
;
1913 fprintf(f
, "%d\n", version
);
1917 if (fn(&c
, 1, 1, f
) != 1)
1921 // Remove '\n' character
1927 for (int i
=0; i
<MAP_SIZE
; i
++)
1928 for (int j
=0; j
<MAP_SIZE
; j
++)
1930 delete [] special
[i
][j
];
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];
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
;
1965 memset(map
, 0, sizeof(map
));
1966 memset(map_item
, 0, sizeof(map_item
));
1970 fn(&levelPar
, 1, sizeof(levelPar
), f
);
1975 fn(&levelDiff
, 1, sizeof(levelDiff
), f
);
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;
2015 for (int i
=bounds
[0]; i
<=bounds
[1]; i
++)
2016 for (int j
=bounds
[2]; j
<=bounds
[3]; 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
);
2032 if (!fn(&x
, sizeof(x
), 1, f
))
2034 fn(&y
, sizeof(y
), 1, f
);
2035 x
+= offsetx
; y
+= offsety
;
2036 fn(&len
, sizeof(len
), 1, f
);
2038 char* tmp
= new char[len
+1];
2042 SetSpecial(Pos(x
,y
), tmp
, true, false);
2047 return; // Unsupported version!
2051 // Save when returning to map!
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
)
2064 if (p
.y
<0 || p
.y
>MAP_SIZE
)
2066 if (map
[p
.x
][p
.y
] == t
)
2068 if (map
[p
.x
][p
.y
] == t
)
2071 tileCount
[map
[p
.x
][p
.y
]]--;
2075 undo
[numUndo
].Add(Undo::TileChange(p
,GetTile(p
),GetItem(p
)));
2080 renderer(p
).Add(new TileRender(t
, p
), time
);
2083 Tile
GetTile(Pos
const & p
)
2085 if (p
.x
<0 || p
.x
>=MAP_SIZE
)
2087 if (p
.y
<0 || p
.y
>=MAP_SIZE
)
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
)
2101 if (p
.y
<0 || p
.y
>=MAP_SIZE
)
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
)
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
);
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
);
2136 LevelSave
* l
= progress
.GetLevel(x
, false);
2140 if (strcmp(x
, STARTING_LEVEL
)==0)
2142 if (x
[0]=='_' && l
&& l
->unlocked
)
2145 if (l
&& l
->Completed())
2152 int par
= GetPar(x
);
2153 if (progress
.general
.scoringOn
&& l
->PassesPar( par
))
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)
2175 void ActivateSpecial(Pos
const & p
, int type
)
2177 if (p
.x
<0 || p
.x
>=MAP_SIZE
|| p
.y
<0 || p
.y
>=MAP_SIZE
)
2180 char * x
= special
[p
.x
][p
.y
];
2182 if (x
==0 || x
[0]==0)
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);
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
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);
2217 SetTile(p
, EMPTY
, true, false);
2219 renderer(p
,true).Add(lsr
, time
);
2223 //printf("Level: %s\n", x);
2225 int t
= GetLevelState(p
);
2227 if (t
&& !GetItem(p
))
2234 currentLevelInfo
= 0;
2238 LevelSave
* l
= progress
.GetLevel(x
, 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);
2249 if (p
.x
> mapRightBound
) mapRightBound
= p
.x
;
2256 LevelSelectRender
* lsr
= new LevelSelectRender( p
, t
& 0xff, t
>>8 );
2258 #ifdef MAP_EDIT_HACKS
2260 int t
= GetAutoTile(x
, true);
2261 int v
= GetAutoTile(x
, false);
2262 if (MAP_EDIT_HACKS_DISPLAY_UNLOCK
)
2265 lsr
->magic
= GetPar(x
, true);
2267 SetTile(p
, t
, true, false);
2269 SetTile(p
, EMPTY
, true, false);
2272 renderer(p
,true).Add(lsr
, time
);
2277 if (type
==1 && x
[0]!='_') // Clicked on
2279 int t
= GetLevelState(p
);
2287 void SetItem(Pos
const & p
, int t
, bool updateRenderer
=true, bool undoBuffer
=true)
2289 if (p
.x
<0 || p
.x
>MAP_SIZE
)
2291 if (p
.y
<0 || p
.y
>MAP_SIZE
)
2293 if (map_item
[p
.x
][p
.y
] == t
)
2297 undo
[numUndo
].Add(Undo::TileChange(p
,GetTile(p
),GetItem(p
)));
2299 map_item
[p
.x
][p
.y
] = t
;
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
)
2309 if (p
.y
<0 || p
.y
>=MAP_SIZE
)
2311 return map_item
[p
.x
][p
.y
];
2314 void LoadSaveProgress(bool save
)
2316 FILE* f
= file_open(currentSlot
, save
? "wb" : "rb");
2319 progress
.LoadSave(f
, save
);
2330 LoadSaveProgress(false);
2334 LoadSaveProgress(true);
2337 SDL_Surface
* Load(const char * bmp
, bool colourKey
=true)
2339 typedef unsigned int uint32
;
2342 SDL_Surface
* g
= 0;
2345 if (strstr(bmp
, ".bmp"))
2347 g
= SDL_LoadBMP(bmp
);
2351 strcpy(strstr(out
, ".bmp"), ".dat");
2353 // SDL_PixelFormat p;
2355 // SDL_Surface* tmp = SDL_ConvertSurface(g, &p, SDL_SWSURFACE);
2357 short w
=g
->w
, h
=g
->h
;
2358 char* buf
= (char*) g
->pixels
;
2361 while (IsEmpty(g
, w
-1, 0, 1, h
) && w
>1)
2363 while (IsEmpty(g
, 0, h
-1, w
, 1) && h
>1)
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
);
2376 while (i
< (int)w
*h
&& c
== (*(uint32
*)&buf
[(i
%w
)*3 + (i
/w
)*g
->pitch
] | mask
))
2385 fwrite(&c
, sizeof(c
), 1, f
);
2388 fwrite(&i0
, sizeof(i0
), 1, f
);
2398 FILE* f
= file_open(bmp
, "rb");
2399 if (!f
) FATAL("Unable to open file", bmp
);
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
];
2410 for (int p
=0; p
<(int)w
*h
; p
++)
2416 fread(&c
, sizeof(c
), 1, f
);
2419 fread(&cnt
, sizeof(cnt
), 1, f
);
2421 tmp
[p
] = c
| 0xff000000;
2424 g
= SDL_CreateRGBSurfaceFrom(tmp
, w
, h
, 32, w
*4,
2433 if (!g
) FATAL("Unable to create SDL surface");
2435 SDL_SetColorKey(g
, SDL_SRCCOLORKEY
, SDL_MapRGB(g
->format
, WATER_COLOUR
));
2436 SDL_Surface
* out
= SDL_DisplayFormat(g
);
2439 if (!out
) FATAL("Unable to create SDL surface (SDL_DisplayFormat)");
2443 #ifdef USE_LEVEL_PACKFILE
2444 PackFile1 levelFiles
;
2448 SDL_WM_SetCaption(GAMENAME
, 0);
2452 #ifdef USE_LEVEL_PACKFILE
2453 FILE* f
= file_open("levels.dat", "rb");
2455 FATAL("Unable to open file", "levels.dat");
2465 currentLevelInfo
= 0;
2472 memset(map
, 0, sizeof(map
));
2473 memset(map_item
, 0, sizeof(map_item
));
2474 memset(special
, 0, sizeof(special
));
2478 // player = Pos(1,11);
2488 progress
.GetLevel(STARTING_LEVEL
, true)->unlocked
= 1;
2489 if (!progress
.GetLevel(STARTING_LEVEL
, true)->Completed())
2491 LoadSave(STARTING_LEVEL
, false);
2497 LoadSave(mapname
, false);
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
};
2508 int boundW
= mapBG
->w
;
2510 boundW
= MIN(boundW
, (mapRightBound
+4) * TILE_W2
- TILE_W1
);
2512 src
.x
= scrollX
- initScrollX
;
2513 if (src
.x
+src
.w
> boundW
)
2515 int diff
= src
.x
+src
.w
- boundW
;
2526 //scrollY = initScrollY;
2529 mapScrollX
= scrollX
;
2531 SDL_BlitSurface(mapBG
, &src
, screen
, &dst
);
2534 SDL_BlitSurface(gradient
, &src
, screen
, &dst
);
2536 renderer
.Render(time
, true);
2538 if (!hintsDone
&& !isFadeRendering
)
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
++)
2550 dst
.y
= src
.y
= SCREEN_H
-1-i
;
2554 const bool farView
= false;
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 );
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
);
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
)
2580 while (i
>1 && time
<undo
[i
-1].time
)
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
)
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
);
2596 Print(0, SCREEN_H
-FONT_SPACING
, " Par: %d Current: %d", levelPar
, v
[2]);
2599 Print(0,0," Anti-Ice: %d", v
[0]);
2601 Print(0,0," Jumps: %d", v
[1]);
2604 if (isRenderMap
&& isMap
&& !isFadeRendering
)
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
);
2612 if (numComplete
==numLevels
&& progress
.general
.endSequence
>0)
2613 Print(0, SCREEN_H
-FONT_SPACING
, " %d%% Mastered", numMastered
*100/numLevels
);
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);
2633 if ((currentLevelInfo
|| noMouse
) && isMap
&& isRenderMap
&& !activeMenu
&& isFadeRendering
<=0)
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
)
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
);
2671 PrintC(false, dst
.x
, dst
.y
+ FONT_SPACING
*4 - FONT_SPACING
/4, "Completed", player_score
);
2674 PrintC(false, dst
.x
, dst
.y
+ FONT_SPACING
*4 - FONT_SPACING
/4, "Incomplete", player_score
);
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
);
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;
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!");
2720 activeMenu
->Render();
2727 RenderTile(false, editTile
, mousex
+scrollX
, mousey
+scrollY
);
2731 Print(mousex
, mousey
-2, "\x7f");
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...
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
)
2753 SetTile(Pos(x
,y
), t2
);
2755 else if (GetTile(Pos(x
,y
))==t2
)
2758 SetTile(Pos(x
,y
), t
);
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...
2772 for (int x
=0; x
<MAP_SIZE
; x
++)
2773 for (int y
=0; y
<MAP_SIZE
; y
++)
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);
2806 void UpdateCursor(Pos
const & s
)
2809 if (s
.x
!=_s
.x
|| s
.y
!=_s
.y
)
2813 char* sp
= GetSpecial(s
);
2820 currentLevelInfo
= 0;
2821 levelPar
= player_score
= -1;
2822 if (GetLevelState(s
)>=2)
2824 LevelSave
* l
= progress
.GetLevel(sp
, true);
2827 currentLevelInfo
= GetLevelInfo(sp
);
2828 levelPar
= GetPar(sp
);
2829 player_score
= l
->GetScore();
2835 sprintf(tmp
, "Special(%d,%d): %s (%d)", s
.x
, s
.y
, sp
? sp
: "<None>", GetPar(sp
));
2836 SDL_WM_SetCaption(tmp
, NULL
);
2839 else if (currentFile
[0])
2842 SDL_WM_SetCaption(currentFile
, NULL
);
2845 currentLevelInfo
= 0;
2850 virtual void Mouse(int x
, int y
, int dx
, int dy
, int button_pressed
, int button_released
, int button_held
)
2854 activeMenu
->Mouse(x
,y
,dx
,dy
,button_pressed
,button_released
,button_held
);
2858 if (isFadeRendering
)
2863 if (button_pressed
==2 || button_pressed
==4 && isMap
)
2865 KeyPressed(SDLK_ESCAPE
, 0);
2866 keyState
[SDLK_ESCAPE
] = 0;
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
);
2883 if (button_held
& ~button_pressed
& 4)
2892 if (isMap
&& (button_pressed
& 1))
2894 ActivateSpecial(s
, 1);
2897 if (!isMap
&& win
&& winFinal
)
2899 if (button_pressed
& 1)
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)
2915 else if(s
.x
==player
.x
&& s
.y
<player
.y
)
2917 else if(s
.x
==player
.x
&& s
.y
>player
.y
)
2919 else if(s
.y
==player
.y
&& s
.x
<player
.x
)
2921 else if(s
.y
==player
.y
&& s
.x
>player
.x
)
2923 else if(s
.y
+s
.x
==player
.y
+player
.x
&& s
.x
>player
.x
)
2925 else if(s
.y
+s
.x
==player
.y
+player
.x
&& s
.x
<player
.x
)
2928 if ((button_pressed
& 4) || (button_held
& 4) && (undoTime
< 0))
2935 if (!button_pressed
&& !button_held
)
2938 if (button_pressed
==1)
2940 editTile
= GetItem(s
)==1 ? -3 : GetItem(s
)==2 ? -2 : -1;
2942 if (button_held
==1 || button_pressed
==1)
2946 SetTile(s
, editTile
, true, false);
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)
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");
2983 char * l
= strstr(fn
, "Levels");
2986 FILE * f
= file_open(l
,"rb");
2991 else if (l
[6]!=0 && 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
))
3010 strcpy(x
, GetSpecial(s
));
3013 SetSpecial(s
, tmp
[0] ? tmp
: 0);
3015 SetTile(s
, EMPTY
, true, false);
3019 if (button_pressed
==32)
3021 editTile
= editTile
<0 ? 1 : -1;
3026 void CheckFinished()
3029 if (Count(COLLAPSABLE
)==0)
3031 if (Replace(COLLAPSE_DOOR
, COLLAPSABLE
) == 0)
3035 Replace(SWITCH
, NORMAL
);
3040 if (Count(COLLAPSABLE2
)==0)
3041 if (Replace(COLLAPSE_DOOR2
, COLLAPSABLE2
))
3047 bool Collide(Pos p
, bool high
)
3049 Tile t
= GetTile(p
);
3054 return tileSolid
[t
]==1;
3061 if (numUndo
==0) return;
3063 UndoDone(); // Complete previous undo...
3067 if (time
> undo
[numUndo
].endTime
)
3068 time
= undo
[numUndo
].endTime
;
3069 undoTime
= undo
[numUndo
].time
;
3071 undo
[numUndo
].Restore(this);
3077 renderer
.Reset(undoTime
);
3081 void ScoreDestroy(Pos p
)
3083 Tile t
= GetTile(p
);
3084 if (t
==COLLAPSABLE
|| t
==COLLAPSE_DOOR
)
3086 else if (t
!= EMPTY
)
3092 bool LaserTile(Pos p
, int mask
, double fireTime
)
3094 if (&renderer(p
) == &renderer(Pos(-1,-1)))
3096 //if (!renderer.Visible(p))
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
;
3108 tr
= new TileRender(GetTile(p
), p
, mask
| ((TileRender
*)renderer(p
).GetStage(time
+10/*HACKY!*/))->special
);
3109 renderer(p
).Add(tr
, fireTime
);
3112 renderer(p
).Add(tr
= new TileRender(GetTile(p
), p
, mask
), fireTime
);
3116 tr
->specialDuration
= time
+ LASER_LINE_TIME
- fireTime
+ LASER_FADE_TIME
;
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;
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
;
3136 for (range
; range
<MAP_SIZE
; range
++, p
=p
+fd
)
3138 Tile t
= GetTile(p
);
3139 if (tileSolid
[t
]!=-1)
3142 renderer(p
).Add(new TileRender(tileSolid
[t
]==1 ? TILE_WHITE_WALL
: TILE_WHITE_TILE
, p
), fireTime
+0.1);
3145 for (i
=0; i
<numHits
; i
++)
3149 t
==TRAP
&& (hitDir
[i
]&(1<<fd
))==0
3154 if (i
>= sizeof(hits
)/sizeof(hits
[0]))
3156 hitDir
[i
] = 1 << fd
;
3162 hitDir
[i
] |= 1 << fd
;
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
);
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);
3194 double _time
= time
;
3195 time
+= LASER_LINE_TIME
;
3196 for (int i
=0; i
<numHits
; i
++)
3199 Tile t
= GetTile(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);
3211 renderer(p
,true).Add(new ItemRender(GetItem(p
), 1, p
), time
);
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);
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);
3238 int GetLastPlayerRot()
3240 RenderStage
* rs
= renderer
.player
.GetStage(-1);
3242 return ((PlayerRender
*)rs
)->r
;
3246 if (dead
|| win
|| isMap
)
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
;
3272 playerRot
= (playerRot
+MAX_DIR
-1) % MAX_DIR
;
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
);
3286 PlayerRender
*p
= new PlayerRender(playerRot
, player
, oldPlayerHeight
, player
, oldPlayerHeight
, dead
);
3288 renderer
.player
.Add(p
, time
);
3295 if (d
<0 && player_items
[1]==0)
3300 if (tileSolid
[GetTile(newpos
)] == -1)
3305 if (Collide(newpos
, high
))
3312 // Don't change any real state before this point!
3313 if (numUndo
>= MAX_UNDO
)
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
);
3326 int old_score
=player_score
;
3328 double time0
= time
;
3329 time
+= 0.15; //Time for leave-tile fx
3331 switch (GetTile(oldpos
))
3334 SetTile(oldpos
, EMPTY
);
3335 renderer(oldpos
).Add(new DisintegrateRender(oldpos
), time
);
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
);
3347 SetTile(oldpos
, COLLAPSABLE
, false);
3348 renderer(oldpos
).Add(new DisintegrateRender(oldpos
, 0, 1), time
);
3353 case COLLAPSE_DOOR2
:
3354 SetTile(oldpos
, COLLAPSE_DOOR
, false);
3355 renderer(oldpos
).Add(new DisintegrateRender(oldpos
, 1, 1), time
);
3360 SetTile(oldpos
, COLLAPSABLE2
);
3364 time
= time0
; //End of leave-tile fx
3366 int retry_pos_count
=0;
3370 if (GetItem(newpos
)==1)
3372 renderer(newpos
, true).Add(new ItemCollectRender(GetItem(newpos
), newpos
), time
+ JUMP_TIME
/2);
3373 SetItem(newpos
, 0, false);
3376 if (GetItem(newpos
)==2)
3378 renderer(newpos
, true).Add(new ItemCollectRender(GetItem(newpos
), newpos
), time
+ JUMP_TIME
/2);
3379 SetItem(newpos
, 0, false);
3383 if (GetTile(player
) == FLOATING_BALL
)
3385 TileRender
* t
= new TileRender(FLOATING_BALL
, player
);
3387 renderer(oldpos
).Add(t
, time
);
3390 PlayerRender
*p
= new PlayerRender(playerRot
, player
, oldPlayerHeight
, newpos
, GetHeight(newpos
), dead
);
3392 // alternate leg (hacky!)
3400 if (retry_pos_count
!=0 && GetTile(player
)==TRAP
)
3406 p
->speed
= JUMP_TIME
* 1.5;
3407 renderer
.player
.Add(p
, time
);
3408 endAnimTime
= MAX(endAnimTime
, time
+ p
->speed
+0.001);
3413 switch (GetTile(newpos
))
3416 renderer(newpos
).Add(new TileRender(TILE_GREEN_CRACKED
, newpos
), time
);
3419 renderer(newpos
).Add(new TileRender(TILE_GREEN_CRACKED_WALL
, newpos
), time
);
3422 renderer(newpos
).Add(new TileRender(TILE_BLUE_CRACKED
, newpos
), time
);
3424 case COLLAPSE_DOOR2
:
3425 renderer(newpos
).Add(new TileRender(TILE_BLUE_CRACKED_WALL
, newpos
), time
);
3434 double pretime
= time
;
3437 for (Dir fd
=0; fd
<MAX_DIR
; fd
++)
3439 Tile t2
= GetTile(newpos
+ fd
);
3443 SetTile(newpos
+fd
, COLLAPSABLE
, false);
3444 renderer(newpos
+fd
).Add(new BuildRender(newpos
+fd
, fd
, 0), time
);
3446 else if (t2
==COLLAPSABLE
)
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
;
3456 endAnimTime
= MAX(endAnimTime
, time
+ 0.1);
3461 Swap(COLLAPSE_DOOR
, COLLAPSABLE
);
3467 renderer
.player
.Add(new PlayerRender(playerRot
, Pos(-30,-30), 0, Pos(-30,-30), 0, dead
), time
);
3468 while (tileSolid
[GetTile(newpos
+d
)]==-1)
3472 if (!renderer
.Visible(newpos
+d
))
3474 TileRender
* r
= new TileRender(FLOATING_BALL
, newpos
);
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
);
3488 newpos
= oldpos
+ d
;
3490 SetTile(oldpos
, EMPTY
, false);
3491 SetTile(newpos
, FLOATING_BALL
, false);
3493 renderer(oldpos
).Add(new TileRotateRender(FLOATING_BALL
, oldpos
, d
, 2), time
);
3494 renderer(oldpos
).Add(new TileRender(EMPTY
, oldpos
), time
+ ROTATION_TIME
/2);
3495 renderer(newpos
).Add(new TileRotateRender(FLOATING_BALL
, newpos
, (d
+3)%MAX_DIR
, 3), time
+ ROTATION_TIME
/2);
3497 // PlayerRender *p = new PlayerRender(playerRot, oldpos, 0, newpos, 0, dead);
3498 // p->speed = ROTATION_TIME*0.9;
3499 // renderer.player.Add(p, time);
3501 endAnimTime
= MAX(endAnimTime
, time
+ ROTATION_TIME
+ ROTATION_TIME
/2);
3502 time
+= ROTATION_TIME
;
3505 // renderer.player.Add(new PlayerRender(playerRot, player, 0, player, 0, 0), time);
3511 TileRender
* r
= new TileRender(FLOATING_BALL
, newpos
);
3512 r
->special
= playerRot
+ 256;
3513 renderer(newpos
).Add(r
, time
);
3521 SetTile(newpos
, GetTile(newpos
)==LIFT_UP
? LIFT_DOWN
: LIFT_UP
, false);
3522 renderer(newpos
).Add(new TileRender(GetTile(newpos
), newpos
, 1), time
);
3524 PlayerRender
*p
= new PlayerRender(playerRot
, newpos
, 1-GetHeight(newpos
), newpos
, GetHeight(newpos
), dead
);
3525 renderer
.player
.Add(p
, time
);
3526 endAnimTime
= MAX(endAnimTime
, time
+ JUMP_TIME
);
3534 if (Collide(newpos
+ d
, high
))
3536 if (Collide((newpos
+ d
) + d
, high
) == 1)
3537 newpos
= (newpos
+ d
);
3539 newpos
= (newpos
+ d
) + d
;
3540 if (tileSolid
[GetTile(newpos
)] == -1)
3547 for (Dir d
=0; d
<MAX_DIR
; d
++)
3549 Tile tmp
= GetTile(newpos
+ d
);
3550 renderer(newpos
+ d
).Add(new TileRotateRender(tmp
, newpos
+ d
, (d
+2)%MAX_DIR
, false), time
);
3552 Tile tmp
= GetTile(newpos
+ Dir(MAX_DIR
-1));
3553 for (Dir d
=0; d
<MAX_DIR
; d
++)
3555 Tile t2
= GetTile(newpos
+ d
);
3556 SetTile(newpos
+ d
, tmp
, false);
3557 renderer(newpos
+ d
).Add(new TileRotateRender(tmp
, newpos
+ d
, (d
+4)%MAX_DIR
, true), time
+ ROTATION_TIME
/2);
3558 if (GetItem(newpos
+ d
))
3559 renderer(newpos
+ d
,true).Add(new ItemRender(GetItem(newpos
+ d
), GetTile(newpos
+ d
)==EMPTY
, newpos
+d
), time
+ ROTATION_TIME
/2);
3563 endAnimTime
= MAX(endAnimTime
, time
+ROTATION_TIME
);
3564 // renderer(newpos).Add(new TileRotateRender(SPINNER, Dir(0), 0), time);
3572 if (player_items
[0]==0)
3574 if (tileSolid
[GetTile(newpos
+ d
)] == 1)
3576 newpos
= newpos
+ d
;
3577 if (tileSolid
[GetTile(newpos
)] == -1)
3584 SetTile(newpos
, COLLAPSABLE3
);
3592 FireGun(newpos
, d
, false, time
);
3594 endAnimTime
= MAX(endAnimTime
, time
);
3596 if (GetTile(newpos
)==EMPTY
)
3598 PlayerRender
* pr
= new PlayerRender(playerRot
, player
, 0, player
, 0, dead
);
3599 pr
->speed
= JUMP_TIME
*1;
3600 renderer
.player
.Add(pr
, time
);
3610 for (Dir fd=((d<0)?0:d); fd<((d<0)?MAX_DIR:d+1); fd++)
3612 Pos p = newpos + fd;
3614 for (range; range<MAP_SIZE; range++, p=p+fd)
3616 Tile t = GetTile(p);
3617 if (tileSolid[t]!=-1)
3619 hits[numHits++] = p;
3624 renderer().Add(new LaserRender(newpos, fd, range), time);
3627 double _time = time;
3629 for (int i=0; i<numHits; i++)
3632 Tile t = GetTile(p);
3634 renderer().Add(new ExplosionRender(p), time);
3640 for (Dir j=0; j<MAX_DIR; j++)
3642 ScoreDestroy(p + j);
3643 SetTile(p + j, EMPTY);
3645 if (GetTile(newpos)==EMPTY)
3649 endAnimTime = MAX(endAnimTime, time);
3659 endAnimTime
= MAX(endAnimTime
, time
);
3665 PlayerRender
* pr
= new PlayerRender(player
, 0, dead
);
3666 pr
->speed
= 0; // Don't sit around before disappearing!
3667 renderer
.player
.Add(pr
, time
);
3669 // If the tile we're drowning on isn't visible, give the ownership of the splash effect to the player, rather than a tile.
3670 if (renderer
.Visible(player
))
3671 renderer(player
).Add(new ExplosionRender(player
, 0, 1), time
);
3673 renderer
.player
.Add(new ExplosionRender(player
, 0, 1), time
);
3675 endAnimTime
= MAX(endAnimTime
, time
+2);
3682 undo
[numUndo
].endTime
= endAnimTime
;
3687 void Update(double timedelta
)
3694 activeMenu
->Update(timedelta
);
3699 for (int i
=0; i
<SDLK_LAST
; i
++)
3706 if (isMap
&& isRenderMap
)
3709 static double scrollHi
= 0;
3714 int xx
= noMouse
? keyboardp
.getScreenX()-scrollX
: mousex
;
3715 if (xx
> SCREEN_W
) xx
= SCREEN_W
;
3718 x
= (double)xx
/ (w
) - 1;
3719 if (xx
> SCREEN_W
- w
)
3720 x
= 1 - (double)(SCREEN_W
-xx
) / (w
);
3722 if (x
<-min
|| x
>min
)
3724 scrollHi
+= timedelta
* x
;
3725 scrollX
+= (int)scrollHi
;
3726 scrollHi
-= (int)scrollHi
;
3731 if (undoTime
>=0 && undoTime
< time
)
3733 double acc
= (time
- undoTime
) / 2;
3734 if (acc
< 3) acc
= 3;
3735 time
-= timedelta
* acc
;
3736 if (undoTime
>= time
)
3743 time
+= timedelta
* 20;
3746 void FileDrop(const char* filename
)
3748 LoadSave(filename
, false);
3753 if (keyState
[SDLK_LALT
] || keyState
[SDLK_LCTRL
])
3757 if (!isMap
&& !editMode
&& undoTime
< 0)
3759 if (keyState
['z'] || keyState
[SDLK_BACKSPACE
] || keyState
['u'])
3765 if (isMap
&& !editMode
)
3768 if ((keyState
['q'] | keyState
[SDLK_KP7
]) & 2) keyboardp
.x
--;
3769 else if ((keyState
['d'] | keyState
[SDLK_KP3
]) & 2) keyboardp
.x
++;
3770 else if ((keyState
['e'] | keyState
[SDLK_KP9
]) & 2) keyboardp
.x
++, keyboardp
.y
--;
3771 else if ((keyState
['a'] | keyState
[SDLK_KP1
]) & 2) keyboardp
.x
--, keyboardp
.y
++;
3772 else if ((keyState
['w'] | keyState
[SDLK_KP8
] | keyState
[SDLK_UP
]) & 2) keyboardp
.y
--;
3773 else if ((keyState
['s'] | keyState
[SDLK_KP2
] | keyState
[SDLK_DOWN
]) & 2) keyboardp
.y
++;
3774 else if ((keyState
[SDLK_LEFT
]) & 2) keyboardp
.x
--, keyboardp
.y
+=keyboardp
.x
&1;
3775 else if (((keyState
[SDLK_RIGHT
]) & 2)) { if (keyboardp
.x
< mapRightBound
) keyboardp
.y
-=keyboardp
.x
&1, keyboardp
.x
++; }
3776 else if ((keyState
[SDLK_RETURN
] | keyState
[SDLK_KP5
] | keyState
[SDLK_SPACE
] | keyState
[SDLK_KP_ENTER
]) & 2)
3778 // Simulate user clicking on it...
3779 Mouse(keyboardp
.getScreenX()-scrollX
, keyboardp
.getScreenY()-scrollY
, 0, 0, 1, 0, 0);
3786 UpdateCursor(keyboardp
);
3789 int min
[21] = { 17, 16, 15, 14, 13, 13, 13, 13, 13, 13, 12, 11, 11, 13, 12, 11, 8, 8, 7, 6, 7 };
3790 int max
[21] = { 20, 20, 19, 19, 19, 19, 18, 21, 20, 20, 19, 19, 18, 18, 17, 16, 16, 16, 15, 15, 14 };
3791 if (keyboardp
.x
< 3) keyboardp
.x
= 3;
3792 if (keyboardp
.x
> mapRightBound
) keyboardp
.x
= mapRightBound
;
3794 if (keyboardp
.y
< min
[keyboardp
.x
-3]) keyboardp
.y
= min
[keyboardp
.x
-3];
3795 if (keyboardp
.y
> max
[keyboardp
.x
-3]) keyboardp
.y
= max
[keyboardp
.x
-3];
3797 UpdateCursor(keyboardp
);
3799 else if (!editMode
&& (numUndo
==0 || time
>=undo
[numUndo
-1].endTime
))
3801 static int usedDiag
= 0;
3803 if (keyState
['q'] || keyState
[SDLK_KP7
]) HandleKey('q', 0);
3804 else if (keyState
['w'] || keyState
[SDLK_KP8
]) HandleKey('w', 0);
3805 else if (keyState
['e'] || keyState
[SDLK_KP9
]) HandleKey('e', 0);
3806 else if (keyState
['a'] || keyState
[SDLK_KP1
]) HandleKey('a', 0);
3807 else if (keyState
['s'] || keyState
[SDLK_KP2
]) HandleKey('s', 0);
3808 else if (keyState
['d'] || keyState
[SDLK_KP3
]) HandleKey('d', 0);
3810 else if (keyState
[SDLK_UP
] && keyState
[SDLK_LEFT
]) HandleKey('q', 0), usedDiag
=1;
3811 else if (keyState
[SDLK_UP
] && keyState
[SDLK_RIGHT
]) HandleKey('e', 0), usedDiag
=1;
3812 else if (keyState
[SDLK_DOWN
] && keyState
[SDLK_LEFT
]) HandleKey('a', 0), usedDiag
=1;
3813 else if (keyState
[SDLK_DOWN
] && keyState
[SDLK_RIGHT
]) HandleKey('d', 0), usedDiag
=1;
3814 else if (keyState
[SDLK_UP
] && !usedDiag
) HandleKey('w', 0);
3815 else if (keyState
[SDLK_DOWN
] && !usedDiag
) HandleKey('s', 0);
3820 void KeyReleased(int key
)
3824 bool KeyPressed(int key
, int mod
)
3830 bool eat
= activeMenu
->KeyPressed(key
, mod
);
3832 memset(keyState
, 0, sizeof(keyState
));
3837 if ((key
==SDLK_ESCAPE
&& (mod
& KMOD_CTRL
)))
3839 if (mod
& KMOD_SHIFT
)
3843 LoadSaveProgress(false);
3849 if (isFadeRendering
)
3852 return HandleKey(key
, mod
);
3855 bool HandleKey(int key
, int mod
)
3860 if (isMap
&& key
=='r' && (mod
& KMOD_ALT
))
3869 else if ((key
=='p' && !editMode
|| key
==SDLK_PAUSE
|| key
==SDLK_ESCAPE
))
3872 new PauseMenu(isMap
, progress
.GetLevel(STARTING_LEVEL
, true)->Completed(), progress
.general
.endSequence
>=1, progress
.general
.endSequence
>=2);
3876 else if (key
=='e' && (mod
& KMOD_ALT
))
3877 editMode
= !editMode
;
3879 else if (key
=='p' && (mod
& KMOD_ALT
) && numUndo
>0
3880 || key
>='0' && key
<='9' && (mod
& KMOD_SHIFT
) && !isMap
)
3882 if (key
>='0' && key
<='9')
3883 levelDiff
= (key
=='0') ? 10 : key
-'0';
3885 if (key
=='p' && levelPar
==0)
3886 levelPar
= player_score
;
3891 undo
[numUndo
-1].Restore(this);
3895 if (LoadSave(currentFile
, true))
3897 if (key
>='0' && key
<='9')
3903 /////////////////////////////////////////////////////////////////////////
3904 if (isMap
&& !editMode
)
3907 else if (key
==SDLK_KP9
|| key
=='e') Input(1), noMouse
=1;
3908 else if (key
==SDLK_KP3
|| key
=='d') Input(2), noMouse
=1;
3909 else if (key
==SDLK_KP1
|| key
=='a') Input(4), noMouse
=1;
3910 else if (key
==SDLK_KP7
|| key
=='q') Input(5), noMouse
=1;
3911 else if (key
==SDLK_KP8
|| key
=='w') Input(0), noMouse
=1;
3912 else if (key
==SDLK_KP2
|| (key
=='s' && (((mod
& (KMOD_CTRL
|KMOD_ALT
))==0)||!editMode
))) Input(3), noMouse
=1;
3913 else if (key
==SDLK_KP5
|| key
==SDLK_SPACE
|| key
==SDLK_RETURN
|| key
==SDLK_KP_ENTER
)
3916 if (win
&& winFinal
)
3917 LoadMap(), memset(keyState
, 0, sizeof(keyState
));
3922 else if (key
=='r' && (mod
& KMOD_CTRL
))
3923 LoadSave(currentFile
, false);
3926 else if (key
=='z' && (mod
& KMOD_ALT
))
3928 if (numUndo
>0 && !isMap
)
3930 time
= undo
[numUndo
-1].endTime
;
3931 undoTime
= undo
[0].time
;
3934 undo
[numUndo
-1].Restore(this);
3939 else if (key
=='z' || key
==SDLK_BACKSPACE
|| key
==SDLK_DELETE
|| key
=='u')
3946 else if (key
=='s' && (mod
& KMOD_ALT
)){
3947 if (win
&& strlen(currentFile
)>0 && !isMap
)
3950 strcpy(tmp
, currentFile
);
3951 ChangeSuffix(tmp
, "sol");
3952 FILE* f
= file_open(tmp
, "wb");
3955 for (int i
=0; i
<numUndo
; i
++)
3957 fputc(undo
[i
].playerMovement
, f
);
3966 else if (key
=='/' && (mod
& KMOD_ALT
)){
3974 if (mod
& KMOD_SHIFT
)
3976 LevelSave
* l
= progress
.GetLevel(currentFile
, false);
3977 if (l
&& l
->Completed())
3979 for (int i
=0; i
<l
->bestSolutionLength
; i
++)
3980 Input(l
->bestSolution
[i
]);
3989 strcpy(tmp
, currentFile
);
3990 ChangeSuffix(tmp
, "sol");
3991 FILE* f
= file_open(tmp
, "rb");
3995 while ((dir
= fgetc(f
)) != -1)
4016 else if (key
>='0' && key
<='9' && (mod
& KMOD_ALT
) && !isMap
)
4017 levelPar
= levelPar
*10 + key
-'0';
4018 else if (key
==SDLK_BACKSPACE
&& (mod
& KMOD_ALT
) && !isMap
)
4022 Mouse(mousex
, mousey
, 0, 0, 32, 0, mouse_buttons
);
4023 else if (key
=='p' && !(mod
& KMOD_ALT
))
4024 Mouse(mousex
, mousey
, 0, 0, 64, 0, mouse_buttons
);
4026 Mouse(mousex
, mousey
, 0, 0, 128, 0, mouse_buttons
);
4027 else if (key
==SDLK_RETURN
)
4028 Mouse(mousex
, mousey
, 0, 0, 256, 0, mouse_buttons
);
4029 else if (key
==SDLK_BACKSPACE
)
4030 Mouse(mousex
, mousey
, 0, 0, 512, 0, mouse_buttons
);
4032 Mouse(mousex
, mousey
, 0, 0, 1024, 0, mouse_buttons
);
4034 else if (key
=='s' && (mod
& KMOD_CTRL
)){
4035 char *fn
= LoadSaveDialog(true, true, "Save level");
4037 SDL_WM_SetCaption(currentFile
, NULL
);
4040 else if (key
=='o' && (mod
& KMOD_CTRL
)){
4041 char* fn
= LoadSaveDialog(false, true, "Open level");
4042 LoadSave(fn
, false);
4043 SDL_WM_SetCaption(currentFile
, NULL
);
4054 #define X(NAME,FILE,ALPHA) NAME = Load("graphics/" FILE BMP_SUFFIX, ALPHA);
4055 #include "gfx_list.h"
4057 static int first
= 1;
4065 // unsigned int d = {
4068 // static SDL_Cursor * c = SDL_CreateCursor(data, mask, 32, 32, 1, 1);
4069 // SDL_SetCursor(c);
4074 #define X(NAME,FILE,ALPHA) if (NAME) SDL_FreeSurface(NAME), NAME=0;
4075 #include "gfx_list.h"
4077 virtual void ScreenModeChanged()
4084 MAKE_STATE(HexPuzzle
, SDLK_F1
, false);
4086 char * HexPuzzle::loadPtr
= 0;
4087 char * HexPuzzle::endLoad
= 0;