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
;
447 // somewhere else Tile map[][] is assigned to an unsigned char not int32_t
448 // but the data file format expects it to be 32 bit wide!??
449 typedef int32_t Tile
;
453 Pos() : x(0), y(0) {}
454 Pos(int a
, int b
) : x(a
), y(b
) {}
455 bool operator == (Pos
const & p
) const
457 return x
==p
.x
&& y
==p
.y
;
459 Pos
operator + (Dir
const d
) const
462 x
+ ((d
==1 || d
==2) ? 1 : (d
==4 || d
==5) ? -1 : 0),
463 y
+ ((d
==0 || d
==1) ? -1 : (d
==3 || d
==4) ? 1 : 0)
466 int getScreenX() const {
469 int getScreenY() const {
470 return x
*TILE_H1
+ y
*TILE_H2
;
472 static Pos
GetFromWorld(double x
, double y
)
477 tx
= (int)floor(x
/TILE_W2
);
479 ty
= (int)floor(y
/TILE_H2
);
484 if (x
< TILE_W1
&& y
< TILE_H1
)
485 if (x
*TILE_H1
+ y
* TILE_W1
< TILE_H1
*TILE_W1
)
487 if (x
< TILE_W1
&& y
> TILE_H1
)
488 if (x
*TILE_H1
+ (TILE_H2
-y
) * TILE_W1
< TILE_H1
*TILE_W1
)
494 Pos
mousep(0,0), keyboardp(4,20);
500 virtual void Render(RenderObject
* r
, double time
, bool reflect
) = 0;
501 virtual int GetDepth(double time
) { return 1; }
518 if (maxStages
<= numStages
)
520 maxStages
= maxStages
? maxStages
*2 : 4;
521 stage
= (RenderStage
**) realloc(stage
, sizeof(stage
[0])*maxStages
);
522 time
= (double*) realloc(time
, sizeof(time
[0])*maxStages
);
526 RenderObject() : stage(0), time(0), numStages(0), maxStages(0), currentStage(0)
528 // TODO: use a random number with better range
529 // or maybe make seed an int or float...
530 seed
= rand() / (double)RAND_MAX
;
534 free(stage
); free(time
);
536 bool Active(double t
)
538 if (numStages
==0) return false;
539 if (t
< time
[0]) return false;
542 void UpdateCurrent(double t
)
544 if (currentStage
>= numStages
) currentStage
= numStages
-1;
545 if (currentStage
< 0) currentStage
= 0;
547 while (currentStage
>0 && time
[currentStage
]>t
)
549 while (currentStage
<numStages
-1 && time
[currentStage
+1]<=t
)
554 RenderStage
* GetStage(double t
)
556 if (t
==-1 && numStages
>0)
557 return stage
[numStages
-1];
559 if (!Active(t
)) return 0;
561 return stage
[currentStage
];
565 return numStages
>0 ? time
[numStages
-1] : -1;
567 void Render(double t
, bool reflect
)
572 stage
[currentStage
]->Render(this, t
- time
[currentStage
], reflect
);
574 int GetDepth(double t
)
579 return stage
[currentStage
]->GetDepth(t
- time
[currentStage
]);
584 numStages
= currentStage
= 0;
587 while (numStages
> 0 && time
[numStages
-1] >= t
)
593 if (currentStage
> 0 && numStages
> 0)
595 memmove(&time
[0], &time
[currentStage
], sizeof(time
[0]) * numStages
-currentStage
);
596 memmove(&stage
[0], &stage
[currentStage
], sizeof(stage
[0]) * numStages
-currentStage
);
597 numStages
-= currentStage
;
601 void Add(RenderStage
* s
, double t
)
605 if (currentStage
<numStages
&& time
[currentStage
]<=t
)
608 while (i
<numStages
&& time
[i
]<t
)
611 if (i
<numStages
&& time
[i
]==t
)
619 memmove(&time
[i
+1], &time
[i
], (numStages
-i
) * sizeof(time
[0]));
620 memmove(&stage
[i
+1], &stage
[i
], (numStages
-i
) * sizeof(stage
[0]));
634 RenderObject tile
[SIZE
][SIZE
][2];
647 void Reset(double t
= -1)
653 for (int i
=0; i
<SIZE
; i
++)
654 for (int j
=0; j
<SIZE
; j
++)
655 for (int q
=0; q
<2; q
++)
656 tile
[i
][j
][q
].Reset(t
);
658 for (int j
=0; j
<FX
; j
++)
667 for (int i
=0; i
<SIZE
; i
++)
668 for (int j
=0; j
<SIZE
; j
++)
669 for (int q
=0; q
<2; q
++)
670 tile
[i
][j
][q
].Wipe();
672 for (int j
=0; j
<FX
; j
++)
678 int x0
= (scrollX
+TILE_W2
) / TILE_W2
;
679 int x1
= (scrollX
+SCREEN_W
+TILE_W3
+TILE_W1
) / TILE_W2
;
680 if (p
.x
<0 || p
.y
<0 || p
.x
>=SIZE
|| p
.y
>=SIZE
) return false;
681 if (p
.x
<x0
) return false;
682 if (p
.x
>=x1
-1) return false;
683 for (int j0
=0; j0
<SIZE
*3; j0
++)
685 if (j0
* TILE_H1
< scrollY
-TILE_H1
) continue;
686 if (j0
* TILE_H1
> scrollY
+SCREEN_H
+TILE_H1
) break;
691 if (j
>=SIZE
) i
+=(j
+1-SIZE
)*2, j
=SIZE
-1;
692 for (; i
<x1
&& j
>=0; i
+=2, j
--)
701 void Render(double t
, bool reflect
)
705 int playerDepth
= player
.GetDepth(t
);
706 if (reflect
) playerDepth
-=4;
708 player
.Render(t
, reflect
);
710 int x0
= (scrollX
+TILE_W2
) / TILE_W2
;
711 int x1
= (scrollX
+SCREEN_W
+TILE_W3
+TILE_W1
) / TILE_W2
;
714 for (int j0
=0; j0
<SIZE
*3; j0
++)
716 if (j0
* TILE_H1
< scrollY
-TILE_H1
) continue;
717 if (j0
* TILE_H1
> scrollY
+SCREEN_H
+TILE_H1
) break;
722 if (j
>=SIZE
) i
+=(j
+1-SIZE
)*2, j
=SIZE
-1;
723 for (; i
<x1
&& j
>=0; i
+=2, j
--)
725 for (int q
=reflect
?1:0; q
!=2 && q
!=-1; q
+= (reflect
? -1 : 1))
726 if (tile
[i
][j
][q
].Active(t
))
728 tile
[i
][j
][q
].Render(t
, reflect
);
732 if (playerDepth
==j0
|| j0
==SIZE
*3 && playerDepth
>j0
)
733 player
.Render(t
, reflect
);
736 for (int j
=0; j
<FX
; j
++)
739 fx
[j
].Render(t
, reflect
);
743 RenderObject
& operator () ()
746 if (fxPos
==FX
) fxPos
= 0;
749 RenderObject
& operator () (Pos
const & p
, bool item
=false)
751 if (p
.x
<0 || p
.y
<0 || p
.x
>=SIZE
|| p
.y
>=SIZE
)
753 return tile
[p
.x
][p
.y
][item
? 1 : 0];
757 void RenderTile(bool reflect
, int t
, int x
, int y
, int cliplift
)
759 SDL_Rect src
= tile
[reflect
][t
];
760 SDL_Rect dst
= {x
-scrollX
-GFX_SIZE
/2, y
-scrollY
-GFX_SIZE
+TILE_H1
};
761 dst
.x
+= tileOffset
[reflect
][t
][0];
762 dst
.y
+= tileOffset
[reflect
][t
][1];
764 dst
.y
+= TILE_H_REFLECT_OFFSET
;
765 if (cliplift
==-1 || reflect
)
767 // dst.w=src.w; dst.h=src.h;
768 // SDL_FillRect(screen, &dst, rand());
769 SDL_BlitSurface(reflect
? tileGraphicsR
: tileGraphics
, &src
, screen
, &dst
);
777 SDL_BlitSurface(tileGraphics
, &src
, screen
, &dst
);
784 src
.w
-= TILE_W1
*2, src
.x
+= TILE_W1
;
786 SDL_BlitSurface(tileGraphics
, &src
, screen
, &dst
);
790 void RenderGirl(bool reflect
, int r
, int frame
, int x
, int y
, int h
)
793 int sy
= frame
* 80*2;
795 y
+= TILE_H_REFLECT_OFFSET
+20+h
, sy
+= 80;
798 SDL_Rect src
= {sx
, sy
, 64, 80};
799 SDL_Rect dst
= {x
-scrollX
-32, y
-scrollY
-65};
800 SDL_BlitSurface(girlGraphics
, &src
, screen
, &dst
);
803 struct ItemRender
: public RenderStage
809 ItemRender(int i2
, int _water
, Pos
const & _p
) : item(i2
), p(_p
), water(_water
)
812 double Translate(double seed
, double time
)
814 double bob
= time
*2 + seed
*PI2
;
818 void Render(RenderObject
* r
, double time
, bool reflect
)
823 int y
= -5 + (int)Translate(r
->seed
, r
->currentTime
+ time
);
826 if (!reflect
&& !water
)
827 RenderTile( false, TILE_SPHERE
, p
.getScreenX(), p
.getScreenY());
830 item
==1 ? TILE_ITEM1
: TILE_ITEM2
,
831 p
.getScreenX(), p
.getScreenY()+y
836 void RenderFade(double time
, int dir
, int seed
)
840 for(int x
=rand()%22-11; x
<SCREEN_W
+22; x
+=32, ys
^= 1)
842 for (int y
=ys
*20; y
<SCREEN_H
+30; y
+=40)
844 double a
= (rand()&0xff)*dir
;
845 double b
= (time
* 0x400 + (y
- SCREEN_H
) * 0x140/SCREEN_H
)*dir
;
848 RenderTile(false, TILE_BLACK_TILE
, x
+scrollX
, y
+scrollY
);
854 struct FadeRender
: public RenderStage
858 FadeRender(int d
=-1) : seed(rand()), dir(d
)
863 void Render(RenderObject
* r
, double time
, bool reflect
)
868 if (dir
==1) dir
=0, isFadeRendering
=0;
871 RenderFade(time
, dir
, seed
);
875 struct ScrollRender
: public RenderStage
879 ScrollRender(int a
,int b
) : x(a
), y(b
), done(false) {}
881 void Render(RenderObject
* r
, double time
, bool reflect
)
884 scrollX
= x
, scrollY
= y
;
890 struct LevelSelectRender
: public RenderStage
895 #ifdef MAP_EDIT_HACKS
899 LevelSelectRender(Pos
const & _p
, int i2
, int adj
) : p(_p
), item(i2
), adj(adj
)
902 void Render(RenderObject
* r
, double time
, bool reflect
)
907 #ifndef MAP_LOCKED_VISIBLE
912 for (int i
=0; i
<MAX_DIR
; i
++)
914 RenderTile( false, TILE_LINK_0
+i
, p
.getScreenX(), p
.getScreenY());
923 TILE_SPHERE
+ item
-1,
924 p
.getScreenX(), p
.getScreenY()
927 #ifdef MAP_EDIT_HACKS
928 int x
= p
.getScreenX()-scrollX
, y
= p
.getScreenY()-scrollY
;
929 Print(x
+5,y
-25,"%d",magic
);
935 struct ItemCollectRender
: public ItemRender
937 ItemCollectRender(int i2
, Pos
const & p
) : ItemRender(i2
, 0, p
)
940 void Render(RenderObject
* r
, double time
, bool reflect
)
945 int GetLiftHeight(double time
, int t
)
948 time
= LIFT_TIME
-time
;
949 time
= time
/ LIFT_TIME
;
954 time
= (3 - 2*time
)*time
*time
;
956 time
= (3 - 2*time
)*time
*time
;
958 return (int)((TILE_H_LIFT_UP
+4) * time
);
960 return (int)((TILE_H_LIFT_UP
-4) * time
) + 4;
963 struct TileRender
: public RenderStage
968 double specialDuration
;
970 TileRender(int i
, Pos
const & _p
, int _special
=0) : t(i
), p(_p
), special(_special
), specialDuration(LASER_LINE_TIME
)
973 void Render(RenderObject
* r
, double time
, bool reflect
)
975 if (t
==0 && special
==0)
978 if (special
&& (t
==LIFT_UP
|| t
==LIFT_DOWN
) && time
<LIFT_TIME
)
980 int y
= GetLiftHeight(time
, t
);
983 RenderTile(reflect
, TILE_LIFT_BACK
, p
.getScreenX(), p
.getScreenY());
984 RenderTile(reflect
, TILE_LIFT_SHAFT
, p
.getScreenX(), p
.getScreenY()+y
, y
-8);
985 RenderTile(reflect
, TILE_LIFT_FRONT
, p
.getScreenX(), p
.getScreenY());
989 RenderTile(reflect
, TILE_LIFT_SHAFT
, p
.getScreenX(), p
.getScreenY()-y
, y
);
990 RenderTile(reflect
, LIFT_DOWN
, p
.getScreenX(), p
.getScreenY());
993 else if (special
&& (t
==EMPTY
|| t
==TRAP
) && !reflect
&& time
< specialDuration
)
996 if (time
< specialDuration
-LASER_FADE_TIME
)
997 RenderTile(reflect
, TILE_ICE_LASER_REFRACT
, p
.getScreenX(), p
.getScreenY());
999 RenderTile(reflect
, t
, p
.getScreenX(), p
.getScreenY());
1000 int base
= ((t
==EMPTY
) ? TILE_LASER_0
: TILE_LASER_REFRACT
);
1001 if (t
==EMPTY
&& time
>= specialDuration
-LASER_FADE_TIME
)
1002 base
= TILE_LASER_FADE_0
;
1005 for(int i
=0; foo
; foo
>>=1, i
++)
1007 RenderTile(reflect
, base
+i
, p
.getScreenX(), p
.getScreenY());
1009 else if (t
==FLOATING_BALL
)
1011 int y
= int(1.8 * sin(r
->seed
*PI
+ time
*4));
1014 if (time
> 2) return;
1015 if (reflect
) return;
1016 srand(int(r
->seed
* 0xfff));
1017 for (int i
=0; i
<20 - int(time
*10); i
++)
1019 int x
= int((((rand() & 0xfff) - 0x800) / 10) * time
);
1020 int y
= int((((rand() & 0xfff) - 0x800) / 10) * time
);
1021 RenderTile(true, 19 + ((i
+int(time
*5))&1)*10, p
.getScreenX() + x
, p
.getScreenY() - 14 + y
);
1025 RenderTile(true, 18, p
.getScreenX(), p
.getScreenY() - 14);
1028 RenderBoat(reflect
, int(special
)&255, p
.getScreenX(), p
.getScreenY(), y
);
1030 RenderTile(reflect
, t
, p
.getScreenX(), p
.getScreenY() + (reflect
? -y
: y
));
1032 else if (t
!= EMPTY
)
1033 RenderTile(reflect
, t
, p
.getScreenX(), p
.getScreenY());
1035 static void RenderBoat(bool reflect
, int d
, int x
, int y
, int yo
)
1038 RenderGirl(reflect
, d
, 0, x
, y
, -yo
);
1039 RenderTile(reflect
, FLOATING_BALL
, x
, y
+yo
);
1042 RenderGirl(reflect
, d
, 0, x
, y
, -yo
);
1043 RenderTile(true, 17, x
, y
+yo
-TILE_H_REFLECT_OFFSET
);
1048 struct TileRotateRender
: public TileRender
1053 TileRotateRender(int i
, Pos
const & p
, Dir _d
, int m
) : TileRender(i
, p
), d(_d
), mode(m
)
1055 void Render(RenderObject
* r
, double time
, bool reflect
)
1059 double f
= time
/ ROTATION_TIME
;
1061 if (mode
& 1) f
+= 0.5;
1070 if (mode
& 1) f
=1-f
; else f
=f
;
1074 TileRender::Render(r
, time
, reflect
);
1077 Pos dd
= (Pos(0,0)+d
);
1078 int x
= p
.getScreenX() + int(dd
.getScreenX()*(f
));
1079 int y
= p
.getScreenY() + int(dd
.getScreenY()*(f
));
1082 RenderBoat(reflect
, (mode
&1) ? (d
+MAX_DIR
/2)%MAX_DIR
: d
, x
, y
, 2);
1084 RenderTile(reflect
, t
, x
, y
);
1089 struct LaserRender
: public RenderStage
1095 LaserRender(Pos _p
, int dir
, int r
) : p(_p
), d(dir
), range(r
)
1098 void Render(RenderObject
* r
, double time
)
1103 struct ExplosionRender
: public RenderStage
1110 ExplosionRender(Pos _p
, int _pow
=0, int t
=0) : p(_p
), power(_pow
), type(t
)
1115 virtual int GetDepth(double time
)
1120 void Render(RenderObject
* r
, double time
, bool reflect
)
1122 if (type
==1 && time
> 2.5)
1123 type
= -1, new WinLoseScreen(false);
1125 // if (reflect) return;
1126 if (time
> 3) return;
1128 int q
= 50 - int(time
* 35);
1131 for (int i
=0; i
<q
; i
++)
1133 int x
= p
.getScreenX();
1134 int y
= p
.getScreenY() + (rand() & 31)-16;
1135 int xs
= ((rand() & 63) - 32);
1136 int ys
= (-10 - (rand() & 127)) * (1+power
);
1137 if (type
) ys
*=2, xs
/=2;
1138 x
+= int(xs
* (1+time
*(2+power
)));
1139 int yo
= int(time
*time
*128 + ys
*time
);
1140 //if (yo > 0) yo=-yo;//continue;
1146 if (!reflect
&& ys
<-60)
1148 const double T
= 0.06;
1149 double ct
= -ys
/ 128.0;
1152 x
= p
.getScreenX() + int(xs
* (1+ct
*(2+power
)));
1155 time
> ct
+3*T
? TILE_SPLASH_3
: time
> ct
+2*T
? TILE_SPLASH_2
: time
> ct
+T
? TILE_SPLASH_1
: TILE_WATER_PARTICLE
+1,
1163 time
- i
*0.003 < 0.2 ? TILE_WATER_PARTICLE
+1 : TILE_WATER_PARTICLE
,
1164 x
, y
+(reflect
?-1:1)*yo
);
1173 i
<q
-20 || time
<0.3 ? TILE_LASER_HEAD
: i
<q
-10 || time
<0.6 ? TILE_FIRE_PARTICLE_1
: TILE_FIRE_PARTICLE_2
,
1174 x
, y
+(reflect
?-1:1)*yo
);
1179 struct DisintegrateRender
: public RenderStage
1186 DisintegrateRender(Pos _p
, int _pow
=0, int _t
=0) : p(_p
), height(_pow
), type(_t
)
1191 void Render(RenderObject
* r
, double time
, bool reflect
)
1194 RenderTile(reflect
, height
? COLLAPSE_DOOR
: COLLAPSABLE
, p
.getScreenX(), p
.getScreenY());
1196 if (time
> 50.0/70.0) return;
1197 if (reflect
) return;
1199 int q
= 50 - int(time
* 70);
1201 for (int i
=0; i
<q
; i
++)
1203 int x
= (rand() % (TILE_W3
-8))-TILE_W3
/2+4;
1204 int y
= (rand() % (TILE_H2
-8))-TILE_H1
+4;
1205 if (x
<-TILE_WL
/2 && ABS(y
)<-TILE_WL
/2-x
) continue;
1206 if (x
>TILE_WL
/2 && ABS(y
)>x
-TILE_WL
/2) continue;
1208 if (height
) yo
-= rand() % TILE_HUP
;
1209 x
+= p
.getScreenX();
1210 y
+= p
.getScreenY() + 4;
1211 int xs
= 0;//((rand() & 63) - 32);
1212 int ys
= (- (rand() & 31));
1213 x
+= int(xs
* (1+time
*(2)));
1215 yo
+= int(time
*time
*128 + ys
*time
);
1216 if (type
) yo
= -yo
*2;
1217 //if (yo > 0) yo=-yo;//continue;
1218 int t
= type
? TILE_BLUE_FRAGMENT
: TILE_GREEN_FRAGMENT
;
1222 RenderTile(false, t
, x
, y
+(reflect
?-yo
:yo
));
1226 struct BuildRender
: public RenderStage
1234 BuildRender(Pos _p
, Dir _d
, int _h
, int _r
=0, int _type
=0) : p(_p
), dir(_d
), height(_h
), reverse(_r
), type(_type
)
1238 void Render(RenderObject
* r
, double time
, bool reflect
)
1240 if (time
>= BUILD_TIME
)
1241 RenderTile(reflect
, height
^ reverse
? (type
? COLLAPSE_DOOR2
: COLLAPSE_DOOR
) : (type
? COLLAPSABLE2
: COLLAPSABLE
), p
.getScreenX(), p
.getScreenY());
1245 RenderTile(reflect
, type
? COLLAPSABLE2
: COLLAPSABLE
, p
.getScreenX(), p
.getScreenY());
1247 double dist
= time
* 2 / BUILD_TIME
;
1250 Pos from
= p
+ ((dir
+MAX_DIR
/2)%MAX_DIR
);
1254 double offset
= (dist
*0.7) + 0.3;
1255 int x
= from
.getScreenX() + int((p
.getScreenX()-from
.getScreenX()) * offset
);
1256 int y
= from
.getScreenY() + int((p
.getScreenY()-from
.getScreenY()) * offset
- dist
*(1-dist
)*(TILE_HUP
*4));
1257 RenderTile(reflect
, TILE_GREEN_FRAGMENT
, x
, y
);
1263 if (reverse
) dist
= 1-dist
;
1265 if (dist
> 0 && !height
)
1268 for (int i
=0; i
<=int(dist
*15); i
++)
1270 int x
= p
.getScreenX(), y
= p
.getScreenY();
1271 double d
= (i
+ fmod(dist
*15, 1))/10.0;
1272 int x1
= int(sin(d
*5+time
)*MIN(d
,1)*TILE_W2
/2);
1273 int y1
= int(cos(d
*5+time
)*MIN(d
,1)*TILE_H1
*0.7);
1274 RenderTile(reflect
, TILE_GREEN_FRAGMENT
, x
+x1
, y
+y1
+4);
1275 RenderTile(reflect
, TILE_GREEN_FRAGMENT
, x
-x1
, y
-y1
+4);
1278 if (dist
> 0 && height
)
1280 int yo
= int((1-dist
)*(TILE_HUP
*1.3));
1281 if (yo
> TILE_HUP
*1.1)
1282 RenderTile(reflect
, TILE_WHITE_TILE
, p
.getScreenX(), p
.getScreenY());
1285 RenderTile(reflect
, type
? COLLAPSABLE2
: COLLAPSABLE
, p
.getScreenX(), p
.getScreenY());
1286 RenderTile(reflect
, type
? COLLAPSE_DOOR2
: COLLAPSE_DOOR
, p
.getScreenX(), p
.getScreenY()+(reflect
? -yo
: yo
), yo
+6);
1287 RenderTile(reflect
, type
? TILE_BLUE_FRONT
: TILE_GREEN_FRONT
, p
.getScreenX(), p
.getScreenY());
1291 if (yo
< TILE_HUP
/2)
1293 RenderTile(reflect
, type
? COLLAPSE_DOOR2
: COLLAPSE_DOOR
, p
.getScreenX(), p
.getScreenY()+(reflect
? -yo
: yo
), yo
);
1296 RenderTile(reflect
, type
? COLLAPSABLE2
: COLLAPSABLE
, p
.getScreenX(), p
.getScreenY());
1304 struct PlayerRender
: public RenderStage
1314 PlayerRender(Pos a
, int h
, bool d
) : p(a
), dead(d
), target(a
), speed(0), p_h(h
), target_h(h
), r(3), type(0)
1316 PlayerRender(int _r
, Pos a
, int h1
, Pos t
, int h2
, bool d
) : p(a
), dead(d
), target(t
), speed(JUMP_TIME
), p_h(h1
), target_h(h2
), r(_r
), type(0)
1318 int dist
= MAX(ABS(p
.x
-target
.x
), ABS(p
.y
-target
.y
));
1325 virtual int GetDepth(double time
)
1327 double f
= speed
? time
/ speed
: 1;
1329 if (f
==1) dead
= this->dead
;
1331 double x
= p
.x
+ (target
.x
- p
.x
) * f
;
1332 double y
= p
.y
+ (target
.y
- p
.y
) * f
;
1334 if (f
==1 || f
>0.5 && p_h
>target_h
)
1335 return target
.x
+target
.y
*2;
1336 return MAX(target
.x
+target
.y
*2 , p
.x
+p
.y
*2);
1339 void Render(RenderObject
* ro
, double time
, bool reflect
)
1342 double f
= speed
? time
/ speed
: 1;
1344 if (f
==1) dead
= this->dead
;
1346 int x
= p
.getScreenX();
1347 int y
= p
.getScreenY();
1348 int x2
= target
.getScreenX();
1349 int y2
= target
.getScreenY();
1351 int shadow_h
= (int)((p_h
+(target_h
-p_h
)*f
)*TILE_HUP2
);
1353 if (x
==x2
&& y
==y2
&& p_h
!=target_h
)
1355 h
= TILE_H_LIFT_UP
- GetLiftHeight(time
, p_h
? LIFT_DOWN
: LIFT_UP
);
1360 int dist
= MAX(ABS(p
.x
-target
.x
), ABS(p
.y
-target
.y
));
1361 int arc
= dist
*dist
;
1362 int h1
= p_h
* TILE_HUP2
;;
1363 int h2
= target_h
* TILE_HUP2
;
1364 if (dist
==2 && h1
!=0)
1368 shadow_h
= f
>=0.7 ? int(shadow_h
*(f
-0.7)/0.3) : 0;
1371 arc
= speed
> JUMP_TIME
? 7 : 2;
1373 h
= (int)(h1
+(h2
-h1
)*f
);
1374 // if (x==x2 && y==y2)
1378 //h += int(TILE_H_LIFT_UP/3 * (1-f));
1379 h
+= (int)(f
*(1-f
)*TILE_HUP2
*arc
);
1391 //frame = ((int)(f*4) % 4);
1392 //if (frame==2) frame=0; else if (frame==3) frame=2;
1395 else if (f
==1 || x
==x2
&& y
==y2
) // stationary
1401 frame
= type
? 2 : 1;
1407 RenderTile( false, TILE_SPHERE
,
1409 (int)(y
+(y2
-y
)*f
) - shadow_h
1422 dead ? TILE_SPHERE_OPEN : TILE_SPHERE_DONE,
1431 struct HexPuzzle
: public State
1435 #define MAX_TILECHANGE 64 // TODO: don't have a magic upper limit
1444 TileChange(Pos _p
, Tile _t
, int _i
) : p(_p
), t(_t
), item(_i
)
1446 void Restore(HexPuzzle
* w
)
1448 w
->SetTile(p
,t
,false,false);
1449 w
->SetItem(p
,item
,false,false);
1453 TileChange t
[MAX_TILECHANGE
];
1462 void Add(TileChange
const & tc
)
1464 for (int i
=0; i
<numT
; i
++)
1467 if (numT
>=MAX_TILECHANGE
)
1468 FATAL("numT>=MAX_TILECHANGE");
1472 void New(Dir pmove
, Pos
& pp
, int* items
, double t
, int sc
)
1474 numItems
[0] = items
[0];
1475 numItems
[1] = items
[1];
1477 playerMovement
= pmove
;
1482 void Restore(HexPuzzle
* w
)
1484 for (int i
=numT
-1; i
>=0; i
--)
1488 w
->player
= playerPos
;
1489 w
->player_items
[0] = numItems
[0];
1490 w
->player_items
[1] = numItems
[1];
1491 w
->player_score
= score
;
1493 //w->renderer.player.Add(new PlayerRender(playerPos, w->GetHeight(playerPos), false), w->time);
1498 char* special
[MAP_SIZE
][MAP_SIZE
];
1499 Tile map
[MAP_SIZE
][MAP_SIZE
];
1500 int32_t map_item
[MAP_SIZE
][MAP_SIZE
];
1501 int tileCount
[NumTileTypes
];
1502 int32_t levelPar
, levelDiff
;
1505 int player_items
[2];
1507 int numComplete
, numLevels
, numMastered
, numLevelsFound
;
1514 WorldRenderer renderer
;
1519 Undo undo
[MAX_UNDO
];
1521 LevelInfo
* currentLevelInfo
;
1523 char currentFile
[1000];
1530 LevelInfo
* GetLevelInfo(const char* f
)
1532 if (strstr(f
, "Levels\\") == f
)
1534 if (currentLevelInfo
!=0 && strcmp(currentLevelInfo
->file
, f
)==0)
1535 return currentLevelInfo
;
1540 if (t
<= numComplete
)
1543 static char tmp1
[100];
1544 static LevelInfo tmp
= {0, "", tmp1
};
1545 sprintf(tmp1
, "Complete %d more %s to unlock!", t
-numComplete
, t
-numComplete
==1?"level":"levels");
1549 for (int i
=0; i
<sizeof(levelNames
)/sizeof(levelNames
[0]); i
++)
1550 if (strcmp(f
, levelNames
[i
].file
)==0)
1551 return &levelNames
[i
];
1552 static LevelInfo tmp
= {0, "", "<<NO NAME>>"};
1556 #ifdef MAP_EDIT_HACKS
1557 int GetAutoTile(const char * level
, bool tiletype
)
1559 FILE* f
= file_open(filename
, "rb");
1563 if (f
&& fscanf(f
, "%d", &version
)==1 && (version
==3 || version
==4))
1565 if (strstr(level
,"mk"))
1568 fgetc(f
); // Remove '\n' character
1571 unsigned char bounds
[4];
1573 fread(&par
, sizeof(par
), 1, f
);
1577 fread(&diff
, sizeof(diff
), 1, f
);
1578 diff
= SWAP32(diff
);
1580 fread(bounds
, sizeof(bounds
), 1, f
);
1581 fread(&playerStart
, sizeof(playerStart
), 1, f
);
1582 playerStart
.x
= SWAP32(playerStart
.x
);
1583 playerStart
.y
= SWAP32(playerStart
.y
);
1587 for (int i
=bounds
[0]; i
<=bounds
[1]; i
++)
1588 for (int j
=bounds
[2]; j
<=bounds
[3]; j
++)
1590 unsigned char comp
= map
[i
][j
] | (map_item
[i
][j
]<<5);
1591 fread(&comp
, sizeof(comp
), 1, f
);
1592 int t
= comp
& 0x1f;
1593 int item
= (comp
>> 5) & 3;
1594 for (int i
=highval
+1; i
<sizeof(value_order
)/sizeof(value_order
[0]); i
++)
1595 if (t
!=0 && t
==value_order
[i
]
1596 || item
!=0 && item
==(value_order
[i
]>>8))
1602 tile
= value_order
[highval
];
1603 if (tile
==0x100) tile
= COLLAPSABLE3
;
1604 if (tile
==0x200) tile
= SWITCH
;
1605 if (tile
==LIFT_UP
) tile
= LIFT_DOWN
;
1609 if (value_order
[highval
] == LIFT_UP
)
1627 numComplete
= numLevels
= numMastered
= numLevelsFound
= 0;
1628 for (int i
=0; i
<MAP_SIZE
; i
++)
1629 for (int j
=0; j
<MAP_SIZE
; j
++)
1630 ActivateSpecial(Pos(i
,j
), 0);
1631 for (int i
=0; i
<MAP_SIZE
; i
++)
1632 for (int j
=0; j
<MAP_SIZE
; j
++)
1633 ActivateSpecial(Pos(i
,j
), 2);
1634 numComplete
= numLevels
= numMastered
= numLevelsFound
= 0;
1635 for (int i
=0; i
<MAP_SIZE
; i
++)
1636 for (int j
=0; j
<MAP_SIZE
; j
++)
1637 ActivateSpecial(Pos(i
,j
), 0);
1643 if (strcmp(mapname
, currentFile
)==0)
1645 // for (int i=0; i<32; i++)
1646 // HintMessage::FlagTile(i);
1647 if (numComplete
>= UNLOCK_SCORING
&& !progress
.general
.scoringOn
)
1649 HintMessage::FlagTile(26);
1650 progress
.general
.scoringOn
= 1;
1651 InitSpecials(); // Re-initialise with gold ones available
1653 HintMessage::FlagTile(25);
1657 for (int i
=0; i
<MAP_SIZE
; i
++)
1658 for (int j
=0; j
<MAP_SIZE
; j
++)
1660 int t
= GetTile(Pos(i
,j
));
1661 int item
= GetItem(Pos(i
,j
));
1663 HintMessage::FlagTile(t
);
1665 HintMessage::FlagTile(item
+20);
1667 HintMessage::FlagTile(EMPTY
);
1676 UpdateCursor(Pos(-1,-1));
1688 player_items
[0] = player_items
[1] = 0;
1690 if (strlen(currentSlot
) == 0)
1697 if (!isFadeRendering
&& time
!=0)
1699 renderer().Add(new FadeRender(-1), time
);
1705 renderer
.Reset(time
);
1708 for (int t
=0; t
<NumTileTypes
; t
++)
1711 for (int i
=0; i
<MAP_SIZE
; i
++)
1712 for (int j
=0; j
<MAP_SIZE
; j
++)
1715 int item
= GetItem(p
);
1717 renderer(p
,true).Add(new ItemRender(item
, GetTile(p
)==EMPTY
, p
), time
);
1722 for (int i
=0; i
<MAP_SIZE
; i
++)
1723 for (int j
=0; j
<MAP_SIZE
; j
++)
1733 renderer(p
).Add(new TileRender(t
, p
), time
);
1737 renderer
.player
.Add(new PlayerRender(player
, GetHeight(player
), dead
), time
);
1739 renderer
.player
.Add(new PlayerRender(Pos(-100,-100), 0, true), time
);
1742 int bounds
[4] = {player
.getScreenX(),player
.getScreenX(),player
.getScreenY(),player
.getScreenY()};
1743 for (int i
=0; i
<MAP_SIZE
; i
++)
1744 for (int j
=0; j
<MAP_SIZE
; j
++)
1747 if (map
[i
][j
] !=0 || map_item
[i
][j
]!=0)
1749 int x1
= p
.getScreenX();
1750 int y1
= p
.getScreenY();
1751 int x2
= x1
+ TILE_W3
;
1752 int y2
= y1
+ TILE_H2
;
1753 y1
-= TILE_H2
; // Make sure objects/player will be properly visible
1755 if (x1
<bounds
[0]) bounds
[0] = x1
;
1756 if (x2
>bounds
[1]) bounds
[1] = x2
;
1757 if (y1
<bounds
[2]) bounds
[2] = y1
;
1758 if (y2
>bounds
[3]) bounds
[3] = y2
;
1765 sx
= bounds
[0] - int(TILE_W2
*6.35);
1766 sy
= (bounds
[3] + bounds
[2] - SCREEN_H
) / 2 - TILE_H2
/2;
1770 sx
= (bounds
[1] + bounds
[0] - SCREEN_W
) / 2 - TILE_W3
/2;
1771 sy
= (bounds
[3] + bounds
[2] - SCREEN_H
) / 2 - TILE_H2
/2;
1783 // time = 1; // Guarantee we can't try and do things at time=0
1785 renderer().Add(new ScrollRender(sx
, sy
), time
);
1786 renderer().Add(new FadeRender(1), time
);
1791 char* ReadAll(FILE* f
)
1794 fseek(f
, 0, SEEK_END
);
1796 fseek(f
, 0, SEEK_SET
);
1797 char* c
= loadPtr
= new char [size
];
1798 endLoad
= loadPtr
+ size
;
1799 fread(c
, 1, size
, f
);
1803 static char *loadPtr
, *endLoad
;
1804 static unsigned int fread_replace(void* d
, unsigned int size
, unsigned int num
, FILE*)
1806 unsigned int remain
= (endLoad
- loadPtr
) / size
;
1807 if (remain
< num
) num
= remain
;
1808 memcpy(d
, loadPtr
, size
*num
);
1809 loadPtr
+= size
*num
;
1813 int GetPar(const char * level
, bool getdiff
=false)
1815 if (strcmp(level
, currentFile
)==0)
1816 return getdiff
? levelDiff
: levelPar
;
1818 #ifdef USE_LEVEL_PACKFILE
1819 PackFile1::Entry
* e
= levelFiles
.Find(level
);
1821 loadPtr
= (char*)e
->Data();
1822 endLoad
= loadPtr
+ e
->DataLen();
1826 FILE* f
= file_open(level
, "rb");
1829 typedef unsigned int _fn(void*, unsigned int, unsigned int, FILE*);
1830 _fn
* fn
= (loadPtr
? (_fn
*)fread_replace
: (_fn
*)fread
);
1832 int32_t par
= 99999, diff
= 0;
1836 return getdiff
? diff
: par
;
1838 fn(&version
, 2, 1, f
); // skip to relevant point
1840 if (fn(&par
, sizeof(par
), 1, f
) != 1)
1844 size_t ret
= fn(&diff
, sizeof(diff
), 1, f
);
1845 diff
= SWAP32(diff
);
1846 if (ret
!= 1 || diff
<0 || diff
>10)
1849 #ifdef USE_LEVEL_PACKFILE
1850 loadPtr
= endLoad
= 0;
1856 return getdiff
? diff
: par
;
1859 bool LoadSave(const char * filename
, bool save
)
1866 showScoring
= false;
1867 LevelSave
* l
= progress
.GetLevel(filename
, true);
1868 if (progress
.general
.scoringOn
&& l
&& l
->Completed() )
1872 #ifdef USE_LEVEL_PACKFILE
1875 PackFile1::Entry
* e
= levelFiles
.Find(filename
);
1876 if (!e
) return false;
1878 strcpy(currentFile
, filename
);
1879 currentLevelInfo
= GetLevelInfo(currentFile
);
1881 loadPtr
= (char*)e
->Data();
1882 endLoad
= loadPtr
+ e
->DataLen();
1883 _LoadSave(NULL
, save
);
1884 loadPtr
= endLoad
= 0;
1890 FILE* f
= file_open(filename
, save
? "wb" : "rb");
1893 strcpy(currentFile
, filename
);
1895 currentLevelInfo
= GetLevelInfo(currentFile
);
1899 char* data
= ReadAll(f
);
1902 loadPtr
= endLoad
= 0;
1917 /** \brief Writes/reads game status to/from a file
1919 * The game data file is written in little endian so it can be shared
1920 * across different machines.
1922 void _LoadSave(FILE* f
, bool save
)
1924 typedef unsigned int _fn(void*, unsigned int, unsigned int, FILE*);
1925 _fn
* fn
= save
? (_fn
*)fwrite
: (loadPtr
? (_fn
*)fread_replace
: (_fn
*)fread
);
1928 int version
= VERSION
; // 1--9
1930 fprintf(f
, "%d\n", version
);
1934 if (fn(&c
, 1, 1, f
) != 1)
1938 // Remove '\n' character
1944 for (int i
=0; i
<MAP_SIZE
; i
++)
1945 for (int j
=0; j
<MAP_SIZE
; j
++)
1947 delete [] special
[i
][j
];
1954 for (int i
=0; i
<MAP_SIZE
; i
++)
1955 for (int j
=0; j
<MAP_SIZE
; j
++) {
1956 map
[i
][j
] = SWAP32(map
[i
][j
]);
1957 fn(&map
[i
][j
], sizeof(map
[i
][j
]), 1, f
);
1958 map
[i
][j
] = SWAP32(map
[i
][j
]);
1961 player
.x
= SWAP32(player
.x
);
1962 player
.y
= SWAP32(player
.y
);
1963 fn(&player
, sizeof(player
), 1, f
);
1964 player
.x
= SWAP32(player
.x
);
1965 player
.y
= SWAP32(player
.y
);
1967 for (int i
=0; i
<MAP_SIZE
; ++i
)
1968 for (int j
=0; j
<MAP_SIZE
; ++j
)
1969 map_item
[i
][j
] = SWAP32(map_item
[i
][j
]);
1970 if (fn(map_item
, sizeof(map_item
), 1, f
) == 0)
1971 memset(map_item
, 0, sizeof(map_item
));
1972 for (int i
=0; i
<MAP_SIZE
; ++i
)
1973 for (int j
=0; j
<MAP_SIZE
; ++j
)
1974 map_item
[i
][j
] = SWAP32(map_item
[i
][j
]);
1976 else if (version
>=2 && version
<=4)
1978 unsigned char bounds
[4];
1981 bounds
[0]=bounds
[1]=player
.x
;
1982 bounds
[2]=bounds
[3]=player
.y
;
1983 for (int i
=0; i
<MAP_SIZE
; i
++)
1984 for (int j
=0; j
<MAP_SIZE
; j
++)
1985 if (map
[i
][j
] !=0 || map_item
[i
][j
]!=0 || special
[i
][j
]!=0)
1987 if (i
<bounds
[0]) bounds
[0] = i
;
1988 if (i
>bounds
[1]) bounds
[1] = i
;
1989 if (j
<bounds
[2]) bounds
[2] = j
;
1990 if (j
>bounds
[3]) bounds
[3] = j
;
1995 memset(map
, 0, sizeof(map
));
1996 memset(map_item
, 0, sizeof(map_item
));
2000 levelPar
= SWAP32(levelPar
);
2001 fn(&levelPar
, 1, sizeof(levelPar
), f
);
2002 levelPar
= SWAP32(levelPar
);
2008 levelDiff
= SWAP32(levelDiff
);
2009 fn(&levelDiff
, 1, sizeof(levelDiff
), f
);
2010 levelDiff
= SWAP32(levelDiff
);
2015 fn(bounds
, sizeof(bounds
), 1, f
);
2016 player
.x
= SWAP32(player
.x
);
2017 player
.y
= SWAP32(player
.y
);
2018 fn(&player
, sizeof(player
), 1, f
);
2019 player
.x
= SWAP32(player
.x
);
2020 player
.y
= SWAP32(player
.y
);
2022 int offsetx
=0, offsety
=0;
2024 if (!save
&& bounds
[1]-bounds
[0]<15) // Hacky - don't recenter map...
2026 // Re-position map to top left (but leave a bit of space)
2027 // (This ensures the laser/boat effects don't clip prematurely against the edges of the screen)
2028 offsetx
= SCREEN_W
/2/TILE_W2
+ 1 - (bounds
[0]+bounds
[1]/2);
2029 offsety
= SCREEN_H
/2/TILE_H2
+ SCREEN_W
/2/TILE_W2
- (bounds
[2]+bounds
[3]/2);
2030 offsetx
= MAX(0, offsetx
);
2031 offsety
= MAX(0, offsety
);
2032 // if (bounds[0] > 2)
2033 // offsetx = 2 - bounds[0];
2034 // if (bounds[2] > 2)
2035 // offsety = 2 - bounds[2];
2037 bounds
[0] += offsetx
;
2038 bounds
[1] += offsetx
;
2039 bounds
[2] += offsety
;
2040 bounds
[3] += offsety
;
2041 player
.x
+= offsetx
;
2042 player
.y
+= offsety
;
2044 for (int i
=bounds
[0]; i
<=bounds
[1]; i
++)
2045 for (int j
=bounds
[2]; j
<=bounds
[3]; j
++)
2047 unsigned char comp
= map
[i
][j
] | (map_item
[i
][j
]<<5);
2048 fn(&comp
, sizeof(comp
), 1, f
);
2049 map
[i
][j
] = comp
& 0x1f;
2050 map_item
[i
][j
] = (comp
>> 5) & 3;
2055 for (int i
=bounds
[0]; i
<=bounds
[1]; i
++)
2056 for (int j
=bounds
[2]; j
<=bounds
[3]; j
++)
2059 int16_t len
= strlen(special
[i
][j
]);
2060 unsigned char x
=i
, y
=j
;
2061 fn(&x
, sizeof(x
), 1, f
);
2062 fn(&y
, sizeof(y
), 1, f
);
2064 fn(&len
, sizeof(len
), 1, f
);
2066 fn(special
[i
][j
], 1, len
, f
);
2074 if (!fn(&x
, sizeof(x
), 1, f
))
2076 fn(&y
, sizeof(y
), 1, f
);
2077 x
+= offsetx
; y
+= offsety
;
2078 fn(&len
, sizeof(len
), 1, f
);
2081 char* tmp
= new char[len
+1];
2085 SetSpecial(Pos(x
,y
), tmp
, true, false);
2090 return; // Unsupported version!
2094 // Save when returning to map!
2097 progress
.general
.completionPercentage
= numComplete
*100/numLevels
;
2098 progress
.general
.masteredPercentage
= numMastered
*100/numLevels
;
2099 LoadSaveProgress(true);
2103 void SetTile(Pos
const & p
, Tile t
, bool updateRenderer
=true, bool undoBuffer
=true)
2105 if (p
.x
<0 || p
.x
>MAP_SIZE
)
2107 if (p
.y
<0 || p
.y
>MAP_SIZE
)
2109 if (map
[p
.x
][p
.y
] == t
)
2111 if (map
[p
.x
][p
.y
] == t
)
2114 tileCount
[map
[p
.x
][p
.y
]]--;
2118 undo
[numUndo
].Add(Undo::TileChange(p
,GetTile(p
),GetItem(p
)));
2123 renderer(p
).Add(new TileRender(t
, p
), time
);
2126 Tile
GetTile(Pos
const & p
)
2128 if (p
.x
<0 || p
.x
>=MAP_SIZE
)
2130 if (p
.y
<0 || p
.y
>=MAP_SIZE
)
2132 return map
[p
.x
][p
.y
];
2135 int GetHeight(Pos
const & p
)
2137 return tileSolid
[GetTile(p
)]==1;
2140 char* GetSpecial(Pos
const & p
)
2142 if (p
.x
<0 || p
.x
>=MAP_SIZE
)
2144 if (p
.y
<0 || p
.y
>=MAP_SIZE
)
2146 return special
[p
.x
][p
.y
];
2149 void SetSpecial(Pos
const & p
, char * d
, bool use_pointer
=false, bool auto_activate
=true)
2151 if (p
.x
<0 || p
.x
>=MAP_SIZE
|| p
.y
<0 || p
.y
>=MAP_SIZE
)
2158 delete [] special
[p
.x
][p
.y
];
2159 if (!use_pointer
&& d
)
2162 special
[p
.x
][p
.y
] = new char [strlen(d
) + 1];
2163 strcpy(special
[p
.x
][p
.y
], d
);
2166 special
[p
.x
][p
.y
] = d
;
2168 if (special
[p
.x
][p
.y
]==0)
2169 renderer(p
,true).Add(new ItemRender(GetItem(p
), GetTile(p
)==EMPTY
, p
), time
);
2170 else if (auto_activate
)
2171 ActivateSpecial(p
, 0);
2174 int GetLevelState(Pos
const & p
, int recurse
=0)
2176 char* x
= GetSpecial(p
);
2179 LevelSave
* l
= progress
.GetLevel(x
, false);
2183 if (strcmp(x
, STARTING_LEVEL
)==0)
2185 if (x
[0]=='_' && l
&& l
->unlocked
)
2188 if (l
&& l
->Completed())
2195 int par
= GetPar(x
);
2196 if (progress
.general
.scoringOn
&& l
->PassesPar( par
))
2203 for (Dir d
=0; d
<MAX_DIR
; d
++)
2205 int i
= GetLevelState(p
+d
, 1);
2206 // if (i>1 || i==1 && t>1)
2207 if (i
>=1 && t
>2 || t
>=1 && i
>2)
2218 void ActivateSpecial(Pos
const & p
, int type
)
2220 if (p
.x
<0 || p
.x
>=MAP_SIZE
|| p
.y
<0 || p
.y
>=MAP_SIZE
)
2223 char * x
= special
[p
.x
][p
.y
];
2225 if (x
==0 || x
[0]==0)
2228 if (type
==2 && x
[0]=='_') // Phase2 init - unlock
2230 int t
= GetLevelState(p
);
2231 int target
= atoi(x
+1), targetM
= 0;
2232 if (target
>1000) targetM
=target
=target
-100;
2233 if (t
> 1 && numComplete
>= target
&& numMastered
>= targetM
)
2235 LevelSave
* l
= progress
.GetLevel(x
, true);
2240 renderer(p
, true).Add(new LevelSelectRender(p
, 5, GetLevelState(p
)>>8), time
+0.01);
2241 renderer().Add(new ExplosionRender(p
, 0), time
+ 0.6);
2242 renderer().Add(new ExplosionRender(p
, 1), time
+ 1.1);
2243 renderer(p
, true).Add(new LevelSelectRender(p
, -1, GetLevelState(p
)>>8), time
+ 1.1);
2248 if (type
==0) // Init & count levels
2252 int t
= GetLevelState(p
);
2253 int unlock
= progress
.GetLevel(x
, true)->unlocked
;
2254 LevelSelectRender
* lsr
= new LevelSelectRender( p
, unlock
? -1 : (t
>>8) ? 5 : 1, t
>>8 );
2255 if ((t
>>8) && p
.x
> mapRightBound
) mapRightBound
= p
.x
;
2256 #ifdef MAP_EDIT_HACKS
2257 lsr
->magic
= -atoi(x
+1);
2258 SetTile(p
, LIFT_DOWN
, true, false);
2260 SetTile(p
, EMPTY
, true, false);
2262 renderer(p
,true).Add(lsr
, time
);
2266 //printf("Level: %s\n", x);
2268 int t
= GetLevelState(p
);
2270 if (t
&& !GetItem(p
))
2277 currentLevelInfo
= 0;
2281 LevelSave
* l
= progress
.GetLevel(x
, true);
2286 renderer(p
, true).Add(new LevelSelectRender(p
, -1, 0), time
+0.01);
2287 renderer().Add(new ExplosionRender(p
, 0), time
+ 0.6);
2288 renderer(p
, true).Add(new LevelSelectRender(p
, t
& 0xff, t
>>8), time
+ 0.6);
2292 if (p
.x
> mapRightBound
) mapRightBound
= p
.x
;
2299 LevelSelectRender
* lsr
= new LevelSelectRender( p
, t
& 0xff, t
>>8 );
2301 #ifdef MAP_EDIT_HACKS
2303 int t
= GetAutoTile(x
, true);
2304 int v
= GetAutoTile(x
, false);
2305 if (MAP_EDIT_HACKS_DISPLAY_UNLOCK
)
2308 lsr
->magic
= GetPar(x
, true);
2310 SetTile(p
, t
, true, false);
2312 SetTile(p
, EMPTY
, true, false);
2315 renderer(p
,true).Add(lsr
, time
);
2320 if (type
==1 && x
[0]!='_') // Clicked on
2322 int t
= GetLevelState(p
);
2330 void SetItem(Pos
const & p
, int t
, bool updateRenderer
=true, bool undoBuffer
=true)
2332 if (p
.x
<0 || p
.x
>MAP_SIZE
)
2334 if (p
.y
<0 || p
.y
>MAP_SIZE
)
2336 if (map_item
[p
.x
][p
.y
] == t
)
2340 undo
[numUndo
].Add(Undo::TileChange(p
,GetTile(p
),GetItem(p
)));
2342 map_item
[p
.x
][p
.y
] = t
;
2345 renderer(p
,true).Add(new ItemRender(t
, GetTile(p
)==EMPTY
, p
), time
);
2348 Tile
GetItem(Pos
const & p
)
2350 if (p
.x
<0 || p
.x
>=MAP_SIZE
)
2352 if (p
.y
<0 || p
.y
>=MAP_SIZE
)
2354 return map_item
[p
.x
][p
.y
];
2357 void LoadSaveProgress(bool save
)
2359 FILE* f
= file_open(currentSlot
, save
? "wb" : "rb");
2362 progress
.LoadSave(f
, save
);
2373 LoadSaveProgress(false);
2377 LoadSaveProgress(true);
2380 SDL_Surface
* Load(const char * bmp
, bool colourKey
=true)
2382 typedef unsigned int uint32
;
2385 SDL_Surface
* g
= 0;
2388 if (strstr(bmp
, ".bmp"))
2390 g
= SDL_LoadBMP(bmp
);
2394 strcpy(strstr(out
, ".bmp"), ".dat");
2396 // SDL_PixelFormat p;
2398 // SDL_Surface* tmp = SDL_ConvertSurface(g, &p, SDL_SWSURFACE);
2400 short w
=g
->w
, h
=g
->h
;
2401 char* buf
= (char*) g
->pixels
;
2404 while (IsEmpty(g
, w
-1, 0, 1, h
) && w
>1)
2406 while (IsEmpty(g
, 0, h
-1, w
, 1) && h
>1)
2410 FILE* f
= file_open(out
, "wb");
2411 fwrite(&w
, sizeof(w
), 1, f
);
2412 fwrite(&h
, sizeof(h
), 1, f
);
2414 uint32 mask
= IMAGE_DAT_OR_MASK
;
2415 for (int i
=0; i
<(int)w
*h
; )
2417 uint32 c
= (*(uint32
*)&buf
[(i
%w
)*3 + (i
/w
)*g
->pitch
] | mask
);
2419 while (i
< (int)w
*h
&& c
== (*(uint32
*)&buf
[(i
%w
)*3 + (i
/w
)*g
->pitch
] | mask
))
2428 fwrite(&c
, sizeof(c
), 1, f
);
2431 fwrite(&i0
, sizeof(i0
), 1, f
);
2441 FILE* f
= file_open(bmp
, "rb");
2442 if (!f
) FATAL("Unable to open file", bmp
);
2445 fread(&w
, sizeof(w
), 1, f
);
2446 fread(&h
, sizeof(h
), 1, f
);
2449 if (w
>1500 || h
>1500 || w
<=0 || h
<=0) FATAL("Invalid file", bmp
);
2451 tmp
= new uint32
[(int)w
*h
];
2455 for (int p
=0; p
<(int)w
*h
; p
++)
2461 fread(&c
, sizeof(c
), 1, f
);
2465 fread(&cnt
, sizeof(cnt
), 1, f
);
2469 tmp
[p
] = c
| 0xff000000;
2472 g
= SDL_CreateRGBSurfaceFrom(tmp
, w
, h
, 32, w
*4,
2481 if (!g
) FATAL("Unable to create SDL surface");
2483 SDL_SetColorKey(g
, SDL_SRCCOLORKEY
, SDL_MapRGB(g
->format
, WATER_COLOUR
));
2484 SDL_Surface
* out
= SDL_DisplayFormat(g
);
2487 if (!out
) FATAL("Unable to create SDL surface (SDL_DisplayFormat)");
2491 #ifdef USE_LEVEL_PACKFILE
2492 PackFile1 levelFiles
;
2496 SDL_WM_SetCaption(GAMENAME
, 0);
2500 #ifdef USE_LEVEL_PACKFILE
2501 FILE* f
= file_open("levels.dat", "rb");
2503 FATAL("Unable to open file", "levels.dat");
2513 currentLevelInfo
= 0;
2520 memset(map
, 0, sizeof(map
));
2521 memset(map_item
, 0, sizeof(map_item
));
2522 memset(special
, 0, sizeof(special
));
2526 // player = Pos(1,11);
2536 progress
.GetLevel(STARTING_LEVEL
, true)->unlocked
= 1;
2537 if (!progress
.GetLevel(STARTING_LEVEL
, true)->Completed())
2539 LoadSave(STARTING_LEVEL
, false);
2545 LoadSave(mapname
, false);
2550 if (!activeMenu
|| activeMenu
->renderBG
)
2552 SDL_Rect src
= {0,0,screen
->w
,screen
->h
};
2553 SDL_Rect dst
= {0,0,screen
->w
,screen
->h
};
2556 int boundW
= mapBG
->w
;
2558 boundW
= MIN(boundW
, (mapRightBound
+4) * TILE_W2
- TILE_W1
);
2560 src
.x
= scrollX
- initScrollX
;
2561 if (src
.x
+src
.w
> boundW
)
2563 int diff
= src
.x
+src
.w
- boundW
;
2574 //scrollY = initScrollY;
2577 mapScrollX
= scrollX
;
2579 SDL_BlitSurface(mapBG
, &src
, screen
, &dst
);
2582 SDL_BlitSurface(gradient
, &src
, screen
, &dst
);
2584 renderer
.Render(time
, true);
2586 if (!hintsDone
&& !isFadeRendering
)
2593 SDL_Rect src
= {0,SCREEN_H
-1,SCREEN_W
,1};
2594 SDL_Rect dst
= {0,SCREEN_H
-1,SCREEN_W
,1};
2595 for (int i
=0; i
<SCREEN_H
; i
++)
2598 dst
.y
= src
.y
= SCREEN_H
-1-i
;
2602 const bool farView
= false;
2605 src
.x
+= (int)( sin(i
*0.9 + time
*3.7) * sin(i
*0.3 + time
*0.7)*4 );
2606 src
.y
+= (int)( (sin(i
*0.3 - time
*2.2) * sin(i
*0.48 + time
*0.47) - 1) * 1.99 );
2610 src
.x
+= (int)( sin(i
*0.5 + time
*6.2) * sin(i
*0.3 + time
*1.05) * 5 );
2611 src
.y
+= (int)( (sin(i
*0.4 - time
*4.3) * sin(i
*0.08 + time
*1.9) - 1) * 2.5 );
2613 SDL_BlitSurface(screen
, &src
, screen
, &dst
);
2618 SDL_BlitSurface(mapBG2
, &src
, screen
, &dst
);
2620 renderer
.Render(time
, false);
2622 if (!isRenderMap
&& !isMap
&& !isFadeRendering
)
2624 int v
[3] = {player_items
[0], player_items
[1], player_score
};
2625 if (numUndo
> 1 && time
< undo
[numUndo
-2].endTime
)
2628 while (i
>1 && time
<undo
[i
-1].time
)
2630 v
[0] = undo
[i
].numItems
[0];
2631 v
[1] = undo
[i
].numItems
[1];
2632 v
[2] = undo
[i
].score
;
2634 if (numUndo
>1 && time
< undo
[0].time
)
2637 Print(0,0,"Anti-Ice: %d", v
[0]);
2638 Print(0,FONT_SPACING
,"Jumps: %d", v
[1]);
2639 Print(0,FONT_SPACING
*2,"Score: %d (%d)", v
[2], player_score
);
2640 Print(0,FONT_SPACING
*3,"Par: %d", levelPar
);
2641 Print(0,FONT_SPACING
*4,"Diff: %d", levelDiff
);
2644 Print(0, SCREEN_H
-FONT_SPACING
, " Par: %d Current: %d", levelPar
, v
[2]);
2647 Print(0,0," Anti-Ice: %d", v
[0]);
2649 Print(0,0," Jumps: %d", v
[1]);
2652 if (isRenderMap
&& isMap
&& !isFadeRendering
)
2655 Print(0,0,"Points: %d", numComplete
+numMastered
);
2656 Print(0,FONT_SPACING
,"Discovered: %d%% (%d/%d)", numLevelsFound
*100/numLevels
, numLevelsFound
, numLevels
);
2657 Print(0,FONT_SPACING
*2,"Complete: %d%% (%d)", numComplete
*100/numLevels
, numComplete
);
2658 Print(0,FONT_SPACING
*3,"Mastered: %d%% (%d)", numMastered
*100/numLevels
, numMastered
);
2660 if (numComplete
==numLevels
&& progress
.general
.endSequence
>0)
2661 Print(0, SCREEN_H
-FONT_SPACING
, " %d%% Mastered", numMastered
*100/numLevels
);
2663 Print(0, SCREEN_H
-FONT_SPACING
, " %d%% Complete", numComplete
*100/numLevels
);
2665 if (numMastered
>= numLevels
&& progress
.general
.endSequence
< 2)
2667 progress
.general
.endSequence
= 2;
2668 LoadSaveProgress(true);
2670 new Fader(-1, -7, 0.3);
2672 if (numComplete
>= numLevels
&& progress
.general
.endSequence
< 1)
2674 progress
.general
.endSequence
= 1;
2675 LoadSaveProgress(true);
2677 new Fader(-1, -5, 0.3);
2681 if ((currentLevelInfo
|| noMouse
) && isMap
&& isRenderMap
&& !activeMenu
&& isFadeRendering
<=0)
2688 int pad
= SCREEN_W
/80;
2689 // SDL_Rect src = {0, 0, uiGraphics->w, uiGraphics->h};
2690 SDL_Rect dst
= {pad
, SCREEN_H
-TILE_H2
-pad
, 0, 0};
2691 // dst.x = p.getScreenX() + TILE_W3/2 - scrollX;
2692 // dst.y = p.getScreenY() - src.h/2 - scrollY;
2693 dst
.x
= p
.getScreenX() - scrollX
;
2694 dst
.y
= p
.getScreenY() - scrollY
- FONT_SPACING
*3 - FONT_SPACING
/2;
2695 // if (dst.x > SCREEN_W*2/3) dst.x -= TILE_W3 + src.w;
2696 // if (dst.y+src.h > screen->h-pad) dst.y = screen->h-pad - src.h;
2698 RenderTile(false, 0, p
.getScreenX(), p
.getScreenY());
2699 // SDL_BlitSurface(uiGraphics, &src, screen, &dst);
2701 // dst.x += src.w/2;
2703 if (currentLevelInfo
)
2707 PrintC(true, dst
.x
, dst
.y
- FONT_SPACING
/4, currentLevelInfo
->name
);
2709 if (currentLevelInfo
->file
[0]!=0)
2711 if (player_score
> 0)
2713 if (progress
.general
.scoringOn
)
2715 PrintC(false, dst
.x
, dst
.y
+ FONT_SPACING
*4 - FONT_SPACING
/4, "Best:% 3d", player_score
);
2716 PrintC(false, dst
.x
, dst
.y
+ FONT_SPACING
*5 - FONT_SPACING
/4, "Par:% 3d", levelPar
);
2719 PrintC(false, dst
.x
, dst
.y
+ FONT_SPACING
*4 - FONT_SPACING
/4, "Completed", player_score
);
2722 PrintC(false, dst
.x
, dst
.y
+ FONT_SPACING
*4 - FONT_SPACING
/4, "Incomplete", player_score
);
2728 if (win
&& numUndo
> 0 && time
> undo
[numUndo
-1].endTime
+ 2)
2730 if (currentFile
[0] && winFinal
==0)
2732 LevelSave
* l
= progress
.GetLevel(currentFile
, true);
2734 new WinLoseScreen(true, player_score
, showScoring
? levelPar
: 0, l
&& showScoring
&& l
->Completed() ? l
->GetScore() : 0);
2736 if (l
->IsNewCompletionBetter(player_score
))
2738 l
->SetScore(player_score
);
2740 l
->SetSolution(numUndo
);
2742 for (int i
=0; i
<numUndo
; i
++)
2743 l
->SetSolutionStep(i
, undo
[i
].playerMovement
);
2754 // Move up "level complete" writing so it doesn't feel like everything's ground to a halt...
2755 if (win
&& numUndo
> 0 && time
> undo
[numUndo
-1].endTime
&& !winFinal
)
2757 double t
= (time
- undo
[numUndo
-1].endTime
) / 2;
2761 int y
= SCREEN_H
/3 - FONT_SPACING
+ 1;
2762 y
= SCREEN_H
+ int((y
-SCREEN_H
)*t
);
2763 PrintC(true, SCREEN_W
/2, y
, "Level Complete!");
2768 activeMenu
->Render();
2775 RenderTile(false, editTile
, mousex
+scrollX
, mousey
+scrollY
);
2779 Print(mousex
, mousey
-2, "\x7f");
2786 return tileCount
[t
];
2788 int Swap(Tile t
, Tile t2
)
2790 const int num
= Count(t
) + Count(t2
);
2791 if (t
==t2
|| num
==0)
2792 return Count(t
); // Nothing to do...
2795 for (int x
=0; x
<MAP_SIZE
; x
++)
2796 for (int y
=0; y
<MAP_SIZE
; y
++)
2798 if (GetTile(Pos(x
,y
))==t
)
2801 SetTile(Pos(x
,y
), t2
);
2803 else if (GetTile(Pos(x
,y
))==t2
)
2806 SetTile(Pos(x
,y
), t
);
2813 int Replace(Tile t
, Tile t2
)
2815 const int num
= Count(t
);
2816 if (t
==t2
|| num
==0)
2817 return num
; // Nothing to do...
2820 for (int x
=0; x
<MAP_SIZE
; x
++)
2821 for (int y
=0; y
<MAP_SIZE
; y
++)
2828 SetTile(p
, t2
, false);
2830 if (t
==COLLAPSE_DOOR
&& t2
==COLLAPSABLE
)
2831 renderer(p
).Add(new BuildRender(p
, -1, 1, 1), time
+ (rand() & 255)*0.001);
2832 else if (t
==COLLAPSE_DOOR2
&& t2
==COLLAPSABLE2
)
2833 renderer(p
).Add(new BuildRender(p
, -1, 1, 1, 1), time
+ (rand() & 255)*0.001);
2854 void UpdateCursor(Pos
const & s
)
2857 if (s
.x
!=_s
.x
|| s
.y
!=_s
.y
)
2861 char* sp
= GetSpecial(s
);
2868 currentLevelInfo
= 0;
2869 levelPar
= player_score
= -1;
2870 if (GetLevelState(s
)>=2)
2872 LevelSave
* l
= progress
.GetLevel(sp
, true);
2875 currentLevelInfo
= GetLevelInfo(sp
);
2876 levelPar
= GetPar(sp
);
2877 player_score
= l
->GetScore();
2883 sprintf(tmp
, "Special(%d,%d): %s (%d)", s
.x
, s
.y
, sp
? sp
: "<None>", GetPar(sp
));
2884 SDL_WM_SetCaption(tmp
, NULL
);
2887 else if (currentFile
[0])
2890 SDL_WM_SetCaption(currentFile
, NULL
);
2893 currentLevelInfo
= 0;
2898 virtual void Mouse(int x
, int y
, int dx
, int dy
, int button_pressed
, int button_released
, int button_held
)
2902 activeMenu
->Mouse(x
,y
,dx
,dy
,button_pressed
,button_released
,button_held
);
2906 if (isFadeRendering
)
2911 if (button_pressed
==2 || button_pressed
==4 && isMap
)
2913 KeyPressed(SDLK_ESCAPE
, 0);
2914 keyState
[SDLK_ESCAPE
] = 0;
2922 Pos s
= Pos::GetFromWorld(x
,y
);
2923 if (tileSolid
[GetTile(Pos::GetFromWorld(x
,y
+TILE_HUP
))] == 1)
2924 s
= Pos::GetFromWorld(x
,y
+TILE_HUP
);
2931 if (button_held
& ~button_pressed
& 4)
2940 if (isMap
&& (button_pressed
& 1))
2942 ActivateSpecial(s
, 1);
2945 if (!isMap
&& win
&& winFinal
)
2947 if (button_pressed
& 1)
2955 if((button_pressed
& 1) || (button_held
& 1) && (numUndo
==0 || time
>=undo
[numUndo
-1].endTime
))
2957 if(s
.x
==player
.x
&& s
.y
==player
.y
)
2959 // Don't activate jump powerup without a new click
2960 if (button_pressed
& 1)
2963 else if(s
.x
==player
.x
&& s
.y
<player
.y
)
2965 else if(s
.x
==player
.x
&& s
.y
>player
.y
)
2967 else if(s
.y
==player
.y
&& s
.x
<player
.x
)
2969 else if(s
.y
==player
.y
&& s
.x
>player
.x
)
2971 else if(s
.y
+s
.x
==player
.y
+player
.x
&& s
.x
>player
.x
)
2973 else if(s
.y
+s
.x
==player
.y
+player
.x
&& s
.x
<player
.x
)
2976 if ((button_pressed
& 4) || (button_held
& 4) && (undoTime
< 0))
2983 if (!button_pressed
&& !button_held
)
2986 if (button_pressed
==1)
2988 editTile
= GetItem(s
)==1 ? -3 : GetItem(s
)==2 ? -2 : -1;
2990 if (button_held
==1 || button_pressed
==1)
2994 SetTile(s
, editTile
, true, false);
2996 SetItem(s
, editTile
==-2 ? 0 : editTile
==-1 ? 1 : 2, true, false);
2999 if (button_pressed
==2)
3001 editTile
= GetTile(s
);
3004 if (button_pressed
==8)
3006 editTile
=editTile
-1;
3007 if (editTile
<=0) editTile
=NumTileTypes
-1;
3010 if (button_pressed
==16)
3012 editTile
=editTile
+1;
3013 if (editTile
<=0) editTile
=1;
3014 if (editTile
==NumTileTypes
) editTile
=0;
3017 if (button_pressed
==64)
3022 renderer
.player
.Reset(-1);
3023 renderer
.player
.Add(new PlayerRender(player
, GetHeight(player
), dead
), 0);
3026 if (button_pressed
==256)
3028 char* fn
= LoadSaveDialog(false, true, "Select level");
3031 char * l
= strstr(fn
, "Levels");
3034 FILE * f
= file_open(l
,"rb");
3039 else if (l
[6]!=0 && l
[7]=='_')
3042 UpdateCursor(Pos(-1,-1));
3045 if (button_pressed
==512)
3047 SetSpecial(s
, NULL
);
3048 UpdateCursor(Pos(-1,-1));
3050 if (button_pressed
==1024)
3052 static char x
[1000] = "";
3053 if (!(s
.x
<0 || s
.x
>=MAP_SIZE
|| s
.y
<0 || s
.y
>=MAP_SIZE
))
3058 strcpy(x
, GetSpecial(s
));
3061 SetSpecial(s
, tmp
[0] ? tmp
: 0);
3063 SetTile(s
, EMPTY
, true, false);
3067 if (button_pressed
==32)
3069 editTile
= editTile
<0 ? 1 : -1;
3074 void CheckFinished()
3077 if (Count(COLLAPSABLE
)==0)
3079 if (Replace(COLLAPSE_DOOR
, COLLAPSABLE
) == 0)
3083 Replace(SWITCH
, NORMAL
);
3088 if (Count(COLLAPSABLE2
)==0)
3089 if (Replace(COLLAPSE_DOOR2
, COLLAPSABLE2
))
3095 bool Collide(Pos p
, bool high
)
3097 Tile t
= GetTile(p
);
3102 return tileSolid
[t
]==1;
3109 if (numUndo
==0) return;
3111 UndoDone(); // Complete previous undo...
3115 if (time
> undo
[numUndo
].endTime
)
3116 time
= undo
[numUndo
].endTime
;
3117 undoTime
= undo
[numUndo
].time
;
3119 undo
[numUndo
].Restore(this);
3125 renderer
.Reset(undoTime
);
3129 void ScoreDestroy(Pos p
)
3131 Tile t
= GetTile(p
);
3132 if (t
==COLLAPSABLE
|| t
==COLLAPSE_DOOR
)
3134 else if (t
!= EMPTY
)
3140 bool LaserTile(Pos p
, int mask
, double fireTime
)
3142 if (&renderer(p
) == &renderer(Pos(-1,-1)))
3144 //if (!renderer.Visible(p))
3148 if (time
<= renderer(p
).GetLastTime())
3149 if (fireTime
< renderer(p
).GetLastTime())
3151 renderer(p
).Add(tr
= new TileRender(GetTile(p
), p
, mask
), fireTime
);
3152 ((TileRender
*)renderer(p
).GetStage(time
+10/*HACKY!*/))->special
|= mask
;
3156 tr
= new TileRender(GetTile(p
), p
, mask
| ((TileRender
*)renderer(p
).GetStage(time
+10/*HACKY!*/))->special
);
3157 renderer(p
).Add(tr
, fireTime
);
3160 renderer(p
).Add(tr
= new TileRender(GetTile(p
), p
, mask
), fireTime
);
3164 tr
->specialDuration
= time
+ LASER_LINE_TIME
- fireTime
+ LASER_FADE_TIME
;
3168 void FireGun(Pos newpos
, Dir d
, bool recurse
, double fireTime
)
3170 static Pos hits
[100];
3171 static Dir hitDir
[100];
3172 static int numHits
=0;
3176 double starttime
= fireTime
;
3177 for (Dir fd
=((d
<0)?0:d
); fd
<((d
<0)?MAX_DIR
:d
+1); fd
++)
3179 fireTime
= starttime
;
3180 // starttime += 0.03;
3182 Pos p
= newpos
+ fd
;
3184 for (range
; range
<MAP_SIZE
; range
++, p
=p
+fd
)
3186 Tile t
= GetTile(p
);
3187 if (tileSolid
[t
]!=-1)
3190 renderer(p
).Add(new TileRender(tileSolid
[t
]==1 ? TILE_WHITE_WALL
: TILE_WHITE_TILE
, p
), fireTime
+0.1);
3193 for (i
=0; i
<numHits
; i
++)
3197 t
==TRAP
&& (hitDir
[i
]&(1<<fd
))==0
3202 if (i
>= sizeof(hits
)/sizeof(hits
[0]))
3204 hitDir
[i
] = 1 << fd
;
3210 hitDir
[i
] |= 1 << fd
;
3215 1<<((fd
+2) % MAX_DIR
)
3216 | 1<<((fd
+MAX_DIR
-2) % MAX_DIR
);
3218 if (LaserTile(p
, dirmask
, fireTime
))
3219 fireTime
+= (time
+LASER_LINE_TIME
- fireTime
) / 40;
3220 // fireTime += LASER_SEGMENT_TIME;
3222 FireGun(p
, (fd
+1) % MAX_DIR
, true, fireTime
);
3223 FireGun(p
, (fd
+MAX_DIR
-1) % MAX_DIR
, true, fireTime
);
3230 LaserTile(p
, 1<<(fd
%3), fireTime
);
3232 fireTime
+= (time
+LASER_LINE_TIME
- fireTime
) / 40;
3233 // fireTime += LASER_SEGMENT_TIME;
3237 // renderer().Add(new LaserRender(newpos, fd, range), time);
3242 double _time
= time
;
3243 time
+= LASER_LINE_TIME
;
3244 for (int i
=0; i
<numHits
; i
++)
3247 Tile t
= GetTile(p
);
3254 renderer(p
).Add(new ExplosionRender(p
, t
==GUN
), time
);
3255 //renderer(p).Add(new TileRender(EMPTY, p), time+2);
3256 SetTile(p
, EMPTY
, false);
3259 renderer(p
,true).Add(new ItemRender(GetItem(p
), 1, p
), time
);
3263 for (Dir j
=0; j
<MAX_DIR
; j
++)
3265 if (GetTile(p
+j
)!=EMPTY
)
3267 renderer(p
+j
).Add(new TileRender(tileSolid
[GetTile(p
+j
)]==1 ? TILE_WHITE_WALL
: TILE_WHITE_TILE
, p
+j
), time
+0.05);
3268 renderer(p
+j
).Add(new ExplosionRender(p
+j
), time
+0.2);
3271 renderer(p
+j
,true).Add(new ItemRender(GetItem(p
+j
), 1, p
), time
);
3273 //renderer(p+j).Add(new TileRender(EMPTY, p+j), time+2.2);
3275 ScoreDestroy(p
+ j
);
3276 SetTile(p
+ j
, EMPTY
, false);
3281 time
+= MAX(LASER_FADE_TIME
, 0.15);
3286 int GetLastPlayerRot()
3288 RenderStage
* rs
= renderer
.player
.GetStage(-1);
3290 return ((PlayerRender
*)rs
)->r
;
3294 if (dead
|| win
|| isMap
)
3300 // Jump forwards in time to last move finishing
3301 if (numUndo
> 0 && time
< undo
[numUndo
-1].endTime
)
3302 time
= undo
[numUndo
-1].endTime
;
3304 double realTime
= time
;
3305 double endAnimTime
= time
;
3306 bool high
= (tileSolid
[GetTile(player
)] == 1);
3307 Pos playerStartPos
= player
;
3308 Pos oldpos
= player
;
3309 int oldPlayerHeight
= GetHeight(oldpos
);
3310 Pos newpos
= player
+ d
;
3312 int playerRot
= GetLastPlayerRot();
3313 if (d
!=-1 && d
!=playerRot
)
3315 while (d
!=playerRot
)
3317 if ((d
+6-playerRot
) % MAX_DIR
< MAX_DIR
/2)
3318 playerRot
= (playerRot
+1) % MAX_DIR
;
3320 playerRot
= (playerRot
+MAX_DIR
-1) % MAX_DIR
;
3324 if (GetTile(oldpos
) == FLOATING_BALL
)
3326 TileRender
* t
= new TileRender(FLOATING_BALL
, oldpos
);
3327 t
->special
= playerRot
+ 256;
3328 renderer(oldpos
).Add(t
, time
);
3330 renderer
.player
.Add(new PlayerRender(playerRot
, Pos(-20,-20), oldPlayerHeight
, Pos(-20,-20), oldPlayerHeight
, dead
), time
);
3334 PlayerRender
*p
= new PlayerRender(playerRot
, player
, oldPlayerHeight
, player
, oldPlayerHeight
, dead
);
3336 renderer
.player
.Add(p
, time
);
3343 if (d
<0 && player_items
[1]==0)
3348 if (tileSolid
[GetTile(newpos
)] == -1)
3353 if (Collide(newpos
, high
))
3360 // Don't change any real state before this point!
3361 if (numUndo
>= MAX_UNDO
)
3364 for(int i
=0; i
<MAX_UNDO
-1; i
++)
3365 undo
[i
] = undo
[i
+1];
3367 undo
[numUndo
].New(d
, player
, player_items
, time
, player_score
);
3374 int old_score
=player_score
;
3376 double time0
= time
;
3377 time
+= 0.15; //Time for leave-tile fx
3379 switch (GetTile(oldpos
))
3382 SetTile(oldpos
, EMPTY
);
3383 renderer(oldpos
).Add(new DisintegrateRender(oldpos
), time
);
3388 // Don't need to CheckFinished - can't be collapse doors around
3389 // unless there're still collapsable tiles around.
3390 SetTile(oldpos
, EMPTY
);
3391 renderer(oldpos
).Add(new DisintegrateRender(oldpos
, 1), time
);
3395 SetTile(oldpos
, COLLAPSABLE
, false);
3396 renderer(oldpos
).Add(new DisintegrateRender(oldpos
, 0, 1), time
);
3401 case COLLAPSE_DOOR2
:
3402 SetTile(oldpos
, COLLAPSE_DOOR
, false);
3403 renderer(oldpos
).Add(new DisintegrateRender(oldpos
, 1, 1), time
);
3408 SetTile(oldpos
, COLLAPSABLE2
);
3412 time
= time0
; //End of leave-tile fx
3414 int retry_pos_count
=0;
3418 if (GetItem(newpos
)==1)
3420 renderer(newpos
, true).Add(new ItemCollectRender(GetItem(newpos
), newpos
), time
+ JUMP_TIME
/2);
3421 SetItem(newpos
, 0, false);
3424 if (GetItem(newpos
)==2)
3426 renderer(newpos
, true).Add(new ItemCollectRender(GetItem(newpos
), newpos
), time
+ JUMP_TIME
/2);
3427 SetItem(newpos
, 0, false);
3431 if (GetTile(player
) == FLOATING_BALL
)
3433 TileRender
* t
= new TileRender(FLOATING_BALL
, player
);
3435 renderer(oldpos
).Add(t
, time
);
3438 PlayerRender
*p
= new PlayerRender(playerRot
, player
, oldPlayerHeight
, newpos
, GetHeight(newpos
), dead
);
3440 // alternate leg (hacky!)
3448 if (retry_pos_count
!=0 && GetTile(player
)==TRAP
)
3454 p
->speed
= JUMP_TIME
* 1.5;
3455 renderer
.player
.Add(p
, time
);
3456 endAnimTime
= MAX(endAnimTime
, time
+ p
->speed
+0.001);
3461 switch (GetTile(newpos
))
3464 renderer(newpos
).Add(new TileRender(TILE_GREEN_CRACKED
, newpos
), time
);
3467 renderer(newpos
).Add(new TileRender(TILE_GREEN_CRACKED_WALL
, newpos
), time
);
3470 renderer(newpos
).Add(new TileRender(TILE_BLUE_CRACKED
, newpos
), time
);
3472 case COLLAPSE_DOOR2
:
3473 renderer(newpos
).Add(new TileRender(TILE_BLUE_CRACKED_WALL
, newpos
), time
);
3482 double pretime
= time
;
3485 for (Dir fd
=0; fd
<MAX_DIR
; fd
++)
3487 Tile t2
= GetTile(newpos
+ fd
);
3491 SetTile(newpos
+fd
, COLLAPSABLE
, false);
3492 renderer(newpos
+fd
).Add(new BuildRender(newpos
+fd
, fd
, 0), time
);
3494 else if (t2
==COLLAPSABLE
)
3497 SetTile(newpos
+fd
, COLLAPSE_DOOR
, false);
3498 renderer(newpos
+fd
).Add(new BuildRender(newpos
+fd
, fd
, 1), time
);
3501 if (done
) time
+= BUILD_TIME
;
3502 else time
= pretime
;
3504 endAnimTime
= MAX(endAnimTime
, time
+ 0.1);
3509 Swap(COLLAPSE_DOOR
, COLLAPSABLE
);
3515 renderer
.player
.Add(new PlayerRender(playerRot
, Pos(-30,-30), 0, Pos(-30,-30), 0, dead
), time
);
3516 while (tileSolid
[GetTile(newpos
+d
)]==-1)
3520 if (!renderer
.Visible(newpos
+d
))
3522 TileRender
* r
= new TileRender(FLOATING_BALL
, newpos
);
3524 renderer(newpos
).Add(r
, time
);
3526 PlayerRender
* pr
= new PlayerRender(playerRot
, newpos
, 0, newpos
, 0, dead
);
3527 pr
->speed
= JUMP_TIME
*1;
3528 renderer
.player
.Add(pr
, time
);
3536 newpos
= oldpos
+ d
;
3538 SetTile(oldpos
, EMPTY
, false);
3539 SetTile(newpos
, FLOATING_BALL
, false);
3541 renderer(oldpos
).Add(new TileRotateRender(FLOATING_BALL
, oldpos
, d
, 2), time
);
3542 renderer(oldpos
).Add(new TileRender(EMPTY
, oldpos
), time
+ ROTATION_TIME
/2);
3543 renderer(newpos
).Add(new TileRotateRender(FLOATING_BALL
, newpos
, (d
+3)%MAX_DIR
, 3), time
+ ROTATION_TIME
/2);
3545 // PlayerRender *p = new PlayerRender(playerRot, oldpos, 0, newpos, 0, dead);
3546 // p->speed = ROTATION_TIME*0.9;
3547 // renderer.player.Add(p, time);
3549 endAnimTime
= MAX(endAnimTime
, time
+ ROTATION_TIME
+ ROTATION_TIME
/2);
3550 time
+= ROTATION_TIME
;
3553 // renderer.player.Add(new PlayerRender(playerRot, player, 0, player, 0, 0), time);
3559 TileRender
* r
= new TileRender(FLOATING_BALL
, newpos
);
3560 r
->special
= playerRot
+ 256;
3561 renderer(newpos
).Add(r
, time
);
3569 SetTile(newpos
, GetTile(newpos
)==LIFT_UP
? LIFT_DOWN
: LIFT_UP
, false);
3570 renderer(newpos
).Add(new TileRender(GetTile(newpos
), newpos
, 1), time
);
3572 PlayerRender
*p
= new PlayerRender(playerRot
, newpos
, 1-GetHeight(newpos
), newpos
, GetHeight(newpos
), dead
);
3573 renderer
.player
.Add(p
, time
);
3574 endAnimTime
= MAX(endAnimTime
, time
+ JUMP_TIME
);
3582 if (Collide(newpos
+ d
, high
))
3584 if (Collide((newpos
+ d
) + d
, high
) == 1)
3585 newpos
= (newpos
+ d
);
3587 newpos
= (newpos
+ d
) + d
;
3588 if (tileSolid
[GetTile(newpos
)] == -1)
3595 for (Dir d
=0; d
<MAX_DIR
; d
++)
3597 Tile tmp
= GetTile(newpos
+ d
);
3598 renderer(newpos
+ d
).Add(new TileRotateRender(tmp
, newpos
+ d
, (d
+2)%MAX_DIR
, false), time
);
3600 Tile tmp
= GetTile(newpos
+ Dir(MAX_DIR
-1));
3601 for (Dir d
=0; d
<MAX_DIR
; d
++)
3603 Tile t2
= GetTile(newpos
+ d
);
3604 SetTile(newpos
+ d
, tmp
, false);
3605 renderer(newpos
+ d
).Add(new TileRotateRender(tmp
, newpos
+ d
, (d
+4)%MAX_DIR
, true), time
+ ROTATION_TIME
/2);
3606 if (GetItem(newpos
+ d
))
3607 renderer(newpos
+ d
,true).Add(new ItemRender(GetItem(newpos
+ d
), GetTile(newpos
+ d
)==EMPTY
, newpos
+d
), time
+ ROTATION_TIME
/2);
3611 endAnimTime
= MAX(endAnimTime
, time
+ROTATION_TIME
);
3612 // renderer(newpos).Add(new TileRotateRender(SPINNER, Dir(0), 0), time);
3620 if (player_items
[0]==0)
3622 if (tileSolid
[GetTile(newpos
+ d
)] == 1)
3624 newpos
= newpos
+ d
;
3625 if (tileSolid
[GetTile(newpos
)] == -1)
3632 SetTile(newpos
, COLLAPSABLE3
);
3640 FireGun(newpos
, d
, false, time
);
3642 endAnimTime
= MAX(endAnimTime
, time
);
3644 if (GetTile(newpos
)==EMPTY
)
3646 PlayerRender
* pr
= new PlayerRender(playerRot
, player
, 0, player
, 0, dead
);
3647 pr
->speed
= JUMP_TIME
*1;
3648 renderer
.player
.Add(pr
, time
);
3658 for (Dir fd=((d<0)?0:d); fd<((d<0)?MAX_DIR:d+1); fd++)
3660 Pos p = newpos + fd;
3662 for (range; range<MAP_SIZE; range++, p=p+fd)
3664 Tile t = GetTile(p);
3665 if (tileSolid[t]!=-1)
3667 hits[numHits++] = p;
3672 renderer().Add(new LaserRender(newpos, fd, range), time);
3675 double _time = time;
3677 for (int i=0; i<numHits; i++)
3680 Tile t = GetTile(p);
3682 renderer().Add(new ExplosionRender(p), time);
3688 for (Dir j=0; j<MAX_DIR; j++)
3690 ScoreDestroy(p + j);
3691 SetTile(p + j, EMPTY);
3693 if (GetTile(newpos)==EMPTY)
3697 endAnimTime = MAX(endAnimTime, time);
3707 endAnimTime
= MAX(endAnimTime
, time
);
3713 PlayerRender
* pr
= new PlayerRender(player
, 0, dead
);
3714 pr
->speed
= 0; // Don't sit around before disappearing!
3715 renderer
.player
.Add(pr
, time
);
3717 // If the tile we're drowning on isn't visible, give the ownership of the splash effect to the player, rather than a tile.
3718 if (renderer
.Visible(player
))
3719 renderer(player
).Add(new ExplosionRender(player
, 0, 1), time
);
3721 renderer
.player
.Add(new ExplosionRender(player
, 0, 1), time
);
3723 endAnimTime
= MAX(endAnimTime
, time
+2);
3730 undo
[numUndo
].endTime
= endAnimTime
;
3735 void Update(double timedelta
)
3742 activeMenu
->Update(timedelta
);
3747 for (int i
=0; i
<SDLK_LAST
; i
++)
3754 if (isMap
&& isRenderMap
)
3757 static double scrollHi
= 0;
3762 int xx
= noMouse
? keyboardp
.getScreenX()-scrollX
: mousex
;
3763 if (xx
> SCREEN_W
) xx
= SCREEN_W
;
3766 x
= (double)xx
/ (w
) - 1;
3767 if (xx
> SCREEN_W
- w
)
3768 x
= 1 - (double)(SCREEN_W
-xx
) / (w
);
3770 if (x
<-min
|| x
>min
)
3772 scrollHi
+= timedelta
* x
;
3773 scrollX
+= (int)scrollHi
;
3774 scrollHi
-= (int)scrollHi
;
3779 if (undoTime
>=0 && undoTime
< time
)
3781 double acc
= (time
- undoTime
) / 2;
3782 if (acc
< 3) acc
= 3;
3783 time
-= timedelta
* acc
;
3784 if (undoTime
>= time
)
3791 time
+= timedelta
* 20;
3794 void FileDrop(const char* filename
)
3796 LoadSave(filename
, false);
3801 if (keyState
[SDLK_LALT
] || keyState
[SDLK_LCTRL
])
3805 if (!isMap
&& !editMode
&& undoTime
< 0)
3807 if (keyState
['z'] || keyState
[SDLK_BACKSPACE
] || keyState
['u'])
3813 if (isMap
&& !editMode
)
3816 if ((keyState
['q'] | keyState
[SDLK_KP7
]) & 2) keyboardp
.x
--;
3817 else if ((keyState
['d'] | keyState
[SDLK_KP3
]) & 2) keyboardp
.x
++;
3818 else if ((keyState
['e'] | keyState
[SDLK_KP9
]) & 2) keyboardp
.x
++, keyboardp
.y
--;
3819 else if ((keyState
['a'] | keyState
[SDLK_KP1
]) & 2) keyboardp
.x
--, keyboardp
.y
++;
3820 else if ((keyState
['w'] | keyState
[SDLK_KP8
] | keyState
[SDLK_UP
]) & 2) keyboardp
.y
--;
3821 else if ((keyState
['s'] | keyState
[SDLK_KP2
] | keyState
[SDLK_DOWN
]) & 2) keyboardp
.y
++;
3822 else if ((keyState
[SDLK_LEFT
]) & 2) keyboardp
.x
--, keyboardp
.y
+=keyboardp
.x
&1;
3823 else if (((keyState
[SDLK_RIGHT
]) & 2)) { if (keyboardp
.x
< mapRightBound
) keyboardp
.y
-=keyboardp
.x
&1, keyboardp
.x
++; }
3824 else if ((keyState
[SDLK_RETURN
] | keyState
[SDLK_KP5
] | keyState
[SDLK_SPACE
] | keyState
[SDLK_KP_ENTER
]) & 2)
3826 // Simulate user clicking on it...
3827 Mouse(keyboardp
.getScreenX()-scrollX
, keyboardp
.getScreenY()-scrollY
, 0, 0, 1, 0, 0);
3834 UpdateCursor(keyboardp
);
3837 int min
[21] = { 17, 16, 15, 14, 13, 13, 13, 13, 13, 13, 12, 11, 11, 13, 12, 11, 8, 8, 7, 6, 7 };
3838 int max
[21] = { 20, 20, 19, 19, 19, 19, 18, 21, 20, 20, 19, 19, 18, 18, 17, 16, 16, 16, 15, 15, 14 };
3839 if (keyboardp
.x
< 3) keyboardp
.x
= 3;
3840 if (keyboardp
.x
> mapRightBound
) keyboardp
.x
= mapRightBound
;
3842 if (keyboardp
.y
< min
[keyboardp
.x
-3]) keyboardp
.y
= min
[keyboardp
.x
-3];
3843 if (keyboardp
.y
> max
[keyboardp
.x
-3]) keyboardp
.y
= max
[keyboardp
.x
-3];
3845 UpdateCursor(keyboardp
);
3847 else if (!editMode
&& (numUndo
==0 || time
>=undo
[numUndo
-1].endTime
))
3849 static int usedDiag
= 0;
3851 if (keyState
['q'] || keyState
[SDLK_KP7
]) HandleKey('q', 0);
3852 else if (keyState
['w'] || keyState
[SDLK_KP8
]) HandleKey('w', 0);
3853 else if (keyState
['e'] || keyState
[SDLK_KP9
]) HandleKey('e', 0);
3854 else if (keyState
['a'] || keyState
[SDLK_KP1
]) HandleKey('a', 0);
3855 else if (keyState
['s'] || keyState
[SDLK_KP2
]) HandleKey('s', 0);
3856 else if (keyState
['d'] || keyState
[SDLK_KP3
]) HandleKey('d', 0);
3858 else if (keyState
[SDLK_UP
] && keyState
[SDLK_LEFT
]) HandleKey('q', 0), usedDiag
=1;
3859 else if (keyState
[SDLK_UP
] && keyState
[SDLK_RIGHT
]) HandleKey('e', 0), usedDiag
=1;
3860 else if (keyState
[SDLK_DOWN
] && keyState
[SDLK_LEFT
]) HandleKey('a', 0), usedDiag
=1;
3861 else if (keyState
[SDLK_DOWN
] && keyState
[SDLK_RIGHT
]) HandleKey('d', 0), usedDiag
=1;
3862 else if (keyState
[SDLK_UP
] && !usedDiag
) HandleKey('w', 0);
3863 else if (keyState
[SDLK_DOWN
] && !usedDiag
) HandleKey('s', 0);
3868 void KeyReleased(int key
)
3872 bool KeyPressed(int key
, int mod
)
3878 bool eat
= activeMenu
->KeyPressed(key
, mod
);
3880 memset(keyState
, 0, sizeof(keyState
));
3885 if ((key
==SDLK_ESCAPE
&& (mod
& KMOD_CTRL
)))
3887 if (mod
& KMOD_SHIFT
)
3891 LoadSaveProgress(false);
3897 if (isFadeRendering
)
3900 return HandleKey(key
, mod
);
3903 bool HandleKey(int key
, int mod
)
3908 if (isMap
&& key
=='r' && (mod
& KMOD_ALT
))
3917 else if ((key
=='p' && !editMode
|| key
==SDLK_PAUSE
|| key
==SDLK_ESCAPE
))
3920 new PauseMenu(isMap
, progress
.GetLevel(STARTING_LEVEL
, true)->Completed(), progress
.general
.endSequence
>=1, progress
.general
.endSequence
>=2);
3924 else if (key
=='e' && (mod
& KMOD_ALT
))
3925 editMode
= !editMode
;
3927 else if (key
=='p' && (mod
& KMOD_ALT
) && numUndo
>0
3928 || key
>='0' && key
<='9' && (mod
& KMOD_SHIFT
) && !isMap
)
3930 if (key
>='0' && key
<='9')
3931 levelDiff
= (key
=='0') ? 10 : key
-'0';
3933 if (key
=='p' && levelPar
==0)
3934 levelPar
= player_score
;
3939 undo
[numUndo
-1].Restore(this);
3943 if (LoadSave(currentFile
, true))
3945 if (key
>='0' && key
<='9')
3951 /////////////////////////////////////////////////////////////////////////
3952 if (isMap
&& !editMode
)
3955 else if (key
==SDLK_KP9
|| key
=='e') Input(1), noMouse
=1;
3956 else if (key
==SDLK_KP3
|| key
=='d') Input(2), noMouse
=1;
3957 else if (key
==SDLK_KP1
|| key
=='a') Input(4), noMouse
=1;
3958 else if (key
==SDLK_KP7
|| key
=='q') Input(5), noMouse
=1;
3959 else if (key
==SDLK_KP8
|| key
=='w') Input(0), noMouse
=1;
3960 else if (key
==SDLK_KP2
|| (key
=='s' && (((mod
& (KMOD_CTRL
|KMOD_ALT
))==0)||!editMode
))) Input(3), noMouse
=1;
3961 else if (key
==SDLK_KP5
|| key
==SDLK_SPACE
|| key
==SDLK_RETURN
|| key
==SDLK_KP_ENTER
)
3964 if (win
&& winFinal
)
3965 LoadMap(), memset(keyState
, 0, sizeof(keyState
));
3970 else if (key
=='r' && (mod
& KMOD_CTRL
))
3971 LoadSave(currentFile
, false);
3974 else if (key
=='z' && (mod
& KMOD_ALT
))
3976 if (numUndo
>0 && !isMap
)
3978 time
= undo
[numUndo
-1].endTime
;
3979 undoTime
= undo
[0].time
;
3982 undo
[numUndo
-1].Restore(this);
3987 else if (key
=='z' || key
==SDLK_BACKSPACE
|| key
==SDLK_DELETE
|| key
=='u')
3994 else if (key
=='s' && (mod
& KMOD_ALT
)){
3995 if (win
&& strlen(currentFile
)>0 && !isMap
)
3998 strcpy(tmp
, currentFile
);
3999 ChangeSuffix(tmp
, "sol");
4000 FILE* f
= file_open(tmp
, "wb");
4003 for (int i
=0; i
<numUndo
; i
++)
4005 fputc(undo
[i
].playerMovement
, f
);
4014 else if (key
=='/' && (mod
& KMOD_ALT
)){
4022 if (mod
& KMOD_SHIFT
)
4024 LevelSave
* l
= progress
.GetLevel(currentFile
, false);
4025 if (l
&& l
->Completed())
4027 for (int i
=0; i
<l
->bestSolutionLength
; i
++)
4028 Input(l
->bestSolution
[i
]);
4037 strcpy(tmp
, currentFile
);
4038 ChangeSuffix(tmp
, "sol");
4039 FILE* f
= file_open(tmp
, "rb");
4043 while ((dir
= fgetc(f
)) != -1)
4064 else if (key
>='0' && key
<='9' && (mod
& KMOD_ALT
) && !isMap
)
4065 levelPar
= levelPar
*10 + key
-'0';
4066 else if (key
==SDLK_BACKSPACE
&& (mod
& KMOD_ALT
) && !isMap
)
4070 Mouse(mousex
, mousey
, 0, 0, 32, 0, mouse_buttons
);
4071 else if (key
=='p' && !(mod
& KMOD_ALT
))
4072 Mouse(mousex
, mousey
, 0, 0, 64, 0, mouse_buttons
);
4074 Mouse(mousex
, mousey
, 0, 0, 128, 0, mouse_buttons
);
4075 else if (key
==SDLK_RETURN
)
4076 Mouse(mousex
, mousey
, 0, 0, 256, 0, mouse_buttons
);
4077 else if (key
==SDLK_BACKSPACE
)
4078 Mouse(mousex
, mousey
, 0, 0, 512, 0, mouse_buttons
);
4080 Mouse(mousex
, mousey
, 0, 0, 1024, 0, mouse_buttons
);
4082 else if (key
=='s' && (mod
& KMOD_CTRL
)){
4083 char *fn
= LoadSaveDialog(true, true, "Save level");
4085 SDL_WM_SetCaption(currentFile
, NULL
);
4088 else if (key
=='o' && (mod
& KMOD_CTRL
)){
4089 char* fn
= LoadSaveDialog(false, true, "Open level");
4090 LoadSave(fn
, false);
4091 SDL_WM_SetCaption(currentFile
, NULL
);
4102 #define X(NAME,FILE,ALPHA) NAME = Load("graphics/" FILE BMP_SUFFIX, ALPHA);
4103 #include "gfx_list.h"
4105 static int first
= 1;
4113 // unsigned int d = {
4116 // static SDL_Cursor * c = SDL_CreateCursor(data, mask, 32, 32, 1, 1);
4117 // SDL_SetCursor(c);
4122 #define X(NAME,FILE,ALPHA) if (NAME) SDL_FreeSurface(NAME), NAME=0;
4123 #include "gfx_list.h"
4125 virtual void ScreenModeChanged()
4132 MAKE_STATE(HexPuzzle
, SDLK_F1
, false);
4134 char * HexPuzzle::loadPtr
= 0;
4135 char * HexPuzzle::endLoad
= 0;