save game mechanics seems to work again
[awish.git] / src / game.c
bloba51ef2e8cbbc6bf5bcd3bf3eda48f9d7a1ff24b9
1 /*
2 * This program is free software: you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation, either version 3 of the License, or
5 * (at your option) any later version.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <time.h>
19 #include <unistd.h>
21 #include "game.h"
23 #include "resfile.h"
24 #include "video.h"
25 #include "mainloop.h"
26 #include "vm.h"
27 #include "gameglobals.h"
28 #include "title.h"
31 extern int goobers;
35 ////////////////////////////////////////////////////////////////////////////////
36 #define MAP_WIDTH (44)
37 #define MAP_HEIGHT (41)
38 #define VIS_WIDTH (15)
39 #define VIS_HEIGHT (17)
40 #define VIS_X (10)
41 #define VIS_Y (20)
44 ////////////////////////////////////////////////////////////////////////////////
45 static const char *itemNames[] = {
46 "",
47 "key",
48 "fan",
49 "dynamite",
50 "drill",
51 "umbrella",
52 "monster",
53 "rocket",
54 "apple",
55 "vial",
56 "gloves"
60 enum {
61 ITEM_NOTHING,
62 ITEM_KEY,
63 ITEM_FAN,
64 ITEM_DYNAMITE,
65 ITEM_DRILL,
66 ITEM_UMBRELLA,
67 ITEM_HARRY,
68 ITEM_ROCKET,
69 ITEM_APPLE,
70 ITEM_VIAL,
71 ITEM_GLOVES,
72 ITEM_MAX
76 typedef struct {
77 int type; // 0: inactive
78 int x, y; // in map
79 int frame; // animation frame
80 int fframe, lframe; // first and last anim frame
81 int animated;
82 int tid;
83 } Item;
86 ////////////////////////////////////////////////////////////////////////////////
87 static int curLevel;
88 static char levelname[257];
89 static int lnamex;
90 static int levelback;
91 static Uint8 bmap[MAP_HEIGHT][MAP_WIDTH];
92 static Uint8 fmap[MAP_HEIGHT][MAP_WIDTH];
93 static Uint8 xflags[MAP_WIDTH];
94 static int fkeys[10];
95 static int inMinimap;
97 static int doSave = 0;
99 static int demorecmode = 0; // 0: none; 1: recording; -1: playing
100 static int demoSize = 0;
101 static int demoPos = 0;
102 static Uint8 *demoData = NULL;
103 static Uint8 demoPrevKeyState = 0;
104 static int demoKeyStateRepeats = 0;
106 static char message[256];
107 //static Uint32 msgEndTime = 0;
109 static Uint32 demo_m_w = 0;
110 static Uint32 demo_m_z = 0;
113 ////////////////////////////////////////////////////////////////////////////////
114 static int gamekeys[12];
117 static void setGameKeysGVars (void) {
118 vmGVars[GVAR_KEY_LEFT] = gamekeys[0] ? 1 : 0;
119 vmGVars[GVAR_KEY_RIGHT] = gamekeys[1] ? 1 : 0;
120 vmGVars[GVAR_KEY_UP] = gamekeys[2] ? 1 : 0;
121 vmGVars[GVAR_KEY_DOWN] = gamekeys[3] ? 1 : 0;
122 vmGVars[GVAR_KEY_TAKE] = gamekeys[4] ? 1 : 0;
123 vmGVars[GVAR_KEY_USE] = gamekeys[5] ? 1 : 0;
125 vmGVars[GVAR_KEY_MINIMAP] = gamekeys[6] ? 1 : 0;
126 vmGVars[GVAR_KEY_RESTART] = gamekeys[7] ? 1 : 0;
128 vmGVars[GVAR_KEY_FALL_CHEAT] = gamekeys[8] ? 1 : 0;
129 vmGVars[GVAR_KEY_WALK_CHEAT] = gamekeys[9] ? 1 : 0;
130 vmGVars[GVAR_KEY_PLEV_CHEAT] = gamekeys[10] ? 1 : 0;
131 vmGVars[GVAR_KEY_NLEV_CHEAT] = gamekeys[11] ? 1 : 0;
135 ////////////////////////////////////////////////////////////////////////////////
136 #define ROOM_SCRIPTS_MARK " room script labels starts here "
139 static void loadMapScript (ResFile *resfile) {
140 VMLabelInfo *l;
141 int csz, tid;
143 vmGVars[GVAR_ROOM_SCRIPT_TID] = -1;
144 l = vmLabelAddMark(ROOM_SCRIPTS_MARK);
145 l->value = vmCodeSize;
146 if ((csz = loadCodeFile(resfile, vmCodeSize, 94+curLevel)) > 1) {
147 VMLabelInfo *l = vmFindLabel("entry_local_room_script");
149 if (l != NULL && l->type == LB_CODE) {
150 vmCodeSize += csz;
151 tid = vmNewThread(l->value);
152 vmGVars[GVAR_ROOM_SCRIPT_TID] = tid;
153 if (goobers) fprintf(stderr, "local room script loaded and initialized\n");
159 static void unloadMapScript (void) {
160 VMLabelInfo *l;
162 while ((l = vmFindMark(ROOM_SCRIPTS_MARK)) != NULL) {
163 VMLabelInfo *ucl = vmFindLabel("entry_local_room_script_unload");
165 if (ucl != NULL && ucl->type == LB_CODE) {
166 vmExecuteBSR(0, ucl->value, 0);
169 vmCodeSize = l->value;
170 vmFreeLabelsUntilMark(ROOM_SCRIPTS_MARK);
175 ////////////////////////////////////////////////////////////////////////////////
176 #define SECRET (42)
177 //#define SECRET (0)
180 static int readBuf (FILE *fl, void *buf, int len) {
181 unsigned char *c = (unsigned char *)buf;
183 while (len-- > 0) {
184 unsigned char b;
186 if (fread(&b, 1, 1, fl) != 1) return -1;
187 b ^= SECRET;
188 *c++ = b;
190 return 0;
194 static int writeBuf (FILE *fl, const void *buf, int len) {
195 const unsigned char *c = (const unsigned char *)buf;
197 while (len-- > 0) {
198 unsigned char b = *c++;
200 b ^= SECRET;
201 if (fwrite(&b, 1, 1, fl) != 1) return -1;
203 return 0;
207 static int readDW (FILE *fl, int *v) {
208 int t[4];
210 for (int f = 0; f < 4; ++f) {
211 unsigned char b;
213 if (fread(&b, 1, 1, fl) != 1) return -1;
214 t[f] = b^SECRET;
217 if (v) {
218 *v = 0;
219 for (int f = 0; f < 4; ++f) *v |= t[f]<<(8*f);
221 return 0;
225 static int writeDW (FILE *fl, int v) {
226 for (int f = 0; f < 4; ++f) {
227 unsigned char b;
229 b = v&0xff;
230 b ^= SECRET;
231 if (fwrite(&b, 1, 1, fl) != 1) return -1;
232 v >>= 8;
234 return 0;
238 static int readUDW (FILE *fl, Uint32 *v) {
239 int t[4];
241 for (int f = 0; f < 4; ++f) {
242 unsigned char b;
244 if (fread(&b, 1, 1, fl) != 1) return -1;
245 t[f] = b^SECRET;
248 if (v) {
249 *v = 0;
250 for (int f = 0; f < 4; ++f) *v |= t[f]<<(8*f);
252 return 0;
256 static int writeUDW (FILE *fl, Uint32 v) {
257 for (int f = 0; f < 4; ++f) {
258 unsigned char b;
260 b = v&0xff;
261 b ^= SECRET;
262 if (fwrite(&b, 1, 1, fl) != 1) return -1;
263 v >>= 8;
265 return 0;
269 ////////////////////////////////////////////////////////////////////////////////
270 static const char *SAVE_GAME_SIGNATURE = "ASG0";
272 static int loadGame (void) {
273 FILE *fl = fopen("awish.sav", "rb");
275 if (fl != NULL) {
276 char sign[4];
278 if (fread(sign, 4, 1, fl) != 1) goto error;
279 if (memcmp(sign, SAVE_GAME_SIGNATURE, 4) != 0) goto error;
280 if (vmLoadState(fl) != 0) goto error;
281 if (readUDW(fl, &demo_m_w) != 0) goto error;
282 if (readUDW(fl, &demo_m_z) != 0) goto error;
283 if (readDW(fl, &inMinimap) != 0) goto error;
284 if (readDW(fl, &curLevel) != 0) goto error;
285 if (readBuf(fl, levelname, sizeof(levelname)) != 0) goto error;
286 if (readDW(fl, &lnamex) != 0) goto error;
287 if (readDW(fl, &levelback) != 0) goto error;
288 if (readBuf(fl, bmap, sizeof(bmap)) != 0) goto error;
289 if (readBuf(fl, fmap, sizeof(fmap)) != 0) goto error;
290 if (readBuf(fl, xflags, sizeof(xflags)) != 0) goto error;
291 fclose(fl);
292 setSeedL(demo_m_w);
293 setSeedL(demo_m_z);
294 return 0;
296 error:
297 fclose(fl);
298 if (goobers) fprintf(stderr, "error loading saved game\n");
299 return -1;
303 static int saveGame (void) {
304 FILE *fl = fopen("awish.sav", "wb");
306 if (fl != NULL) {
307 demo_m_w = getSeedL();
308 demo_m_z = getSeedH();
309 if (fwrite(SAVE_GAME_SIGNATURE, 4, 1, fl) != 1) goto error;
310 if (vmSaveState(fl) != 0) goto error;
311 if (writeUDW(fl, demo_m_w) != 0) goto error;
312 if (writeUDW(fl, demo_m_z) != 0) goto error;
313 if (writeDW(fl, inMinimap) != 0) goto error;
314 if (writeDW(fl, curLevel) != 0) goto error;
315 if (writeBuf(fl, levelname, sizeof(levelname)) != 0) goto error;
316 if (writeDW(fl, lnamex) != 0) goto error;
317 if (writeDW(fl, levelback) != 0) goto error;
318 if (writeBuf(fl, bmap, sizeof(bmap)) != 0) goto error;
319 if (writeBuf(fl, fmap, sizeof(fmap)) != 0) goto error;
320 if (writeBuf(fl, xflags, sizeof(xflags)) != 0) goto error;
321 fclose(fl);
322 return 0;
324 error:
325 fclose(fl);
326 unlink("awish.sav");
327 if (goobers) fprintf(stderr, "error saving game\n");
328 return -1;
332 ////////////////////////////////////////////////////////////////////////////////
333 static void demoClear (void) {
334 //demorecmode = 0;
335 demoSize = 0;
336 demoPos = 0;
337 demoPrevKeyState = 0;
338 demoKeyStateRepeats = 0;
339 if (demoData != NULL) free(demoData);
340 demoData = NULL;
344 static void demoAddByte (Uint8 b) {
345 if (demoPos+1 > demoSize && demoSize > 1024*1024*16) fatal("out of memory in demo recording!");
346 if (demoPos+1 > demoSize) {
347 int newsz = demoSize+1024*32;
348 Uint8 *nn = realloc(demoData, newsz);
350 if (!nn) fatal("out of memory in demo recording!");
351 demoData = nn;
352 demoSize = newsz;
354 demoData[demoPos++] = b;
358 static Uint8 demoGetKeyState (void) {
359 return
360 ((gamekeys[0]?1:0)<<0) |
361 ((gamekeys[1]?1:0)<<1) |
362 ((gamekeys[2]?1:0)<<2) |
363 ((gamekeys[3]?1:0)<<3) |
364 ((gamekeys[4]?1:0)<<4) |
365 ((gamekeys[5]?1:0)<<5);
369 static void demoSetKeyState (Uint8 kstate) {
370 gamekeys[0] = (kstate&(1<<0)) != 0;
371 gamekeys[1] = (kstate&(1<<1)) != 0;
372 gamekeys[2] = (kstate&(1<<2)) != 0;
373 gamekeys[3] = (kstate&(1<<3)) != 0;
374 gamekeys[4] = (kstate&(1<<4)) != 0;
375 gamekeys[5] = (kstate&(1<<5)) != 0;
379 static void demoAddFrameData (void) {
380 Uint8 kstate = demoGetKeyState();
382 if (demoKeyStateRepeats == 256) {
383 demoAddByte(demoKeyStateRepeats-1);
384 demoAddByte(demoPrevKeyState);
385 demoKeyStateRepeats = 0;
388 if (kstate != demoPrevKeyState) {
389 if (demoKeyStateRepeats > 0) {
390 demoAddByte(demoKeyStateRepeats-1);
391 demoAddByte(demoPrevKeyState);
393 demoPrevKeyState = kstate;
394 demoKeyStateRepeats = 1;
395 } else {
396 ++demoKeyStateRepeats;
401 static void demoGetFrameData (void) {
402 while (demoKeyStateRepeats < 1) {
403 if (demoPos+2 > demoSize) {
404 demoClear();
405 demorecmode = 0;
406 if (goobers) fprintf(stderr, "demo complete\n");
407 strcpy(message, "demo stopped");
408 return;
410 demoKeyStateRepeats = demoData[demoPos++];
411 ++demoKeyStateRepeats;
412 demoPrevKeyState = demoData[demoPos++];
414 --demoKeyStateRepeats;
416 demoSetKeyState(demoPrevKeyState);
420 static int demoSave (void) {
421 FILE *fl;
422 char buf[128];
423 char sign[5];
425 if (demoKeyStateRepeats > 0) {
426 demoAddByte(demoKeyStateRepeats-1);
427 demoAddByte(demoPrevKeyState);
430 sprintf(buf, "awish%02d.dmo", curLevel+1);
431 fl = fopen(buf, "wb");
432 if (fl == NULL) {
433 if (goobers) fprintf(stderr, "can't create demo file '%s'\n", buf);
434 return -1;
436 strcpy(sign, "AWD1");
437 if (fwrite(sign, 4, 1, fl) != 1) goto error;
438 if (writeDW(fl, curLevel) != 0) goto error;
439 if (writeUDW(fl, demo_m_w) != 0) goto error;
440 if (writeUDW(fl, demo_m_z) != 0) goto error;
441 if (writeDW(fl, demoPos) != 0) goto error;
442 if (writeBuf(fl, demoData, demoPos) != 0) goto error;
443 fclose(fl);
444 if (goobers) fprintf(stderr, "demo saved to file '%s'\n", buf);
445 return 0;
446 error:
447 fclose(fl);
448 unlink(buf);
449 if (goobers) fprintf(stderr, "can't write demo file '%s'\n", buf);
450 return -1;
454 static int demoLoad (void) {
455 FILE *fl;
456 char buf[128];
457 int size, level;
458 char sign[4];
460 demoClear();
461 sprintf(buf, "awish%02d.dmo", curLevel+1);
462 fl = fopen(buf, "rb");
463 if (fl == NULL) {
464 if (goobers) fprintf(stderr, "can't open demo file '%s'\n", buf);
465 return -1;
468 if (fread(sign, 4, 1, fl) != 1) goto error;
469 if (memcmp(sign, "AWD1", 4) != 0) goto error;
470 if (readDW(fl, &level) != 0) goto error;
471 if (readUDW(fl, &demo_m_w) != 0) goto error;
472 if (readUDW(fl, &demo_m_z) != 0) goto error;
473 if (readDW(fl, &size) != 0) goto error;
474 if (size < 1 || size > 1024*1024*16) goto error;
475 demoData = malloc(size);
476 if (demoData == NULL) goto error;
477 if (readBuf(fl, demoData, size) != 0) goto error;
478 demoSize = size;
479 demoPos = 0;
480 fclose(fl);
481 setSeedL(demo_m_w);
482 setSeedL(demo_m_z);
483 if (goobers) fprintf(stderr, "loaded demo file '%s'\n", buf);
485 return 0;
486 error:
487 fclose(fl);
488 if (goobers) fprintf(stderr, "can't load demo file '%s'\n", buf);
489 demoClear();
490 return -1;
494 // CODE_GAME_RESTART_LEVEL
495 // GVAR_GAME_STATE
496 // CONST_GAME_STATE_DEAD
497 // CONST_GAME_STATE_COMPLETE
498 // CONST_GAME_STATE_STARTED
499 // CONST_GAME_STATE_PLAYING
500 // GVAR_CUR_LEVEL
501 static inline int isGameStopped (void) {
502 return
503 vmGVars[GVAR_GAME_STATE] == CONST_GAME_STATE_DEAD ||
504 vmGVars[GVAR_GAME_STATE] == CONST_GAME_STATE_COMPLETE;
508 static void demoCB (void) {
509 if (vmGVars[GVAR_GAME_STATE] == CONST_GAME_STATE_STARTED) unloadMapScript();
511 switch (demorecmode) {
512 case -666: // prepare to load demo
513 if (demoLoad() != 0) {
514 demorecmode = 0;
515 break;
517 demorecmode = -2;
518 // fallthru
519 case 666: // prepare to save demo
520 vmSetPC(0, CODE_GAME_RESTART_LEVEL);
521 vmGVars[GVAR_CUR_LEVEL] = curLevel;
522 vmGVars[GVAR_GAME_STATE] = -1; // invalid state
523 if (demorecmode > 0) {
524 demoClear();
525 demorecmode = 2;
527 break;
528 case -1: // demo replaying
529 case 1: // demo saving
530 if (isGameStopped()) {
531 // the level is over or prof is dead
532 if (demorecmode > 0) demoSave();
533 demoClear();
534 demorecmode = 0;
535 } else {
536 ((demorecmode < 0) ? demoGetFrameData : demoAddFrameData)();
538 break;
539 case -2: // waiting for 'game started' trigger
540 case 2: // waiting for 'game started' trigger
541 if (vmGVars[GVAR_GAME_STATE] >= 0) {
542 if (goobers) fprintf(stderr, "demo %s started...\n", demorecmode<0?"replaying":"recording");
543 if (demorecmode < 0) {
544 // replay
545 setSeedL(demo_m_w);
546 setSeedL(demo_m_z);
547 } else {
548 // record
549 demo_m_w = getSeedL();
550 demo_m_z = getSeedH();
552 demorecmode = (demorecmode < 0) ? -1 : 1;
554 break;
556 setGameKeysGVars();
560 ////////////////////////////////////////////////////////////////////////////////
561 static void frmGameKey (SDL_KeyboardEvent *key) {
562 if (key->type == SDL_KEYDOWN) {
563 switch (key->keysym.sym) {
564 case SDLK_ESCAPE:
565 case SDLK_SPACE:
566 case SDLK_RETURN:
567 if (vmPaused && inMinimap) {
568 inMinimap = 0;
569 doSave = 0;
570 vmPaused = 0;
571 gamekeys[6] = 0;
573 break;
574 case SDLK_F12:
575 vmGVars[GVAR_KEY_QUIT] = 1;
576 break;
577 case SDLK_p:
578 if (vmPaused == 0) {
579 gamekeys[6] = 1;
580 } else {
581 if (inMinimap) {
582 inMinimap = 0;
583 doSave = 0;
584 vmPaused = 0;
585 gamekeys[6] = 1;
588 break;
589 case SDLK_r:
590 if (vmPaused == 0) {
591 if (demorecmode != 0) { demoClear(); demorecmode = 0; }
592 if ((key->keysym.mod&(KMOD_CTRL)) != 0) {
593 gamekeys[7] = 1;
596 break;
597 case SDLK_s:
598 if (vmPaused == 0 && !demorecmode) {
599 if (goobers && (key->keysym.mod&(KMOD_CTRL)) != 0) { doSave = 1; vmPaused = 1; }
601 break;
602 case SDLK_l:
603 if (vmPaused == 0 && !demorecmode) {
604 if (goobers && (key->keysym.mod&(KMOD_CTRL)) != 0) { doSave = -1; vmPaused = 1; }
606 break;
607 case SDLK_d:
608 if (vmPaused == 0) {
609 if (goobers && (key->keysym.mod&(KMOD_CTRL)) != 0) {
610 if (!demorecmode) {
611 demorecmode = 666; // 'start saving'
612 } else {
613 demoSave();
614 demoClear();
615 demorecmode = 0;
619 break;
620 case SDLK_m:
621 if (vmPaused == 0) {
622 if (goobers && (key->keysym.mod&(KMOD_CTRL)) != 0) {
623 if (!demorecmode) {
624 demorecmode = -666; // 'start playing'
625 } else {
626 demoClear();
627 demorecmode = 0;
628 demoSetKeyState(0);
632 break;
633 default: ;
637 if (demorecmode == 0 || demorecmode == 1) {
638 switch (key->keysym.sym) {
639 case SDLK_LEFT: case SDLK_KP4: gamekeys[0] = (key->type == SDL_KEYDOWN); break;
640 case SDLK_RIGHT: case SDLK_KP6: gamekeys[1] = (key->type == SDL_KEYDOWN); break;
641 case SDLK_UP: case SDLK_KP8: gamekeys[2] = (key->type == SDL_KEYDOWN); break;
642 case SDLK_DOWN: case SDLK_KP2: gamekeys[3] = (key->type == SDL_KEYDOWN); break;
643 case SDLK_SPACE: case SDLK_KP0: gamekeys[4] = (key->type == SDL_KEYDOWN); break;
644 case SDLK_RETURN: case SDLK_KP_PERIOD: gamekeys[5] = (key->type == SDL_KEYDOWN); break;
645 default: ;
648 if (goobers && !demorecmode) {
649 switch (key->keysym.sym) {
650 case SDLK_f: gamekeys[8] = (key->type == SDL_KEYDOWN); break;
651 case SDLK_w: gamekeys[9] = (key->type == SDL_KEYDOWN); break;
652 case SDLK_KP_MINUS: gamekeys[10] = (key->type == SDL_KEYDOWN); break;
653 case SDLK_KP_PLUS: gamekeys[11] = (key->type == SDL_KEYDOWN); break;
655 case SDLK_F1: fkeys[1] = (key->type == SDL_KEYDOWN); break;
656 case SDLK_F2: fkeys[2] = (key->type == SDL_KEYDOWN); break;
657 case SDLK_F3: fkeys[3] = (key->type == SDL_KEYDOWN); break;
658 case SDLK_F4: fkeys[4] = (key->type == SDL_KEYDOWN); break;
659 case SDLK_F5: fkeys[5] = (key->type == SDL_KEYDOWN); break;
660 case SDLK_F6: fkeys[6] = (key->type == SDL_KEYDOWN); break;
661 case SDLK_F7: fkeys[7] = (key->type == SDL_KEYDOWN); break;
662 case SDLK_F8: fkeys[8] = (key->type == SDL_KEYDOWN); break;
663 case SDLK_F9: fkeys[9] = (key->type == SDL_KEYDOWN); break;
664 default: ;
671 ////////////////////////////////////////////////////////////////////////////////
672 static int loadLevelInternal (ResFile *resfile, int lidx) {
673 Uint8 *lvl;
674 int sz, res = -1, pos;
675 static Item items[256];
676 static int itemCount;
677 static int scrX, scrY;
678 static int profX, profY;
679 char buf[64];
681 unloadMapScript();
683 message[0] = 0;
685 vmGVars[GVAR_MAP_WIDTH] = MAP_WIDTH;
686 vmGVars[GVAR_MAP_HEIGHT] = MAP_HEIGHT;
687 vmGVars[GVAR_VIS_WIDTH] = VIS_WIDTH;
688 vmGVars[GVAR_VIS_HEIGHT] = VIS_HEIGHT;
690 if ((lvl = loadResFile(resfile, lidx+9, &sz)) == NULL) return -1;
691 if (sz < 3700) goto quit;
692 if (lvl[0] > 25) goto quit;
693 memset(levelname, 0, sizeof(levelname));
694 if (lvl[0] > 0) memcpy(levelname, lvl+1, lvl[0]);
695 lnamex = (320-strlen(levelname)*8)/2;
696 sprintf(buf, "%d", lidx+1);
697 while (lnamex+strlen(levelname)*8+strlen(buf)*8+6*8 > 320) lnamex -= 8;
698 levelback = lvl[26];
699 if (levelback < 1 || levelback > 7) levelback = 1;
700 profX = lvl[27];
701 profY = lvl[28];
702 vmSetTVar(0, TVAR_POS_X, profX);
703 vmSetTVar(0, TVAR_POS_Y, profY);
704 scrX = lvl[29];
705 scrY = lvl[30]; if (scrY > 0) --scrY;
706 vmGVars[GVAR_SCR_X] = scrX;
707 vmGVars[GVAR_SCR_Y] = scrY;
708 pos = 31;
709 memset(bmap, 0, sizeof(bmap));
710 memset(fmap, 0, sizeof(fmap));
711 memset(xflags, 0, sizeof(xflags));
712 for (int x = 1; x < MAP_WIDTH; ++x) {
713 for (int y = 0; y < MAP_HEIGHT; ++y) {
714 bmap[y][x] = lvl[pos++];
717 for (int x = 1; x < MAP_WIDTH; ++x) {
718 for (int y = 0; y < MAP_HEIGHT; ++y) {
719 fmap[y][x] = lvl[pos++];
721 pos += 3; // skip unknown bytes
724 itemCount = lvl[pos++]+1; // at least one item is always here
725 for (int f = 0; f < itemCount; ++f) {
726 int tid;
728 items[f].x = lvl[pos++];
729 items[f].y = lvl[pos++];
730 items[f].fframe = lvl[pos++];
731 items[f].lframe = lvl[pos++];
732 items[f].frame = lvl[pos++];
733 items[f].animated = lvl[pos++];
734 if (!items[f].animated) items[f].frame = items[f].fframe;
735 ++pos; // 0xff
736 pos += 4; // dunno
737 items[f].type = lvl[pos++];
738 ++pos; // dunno
739 // spawn thread
740 tid = vmNewThread(CODE_ENTRY_ITEM);
741 if (tid < 0) goto quit;
742 vmSetTVar(tid, TVAR_ITEM_ID, items[f].type);
743 vmSetTVar(tid, TVAR_ANIMATED, items[f].animated);
744 vmSetTVar(tid, TVAR_AFIRST_FRAME, items[f].fframe);
745 vmSetTVar(tid, TVAR_ALAST_FRAME, items[f].lframe);
746 vmSetTVar(tid, TVAR_AFRAME, items[f].frame);
747 if (items[f].type == 6) vmSetTVar(tid, TVAR_AFRAME, lvl[pos-6]);
748 vmSetTVar(tid, TVAR_POS_X, items[f].x);
749 vmSetTVar(tid, TVAR_POS_Y, items[f].y);
750 vmSetTVar(tid, TVAR_POS_TX, 0);
751 vmSetTVar(tid, TVAR_POS_TY, 0);
752 vmSetTVar(tid, TVAR_SPR_BANK, CONST_BANK_ITEMS);
753 vmSetTVar(tid, TVAR_SPR_NUM, items[f].frame);
754 vmSetTVar(tid, TVAR_SPR_DIR, 0);
756 curLevel = lidx;
757 inMinimap = 0;
758 doSave = 0;
759 res = 0;
761 loadMapScript(resfile);
763 quit:
764 free(lvl);
765 return res;
769 ////////////////////////////////////////////////////////////////////////////////
770 static inline Uint8 fgTile (int x, int y) {
771 return (x >= 0 && y >= 0 && x < MAP_WIDTH && y < MAP_HEIGHT) ? fmap[y][x]&0x0f : 0;
775 static inline Uint8 bgTile (int x, int y) {
776 if (y == -666) return (x >= 0 && x < MAP_WIDTH) ? xflags[x] : 0;
777 return (x >= 0 && y >= 0 && x < MAP_WIDTH && y < MAP_HEIGHT) ? bmap[y][x]&0x0f : 0;
781 static int mapGet (int tid, int fg, int x, int y) {
782 return fg ? fgTile(x, y) : bgTile(x, y);
786 static inline void setFGTile (int x, int y, Uint8 tile) {
787 if (x >= 0 && y >= 0 && x < MAP_WIDTH && y < MAP_HEIGHT) fmap[y][x] = (fmap[y][x]&0xf0)|(tile&0x0f);
791 static inline void setBGTile (int x, int y, Uint8 tile) {
792 if (y == -666 && x >= 0 && x < MAP_WIDTH) xflags[x] = tile;
793 if (x >= 0 && y >= 0 && x < MAP_WIDTH && y < MAP_HEIGHT) bmap[y][x] = (bmap[y][x]&0xf0)|(tile&0x0f);
797 static void mapSet (int tid, int fg, int x, int y, int tile) {
798 if (fg) setFGTile(x, y, tile); else setBGTile(x, y, tile);
802 ////////////////////////////////////////////////////////////////////////////////
803 // cliprect should be set
804 static void levelDrawMap (SDL_Surface *frame) {
805 if (!inMinimap) {
806 int scrX = vmGVars[GVAR_SCR_X], scrY = vmGVars[GVAR_SCR_Y];
808 for (int dy = 0; dy < VIS_HEIGHT; ++dy) {
809 for (int dx = 0; dx < VIS_WIDTH; ++dx) {
810 int x = scrX+dx, y = scrY+dy;
811 Uint8 b = bgTile(x, y), f = fgTile(x, y);
813 if (b) blitSurface2x(frame, VIS_X+dx*20, VIS_Y+dy*10, banks[CONST_BANK_BG_TILES].spr[b-1][0]);
814 if (f) blitSurface2x(frame, VIS_X+dx*20, VIS_Y+dy*10, banks[CONST_BANK_FG_TILES].spr[f-1][0]);
817 } else {
818 for (int dy = 0; dy < MAP_HEIGHT; ++dy) {
819 for (int dx = 1; dx < MAP_WIDTH; ++dx) {
820 Uint8 b = bgTile(dx, dy), f = fgTile(dx, dy), t = 8;
822 if (f == 0) {
823 if (b >= 1 && b <= 8) t = b-1; else t = 8;
824 } else {
825 t = f+8;
827 if (banks[CONST_BANK_MAP_TILES].spr[t][0]) {
828 blitSurface2x(frame, VIS_X+dx*6, VIS_Y+dy*4, banks[CONST_BANK_MAP_TILES].spr[t][0]);
836 static void levelDrawSprites (SDL_Surface *frame) {
837 if (!inMinimap) {
838 int scrX = vmGVars[GVAR_SCR_X], scrY = vmGVars[GVAR_SCR_Y];
839 int py = vmGetTVar(0, TVAR_POS_Y)-scrY;
841 for (int cc = 0; cc <= 2; ++cc) {
842 for (int f = cc==1?1:0; f <= vmLastThread(); ++f) {
843 if (vmIsThreadAlive(f) && !vmIsSuspendedThread(f)) {
844 int b = vmGetTVar(f, TVAR_SPR_BANK);
845 int s = vmGetTVar(f, TVAR_SPR_NUM);
846 int d = vmGetTVar(f, TVAR_SPR_DIR);
848 if (d >= 0 && d <= 1 && s >= 0 && b >= 0 && b <= 255 && s < banks[b].count && banks[b].spr[s][d]) {
849 SDL_Surface *sf = banks[b].spr[s][d];
850 int x = vmGetTVar(f, TVAR_POS_X)-scrX;
851 int y = vmGetTVar(f, TVAR_POS_Y)-scrY;
852 int tx = vmGetTVar(f, TVAR_POS_TX);
853 int ty = vmGetTVar(f, TVAR_POS_TY);
854 int si = vmGetTVar(f, TVAR_SPR_ITEM);
856 if (cc == 0 && b == CONST_BANK_ITEMS) continue;
857 if (cc == 1 && b != CONST_BANK_ITEMS) continue;
858 if (cc == 2) {
859 if (b != CONST_BANK_PROF) {
860 if (b != CONST_BANK_ITEMS) continue;
861 if (y < py) continue;
864 if (b == CONST_BANK_IM_PROF || b == CONST_BANK_PROF) tx -= 5;
865 blitSurface2x(frame, VIS_X+x*20+tx, VIS_Y+y*10+ty-sf->h/2, sf);
866 if (si > 0 && si < banks[CONST_BANK_ITEMS].count) {
867 sf = banks[CONST_BANK_ITEMS].spr[si][0];
868 blitSurface2x(frame, VIS_X+x*20+tx, VIS_Y+y*10+ty-sf->h/2-24, sf);
874 } else {
875 blitSurface2x(frame, VIS_X+(vmGetTVar(0, TVAR_POS_X)+0)*6, VIS_Y+(vmGetTVar(0, TVAR_POS_Y)-4)*4, banks[CONST_BANK_MAP_ITEMS].spr[0][0]);
876 for (int f = 1; f < VM_MAX_THREADS; ++f) {
877 if (vmIsThreadAlive(f) && !vmIsSuspendedThread(f)) {
878 int i = vmGetTVar(f, TVAR_ITEM_ID);
879 if (i > 0 && i < ITEM_MAX) {
880 SDL_Surface *sf = banks[CONST_BANK_MAP_ITEMS].spr[i][0];
881 int x = vmGetTVar(f, TVAR_POS_X);
882 int y = vmGetTVar(f, TVAR_POS_Y);
884 blitSurface2x(frame, VIS_X+(x+0)*6, VIS_Y+(y-2)*4, sf);
892 static void levelDrawCode (SDL_Surface *frame) {
893 int pos = vmGVars[GVAR_LEVEL_CODE_OFS], len = vmGVars[GVAR_LEVEL_CODE_LEN], x;
894 char buf[128];
896 if (pos < 0 || len < 1 || pos+len > vmCodeSize) return;
897 x = 320-len*8;
898 sprintf(buf, "%d", curLevel);
899 x -= strlen(buf)*8;
900 for (; len > 0; x += 8, ++pos, --len) drawChar(frame, vmCode[pos], x, 0, 3);
904 static void levelDrawMisc (SDL_Surface *frame) {
905 if (demorecmode < 0) {
906 if (SDL_GetTicks()%1000 < 500) drawChar(frame, 'R', 0, 8, 3);
907 } else if (demorecmode > 0) {
908 if (SDL_GetTicks()%1000 < 500) drawChar(frame, 'D', 0, 8, 3);
913 static void drawSpriteBank (SDL_Surface *frame, SpriteBank *bank) {
914 SDL_Rect dst;
915 int x = 0, y = 0, maxh = 0;
917 SDL_SetClipRect(frame, NULL);
918 dst.x = 0;
919 dst.y = 0;
920 dst.w = frame->w;
921 dst.h = frame->h;
922 SDL_FillRect(frame, &dst, palette[3]);
924 for (int num = 0; num < bank->count; ++num) {
925 SDL_Surface *s = bank->spr[num][0];
927 if (s != NULL) {
928 char buf[16];
929 int w = s->w/2;
931 sprintf(buf, "%d", num);
932 if (w < strlen(buf)*8+2) w = strlen(buf)*8+2;
933 if (x+w > 320) {
934 x = 0;
935 y += maxh;
936 maxh = 0;
938 if (maxh < s->h/2+1) maxh = s->h/2+1;
939 blitSurface2x(frame, x, y, s);
940 drawString(frame, buf, x, y, 1);
941 x += w+1;
947 static void frmGameDraw (SDL_Surface *frame) {
948 SDL_Rect rc;
949 char buf[8];
950 int pi;
952 if (vmPaused && doSave) {
953 if (doSave < 0) {
954 if (loadGame() != 0) fatal("can't load game, VM is inconsistent, dying.");
955 } else {
956 saveGame();
958 doSave = 0;
959 vmPaused = inMinimap;
962 SDL_SetClipRect(frame, NULL);
964 if (curLevel >= 0) {
965 if (!inMinimap) {
966 blitSurface(frame, 0, 0, backs[levelback]);
967 } else {
968 blitSurface(frame, 0, 0, backs[1]);
970 drawString(frame, levelname, lnamex, 0, 0x76);
971 levelDrawCode(frame);
972 levelDrawMisc(frame);
974 pi = vmGVars[GVAR_PROF_ITEM];
975 if (vmIsThreadAlive(pi) && vmIsSuspendedThread(pi)) {
976 pi = vmGetTVar(pi, TVAR_ITEM_ID);
977 if (pi > 0 && pi < 11) {
978 drawString(frame, itemNames[pi], 0, 0, 1);
982 sprintf(buf, "%d", curLevel+1);
983 drawString(frame, buf, 320-strlen(buf)*8, 0, 1);
985 rc.x = VIS_X*2;
986 rc.y = VIS_Y*2;
987 rc.w = VIS_WIDTH*20*2;
988 rc.h = VIS_HEIGHT*10*2;
989 SDL_SetClipRect(frame, &rc);
991 levelDrawMap(frame);
992 levelDrawSprites(frame);
993 } else {
994 SDL_Rect dst;
996 dst.x = 0;
997 dst.y = 0;
998 dst.w = frame->w;
999 dst.h = frame->h;
1000 SDL_FillRect(frame, &dst, palette[0]);
1003 SDL_SetClipRect(frame, NULL);
1004 levelDrawMisc(frame);
1006 if (goobers) {
1007 for (int f = 84; f <= 90; ++f) if (fkeys[f-83]) drawSpriteBank(frame, &banks[f]);
1012 ////////////////////////////////////////////////////////////////////////////////
1013 int loadLevel (int lidx) {
1014 if (lidx < 0 || lidx > 73) return -1;
1015 return loadLevelInternal(&resfile, lidx);
1019 void activateMinimap (void) {
1020 inMinimap = 1;
1021 vmPaused = 1;
1022 doSave = 0;
1026 ////////////////////////////////////////////////////////////////////////////////
1027 void setMainLoopGame (void) {
1028 frameCB = frmGameDraw;
1029 keyCB = frmGameKey;
1030 beforeVMCB = demoCB;
1031 vmMapGetCB = mapGet;
1032 vmMapSetCB = mapSet;
1034 setSeed(time(NULL));
1035 vmPaused = 0;
1036 inMinimap = 0;
1037 doSave = 0;
1038 //curLevel = -1;
1039 //demorecmode = 0;
1040 message[0] = 0;
1041 memset(fkeys, 0, sizeof(fkeys));
1042 memset(gamekeys, 0, sizeof(gamekeys));