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
22 #include <cctype> // TODO: remove it later
26 //////////////////////////////////////////////////////
34 //#define MAP_LOCKED_VISIBLE
36 #define GAME_NAME "Hex-a-hop"
39 // #define MAP_EDIT_HACKS
40 #define MAP_EDIT_HACKS_DISPLAY_UNLOCK 0
42 #define BMP_SUFFIX ".bmp"
44 #define USE_LEVEL_PACKFILE
45 #define BMP_SUFFIX ".dat"
51 #define GAMENAME GAME_NAME " (EDIT MODE)"
54 #define GAMENAME GAME_NAME
57 #define IMAGE_DAT_OR_MASK 0xff030303 // Reduce colour depth of images slightly for better compression (and remove useless top 8 bits!)
58 #define STARTING_LEVEL "Levels\\0_green\\triangular.lev"
59 #define UNLOCK_SCORING 75
60 const char * mapname
= "Levels\\map_maybe\\map.lev";
62 //////////////////////////////////////////////////////
70 #include "tiletypes.h"
72 #ifdef USE_LEVEL_PACKFILE
76 void RenderTile(bool reflect
, int t
, int x
, int y
, int cliplift
=-1);
78 int keyState
[SDLK_LAST
] = {0};
80 FILE *file_open( const char *file
, const char *flags
)
82 // printf("file_open( \"%s\", \"%s\" )\n", file, flags );
83 extern String base_path
;
84 static String filename
; // static to reduce memory alloc/free calls.
85 if (file
[0]=='\0' || file
[1]!=':') //If a full path is specified, don't prepend base_path
88 // printf(" -> \"%s\"\n", filename );
90 filename
.fix_backslashes();
91 FILE* f
= fopen( filename
, flags
);
95 printf("Warning: unable to open file \"%s\" for %s\n", (const char*)filename
, strchr(flags
, 'r') ? "reading" : "writing");
102 #ifdef MAP_EDIT_HACKS
103 static const short value_order
[]={
112 COLLAPSE_DOOR
, COLLAPSABLE2
,
124 //#define PROGRESS_FILE "progress.dat"
126 #define PI (3.1415926535897931)
128 #define MAX(a,b) ((a)>(b) ? (a) : (b))
129 #define MIN(a,b) ((a)<(b) ? (a) : (b))
130 #define ABS(a) ((a)<0 ? -(a) : (a))
132 #define WATER_COLOUR 31 | (IMAGE_DAT_OR_MASK>>16)&255, 37 | (IMAGE_DAT_OR_MASK>>8)&255, 135 | (IMAGE_DAT_OR_MASK>>0)&255
134 #define ROTATION_TIME 0.25
136 #define LASER_LINE_TIME 0.7
137 #define LASER_FADE_TIME 0.1
138 #define LASER_SEGMENT_TIME 0.01
139 #define LIFT_TIME 0.5
140 #define JUMP_TIME 0.4
142 #define X(NAME,FILE,ALPHA) SDL_Surface* NAME = 0;
143 #include "gfx_list.h"
144 int scrollX
=0, scrollY
=0, initScrollX
=0, initScrollY
=0;
145 int mapRightBound
= 0;
147 bool showScoring
= false;
148 bool hintsDone
= false;
174 TILE_GREEN_FRAGMENT_1
,
175 TILE_GREEN_FRAGMENT_2
,
180 TILE_GREEN_CRACKED_WALL
,
182 TILE_BLUE_CRACKED_WALL
,
184 TILE_FIRE_PARTICLE_1
,
185 TILE_FIRE_PARTICLE_2
,
189 TILE_LASER_FADE_0
= 53,
190 TILE_BLUE_FRAGMENT
= 56,
191 TILE_BLUE_FRAGMENT_1
,
192 TILE_BLUE_FRAGMENT_2
,
194 TILE_LASER_REFRACT
= 60,
195 TILE_ICE_LASER_REFRACT
= TILE_LASER_REFRACT
+6,
202 const int colours
[] = {
203 #define X(n,col, solid) col,
204 #include "tiletypes.h"
207 const int tileSolid
[] = {
208 #define X(n,col, solid) solid,
209 #include "tiletypes.h"
212 void ChangeSuffix(char* filename
, char* newsuffix
)
214 int len
= strlen(filename
);
216 while (i
>=0 && filename
[i
]!='\\' && filename
[i
]!='.' && filename
[i
]!='/')
218 if (filename
[i
]=='.')
219 strcpy(filename
+i
+1, newsuffix
);
222 strcat(filename
, ".");
223 strcat(filename
, newsuffix
);
227 bool isMap
=false, isRenderMap
=false;
228 int isFadeRendering
=0;
234 |-----------| TILE_W3
237 / \ |TILE_H1 |TILE_H2
244 WL = sqrt(h1*h1 + w1*w1)
245 wl**2 = h1**2 + w1**2
254 #define GFX_SIZE TILE_W3
255 #define TILE_W2 (TILE_W3-TILE_W1)
256 #define TILE_H1 TILE_W1
257 #define TILE_HUP 22 //extra visible height of wall (used for determining whether a wall was clicked on)
258 #define TILE_H2 (TILE_H1*2)
259 #define TILE_WL (TILE_W2-TILE_W1)
260 #define TILE_H_LIFT_UP 26
261 #define TILE_H_REFLECT_OFFSET 24
262 #define TILE_HUP2 TILE_H_LIFT_UP // Displacement of object on top of wall
263 #define FONT_SPACING 25
264 #define FONT_X_SPACING (-1) // -1 in order to try and overlap the black borders of adjacent characters
267 #define TILE_W1 (TILE_WL/2)
268 #define TILE_W2 (TILE_W1+TILE_WL)
269 #define TILE_W3 (TILE_W1+TILE_W2)
270 #define TILE_H1 (TILE_WL*0.8660254037844386)
271 #define TILE_H2 (TILE_H1*2)
276 SDL_Rect tile
[2][70];
277 short tileOffset
[2][70][2];
278 int Peek(SDL_Surface
* i
, int x
, int y
)
280 if (x
<0 || y
<0 || x
>=i
->w
|| y
>=i
->h
)
283 const int BytesPerPixel
= i
->format
->BytesPerPixel
;
284 const int BitsPerPixel
= i
->format
->BitsPerPixel
;
286 p
= ((unsigned char*)i
->pixels
)[i
->pitch
*y
+ x
*BytesPerPixel
];
287 else if (BitsPerPixel
==15 || BitsPerPixel
==16)
288 p
= *(short*)(((char*)i
->pixels
) + (i
->pitch
*y
+ x
*BytesPerPixel
));
289 else if (BitsPerPixel
==32)
290 p
= *(unsigned int*)(((char*)i
->pixels
) + (i
->pitch
*y
+ x
*BytesPerPixel
));
291 else if (BitsPerPixel
==24)
292 p
= (int)((unsigned char*)i
->pixels
)[i
->pitch
*y
+ x
*BytesPerPixel
]
293 | (int)((unsigned char*)i
->pixels
)[i
->pitch
*y
+ x
*BytesPerPixel
] << 8
294 | (int)((unsigned char*)i
->pixels
)[i
->pitch
*y
+ x
*BytesPerPixel
] << 16;
298 bool IsEmpty(SDL_Surface
* im
, int x
, int y
, int w
, int h
)
300 for (int i
=x
; i
<x
+w
; i
++)
301 for (int j
=y
; j
<y
+h
; j
++)
302 if (Peek(im
,i
,j
) != Peek(im
,0,im
->h
-1))
309 for (int i
=0; i
<140; i
++)
311 SDL_Rect r
= {(i
%10)*GFX_SIZE
, ((i
/10)%7)*GFX_SIZE
, GFX_SIZE
, GFX_SIZE
};
312 short * outOffset
= tileOffset
[i
/70][i
%70];
313 SDL_Surface
* im
= (i
/70) ? tileGraphicsR
: tileGraphics
;
315 outOffset
[0] = outOffset
[1] = 0;
317 while (r
.h
>1 && IsEmpty(im
, r
.x
, r
.y
, r
.w
, 1)) r
.h
--, r
.y
++, outOffset
[1]++;
318 while (r
.h
>1 && IsEmpty(im
, r
.x
, r
.y
+r
.h
-1, r
.w
, 1)) r
.h
--;
319 while (r
.w
>1 && IsEmpty(im
, r
.x
, r
.y
, 1, r
.h
)) r
.w
--, r
.x
++, outOffset
[0]++;
320 while (r
.w
>1 && IsEmpty(im
, r
.x
+r
.w
-1, r
.y
, 1, r
.h
)) r
.w
--;
322 tile
[i
/70][i
%70] = r
;
326 void ConvertToUTF8(const std::string
&text_locally_encoded
, char *text_utf8
, size_t text_utf8_length
)
329 size_t text_length
= text_locally_encoded
.length()+1;
331 static const char *locale_enc
= gettext_init
.GetEncoding();
332 iconv_t cd
= iconv_open("UTF-8", locale_enc
);
333 char *in_buf
= const_cast<char *>(&text_locally_encoded
[0]);
334 char *out_buf
= &text_utf8
[0];
335 iconv(cd
, &in_buf
, &text_length
, &out_buf
, &text_utf8_length
);
338 std::cerr
<< "An error occurred recoding " << text_locally_encoded
<< " to UTF8" << std::endl
;
341 int SDLPangoTextWidth(const std::string
&text_utf8
);
342 void Print_Pango(int x
, int y
, const std::string
&text_utf8
);
343 void Print_Pango_Aligned(int x
, int y
, int width
, const std::string
&text_utf8
, int align
);
345 /// Prints a left aligned string (a single line) beginning at (x,y)
346 // TODO: Check that the maximal text width is already set
347 void Print(int x
, int y
, const char * string
, ...)
350 va_start( marker
, string
); /* Initialize variable arguments. */
352 char tmp
[1000], tmp_utf8
[5000]; // FIXME: Check this limit
353 vsprintf((char*)tmp
, string
, marker
);
355 ConvertToUTF8(tmp
, tmp_utf8
, sizeof(tmp_utf8
)/sizeof(char));
356 Print_Pango(x
, y
, tmp_utf8
);
358 va_end( marker
); /* Reset variable arguments. */
361 /// Prints a string right aligned so that it ends at (x,y)
362 // TODO: Check that the maximal text width is already set
363 void PrintR(int x
, int y
, const char * string
, ...)
366 va_start( marker
, string
); /* Initialize variable arguments. */
368 char tmp
[1000], tmp_utf8
[5000]; // FIXME: Check this limit
369 vsprintf((char*)tmp
, string
, marker
);
371 ConvertToUTF8(tmp
, tmp_utf8
, sizeof(tmp_utf8
)/sizeof(char));
372 Print_Pango(x
-SDLPangoTextWidth(tmp_utf8
), y
, tmp_utf8
);
374 va_end( marker
); /* Reset variable arguments. */
377 /** \brief Prints a string horizontally centered around (x,y)
379 * " " in the string is interpreted as linebreak
381 void Print_Aligned(bool split
, int x
, int y
, int width
, const char * string
, int align
)
383 char tmp_utf8
[5000]; // FIXME: Check this limit
385 ConvertToUTF8(string
, tmp_utf8
, sizeof(tmp_utf8
)/sizeof(char));
387 std::string
msg(tmp_utf8
);
388 while (split
&& msg
.find(" ") != std::string::npos
)
389 msg
.replace(msg
.find(" "), 2, "\n");
391 Print_Pango_Aligned(x
, y
, width
, msg
, align
);
394 void PrintC(bool split
, int x
, int y
, const char * string
, ...)
397 va_start( marker
, string
); /* Initialize variable arguments. */
399 char tmp
[1000]; // FIXME: Check this limit
400 vsprintf((char*)tmp
, string
, marker
);
402 va_end( marker
); /* Reset variable arguments. */
404 static bool print
= true; // avoid flickering!
406 std::cerr
<< "Warning: don't know window width for message:\n" << tmp
<< "\n";
407 for (unsigned int i
=0; i
<strlen(tmp
); ++i
)
408 if (!std::isspace(tmp
[i
]))
411 Print_Aligned(split
, x
, y
, 2*std::min(x
, SCREEN_W
-x
), tmp
, 1);
414 #include "savestate.h"
416 #include "level_list.h"
418 void SaveState::GetStuff()
420 general
.hintFlags
= HintMessage::flags
;
422 void SaveState::ApplyStuff()
424 HintMessage::flags
= general
.hintFlags
;
432 Pos() : x(0), y(0) {}
433 Pos(int a
, int b
) : x(a
), y(b
) {}
434 bool operator == (Pos
const & p
) const
436 return x
==p
.x
&& y
==p
.y
;
438 Pos
operator + (Dir
const d
) const
441 x
+ ((d
==1 || d
==2) ? 1 : (d
==4 || d
==5) ? -1 : 0),
442 y
+ ((d
==0 || d
==1) ? -1 : (d
==3 || d
==4) ? 1 : 0)
445 int getScreenX() const {
448 int getScreenY() const {
449 return x
*TILE_H1
+ y
*TILE_H2
;
451 static Pos
GetFromWorld(double x
, double y
)
456 tx
= (int)floor(x
/TILE_W2
);
458 ty
= (int)floor(y
/TILE_H2
);
463 if (x
< TILE_W1
&& y
< TILE_H1
)
464 if (x
*TILE_H1
+ y
* TILE_W1
< TILE_H1
*TILE_W1
)
466 if (x
< TILE_W1
&& y
> TILE_H1
)
467 if (x
*TILE_H1
+ (TILE_H2
-y
) * TILE_W1
< TILE_H1
*TILE_W1
)
473 Pos
mousep(0,0), keyboardp(4,20);
479 virtual void Render(RenderObject
* r
, double time
, bool reflect
) = 0;
480 virtual int GetDepth(double time
) { return 1; }
497 if (maxStages
<= numStages
)
499 maxStages
= maxStages
? maxStages
*2 : 4;
500 stage
= (RenderStage
**) realloc(stage
, sizeof(stage
[0])*maxStages
);
501 time
= (double*) realloc(time
, sizeof(time
[0])*maxStages
);
505 RenderObject() : stage(0), time(0), numStages(0), maxStages(0), currentStage(0)
507 // TODO: use a random number with better range
508 // or maybe make seed an int or float...
509 seed
= rand() / (double)RAND_MAX
;
513 free(stage
); free(time
);
515 bool Active(double t
)
517 if (numStages
==0) return false;
518 if (t
< time
[0]) return false;
521 void UpdateCurrent(double t
)
523 if (currentStage
>= numStages
) currentStage
= numStages
-1;
524 if (currentStage
< 0) currentStage
= 0;
526 while (currentStage
>0 && time
[currentStage
]>t
)
528 while (currentStage
<numStages
-1 && time
[currentStage
+1]<=t
)
533 RenderStage
* GetStage(double t
)
535 if (t
==-1 && numStages
>0)
536 return stage
[numStages
-1];
538 if (!Active(t
)) return 0;
540 return stage
[currentStage
];
544 return numStages
>0 ? time
[numStages
-1] : -1;
546 void Render(double t
, bool reflect
)
551 stage
[currentStage
]->Render(this, t
- time
[currentStage
], reflect
);
553 int GetDepth(double t
)
558 return stage
[currentStage
]->GetDepth(t
- time
[currentStage
]);
563 numStages
= currentStage
= 0;
566 while (numStages
> 0 && time
[numStages
-1] >= t
)
572 if (currentStage
> 0 && numStages
> 0)
574 memmove(&time
[0], &time
[currentStage
], sizeof(time
[0]) * numStages
-currentStage
);
575 memmove(&stage
[0], &stage
[currentStage
], sizeof(stage
[0]) * numStages
-currentStage
);
576 numStages
-= currentStage
;
580 void Add(RenderStage
* s
, double t
)
584 if (currentStage
<numStages
&& time
[currentStage
]<=t
)
587 while (i
<numStages
&& time
[i
]<t
)
590 if (i
<numStages
&& time
[i
]==t
)
598 memmove(&time
[i
+1], &time
[i
], (numStages
-i
) * sizeof(time
[0]));
599 memmove(&stage
[i
+1], &stage
[i
], (numStages
-i
) * sizeof(stage
[0]));
613 RenderObject tile
[SIZE
][SIZE
][2];
626 void Reset(double t
= -1)
632 for (int i
=0; i
<SIZE
; i
++)
633 for (int j
=0; j
<SIZE
; j
++)
634 for (int q
=0; q
<2; q
++)
635 tile
[i
][j
][q
].Reset(t
);
637 for (int j
=0; j
<FX
; j
++)
646 for (int i
=0; i
<SIZE
; i
++)
647 for (int j
=0; j
<SIZE
; j
++)
648 for (int q
=0; q
<2; q
++)
649 tile
[i
][j
][q
].Wipe();
651 for (int j
=0; j
<FX
; j
++)
657 int x0
= (scrollX
+TILE_W2
) / TILE_W2
;
658 int x1
= (scrollX
+SCREEN_W
+TILE_W3
+TILE_W1
) / TILE_W2
;
659 if (p
.x
<0 || p
.y
<0 || p
.x
>=SIZE
|| p
.y
>=SIZE
) return false;
660 if (p
.x
<x0
) return false;
661 if (p
.x
>=x1
-1) return false;
662 for (int j0
=0; j0
<SIZE
*3; j0
++)
664 if (j0
* TILE_H1
< scrollY
-TILE_H1
) continue;
665 if (j0
* TILE_H1
> scrollY
+SCREEN_H
+TILE_H1
) break;
670 if (j
>=SIZE
) i
+=(j
+1-SIZE
)*2, j
=SIZE
-1;
671 for (; i
<x1
&& j
>=0; i
+=2, j
--)
680 void Render(double t
, bool reflect
)
684 int playerDepth
= player
.GetDepth(t
);
685 if (reflect
) playerDepth
-=4;
687 player
.Render(t
, reflect
);
689 int x0
= (scrollX
+TILE_W2
) / TILE_W2
;
690 int x1
= (scrollX
+SCREEN_W
+TILE_W3
+TILE_W1
) / TILE_W2
;
693 for (int j0
=0; j0
<SIZE
*3; j0
++)
695 if (j0
* TILE_H1
< scrollY
-TILE_H1
) continue;
696 if (j0
* TILE_H1
> scrollY
+SCREEN_H
+TILE_H1
) break;
701 if (j
>=SIZE
) i
+=(j
+1-SIZE
)*2, j
=SIZE
-1;
702 for (; i
<x1
&& j
>=0; i
+=2, j
--)
704 for (int q
=reflect
?1:0; q
!=2 && q
!=-1; q
+= (reflect
? -1 : 1))
705 if (tile
[i
][j
][q
].Active(t
))
707 tile
[i
][j
][q
].Render(t
, reflect
);
711 if (playerDepth
==j0
|| j0
==SIZE
*3 && playerDepth
>j0
)
712 player
.Render(t
, reflect
);
715 for (int j
=0; j
<FX
; j
++)
718 fx
[j
].Render(t
, reflect
);
722 RenderObject
& operator () ()
725 if (fxPos
==FX
) fxPos
= 0;
728 RenderObject
& operator () (Pos
const & p
, bool item
=false)
730 if (p
.x
<0 || p
.y
<0 || p
.x
>=SIZE
|| p
.y
>=SIZE
)
732 return tile
[p
.x
][p
.y
][item
? 1 : 0];
736 void RenderTile(bool reflect
, int t
, int x
, int y
, int cliplift
)
738 SDL_Rect src
= tile
[reflect
][t
];
739 SDL_Rect dst
= {x
-scrollX
-GFX_SIZE
/2, y
-scrollY
-GFX_SIZE
+TILE_H1
};
740 dst
.x
+= tileOffset
[reflect
][t
][0];
741 dst
.y
+= tileOffset
[reflect
][t
][1];
743 dst
.y
+= TILE_H_REFLECT_OFFSET
;
744 if (cliplift
==-1 || reflect
)
746 // dst.w=src.w; dst.h=src.h;
747 // SDL_FillRect(screen, &dst, rand());
748 SDL_BlitSurface(reflect
? tileGraphicsR
: tileGraphics
, &src
, screen
, &dst
);
756 SDL_BlitSurface(tileGraphics
, &src
, screen
, &dst
);
763 src
.w
-= TILE_W1
*2, src
.x
+= TILE_W1
;
765 SDL_BlitSurface(tileGraphics
, &src
, screen
, &dst
);
769 void RenderGirl(bool reflect
, int r
, int frame
, int x
, int y
, int h
)
772 int sy
= frame
* 80*2;
774 y
+= TILE_H_REFLECT_OFFSET
+20+h
, sy
+= 80;
777 SDL_Rect src
= {sx
, sy
, 64, 80};
778 SDL_Rect dst
= {x
-scrollX
-32, y
-scrollY
-65};
779 SDL_BlitSurface(girlGraphics
, &src
, screen
, &dst
);
782 struct ItemRender
: public RenderStage
788 ItemRender(int i2
, int _water
, Pos
const & _p
) : item(i2
), p(_p
), water(_water
)
791 double Translate(double seed
, double time
)
793 double bob
= time
*2 + seed
*PI2
;
797 void Render(RenderObject
* r
, double time
, bool reflect
)
802 int y
= -5 + (int)Translate(r
->seed
, r
->currentTime
+ time
);
805 if (!reflect
&& !water
)
806 RenderTile( false, TILE_SPHERE
, p
.getScreenX(), p
.getScreenY());
809 item
==1 ? TILE_ITEM1
: TILE_ITEM2
,
810 p
.getScreenX(), p
.getScreenY()+y
815 void RenderFade(double time
, int dir
, int seed
)
819 for(int x
=rand()%22-11; x
<SCREEN_W
+22; x
+=32, ys
^= 1)
821 for (int y
=ys
*20; y
<SCREEN_H
+30; y
+=40)
823 double a
= (rand()&0xff)*dir
;
824 double b
= (time
* 0x400 + (y
- SCREEN_H
) * 0x140/SCREEN_H
)*dir
;
827 RenderTile(false, TILE_BLACK_TILE
, x
+scrollX
, y
+scrollY
);
833 struct FadeRender
: public RenderStage
837 FadeRender(int d
=-1) : seed(rand()), dir(d
)
842 void Render(RenderObject
* r
, double time
, bool reflect
)
847 if (dir
==1) dir
=0, isFadeRendering
=0;
850 RenderFade(time
, dir
, seed
);
854 struct ScrollRender
: public RenderStage
858 ScrollRender(int a
,int b
) : x(a
), y(b
), done(false) {}
860 void Render(RenderObject
* r
, double time
, bool reflect
)
863 scrollX
= x
, scrollY
= y
;
869 struct LevelSelectRender
: public RenderStage
874 #ifdef MAP_EDIT_HACKS
878 LevelSelectRender(Pos
const & _p
, int i2
, int adj
) : p(_p
), item(i2
), adj(adj
)
881 void Render(RenderObject
* r
, double time
, bool reflect
)
886 #ifndef MAP_LOCKED_VISIBLE
891 for (int i
=0; i
<MAX_DIR
; i
++)
893 RenderTile( false, TILE_LINK_0
+i
, p
.getScreenX(), p
.getScreenY());
902 TILE_SPHERE
+ item
-1,
903 p
.getScreenX(), p
.getScreenY()
906 #ifdef MAP_EDIT_HACKS
907 int x
= p
.getScreenX()-scrollX
, y
= p
.getScreenY()-scrollY
;
908 Print(x
+5,y
-25,"%d",magic
);
914 struct ItemCollectRender
: public ItemRender
916 ItemCollectRender(int i2
, Pos
const & p
) : ItemRender(i2
, 0, p
)
919 void Render(RenderObject
* r
, double time
, bool reflect
)
924 int GetLiftHeight(double time
, int t
)
927 time
= LIFT_TIME
-time
;
928 time
= time
/ LIFT_TIME
;
933 time
= (3 - 2*time
)*time
*time
;
935 time
= (3 - 2*time
)*time
*time
;
937 return (int)((TILE_H_LIFT_UP
+4) * time
);
939 return (int)((TILE_H_LIFT_UP
-4) * time
) + 4;
942 struct TileRender
: public RenderStage
947 double specialDuration
;
949 TileRender(int i
, Pos
const & _p
, int _special
=0) : t(i
), p(_p
), special(_special
), specialDuration(LASER_LINE_TIME
)
952 void Render(RenderObject
* r
, double time
, bool reflect
)
954 if (t
==0 && special
==0)
957 if (special
&& (t
==LIFT_UP
|| t
==LIFT_DOWN
) && time
<LIFT_TIME
)
959 int y
= GetLiftHeight(time
, t
);
962 RenderTile(reflect
, TILE_LIFT_BACK
, p
.getScreenX(), p
.getScreenY());
963 RenderTile(reflect
, TILE_LIFT_SHAFT
, p
.getScreenX(), p
.getScreenY()+y
, y
-8);
964 RenderTile(reflect
, TILE_LIFT_FRONT
, p
.getScreenX(), p
.getScreenY());
968 RenderTile(reflect
, TILE_LIFT_SHAFT
, p
.getScreenX(), p
.getScreenY()-y
, y
);
969 RenderTile(reflect
, LIFT_DOWN
, p
.getScreenX(), p
.getScreenY());
972 else if (special
&& (t
==EMPTY
|| t
==TRAP
) && !reflect
&& time
< specialDuration
)
975 if (time
< specialDuration
-LASER_FADE_TIME
)
976 RenderTile(reflect
, TILE_ICE_LASER_REFRACT
, p
.getScreenX(), p
.getScreenY());
978 RenderTile(reflect
, t
, p
.getScreenX(), p
.getScreenY());
979 int base
= ((t
==EMPTY
) ? TILE_LASER_0
: TILE_LASER_REFRACT
);
980 if (t
==EMPTY
&& time
>= specialDuration
-LASER_FADE_TIME
)
981 base
= TILE_LASER_FADE_0
;
984 for(int i
=0; foo
; foo
>>=1, i
++)
986 RenderTile(reflect
, base
+i
, p
.getScreenX(), p
.getScreenY());
988 else if (t
==FLOATING_BALL
)
990 int y
= int(1.8 * sin(r
->seed
*PI
+ time
*4));
993 if (time
> 2) return;
995 srand(int(r
->seed
* 0xfff));
996 for (int i
=0; i
<20 - int(time
*10); i
++)
998 int x
= int((((rand() & 0xfff) - 0x800) / 10) * time
);
999 int y
= int((((rand() & 0xfff) - 0x800) / 10) * time
);
1000 RenderTile(true, 19 + ((i
+int(time
*5))&1)*10, p
.getScreenX() + x
, p
.getScreenY() - 14 + y
);
1004 RenderTile(true, 18, p
.getScreenX(), p
.getScreenY() - 14);
1007 RenderBoat(reflect
, int(special
)&255, p
.getScreenX(), p
.getScreenY(), y
);
1009 RenderTile(reflect
, t
, p
.getScreenX(), p
.getScreenY() + (reflect
? -y
: y
));
1011 else if (t
!= EMPTY
)
1012 RenderTile(reflect
, t
, p
.getScreenX(), p
.getScreenY());
1014 static void RenderBoat(bool reflect
, int d
, int x
, int y
, int yo
)
1017 RenderGirl(reflect
, d
, 0, x
, y
, -yo
);
1018 RenderTile(reflect
, FLOATING_BALL
, x
, y
+yo
);
1021 RenderGirl(reflect
, d
, 0, x
, y
, -yo
);
1022 RenderTile(true, 17, x
, y
+yo
-TILE_H_REFLECT_OFFSET
);
1027 struct TileRotateRender
: public TileRender
1032 TileRotateRender(int i
, Pos
const & p
, Dir _d
, int m
) : TileRender(i
, p
), d(_d
), mode(m
)
1034 void Render(RenderObject
* r
, double time
, bool reflect
)
1038 double f
= time
/ ROTATION_TIME
;
1040 if (mode
& 1) f
+= 0.5;
1049 if (mode
& 1) f
=1-f
; else f
=f
;
1053 TileRender::Render(r
, time
, reflect
);
1056 Pos dd
= (Pos(0,0)+d
);
1057 int x
= p
.getScreenX() + int(dd
.getScreenX()*(f
));
1058 int y
= p
.getScreenY() + int(dd
.getScreenY()*(f
));
1061 RenderBoat(reflect
, (mode
&1) ? (d
+MAX_DIR
/2)%MAX_DIR
: d
, x
, y
, 2);
1063 RenderTile(reflect
, t
, x
, y
);
1068 struct LaserRender
: public RenderStage
1074 LaserRender(Pos _p
, int dir
, int r
) : p(_p
), d(dir
), range(r
)
1077 void Render(RenderObject
* r
, double time
)
1082 struct ExplosionRender
: public RenderStage
1089 ExplosionRender(Pos _p
, int _pow
=0, int t
=0) : p(_p
), power(_pow
), type(t
)
1094 virtual int GetDepth(double time
)
1099 void Render(RenderObject
* r
, double time
, bool reflect
)
1101 if (type
==1 && time
> 2.5)
1102 type
= -1, new WinLoseScreen(false);
1104 // if (reflect) return;
1105 if (time
> 3) return;
1107 int q
= 50 - int(time
* 35);
1110 for (int i
=0; i
<q
; i
++)
1112 int x
= p
.getScreenX();
1113 int y
= p
.getScreenY() + (rand() & 31)-16;
1114 int xs
= ((rand() & 63) - 32);
1115 int ys
= (-10 - (rand() & 127)) * (1+power
);
1116 if (type
) ys
*=2, xs
/=2;
1117 x
+= int(xs
* (1+time
*(2+power
)));
1118 int yo
= int(time
*time
*128 + ys
*time
);
1119 //if (yo > 0) yo=-yo;//continue;
1125 if (!reflect
&& ys
<-60)
1127 const double T
= 0.06;
1128 double ct
= -ys
/ 128.0;
1131 x
= p
.getScreenX() + int(xs
* (1+ct
*(2+power
)));
1134 time
> ct
+3*T
? TILE_SPLASH_3
: time
> ct
+2*T
? TILE_SPLASH_2
: time
> ct
+T
? TILE_SPLASH_1
: TILE_WATER_PARTICLE
+1,
1142 time
- i
*0.003 < 0.2 ? TILE_WATER_PARTICLE
+1 : TILE_WATER_PARTICLE
,
1143 x
, y
+(reflect
?-1:1)*yo
);
1152 i
<q
-20 || time
<0.3 ? TILE_LASER_HEAD
: i
<q
-10 || time
<0.6 ? TILE_FIRE_PARTICLE_1
: TILE_FIRE_PARTICLE_2
,
1153 x
, y
+(reflect
?-1:1)*yo
);
1158 struct DisintegrateRender
: public RenderStage
1165 DisintegrateRender(Pos _p
, int _pow
=0, int _t
=0) : p(_p
), height(_pow
), type(_t
)
1170 void Render(RenderObject
* r
, double time
, bool reflect
)
1173 RenderTile(reflect
, height
? COLLAPSE_DOOR
: COLLAPSABLE
, p
.getScreenX(), p
.getScreenY());
1175 if (time
> 50.0/70.0) return;
1176 if (reflect
) return;
1178 int q
= 50 - int(time
* 70);
1180 for (int i
=0; i
<q
; i
++)
1182 int x
= (rand() % (TILE_W3
-8))-TILE_W3
/2+4;
1183 int y
= (rand() % (TILE_H2
-8))-TILE_H1
+4;
1184 if (x
<-TILE_WL
/2 && ABS(y
)<-TILE_WL
/2-x
) continue;
1185 if (x
>TILE_WL
/2 && ABS(y
)>x
-TILE_WL
/2) continue;
1187 if (height
) yo
-= rand() % TILE_HUP
;
1188 x
+= p
.getScreenX();
1189 y
+= p
.getScreenY() + 4;
1190 int xs
= 0;//((rand() & 63) - 32);
1191 int ys
= (- (rand() & 31));
1192 x
+= int(xs
* (1+time
*(2)));
1194 yo
+= int(time
*time
*128 + ys
*time
);
1195 if (type
) yo
= -yo
*2;
1196 //if (yo > 0) yo=-yo;//continue;
1197 int t
= type
? TILE_BLUE_FRAGMENT
: TILE_GREEN_FRAGMENT
;
1201 RenderTile(false, t
, x
, y
+(reflect
?-yo
:yo
));
1205 struct BuildRender
: public RenderStage
1213 BuildRender(Pos _p
, Dir _d
, int _h
, int _r
=0, int _type
=0) : p(_p
), dir(_d
), height(_h
), reverse(_r
), type(_type
)
1217 void Render(RenderObject
* r
, double time
, bool reflect
)
1219 if (time
>= BUILD_TIME
)
1220 RenderTile(reflect
, height
^ reverse
? (type
? COLLAPSE_DOOR2
: COLLAPSE_DOOR
) : (type
? COLLAPSABLE2
: COLLAPSABLE
), p
.getScreenX(), p
.getScreenY());
1224 RenderTile(reflect
, type
? COLLAPSABLE2
: COLLAPSABLE
, p
.getScreenX(), p
.getScreenY());
1226 double dist
= time
* 2 / BUILD_TIME
;
1229 Pos from
= p
+ ((dir
+MAX_DIR
/2)%MAX_DIR
);
1233 double offset
= (dist
*0.7) + 0.3;
1234 int x
= from
.getScreenX() + int((p
.getScreenX()-from
.getScreenX()) * offset
);
1235 int y
= from
.getScreenY() + int((p
.getScreenY()-from
.getScreenY()) * offset
- dist
*(1-dist
)*(TILE_HUP
*4));
1236 RenderTile(reflect
, TILE_GREEN_FRAGMENT
, x
, y
);
1242 if (reverse
) dist
= 1-dist
;
1244 if (dist
> 0 && !height
)
1247 for (int i
=0; i
<=int(dist
*15); i
++)
1249 int x
= p
.getScreenX(), y
= p
.getScreenY();
1250 double d
= (i
+ fmod(dist
*15, 1))/10.0;
1251 int x1
= int(sin(d
*5+time
)*MIN(d
,1)*TILE_W2
/2);
1252 int y1
= int(cos(d
*5+time
)*MIN(d
,1)*TILE_H1
*0.7);
1253 RenderTile(reflect
, TILE_GREEN_FRAGMENT
, x
+x1
, y
+y1
+4);
1254 RenderTile(reflect
, TILE_GREEN_FRAGMENT
, x
-x1
, y
-y1
+4);
1257 if (dist
> 0 && height
)
1259 int yo
= int((1-dist
)*(TILE_HUP
*1.3));
1260 if (yo
> TILE_HUP
*1.1)
1261 RenderTile(reflect
, TILE_WHITE_TILE
, p
.getScreenX(), p
.getScreenY());
1264 RenderTile(reflect
, type
? COLLAPSABLE2
: COLLAPSABLE
, p
.getScreenX(), p
.getScreenY());
1265 RenderTile(reflect
, type
? COLLAPSE_DOOR2
: COLLAPSE_DOOR
, p
.getScreenX(), p
.getScreenY()+(reflect
? -yo
: yo
), yo
+6);
1266 RenderTile(reflect
, type
? TILE_BLUE_FRONT
: TILE_GREEN_FRONT
, p
.getScreenX(), p
.getScreenY());
1270 if (yo
< TILE_HUP
/2)
1272 RenderTile(reflect
, type
? COLLAPSE_DOOR2
: COLLAPSE_DOOR
, p
.getScreenX(), p
.getScreenY()+(reflect
? -yo
: yo
), yo
);
1275 RenderTile(reflect
, type
? COLLAPSABLE2
: COLLAPSABLE
, p
.getScreenX(), p
.getScreenY());
1283 struct PlayerRender
: public RenderStage
1293 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)
1295 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)
1297 int dist
= MAX(ABS(p
.x
-target
.x
), ABS(p
.y
-target
.y
));
1304 virtual int GetDepth(double time
)
1306 double f
= speed
? time
/ speed
: 1;
1308 if (f
==1) dead
= this->dead
;
1310 double x
= p
.x
+ (target
.x
- p
.x
) * f
;
1311 double y
= p
.y
+ (target
.y
- p
.y
) * f
;
1313 if (f
==1 || f
>0.5 && p_h
>target_h
)
1314 return target
.x
+target
.y
*2;
1315 return MAX(target
.x
+target
.y
*2 , p
.x
+p
.y
*2);
1318 void Render(RenderObject
* ro
, double time
, bool reflect
)
1321 double f
= speed
? time
/ speed
: 1;
1323 if (f
==1) dead
= this->dead
;
1325 int x
= p
.getScreenX();
1326 int y
= p
.getScreenY();
1327 int x2
= target
.getScreenX();
1328 int y2
= target
.getScreenY();
1330 int shadow_h
= (int)((p_h
+(target_h
-p_h
)*f
)*TILE_HUP2
);
1332 if (x
==x2
&& y
==y2
&& p_h
!=target_h
)
1334 h
= TILE_H_LIFT_UP
- GetLiftHeight(time
, p_h
? LIFT_DOWN
: LIFT_UP
);
1339 int dist
= MAX(ABS(p
.x
-target
.x
), ABS(p
.y
-target
.y
));
1340 int arc
= dist
*dist
;
1341 int h1
= p_h
* TILE_HUP2
;;
1342 int h2
= target_h
* TILE_HUP2
;
1343 if (dist
==2 && h1
!=0)
1347 shadow_h
= f
>=0.7 ? int(shadow_h
*(f
-0.7)/0.3) : 0;
1350 arc
= speed
> JUMP_TIME
? 7 : 2;
1352 h
= (int)(h1
+(h2
-h1
)*f
);
1353 // if (x==x2 && y==y2)
1357 //h += int(TILE_H_LIFT_UP/3 * (1-f));
1358 h
+= (int)(f
*(1-f
)*TILE_HUP2
*arc
);
1370 //frame = ((int)(f*4) % 4);
1371 //if (frame==2) frame=0; else if (frame==3) frame=2;
1374 else if (f
==1 || x
==x2
&& y
==y2
) // stationary
1380 frame
= type
? 2 : 1;
1386 RenderTile( false, TILE_SPHERE
,
1388 (int)(y
+(y2
-y
)*f
) - shadow_h
1401 dead ? TILE_SPHERE_OPEN : TILE_SPHERE_DONE,
1410 struct HexPuzzle
: public State
1414 #define MAX_TILECHANGE 64 // TODO: don't have a magic upper limit
1423 TileChange(Pos _p
, Tile _t
, int _i
) : p(_p
), t(_t
), item(_i
)
1425 void Restore(HexPuzzle
* w
)
1427 w
->SetTile(p
,t
,false,false);
1428 w
->SetItem(p
,item
,false,false);
1432 TileChange t
[MAX_TILECHANGE
];
1441 void Add(TileChange
const & tc
)
1443 for (int i
=0; i
<numT
; i
++)
1446 if (numT
>=MAX_TILECHANGE
)
1447 FATAL("numT>=MAX_TILECHANGE");
1451 void New(Dir pmove
, Pos
& pp
, int* items
, double t
, int sc
)
1453 numItems
[0] = items
[0];
1454 numItems
[1] = items
[1];
1456 playerMovement
= pmove
;
1461 void Restore(HexPuzzle
* w
)
1463 for (int i
=numT
-1; i
>=0; i
--)
1467 w
->player
= playerPos
;
1468 w
->player_items
[0] = numItems
[0];
1469 w
->player_items
[1] = numItems
[1];
1470 w
->player_score
= score
;
1472 //w->renderer.player.Add(new PlayerRender(playerPos, w->GetHeight(playerPos), false), w->time);
1477 char* special
[MAP_SIZE
][MAP_SIZE
];
1478 Tile map
[MAP_SIZE
][MAP_SIZE
];
1479 int map_item
[MAP_SIZE
][MAP_SIZE
];
1480 int tileCount
[NumTileTypes
];
1481 int levelPar
, levelDiff
;
1484 int player_items
[2];
1486 int numComplete
, numLevels
, numMastered
, numLevelsFound
;
1493 WorldRenderer renderer
;
1498 Undo undo
[MAX_UNDO
];
1500 LevelInfo
* currentLevelInfo
;
1502 char currentFile
[1000];
1509 LevelInfo
* GetLevelInfo(const char* f
)
1511 if (strstr(f
, "Levels\\") == f
)
1513 if (currentLevelInfo
!=0 && strcmp(currentLevelInfo
->file
, f
)==0)
1514 return currentLevelInfo
;
1519 if (t
<= numComplete
)
1522 static char tmp1
[1000];
1523 static LevelInfo tmp
= {0, "", tmp1
};
1524 sprintf(tmp1
, ngettext("Complete 1 more level to unlock!", "Complete %d more levels to unlock!", t
-numComplete
), t
-numComplete
);
1528 for (int i
=0; i
<sizeof(levelNames
)/sizeof(levelNames
[0]); i
++)
1529 if (strcmp(f
, levelNames
[i
].file
)==0)
1530 return &levelNames
[i
];
1531 static LevelInfo tmp
= {0, "", _("<<NO NAME>>")};
1535 #ifdef MAP_EDIT_HACKS
1536 int GetAutoTile(const char * level
, bool tiletype
)
1538 FILE* f
= file_open(filename
, "rb");
1542 if (f
&& fscanf(f
, "%d", &version
)==1 && (version
==3 || version
==4))
1544 if (strstr(level
,"mk"))
1547 fgetc(f
); // Remove '\n' character
1550 unsigned char bounds
[4];
1552 fread(&par
, sizeof(par
), 1, f
);
1554 fread(&diff
, sizeof(diff
), 1, f
);
1555 fread(bounds
, sizeof(bounds
), 1, f
);
1556 fread(&playerStart
, sizeof(player
), 1, f
);
1560 for (int i
=bounds
[0]; i
<=bounds
[1]; i
++)
1561 for (int j
=bounds
[2]; j
<=bounds
[3]; j
++)
1563 unsigned char comp
= map
[i
][j
] | (map_item
[i
][j
]<<5);
1564 fread(&comp
, sizeof(comp
), 1, f
);
1565 int t
= comp
& 0x1f;
1566 int item
= (comp
>> 5) & 3;
1567 for (int i
=highval
+1; i
<sizeof(value_order
)/sizeof(value_order
[0]); i
++)
1568 if (t
!=0 && t
==value_order
[i
]
1569 || item
!=0 && item
==(value_order
[i
]>>8))
1575 tile
= value_order
[highval
];
1576 if (tile
==0x100) tile
= COLLAPSABLE3
;
1577 if (tile
==0x200) tile
= SWITCH
;
1578 if (tile
==LIFT_UP
) tile
= LIFT_DOWN
;
1582 if (value_order
[highval
] == LIFT_UP
)
1600 numComplete
= numLevels
= numMastered
= numLevelsFound
= 0;
1601 for (int i
=0; i
<MAP_SIZE
; i
++)
1602 for (int j
=0; j
<MAP_SIZE
; j
++)
1603 ActivateSpecial(Pos(i
,j
), 0);
1604 for (int i
=0; i
<MAP_SIZE
; i
++)
1605 for (int j
=0; j
<MAP_SIZE
; j
++)
1606 ActivateSpecial(Pos(i
,j
), 2);
1607 numComplete
= numLevels
= numMastered
= numLevelsFound
= 0;
1608 for (int i
=0; i
<MAP_SIZE
; i
++)
1609 for (int j
=0; j
<MAP_SIZE
; j
++)
1610 ActivateSpecial(Pos(i
,j
), 0);
1616 if (strcmp(mapname
, currentFile
)==0)
1618 // for (int i=0; i<32; i++)
1619 // HintMessage::FlagTile(i);
1620 if (numComplete
>= UNLOCK_SCORING
&& !progress
.general
.scoringOn
)
1622 HintMessage::FlagTile(26);
1623 progress
.general
.scoringOn
= 1;
1624 InitSpecials(); // Re-initialise with gold ones available
1626 HintMessage::FlagTile(25);
1630 for (int i
=0; i
<MAP_SIZE
; i
++)
1631 for (int j
=0; j
<MAP_SIZE
; j
++)
1633 int t
= GetTile(Pos(i
,j
));
1634 int item
= GetItem(Pos(i
,j
));
1636 HintMessage::FlagTile(t
);
1638 HintMessage::FlagTile(item
+20);
1640 HintMessage::FlagTile(EMPTY
);
1649 UpdateCursor(Pos(-1,-1));
1661 player_items
[0] = player_items
[1] = 0;
1663 if (strlen(currentSlot
) == 0)
1670 if (!isFadeRendering
&& time
!=0)
1672 renderer().Add(new FadeRender(-1), time
);
1678 renderer
.Reset(time
);
1681 for (int t
=0; t
<NumTileTypes
; t
++)
1684 for (int i
=0; i
<MAP_SIZE
; i
++)
1685 for (int j
=0; j
<MAP_SIZE
; j
++)
1688 int item
= GetItem(p
);
1690 renderer(p
,true).Add(new ItemRender(item
, GetTile(p
)==EMPTY
, p
), time
);
1695 for (int i
=0; i
<MAP_SIZE
; i
++)
1696 for (int j
=0; j
<MAP_SIZE
; j
++)
1706 renderer(p
).Add(new TileRender(t
, p
), time
);
1710 renderer
.player
.Add(new PlayerRender(player
, GetHeight(player
), dead
), time
);
1712 renderer
.player
.Add(new PlayerRender(Pos(-100,-100), 0, true), time
);
1715 int bounds
[4] = {player
.getScreenX(),player
.getScreenX(),player
.getScreenY(),player
.getScreenY()};
1716 for (int i
=0; i
<MAP_SIZE
; i
++)
1717 for (int j
=0; j
<MAP_SIZE
; j
++)
1720 if (map
[i
][j
] !=0 || map_item
[i
][j
]!=0)
1722 int x1
= p
.getScreenX();
1723 int y1
= p
.getScreenY();
1724 int x2
= x1
+ TILE_W3
;
1725 int y2
= y1
+ TILE_H2
;
1726 y1
-= TILE_H2
; // Make sure objects/player will be properly visible
1728 if (x1
<bounds
[0]) bounds
[0] = x1
;
1729 if (x2
>bounds
[1]) bounds
[1] = x2
;
1730 if (y1
<bounds
[2]) bounds
[2] = y1
;
1731 if (y2
>bounds
[3]) bounds
[3] = y2
;
1738 sx
= bounds
[0] - int(TILE_W2
*6.35);
1739 sy
= (bounds
[3] + bounds
[2] - SCREEN_H
) / 2 - TILE_H2
/2;
1743 sx
= (bounds
[1] + bounds
[0] - SCREEN_W
) / 2 - TILE_W3
/2;
1744 sy
= (bounds
[3] + bounds
[2] - SCREEN_H
) / 2 - TILE_H2
/2;
1756 // time = 1; // Guarantee we can't try and do things at time=0
1758 renderer().Add(new ScrollRender(sx
, sy
), time
);
1759 renderer().Add(new FadeRender(1), time
);
1764 char* ReadAll(FILE* f
)
1767 fseek(f
, 0, SEEK_END
);
1769 fseek(f
, 0, SEEK_SET
);
1770 char* c
= loadPtr
= new char [size
];
1771 endLoad
= loadPtr
+ size
;
1772 fread(c
, 1, size
, f
);
1776 static char *loadPtr
, *endLoad
;
1777 static unsigned int fread_replace(void* d
, unsigned int size
, unsigned int num
, FILE*)
1779 unsigned int remain
= (endLoad
- loadPtr
) / size
;
1780 if (remain
< num
) num
= remain
;
1781 memcpy(d
, loadPtr
, size
*num
);
1782 loadPtr
+= size
*num
;
1786 int GetPar(const char * level
, bool getdiff
=false)
1788 if (strcmp(level
, currentFile
)==0)
1789 return getdiff
? levelDiff
: levelPar
;
1791 #ifdef USE_LEVEL_PACKFILE
1792 PackFile1::Entry
* e
= levelFiles
.Find(level
);
1794 loadPtr
= (char*)e
->Data();
1795 endLoad
= loadPtr
+ e
->DataLen();
1799 FILE* f
= file_open(level
, "rb");
1802 typedef unsigned int _fn(void*, unsigned int, unsigned int, FILE*);
1803 _fn
* fn
= (loadPtr
? (_fn
*)fread_replace
: (_fn
*)fread
);
1805 int par
= 99999, diff
= 0;
1809 return getdiff
? diff
: par
;
1811 fn(&version
, 2, 1, f
); // skip to relevant point
1813 if (fn(&par
, sizeof(par
), 1, f
) != 1)
1815 if (fn(&diff
, sizeof(diff
), 1, f
) != 1 || diff
<0 || diff
>10)
1818 #ifdef USE_LEVEL_PACKFILE
1819 loadPtr
= endLoad
= 0;
1825 return getdiff
? diff
: par
;
1828 bool LoadSave(const char * filename
, bool save
)
1835 showScoring
= false;
1836 LevelSave
* l
= progress
.GetLevel(filename
, true);
1837 if (progress
.general
.scoringOn
&& l
&& l
->Completed() )
1841 #ifdef USE_LEVEL_PACKFILE
1844 PackFile1::Entry
* e
= levelFiles
.Find(filename
);
1845 if (!e
) return false;
1847 strcpy(currentFile
, filename
);
1848 currentLevelInfo
= GetLevelInfo(currentFile
);
1850 loadPtr
= (char*)e
->Data();
1851 endLoad
= loadPtr
+ e
->DataLen();
1852 _LoadSave(NULL
, save
);
1853 loadPtr
= endLoad
= 0;
1859 FILE* f
= file_open(filename
, save
? "wb" : "rb");
1862 strcpy(currentFile
, filename
);
1864 currentLevelInfo
= GetLevelInfo(currentFile
);
1868 char* data
= ReadAll(f
);
1871 loadPtr
= endLoad
= 0;
1886 void _LoadSave(FILE* f
, bool save
)
1888 typedef unsigned int _fn(void*, unsigned int, unsigned int, FILE*);
1889 _fn
* fn
= save
? (_fn
*)fwrite
: (loadPtr
? (_fn
*)fread_replace
: (_fn
*)fread
);
1892 int version
= VERSION
;
1894 fprintf(f
, "%d\n", version
);
1898 if (fn(&c
, 1, 1, f
) != 1)
1902 // Remove '\n' character
1908 for (int i
=0; i
<MAP_SIZE
; i
++)
1909 for (int j
=0; j
<MAP_SIZE
; j
++)
1911 delete [] special
[i
][j
];
1918 for (int i
=0; i
<MAP_SIZE
; i
++)
1919 for (int j
=0; j
<MAP_SIZE
; j
++)
1920 fn(&map
[i
][j
], sizeof(map
[i
][j
]), 1, f
);
1922 fn(&player
, sizeof(player
), 1, f
);
1924 if (fn(map_item
, sizeof(map_item
), 1, f
) == 0)
1925 memset(map_item
, 0, sizeof(map_item
));
1927 else if (version
>=2 && version
<=4)
1929 unsigned char bounds
[4];
1932 bounds
[0]=bounds
[1]=player
.x
;
1933 bounds
[2]=bounds
[3]=player
.y
;
1934 for (int i
=0; i
<MAP_SIZE
; i
++)
1935 for (int j
=0; j
<MAP_SIZE
; j
++)
1936 if (map
[i
][j
] !=0 || map_item
[i
][j
]!=0 || special
[i
][j
]!=0)
1938 if (i
<bounds
[0]) bounds
[0] = i
;
1939 if (i
>bounds
[1]) bounds
[1] = i
;
1940 if (j
<bounds
[2]) bounds
[2] = j
;
1941 if (j
>bounds
[3]) bounds
[3] = j
;
1946 memset(map
, 0, sizeof(map
));
1947 memset(map_item
, 0, sizeof(map_item
));
1951 fn(&levelPar
, 1, sizeof(levelPar
), f
);
1956 fn(&levelDiff
, 1, sizeof(levelDiff
), f
);
1960 fn(bounds
, sizeof(bounds
), 1, f
);
1961 fn(&player
, sizeof(player
), 1, f
);
1963 int offsetx
=0, offsety
=0;
1965 if (!save
&& bounds
[1]-bounds
[0]<15) // Hacky - don't recenter map...
1967 // Re-position map to top left (but leave a bit of space)
1968 // (This ensures the laser/boat effects don't clip prematurely against the edges of the screen)
1969 offsetx
= SCREEN_W
/2/TILE_W2
+ 1 - (bounds
[0]+bounds
[1]/2);
1970 offsety
= SCREEN_H
/2/TILE_H2
+ SCREEN_W
/2/TILE_W2
- (bounds
[2]+bounds
[3]/2);
1971 offsetx
= MAX(0, offsetx
);
1972 offsety
= MAX(0, offsety
);
1973 // if (bounds[0] > 2)
1974 // offsetx = 2 - bounds[0];
1975 // if (bounds[2] > 2)
1976 // offsety = 2 - bounds[2];
1978 bounds
[0] += offsetx
;
1979 bounds
[1] += offsetx
;
1980 bounds
[2] += offsety
;
1981 bounds
[3] += offsety
;
1982 player
.x
+= offsetx
;
1983 player
.y
+= offsety
;
1985 for (int i
=bounds
[0]; i
<=bounds
[1]; i
++)
1986 for (int j
=bounds
[2]; j
<=bounds
[3]; j
++)
1988 unsigned char comp
= map
[i
][j
] | (map_item
[i
][j
]<<5);
1989 fn(&comp
, sizeof(comp
), 1, f
);
1990 map
[i
][j
] = comp
& 0x1f;
1991 map_item
[i
][j
] = (comp
>> 5) & 3;
1996 for (int i
=bounds
[0]; i
<=bounds
[1]; i
++)
1997 for (int j
=bounds
[2]; j
<=bounds
[3]; j
++)
2000 short len
= strlen(special
[i
][j
]);
2001 unsigned char x
=i
, y
=j
;
2002 fn(&x
, sizeof(x
), 1, f
);
2003 fn(&y
, sizeof(y
), 1, f
);
2004 fn(&len
, sizeof(len
), 1, f
);
2005 fn(special
[i
][j
], 1, len
, f
);
2013 if (!fn(&x
, sizeof(x
), 1, f
))
2015 fn(&y
, sizeof(y
), 1, f
);
2016 x
+= offsetx
; y
+= offsety
;
2017 fn(&len
, sizeof(len
), 1, f
);
2019 char* tmp
= new char[len
+1];
2023 SetSpecial(Pos(x
,y
), tmp
, true, false);
2028 return; // Unsupported version!
2032 // Save when returning to map!
2035 progress
.general
.completionPercentage
= numComplete
*100/numLevels
;
2036 progress
.general
.masteredPercentage
= numMastered
*100/numLevels
;
2037 LoadSaveProgress(true);
2041 void SetTile(Pos
const & p
, Tile t
, bool updateRenderer
=true, bool undoBuffer
=true)
2043 if (p
.x
<0 || p
.x
>MAP_SIZE
)
2045 if (p
.y
<0 || p
.y
>MAP_SIZE
)
2047 if (map
[p
.x
][p
.y
] == t
)
2049 if (map
[p
.x
][p
.y
] == t
)
2052 tileCount
[map
[p
.x
][p
.y
]]--;
2056 undo
[numUndo
].Add(Undo::TileChange(p
,GetTile(p
),GetItem(p
)));
2061 renderer(p
).Add(new TileRender(t
, p
), time
);
2064 Tile
GetTile(Pos
const & p
)
2066 if (p
.x
<0 || p
.x
>=MAP_SIZE
)
2068 if (p
.y
<0 || p
.y
>=MAP_SIZE
)
2070 return map
[p
.x
][p
.y
];
2073 int GetHeight(Pos
const & p
)
2075 return tileSolid
[GetTile(p
)]==1;
2078 char* GetSpecial(Pos
const & p
)
2080 if (p
.x
<0 || p
.x
>=MAP_SIZE
)
2082 if (p
.y
<0 || p
.y
>=MAP_SIZE
)
2084 return special
[p
.x
][p
.y
];
2087 void SetSpecial(Pos
const & p
, char * d
, bool use_pointer
=false, bool auto_activate
=true)
2089 if (p
.x
<0 || p
.x
>=MAP_SIZE
|| p
.y
<0 || p
.y
>=MAP_SIZE
)
2096 delete [] special
[p
.x
][p
.y
];
2097 if (!use_pointer
&& d
)
2100 special
[p
.x
][p
.y
] = new char [strlen(d
) + 1];
2101 strcpy(special
[p
.x
][p
.y
], d
);
2104 special
[p
.x
][p
.y
] = d
;
2106 if (special
[p
.x
][p
.y
]==0)
2107 renderer(p
,true).Add(new ItemRender(GetItem(p
), GetTile(p
)==EMPTY
, p
), time
);
2108 else if (auto_activate
)
2109 ActivateSpecial(p
, 0);
2112 int GetLevelState(Pos
const & p
, int recurse
=0)
2114 char* x
= GetSpecial(p
);
2117 LevelSave
* l
= progress
.GetLevel(x
, false);
2121 if (strcmp(x
, STARTING_LEVEL
)==0)
2123 if (x
[0]=='_' && l
&& l
->unlocked
)
2126 if (l
&& l
->Completed())
2133 int par
= GetPar(x
);
2134 if (progress
.general
.scoringOn
&& l
->PassesPar( par
))
2141 for (Dir d
=0; d
<MAX_DIR
; d
++)
2143 int i
= GetLevelState(p
+d
, 1);
2144 // if (i>1 || i==1 && t>1)
2145 if (i
>=1 && t
>2 || t
>=1 && i
>2)
2156 void ActivateSpecial(Pos
const & p
, int type
)
2158 if (p
.x
<0 || p
.x
>=MAP_SIZE
|| p
.y
<0 || p
.y
>=MAP_SIZE
)
2161 char * x
= special
[p
.x
][p
.y
];
2163 if (x
==0 || x
[0]==0)
2166 if (type
==2 && x
[0]=='_') // Phase2 init - unlock
2168 int t
= GetLevelState(p
);
2169 int target
= atoi(x
+1), targetM
= 0;
2170 if (target
>1000) targetM
=target
=target
-100;
2171 if (t
> 1 && numComplete
>= target
&& numMastered
>= targetM
)
2173 LevelSave
* l
= progress
.GetLevel(x
, true);
2178 renderer(p
, true).Add(new LevelSelectRender(p
, 5, GetLevelState(p
)>>8), time
+0.01);
2179 renderer().Add(new ExplosionRender(p
, 0), time
+ 0.6);
2180 renderer().Add(new ExplosionRender(p
, 1), time
+ 1.1);
2181 renderer(p
, true).Add(new LevelSelectRender(p
, -1, GetLevelState(p
)>>8), time
+ 1.1);
2186 if (type
==0) // Init & count levels
2190 int t
= GetLevelState(p
);
2191 int unlock
= progress
.GetLevel(x
, true)->unlocked
;
2192 LevelSelectRender
* lsr
= new LevelSelectRender( p
, unlock
? -1 : (t
>>8) ? 5 : 1, t
>>8 );
2193 if ((t
>>8) && p
.x
> mapRightBound
) mapRightBound
= p
.x
;
2194 #ifdef MAP_EDIT_HACKS
2195 lsr
->magic
= -atoi(x
+1);
2196 SetTile(p
, LIFT_DOWN
, true, false);
2198 SetTile(p
, EMPTY
, true, false);
2200 renderer(p
,true).Add(lsr
, time
);
2204 //printf("Level: %s\n", x);
2206 int t
= GetLevelState(p
);
2208 if (t
&& !GetItem(p
))
2215 currentLevelInfo
= 0;
2219 LevelSave
* l
= progress
.GetLevel(x
, true);
2224 renderer(p
, true).Add(new LevelSelectRender(p
, -1, 0), time
+0.01);
2225 renderer().Add(new ExplosionRender(p
, 0), time
+ 0.6);
2226 renderer(p
, true).Add(new LevelSelectRender(p
, t
& 0xff, t
>>8), time
+ 0.6);
2230 if (p
.x
> mapRightBound
) mapRightBound
= p
.x
;
2237 LevelSelectRender
* lsr
= new LevelSelectRender( p
, t
& 0xff, t
>>8 );
2239 #ifdef MAP_EDIT_HACKS
2241 int t
= GetAutoTile(x
, true);
2242 int v
= GetAutoTile(x
, false);
2243 if (MAP_EDIT_HACKS_DISPLAY_UNLOCK
)
2246 lsr
->magic
= GetPar(x
, true);
2248 SetTile(p
, t
, true, false);
2250 SetTile(p
, EMPTY
, true, false);
2253 renderer(p
,true).Add(lsr
, time
);
2258 if (type
==1 && x
[0]!='_') // Clicked on
2260 int t
= GetLevelState(p
);
2268 void SetItem(Pos
const & p
, int t
, bool updateRenderer
=true, bool undoBuffer
=true)
2270 if (p
.x
<0 || p
.x
>MAP_SIZE
)
2272 if (p
.y
<0 || p
.y
>MAP_SIZE
)
2274 if (map_item
[p
.x
][p
.y
] == t
)
2278 undo
[numUndo
].Add(Undo::TileChange(p
,GetTile(p
),GetItem(p
)));
2280 map_item
[p
.x
][p
.y
] = t
;
2283 renderer(p
,true).Add(new ItemRender(t
, GetTile(p
)==EMPTY
, p
), time
);
2286 Tile
GetItem(Pos
const & p
)
2288 if (p
.x
<0 || p
.x
>=MAP_SIZE
)
2290 if (p
.y
<0 || p
.y
>=MAP_SIZE
)
2292 return map_item
[p
.x
][p
.y
];
2295 void LoadSaveProgress(bool save
)
2297 FILE* f
= file_open(currentSlot
, save
? "wb" : "rb");
2300 progress
.LoadSave(f
, save
);
2311 LoadSaveProgress(false);
2315 LoadSaveProgress(true);
2318 SDL_Surface
* Load(const char * bmp
, bool colourKey
=true)
2320 typedef unsigned int uint32
;
2323 SDL_Surface
* g
= 0;
2326 if (strstr(bmp
, ".bmp"))
2328 g
= SDL_LoadBMP(bmp
);
2332 strcpy(strstr(out
, ".bmp"), ".dat");
2334 // SDL_PixelFormat p;
2336 // SDL_Surface* tmp = SDL_ConvertSurface(g, &p, SDL_SWSURFACE);
2338 short w
=g
->w
, h
=g
->h
;
2339 char* buf
= (char*) g
->pixels
;
2342 while (IsEmpty(g
, w
-1, 0, 1, h
) && w
>1)
2344 while (IsEmpty(g
, 0, h
-1, w
, 1) && h
>1)
2348 FILE* f
= file_open(out
, "wb");
2349 fwrite(&w
, sizeof(w
), 1, f
);
2350 fwrite(&h
, sizeof(h
), 1, f
);
2352 uint32 mask
= IMAGE_DAT_OR_MASK
;
2353 for (int i
=0; i
<(int)w
*h
; )
2355 uint32 c
= (*(uint32
*)&buf
[(i
%w
)*3 + (i
/w
)*g
->pitch
] | mask
);
2357 while (i
< (int)w
*h
&& c
== (*(uint32
*)&buf
[(i
%w
)*3 + (i
/w
)*g
->pitch
] | mask
))
2366 fwrite(&c
, sizeof(c
), 1, f
);
2369 fwrite(&i0
, sizeof(i0
), 1, f
);
2379 FILE* f
= file_open(bmp
, "rb");
2380 if (!f
) FATAL("Unable to open file", bmp
);
2383 fread(&w
, sizeof(w
), 1, f
);
2384 fread(&h
, sizeof(h
), 1, f
);
2385 if (w
>1500 || h
>1500) FATAL("Invalid file", bmp
);
2387 tmp
= new uint32
[(int)w
*h
];
2391 for (int p
=0; p
<(int)w
*h
; p
++)
2397 fread(&c
, sizeof(c
), 1, f
);
2400 fread(&cnt
, sizeof(cnt
), 1, f
);
2402 tmp
[p
] = c
| 0xff000000;
2405 g
= SDL_CreateRGBSurfaceFrom(tmp
, w
, h
, 32, w
*4,
2414 if (!g
) FATAL("Unable to create SDL surface");
2416 SDL_SetColorKey(g
, SDL_SRCCOLORKEY
, SDL_MapRGB(g
->format
, WATER_COLOUR
));
2417 SDL_Surface
* out
= SDL_DisplayFormat(g
);
2420 if (!out
) FATAL("Unable to create SDL surface (SDL_DisplayFormat)");
2424 #ifdef USE_LEVEL_PACKFILE
2425 PackFile1 levelFiles
;
2429 SDL_WM_SetCaption(GAMENAME
, 0);
2433 #ifdef USE_LEVEL_PACKFILE
2434 FILE* f
= file_open("levels.dat", "rb");
2436 FATAL("Unable to open file", "levels.dat");
2446 currentLevelInfo
= 0;
2453 memset(map
, 0, sizeof(map
));
2454 memset(map_item
, 0, sizeof(map_item
));
2455 memset(special
, 0, sizeof(special
));
2459 // player = Pos(1,11);
2469 progress
.GetLevel(STARTING_LEVEL
, true)->unlocked
= 1;
2470 if (!progress
.GetLevel(STARTING_LEVEL
, true)->Completed())
2472 LoadSave(STARTING_LEVEL
, false);
2478 LoadSave(mapname
, false);
2483 if (!activeMenu
|| activeMenu
->renderBG
)
2485 SDL_Rect src
= {0,0,screen
->w
,screen
->h
};
2486 SDL_Rect dst
= {0,0,screen
->w
,screen
->h
};
2489 int boundW
= mapBG
->w
;
2491 boundW
= MIN(boundW
, (mapRightBound
+4) * TILE_W2
- TILE_W1
);
2493 src
.x
= scrollX
- initScrollX
;
2494 if (src
.x
+src
.w
> boundW
)
2496 int diff
= src
.x
+src
.w
- boundW
;
2507 //scrollY = initScrollY;
2510 mapScrollX
= scrollX
;
2512 SDL_BlitSurface(mapBG
, &src
, screen
, &dst
);
2515 SDL_BlitSurface(gradient
, &src
, screen
, &dst
);
2517 renderer
.Render(time
, true);
2519 if (!hintsDone
&& !isFadeRendering
)
2526 SDL_Rect src
= {0,SCREEN_H
-1,SCREEN_W
,1};
2527 SDL_Rect dst
= {0,SCREEN_H
-1,SCREEN_W
,1};
2528 for (int i
=0; i
<SCREEN_H
; i
++)
2531 dst
.y
= src
.y
= SCREEN_H
-1-i
;
2535 const bool farView
= false;
2538 src
.x
+= (int)( sin(i
*0.9 + time
*3.7) * sin(i
*0.3 + time
*0.7)*4 );
2539 src
.y
+= (int)( (sin(i
*0.3 - time
*2.2) * sin(i
*0.48 + time
*0.47) - 1) * 1.99 );
2543 src
.x
+= (int)( sin(i
*0.5 + time
*6.2) * sin(i
*0.3 + time
*1.05) * 5 );
2544 src
.y
+= (int)( (sin(i
*0.4 - time
*4.3) * sin(i
*0.08 + time
*1.9) - 1) * 2.5 );
2546 SDL_BlitSurface(screen
, &src
, screen
, &dst
);
2551 SDL_BlitSurface(mapBG2
, &src
, screen
, &dst
);
2553 renderer
.Render(time
, false);
2555 if (!isRenderMap
&& !isMap
&& !isFadeRendering
)
2557 int v
[3] = {player_items
[0], player_items
[1], player_score
};
2558 if (numUndo
> 1 && time
< undo
[numUndo
-2].endTime
)
2561 while (i
>1 && time
<undo
[i
-1].time
)
2563 v
[0] = undo
[i
].numItems
[0];
2564 v
[1] = undo
[i
].numItems
[1];
2565 v
[2] = undo
[i
].score
;
2567 if (numUndo
>1 && time
< undo
[0].time
)
2570 /* TRANSLATORS: Anti-Ice are pickups, which turn ice plates into solid
2571 plates once you step on them. Each pickup changes one ice plate */
2572 Print(0,0,_("Anti-Ice: %d"), v
[0]);
2573 Print(0,FONT_SPACING
,_("Jumps: %d"), v
[1]);
2574 Print(0,FONT_SPACING
*2,_("Score: %d (%d)"), v
[2], player_score
);
2575 /* TRANSLATORS: Par is similar to golf, a pre defined score which you
2576 can attempt to beat */
2577 Print(0,FONT_SPACING
*3,_("Par: %d"), levelPar
);
2578 Print(0,FONT_SPACING
*4,_("Diff: %d"), levelDiff
);
2581 Print(0, SCREEN_H
-FONT_SPACING
, _(" Par: %d Current: %d"), levelPar
, v
[2]);
2584 Print(0,0,_(" Anti-Ice: %d"), v
[0]);
2586 Print(0,0,_(" Jumps: %d"), v
[1]);
2589 if (isRenderMap
&& isMap
&& !isFadeRendering
)
2592 Print(0,0,_("Points: %d"), numComplete
+numMastered
);
2593 Print(0,FONT_SPACING
,_("Discovered: %d%% (%d/%d)"), numLevelsFound
*100/numLevels
, numLevelsFound
, numLevels
);
2594 Print(0,FONT_SPACING
*2,_("Complete: %d%% (%d)"), numComplete
*100/numLevels
, numComplete
);
2595 Print(0,FONT_SPACING
*3,_("Mastered: %d%% (%d)"), numMastered
*100/numLevels
, numMastered
);
2597 if (numComplete
==numLevels
&& progress
.general
.endSequence
>0)
2598 Print(0, SCREEN_H
-FONT_SPACING
, _(" %d%% Mastered"), numMastered
*100/numLevels
);
2600 Print(0, SCREEN_H
-FONT_SPACING
, _(" %d%% Complete"), numComplete
*100/numLevels
);
2602 if (numMastered
>= numLevels
&& progress
.general
.endSequence
< 2)
2604 progress
.general
.endSequence
= 2;
2605 LoadSaveProgress(true);
2607 new Fader(-1, -7, 0.3);
2609 if (numComplete
>= numLevels
&& progress
.general
.endSequence
< 1)
2611 progress
.general
.endSequence
= 1;
2612 LoadSaveProgress(true);
2614 new Fader(-1, -5, 0.3);
2618 if ((currentLevelInfo
|| noMouse
) && isMap
&& isRenderMap
&& !activeMenu
&& isFadeRendering
<=0)
2625 int pad
= SCREEN_W
/80;
2626 // SDL_Rect src = {0, 0, uiGraphics->w, uiGraphics->h};
2627 SDL_Rect dst
= {pad
, SCREEN_H
-TILE_H2
-pad
, 0, 0};
2628 // dst.x = p.getScreenX() + TILE_W3/2 - scrollX;
2629 // dst.y = p.getScreenY() - src.h/2 - scrollY;
2630 dst
.x
= p
.getScreenX() - scrollX
;
2631 dst
.y
= p
.getScreenY() - scrollY
- FONT_SPACING
*3 - FONT_SPACING
/2;
2632 // if (dst.x > SCREEN_W*2/3) dst.x -= TILE_W3 + src.w;
2633 // if (dst.y+src.h > screen->h-pad) dst.y = screen->h-pad - src.h;
2635 RenderTile(false, 0, p
.getScreenX(), p
.getScreenY());
2636 // SDL_BlitSurface(uiGraphics, &src, screen, &dst);
2638 // dst.x += src.w/2;
2640 if (currentLevelInfo
)
2644 PrintC(true, dst
.x
, dst
.y
- FONT_SPACING
/4, currentLevelInfo
->name
);
2646 if (currentLevelInfo
->file
[0]!=0)
2648 if (player_score
> 0)
2650 if (progress
.general
.scoringOn
)
2652 PrintC(false, dst
.x
, dst
.y
+ FONT_SPACING
*4 - FONT_SPACING
/4, _("Best:% 3d"), player_score
);
2653 PrintC(false, dst
.x
, dst
.y
+ FONT_SPACING
*5 - FONT_SPACING
/4, _("Par:% 3d"), levelPar
);
2656 PrintC(false, dst
.x
, dst
.y
+ FONT_SPACING
*4 - FONT_SPACING
/4, _("Completed"), player_score
);
2659 PrintC(false, dst
.x
, dst
.y
+ FONT_SPACING
*4 - FONT_SPACING
/4, _("Incomplete"), player_score
);
2665 if (win
&& numUndo
> 0 && time
> undo
[numUndo
-1].endTime
+ 2)
2667 if (currentFile
[0] && winFinal
==0)
2669 LevelSave
* l
= progress
.GetLevel(currentFile
, true);
2671 new WinLoseScreen(true, player_score
, showScoring
? levelPar
: 0, l
&& showScoring
&& l
->Completed() ? l
->GetScore() : 0);
2673 if (l
->IsNewCompletionBetter(player_score
))
2675 l
->SetScore(player_score
);
2677 l
->SetSolution(numUndo
);
2679 for (int i
=0; i
<numUndo
; i
++)
2680 l
->SetSolutionStep(i
, undo
[i
].playerMovement
);
2691 // Move up "level complete" writing so it doesn't feel like everything's ground to a halt...
2692 if (win
&& numUndo
> 0 && time
> undo
[numUndo
-1].endTime
&& !winFinal
)
2694 double t
= (time
- undo
[numUndo
-1].endTime
) / 2;
2698 int y
= SCREEN_H
/3 - FONT_SPACING
+ 1;
2699 y
= SCREEN_H
+ int((y
-SCREEN_H
)*t
);
2700 PrintC(true, SCREEN_W
/2, y
, _("Level Complete!"));
2705 activeMenu
->Render();
2712 RenderTile(false, editTile
, mousex
+scrollX
, mousey
+scrollY
);
2719 return tileCount
[t
];
2721 int Swap(Tile t
, Tile t2
)
2723 const int num
= Count(t
) + Count(t2
);
2724 if (t
==t2
|| num
==0)
2725 return Count(t
); // Nothing to do...
2728 for (int x
=0; x
<MAP_SIZE
; x
++)
2729 for (int y
=0; y
<MAP_SIZE
; y
++)
2731 if (GetTile(Pos(x
,y
))==t
)
2734 SetTile(Pos(x
,y
), t2
);
2736 else if (GetTile(Pos(x
,y
))==t2
)
2739 SetTile(Pos(x
,y
), t
);
2746 int Replace(Tile t
, Tile t2
)
2748 const int num
= Count(t
);
2749 if (t
==t2
|| num
==0)
2750 return num
; // Nothing to do...
2753 for (int x
=0; x
<MAP_SIZE
; x
++)
2754 for (int y
=0; y
<MAP_SIZE
; y
++)
2761 SetTile(p
, t2
, false);
2763 if (t
==COLLAPSE_DOOR
&& t2
==COLLAPSABLE
)
2764 renderer(p
).Add(new BuildRender(p
, -1, 1, 1), time
+ (rand() & 255)*0.001);
2765 else if (t
==COLLAPSE_DOOR2
&& t2
==COLLAPSABLE2
)
2766 renderer(p
).Add(new BuildRender(p
, -1, 1, 1, 1), time
+ (rand() & 255)*0.001);
2787 void UpdateCursor(Pos
const & s
)
2790 if (s
.x
!=_s
.x
|| s
.y
!=_s
.y
)
2794 char* sp
= GetSpecial(s
);
2801 currentLevelInfo
= 0;
2802 levelPar
= player_score
= -1;
2803 if (GetLevelState(s
)>=2)
2805 LevelSave
* l
= progress
.GetLevel(sp
, true);
2808 currentLevelInfo
= GetLevelInfo(sp
);
2809 levelPar
= GetPar(sp
);
2810 player_score
= l
->GetScore();
2816 sprintf(tmp
, _("Special(%d,%d): %s (%d)"), s
.x
, s
.y
, sp
? sp
: _("<None>"), GetPar(sp
));
2817 SDL_WM_SetCaption(tmp
, NULL
);
2820 else if (currentFile
[0])
2823 SDL_WM_SetCaption(currentFile
, NULL
);
2826 currentLevelInfo
= 0;
2831 virtual void Mouse(int x
, int y
, int dx
, int dy
, int button_pressed
, int button_released
, int button_held
)
2835 activeMenu
->Mouse(x
,y
,dx
,dy
,button_pressed
,button_released
,button_held
);
2839 if (isFadeRendering
)
2844 if (button_pressed
==2 || button_pressed
==4 && isMap
)
2846 KeyPressed(SDLK_ESCAPE
, 0);
2847 keyState
[SDLK_ESCAPE
] = 0;
2855 Pos s
= Pos::GetFromWorld(x
,y
);
2856 if (tileSolid
[GetTile(Pos::GetFromWorld(x
,y
+TILE_HUP
))] == 1)
2857 s
= Pos::GetFromWorld(x
,y
+TILE_HUP
);
2864 if (button_held
& ~button_pressed
& 4)
2873 if (isMap
&& (button_pressed
& 1))
2875 ActivateSpecial(s
, 1);
2878 if (!isMap
&& win
&& winFinal
)
2880 if (button_pressed
& 1)
2888 if((button_pressed
& 1) || (button_held
& 1) && (numUndo
==0 || time
>=undo
[numUndo
-1].endTime
))
2890 if(s
.x
==player
.x
&& s
.y
==player
.y
)
2892 // Don't activate jump powerup without a new click
2893 if (button_pressed
& 1)
2896 else if(s
.x
==player
.x
&& s
.y
<player
.y
)
2898 else if(s
.x
==player
.x
&& s
.y
>player
.y
)
2900 else if(s
.y
==player
.y
&& s
.x
<player
.x
)
2902 else if(s
.y
==player
.y
&& s
.x
>player
.x
)
2904 else if(s
.y
+s
.x
==player
.y
+player
.x
&& s
.x
>player
.x
)
2906 else if(s
.y
+s
.x
==player
.y
+player
.x
&& s
.x
<player
.x
)
2909 if ((button_pressed
& 4) || (button_held
& 4) && (undoTime
< 0))
2916 if (!button_pressed
&& !button_held
)
2919 if (button_pressed
==1)
2921 editTile
= GetItem(s
)==1 ? -3 : GetItem(s
)==2 ? -2 : -1;
2923 if (button_held
==1 || button_pressed
==1)
2927 SetTile(s
, editTile
, true, false);
2929 SetItem(s
, editTile
==-2 ? 0 : editTile
==-1 ? 1 : 2, true, false);
2932 if (button_pressed
==2)
2934 editTile
= GetTile(s
);
2937 if (button_pressed
==8)
2939 editTile
=editTile
-1;
2940 if (editTile
<=0) editTile
=NumTileTypes
-1;
2943 if (button_pressed
==16)
2945 editTile
=editTile
+1;
2946 if (editTile
<=0) editTile
=1;
2947 if (editTile
==NumTileTypes
) editTile
=0;
2950 if (button_pressed
==64)
2955 renderer
.player
.Reset(-1);
2956 renderer
.player
.Add(new PlayerRender(player
, GetHeight(player
), dead
), 0);
2959 if (button_pressed
==256)
2961 char* fn
= LoadSaveDialog(false, true, _("Select level"));
2964 char * l
= strstr(fn
, "Levels");
2967 FILE * f
= file_open(l
,"rb");
2972 else if (l
[6]!=0 && l
[7]=='_')
2975 UpdateCursor(Pos(-1,-1));
2978 if (button_pressed
==512)
2980 SetSpecial(s
, NULL
);
2981 UpdateCursor(Pos(-1,-1));
2983 if (button_pressed
==1024)
2985 static char x
[1000] = "";
2986 if (!(s
.x
<0 || s
.x
>=MAP_SIZE
|| s
.y
<0 || s
.y
>=MAP_SIZE
))
2991 strcpy(x
, GetSpecial(s
));
2994 SetSpecial(s
, tmp
[0] ? tmp
: 0);
2996 SetTile(s
, EMPTY
, true, false);
3000 if (button_pressed
==32)
3002 editTile
= editTile
<0 ? 1 : -1;
3007 void CheckFinished()
3010 if (Count(COLLAPSABLE
)==0)
3012 if (Replace(COLLAPSE_DOOR
, COLLAPSABLE
) == 0)
3016 Replace(SWITCH
, NORMAL
);
3021 if (Count(COLLAPSABLE2
)==0)
3022 if (Replace(COLLAPSE_DOOR2
, COLLAPSABLE2
))
3028 bool Collide(Pos p
, bool high
)
3030 Tile t
= GetTile(p
);
3035 return tileSolid
[t
]==1;
3042 if (numUndo
==0) return;
3044 UndoDone(); // Complete previous undo...
3048 if (time
> undo
[numUndo
].endTime
)
3049 time
= undo
[numUndo
].endTime
;
3050 undoTime
= undo
[numUndo
].time
;
3052 undo
[numUndo
].Restore(this);
3058 renderer
.Reset(undoTime
);
3062 void ScoreDestroy(Pos p
)
3064 Tile t
= GetTile(p
);
3065 if (t
==COLLAPSABLE
|| t
==COLLAPSE_DOOR
)
3067 else if (t
!= EMPTY
)
3073 bool LaserTile(Pos p
, int mask
, double fireTime
)
3075 if (&renderer(p
) == &renderer(Pos(-1,-1)))
3077 //if (!renderer.Visible(p))
3081 if (time
<= renderer(p
).GetLastTime())
3082 if (fireTime
< renderer(p
).GetLastTime())
3084 renderer(p
).Add(tr
= new TileRender(GetTile(p
), p
, mask
), fireTime
);
3085 ((TileRender
*)renderer(p
).GetStage(time
+10/*HACKY!*/))->special
|= mask
;
3089 tr
= new TileRender(GetTile(p
), p
, mask
| ((TileRender
*)renderer(p
).GetStage(time
+10/*HACKY!*/))->special
);
3090 renderer(p
).Add(tr
, fireTime
);
3093 renderer(p
).Add(tr
= new TileRender(GetTile(p
), p
, mask
), fireTime
);
3097 tr
->specialDuration
= time
+ LASER_LINE_TIME
- fireTime
+ LASER_FADE_TIME
;
3101 void FireGun(Pos newpos
, Dir d
, bool recurse
, double fireTime
)
3103 static Pos hits
[100];
3104 static Dir hitDir
[100];
3105 static int numHits
=0;
3109 double starttime
= fireTime
;
3110 for (Dir fd
=((d
<0)?0:d
); fd
<((d
<0)?MAX_DIR
:d
+1); fd
++)
3112 fireTime
= starttime
;
3113 // starttime += 0.03;
3115 Pos p
= newpos
+ fd
;
3117 for (range
; range
<MAP_SIZE
; range
++, p
=p
+fd
)
3119 Tile t
= GetTile(p
);
3120 if (tileSolid
[t
]!=-1)
3123 renderer(p
).Add(new TileRender(tileSolid
[t
]==1 ? TILE_WHITE_WALL
: TILE_WHITE_TILE
, p
), fireTime
+0.1);
3126 for (i
=0; i
<numHits
; i
++)
3130 t
==TRAP
&& (hitDir
[i
]&(1<<fd
))==0
3135 if (i
>= sizeof(hits
)/sizeof(hits
[0]))
3137 hitDir
[i
] = 1 << fd
;
3143 hitDir
[i
] |= 1 << fd
;
3148 1<<((fd
+2) % MAX_DIR
)
3149 | 1<<((fd
+MAX_DIR
-2) % MAX_DIR
);
3151 if (LaserTile(p
, dirmask
, fireTime
))
3152 fireTime
+= (time
+LASER_LINE_TIME
- fireTime
) / 40;
3153 // fireTime += LASER_SEGMENT_TIME;
3155 FireGun(p
, (fd
+1) % MAX_DIR
, true, fireTime
);
3156 FireGun(p
, (fd
+MAX_DIR
-1) % MAX_DIR
, true, fireTime
);
3163 LaserTile(p
, 1<<(fd
%3), fireTime
);
3165 fireTime
+= (time
+LASER_LINE_TIME
- fireTime
) / 40;
3166 // fireTime += LASER_SEGMENT_TIME;
3170 // renderer().Add(new LaserRender(newpos, fd, range), time);
3175 double _time
= time
;
3176 time
+= LASER_LINE_TIME
;
3177 for (int i
=0; i
<numHits
; i
++)
3180 Tile t
= GetTile(p
);
3187 renderer(p
).Add(new ExplosionRender(p
, t
==GUN
), time
);
3188 //renderer(p).Add(new TileRender(EMPTY, p), time+2);
3189 SetTile(p
, EMPTY
, false);
3192 renderer(p
,true).Add(new ItemRender(GetItem(p
), 1, p
), time
);
3196 for (Dir j
=0; j
<MAX_DIR
; j
++)
3198 if (GetTile(p
+j
)!=EMPTY
)
3200 renderer(p
+j
).Add(new TileRender(tileSolid
[GetTile(p
+j
)]==1 ? TILE_WHITE_WALL
: TILE_WHITE_TILE
, p
+j
), time
+0.05);
3201 renderer(p
+j
).Add(new ExplosionRender(p
+j
), time
+0.2);
3204 renderer(p
+j
,true).Add(new ItemRender(GetItem(p
+j
), 1, p
), time
);
3206 //renderer(p+j).Add(new TileRender(EMPTY, p+j), time+2.2);
3208 ScoreDestroy(p
+ j
);
3209 SetTile(p
+ j
, EMPTY
, false);
3214 time
+= MAX(LASER_FADE_TIME
, 0.15);
3219 int GetLastPlayerRot()
3221 RenderStage
* rs
= renderer
.player
.GetStage(-1);
3223 return ((PlayerRender
*)rs
)->r
;
3227 if (dead
|| win
|| isMap
)
3233 // Jump forwards in time to last move finishing
3234 if (numUndo
> 0 && time
< undo
[numUndo
-1].endTime
)
3235 time
= undo
[numUndo
-1].endTime
;
3237 double realTime
= time
;
3238 double endAnimTime
= time
;
3239 bool high
= (tileSolid
[GetTile(player
)] == 1);
3240 Pos playerStartPos
= player
;
3241 Pos oldpos
= player
;
3242 int oldPlayerHeight
= GetHeight(oldpos
);
3243 Pos newpos
= player
+ d
;
3245 int playerRot
= GetLastPlayerRot();
3246 if (d
!=-1 && d
!=playerRot
)
3248 while (d
!=playerRot
)
3250 if ((d
+6-playerRot
) % MAX_DIR
< MAX_DIR
/2)
3251 playerRot
= (playerRot
+1) % MAX_DIR
;
3253 playerRot
= (playerRot
+MAX_DIR
-1) % MAX_DIR
;
3257 if (GetTile(oldpos
) == FLOATING_BALL
)
3259 TileRender
* t
= new TileRender(FLOATING_BALL
, oldpos
);
3260 t
->special
= playerRot
+ 256;
3261 renderer(oldpos
).Add(t
, time
);
3263 renderer
.player
.Add(new PlayerRender(playerRot
, Pos(-20,-20), oldPlayerHeight
, Pos(-20,-20), oldPlayerHeight
, dead
), time
);
3267 PlayerRender
*p
= new PlayerRender(playerRot
, player
, oldPlayerHeight
, player
, oldPlayerHeight
, dead
);
3269 renderer
.player
.Add(p
, time
);
3276 if (d
<0 && player_items
[1]==0)
3281 if (tileSolid
[GetTile(newpos
)] == -1)
3286 if (Collide(newpos
, high
))
3293 // Don't change any real state before this point!
3294 if (numUndo
>= MAX_UNDO
)
3297 for(int i
=0; i
<MAX_UNDO
-1; i
++)
3298 undo
[i
] = undo
[i
+1];
3300 undo
[numUndo
].New(d
, player
, player_items
, time
, player_score
);
3307 int old_score
=player_score
;
3309 double time0
= time
;
3310 time
+= 0.15; //Time for leave-tile fx
3312 switch (GetTile(oldpos
))
3315 SetTile(oldpos
, EMPTY
);
3316 renderer(oldpos
).Add(new DisintegrateRender(oldpos
), time
);
3321 // Don't need to CheckFinished - can't be collapse doors around
3322 // unless there're still collapsable tiles around.
3323 SetTile(oldpos
, EMPTY
);
3324 renderer(oldpos
).Add(new DisintegrateRender(oldpos
, 1), time
);
3328 SetTile(oldpos
, COLLAPSABLE
, false);
3329 renderer(oldpos
).Add(new DisintegrateRender(oldpos
, 0, 1), time
);
3334 case COLLAPSE_DOOR2
:
3335 SetTile(oldpos
, COLLAPSE_DOOR
, false);
3336 renderer(oldpos
).Add(new DisintegrateRender(oldpos
, 1, 1), time
);
3341 SetTile(oldpos
, COLLAPSABLE2
);
3345 time
= time0
; //End of leave-tile fx
3347 int retry_pos_count
=0;
3351 if (GetItem(newpos
)==1)
3353 renderer(newpos
, true).Add(new ItemCollectRender(GetItem(newpos
), newpos
), time
+ JUMP_TIME
/2);
3354 SetItem(newpos
, 0, false);
3357 if (GetItem(newpos
)==2)
3359 renderer(newpos
, true).Add(new ItemCollectRender(GetItem(newpos
), newpos
), time
+ JUMP_TIME
/2);
3360 SetItem(newpos
, 0, false);
3364 if (GetTile(player
) == FLOATING_BALL
)
3366 TileRender
* t
= new TileRender(FLOATING_BALL
, player
);
3368 renderer(oldpos
).Add(t
, time
);
3371 PlayerRender
*p
= new PlayerRender(playerRot
, player
, oldPlayerHeight
, newpos
, GetHeight(newpos
), dead
);
3373 // alternate leg (hacky!)
3381 if (retry_pos_count
!=0 && GetTile(player
)==TRAP
)
3387 p
->speed
= JUMP_TIME
* 1.5;
3388 renderer
.player
.Add(p
, time
);
3389 endAnimTime
= MAX(endAnimTime
, time
+ p
->speed
+0.001);
3394 switch (GetTile(newpos
))
3397 renderer(newpos
).Add(new TileRender(TILE_GREEN_CRACKED
, newpos
), time
);
3400 renderer(newpos
).Add(new TileRender(TILE_GREEN_CRACKED_WALL
, newpos
), time
);
3403 renderer(newpos
).Add(new TileRender(TILE_BLUE_CRACKED
, newpos
), time
);
3405 case COLLAPSE_DOOR2
:
3406 renderer(newpos
).Add(new TileRender(TILE_BLUE_CRACKED_WALL
, newpos
), time
);
3415 double pretime
= time
;
3418 for (Dir fd
=0; fd
<MAX_DIR
; fd
++)
3420 Tile t2
= GetTile(newpos
+ fd
);
3424 SetTile(newpos
+fd
, COLLAPSABLE
, false);
3425 renderer(newpos
+fd
).Add(new BuildRender(newpos
+fd
, fd
, 0), time
);
3427 else if (t2
==COLLAPSABLE
)
3430 SetTile(newpos
+fd
, COLLAPSE_DOOR
, false);
3431 renderer(newpos
+fd
).Add(new BuildRender(newpos
+fd
, fd
, 1), time
);
3434 if (done
) time
+= BUILD_TIME
;
3435 else time
= pretime
;
3437 endAnimTime
= MAX(endAnimTime
, time
+ 0.1);
3442 Swap(COLLAPSE_DOOR
, COLLAPSABLE
);
3448 renderer
.player
.Add(new PlayerRender(playerRot
, Pos(-30,-30), 0, Pos(-30,-30), 0, dead
), time
);
3449 while (tileSolid
[GetTile(newpos
+d
)]==-1)
3453 if (!renderer
.Visible(newpos
+d
))
3455 TileRender
* r
= new TileRender(FLOATING_BALL
, newpos
);
3457 renderer(newpos
).Add(r
, time
);
3459 PlayerRender
* pr
= new PlayerRender(playerRot
, newpos
, 0, newpos
, 0, dead
);
3460 pr
->speed
= JUMP_TIME
*1;
3461 renderer
.player
.Add(pr
, time
);
3469 newpos
= oldpos
+ d
;
3471 SetTile(oldpos
, EMPTY
, false);
3472 SetTile(newpos
, FLOATING_BALL
, false);
3474 renderer(oldpos
).Add(new TileRotateRender(FLOATING_BALL
, oldpos
, d
, 2), time
);
3475 renderer(oldpos
).Add(new TileRender(EMPTY
, oldpos
), time
+ ROTATION_TIME
/2);
3476 renderer(newpos
).Add(new TileRotateRender(FLOATING_BALL
, newpos
, (d
+3)%MAX_DIR
, 3), time
+ ROTATION_TIME
/2);
3478 // PlayerRender *p = new PlayerRender(playerRot, oldpos, 0, newpos, 0, dead);
3479 // p->speed = ROTATION_TIME*0.9;
3480 // renderer.player.Add(p, time);
3482 endAnimTime
= MAX(endAnimTime
, time
+ ROTATION_TIME
+ ROTATION_TIME
/2);
3483 time
+= ROTATION_TIME
;
3486 // renderer.player.Add(new PlayerRender(playerRot, player, 0, player, 0, 0), time);
3492 TileRender
* r
= new TileRender(FLOATING_BALL
, newpos
);
3493 r
->special
= playerRot
+ 256;
3494 renderer(newpos
).Add(r
, time
);
3502 SetTile(newpos
, GetTile(newpos
)==LIFT_UP
? LIFT_DOWN
: LIFT_UP
, false);
3503 renderer(newpos
).Add(new TileRender(GetTile(newpos
), newpos
, 1), time
);
3505 PlayerRender
*p
= new PlayerRender(playerRot
, newpos
, 1-GetHeight(newpos
), newpos
, GetHeight(newpos
), dead
);
3506 renderer
.player
.Add(p
, time
);
3507 endAnimTime
= MAX(endAnimTime
, time
+ JUMP_TIME
);
3515 if (Collide(newpos
+ d
, high
))
3517 if (Collide((newpos
+ d
) + d
, high
) == 1)
3518 newpos
= (newpos
+ d
);
3520 newpos
= (newpos
+ d
) + d
;
3521 if (tileSolid
[GetTile(newpos
)] == -1)
3528 for (Dir d
=0; d
<MAX_DIR
; d
++)
3530 Tile tmp
= GetTile(newpos
+ d
);
3531 renderer(newpos
+ d
).Add(new TileRotateRender(tmp
, newpos
+ d
, (d
+2)%MAX_DIR
, false), time
);
3533 Tile tmp
= GetTile(newpos
+ Dir(MAX_DIR
-1));
3534 for (Dir d
=0; d
<MAX_DIR
; d
++)
3536 Tile t2
= GetTile(newpos
+ d
);
3537 SetTile(newpos
+ d
, tmp
, false);
3538 renderer(newpos
+ d
).Add(new TileRotateRender(tmp
, newpos
+ d
, (d
+4)%MAX_DIR
, true), time
+ ROTATION_TIME
/2);
3539 if (GetItem(newpos
+ d
))
3540 renderer(newpos
+ d
,true).Add(new ItemRender(GetItem(newpos
+ d
), GetTile(newpos
+ d
)==EMPTY
, newpos
+d
), time
+ ROTATION_TIME
/2);
3544 endAnimTime
= MAX(endAnimTime
, time
+ROTATION_TIME
);
3545 // renderer(newpos).Add(new TileRotateRender(SPINNER, Dir(0), 0), time);
3553 if (player_items
[0]==0)
3555 if (tileSolid
[GetTile(newpos
+ d
)] == 1)
3557 newpos
= newpos
+ d
;
3558 if (tileSolid
[GetTile(newpos
)] == -1)
3565 SetTile(newpos
, COLLAPSABLE3
);
3573 FireGun(newpos
, d
, false, time
);
3575 endAnimTime
= MAX(endAnimTime
, time
);
3577 if (GetTile(newpos
)==EMPTY
)
3579 PlayerRender
* pr
= new PlayerRender(playerRot
, player
, 0, player
, 0, dead
);
3580 pr
->speed
= JUMP_TIME
*1;
3581 renderer
.player
.Add(pr
, time
);
3591 for (Dir fd=((d<0)?0:d); fd<((d<0)?MAX_DIR:d+1); fd++)
3593 Pos p = newpos + fd;
3595 for (range; range<MAP_SIZE; range++, p=p+fd)
3597 Tile t = GetTile(p);
3598 if (tileSolid[t]!=-1)
3600 hits[numHits++] = p;
3605 renderer().Add(new LaserRender(newpos, fd, range), time);
3608 double _time = time;
3610 for (int i=0; i<numHits; i++)
3613 Tile t = GetTile(p);
3615 renderer().Add(new ExplosionRender(p), time);
3621 for (Dir j=0; j<MAX_DIR; j++)
3623 ScoreDestroy(p + j);
3624 SetTile(p + j, EMPTY);
3626 if (GetTile(newpos)==EMPTY)
3630 endAnimTime = MAX(endAnimTime, time);
3640 endAnimTime
= MAX(endAnimTime
, time
);
3646 PlayerRender
* pr
= new PlayerRender(player
, 0, dead
);
3647 pr
->speed
= 0; // Don't sit around before disappearing!
3648 renderer
.player
.Add(pr
, time
);
3650 // If the tile we're drowning on isn't visible, give the ownership of the splash effect to the player, rather than a tile.
3651 if (renderer
.Visible(player
))
3652 renderer(player
).Add(new ExplosionRender(player
, 0, 1), time
);
3654 renderer
.player
.Add(new ExplosionRender(player
, 0, 1), time
);
3656 endAnimTime
= MAX(endAnimTime
, time
+2);
3663 undo
[numUndo
].endTime
= endAnimTime
;
3668 void Update(double timedelta
)
3675 activeMenu
->Update(timedelta
);
3680 for (int i
=0; i
<SDLK_LAST
; i
++)
3687 if (isMap
&& isRenderMap
)
3690 static double scrollHi
= 0;
3695 int xx
= noMouse
? keyboardp
.getScreenX()-scrollX
: mousex
;
3696 if (xx
> SCREEN_W
) xx
= SCREEN_W
;
3699 x
= (double)xx
/ (w
) - 1;
3700 if (xx
> SCREEN_W
- w
)
3701 x
= 1 - (double)(SCREEN_W
-xx
) / (w
);
3703 if (x
<-min
|| x
>min
)
3705 scrollHi
+= timedelta
* x
;
3706 scrollX
+= (int)scrollHi
;
3707 scrollHi
-= (int)scrollHi
;
3712 if (undoTime
>=0 && undoTime
< time
)
3714 double acc
= (time
- undoTime
) / 2;
3715 if (acc
< 3) acc
= 3;
3716 time
-= timedelta
* acc
;
3717 if (undoTime
>= time
)
3724 time
+= timedelta
* 20;
3727 void FileDrop(const char* filename
)
3729 LoadSave(filename
, false);
3734 if (keyState
[SDLK_LALT
] || keyState
[SDLK_LCTRL
])
3738 if (!isMap
&& !editMode
&& undoTime
< 0)
3740 if (keyState
['z'] || keyState
[SDLK_BACKSPACE
] || keyState
['u'])
3746 if (isMap
&& !editMode
)
3749 if ((keyState
['q'] | keyState
[SDLK_KP7
]) & 2) keyboardp
.x
--;
3750 else if ((keyState
['d'] | keyState
[SDLK_KP3
]) & 2) keyboardp
.x
++;
3751 else if ((keyState
['e'] | keyState
[SDLK_KP9
]) & 2) keyboardp
.x
++, keyboardp
.y
--;
3752 else if ((keyState
['a'] | keyState
[SDLK_KP1
]) & 2) keyboardp
.x
--, keyboardp
.y
++;
3753 else if ((keyState
['w'] | keyState
[SDLK_KP8
] | keyState
[SDLK_UP
]) & 2) keyboardp
.y
--;
3754 else if ((keyState
['s'] | keyState
[SDLK_KP2
] | keyState
[SDLK_DOWN
]) & 2) keyboardp
.y
++;
3755 else if ((keyState
[SDLK_LEFT
]) & 2) keyboardp
.x
--, keyboardp
.y
+=keyboardp
.x
&1;
3756 else if (((keyState
[SDLK_RIGHT
]) & 2)) { if (keyboardp
.x
< mapRightBound
) keyboardp
.y
-=keyboardp
.x
&1, keyboardp
.x
++; }
3757 else if ((keyState
[SDLK_RETURN
] | keyState
[SDLK_KP5
] | keyState
[SDLK_SPACE
] | keyState
[SDLK_KP_ENTER
]) & 2)
3759 // Simulate user clicking on it...
3760 Mouse(keyboardp
.getScreenX()-scrollX
, keyboardp
.getScreenY()-scrollY
, 0, 0, 1, 0, 0);
3767 UpdateCursor(keyboardp
);
3770 int min
[21] = { 17, 16, 15, 14, 13, 13, 13, 13, 13, 13, 12, 11, 11, 13, 12, 11, 8, 8, 7, 6, 7 };
3771 int max
[21] = { 20, 20, 19, 19, 19, 19, 18, 21, 20, 20, 19, 19, 18, 18, 17, 16, 16, 16, 15, 15, 14 };
3772 if (keyboardp
.x
< 3) keyboardp
.x
= 3;
3773 if (keyboardp
.x
> mapRightBound
) keyboardp
.x
= mapRightBound
;
3775 if (keyboardp
.y
< min
[keyboardp
.x
-3]) keyboardp
.y
= min
[keyboardp
.x
-3];
3776 if (keyboardp
.y
> max
[keyboardp
.x
-3]) keyboardp
.y
= max
[keyboardp
.x
-3];
3778 UpdateCursor(keyboardp
);
3780 else if (!editMode
&& (numUndo
==0 || time
>=undo
[numUndo
-1].endTime
))
3782 static int usedDiag
= 0;
3784 if (keyState
['q'] || keyState
[SDLK_KP7
]) HandleKey('q', 0);
3785 else if (keyState
['w'] || keyState
[SDLK_KP8
]) HandleKey('w', 0);
3786 else if (keyState
['e'] || keyState
[SDLK_KP9
]) HandleKey('e', 0);
3787 else if (keyState
['a'] || keyState
[SDLK_KP1
]) HandleKey('a', 0);
3788 else if (keyState
['s'] || keyState
[SDLK_KP2
]) HandleKey('s', 0);
3789 else if (keyState
['d'] || keyState
[SDLK_KP3
]) HandleKey('d', 0);
3791 else if (keyState
[SDLK_UP
] && keyState
[SDLK_LEFT
]) HandleKey('q', 0), usedDiag
=1;
3792 else if (keyState
[SDLK_UP
] && keyState
[SDLK_RIGHT
]) HandleKey('e', 0), usedDiag
=1;
3793 else if (keyState
[SDLK_DOWN
] && keyState
[SDLK_LEFT
]) HandleKey('a', 0), usedDiag
=1;
3794 else if (keyState
[SDLK_DOWN
] && keyState
[SDLK_RIGHT
]) HandleKey('d', 0), usedDiag
=1;
3795 else if (keyState
[SDLK_UP
] && !usedDiag
) HandleKey('w', 0);
3796 else if (keyState
[SDLK_DOWN
] && !usedDiag
) HandleKey('s', 0);
3801 void KeyReleased(int key
)
3805 bool KeyPressed(int key
, int mod
)
3811 bool eat
= activeMenu
->KeyPressed(key
, mod
);
3813 memset(keyState
, 0, sizeof(keyState
));
3818 if ((key
==SDLK_ESCAPE
&& (mod
& KMOD_CTRL
)))
3820 if (mod
& KMOD_SHIFT
)
3824 LoadSaveProgress(false);
3830 if (isFadeRendering
)
3833 return HandleKey(key
, mod
);
3836 bool HandleKey(int key
, int mod
)
3841 if (isMap
&& key
=='r' && (mod
& KMOD_ALT
))
3850 else if ((key
=='p' && !editMode
|| key
==SDLK_PAUSE
|| key
==SDLK_ESCAPE
))
3853 new PauseMenu(isMap
, progress
.GetLevel(STARTING_LEVEL
, true)->Completed(), progress
.general
.endSequence
>=1, progress
.general
.endSequence
>=2);
3857 else if (key
=='e' && (mod
& KMOD_ALT
))
3858 editMode
= !editMode
;
3860 else if (key
=='p' && (mod
& KMOD_ALT
) && numUndo
>0
3861 || key
>='0' && key
<='9' && (mod
& KMOD_SHIFT
) && !isMap
)
3863 if (key
>='0' && key
<='9')
3864 levelDiff
= (key
=='0') ? 10 : key
-'0';
3866 if (key
=='p' && levelPar
==0)
3867 levelPar
= player_score
;
3872 undo
[numUndo
-1].Restore(this);
3876 if (LoadSave(currentFile
, true))
3878 if (key
>='0' && key
<='9')
3884 /////////////////////////////////////////////////////////////////////////
3885 if (isMap
&& !editMode
)
3888 else if (key
==SDLK_KP9
|| key
=='e') Input(1), noMouse
=1;
3889 else if (key
==SDLK_KP3
|| key
=='d') Input(2), noMouse
=1;
3890 else if (key
==SDLK_KP1
|| key
=='a') Input(4), noMouse
=1;
3891 else if (key
==SDLK_KP7
|| key
=='q') Input(5), noMouse
=1;
3892 else if (key
==SDLK_KP8
|| key
=='w') Input(0), noMouse
=1;
3893 else if (key
==SDLK_KP2
|| (key
=='s' && (((mod
& (KMOD_CTRL
|KMOD_ALT
))==0)||!editMode
))) Input(3), noMouse
=1;
3894 else if (key
==SDLK_KP5
|| key
==SDLK_SPACE
|| key
==SDLK_RETURN
|| key
==SDLK_KP_ENTER
)
3897 if (win
&& winFinal
)
3898 LoadMap(), memset(keyState
, 0, sizeof(keyState
));
3903 else if (key
=='r' && (mod
& KMOD_CTRL
))
3904 LoadSave(currentFile
, false);
3907 else if (key
=='z' && (mod
& KMOD_ALT
))
3909 if (numUndo
>0 && !isMap
)
3911 time
= undo
[numUndo
-1].endTime
;
3912 undoTime
= undo
[0].time
;
3915 undo
[numUndo
-1].Restore(this);
3920 else if (key
=='z' || key
==SDLK_BACKSPACE
|| key
==SDLK_DELETE
|| key
=='u')
3927 else if (key
=='s' && (mod
& KMOD_ALT
)){
3928 if (win
&& strlen(currentFile
)>0 && !isMap
)
3931 strcpy(tmp
, currentFile
);
3932 ChangeSuffix(tmp
, "sol");
3933 FILE* f
= file_open(tmp
, "wb");
3936 for (int i
=0; i
<numUndo
; i
++)
3938 fputc(undo
[i
].playerMovement
, f
);
3947 else if (key
=='/' && (mod
& KMOD_ALT
)){
3955 if (mod
& KMOD_SHIFT
)
3957 LevelSave
* l
= progress
.GetLevel(currentFile
, false);
3958 if (l
&& l
->Completed())
3960 for (int i
=0; i
<l
->bestSolutionLength
; i
++)
3961 Input(l
->bestSolution
[i
]);
3970 strcpy(tmp
, currentFile
);
3971 ChangeSuffix(tmp
, "sol");
3972 FILE* f
= file_open(tmp
, "rb");
3976 while ((dir
= fgetc(f
)) != -1)
3997 else if (key
>='0' && key
<='9' && (mod
& KMOD_ALT
) && !isMap
)
3998 levelPar
= levelPar
*10 + key
-'0';
3999 else if (key
==SDLK_BACKSPACE
&& (mod
& KMOD_ALT
) && !isMap
)
4003 Mouse(mousex
, mousey
, 0, 0, 32, 0, mouse_buttons
);
4004 else if (key
=='p' && !(mod
& KMOD_ALT
))
4005 Mouse(mousex
, mousey
, 0, 0, 64, 0, mouse_buttons
);
4007 Mouse(mousex
, mousey
, 0, 0, 128, 0, mouse_buttons
);
4008 else if (key
==SDLK_RETURN
)
4009 Mouse(mousex
, mousey
, 0, 0, 256, 0, mouse_buttons
);
4010 else if (key
==SDLK_BACKSPACE
)
4011 Mouse(mousex
, mousey
, 0, 0, 512, 0, mouse_buttons
);
4013 Mouse(mousex
, mousey
, 0, 0, 1024, 0, mouse_buttons
);
4015 else if (key
=='s' && (mod
& KMOD_CTRL
)){
4016 char *fn
= LoadSaveDialog(true, true, _("Save level"));
4018 SDL_WM_SetCaption(currentFile
, NULL
);
4021 else if (key
=='o' && (mod
& KMOD_CTRL
)){
4022 char* fn
= LoadSaveDialog(false, true, _("Open level"));
4023 LoadSave(fn
, false);
4024 SDL_WM_SetCaption(currentFile
, NULL
);
4035 #define X(NAME,FILE,ALPHA) NAME = Load("graphics/" FILE BMP_SUFFIX, ALPHA);
4036 #include "gfx_list.h"
4038 static int first
= 1;
4045 // unsigned int d = {
4048 // static SDL_Cursor * c = SDL_CreateCursor(data, mask, 32, 32, 1, 1);
4049 // SDL_SetCursor(c);
4054 #define X(NAME,FILE,ALPHA) if (NAME) SDL_FreeSurface(NAME), NAME=0;
4055 #include "gfx_list.h"
4057 virtual void ScreenModeChanged()
4064 MAKE_STATE(HexPuzzle
, SDLK_F1
, false);
4066 char * HexPuzzle::loadPtr
= 0;
4067 char * HexPuzzle::endLoad
= 0;