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
37 #define GAME_NAME "Hex-a-hop"
45 // #define MAP_EDIT_HACKS
46 #define MAP_EDIT_HACKS_DISPLAY_UNLOCK 0
48 #define BMP_SUFFIX ".bmp"
50 #define USE_LEVEL_PACKFILE
51 #define BMP_SUFFIX ".dat"
57 #define GAMENAME GAME_NAME " (EDIT MODE)"
60 #define GAMENAME GAME_NAME
63 #define IMAGE_DAT_OR_MASK 0xff030303 // Reduce colour depth of images slightly for better compression (and remove useless top 8 bits!)
64 #define STARTING_LEVEL "Levels\\0_green\\triangular.lev"
65 #define UNLOCK_SCORING 75
66 const char * mapname
= "Levels\\map_maybe\\map.lev";
68 //////////////////////////////////////////////////////
76 #include "tiletypes.h"
78 #ifdef USE_LEVEL_PACKFILE
85 #include <sys/types.h>
91 void RenderTile(bool reflect
, int t
, int x
, int y
, int cliplift
=-1);
93 int keyState
[SDLK_LAST
] = {0};
95 FILE *file_open( const char *file
, const char *flags
)
97 // printf("file_open( \"%s\", \"%s\" )\n", file, flags );
98 extern String base_path
;
99 static String filename
; // static to reduce memory alloc/free calls.
100 if (file
[0]=='/') //If a full path is specified, don't prepend base_path
104 if (strncmp(file
, "save", 4) == 0)
106 const char *home
= getenv("HOME");
109 char save_path
[PATH_MAX
];
110 snprintf(save_path
, sizeof(save_path
), "%s/.hex-a-hop", home
);
111 if (!strchr(flags
, 'r'))
112 if (mkdir(save_path
, S_IRWXU
| S_IRWXG
| S_IROTH
| S_IXOTH
) != -1)
113 printf("Creating directory \"%s\"\n", (const char *)save_path
);
114 strncat(save_path
, "/", sizeof(save_path
));
115 filename
= save_path
;
117 else filename
= "/tmp/";
119 else filename
= base_path
;
122 // printf(" -> \"%s\"\n", filename );
124 filename
.fix_backslashes();
125 FILE* f
= fopen( filename
, flags
);
127 if (!f
&& strncmp(file
, "save", 4) != 0)
129 printf("Warning: unable to open file \"%s\" for %s\n", (const char*)filename
, strchr(flags
, 'r') ? "reading" : "writing");
136 #ifdef MAP_EDIT_HACKS
137 static const short value_order
[]={
146 COLLAPSE_DOOR
, COLLAPSABLE2
,
158 //#define PROGRESS_FILE "progress.dat"
160 #define PI (3.1415926535897931)
162 #define MAX(a,b) ((a)>(b) ? (a) : (b))
163 #define MIN(a,b) ((a)<(b) ? (a) : (b))
164 #define ABS(a) ((a)<0 ? -(a) : (a))
166 #define WATER_COLOUR 31 | (IMAGE_DAT_OR_MASK>>16)&255, 37 | (IMAGE_DAT_OR_MASK>>8)&255, 135 | (IMAGE_DAT_OR_MASK>>0)&255
168 #define ROTATION_TIME 0.25
170 #define LASER_LINE_TIME 0.7
171 #define LASER_FADE_TIME 0.1
172 #define LASER_SEGMENT_TIME 0.01
173 #define LIFT_TIME 0.5
174 #define JUMP_TIME 0.4
176 #define X(NAME,FILE,ALPHA) SDL_Surface* NAME = 0;
177 #include "gfx_list.h"
178 int scrollX
=0, scrollY
=0, initScrollX
=0, initScrollY
=0;
179 int mapRightBound
= 0;
181 bool showScoring
= false;
182 bool hintsDone
= false;
208 TILE_GREEN_FRAGMENT_1
,
209 TILE_GREEN_FRAGMENT_2
,
214 TILE_GREEN_CRACKED_WALL
,
216 TILE_BLUE_CRACKED_WALL
,
218 TILE_FIRE_PARTICLE_1
,
219 TILE_FIRE_PARTICLE_2
,
223 TILE_LASER_FADE_0
= 53,
224 TILE_BLUE_FRAGMENT
= 56,
225 TILE_BLUE_FRAGMENT_1
,
226 TILE_BLUE_FRAGMENT_2
,
228 TILE_LASER_REFRACT
= 60,
229 TILE_ICE_LASER_REFRACT
= TILE_LASER_REFRACT
+6,
236 const int colours
[] = {
237 #define X(n,col, solid) col,
238 #include "tiletypes.h"
241 const int tileSolid
[] = {
242 #define X(n,col, solid) solid,
243 #include "tiletypes.h"
246 void ChangeSuffix(char* filename
, char* newsuffix
)
248 int len
= strlen(filename
);
250 while (i
>=0 && filename
[i
]!='\\' && filename
[i
]!='.' && filename
[i
]!='/')
252 if (filename
[i
]=='.')
253 strcpy(filename
+i
+1, newsuffix
);
256 strcat(filename
, ".");
257 strcat(filename
, newsuffix
);
261 bool isMap
=false, isRenderMap
=false;
262 int isFadeRendering
=0;
268 |-----------| TILE_W3
271 / \ |TILE_H1 |TILE_H2
278 WL = sqrt(h1*h1 + w1*w1)
279 wl**2 = h1**2 + w1**2
288 #define GFX_SIZE TILE_W3
289 #define TILE_W2 (TILE_W3-TILE_W1)
290 #define TILE_H1 TILE_W1
291 #define TILE_HUP 22 //extra visible height of wall (used for determining whether a wall was clicked on)
292 #define TILE_H2 (TILE_H1*2)
293 #define TILE_WL (TILE_W2-TILE_W1)
294 #define TILE_H_LIFT_UP 26
295 #define TILE_H_REFLECT_OFFSET 24
296 #define TILE_HUP2 TILE_H_LIFT_UP // Displacement of object on top of wall
297 #define FONT_SPACING 25
298 #define FONT_X_SPACING (-1) // -1 in order to try and overlap the black borders of adjacent characters
301 #define TILE_W1 (TILE_WL/2)
302 #define TILE_W2 (TILE_W1+TILE_WL)
303 #define TILE_W3 (TILE_W1+TILE_W2)
304 #define TILE_H1 (TILE_WL*0.8660254037844386)
305 #define TILE_H2 (TILE_H1*2)
310 SDL_Rect tile
[2][70];
311 short tileOffset
[2][70][2];
312 int Peek(SDL_Surface
* i
, int x
, int y
)
314 if (x
<0 || y
<0 || x
>=i
->w
|| y
>=i
->h
)
317 const int BytesPerPixel
= i
->format
->BytesPerPixel
;
318 const int BitsPerPixel
= i
->format
->BitsPerPixel
;
320 p
= ((unsigned char*)i
->pixels
)[i
->pitch
*y
+ x
*BytesPerPixel
];
321 else if (BitsPerPixel
==15 || BitsPerPixel
==16)
322 p
= *(short*)(((char*)i
->pixels
) + (i
->pitch
*y
+ x
*BytesPerPixel
));
323 else if (BitsPerPixel
==32)
324 p
= *(unsigned int*)(((char*)i
->pixels
) + (i
->pitch
*y
+ x
*BytesPerPixel
));
325 else if (BitsPerPixel
==24)
326 p
= (int)((unsigned char*)i
->pixels
)[i
->pitch
*y
+ x
*BytesPerPixel
]
327 | (int)((unsigned char*)i
->pixels
)[i
->pitch
*y
+ x
*BytesPerPixel
] << 8
328 | (int)((unsigned char*)i
->pixels
)[i
->pitch
*y
+ x
*BytesPerPixel
] << 16;
332 bool IsEmpty(SDL_Surface
* im
, int x
, int y
, int w
, int h
)
334 for (int i
=x
; i
<x
+w
; i
++)
335 for (int j
=y
; j
<y
+h
; j
++)
336 if (Peek(im
,i
,j
) != Peek(im
,0,im
->h
-1))
343 for (int i
=0; i
<140; i
++)
345 SDL_Rect r
= {(i
%10)*GFX_SIZE
, ((i
/10)%7)*GFX_SIZE
, GFX_SIZE
, GFX_SIZE
};
346 short * outOffset
= tileOffset
[i
/70][i
%70];
347 SDL_Surface
* im
= (i
/70) ? tileGraphicsR
: tileGraphics
;
349 outOffset
[0] = outOffset
[1] = 0;
351 while (r
.h
>1 && IsEmpty(im
, r
.x
, r
.y
, r
.w
, 1)) r
.h
--, r
.y
++, outOffset
[1]++;
352 while (r
.h
>1 && IsEmpty(im
, r
.x
, r
.y
+r
.h
-1, r
.w
, 1)) r
.h
--;
353 while (r
.w
>1 && IsEmpty(im
, r
.x
, r
.y
, 1, r
.h
)) r
.w
--, r
.x
++, outOffset
[0]++;
354 while (r
.w
>1 && IsEmpty(im
, r
.x
+r
.w
-1, r
.y
, 1, r
.h
)) r
.w
--;
356 tile
[i
/70][i
%70] = r
;
360 void ConvertToUTF8(const std::string
&text_locally_encoded
, char *text_utf8
, size_t text_utf8_length
)
363 size_t text_length
= text_locally_encoded
.length()+1;
365 static const char *locale_enc
= gettext_init
.GetEncoding();
366 iconv_t cd
= iconv_open("UTF-8", locale_enc
);
367 char *in_buf
= const_cast<char *>(&text_locally_encoded
[0]);
368 char *out_buf
= &text_utf8
[0];
369 iconv(cd
, &in_buf
, &text_length
, &out_buf
, &text_utf8_length
);
372 std::cerr
<< "An error occurred recoding " << text_locally_encoded
<< " to UTF8" << std::endl
;
375 int SDLPangoTextWidth(const std::string
&text_utf8
);
376 void Print_Pango(int x
, int y
, const std::string
&text_utf8
);
377 void Print_Pango_Aligned(int x
, int y
, int width
, const std::string
&text_utf8
, int align
);
379 /// Prints a left aligned string (a single line) beginning at (x,y)
380 // TODO: Check that the maximal text width is already set
381 void Print(int x
, int y
, const char * string
, ...)
384 va_start( marker
, string
); /* Initialize variable arguments. */
386 char tmp
[1000], tmp_utf8
[5000]; // FIXME: Check this limit
387 vsprintf((char*)tmp
, string
, marker
);
389 ConvertToUTF8(tmp
, tmp_utf8
, sizeof(tmp_utf8
)/sizeof(char));
390 Print_Pango(x
, y
, tmp_utf8
);
392 va_end( marker
); /* Reset variable arguments. */
395 /// Prints a string right aligned so that it ends at (x,y)
396 // TODO: Check that the maximal text width is already set
397 void PrintR(int x
, int y
, const char * string
, ...)
400 va_start( marker
, string
); /* Initialize variable arguments. */
402 char tmp
[1000], tmp_utf8
[5000]; // FIXME: Check this limit
403 vsprintf((char*)tmp
, string
, marker
);
405 ConvertToUTF8(tmp
, tmp_utf8
, sizeof(tmp_utf8
)/sizeof(char));
406 Print_Pango(x
-SDLPangoTextWidth(tmp_utf8
), y
, tmp_utf8
);
408 va_end( marker
); /* Reset variable arguments. */
411 /** \brief Prints a string horizontally centered around (x,y)
413 * " " in the string is interpreted as linebreak
415 void Print_Aligned(bool split
, int x
, int y
, int width
, const char * string
, int align
)
417 char tmp_utf8
[5000]; // FIXME: Check this limit
419 ConvertToUTF8(string
, tmp_utf8
, sizeof(tmp_utf8
)/sizeof(char));
421 std::string
msg(tmp_utf8
);
422 while (split
&& msg
.find(" ") != std::string::npos
)
423 msg
.replace(msg
.find(" "), 2, "\n");
425 Print_Pango_Aligned(x
, y
, width
, msg
, align
);
428 void PrintC(bool split
, int x
, int y
, const char * string
, ...)
431 va_start( marker
, string
); /* Initialize variable arguments. */
433 char tmp
[1000]; // FIXME: Check this limit
434 vsprintf((char*)tmp
, string
, marker
);
436 va_end( marker
); /* Reset variable arguments. */
438 static bool print
= true; // avoid flickering!
440 std::cerr
<< "Warning: don't know window width for message:\n" << tmp
<< "\n";
441 for (unsigned int i
=0; i
<strlen(tmp
); ++i
)
442 if (!std::isspace(tmp
[i
]))
445 Print_Aligned(split
, x
, y
, 2*std::min(x
, SCREEN_W
-x
), tmp
, 1);
448 #include "savestate.h"
450 #include "level_list.h"
452 void SaveState::GetStuff()
454 general
.hintFlags
= HintMessage::flags
;
456 void SaveState::ApplyStuff()
458 HintMessage::flags
= general
.hintFlags
;
462 // somewhere else Tile map[][] is assigned to an unsigned char not int32_t
463 // but the data file format expects it to be 32 bit wide!??
464 typedef int32_t Tile
;
468 Pos() : x(0), y(0) {}
469 Pos(int a
, int b
) : x(a
), y(b
) {}
470 bool operator == (Pos
const & p
) const
472 return x
==p
.x
&& y
==p
.y
;
474 Pos
operator + (Dir
const d
) const
477 x
+ ((d
==1 || d
==2) ? 1 : (d
==4 || d
==5) ? -1 : 0),
478 y
+ ((d
==0 || d
==1) ? -1 : (d
==3 || d
==4) ? 1 : 0)
481 int getScreenX() const {
484 int getScreenY() const {
485 return x
*TILE_H1
+ y
*TILE_H2
;
487 static Pos
GetFromWorld(double x
, double y
)
492 tx
= (int)floor(x
/TILE_W2
);
494 ty
= (int)floor(y
/TILE_H2
);
499 if (x
< TILE_W1
&& y
< TILE_H1
)
500 if (x
*TILE_H1
+ y
* TILE_W1
< TILE_H1
*TILE_W1
)
502 if (x
< TILE_W1
&& y
> TILE_H1
)
503 if (x
*TILE_H1
+ (TILE_H2
-y
) * TILE_W1
< TILE_H1
*TILE_W1
)
509 Pos
mousep(0,0), keyboardp(4,20);
515 virtual ~RenderStage() {}
516 virtual void Render(RenderObject
* r
, double time
, bool reflect
) = 0;
517 virtual int GetDepth(double /*time*/) { return 1; }
534 if (maxStages
<= numStages
)
536 maxStages
= maxStages
? maxStages
*2 : 4;
537 stage
= (RenderStage
**) realloc(stage
, sizeof(stage
[0])*maxStages
);
538 time
= (double*) realloc(time
, sizeof(time
[0])*maxStages
);
542 RenderObject() : stage(0), time(0), numStages(0), maxStages(0), currentStage(0)
544 // TODO: use a random number with better range
545 // or maybe make seed an int or float...
546 seed
= rand() / (double)RAND_MAX
;
550 free(stage
); free(time
);
552 bool Active(double t
)
554 if (numStages
==0) return false;
555 if (t
< time
[0]) return false;
558 void UpdateCurrent(double t
)
560 if (currentStage
>= numStages
) currentStage
= numStages
-1;
561 if (currentStage
< 0) currentStage
= 0;
563 while (currentStage
>0 && time
[currentStage
]>t
)
565 while (currentStage
<numStages
-1 && time
[currentStage
+1]<=t
)
570 RenderStage
* GetStage(double t
)
572 if (t
==-1 && numStages
>0)
573 return stage
[numStages
-1];
575 if (!Active(t
)) return 0;
577 return stage
[currentStage
];
581 return numStages
>0 ? time
[numStages
-1] : -1;
583 void Render(double t
, bool reflect
)
588 stage
[currentStage
]->Render(this, t
- time
[currentStage
], reflect
);
590 int GetDepth(double t
)
595 return stage
[currentStage
]->GetDepth(t
- time
[currentStage
]);
600 numStages
= currentStage
= 0;
603 while (numStages
> 0 && time
[numStages
-1] >= t
)
605 if (currentStage
> 0 && currentStage
>= numStages
)
606 currentStage
= numStages
- 1;
608 if (currentStage
< 0) currentStage
= 0;
612 if (currentStage
> 0 && numStages
> 0)
614 memmove(&time
[0], &time
[currentStage
], sizeof(time
[0]) * (numStages
-currentStage
));
615 memmove(&stage
[0], &stage
[currentStage
], sizeof(stage
[0]) * (numStages
-currentStage
));
616 numStages
-= currentStage
;
620 void Add(RenderStage
* s
, double t
)
624 if (currentStage
<numStages
&& time
[currentStage
]<=t
)
627 while (i
<numStages
&& time
[i
]<t
)
630 if (i
<numStages
&& time
[i
]==t
)
638 memmove(&time
[i
+1], &time
[i
], (numStages
-i
) * sizeof(time
[0]));
639 memmove(&stage
[i
+1], &stage
[i
], (numStages
-i
) * sizeof(stage
[0]));
653 RenderObject tile
[SIZE
][SIZE
][2];
666 void Reset(double t
= -1)
672 for (int i
=0; i
<SIZE
; i
++)
673 for (int j
=0; j
<SIZE
; j
++)
674 for (int q
=0; q
<2; q
++)
675 tile
[i
][j
][q
].Reset(t
);
677 for (int j
=0; j
<FX
; j
++)
686 for (int i
=0; i
<SIZE
; i
++)
687 for (int j
=0; j
<SIZE
; j
++)
688 for (int q
=0; q
<2; q
++)
689 tile
[i
][j
][q
].Wipe();
691 for (int j
=0; j
<FX
; j
++)
697 int x0
= (scrollX
+TILE_W2
) / TILE_W2
;
698 int x1
= (scrollX
+SCREEN_W
+TILE_W3
+TILE_W1
) / TILE_W2
;
699 if (p
.x
<0 || p
.y
<0 || p
.x
>=SIZE
|| p
.y
>=SIZE
) return false;
700 if (p
.x
<x0
) return false;
701 if (p
.x
>=x1
-1) return false;
702 for (int j0
=0; j0
<SIZE
*3; j0
++)
704 if (j0
* TILE_H1
< scrollY
-TILE_H1
) continue;
705 if (j0
* TILE_H1
> scrollY
+SCREEN_H
+TILE_H1
) break;
710 if (j
>=SIZE
) i
+=(j
+1-SIZE
)*2, j
=SIZE
-1;
711 for (; i
<x1
&& j
>=0; i
+=2, j
--)
720 void Render(double t
, bool reflect
)
724 int playerDepth
= player
.GetDepth(t
);
725 if (reflect
) playerDepth
-=4;
727 player
.Render(t
, reflect
);
729 int x0
= (scrollX
+TILE_W2
) / TILE_W2
;
730 int x1
= (scrollX
+SCREEN_W
+TILE_W3
+TILE_W1
) / TILE_W2
;
733 for (int j0
=0; j0
<SIZE
*3; j0
++)
735 if (j0
* TILE_H1
< scrollY
-TILE_H1
) continue;
736 if (j0
* TILE_H1
> scrollY
+SCREEN_H
+TILE_H1
) break;
741 if (j
>=SIZE
) i
+=(j
+1-SIZE
)*2, j
=SIZE
-1;
742 for (; i
<x1
&& j
>=0; i
+=2, j
--)
744 for (int q
=reflect
?1:0; q
!=2 && q
!=-1; q
+= (reflect
? -1 : 1))
745 if (tile
[i
][j
][q
].Active(t
))
747 tile
[i
][j
][q
].Render(t
, reflect
);
751 if (playerDepth
==j0
|| j0
==SIZE
*3 && playerDepth
>j0
)
752 player
.Render(t
, reflect
);
755 for (int j
=0; j
<FX
; j
++)
758 fx
[j
].Render(t
, reflect
);
762 RenderObject
& operator () ()
765 if (fxPos
==FX
) fxPos
= 0;
768 RenderObject
& operator () (Pos
const & p
, bool item
=false)
770 if (p
.x
<0 || p
.y
<0 || p
.x
>=SIZE
|| p
.y
>=SIZE
)
772 return tile
[p
.x
][p
.y
][item
? 1 : 0];
776 void RenderTile(bool reflect
, int t
, int x
, int y
, int cliplift
)
778 SDL_Rect src
= tile
[reflect
][t
];
779 SDL_Rect dst
= {x
-scrollX
-GFX_SIZE
/2, y
-scrollY
-GFX_SIZE
+TILE_H1
, 0, 0};
780 dst
.x
+= tileOffset
[reflect
][t
][0];
781 dst
.y
+= tileOffset
[reflect
][t
][1];
783 dst
.y
+= TILE_H_REFLECT_OFFSET
;
784 if (cliplift
==-1 || reflect
)
786 // dst.w=src.w; dst.h=src.h;
787 // SDL_FillRect(screen, &dst, rand());
788 SDL_BlitSurface(reflect
? tileGraphicsR
: tileGraphics
, &src
, screen
, &dst
);
796 SDL_BlitSurface(tileGraphics
, &src
, screen
, &dst
);
803 src
.w
-= TILE_W1
*2, src
.x
+= TILE_W1
;
805 SDL_BlitSurface(tileGraphics
, &src
, screen
, &dst
);
809 void RenderGirl(bool reflect
, int r
, int frame
, int x
, int y
, int h
)
812 int sy
= frame
* 80*2;
814 y
+= TILE_H_REFLECT_OFFSET
+20+h
, sy
+= 80;
817 SDL_Rect src
= {sx
, sy
, 64, 80};
818 SDL_Rect dst
= {x
-scrollX
-32, y
-scrollY
-65, 0, 0};
819 SDL_BlitSurface(girlGraphics
, &src
, screen
, &dst
);
822 struct ItemRender
: public RenderStage
828 ItemRender(int i2
, int _water
, Pos
const & _p
) : item(i2
), p(_p
), water(_water
)
831 double Translate(double seed
, double time
)
833 double bob
= time
*2 + seed
*PI2
;
837 void Render(RenderObject
* r
, double time
, bool reflect
)
842 int y
= -5 + (int)Translate(r
->seed
, r
->currentTime
+ time
);
845 if (!reflect
&& !water
)
846 RenderTile( false, TILE_SPHERE
, p
.getScreenX(), p
.getScreenY());
849 item
==1 ? TILE_ITEM1
: TILE_ITEM2
,
850 p
.getScreenX(), p
.getScreenY()+y
855 void RenderFade(double time
, int dir
, int seed
)
859 for(int x
=rand()%22-11; x
<SCREEN_W
+22; x
+=32, ys
^= 1)
861 for (int y
=ys
*20; y
<SCREEN_H
+30; y
+=40)
863 double a
= (rand()&0xff)*dir
;
864 double b
= (time
* 0x400 + (y
- SCREEN_H
) * 0x140/SCREEN_H
)*dir
;
867 RenderTile(false, TILE_BLACK_TILE
, x
+scrollX
, y
+scrollY
);
873 struct FadeRender
: public RenderStage
877 FadeRender(int d
=-1) : seed(rand()), dir(d
)
882 void Render(RenderObject
* /*r*/, double time
, bool reflect
)
887 if (dir
==1) dir
=0, isFadeRendering
=0;
890 RenderFade(time
, dir
, seed
);
894 struct ScrollRender
: public RenderStage
898 ScrollRender(int a
,int b
) : x(a
), y(b
), done(false) {}
900 void Render(RenderObject
* /*r*/, double /*time*/, bool /*reflect*/)
903 scrollX
= x
, scrollY
= y
;
909 struct LevelSelectRender
: public RenderStage
914 #ifdef MAP_EDIT_HACKS
918 LevelSelectRender(Pos
const & _p
, int i2
, int adj
) : p(_p
), item(i2
), adj(adj
)
921 void Render(RenderObject
* /*r*/, double /*time*/, bool reflect
)
926 #ifndef MAP_LOCKED_VISIBLE
931 for (int i
=0; i
<MAX_DIR
; i
++)
933 RenderTile( false, TILE_LINK_0
+i
, p
.getScreenX(), p
.getScreenY());
942 TILE_SPHERE
+ item
-1,
943 p
.getScreenX(), p
.getScreenY()
946 #ifdef MAP_EDIT_HACKS
947 int x
= p
.getScreenX()-scrollX
, y
= p
.getScreenY()-scrollY
;
948 Print(x
+5,y
-25,"%d",magic
);
954 struct ItemCollectRender
: public ItemRender
956 ItemCollectRender(int i2
, Pos
const & p
) : ItemRender(i2
, 0, p
)
959 void Render(RenderObject
* /*r*/, double /*time*/, bool /*reflect*/)
964 int GetLiftHeight(double time
, int t
)
967 time
= LIFT_TIME
-time
;
968 time
= time
/ LIFT_TIME
;
973 time
= (3 - 2*time
)*time
*time
;
975 time
= (3 - 2*time
)*time
*time
;
977 return (int)((TILE_H_LIFT_UP
+4) * time
);
979 return (int)((TILE_H_LIFT_UP
-4) * time
) + 4;
982 struct TileRender
: public RenderStage
987 double specialDuration
;
989 TileRender(int i
, Pos
const & _p
, int _special
=0) : special(_special
), t(i
), p(_p
), specialDuration(LASER_LINE_TIME
)
992 void Render(RenderObject
* r
, double time
, bool reflect
)
994 if (t
==0 && special
==0)
997 if (special
&& (t
==LIFT_UP
|| t
==LIFT_DOWN
) && time
<LIFT_TIME
)
999 int y
= GetLiftHeight(time
, t
);
1002 RenderTile(reflect
, TILE_LIFT_BACK
, p
.getScreenX(), p
.getScreenY());
1003 RenderTile(reflect
, TILE_LIFT_SHAFT
, p
.getScreenX(), p
.getScreenY()+y
, y
-8);
1004 RenderTile(reflect
, TILE_LIFT_FRONT
, p
.getScreenX(), p
.getScreenY());
1008 RenderTile(reflect
, TILE_LIFT_SHAFT
, p
.getScreenX(), p
.getScreenY()-y
, y
);
1009 RenderTile(reflect
, LIFT_DOWN
, p
.getScreenX(), p
.getScreenY());
1012 else if (special
&& (t
==EMPTY
|| t
==TRAP
) && !reflect
&& time
< specialDuration
)
1015 if (time
< specialDuration
-LASER_FADE_TIME
)
1016 RenderTile(reflect
, TILE_ICE_LASER_REFRACT
, p
.getScreenX(), p
.getScreenY());
1018 RenderTile(reflect
, t
, p
.getScreenX(), p
.getScreenY());
1019 int base
= ((t
==EMPTY
) ? TILE_LASER_0
: TILE_LASER_REFRACT
);
1020 if (t
==EMPTY
&& time
>= specialDuration
-LASER_FADE_TIME
)
1021 base
= TILE_LASER_FADE_0
;
1024 for(int i
=0; foo
; foo
>>=1, i
++)
1026 RenderTile(reflect
, base
+i
, p
.getScreenX(), p
.getScreenY());
1028 else if (t
==FLOATING_BALL
)
1030 int y
= int(1.8 * sin(r
->seed
*PI
+ time
*4));
1033 if (time
> 2) return;
1034 if (reflect
) return;
1035 srand(int(r
->seed
* 0xfff));
1036 for (int i
=0; i
<20 - int(time
*10); i
++)
1038 int x
= int((((rand() & 0xfff) - 0x800) / 10) * time
);
1039 int y
= int((((rand() & 0xfff) - 0x800) / 10) * time
);
1040 RenderTile(true, 19 + ((i
+int(time
*5))&1)*10, p
.getScreenX() + x
, p
.getScreenY() - 14 + y
);
1044 RenderTile(true, 18, p
.getScreenX(), p
.getScreenY() - 14);
1047 RenderBoat(reflect
, int(special
)&255, p
.getScreenX(), p
.getScreenY(), y
);
1049 RenderTile(reflect
, t
, p
.getScreenX(), p
.getScreenY() + (reflect
? -y
: y
));
1051 else if (t
!= EMPTY
)
1052 RenderTile(reflect
, t
, p
.getScreenX(), p
.getScreenY());
1054 static void RenderBoat(bool reflect
, int d
, int x
, int y
, int yo
)
1057 RenderGirl(reflect
, d
, 0, x
, y
, -yo
);
1058 RenderTile(reflect
, FLOATING_BALL
, x
, y
+yo
);
1061 RenderGirl(reflect
, d
, 0, x
, y
, -yo
);
1062 RenderTile(true, 17, x
, y
+yo
-TILE_H_REFLECT_OFFSET
);
1067 struct TileRotateRender
: public TileRender
1072 TileRotateRender(int i
, Pos
const & p
, Dir _d
, int m
) : TileRender(i
, p
), d(_d
), mode(m
)
1074 void Render(RenderObject
* r
, double time
, bool reflect
)
1078 double f
= time
/ ROTATION_TIME
;
1080 if (mode
& 1) f
+= 0.5;
1089 if (mode
& 1) f
=1-f
; else f
=f
;
1093 TileRender::Render(r
, time
, reflect
);
1096 Pos dd
= (Pos(0,0)+d
);
1097 int x
= p
.getScreenX() + int(dd
.getScreenX()*(f
));
1098 int y
= p
.getScreenY() + int(dd
.getScreenY()*(f
));
1101 RenderBoat(reflect
, (mode
&1) ? (d
+MAX_DIR
/2)%MAX_DIR
: d
, x
, y
, 2);
1103 RenderTile(reflect
, t
, x
, y
);
1108 struct LaserRender
: public RenderStage
1114 LaserRender(Pos _p
, int dir
, int r
) : p(_p
), d(dir
), range(r
)
1117 void Render(RenderObject
* /*r*/, double /*time*/)
1122 struct ExplosionRender
: public RenderStage
1129 ExplosionRender(Pos _p
, int _pow
=0, int t
=0) : p(_p
), power(_pow
), type(t
)
1134 virtual int GetDepth(double /*time*/)
1139 void Render(RenderObject
* /*r*/, double time
, bool reflect
)
1141 if (type
==1 && time
> 2.5)
1142 type
= -1, new WinLoseScreen(false);
1144 // if (reflect) return;
1145 if (time
> 3) return;
1147 int q
= 50 - int(time
* 35);
1150 for (int i
=0; i
<q
; i
++)
1152 int x
= p
.getScreenX();
1153 int y
= p
.getScreenY() + (rand() & 31)-16;
1154 int xs
= ((rand() & 63) - 32);
1155 int ys
= (-10 - (rand() & 127)) * (1+power
);
1156 if (type
) ys
*=2, xs
/=2;
1157 x
+= int(xs
* (1+time
*(2+power
)));
1158 int yo
= int(time
*time
*128 + ys
*time
);
1159 //if (yo > 0) yo=-yo;//continue;
1165 if (!reflect
&& ys
<-60)
1167 const double T
= 0.06;
1168 double ct
= -ys
/ 128.0;
1171 x
= p
.getScreenX() + int(xs
* (1+ct
*(2+power
)));
1174 time
> ct
+3*T
? TILE_SPLASH_3
: time
> ct
+2*T
? TILE_SPLASH_2
: time
> ct
+T
? TILE_SPLASH_1
: TILE_WATER_PARTICLE
+1,
1182 time
- i
*0.003 < 0.2 ? TILE_WATER_PARTICLE
+1 : TILE_WATER_PARTICLE
,
1183 x
, y
+(reflect
?-1:1)*yo
);
1192 i
<q
-20 || time
<0.3 ? TILE_LASER_HEAD
: i
<q
-10 || time
<0.6 ? TILE_FIRE_PARTICLE_1
: TILE_FIRE_PARTICLE_2
,
1193 x
, y
+(reflect
?-1:1)*yo
);
1198 struct DisintegrateRender
: public RenderStage
1205 DisintegrateRender(Pos _p
, int _pow
=0, int _t
=0) : p(_p
), height(_pow
), type(_t
)
1210 void Render(RenderObject
* /*r*/, double time
, bool reflect
)
1213 RenderTile(reflect
, height
? COLLAPSE_DOOR
: COLLAPSABLE
, p
.getScreenX(), p
.getScreenY());
1215 if (time
> 50.0/70.0) return;
1216 if (reflect
) return;
1218 int q
= 50 - int(time
* 70);
1220 for (int i
=0; i
<q
; i
++)
1222 int x
= (rand() % (TILE_W3
-8))-TILE_W3
/2+4;
1223 int y
= (rand() % (TILE_H2
-8))-TILE_H1
+4;
1224 if (x
<-TILE_WL
/2 && ABS(y
)<-TILE_WL
/2-x
) continue;
1225 if (x
>TILE_WL
/2 && ABS(y
)>x
-TILE_WL
/2) continue;
1227 if (height
) yo
-= rand() % TILE_HUP
;
1228 x
+= p
.getScreenX();
1229 y
+= p
.getScreenY() + 4;
1230 int xs
= 0;//((rand() & 63) - 32);
1231 int ys
= (- (rand() & 31));
1232 x
+= int(xs
* (1+time
*(2)));
1234 yo
+= int(time
*time
*128 + ys
*time
);
1235 if (type
) yo
= -yo
*2;
1236 //if (yo > 0) yo=-yo;//continue;
1237 int t
= type
? TILE_BLUE_FRAGMENT
: TILE_GREEN_FRAGMENT
;
1241 RenderTile(false, t
, x
, y
+(reflect
?-yo
:yo
));
1245 struct BuildRender
: public RenderStage
1253 BuildRender(Pos _p
, Dir _d
, int _h
, int _r
=0, int _type
=0) : p(_p
), dir(_d
), reverse(_r
), height(_h
), type(_type
)
1257 void Render(RenderObject
* /*r*/, double time
, bool reflect
)
1259 if (time
>= BUILD_TIME
)
1260 RenderTile(reflect
, height
^ reverse
? (type
? COLLAPSE_DOOR2
: COLLAPSE_DOOR
) : (type
? COLLAPSABLE2
: COLLAPSABLE
), p
.getScreenX(), p
.getScreenY());
1264 RenderTile(reflect
, type
? COLLAPSABLE2
: COLLAPSABLE
, p
.getScreenX(), p
.getScreenY());
1266 double dist
= time
* 2 / BUILD_TIME
;
1269 Pos from
= p
+ ((dir
+MAX_DIR
/2)%MAX_DIR
);
1273 double offset
= (dist
*0.7) + 0.3;
1274 int x
= from
.getScreenX() + int((p
.getScreenX()-from
.getScreenX()) * offset
);
1275 int y
= from
.getScreenY() + int((p
.getScreenY()-from
.getScreenY()) * offset
- dist
*(1-dist
)*(TILE_HUP
*4));
1276 RenderTile(reflect
, TILE_GREEN_FRAGMENT
, x
, y
);
1282 if (reverse
) dist
= 1-dist
;
1284 if (dist
> 0 && !height
)
1287 for (int i
=0; i
<=int(dist
*15); i
++)
1289 int x
= p
.getScreenX(), y
= p
.getScreenY();
1290 double d
= (i
+ fmod(dist
*15, 1))/10.0;
1291 int x1
= int(sin(d
*5+time
)*MIN(d
,1)*TILE_W2
/2);
1292 int y1
= int(cos(d
*5+time
)*MIN(d
,1)*TILE_H1
*0.7);
1293 RenderTile(reflect
, TILE_GREEN_FRAGMENT
, x
+x1
, y
+y1
+4);
1294 RenderTile(reflect
, TILE_GREEN_FRAGMENT
, x
-x1
, y
-y1
+4);
1297 if (dist
> 0 && height
)
1299 int yo
= int((1-dist
)*(TILE_HUP
*1.3));
1300 if (yo
> TILE_HUP
*1.1)
1301 RenderTile(reflect
, TILE_WHITE_TILE
, p
.getScreenX(), p
.getScreenY());
1304 RenderTile(reflect
, type
? COLLAPSABLE2
: COLLAPSABLE
, p
.getScreenX(), p
.getScreenY());
1305 RenderTile(reflect
, type
? COLLAPSE_DOOR2
: COLLAPSE_DOOR
, p
.getScreenX(), p
.getScreenY()+(reflect
? -yo
: yo
), yo
+6);
1306 RenderTile(reflect
, type
? TILE_BLUE_FRONT
: TILE_GREEN_FRONT
, p
.getScreenX(), p
.getScreenY());
1310 if (yo
< TILE_HUP
/2)
1312 RenderTile(reflect
, type
? COLLAPSE_DOOR2
: COLLAPSE_DOOR
, p
.getScreenX(), p
.getScreenY()+(reflect
? -yo
: yo
), yo
);
1315 RenderTile(reflect
, type
? COLLAPSABLE2
: COLLAPSABLE
, p
.getScreenX(), p
.getScreenY());
1323 struct PlayerRender
: public RenderStage
1333 PlayerRender(Pos a
, int h
, bool d
) : p(a
), target(a
), p_h(h
), target_h(h
), r(3), type(0), speed(0), dead(d
)
1335 PlayerRender(int _r
, Pos a
, int h1
, Pos t
, int h2
, bool d
) : p(a
), target(t
), p_h(h1
), target_h(h2
), r(_r
), type(0), speed(JUMP_TIME
), dead(d
)
1337 int dist
= MAX(ABS(p
.x
-target
.x
), ABS(p
.y
-target
.y
));
1344 virtual int GetDepth(double time
)
1346 double f
= speed
? time
/ speed
: 1;
1348 if (f
==1) dead
= this->dead
;
1350 if (f
==1 || f
>0.5 && p_h
>target_h
)
1351 return target
.x
+target
.y
*2;
1352 return MAX(target
.x
+target
.y
*2 , p
.x
+p
.y
*2);
1355 void Render(RenderObject
* /*ro*/, double time
, bool reflect
)
1358 double f
= speed
? time
/ speed
: 1;
1360 if (f
==1) dead
= this->dead
;
1362 int x
= p
.getScreenX();
1363 int y
= p
.getScreenY();
1364 int x2
= target
.getScreenX();
1365 int y2
= target
.getScreenY();
1367 int shadow_h
= (int)((p_h
+(target_h
-p_h
)*f
)*TILE_HUP2
);
1369 if (x
==x2
&& y
==y2
&& p_h
!=target_h
)
1371 h
= TILE_H_LIFT_UP
- GetLiftHeight(time
, p_h
? LIFT_DOWN
: LIFT_UP
);
1376 int dist
= MAX(ABS(p
.x
-target
.x
), ABS(p
.y
-target
.y
));
1377 int arc
= dist
*dist
;
1378 int h1
= p_h
* TILE_HUP2
;;
1379 int h2
= target_h
* TILE_HUP2
;
1380 if (dist
==2 && h1
!=0)
1384 shadow_h
= f
>=0.7 ? int(shadow_h
*(f
-0.7)/0.3) : 0;
1387 arc
= speed
> JUMP_TIME
? 7 : 2;
1389 h
= (int)(h1
+(h2
-h1
)*f
);
1390 // if (x==x2 && y==y2)
1394 //h += int(TILE_H_LIFT_UP/3 * (1-f));
1395 h
+= (int)(f
*(1-f
)*TILE_HUP2
*arc
);
1407 //frame = ((int)(f*4) % 4);
1408 //if (frame==2) frame=0; else if (frame==3) frame=2;
1411 else if (f
==1 || x
==x2
&& y
==y2
) // stationary
1417 frame
= type
? 2 : 1;
1423 RenderTile( false, TILE_SPHERE
,
1425 (int)(y
+(y2
-y
)*f
) - shadow_h
1438 dead ? TILE_SPHERE_OPEN : TILE_SPHERE_DONE,
1447 struct HexPuzzle
: public State
1451 #define MAX_TILECHANGE 64 // TODO: don't have a magic upper limit
1460 TileChange(Pos _p
, Tile _t
, int _i
) : p(_p
), t(_t
), item(_i
)
1462 void Restore(HexPuzzle
* w
)
1464 w
->SetTile(p
,t
,false,false);
1465 w
->SetItem(p
,item
,false,false);
1469 TileChange t
[MAX_TILECHANGE
];
1478 void Add(TileChange
const & tc
)
1480 for (int i
=0; i
<numT
; i
++)
1483 if (numT
>=MAX_TILECHANGE
)
1484 FATAL("numT>=MAX_TILECHANGE");
1488 void New(Dir pmove
, Pos
& pp
, int* items
, double t
, int sc
)
1490 numItems
[0] = items
[0];
1491 numItems
[1] = items
[1];
1493 playerMovement
= pmove
;
1498 void Restore(HexPuzzle
* w
)
1500 for (int i
=numT
-1; i
>=0; i
--)
1504 w
->player
= playerPos
;
1505 w
->player_items
[0] = numItems
[0];
1506 w
->player_items
[1] = numItems
[1];
1507 w
->player_score
= score
;
1509 //w->renderer.player.Add(new PlayerRender(playerPos, w->GetHeight(playerPos), false), w->time);
1514 char* special
[MAP_SIZE
][MAP_SIZE
];
1515 Tile map
[MAP_SIZE
][MAP_SIZE
];
1516 int32_t map_item
[MAP_SIZE
][MAP_SIZE
];
1517 int tileCount
[NumTileTypes
];
1518 int32_t levelPar
, levelDiff
;
1521 int player_items
[2];
1523 int numComplete
, numLevels
, numMastered
, numLevelsFound
;
1530 WorldRenderer renderer
;
1535 Undo undo
[MAX_UNDO
];
1537 LevelInfo
* currentLevelInfo
;
1539 char currentFile
[1000];
1546 LevelInfo
* GetLevelInfo(const char* f
)
1548 if (strstr(f
, "Levels\\") == f
)
1550 if (currentLevelInfo
!=0 && strcmp(currentLevelInfo
->file
, f
)==0)
1551 return currentLevelInfo
;
1556 if (t
<= numComplete
)
1559 static char tmp1
[1000];
1560 static LevelInfo tmp
= {0, "", tmp1
};
1561 sprintf(tmp1
, ngettext("Complete 1 more level to unlock!", "Complete %d more levels to unlock!", t
-numComplete
), t
-numComplete
);
1565 for (unsigned int i
=0; i
<sizeof(levelNames
)/sizeof(levelNames
[0]); i
++)
1566 if (strcmp(f
, levelNames
[i
].file
)==0)
1567 return &levelNames
[i
];
1568 static LevelInfo tmp
= {0, "", _("<<NO NAME>>")};
1572 #ifdef MAP_EDIT_HACKS
1573 int GetAutoTile(const char * level
, bool tiletype
)
1575 FILE* f
= file_open(filename
, "rb");
1579 if (f
&& fscanf(f
, "%d", &version
)==1 && (version
==3 || version
==4))
1581 if (strstr(level
,"mk"))
1584 fgetc(f
); // Remove '\n' character
1587 unsigned char bounds
[4];
1589 fread(&par
, sizeof(par
), 1, f
);
1593 fread(&diff
, sizeof(diff
), 1, f
);
1594 diff
= SWAP32(diff
);
1596 fread(bounds
, sizeof(bounds
), 1, f
);
1597 fread(&playerStart
, sizeof(playerStart
), 1, f
);
1598 playerStart
.x
= SWAP32(playerStart
.x
);
1599 playerStart
.y
= SWAP32(playerStart
.y
);
1603 for (int i
=bounds
[0]; i
<=bounds
[1]; i
++)
1604 for (int j
=bounds
[2]; j
<=bounds
[3]; j
++)
1606 unsigned char comp
= map
[i
][j
] | (map_item
[i
][j
]<<5);
1607 fread(&comp
, sizeof(comp
), 1, f
);
1608 int t
= comp
& 0x1f;
1609 int item
= (comp
>> 5) & 3;
1610 for (int i
=highval
+1; i
<sizeof(value_order
)/sizeof(value_order
[0]); i
++)
1611 if (t
!=0 && t
==value_order
[i
]
1612 || item
!=0 && item
==(value_order
[i
]>>8))
1618 tile
= value_order
[highval
];
1619 if (tile
==0x100) tile
= COLLAPSABLE3
;
1620 if (tile
==0x200) tile
= SWITCH
;
1621 if (tile
==LIFT_UP
) tile
= LIFT_DOWN
;
1625 if (value_order
[highval
] == LIFT_UP
)
1643 numComplete
= numLevels
= numMastered
= numLevelsFound
= 0;
1644 for (int i
=0; i
<MAP_SIZE
; i
++)
1645 for (int j
=0; j
<MAP_SIZE
; j
++)
1646 ActivateSpecial(Pos(i
,j
), 0);
1647 for (int i
=0; i
<MAP_SIZE
; i
++)
1648 for (int j
=0; j
<MAP_SIZE
; j
++)
1649 ActivateSpecial(Pos(i
,j
), 2);
1650 numComplete
= numLevels
= numMastered
= numLevelsFound
= 0;
1651 for (int i
=0; i
<MAP_SIZE
; i
++)
1652 for (int j
=0; j
<MAP_SIZE
; j
++)
1653 ActivateSpecial(Pos(i
,j
), 0);
1659 if (strcmp(mapname
, currentFile
)==0)
1661 // for (int i=0; i<32; i++)
1662 // HintMessage::FlagTile(i);
1663 if (numComplete
>= UNLOCK_SCORING
&& !progress
.general
.scoringOn
)
1665 HintMessage::FlagTile(26);
1666 progress
.general
.scoringOn
= 1;
1667 InitSpecials(); // Re-initialise with gold ones available
1669 HintMessage::FlagTile(25);
1673 for (int i
=0; i
<MAP_SIZE
; i
++)
1674 for (int j
=0; j
<MAP_SIZE
; j
++)
1676 int t
= GetTile(Pos(i
,j
));
1677 int item
= GetItem(Pos(i
,j
));
1679 HintMessage::FlagTile(t
);
1681 HintMessage::FlagTile(item
+20);
1683 HintMessage::FlagTile(EMPTY
);
1692 UpdateCursor(Pos(-1,-1));
1704 player_items
[0] = player_items
[1] = 0;
1706 if (strlen(currentSlot
) == 0)
1713 if (!isFadeRendering
&& time
!=0)
1715 renderer().Add(new FadeRender(-1), time
);
1721 renderer
.Reset(time
);
1724 for (int t
=0; t
<NumTileTypes
; t
++)
1727 for (int i
=0; i
<MAP_SIZE
; i
++)
1728 for (int j
=0; j
<MAP_SIZE
; j
++)
1731 int item
= GetItem(p
);
1733 renderer(p
,true).Add(new ItemRender(item
, GetTile(p
)==EMPTY
, p
), time
);
1738 for (int i
=0; i
<MAP_SIZE
; i
++)
1739 for (int j
=0; j
<MAP_SIZE
; j
++)
1749 renderer(p
).Add(new TileRender(t
, p
), time
);
1753 renderer
.player
.Add(new PlayerRender(player
, GetHeight(player
), dead
), time
);
1755 renderer
.player
.Add(new PlayerRender(Pos(-100,-100), 0, true), time
);
1758 int bounds
[4] = {player
.getScreenX(),player
.getScreenX(),player
.getScreenY(),player
.getScreenY()};
1759 for (int i
=0; i
<MAP_SIZE
; i
++)
1760 for (int j
=0; j
<MAP_SIZE
; j
++)
1763 if (map
[i
][j
] !=0 || map_item
[i
][j
]!=0)
1765 int x1
= p
.getScreenX();
1766 int y1
= p
.getScreenY();
1767 int x2
= x1
+ TILE_W3
;
1768 int y2
= y1
+ TILE_H2
;
1769 y1
-= TILE_H2
; // Make sure objects/player will be properly visible
1771 if (x1
<bounds
[0]) bounds
[0] = x1
;
1772 if (x2
>bounds
[1]) bounds
[1] = x2
;
1773 if (y1
<bounds
[2]) bounds
[2] = y1
;
1774 if (y2
>bounds
[3]) bounds
[3] = y2
;
1781 sx
= bounds
[0] - int(TILE_W2
*6.35);
1782 sy
= (bounds
[3] + bounds
[2] - SCREEN_H
) / 2 - TILE_H2
/2;
1786 sx
= (bounds
[1] + bounds
[0] - SCREEN_W
) / 2 - TILE_W3
/2;
1787 sy
= (bounds
[3] + bounds
[2] - SCREEN_H
) / 2 - TILE_H2
/2;
1799 // time = 1; // Guarantee we can't try and do things at time=0
1801 renderer().Add(new ScrollRender(sx
, sy
), time
);
1802 renderer().Add(new FadeRender(1), time
);
1807 char* ReadAll(FILE* f
)
1810 // FIXME: According to http://userpage.fu-berlin.de/~ram/pub/pub_jf47ht20Ht/c_faq_de
1811 // undefined for binary streams! (POSIX does not differ between ascii and binary, so
1812 // we are on the safe side in Linux)
1813 fseek(f
, 0, SEEK_END
);
1815 fseek(f
, 0, SEEK_SET
);
1816 char* c
= loadPtr
= new char [size
];
1817 endLoad
= loadPtr
+ size
;
1818 fread(c
, 1, size
, f
);
1822 static char *loadPtr
, *endLoad
;
1823 static unsigned int fread_replace(void* d
, unsigned int size
, unsigned int num
, FILE*)
1825 unsigned int remain
= (endLoad
- loadPtr
) / size
;
1826 if (remain
< num
) num
= remain
;
1827 memcpy(d
, loadPtr
, size
*num
);
1828 loadPtr
+= size
*num
;
1832 int GetPar(const char * level
, bool getdiff
=false)
1834 if (strcmp(level
, currentFile
)==0)
1835 return getdiff
? levelDiff
: levelPar
;
1837 #ifdef USE_LEVEL_PACKFILE
1838 PackFile1::Entry
* e
= levelFiles
.Find(level
);
1840 loadPtr
= (char*)e
->Data();
1841 endLoad
= loadPtr
+ e
->DataLen();
1845 FILE* f
= file_open(level
, "rb");
1848 typedef unsigned int _fn(void*, unsigned int, unsigned int, FILE*);
1849 _fn
* fn
= (loadPtr
? (_fn
*)fread_replace
: (_fn
*)fread
);
1851 int32_t par
= 99999, diff
= 0;
1855 return getdiff
? diff
: par
;
1857 fn(&version
, 2, 1, f
); // skip to relevant point
1859 if (fn(&par
, sizeof(par
), 1, f
) != 1)
1863 size_t ret
= fn(&diff
, sizeof(diff
), 1, f
);
1864 diff
= SWAP32(diff
);
1865 if (ret
!= 1 || diff
<0 || diff
>10)
1868 #ifdef USE_LEVEL_PACKFILE
1869 loadPtr
= endLoad
= 0;
1875 return getdiff
? diff
: par
;
1878 bool LoadSave(const char * filename
, bool save
)
1885 showScoring
= false;
1886 LevelSave
* l
= progress
.GetLevel(filename
, true);
1887 if (progress
.general
.scoringOn
&& l
&& l
->Completed() )
1891 #ifdef USE_LEVEL_PACKFILE
1894 PackFile1::Entry
* e
= levelFiles
.Find(filename
);
1895 if (!e
) return false;
1897 if (currentFile
!= filename
) // equal (overlapping) strings are forbidden
1898 strcpy(currentFile
, filename
);
1899 currentLevelInfo
= GetLevelInfo(currentFile
);
1901 loadPtr
= (char*)e
->Data();
1902 endLoad
= loadPtr
+ e
->DataLen();
1903 _LoadSave(NULL
, save
);
1904 loadPtr
= endLoad
= 0;
1910 FILE* f
= file_open(filename
, save
? "wb" : "rb");
1913 strcpy(currentFile
, filename
);
1915 currentLevelInfo
= GetLevelInfo(currentFile
);
1919 char* data
= ReadAll(f
);
1922 loadPtr
= endLoad
= 0;
1937 /** \brief Writes/reads game status to/from a file
1939 * The game data file is written in little endian so it can be shared
1940 * across different machines.
1942 void _LoadSave(FILE* f
, bool save
)
1944 typedef unsigned int _fn(void*, unsigned int, unsigned int, FILE*);
1945 _fn
* fn
= save
? (_fn
*)fwrite
: (loadPtr
? (_fn
*)fread_replace
: (_fn
*)fread
);
1948 int version
= VERSION
; // 1--9
1950 fprintf(f
, "%d\n", version
);
1954 if (fn(&c
, 1, 1, f
) != 1)
1958 // Remove '\n' character
1964 for (int i
=0; i
<MAP_SIZE
; i
++)
1965 for (int j
=0; j
<MAP_SIZE
; j
++)
1967 delete [] special
[i
][j
];
1974 for (int i
=0; i
<MAP_SIZE
; i
++)
1975 for (int j
=0; j
<MAP_SIZE
; j
++) {
1976 map
[i
][j
] = SWAP32(map
[i
][j
]);
1977 fn(&map
[i
][j
], sizeof(map
[i
][j
]), 1, f
);
1978 map
[i
][j
] = SWAP32(map
[i
][j
]);
1981 player
.x
= SWAP32(player
.x
);
1982 player
.y
= SWAP32(player
.y
);
1983 fn(&player
, sizeof(player
), 1, f
);
1984 player
.x
= SWAP32(player
.x
);
1985 player
.y
= SWAP32(player
.y
);
1987 for (int i
=0; i
<MAP_SIZE
; ++i
)
1988 for (int j
=0; j
<MAP_SIZE
; ++j
)
1989 map_item
[i
][j
] = SWAP32(map_item
[i
][j
]);
1990 if (fn(map_item
, sizeof(map_item
), 1, f
) == 0)
1991 memset(map_item
, 0, sizeof(map_item
));
1992 for (int i
=0; i
<MAP_SIZE
; ++i
)
1993 for (int j
=0; j
<MAP_SIZE
; ++j
)
1994 map_item
[i
][j
] = SWAP32(map_item
[i
][j
]);
1996 else if (version
>=2 && version
<=4)
1998 unsigned char bounds
[4];
2001 bounds
[0]=bounds
[1]=player
.x
;
2002 bounds
[2]=bounds
[3]=player
.y
;
2003 for (int i
=0; i
<MAP_SIZE
; i
++)
2004 for (int j
=0; j
<MAP_SIZE
; j
++)
2005 if (map
[i
][j
] !=0 || map_item
[i
][j
]!=0 || special
[i
][j
]!=0)
2007 if (i
<bounds
[0]) bounds
[0] = i
;
2008 if (i
>bounds
[1]) bounds
[1] = i
;
2009 if (j
<bounds
[2]) bounds
[2] = j
;
2010 if (j
>bounds
[3]) bounds
[3] = j
;
2015 memset(map
, 0, sizeof(map
));
2016 memset(map_item
, 0, sizeof(map_item
));
2020 levelPar
= SWAP32(levelPar
);
2021 fn(&levelPar
, 1, sizeof(levelPar
), f
);
2022 levelPar
= SWAP32(levelPar
);
2028 levelDiff
= SWAP32(levelDiff
);
2029 fn(&levelDiff
, 1, sizeof(levelDiff
), f
);
2030 levelDiff
= SWAP32(levelDiff
);
2035 fn(bounds
, sizeof(bounds
), 1, f
);
2036 player
.x
= SWAP32(player
.x
);
2037 player
.y
= SWAP32(player
.y
);
2038 fn(&player
, sizeof(player
), 1, f
);
2039 player
.x
= SWAP32(player
.x
);
2040 player
.y
= SWAP32(player
.y
);
2042 int offsetx
=0, offsety
=0;
2044 if (!save
&& bounds
[1]-bounds
[0]<15) // Hacky - don't recenter map...
2046 // Re-position map to top left (but leave a bit of space)
2047 // (This ensures the laser/boat effects don't clip prematurely against the edges of the screen)
2048 offsetx
= SCREEN_W
/2/TILE_W2
+ 1 - (bounds
[0]+bounds
[1]/2);
2049 offsety
= SCREEN_H
/2/TILE_H2
+ SCREEN_W
/2/TILE_W2
- (bounds
[2]+bounds
[3]/2);
2050 offsetx
= MAX(0, offsetx
);
2051 offsety
= MAX(0, offsety
);
2052 // if (bounds[0] > 2)
2053 // offsetx = 2 - bounds[0];
2054 // if (bounds[2] > 2)
2055 // offsety = 2 - bounds[2];
2057 bounds
[0] += offsetx
;
2058 bounds
[1] += offsetx
;
2059 bounds
[2] += offsety
;
2060 bounds
[3] += offsety
;
2061 player
.x
+= offsetx
;
2062 player
.y
+= offsety
;
2064 for (int i
=bounds
[0]; i
<=bounds
[1]; i
++)
2065 for (int j
=bounds
[2]; j
<=bounds
[3]; j
++)
2067 unsigned char comp
= map
[i
][j
] | (map_item
[i
][j
]<<5);
2068 fn(&comp
, sizeof(comp
), 1, f
);
2069 map
[i
][j
] = comp
& 0x1f;
2070 map_item
[i
][j
] = (comp
>> 5) & 3;
2075 for (int i
=bounds
[0]; i
<=bounds
[1]; i
++)
2076 for (int j
=bounds
[2]; j
<=bounds
[3]; j
++)
2079 int16_t len
= strlen(special
[i
][j
]);
2080 unsigned char x
=i
, y
=j
;
2081 fn(&x
, sizeof(x
), 1, f
);
2082 fn(&y
, sizeof(y
), 1, f
);
2084 fn(&len
, sizeof(len
), 1, f
);
2086 fn(special
[i
][j
], 1, len
, f
);
2094 if (!fn(&x
, sizeof(x
), 1, f
))
2096 fn(&y
, sizeof(y
), 1, f
);
2097 x
+= offsetx
; y
+= offsety
;
2098 fn(&len
, sizeof(len
), 1, f
);
2101 char* tmp
= new char[len
+1];
2105 SetSpecial(Pos(x
,y
), tmp
, true, false);
2110 return; // Unsupported version!
2114 // Save when returning to map!
2117 progress
.general
.completionPercentage
= numComplete
*100/numLevels
;
2118 progress
.general
.masteredPercentage
= numMastered
*100/numLevels
;
2119 LoadSaveProgress(true);
2123 void SetTile(Pos
const & p
, Tile t
, bool updateRenderer
=true, bool undoBuffer
=true)
2125 if (p
.x
<0 || p
.x
>MAP_SIZE
)
2127 if (p
.y
<0 || p
.y
>MAP_SIZE
)
2129 if (map
[p
.x
][p
.y
] == t
)
2131 if (map
[p
.x
][p
.y
] == t
)
2134 tileCount
[map
[p
.x
][p
.y
]]--;
2138 undo
[numUndo
].Add(Undo::TileChange(p
,GetTile(p
),GetItem(p
)));
2143 renderer(p
).Add(new TileRender(t
, p
), time
);
2146 Tile
GetTile(Pos
const & p
)
2148 if (p
.x
<0 || p
.x
>=MAP_SIZE
)
2150 if (p
.y
<0 || p
.y
>=MAP_SIZE
)
2152 return map
[p
.x
][p
.y
];
2155 int GetHeight(Pos
const & p
)
2157 return tileSolid
[GetTile(p
)]==1;
2160 char* GetSpecial(Pos
const & p
)
2162 if (p
.x
<0 || p
.x
>=MAP_SIZE
)
2164 if (p
.y
<0 || p
.y
>=MAP_SIZE
)
2166 return special
[p
.x
][p
.y
];
2169 void SetSpecial(Pos
const & p
, char * d
, bool use_pointer
=false, bool auto_activate
=true)
2171 if (p
.x
<0 || p
.x
>=MAP_SIZE
|| p
.y
<0 || p
.y
>=MAP_SIZE
)
2178 delete [] special
[p
.x
][p
.y
];
2179 if (!use_pointer
&& d
)
2182 special
[p
.x
][p
.y
] = new char [strlen(d
) + 1];
2183 strcpy(special
[p
.x
][p
.y
], d
);
2186 special
[p
.x
][p
.y
] = d
;
2188 if (special
[p
.x
][p
.y
]==0)
2189 renderer(p
,true).Add(new ItemRender(GetItem(p
), GetTile(p
)==EMPTY
, p
), time
);
2190 else if (auto_activate
)
2191 ActivateSpecial(p
, 0);
2194 int GetLevelState(Pos
const & p
, int recurse
=0)
2196 char* x
= GetSpecial(p
);
2199 LevelSave
* l
= progress
.GetLevel(x
, false);
2203 if (strcmp(x
, STARTING_LEVEL
)==0)
2205 if (x
[0]=='_' && l
&& l
->unlocked
)
2208 if (l
&& l
->Completed())
2215 int par
= GetPar(x
);
2216 if (progress
.general
.scoringOn
&& l
->PassesPar( par
))
2217 t
= l
->BeatsPar( par
) ? 40 : 4;
2223 for (Dir d
=0; d
<MAX_DIR
; d
++)
2225 int i
= GetLevelState(p
+d
, 1);
2226 // if (i>1 || i==1 && t>1)
2227 if (i
>=1 && t
>2 || t
>=1 && i
>2)
2238 void ActivateSpecial(Pos
const & p
, int type
)
2240 if (p
.x
<0 || p
.x
>=MAP_SIZE
|| p
.y
<0 || p
.y
>=MAP_SIZE
)
2243 char * x
= special
[p
.x
][p
.y
];
2245 if (x
==0 || x
[0]==0)
2248 if (type
==2 && x
[0]=='_') // Phase2 init - unlock
2250 int t
= GetLevelState(p
);
2251 int target
= atoi(x
+1), targetM
= 0;
2252 if (target
>1000) targetM
=target
=target
-100;
2253 if (t
> 1 && numComplete
>= target
&& numMastered
>= targetM
)
2255 LevelSave
* l
= progress
.GetLevel(x
, true);
2260 renderer(p
, true).Add(new LevelSelectRender(p
, 5, GetLevelState(p
)>>8), time
+0.01);
2261 renderer().Add(new ExplosionRender(p
, 0), time
+ 0.6);
2262 renderer().Add(new ExplosionRender(p
, 1), time
+ 1.1);
2263 renderer(p
, true).Add(new LevelSelectRender(p
, -1, GetLevelState(p
)>>8), time
+ 1.1);
2268 if (type
==0) // Init & count levels
2272 int t
= GetLevelState(p
);
2273 int unlock
= progress
.GetLevel(x
, true)->unlocked
;
2274 LevelSelectRender
* lsr
= new LevelSelectRender( p
, unlock
? -1 : (t
>>8) ? 5 : 1, t
>>8 );
2275 if ((t
>>8) && p
.x
> mapRightBound
) mapRightBound
= p
.x
;
2276 #ifdef MAP_EDIT_HACKS
2277 lsr
->magic
= -atoi(x
+1);
2278 SetTile(p
, LIFT_DOWN
, true, false);
2280 SetTile(p
, EMPTY
, true, false);
2282 renderer(p
,true).Add(lsr
, time
);
2286 //printf("Level: %s\n", x);
2288 int t
= GetLevelState(p
);
2290 if (t
&& !GetItem(p
))
2297 currentLevelInfo
= 0;
2301 LevelSave
* l
= progress
.GetLevel(x
, true);
2306 renderer(p
, true).Add(new LevelSelectRender(p
, -1, 0), time
+0.01);
2307 renderer().Add(new ExplosionRender(p
, 0), time
+ 0.6);
2308 renderer(p
, true).Add(new LevelSelectRender(p
, t
& 0xff, t
>>8), time
+ 0.6);
2312 if (p
.x
> mapRightBound
) mapRightBound
= p
.x
;
2319 LevelSelectRender
* lsr
= new LevelSelectRender( p
, t
& 0xff, t
>>8 );
2321 #ifdef MAP_EDIT_HACKS
2323 int t
= GetAutoTile(x
, true);
2324 int v
= GetAutoTile(x
, false);
2325 if (MAP_EDIT_HACKS_DISPLAY_UNLOCK
)
2328 lsr
->magic
= GetPar(x
, true);
2330 SetTile(p
, t
, true, false);
2332 SetTile(p
, EMPTY
, true, false);
2335 renderer(p
,true).Add(lsr
, time
);
2340 if (type
==1 && x
[0]!='_') // Clicked on
2342 int t
= GetLevelState(p
);
2350 void SetItem(Pos
const & p
, int t
, bool updateRenderer
=true, bool undoBuffer
=true)
2352 if (p
.x
<0 || p
.x
>MAP_SIZE
)
2354 if (p
.y
<0 || p
.y
>MAP_SIZE
)
2356 if (map_item
[p
.x
][p
.y
] == t
)
2360 undo
[numUndo
].Add(Undo::TileChange(p
,GetTile(p
),GetItem(p
)));
2362 map_item
[p
.x
][p
.y
] = t
;
2365 renderer(p
,true).Add(new ItemRender(t
, GetTile(p
)==EMPTY
, p
), time
);
2368 Tile
GetItem(Pos
const & p
)
2370 if (p
.x
<0 || p
.x
>=MAP_SIZE
)
2372 if (p
.y
<0 || p
.y
>=MAP_SIZE
)
2374 return map_item
[p
.x
][p
.y
];
2377 void LoadSaveProgress(bool save
)
2379 FILE* f
= file_open(currentSlot
, save
? "wb" : "rb");
2382 progress
.LoadSave(f
, save
);
2393 LoadSaveProgress(false);
2397 LoadSaveProgress(true);
2400 SDL_Surface
* Load(const char * bmp
, bool colourKey
=true)
2402 typedef unsigned int uint32
;
2405 SDL_Surface
* g
= 0;
2408 if (strstr(bmp
, ".bmp"))
2410 g
= SDL_LoadBMP(bmp
);
2414 strcpy(strstr(out
, ".bmp"), ".dat");
2416 // SDL_PixelFormat p;
2418 // SDL_Surface* tmp = SDL_ConvertSurface(g, &p, SDL_SWSURFACE);
2420 short w
=g
->w
, h
=g
->h
;
2421 char* buf
= (char*) g
->pixels
;
2424 while (IsEmpty(g
, w
-1, 0, 1, h
) && w
>1)
2426 while (IsEmpty(g
, 0, h
-1, w
, 1) && h
>1)
2430 FILE* f
= file_open(out
, "wb");
2431 fwrite(&w
, sizeof(w
), 1, f
);
2432 fwrite(&h
, sizeof(h
), 1, f
);
2434 uint32 mask
= IMAGE_DAT_OR_MASK
;
2435 for (int i
=0; i
<(int)w
*h
; )
2437 uint32 c
= (*(uint32
*)&buf
[(i
%w
)*3 + (i
/w
)*g
->pitch
] | mask
);
2439 while (i
< (int)w
*h
&& c
== (*(uint32
*)&buf
[(i
%w
)*3 + (i
/w
)*g
->pitch
] | mask
))
2448 fwrite(&c
, sizeof(c
), 1, f
);
2451 fwrite(&i0
, sizeof(i0
), 1, f
);
2461 FILE* f
= file_open(bmp
, "rb");
2462 if (!f
) FATAL("Unable to open file", bmp
);
2465 fread(&w
, sizeof(w
), 1, f
);
2466 fread(&h
, sizeof(h
), 1, f
);
2469 if (w
>1500 || h
>1500 || w
<=0 || h
<=0) FATAL("Invalid file", bmp
);
2471 tmp
= new uint32
[(int)w
*h
];
2475 for (int p
=0; p
<(int)w
*h
; p
++)
2481 fread(&c
, sizeof(c
), 1, f
);
2485 fread(&cnt
, sizeof(cnt
), 1, f
);
2489 tmp
[p
] = c
| 0xff000000;
2492 g
= SDL_CreateRGBSurfaceFrom(tmp
, w
, h
, 32, w
*4,
2501 if (!g
) FATAL("Unable to create SDL surface");
2503 SDL_SetColorKey(g
, SDL_SRCCOLORKEY
, SDL_MapRGB(g
->format
, WATER_COLOUR
));
2504 SDL_Surface
* out
= SDL_DisplayFormat(g
);
2507 if (!out
) FATAL("Unable to create SDL surface (SDL_DisplayFormat)");
2511 #ifdef USE_LEVEL_PACKFILE
2512 PackFile1 levelFiles
;
2516 SDL_WM_SetCaption(GAMENAME
, 0);
2520 #ifdef USE_LEVEL_PACKFILE
2521 FILE* f
= file_open("levels.dat", "rb");
2523 FATAL("Unable to open file", "levels.dat");
2533 currentLevelInfo
= 0;
2540 memset(map
, 0, sizeof(map
));
2541 memset(map_item
, 0, sizeof(map_item
));
2542 memset(special
, 0, sizeof(special
));
2546 // player = Pos(1,11);
2556 progress
.GetLevel(STARTING_LEVEL
, true)->unlocked
= 1;
2557 if (!progress
.GetLevel(STARTING_LEVEL
, true)->Completed())
2559 LoadSave(STARTING_LEVEL
, false);
2565 LoadSave(mapname
, false);
2570 if (!activeMenu
|| activeMenu
->renderBG
)
2572 SDL_Rect src
= {0,0,screen
->w
,screen
->h
};
2573 SDL_Rect dst
= {0,0,screen
->w
,screen
->h
};
2576 int boundW
= mapBG
->w
;
2578 boundW
= MIN(boundW
, (mapRightBound
+4) * TILE_W2
- TILE_W1
);
2580 src
.x
= scrollX
- initScrollX
;
2581 if (src
.x
+src
.w
> boundW
)
2583 int diff
= src
.x
+src
.w
- boundW
;
2594 //scrollY = initScrollY;
2597 mapScrollX
= scrollX
;
2599 SDL_BlitSurface(mapBG
, &src
, screen
, &dst
);
2602 SDL_BlitSurface(gradient
, &src
, screen
, &dst
);
2604 renderer
.Render(time
, true);
2606 if (!hintsDone
&& !isFadeRendering
)
2613 SDL_Rect src
= {0,SCREEN_H
-1,SCREEN_W
,1};
2614 SDL_Rect dst
= {0,SCREEN_H
-1,SCREEN_W
,1};
2615 for (int i
=0; i
<SCREEN_H
; i
++)
2618 dst
.y
= src
.y
= SCREEN_H
-1-i
;
2624 src
.x
+= (int)( sin(i
*0.9 + time
*3.7) * sin(i
*0.3 + time
*0.7)*4 );
2625 src
.y
+= (int)( (sin(i
*0.3 - time
*2.2) * sin(i
*0.48 + time
*0.47) - 1) * 1.99 );
2629 src
.x
+= (int)( sin(i
*0.5 + time
*6.2) * sin(i
*0.3 + time
*1.05) * 5 );
2630 src
.y
+= (int)( (sin(i
*0.4 - time
*4.3) * sin(i
*0.08 + time
*1.9) - 1) * 2.5 );
2632 SDL_BlitSurface(screen
, &src
, screen
, &dst
);
2637 SDL_BlitSurface(mapBG2
, &src
, screen
, &dst
);
2639 renderer
.Render(time
, false);
2641 if (!isRenderMap
&& !isMap
&& !isFadeRendering
)
2643 int v
[3] = {player_items
[0], player_items
[1], player_score
};
2644 if (numUndo
> 1 && time
< undo
[numUndo
-2].endTime
)
2647 while (i
>1 && time
<undo
[i
-1].time
)
2649 v
[0] = undo
[i
].numItems
[0];
2650 v
[1] = undo
[i
].numItems
[1];
2651 v
[2] = undo
[i
].score
;
2653 if (numUndo
>1 && time
< undo
[0].time
)
2656 /* TRANSLATORS: Anti-Ice are pickups, which turn ice plates into solid
2657 plates once you step on them. Each pickup changes one ice plate */
2658 Print(0,0,_("Anti-Ice: %d"), v
[0]);
2659 Print(0,FONT_SPACING
,_("Jumps: %d"), v
[1]);
2660 Print(0,FONT_SPACING
*2,_("Score: %d (%d)"), v
[2], player_score
);
2661 /* TRANSLATORS: Par is similar to golf, a pre defined score which you
2662 can attempt to beat */
2663 Print(0,FONT_SPACING
*3,_("Par: %d"), levelPar
);
2664 Print(0,FONT_SPACING
*4,_("Diff: %d"), levelDiff
);
2667 Print(0, SCREEN_H
-FONT_SPACING
, _(" Par: %d Current: %d"), levelPar
, v
[2]);
2670 Print(0,0,_(" Anti-Ice: %d"), v
[0]);
2672 Print(0,0,_(" Jumps: %d"), v
[1]);
2675 if (isRenderMap
&& isMap
&& !isFadeRendering
)
2678 Print(0,0,_("Points: %d"), numComplete
+numMastered
);
2679 Print(0,FONT_SPACING
,_("Discovered: %d%% (%d/%d)"), numLevelsFound
*100/numLevels
, numLevelsFound
, numLevels
);
2680 Print(0,FONT_SPACING
*2,_("Complete: %d%% (%d)"), numComplete
*100/numLevels
, numComplete
);
2681 Print(0,FONT_SPACING
*3,_("Mastered: %d%% (%d)"), numMastered
*100/numLevels
, numMastered
);
2683 if (numComplete
==numLevels
&& progress
.general
.endSequence
>0)
2684 Print(0, SCREEN_H
-FONT_SPACING
, _(" %d%% Mastered"), numMastered
*100/numLevels
);
2686 Print(0, SCREEN_H
-FONT_SPACING
, _(" %d%% Complete"), numComplete
*100/numLevels
);
2688 if (numMastered
>= numLevels
&& progress
.general
.endSequence
< 2)
2690 progress
.general
.endSequence
= 2;
2691 LoadSaveProgress(true);
2693 new Fader(-1, -7, 0.3);
2695 if (numComplete
>= numLevels
&& progress
.general
.endSequence
< 1)
2697 progress
.general
.endSequence
= 1;
2698 LoadSaveProgress(true);
2700 new Fader(-1, -5, 0.3);
2704 if ((currentLevelInfo
|| noMouse
) && isMap
&& isRenderMap
&& !activeMenu
&& isFadeRendering
<=0)
2711 int pad
= SCREEN_W
/80;
2712 // SDL_Rect src = {0, 0, uiGraphics->w, uiGraphics->h};
2713 SDL_Rect dst
= {pad
, SCREEN_H
-TILE_H2
-pad
, 0, 0};
2714 // dst.x = p.getScreenX() + TILE_W3/2 - scrollX;
2715 // dst.y = p.getScreenY() - src.h/2 - scrollY;
2716 dst
.x
= p
.getScreenX() - scrollX
;
2717 dst
.y
= p
.getScreenY() - scrollY
- FONT_SPACING
*3 - FONT_SPACING
/2;
2718 // if (dst.x > SCREEN_W*2/3) dst.x -= TILE_W3 + src.w;
2719 // if (dst.y+src.h > screen->h-pad) dst.y = screen->h-pad - src.h;
2721 RenderTile(false, 0, p
.getScreenX(), p
.getScreenY());
2722 // SDL_BlitSurface(uiGraphics, &src, screen, &dst);
2724 // dst.x += src.w/2;
2726 if (currentLevelInfo
)
2730 PrintC(true, dst
.x
, dst
.y
- FONT_SPACING
/4, currentLevelInfo
->name
);
2732 if (currentLevelInfo
->file
[0]!=0)
2734 if (player_score
> 0)
2736 if (progress
.general
.scoringOn
)
2738 PrintC(false, dst
.x
, dst
.y
+ FONT_SPACING
*4 - FONT_SPACING
/4, _("Best:% 3d"), player_score
);
2739 PrintC(false, dst
.x
, dst
.y
+ FONT_SPACING
*5 - FONT_SPACING
/4, _("Par:% 3d"), levelPar
);
2742 PrintC(false, dst
.x
, dst
.y
+ FONT_SPACING
*4 - FONT_SPACING
/4, _("Completed"), player_score
);
2745 PrintC(false, dst
.x
, dst
.y
+ FONT_SPACING
*4 - FONT_SPACING
/4, _("Incomplete"), player_score
);
2751 if (win
&& numUndo
> 0 && time
> undo
[numUndo
-1].endTime
+ 2)
2753 if (currentFile
[0] && winFinal
==0)
2755 LevelSave
* l
= progress
.GetLevel(currentFile
, true);
2757 new WinLoseScreen(true, player_score
, showScoring
? levelPar
: 0, l
&& showScoring
&& l
->Completed() ? l
->GetScore() : 0);
2759 if (l
->IsNewCompletionBetter(player_score
))
2761 l
->SetScore(player_score
);
2763 l
->SetSolution(numUndo
);
2765 for (int i
=0; i
<numUndo
; i
++)
2766 l
->SetSolutionStep(i
, undo
[i
].playerMovement
);
2777 // Move up "level complete" writing so it doesn't feel like everything's ground to a halt...
2778 if (win
&& numUndo
> 0 && time
> undo
[numUndo
-1].endTime
&& !winFinal
)
2780 double t
= (time
- undo
[numUndo
-1].endTime
) / 2;
2784 int y
= SCREEN_H
/3 - FONT_SPACING
+ 1;
2785 y
= SCREEN_H
+ int((y
-SCREEN_H
)*t
);
2786 PrintC(true, SCREEN_W
/2, y
, _("Level Complete!"));
2791 activeMenu
->Render();
2798 RenderTile(false, editTile
, mousex
+scrollX
, mousey
+scrollY
);
2805 return tileCount
[t
];
2807 int Swap(Tile t
, Tile t2
)
2809 const int num
= Count(t
) + Count(t2
);
2810 if (t
==t2
|| num
==0)
2811 return Count(t
); // Nothing to do...
2814 for (int x
=0; x
<MAP_SIZE
; x
++)
2815 for (int y
=0; y
<MAP_SIZE
; y
++)
2817 if (GetTile(Pos(x
,y
))==t
)
2820 SetTile(Pos(x
,y
), t2
);
2822 else if (GetTile(Pos(x
,y
))==t2
)
2825 SetTile(Pos(x
,y
), t
);
2832 int Replace(Tile t
, Tile t2
)
2834 const int num
= Count(t
);
2835 if (t
==t2
|| num
==0)
2836 return num
; // Nothing to do...
2839 for (int x
=0; x
<MAP_SIZE
; x
++)
2840 for (int y
=0; y
<MAP_SIZE
; y
++)
2847 SetTile(p
, t2
, false);
2849 if (t
==COLLAPSE_DOOR
&& t2
==COLLAPSABLE
)
2850 renderer(p
).Add(new BuildRender(p
, -1, 1, 1), time
+ (rand() & 255)*0.001);
2851 else if (t
==COLLAPSE_DOOR2
&& t2
==COLLAPSABLE2
)
2852 renderer(p
).Add(new BuildRender(p
, -1, 1, 1, 1), time
+ (rand() & 255)*0.001);
2873 void UpdateCursor(Pos
const & s
)
2876 if (s
.x
!=_s
.x
|| s
.y
!=_s
.y
)
2880 char* sp
= GetSpecial(s
);
2887 currentLevelInfo
= 0;
2888 levelPar
= player_score
= -1;
2889 if (GetLevelState(s
)>=2)
2891 LevelSave
* l
= progress
.GetLevel(sp
, true);
2894 currentLevelInfo
= GetLevelInfo(sp
);
2895 levelPar
= GetPar(sp
);
2896 player_score
= l
->GetScore();
2902 sprintf(tmp
, _("Special(%d,%d): %s (%d)"), s
.x
, s
.y
, sp
? sp
: _("<None>"), GetPar(sp
));
2903 SDL_WM_SetCaption(tmp
, NULL
);
2906 else if (currentFile
[0])
2909 SDL_WM_SetCaption(currentFile
, NULL
);
2912 currentLevelInfo
= 0;
2917 virtual void Mouse(int x
, int y
, int dx
, int dy
, int button_pressed
, int button_released
, int button_held
)
2921 activeMenu
->Mouse(x
,y
,dx
,dy
,button_pressed
,button_released
,button_held
);
2925 if (isFadeRendering
)
2930 if (button_pressed
==2 || button_pressed
==4 && isMap
)
2932 KeyPressed(SDLK_ESCAPE
, 0);
2933 keyState
[SDLK_ESCAPE
] = 0;
2941 Pos s
= Pos::GetFromWorld(x
,y
);
2942 if (tileSolid
[GetTile(Pos::GetFromWorld(x
,y
+TILE_HUP
))] == 1)
2943 s
= Pos::GetFromWorld(x
,y
+TILE_HUP
);
2950 if (button_held
& ~button_pressed
& 4)
2959 if (isMap
&& (button_pressed
& 1))
2961 ActivateSpecial(s
, 1);
2964 if (!isMap
&& win
&& winFinal
)
2966 if (button_pressed
& 1)
2974 if((button_pressed
& 1) || (button_held
& 1) && (numUndo
==0 || time
>=undo
[numUndo
-1].endTime
))
2976 if(s
.x
==player
.x
&& s
.y
==player
.y
)
2978 // Don't activate jump powerup without a new click
2979 if (button_pressed
& 1)
2982 else if(s
.x
==player
.x
&& s
.y
<player
.y
)
2984 else if(s
.x
==player
.x
&& s
.y
>player
.y
)
2986 else if(s
.y
==player
.y
&& s
.x
<player
.x
)
2988 else if(s
.y
==player
.y
&& s
.x
>player
.x
)
2990 else if(s
.y
+s
.x
==player
.y
+player
.x
&& s
.x
>player
.x
)
2992 else if(s
.y
+s
.x
==player
.y
+player
.x
&& s
.x
<player
.x
)
2995 if ((button_pressed
& 4) || (button_held
& 4) && (undoTime
< 0))
3002 if (!button_pressed
&& !button_held
)
3005 if (button_pressed
==1)
3007 editTile
= GetItem(s
)==1 ? -3 : GetItem(s
)==2 ? -2 : -1;
3009 if (button_held
==1 || button_pressed
==1)
3013 SetTile(s
, editTile
, true, false);
3015 SetItem(s
, editTile
==-2 ? 0 : editTile
==-1 ? 1 : 2, true, false);
3018 if (button_pressed
==2)
3020 editTile
= GetTile(s
);
3023 if (button_pressed
==8)
3025 editTile
=editTile
-1;
3026 if (editTile
<=0) editTile
=NumTileTypes
-1;
3029 if (button_pressed
==16)
3031 editTile
=editTile
+1;
3032 if (editTile
<=0) editTile
=1;
3033 if (editTile
==NumTileTypes
) editTile
=0;
3036 if (button_pressed
==64)
3041 renderer
.player
.Reset(-1);
3042 renderer
.player
.Add(new PlayerRender(player
, GetHeight(player
), dead
), 0);
3045 if (button_pressed
==256)
3047 char* fn
= LoadSaveDialog(false, true, _("Select level"));
3050 char * l
= strstr(fn
, "Levels");
3053 FILE * f
= file_open(l
,"rb");
3058 else if (l
[6]!=0 && l
[7]=='_')
3061 UpdateCursor(Pos(-1,-1));
3064 if (button_pressed
==512)
3066 SetSpecial(s
, NULL
);
3067 UpdateCursor(Pos(-1,-1));
3069 if (button_pressed
==1024)
3071 static char x
[1000] = "";
3072 if (!(s
.x
<0 || s
.x
>=MAP_SIZE
|| s
.y
<0 || s
.y
>=MAP_SIZE
))
3077 strcpy(x
, GetSpecial(s
));
3080 SetSpecial(s
, tmp
[0] ? tmp
: 0);
3082 SetTile(s
, EMPTY
, true, false);
3086 if (button_pressed
==32)
3088 editTile
= editTile
<0 ? 1 : -1;
3093 void CheckFinished()
3096 if (Count(COLLAPSABLE
)==0)
3098 if (Replace(COLLAPSE_DOOR
, COLLAPSABLE
) == 0)
3102 Replace(SWITCH
, NORMAL
);
3107 if (Count(COLLAPSABLE2
)==0)
3108 if (Replace(COLLAPSE_DOOR2
, COLLAPSABLE2
))
3114 bool Collide(Pos p
, bool high
)
3116 Tile t
= GetTile(p
);
3121 return tileSolid
[t
]==1;
3128 if (numUndo
==0) return;
3130 UndoDone(); // Complete previous undo...
3134 if (time
> undo
[numUndo
].endTime
)
3135 time
= undo
[numUndo
].endTime
;
3136 undoTime
= undo
[numUndo
].time
;
3138 undo
[numUndo
].Restore(this);
3144 renderer
.Reset(undoTime
);
3148 void ScoreDestroy(Pos p
)
3150 Tile t
= GetTile(p
);
3151 if (t
==COLLAPSABLE
|| t
==COLLAPSE_DOOR
)
3153 else if (t
!= EMPTY
)
3159 bool LaserTile(Pos p
, int mask
, double fireTime
)
3161 if (&renderer(p
) == &renderer(Pos(-1,-1)))
3163 //if (!renderer.Visible(p))
3167 if (time
<= renderer(p
).GetLastTime())
3168 if (fireTime
< renderer(p
).GetLastTime())
3170 renderer(p
).Add(tr
= new TileRender(GetTile(p
), p
, mask
), fireTime
);
3171 ((TileRender
*)renderer(p
).GetStage(time
+10/*HACKY!*/))->special
|= mask
;
3175 tr
= new TileRender(GetTile(p
), p
, mask
| ((TileRender
*)renderer(p
).GetStage(time
+10/*HACKY!*/))->special
);
3176 renderer(p
).Add(tr
, fireTime
);
3179 renderer(p
).Add(tr
= new TileRender(GetTile(p
), p
, mask
), fireTime
);
3183 tr
->specialDuration
= time
+ LASER_LINE_TIME
- fireTime
+ LASER_FADE_TIME
;
3187 void FireGun(Pos newpos
, Dir d
, bool recurse
, double fireTime
)
3189 static Pos hits
[100];
3190 static Dir hitDir
[100];
3191 static unsigned int numHits
=0;
3195 double starttime
= fireTime
;
3196 for (Dir fd
=((d
<0)?0:d
); fd
<((d
<0)?MAX_DIR
:d
+1); fd
++)
3198 fireTime
= starttime
;
3199 // starttime += 0.03;
3201 Pos p
= newpos
+ fd
;
3203 for (; range
<MAP_SIZE
; range
++, p
=p
+fd
)
3205 Tile t
= GetTile(p
);
3206 if (tileSolid
[t
]!=-1)
3209 renderer(p
).Add(new TileRender(tileSolid
[t
]==1 ? TILE_WHITE_WALL
: TILE_WHITE_TILE
, p
), fireTime
+0.1);
3212 for (i
=0; i
<numHits
; i
++)
3216 t
==TRAP
&& (hitDir
[i
]&(1<<fd
))==0
3221 if (i
>= sizeof(hits
)/sizeof(hits
[0]))
3223 hitDir
[i
] = 1 << fd
;
3229 hitDir
[i
] |= 1 << fd
;
3234 1<<((fd
+2) % MAX_DIR
)
3235 | 1<<((fd
+MAX_DIR
-2) % MAX_DIR
);
3237 if (LaserTile(p
, dirmask
, fireTime
))
3238 fireTime
+= (time
+LASER_LINE_TIME
- fireTime
) / 40;
3239 // fireTime += LASER_SEGMENT_TIME;
3241 FireGun(p
, (fd
+1) % MAX_DIR
, true, fireTime
);
3242 FireGun(p
, (fd
+MAX_DIR
-1) % MAX_DIR
, true, fireTime
);
3249 LaserTile(p
, 1<<(fd
%3), fireTime
);
3251 fireTime
+= (time
+LASER_LINE_TIME
- fireTime
) / 40;
3252 // fireTime += LASER_SEGMENT_TIME;
3256 // renderer().Add(new LaserRender(newpos, fd, range), time);
3261 //double _time = time;
3262 time
+= LASER_LINE_TIME
;
3263 for (unsigned int i
=0; i
<numHits
; i
++)
3266 Tile t
= GetTile(p
);
3273 renderer(p
).Add(new ExplosionRender(p
, t
==GUN
), time
);
3274 //renderer(p).Add(new TileRender(EMPTY, p), time+2);
3275 SetTile(p
, EMPTY
, false);
3278 renderer(p
,true).Add(new ItemRender(GetItem(p
), 1, p
), time
);
3282 for (Dir j
=0; j
<MAX_DIR
; j
++)
3284 if (GetTile(p
+j
)!=EMPTY
)
3286 renderer(p
+j
).Add(new TileRender(tileSolid
[GetTile(p
+j
)]==1 ? TILE_WHITE_WALL
: TILE_WHITE_TILE
, p
+j
), time
+0.05);
3287 renderer(p
+j
).Add(new ExplosionRender(p
+j
), time
+0.2);
3290 renderer(p
+j
,true).Add(new ItemRender(GetItem(p
+j
), 1, p
), time
);
3292 //renderer(p+j).Add(new TileRender(EMPTY, p+j), time+2.2);
3294 ScoreDestroy(p
+ j
);
3295 SetTile(p
+ j
, EMPTY
, false);
3300 time
+= MAX(LASER_FADE_TIME
, 0.15);
3305 int GetLastPlayerRot()
3307 RenderStage
* rs
= renderer
.player
.GetStage(-1);
3309 return ((PlayerRender
*)rs
)->r
;
3313 if (dead
|| win
|| isMap
)
3319 // Jump forwards in time to last move finishing
3320 if (numUndo
> 0 && time
< undo
[numUndo
-1].endTime
)
3321 time
= undo
[numUndo
-1].endTime
;
3323 double realTime
= time
;
3324 double endAnimTime
= time
;
3325 bool high
= (tileSolid
[GetTile(player
)] == 1);
3326 Pos playerStartPos
= player
;
3327 Pos oldpos
= player
;
3328 int oldPlayerHeight
= GetHeight(oldpos
);
3329 Pos newpos
= player
+ d
;
3331 int playerRot
= GetLastPlayerRot();
3332 if (d
!=-1 && d
!=playerRot
)
3334 while (d
!=playerRot
)
3336 if ((d
+6-playerRot
) % MAX_DIR
< MAX_DIR
/2)
3337 playerRot
= (playerRot
+1) % MAX_DIR
;
3339 playerRot
= (playerRot
+MAX_DIR
-1) % MAX_DIR
;
3343 if (GetTile(oldpos
) == FLOATING_BALL
)
3345 TileRender
* t
= new TileRender(FLOATING_BALL
, oldpos
);
3346 t
->special
= playerRot
+ 256;
3347 renderer(oldpos
).Add(t
, time
);
3349 renderer
.player
.Add(new PlayerRender(playerRot
, Pos(-20,-20), oldPlayerHeight
, Pos(-20,-20), oldPlayerHeight
, dead
), time
);
3353 PlayerRender
*p
= new PlayerRender(playerRot
, player
, oldPlayerHeight
, player
, oldPlayerHeight
, dead
);
3355 renderer
.player
.Add(p
, time
);
3362 if (d
<0 && player_items
[1]==0)
3367 if (tileSolid
[GetTile(newpos
)] == -1)
3372 if (Collide(newpos
, high
))
3379 // Don't change any real state before this point!
3380 if (numUndo
>= MAX_UNDO
)
3383 for(int i
=0; i
<MAX_UNDO
-1; i
++)
3384 undo
[i
] = undo
[i
+1];
3386 undo
[numUndo
].New(d
, player
, player_items
, time
, player_score
);
3393 double time0
= time
;
3394 time
+= 0.15; //Time for leave-tile fx
3396 switch (GetTile(oldpos
))
3399 SetTile(oldpos
, EMPTY
);
3400 renderer(oldpos
).Add(new DisintegrateRender(oldpos
), time
);
3405 // Don't need to CheckFinished - can't be collapse doors around
3406 // unless there're still collapsable tiles around.
3407 SetTile(oldpos
, EMPTY
);
3408 renderer(oldpos
).Add(new DisintegrateRender(oldpos
, 1), time
);
3412 SetTile(oldpos
, COLLAPSABLE
, false);
3413 renderer(oldpos
).Add(new DisintegrateRender(oldpos
, 0, 1), time
);
3418 case COLLAPSE_DOOR2
:
3419 SetTile(oldpos
, COLLAPSE_DOOR
, false);
3420 renderer(oldpos
).Add(new DisintegrateRender(oldpos
, 1, 1), time
);
3425 SetTile(oldpos
, COLLAPSABLE2
);
3429 time
= time0
; //End of leave-tile fx
3431 int retry_pos_count
=0;
3435 if (GetItem(newpos
)==1)
3437 renderer(newpos
, true).Add(new ItemCollectRender(GetItem(newpos
), newpos
), time
+ JUMP_TIME
/2);
3438 SetItem(newpos
, 0, false);
3441 if (GetItem(newpos
)==2)
3443 renderer(newpos
, true).Add(new ItemCollectRender(GetItem(newpos
), newpos
), time
+ JUMP_TIME
/2);
3444 SetItem(newpos
, 0, false);
3448 if (GetTile(player
) == FLOATING_BALL
)
3450 TileRender
* t
= new TileRender(FLOATING_BALL
, player
);
3452 renderer(oldpos
).Add(t
, time
);
3455 PlayerRender
*p
= new PlayerRender(playerRot
, player
, oldPlayerHeight
, newpos
, GetHeight(newpos
), dead
);
3457 // alternate leg (hacky!)
3465 if (retry_pos_count
!=0 && GetTile(player
)==TRAP
)
3471 p
->speed
= JUMP_TIME
* 1.5;
3472 renderer
.player
.Add(p
, time
);
3473 endAnimTime
= MAX(endAnimTime
, time
+ p
->speed
+0.001);
3478 switch (GetTile(newpos
))
3481 renderer(newpos
).Add(new TileRender(TILE_GREEN_CRACKED
, newpos
), time
);
3484 renderer(newpos
).Add(new TileRender(TILE_GREEN_CRACKED_WALL
, newpos
), time
);
3487 renderer(newpos
).Add(new TileRender(TILE_BLUE_CRACKED
, newpos
), time
);
3489 case COLLAPSE_DOOR2
:
3490 renderer(newpos
).Add(new TileRender(TILE_BLUE_CRACKED_WALL
, newpos
), time
);
3499 double pretime
= time
;
3502 for (Dir fd
=0; fd
<MAX_DIR
; fd
++)
3504 Tile t2
= GetTile(newpos
+ fd
);
3508 SetTile(newpos
+fd
, COLLAPSABLE
, false);
3509 renderer(newpos
+fd
).Add(new BuildRender(newpos
+fd
, fd
, 0), time
);
3511 else if (t2
==COLLAPSABLE
)
3514 SetTile(newpos
+fd
, COLLAPSE_DOOR
, false);
3515 renderer(newpos
+fd
).Add(new BuildRender(newpos
+fd
, fd
, 1), time
);
3518 if (done
) time
+= BUILD_TIME
;
3519 else time
= pretime
;
3521 endAnimTime
= MAX(endAnimTime
, time
+ 0.1);
3526 Swap(COLLAPSE_DOOR
, COLLAPSABLE
);
3532 renderer
.player
.Add(new PlayerRender(playerRot
, Pos(-30,-30), 0, Pos(-30,-30), 0, dead
), time
);
3533 while (tileSolid
[GetTile(newpos
+d
)]==-1)
3537 if (!renderer
.Visible(newpos
+d
))
3539 TileRender
* r
= new TileRender(FLOATING_BALL
, newpos
);
3541 renderer(newpos
).Add(r
, time
);
3543 PlayerRender
* pr
= new PlayerRender(playerRot
, newpos
, 0, newpos
, 0, dead
);
3544 pr
->speed
= JUMP_TIME
*1;
3545 renderer
.player
.Add(pr
, time
);
3553 newpos
= oldpos
+ d
;
3555 SetTile(oldpos
, EMPTY
, false);
3556 SetTile(newpos
, FLOATING_BALL
, false);
3558 renderer(oldpos
).Add(new TileRotateRender(FLOATING_BALL
, oldpos
, d
, 2), time
);
3559 renderer(oldpos
).Add(new TileRender(EMPTY
, oldpos
), time
+ ROTATION_TIME
/2);
3560 renderer(newpos
).Add(new TileRotateRender(FLOATING_BALL
, newpos
, (d
+3)%MAX_DIR
, 3), time
+ ROTATION_TIME
/2);
3562 // PlayerRender *p = new PlayerRender(playerRot, oldpos, 0, newpos, 0, dead);
3563 // p->speed = ROTATION_TIME*0.9;
3564 // renderer.player.Add(p, time);
3566 endAnimTime
= MAX(endAnimTime
, time
+ ROTATION_TIME
+ ROTATION_TIME
/2);
3567 time
+= ROTATION_TIME
;
3570 // renderer.player.Add(new PlayerRender(playerRot, player, 0, player, 0, 0), time);
3576 TileRender
* r
= new TileRender(FLOATING_BALL
, newpos
);
3577 r
->special
= playerRot
+ 256;
3578 renderer(newpos
).Add(r
, time
);
3586 SetTile(newpos
, GetTile(newpos
)==LIFT_UP
? LIFT_DOWN
: LIFT_UP
, false);
3587 renderer(newpos
).Add(new TileRender(GetTile(newpos
), newpos
, 1), time
);
3589 PlayerRender
*p
= new PlayerRender(playerRot
, newpos
, 1-GetHeight(newpos
), newpos
, GetHeight(newpos
), dead
);
3590 renderer
.player
.Add(p
, time
);
3591 endAnimTime
= MAX(endAnimTime
, time
+ JUMP_TIME
);
3599 if (Collide(newpos
+ d
, high
))
3601 if (Collide((newpos
+ d
) + d
, high
) == 1)
3602 newpos
= (newpos
+ d
);
3604 newpos
= (newpos
+ d
) + d
;
3605 if (tileSolid
[GetTile(newpos
)] == -1)
3612 for (Dir d
=0; d
<MAX_DIR
; d
++)
3614 Tile tmp
= GetTile(newpos
+ d
);
3615 renderer(newpos
+ d
).Add(new TileRotateRender(tmp
, newpos
+ d
, (d
+2)%MAX_DIR
, false), time
);
3617 Tile tmp
= GetTile(newpos
+ Dir(MAX_DIR
-1));
3618 for (Dir d
=0; d
<MAX_DIR
; d
++)
3620 Tile t2
= GetTile(newpos
+ d
);
3621 SetTile(newpos
+ d
, tmp
, false);
3622 renderer(newpos
+ d
).Add(new TileRotateRender(tmp
, newpos
+ d
, (d
+4)%MAX_DIR
, true), time
+ ROTATION_TIME
/2);
3623 if (GetItem(newpos
+ d
))
3624 renderer(newpos
+ d
,true).Add(new ItemRender(GetItem(newpos
+ d
), GetTile(newpos
+ d
)==EMPTY
, newpos
+d
), time
+ ROTATION_TIME
/2);
3628 endAnimTime
= MAX(endAnimTime
, time
+ROTATION_TIME
);
3629 // renderer(newpos).Add(new TileRotateRender(SPINNER, Dir(0), 0), time);
3637 if (player_items
[0]==0)
3639 if (tileSolid
[GetTile(newpos
+ d
)] == 1)
3641 newpos
= newpos
+ d
;
3642 if (tileSolid
[GetTile(newpos
)] == -1)
3649 SetTile(newpos
, COLLAPSABLE3
);
3657 FireGun(newpos
, d
, false, time
);
3659 endAnimTime
= MAX(endAnimTime
, time
);
3661 if (GetTile(newpos
)==EMPTY
)
3663 PlayerRender
* pr
= new PlayerRender(playerRot
, player
, 0, player
, 0, dead
);
3664 pr
->speed
= JUMP_TIME
*1;
3665 renderer
.player
.Add(pr
, time
);
3675 for (Dir fd=((d<0)?0:d); fd<((d<0)?MAX_DIR:d+1); fd++)
3677 Pos p = newpos + fd;
3679 for (range; range<MAP_SIZE; range++, p=p+fd)
3681 Tile t = GetTile(p);
3682 if (tileSolid[t]!=-1)
3684 hits[numHits++] = p;
3689 renderer().Add(new LaserRender(newpos, fd, range), time);
3692 double _time = time;
3694 for (int i=0; i<numHits; i++)
3697 Tile t = GetTile(p);
3699 renderer().Add(new ExplosionRender(p), time);
3705 for (Dir j=0; j<MAX_DIR; j++)
3707 ScoreDestroy(p + j);
3708 SetTile(p + j, EMPTY);
3710 if (GetTile(newpos)==EMPTY)
3714 endAnimTime = MAX(endAnimTime, time);
3724 endAnimTime
= MAX(endAnimTime
, time
);
3730 PlayerRender
* pr
= new PlayerRender(player
, 0, dead
);
3731 pr
->speed
= 0; // Don't sit around before disappearing!
3732 renderer
.player
.Add(pr
, time
);
3734 // If the tile we're drowning on isn't visible, give the ownership of the splash effect to the player, rather than a tile.
3735 if (renderer
.Visible(player
))
3736 renderer(player
).Add(new ExplosionRender(player
, 0, 1), time
);
3738 renderer
.player
.Add(new ExplosionRender(player
, 0, 1), time
);
3740 endAnimTime
= MAX(endAnimTime
, time
+2);
3747 undo
[numUndo
].endTime
= endAnimTime
;
3752 void Update(double timedelta
)
3759 activeMenu
->Update(timedelta
);
3764 for (int i
=0; i
<SDLK_LAST
; i
++)
3771 if (isMap
&& isRenderMap
)
3774 static double scrollHi
= 0;
3779 int xx
= noMouse
? keyboardp
.getScreenX()-scrollX
: mousex
;
3780 if (xx
> SCREEN_W
) xx
= SCREEN_W
;
3783 x
= (double)xx
/ (w
) - 1;
3784 if (xx
> SCREEN_W
- w
)
3785 x
= 1 - (double)(SCREEN_W
-xx
) / (w
);
3787 if (x
<-min
|| x
>min
)
3789 scrollHi
+= timedelta
* x
;
3790 scrollX
+= (int)scrollHi
;
3791 scrollHi
-= (int)scrollHi
;
3796 if (undoTime
>=0 && undoTime
< time
)
3798 double acc
= (time
- undoTime
) / 2;
3799 if (acc
< 3) acc
= 3;
3800 time
-= timedelta
* acc
;
3801 if (undoTime
>= time
)
3808 time
+= timedelta
* 20;
3811 void FileDrop(const char* filename
)
3813 LoadSave(filename
, false);
3818 if (keyState
[SDLK_LALT
] || keyState
[SDLK_LCTRL
])
3822 if (!isMap
&& !editMode
&& undoTime
< 0)
3824 if (keyState
[SDLK_z
] || keyState
[SDLK_BACKSPACE
] || keyState
[SDLK_u
])
3830 if (isMap
&& !editMode
)
3833 if ((keyState
[SDLK_q
] | keyState
[SDLK_KP7
]) & 2) keyboardp
.x
--;
3834 else if ((keyState
[SDLK_d
] | keyState
[SDLK_KP3
]) & 2) keyboardp
.x
++;
3835 else if ((keyState
[SDLK_e
] | keyState
[SDLK_KP9
]) & 2) keyboardp
.x
++, keyboardp
.y
--;
3836 else if ((keyState
[SDLK_a
] | keyState
[SDLK_KP1
]) & 2) keyboardp
.x
--, keyboardp
.y
++;
3837 else if ((keyState
[SDLK_w
] | keyState
[SDLK_KP8
] | keyState
[SDLK_UP
]) & 2) keyboardp
.y
--;
3838 else if ((keyState
[SDLK_s
] | keyState
[SDLK_KP2
] | keyState
[SDLK_DOWN
]) & 2) keyboardp
.y
++;
3839 else if ((keyState
[SDLK_LEFT
]) & 2) keyboardp
.x
--, keyboardp
.y
+=keyboardp
.x
&1;
3840 else if (((keyState
[SDLK_RIGHT
]) & 2)) { if (keyboardp
.x
< mapRightBound
) keyboardp
.y
-=keyboardp
.x
&1, keyboardp
.x
++; }
3841 else if ((keyState
[SDLK_RETURN
] | keyState
[SDLK_KP5
] | keyState
[SDLK_SPACE
] | keyState
[SDLK_KP_ENTER
]) & 2)
3843 // Simulate user clicking on it...
3844 Mouse(keyboardp
.getScreenX()-scrollX
, keyboardp
.getScreenY()-scrollY
, 0, 0, 1, 0, 0);
3851 UpdateCursor(keyboardp
);
3854 int min
[21] = { 17, 16, 15, 14, 13, 13, 13, 13, 13, 13, 12, 11, 11, 13, 12, 11, 8, 8, 7, 6, 7 };
3855 int max
[21] = { 20, 20, 19, 19, 19, 19, 18, 21, 20, 20, 19, 19, 18, 18, 17, 16, 16, 16, 15, 15, 14 };
3856 if (keyboardp
.x
< 3) keyboardp
.x
= 3;
3857 if (keyboardp
.x
> mapRightBound
) keyboardp
.x
= mapRightBound
;
3859 if (keyboardp
.y
< min
[keyboardp
.x
-3]) keyboardp
.y
= min
[keyboardp
.x
-3];
3860 if (keyboardp
.y
> max
[keyboardp
.x
-3]) keyboardp
.y
= max
[keyboardp
.x
-3];
3862 UpdateCursor(keyboardp
);
3864 else if (!editMode
&& (numUndo
==0 || time
>=undo
[numUndo
-1].endTime
))
3866 static int usedDiag
= 0;
3868 if (keyState
[SDLK_q
] || keyState
[SDLK_KP7
]) HandleKey('q', 0);
3869 else if (keyState
[SDLK_w
] || keyState
[SDLK_KP8
]) HandleKey('w', 0);
3870 else if (keyState
[SDLK_e
] || keyState
[SDLK_KP9
]) HandleKey('e', 0);
3871 else if (keyState
[SDLK_a
] || keyState
[SDLK_KP1
]) HandleKey('a', 0);
3872 else if (keyState
[SDLK_s
] || keyState
[SDLK_KP2
]) HandleKey('s', 0);
3873 else if (keyState
[SDLK_d
] || keyState
[SDLK_KP3
]) HandleKey('d', 0);
3875 else if (keyState
[SDLK_UP
] && keyState
[SDLK_LEFT
]) HandleKey('q', 0), usedDiag
=1;
3876 else if (keyState
[SDLK_UP
] && keyState
[SDLK_RIGHT
]) HandleKey('e', 0), usedDiag
=1;
3877 else if (keyState
[SDLK_DOWN
] && keyState
[SDLK_LEFT
]) HandleKey('a', 0), usedDiag
=1;
3878 else if (keyState
[SDLK_DOWN
] && keyState
[SDLK_RIGHT
]) HandleKey('d', 0), usedDiag
=1;
3879 else if (keyState
[SDLK_UP
] && !usedDiag
) HandleKey('w', 0);
3880 else if (keyState
[SDLK_DOWN
] && !usedDiag
) HandleKey('s', 0);
3885 void KeyReleased(int key
)
3889 bool KeyPressed(int key
, int mod
)
3895 bool eat
= activeMenu
->KeyPressed(key
, mod
);
3897 memset(keyState
, 0, sizeof(keyState
));
3902 if ((key
==SDLK_ESCAPE
&& (mod
& KMOD_CTRL
)))
3904 if (mod
& KMOD_SHIFT
)
3908 LoadSaveProgress(false);
3914 if (isFadeRendering
)
3917 return HandleKey(key
, mod
);
3920 bool HandleKey(int key
, int mod
)
3925 if (isMap
&& key
=='r' && (mod
& KMOD_ALT
))
3934 else if ((key
=='p' && !editMode
|| key
==SDLK_PAUSE
|| key
==SDLK_ESCAPE
))
3937 new PauseMenu(isMap
, progress
.GetLevel(STARTING_LEVEL
, true)->Completed(), progress
.general
.endSequence
>=1, progress
.general
.endSequence
>=2);
3941 else if (key
=='e' && (mod
& KMOD_ALT
))
3942 editMode
= !editMode
;
3944 else if (key
=='p' && (mod
& KMOD_ALT
) && numUndo
>0
3945 || key
>='0' && key
<='9' && (mod
& KMOD_SHIFT
) && !isMap
)
3947 if (key
>='0' && key
<='9')
3948 levelDiff
= (key
=='0') ? 10 : key
-'0';
3950 if (key
=='p' && levelPar
==0)
3951 levelPar
= player_score
;
3956 undo
[numUndo
-1].Restore(this);
3960 if (LoadSave(currentFile
, true))
3962 if (key
>='0' && key
<='9')
3968 /////////////////////////////////////////////////////////////////////////
3969 if (isMap
&& !editMode
)
3972 else if (key
==SDLK_KP9
|| key
=='e') Input(1), noMouse
=1;
3973 else if (key
==SDLK_KP3
|| key
=='d') Input(2), noMouse
=1;
3974 else if (key
==SDLK_KP1
|| key
=='a') Input(4), noMouse
=1;
3975 else if (key
==SDLK_KP7
|| key
=='q') Input(5), noMouse
=1;
3976 else if (key
==SDLK_KP8
|| key
=='w') Input(0), noMouse
=1;
3977 else if (key
==SDLK_KP2
|| (key
=='s' && (((mod
& (KMOD_CTRL
|KMOD_ALT
))==0)||!editMode
))) Input(3), noMouse
=1;
3978 else if (key
==SDLK_KP5
|| key
==SDLK_SPACE
|| key
==SDLK_RETURN
|| key
==SDLK_KP_ENTER
)
3981 if (win
&& winFinal
)
3982 LoadMap(), memset(keyState
, 0, sizeof(keyState
));
3987 else if (key
=='r' && (mod
& KMOD_CTRL
))
3988 LoadSave(currentFile
, false);
3991 else if (key
=='z' && (mod
& KMOD_ALT
))
3993 if (numUndo
>0 && !isMap
)
3995 time
= undo
[numUndo
-1].endTime
;
3996 undoTime
= undo
[0].time
;
3999 undo
[numUndo
-1].Restore(this);
4004 else if (key
=='z' || key
==SDLK_BACKSPACE
|| key
==SDLK_DELETE
|| key
=='u')
4011 else if (key
=='s' && (mod
& KMOD_ALT
)){
4012 if (win
&& strlen(currentFile
)>0 && !isMap
)
4015 strcpy(tmp
, currentFile
);
4016 ChangeSuffix(tmp
, "sol");
4017 FILE* f
= file_open(tmp
, "wb");
4020 for (int i
=0; i
<numUndo
; i
++)
4022 fputc(undo
[i
].playerMovement
, f
);
4031 else if (key
=='/' && (mod
& KMOD_ALT
)){
4039 if (mod
& KMOD_SHIFT
)
4041 LevelSave
* l
= progress
.GetLevel(currentFile
, false);
4042 if (l
&& l
->Completed())
4044 for (int i
=0; i
<l
->bestSolutionLength
; i
++)
4045 Input(l
->bestSolution
[i
]);
4054 strcpy(tmp
, currentFile
);
4055 ChangeSuffix(tmp
, "sol");
4056 FILE* f
= file_open(tmp
, "rb");
4060 while ((dir
= fgetc(f
)) != -1)
4081 else if (key
>='0' && key
<='9' && (mod
& KMOD_ALT
) && !isMap
)
4082 levelPar
= levelPar
*10 + key
-'0';
4083 else if (key
==SDLK_BACKSPACE
&& (mod
& KMOD_ALT
) && !isMap
)
4087 Mouse(mousex
, mousey
, 0, 0, 32, 0, mouse_buttons
);
4088 else if (key
=='p' && !(mod
& KMOD_ALT
))
4089 Mouse(mousex
, mousey
, 0, 0, 64, 0, mouse_buttons
);
4091 Mouse(mousex
, mousey
, 0, 0, 128, 0, mouse_buttons
);
4092 else if (key
==SDLK_RETURN
)
4093 Mouse(mousex
, mousey
, 0, 0, 256, 0, mouse_buttons
);
4094 else if (key
==SDLK_BACKSPACE
)
4095 Mouse(mousex
, mousey
, 0, 0, 512, 0, mouse_buttons
);
4097 Mouse(mousex
, mousey
, 0, 0, 1024, 0, mouse_buttons
);
4099 else if (key
=='s' && (mod
& KMOD_CTRL
)){
4100 char *fn
= LoadSaveDialog(true, true, _("Save level"));
4102 SDL_WM_SetCaption(currentFile
, NULL
);
4105 else if (key
=='o' && (mod
& KMOD_CTRL
)){
4106 char* fn
= LoadSaveDialog(false, true, _("Open level"));
4107 LoadSave(fn
, false);
4108 SDL_WM_SetCaption(currentFile
, NULL
);
4119 #define X(NAME,FILE,ALPHA) NAME = Load(DATA_DIR "/graphics/" FILE BMP_SUFFIX, ALPHA);
4120 #include "gfx_list.h"
4122 static int first
= 1;
4129 // unsigned int d = {
4132 // static SDL_Cursor * c = SDL_CreateCursor(data, mask, 32, 32, 1, 1);
4133 // SDL_SetCursor(c);
4138 #define X(NAME,FILE,ALPHA) if (NAME) SDL_FreeSurface(NAME), NAME=0;
4139 #include "gfx_list.h"
4141 virtual void ScreenModeChanged()
4148 MAKE_STATE(HexPuzzle
, SDLK_F1
, false);
4150 char * HexPuzzle::loadPtr
= 0;
4151 char * HexPuzzle::endLoad
= 0;