code loader moved to VM source
[awish.git] / src / game.c
blob5ecadb27ad666c516922c7f314f310d461c5f08b
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 int loadGame (void) {
271 FILE *fl = fopen("awish.sav", "rb");
273 if (fl != NULL) {
274 if (vmLoadState(fl) != 0) goto error;
275 if (readUDW(fl, &demo_m_w) != 0) goto error;
276 if (readUDW(fl, &demo_m_z) != 0) goto error;
277 if (readDW(fl, &inMinimap) != 0) goto error;
278 if (readDW(fl, &curLevel) != 0) goto error;
279 if (readBuf(fl, levelname, sizeof(levelname)) != 0) goto error;
280 if (readDW(fl, &lnamex) != 0) goto error;
281 if (readDW(fl, &levelback) != 0) goto error;
282 if (readBuf(fl, bmap, sizeof(bmap)) != 0) goto error;
283 if (readBuf(fl, fmap, sizeof(fmap)) != 0) goto error;
284 if (readBuf(fl, xflags, sizeof(xflags)) != 0) goto error;
285 fclose(fl);
286 setSeedL(demo_m_w);
287 setSeedL(demo_m_z);
288 return 0;
290 error:
291 fclose(fl);
292 if (goobers) fprintf(stderr, "error loading saved game\n");
293 return -1;
297 static int saveGame (void) {
298 FILE *fl = fopen("awish.sav", "wb");
300 if (fl != NULL) {
301 demo_m_w = getSeedL();
302 demo_m_z = getSeedH();
303 if (vmSaveState(fl) != 0) goto error;
304 if (writeUDW(fl, demo_m_w) != 0) goto error;
305 if (writeUDW(fl, demo_m_z) != 0) goto error;
306 if (writeDW(fl, inMinimap) != 0) goto error;
307 if (writeDW(fl, curLevel) != 0) goto error;
308 if (writeBuf(fl, levelname, sizeof(levelname)) != 0) goto error;
309 if (writeDW(fl, lnamex) != 0) goto error;
310 if (writeDW(fl, levelback) != 0) goto error;
311 if (writeBuf(fl, bmap, sizeof(bmap)) != 0) goto error;
312 if (writeBuf(fl, fmap, sizeof(fmap)) != 0) goto error;
313 if (writeBuf(fl, xflags, sizeof(xflags)) != 0) goto error;
314 fclose(fl);
315 return 0;
317 error:
318 fclose(fl);
319 unlink("awish.sav");
320 if (goobers) fprintf(stderr, "error saving game\n");
321 return -1;
325 ////////////////////////////////////////////////////////////////////////////////
326 static void demoClear (void) {
327 //demorecmode = 0;
328 demoSize = 0;
329 demoPos = 0;
330 demoPrevKeyState = 0;
331 demoKeyStateRepeats = 0;
332 if (demoData != NULL) free(demoData);
333 demoData = NULL;
337 static void demoAddByte (Uint8 b) {
338 if (demoPos+1 > demoSize && demoSize > 1024*1024*16) fatal("out of memory in demo recording!");
339 if (demoPos+1 > demoSize) {
340 int newsz = demoSize+1024*32;
341 Uint8 *nn = realloc(demoData, newsz);
343 if (!nn) fatal("out of memory in demo recording!");
344 demoData = nn;
345 demoSize = newsz;
347 demoData[demoPos++] = b;
351 static Uint8 demoGetKeyState (void) {
352 return
353 ((gamekeys[0]?1:0)<<0) |
354 ((gamekeys[1]?1:0)<<1) |
355 ((gamekeys[2]?1:0)<<2) |
356 ((gamekeys[3]?1:0)<<3) |
357 ((gamekeys[4]?1:0)<<4) |
358 ((gamekeys[5]?1:0)<<5);
362 static void demoSetKeyState (Uint8 kstate) {
363 gamekeys[0] = (kstate&(1<<0)) != 0;
364 gamekeys[1] = (kstate&(1<<1)) != 0;
365 gamekeys[2] = (kstate&(1<<2)) != 0;
366 gamekeys[3] = (kstate&(1<<3)) != 0;
367 gamekeys[4] = (kstate&(1<<4)) != 0;
368 gamekeys[5] = (kstate&(1<<5)) != 0;
372 static void demoAddFrameData (void) {
373 Uint8 kstate = demoGetKeyState();
375 if (demoKeyStateRepeats == 256) {
376 demoAddByte(demoKeyStateRepeats-1);
377 demoAddByte(demoPrevKeyState);
378 demoKeyStateRepeats = 0;
381 if (kstate != demoPrevKeyState) {
382 if (demoKeyStateRepeats > 0) {
383 demoAddByte(demoKeyStateRepeats-1);
384 demoAddByte(demoPrevKeyState);
386 demoPrevKeyState = kstate;
387 demoKeyStateRepeats = 1;
388 } else {
389 ++demoKeyStateRepeats;
394 static void demoGetFrameData (void) {
395 while (demoKeyStateRepeats < 1) {
396 if (demoPos+2 > demoSize) {
397 demoClear();
398 demorecmode = 0;
399 if (goobers) fprintf(stderr, "demo complete\n");
400 strcpy(message, "demo stopped");
401 return;
403 demoKeyStateRepeats = demoData[demoPos++];
404 ++demoKeyStateRepeats;
405 demoPrevKeyState = demoData[demoPos++];
407 --demoKeyStateRepeats;
409 demoSetKeyState(demoPrevKeyState);
413 static int demoSave (void) {
414 FILE *fl;
415 char buf[128];
416 char sign[5];
418 if (demoKeyStateRepeats > 0) {
419 demoAddByte(demoKeyStateRepeats-1);
420 demoAddByte(demoPrevKeyState);
423 sprintf(buf, "awish%02d.dmo", curLevel+1);
424 fl = fopen(buf, "wb");
425 if (fl == NULL) {
426 if (goobers) fprintf(stderr, "can't create demo file '%s'\n", buf);
427 return -1;
429 strcpy(sign, "AWD1");
430 if (fwrite(sign, 4, 1, fl) != 1) goto error;
431 if (writeDW(fl, curLevel) != 0) goto error;
432 if (writeUDW(fl, demo_m_w) != 0) goto error;
433 if (writeUDW(fl, demo_m_z) != 0) goto error;
434 if (writeDW(fl, demoPos) != 0) goto error;
435 if (writeBuf(fl, demoData, demoPos) != 0) goto error;
436 fclose(fl);
437 if (goobers) fprintf(stderr, "demo saved to file '%s'\n", buf);
438 return 0;
439 error:
440 fclose(fl);
441 unlink(buf);
442 if (goobers) fprintf(stderr, "can't write demo file '%s'\n", buf);
443 return -1;
447 static int demoLoad (void) {
448 FILE *fl;
449 char buf[128];
450 int size, level;
451 char sign[4];
453 demoClear();
454 sprintf(buf, "awish%02d.dmo", curLevel+1);
455 fl = fopen(buf, "rb");
456 if (fl == NULL) {
457 if (goobers) fprintf(stderr, "can't open demo file '%s'\n", buf);
458 return -1;
461 if (fread(sign, 4, 1, fl) != 1) goto error;
462 if (memcmp(sign, "AWD1", 4) != 0) goto error;
463 if (readDW(fl, &level) != 0) goto error;
464 if (readUDW(fl, &demo_m_w) != 0) goto error;
465 if (readUDW(fl, &demo_m_z) != 0) goto error;
466 if (readDW(fl, &size) != 0) goto error;
467 if (size < 1 || size > 1024*1024*16) goto error;
468 demoData = malloc(size);
469 if (demoData == NULL) goto error;
470 if (readBuf(fl, demoData, size) != 0) goto error;
471 demoSize = size;
472 demoPos = 0;
473 fclose(fl);
474 setSeedL(demo_m_w);
475 setSeedL(demo_m_z);
476 if (goobers) fprintf(stderr, "loaded demo file '%s'\n", buf);
478 return 0;
479 error:
480 fclose(fl);
481 if (goobers) fprintf(stderr, "can't load demo file '%s'\n", buf);
482 demoClear();
483 return -1;
487 // CODE_GAME_RESTART_LEVEL
488 // GVAR_GAME_STATE
489 // CONST_GAME_STATE_DEAD
490 // CONST_GAME_STATE_COMPLETE
491 // CONST_GAME_STATE_STARTED
492 // CONST_GAME_STATE_PLAYING
493 // GVAR_CUR_LEVEL
494 static inline int isGameStopped (void) {
495 return
496 vmGVars[GVAR_GAME_STATE] == CONST_GAME_STATE_DEAD ||
497 vmGVars[GVAR_GAME_STATE] == CONST_GAME_STATE_COMPLETE;
501 static void demoCB (void) {
502 if (vmGVars[GVAR_GAME_STATE] == CONST_GAME_STATE_STARTED) unloadMapScript();
504 switch (demorecmode) {
505 case -666: // prepare to load demo
506 if (demoLoad() != 0) {
507 demorecmode = 0;
508 break;
510 demorecmode = -2;
511 // fallthru
512 case 666: // prepare to save demo
513 vmSetPC(0, CODE_GAME_RESTART_LEVEL);
514 vmGVars[GVAR_CUR_LEVEL] = curLevel;
515 vmGVars[GVAR_GAME_STATE] = -1; // invalid state
516 if (demorecmode > 0) {
517 demoClear();
518 demorecmode = 2;
520 break;
521 case -1: // demo replaying
522 case 1: // demo saving
523 if (isGameStopped()) {
524 // the level is over or prof is dead
525 if (demorecmode > 0) demoSave();
526 demoClear();
527 demorecmode = 0;
528 } else {
529 ((demorecmode < 0) ? demoGetFrameData : demoAddFrameData)();
531 break;
532 case -2: // waiting for 'game started' trigger
533 case 2: // waiting for 'game started' trigger
534 if (vmGVars[GVAR_GAME_STATE] >= 0) {
535 if (goobers) fprintf(stderr, "demo %s started...\n", demorecmode<0?"replaying":"recording");
536 if (demorecmode < 0) {
537 // replay
538 setSeedL(demo_m_w);
539 setSeedL(demo_m_z);
540 } else {
541 // record
542 demo_m_w = getSeedL();
543 demo_m_z = getSeedH();
545 demorecmode = (demorecmode < 0) ? -1 : 1;
547 break;
549 setGameKeysGVars();
553 ////////////////////////////////////////////////////////////////////////////////
554 static void frmGameKey (SDL_KeyboardEvent *key) {
555 if (key->type == SDL_KEYDOWN) {
556 switch (key->keysym.sym) {
557 case SDLK_ESCAPE:
558 case SDLK_SPACE:
559 case SDLK_RETURN:
560 if (vmPaused && inMinimap) {
561 inMinimap = 0;
562 doSave = 0;
563 vmPaused = 0;
564 gamekeys[6] = 0;
566 break;
567 case SDLK_F12:
568 vmGVars[GVAR_KEY_QUIT] = 1;
569 break;
570 case SDLK_p:
571 if (vmPaused == 0) {
572 gamekeys[6] = 1;
573 } else {
574 if (inMinimap) {
575 inMinimap = 0;
576 doSave = 0;
577 vmPaused = 0;
578 gamekeys[6] = 1;
581 break;
582 case SDLK_r:
583 if (vmPaused == 0) {
584 if (demorecmode != 0) { demoClear(); demorecmode = 0; }
585 if ((key->keysym.mod&(KMOD_CTRL)) != 0) {
586 gamekeys[7] = 1;
589 break;
590 case SDLK_s:
591 if (vmPaused == 0 && !demorecmode) {
592 if (goobers && (key->keysym.mod&(KMOD_CTRL)) != 0) { doSave = 1; vmPaused = 1; }
594 break;
595 case SDLK_l:
596 if (vmPaused == 0 && !demorecmode) {
597 if (goobers && (key->keysym.mod&(KMOD_CTRL)) != 0) { doSave = -1; vmPaused = 1; }
599 break;
600 case SDLK_d:
601 if (vmPaused == 0) {
602 if (goobers && (key->keysym.mod&(KMOD_CTRL)) != 0) {
603 if (!demorecmode) {
604 demorecmode = 666; // 'start saving'
605 } else {
606 demoSave();
607 demoClear();
608 demorecmode = 0;
612 break;
613 case SDLK_m:
614 if (vmPaused == 0) {
615 if (goobers && (key->keysym.mod&(KMOD_CTRL)) != 0) {
616 if (!demorecmode) {
617 demorecmode = -666; // 'start playing'
618 } else {
619 demoClear();
620 demorecmode = 0;
621 demoSetKeyState(0);
625 break;
626 default: ;
630 if (demorecmode == 0 || demorecmode == 1) {
631 switch (key->keysym.sym) {
632 case SDLK_LEFT: case SDLK_KP4: gamekeys[0] = (key->type == SDL_KEYDOWN); break;
633 case SDLK_RIGHT: case SDLK_KP6: gamekeys[1] = (key->type == SDL_KEYDOWN); break;
634 case SDLK_UP: case SDLK_KP8: gamekeys[2] = (key->type == SDL_KEYDOWN); break;
635 case SDLK_DOWN: case SDLK_KP2: gamekeys[3] = (key->type == SDL_KEYDOWN); break;
636 case SDLK_SPACE: case SDLK_KP0: gamekeys[4] = (key->type == SDL_KEYDOWN); break;
637 case SDLK_RETURN: case SDLK_KP_PERIOD: gamekeys[5] = (key->type == SDL_KEYDOWN); break;
638 default: ;
641 if (goobers && !demorecmode) {
642 switch (key->keysym.sym) {
643 case SDLK_f: gamekeys[8] = (key->type == SDL_KEYDOWN); break;
644 case SDLK_w: gamekeys[9] = (key->type == SDL_KEYDOWN); break;
645 case SDLK_KP_MINUS: gamekeys[10] = (key->type == SDL_KEYDOWN); break;
646 case SDLK_KP_PLUS: gamekeys[11] = (key->type == SDL_KEYDOWN); break;
648 case SDLK_F1: fkeys[1] = (key->type == SDL_KEYDOWN); break;
649 case SDLK_F2: fkeys[2] = (key->type == SDL_KEYDOWN); break;
650 case SDLK_F3: fkeys[3] = (key->type == SDL_KEYDOWN); break;
651 case SDLK_F4: fkeys[4] = (key->type == SDL_KEYDOWN); break;
652 case SDLK_F5: fkeys[5] = (key->type == SDL_KEYDOWN); break;
653 case SDLK_F6: fkeys[6] = (key->type == SDL_KEYDOWN); break;
654 case SDLK_F7: fkeys[7] = (key->type == SDL_KEYDOWN); break;
655 case SDLK_F8: fkeys[8] = (key->type == SDL_KEYDOWN); break;
656 case SDLK_F9: fkeys[9] = (key->type == SDL_KEYDOWN); break;
657 default: ;
664 ////////////////////////////////////////////////////////////////////////////////
665 static int loadLevelInternal (ResFile *resfile, int lidx) {
666 Uint8 *lvl;
667 int sz, res = -1, pos;
668 static Item items[256];
669 static int itemCount;
670 static int scrX, scrY;
671 static int profX, profY;
672 char buf[64];
674 unloadMapScript();
676 message[0] = 0;
678 vmGVars[GVAR_MAP_WIDTH] = MAP_WIDTH;
679 vmGVars[GVAR_MAP_HEIGHT] = MAP_HEIGHT;
680 vmGVars[GVAR_VIS_WIDTH] = VIS_WIDTH;
681 vmGVars[GVAR_VIS_HEIGHT] = VIS_HEIGHT;
683 if ((lvl = loadResFile(resfile, lidx+9, &sz)) == NULL) return -1;
684 if (sz < 3700) goto quit;
685 if (lvl[0] > 25) goto quit;
686 memset(levelname, 0, sizeof(levelname));
687 if (lvl[0] > 0) memcpy(levelname, lvl+1, lvl[0]);
688 lnamex = (320-strlen(levelname)*8)/2;
689 sprintf(buf, "%d", lidx+1);
690 while (lnamex+strlen(levelname)*8+strlen(buf)*8+6*8 > 320) lnamex -= 8;
691 levelback = lvl[26];
692 if (levelback < 1 || levelback > 7) levelback = 1;
693 profX = lvl[27];
694 profY = lvl[28];
695 vmSetTVar(0, TVAR_POS_X, profX);
696 vmSetTVar(0, TVAR_POS_Y, profY);
697 scrX = lvl[29];
698 scrY = lvl[30]; if (scrY > 0) --scrY;
699 vmGVars[GVAR_SCR_X] = scrX;
700 vmGVars[GVAR_SCR_Y] = scrY;
701 pos = 31;
702 memset(bmap, 0, sizeof(bmap));
703 memset(fmap, 0, sizeof(fmap));
704 memset(xflags, 0, sizeof(xflags));
705 for (int x = 1; x < MAP_WIDTH; ++x) {
706 for (int y = 0; y < MAP_HEIGHT; ++y) {
707 bmap[y][x] = lvl[pos++];
710 for (int x = 1; x < MAP_WIDTH; ++x) {
711 for (int y = 0; y < MAP_HEIGHT; ++y) {
712 fmap[y][x] = lvl[pos++];
714 pos += 3; // skip unknown bytes
717 itemCount = lvl[pos++]+1; // at least one item is always here
718 for (int f = 0; f < itemCount; ++f) {
719 int tid;
721 items[f].x = lvl[pos++];
722 items[f].y = lvl[pos++];
723 items[f].fframe = lvl[pos++];
724 items[f].lframe = lvl[pos++];
725 items[f].frame = lvl[pos++];
726 items[f].animated = lvl[pos++];
727 if (!items[f].animated) items[f].frame = items[f].fframe;
728 ++pos; // 0xff
729 pos += 4; // dunno
730 items[f].type = lvl[pos++];
731 ++pos; // dunno
732 // spawn thread
733 tid = vmNewThread(CODE_ENTRY_ITEM);
734 if (tid < 0) goto quit;
735 vmSetTVar(tid, TVAR_ITEM_ID, items[f].type);
736 vmSetTVar(tid, TVAR_ANIMATED, items[f].animated);
737 vmSetTVar(tid, TVAR_AFIRST_FRAME, items[f].fframe);
738 vmSetTVar(tid, TVAR_ALAST_FRAME, items[f].lframe);
739 vmSetTVar(tid, TVAR_AFRAME, items[f].frame);
740 if (items[f].type == 6) vmSetTVar(tid, TVAR_AFRAME, lvl[pos-6]);
741 vmSetTVar(tid, TVAR_POS_X, items[f].x);
742 vmSetTVar(tid, TVAR_POS_Y, items[f].y);
743 vmSetTVar(tid, TVAR_POS_TX, 0);
744 vmSetTVar(tid, TVAR_POS_TY, 0);
745 vmSetTVar(tid, TVAR_SPR_BANK, CONST_BANK_ITEMS);
746 vmSetTVar(tid, TVAR_SPR_NUM, items[f].frame);
747 vmSetTVar(tid, TVAR_SPR_DIR, 0);
749 curLevel = lidx;
750 inMinimap = 0;
751 doSave = 0;
752 res = 0;
754 loadMapScript(resfile);
756 quit:
757 free(lvl);
758 return res;
762 ////////////////////////////////////////////////////////////////////////////////
763 static inline Uint8 fgTile (int x, int y) {
764 return (x >= 0 && y >= 0 && x < MAP_WIDTH && y < MAP_HEIGHT) ? fmap[y][x]&0x0f : 0;
768 static inline Uint8 bgTile (int x, int y) {
769 if (y == -666) return (x >= 0 && x < MAP_WIDTH) ? xflags[x] : 0;
770 return (x >= 0 && y >= 0 && x < MAP_WIDTH && y < MAP_HEIGHT) ? bmap[y][x]&0x0f : 0;
774 static int mapGet (int tid, int fg, int x, int y) {
775 return fg ? fgTile(x, y) : bgTile(x, y);
779 static inline void setFGTile (int x, int y, Uint8 tile) {
780 if (x >= 0 && y >= 0 && x < MAP_WIDTH && y < MAP_HEIGHT) fmap[y][x] = (fmap[y][x]&0xf0)|(tile&0x0f);
784 static inline void setBGTile (int x, int y, Uint8 tile) {
785 if (y == -666 && x >= 0 && x < MAP_WIDTH) xflags[x] = tile;
786 if (x >= 0 && y >= 0 && x < MAP_WIDTH && y < MAP_HEIGHT) bmap[y][x] = (bmap[y][x]&0xf0)|(tile&0x0f);
790 static void mapSet (int tid, int fg, int x, int y, int tile) {
791 if (fg) setFGTile(x, y, tile); else setBGTile(x, y, tile);
795 ////////////////////////////////////////////////////////////////////////////////
796 // cliprect should be set
797 static void levelDrawMap (SDL_Surface *frame) {
798 if (!inMinimap) {
799 int scrX = vmGVars[GVAR_SCR_X], scrY = vmGVars[GVAR_SCR_Y];
801 for (int dy = 0; dy < VIS_HEIGHT; ++dy) {
802 for (int dx = 0; dx < VIS_WIDTH; ++dx) {
803 int x = scrX+dx, y = scrY+dy;
804 Uint8 b = bgTile(x, y), f = fgTile(x, y);
806 if (b) blitSurface2x(frame, VIS_X+dx*20, VIS_Y+dy*10, banks[CONST_BANK_BG_TILES].spr[b-1][0]);
807 if (f) blitSurface2x(frame, VIS_X+dx*20, VIS_Y+dy*10, banks[CONST_BANK_FG_TILES].spr[f-1][0]);
810 } else {
811 for (int dy = 0; dy < MAP_HEIGHT; ++dy) {
812 for (int dx = 1; dx < MAP_WIDTH; ++dx) {
813 Uint8 b = bgTile(dx, dy), f = fgTile(dx, dy), t = 8;
815 if (f == 0) {
816 if (b >= 1 && b <= 8) t = b-1; else t = 8;
817 } else {
818 t = f+8;
820 if (banks[CONST_BANK_MAP_TILES].spr[t][0]) {
821 blitSurface2x(frame, VIS_X+dx*6, VIS_Y+dy*4, banks[CONST_BANK_MAP_TILES].spr[t][0]);
829 static void levelDrawSprites (SDL_Surface *frame) {
830 if (!inMinimap) {
831 int scrX = vmGVars[GVAR_SCR_X], scrY = vmGVars[GVAR_SCR_Y];
832 int py = vmGetTVar(0, TVAR_POS_Y)-scrY;
834 for (int cc = 0; cc <= 2; ++cc) {
835 for (int f = cc==1?1:0; f <= vmLastThread(); ++f) {
836 if (vmIsThreadAlive(f) && !vmIsSuspendedThread(f)) {
837 int b = vmGetTVar(f, TVAR_SPR_BANK);
838 int s = vmGetTVar(f, TVAR_SPR_NUM);
839 int d = vmGetTVar(f, TVAR_SPR_DIR);
841 if (d >= 0 && d <= 1 && s >= 0 && b >= 0 && b <= 255 && s < banks[b].count && banks[b].spr[s][d]) {
842 SDL_Surface *sf = banks[b].spr[s][d];
843 int x = vmGetTVar(f, TVAR_POS_X)-scrX;
844 int y = vmGetTVar(f, TVAR_POS_Y)-scrY;
845 int tx = vmGetTVar(f, TVAR_POS_TX);
846 int ty = vmGetTVar(f, TVAR_POS_TY);
847 int si = vmGetTVar(f, TVAR_SPR_ITEM);
849 if (cc == 0 && b == CONST_BANK_ITEMS) continue;
850 if (cc == 1 && b != CONST_BANK_ITEMS) continue;
851 if (cc == 2) {
852 if (b != CONST_BANK_PROF) {
853 if (b != CONST_BANK_ITEMS) continue;
854 if (y < py) continue;
857 if (b == CONST_BANK_IM_PROF || b == CONST_BANK_PROF) tx -= 5;
858 blitSurface2x(frame, VIS_X+x*20+tx, VIS_Y+y*10+ty-sf->h/2, sf);
859 if (si > 0 && si < banks[CONST_BANK_ITEMS].count) {
860 sf = banks[CONST_BANK_ITEMS].spr[si][0];
861 blitSurface2x(frame, VIS_X+x*20+tx, VIS_Y+y*10+ty-sf->h/2-24, sf);
867 } else {
868 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]);
869 for (int f = 1; f < VM_MAX_THREADS; ++f) {
870 if (vmIsThreadAlive(f) && !vmIsSuspendedThread(f)) {
871 int i = vmGetTVar(f, TVAR_ITEM_ID);
872 if (i > 0 && i < ITEM_MAX) {
873 SDL_Surface *sf = banks[CONST_BANK_MAP_ITEMS].spr[i][0];
874 int x = vmGetTVar(f, TVAR_POS_X);
875 int y = vmGetTVar(f, TVAR_POS_Y);
877 blitSurface2x(frame, VIS_X+(x+0)*6, VIS_Y+(y-2)*4, sf);
885 static void levelDrawCode (SDL_Surface *frame) {
886 int pos = vmGVars[GVAR_LEVEL_CODE_OFS], len = vmGVars[GVAR_LEVEL_CODE_LEN], x;
887 char buf[128];
889 if (pos < 0 || len < 1 || pos+len > vmCodeSize) return;
890 x = 320-len*8;
891 sprintf(buf, "%d", curLevel);
892 x -= strlen(buf)*8;
893 for (; len > 0; x += 8, ++pos, --len) drawChar(frame, vmCode[pos], x, 0, 3);
897 static void levelDrawMisc (SDL_Surface *frame) {
898 if (demorecmode < 0) {
899 if (SDL_GetTicks()%1000 < 500) drawChar(frame, 'R', 0, 8, 3);
900 } else if (demorecmode > 0) {
901 if (SDL_GetTicks()%1000 < 500) drawChar(frame, 'D', 0, 8, 3);
906 static void drawSpriteBank (SDL_Surface *frame, SpriteBank *bank) {
907 SDL_Rect dst;
908 int x = 0, y = 0, maxh = 0;
910 SDL_SetClipRect(frame, NULL);
911 dst.x = 0;
912 dst.y = 0;
913 dst.w = frame->w;
914 dst.h = frame->h;
915 SDL_FillRect(frame, &dst, palette[3]);
917 for (int num = 0; num < bank->count; ++num) {
918 SDL_Surface *s = bank->spr[num][0];
920 if (s != NULL) {
921 char buf[16];
922 int w = s->w/2;
924 sprintf(buf, "%d", num);
925 if (w < strlen(buf)*8+2) w = strlen(buf)*8+2;
926 if (x+w > 320) {
927 x = 0;
928 y += maxh;
929 maxh = 0;
931 if (maxh < s->h/2+1) maxh = s->h/2+1;
932 blitSurface2x(frame, x, y, s);
933 drawString(frame, buf, x, y, 1);
934 x += w+1;
940 static void frmGameDraw (SDL_Surface *frame) {
941 SDL_Rect rc;
942 char buf[8];
943 int pi;
945 if (vmPaused && doSave) {
946 if (doSave < 0) {
947 loadGame();
948 } else {
949 saveGame();
951 doSave = 0;
952 vmPaused = inMinimap;
955 SDL_SetClipRect(frame, NULL);
957 if (curLevel >= 0) {
958 if (!inMinimap) {
959 blitSurface(frame, 0, 0, backs[levelback]);
960 } else {
961 blitSurface(frame, 0, 0, backs[1]);
963 drawString(frame, levelname, lnamex, 0, 0x76);
964 levelDrawCode(frame);
965 levelDrawMisc(frame);
967 pi = vmGVars[GVAR_PROF_ITEM];
968 if (vmIsThreadAlive(pi) && vmIsSuspendedThread(pi)) {
969 pi = vmGetTVar(pi, TVAR_ITEM_ID);
970 if (pi > 0 && pi < 11) {
971 drawString(frame, itemNames[pi], 0, 0, 1);
975 sprintf(buf, "%d", curLevel+1);
976 drawString(frame, buf, 320-strlen(buf)*8, 0, 1);
978 rc.x = VIS_X*2;
979 rc.y = VIS_Y*2;
980 rc.w = VIS_WIDTH*20*2;
981 rc.h = VIS_HEIGHT*10*2;
982 SDL_SetClipRect(frame, &rc);
984 levelDrawMap(frame);
985 levelDrawSprites(frame);
986 } else {
987 SDL_Rect dst;
989 dst.x = 0;
990 dst.y = 0;
991 dst.w = frame->w;
992 dst.h = frame->h;
993 SDL_FillRect(frame, &dst, palette[0]);
996 SDL_SetClipRect(frame, NULL);
997 levelDrawMisc(frame);
999 if (goobers) {
1000 for (int f = 84; f <= 90; ++f) if (fkeys[f-83]) drawSpriteBank(frame, &banks[f]);
1005 ////////////////////////////////////////////////////////////////////////////////
1006 int loadLevel (int lidx) {
1007 if (lidx < 0 || lidx > 73) return -1;
1008 return loadLevelInternal(&resfile, lidx);
1012 void activateMinimap (void) {
1013 inMinimap = 1;
1014 vmPaused = 1;
1015 doSave = 0;
1019 ////////////////////////////////////////////////////////////////////////////////
1020 void setMainLoopGame (void) {
1021 frameCB = frmGameDraw;
1022 keyCB = frmGameKey;
1023 beforeVMCB = demoCB;
1024 vmMapGetCB = mapGet;
1025 vmMapSetCB = mapSet;
1027 setSeed(time(NULL));
1028 vmPaused = 0;
1029 inMinimap = 0;
1030 doSave = 0;
1031 //curLevel = -1;
1032 //demorecmode = 0;
1033 message[0] = 0;
1034 memset(fkeys, 0, sizeof(fkeys));
1035 memset(gamekeys, 0, sizeof(gamekeys));