added simple polygon filling endine
[awish.git] / src / game.c
blob4b7e8025953e9d7b5135604f59cef33cb0d64426
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"
29 #include "polymod.h"
32 extern int goobers;
36 ////////////////////////////////////////////////////////////////////////////////
37 VMRSTCB gameRSTCB = NULL;
40 ////////////////////////////////////////////////////////////////////////////////
41 #define VIS_WIDTH (15)
42 #define VIS_HEIGHT (17)
43 #define VIS_X (10)
44 #define VIS_Y (20)
47 ////////////////////////////////////////////////////////////////////////////////
48 static int redrawLevel;
49 static int oldScrX, oldScrY;
50 static int mapWidth;
51 static int mapHeight;
52 static int curLevel;
53 static char levelname[257];
54 static int lnamex;
55 static Uint8 *levelData = NULL;
56 static int levelDataSize = 0;
57 static Uint8 *bmap = NULL, *fmap = NULL;
58 static Uint8 *xflags = NULL;
59 static int inMinimap;
61 static int doSave = 0;
63 static int demorecmode = 0; // 0: none; 1: recording; -1: playing
64 static int demoSize = 0;
65 static int demoPos = 0;
66 static Uint8 *demoData = NULL;
67 static Uint8 demoPrevKeyState = 0;
68 static int demoKeyStateRepeats = 0;
70 static char message[256];
71 //static Uint32 msgEndTime = 0;
73 static Uint32 demo_m_w = 0;
74 static Uint32 demo_m_z = 0;
77 ////////////////////////////////////////////////////////////////////////////////
78 static int gamekeys[12];
79 static int fkeys[10];
82 static void setGameKeysGVars (void) {
83 vmGVars[GVAR_KEY_LEFT] = gamekeys[0] ? 1 : 0;
84 vmGVars[GVAR_KEY_RIGHT] = gamekeys[1] ? 1 : 0;
85 vmGVars[GVAR_KEY_UP] = gamekeys[2] ? 1 : 0;
86 vmGVars[GVAR_KEY_DOWN] = gamekeys[3] ? 1 : 0;
87 vmGVars[GVAR_KEY_TAKE] = gamekeys[4] ? 1 : 0;
88 vmGVars[GVAR_KEY_USE] = gamekeys[5] ? 1 : 0;
90 vmGVars[GVAR_KEY_MINIMAP] = gamekeys[6] ? 1 : 0;
91 vmGVars[GVAR_KEY_RESTART] = gamekeys[7] ? 1 : 0;
93 vmGVars[GVAR_KEY_FALL_CHEAT] = gamekeys[8] ? 1 : 0;
94 vmGVars[GVAR_KEY_WALK_CHEAT] = gamekeys[9] ? 1 : 0;
95 vmGVars[GVAR_KEY_PLEV_CHEAT] = gamekeys[10] ? 1 : 0;
96 vmGVars[GVAR_KEY_NLEV_CHEAT] = gamekeys[11] ? 1 : 0;
100 ////////////////////////////////////////////////////////////////////////////////
101 #define SECRET (42)
102 //#define SECRET (0)
105 static int readBuf (FILE *fl, void *buf, int len) {
106 unsigned char *c = (unsigned char *)buf;
108 while (len-- > 0) {
109 unsigned char b;
111 if (fread(&b, 1, 1, fl) != 1) return -1;
112 b ^= SECRET;
113 *c++ = b;
115 return 0;
119 static int writeBuf (FILE *fl, const void *buf, int len) {
120 const unsigned char *c = (const unsigned char *)buf;
122 while (len-- > 0) {
123 unsigned char b = *c++;
125 b ^= SECRET;
126 if (fwrite(&b, 1, 1, fl) != 1) return -1;
128 return 0;
132 static int readDW (FILE *fl, int *v) {
133 int t[4];
135 for (int f = 0; f < 4; ++f) {
136 unsigned char b;
138 if (fread(&b, 1, 1, fl) != 1) return -1;
139 t[f] = b^SECRET;
142 if (v) {
143 *v = 0;
144 for (int f = 0; f < 4; ++f) *v |= t[f]<<(8*f);
146 return 0;
150 static int writeDW (FILE *fl, int v) {
151 for (int f = 0; f < 4; ++f) {
152 unsigned char b;
154 b = v&0xff;
155 b ^= SECRET;
156 if (fwrite(&b, 1, 1, fl) != 1) return -1;
157 v >>= 8;
159 return 0;
163 static int readUDW (FILE *fl, Uint32 *v) {
164 int t[4];
166 for (int f = 0; f < 4; ++f) {
167 unsigned char b;
169 if (fread(&b, 1, 1, fl) != 1) return -1;
170 t[f] = b^SECRET;
173 if (v) {
174 *v = 0;
175 for (int f = 0; f < 4; ++f) *v |= t[f]<<(8*f);
177 return 0;
181 static int writeUDW (FILE *fl, Uint32 v) {
182 for (int f = 0; f < 4; ++f) {
183 unsigned char b;
185 b = v&0xff;
186 b ^= SECRET;
187 if (fwrite(&b, 1, 1, fl) != 1) return -1;
188 v >>= 8;
190 return 0;
194 ////////////////////////////////////////////////////////////////////////////////
195 static const char *SAVE_GAME_SIGNATURE = "ASG1";
198 static int saveGame (void) {
199 FILE *fl = fopen("awish.sav", "wb");
201 if (fl != NULL) {
202 demo_m_w = getSeedL();
203 demo_m_z = getSeedH();
204 if (fwrite(SAVE_GAME_SIGNATURE, 4, 1, fl) != 1) goto error;
205 if (vmSaveState(fl) != 0) goto error;
206 if (writeDW(fl, mapWidth) != 0) goto error;
207 if (writeDW(fl, mapHeight) != 0) goto error;
208 if (writeUDW(fl, demo_m_w) != 0) goto error;
209 if (writeUDW(fl, demo_m_z) != 0) goto error;
210 if (writeDW(fl, inMinimap) != 0) goto error;
211 if (writeDW(fl, curLevel) != 0) goto error;
212 if (writeBuf(fl, levelname, sizeof(levelname)) != 0) goto error;
213 if (writeDW(fl, lnamex) != 0) goto error;
214 if (writeBuf(fl, bmap, mapWidth*mapHeight) != 0) goto error;
215 if (writeBuf(fl, fmap, mapWidth*mapHeight) != 0) goto error;
216 if (writeBuf(fl, xflags, mapWidth) != 0) goto error;
217 fclose(fl);
218 return 0;
220 error:
221 fclose(fl);
222 unlink("awish.sav");
223 if (goobers) fprintf(stderr, "error saving game\n");
224 return -1;
228 static int loadGame (void) {
229 FILE *fl = fopen("awish.sav", "rb");
231 if (fl != NULL) {
232 char sign[4];
234 if (fread(sign, 4, 1, fl) != 1) goto error;
235 if (memcmp(sign, SAVE_GAME_SIGNATURE, 4) != 0) goto error;
236 if (vmLoadState(fl) != 0) goto error;
237 if (readDW(fl, &mapWidth) != 0) goto error;
238 if (readDW(fl, &mapHeight) != 0) goto error;
239 if (readUDW(fl, &demo_m_w) != 0) goto error;
240 if (readUDW(fl, &demo_m_z) != 0) goto error;
241 if (readDW(fl, &inMinimap) != 0) goto error;
242 if (readDW(fl, &curLevel) != 0) goto error;
243 if (readBuf(fl, levelname, sizeof(levelname)) != 0) goto error;
244 if (readDW(fl, &lnamex) != 0) goto error;
245 if (bmap != NULL) free(bmap); bmap = NULL;
246 if (fmap != NULL) free(fmap); fmap = NULL;
247 if (xflags != NULL) free(xflags); xflags = NULL;
248 fmap = calloc(mapWidth*mapHeight, 1); if (fmap == NULL) fatal("out of memory in loadGame");
249 bmap = calloc(mapWidth*mapHeight, 1); if (bmap == NULL) fatal("out of memory in loadGame");
250 xflags = calloc(mapWidth, 1); if (xflags == NULL) fatal("out of memory in loadGame");
251 if (readBuf(fl, bmap, mapWidth*mapHeight) != 0) goto error;
252 if (readBuf(fl, fmap, mapWidth*mapHeight) != 0) goto error;
253 if (readBuf(fl, xflags, mapWidth) != 0) goto error;
254 fclose(fl);
255 setSeedL(demo_m_w);
256 setSeedL(demo_m_z);
257 redrawLevel = 1;
258 oldScrX = oldScrY = -1;
259 return 0;
261 error:
262 fclose(fl);
263 if (goobers) fprintf(stderr, "error loading saved game\n");
264 return -1;
268 ////////////////////////////////////////////////////////////////////////////////
269 static void demoClear (void) {
270 //demorecmode = 0;
271 demoSize = 0;
272 demoPos = 0;
273 demoPrevKeyState = 0;
274 demoKeyStateRepeats = 0;
275 if (demoData != NULL) free(demoData);
276 demoData = NULL;
280 static void demoAddByte (Uint8 b) {
281 if (demoPos+1 > demoSize && demoSize > 1024*1024*16) fatal("out of memory in demo recording!");
282 if (demoPos+1 > demoSize) {
283 int newsz = demoSize+1024*32;
284 Uint8 *nn = realloc(demoData, newsz);
286 if (!nn) fatal("out of memory in demo recording!");
287 demoData = nn;
288 demoSize = newsz;
290 demoData[demoPos++] = b;
294 static Uint8 demoGetKeyState (void) {
295 return
296 ((gamekeys[0]?1:0)<<0) |
297 ((gamekeys[1]?1:0)<<1) |
298 ((gamekeys[2]?1:0)<<2) |
299 ((gamekeys[3]?1:0)<<3) |
300 ((gamekeys[4]?1:0)<<4) |
301 ((gamekeys[5]?1:0)<<5);
305 static void demoSetKeyState (Uint8 kstate) {
306 gamekeys[0] = (kstate&(1<<0)) != 0;
307 gamekeys[1] = (kstate&(1<<1)) != 0;
308 gamekeys[2] = (kstate&(1<<2)) != 0;
309 gamekeys[3] = (kstate&(1<<3)) != 0;
310 gamekeys[4] = (kstate&(1<<4)) != 0;
311 gamekeys[5] = (kstate&(1<<5)) != 0;
315 static void demoAddFrameData (void) {
316 Uint8 kstate = demoGetKeyState();
318 if (demoKeyStateRepeats == 256) {
319 demoAddByte(demoKeyStateRepeats-1);
320 demoAddByte(demoPrevKeyState);
321 demoKeyStateRepeats = 0;
324 if (kstate != demoPrevKeyState) {
325 if (demoKeyStateRepeats > 0) {
326 demoAddByte(demoKeyStateRepeats-1);
327 demoAddByte(demoPrevKeyState);
329 demoPrevKeyState = kstate;
330 demoKeyStateRepeats = 1;
331 } else {
332 ++demoKeyStateRepeats;
337 static void demoGetFrameData (void) {
338 while (demoKeyStateRepeats < 1) {
339 if (demoPos+2 > demoSize) {
340 demoClear();
341 demorecmode = 0;
342 if (goobers) fprintf(stderr, "demo complete\n");
343 strcpy(message, "demo stopped");
344 return;
346 demoKeyStateRepeats = demoData[demoPos++];
347 ++demoKeyStateRepeats;
348 demoPrevKeyState = demoData[demoPos++];
350 --demoKeyStateRepeats;
352 demoSetKeyState(demoPrevKeyState);
356 static int demoSave (void) {
357 FILE *fl;
358 char buf[128];
359 char sign[5];
361 if (demoKeyStateRepeats > 0) {
362 demoAddByte(demoKeyStateRepeats-1);
363 demoAddByte(demoPrevKeyState);
366 sprintf(buf, "awish%02d.dmo", curLevel+1);
367 fl = fopen(buf, "wb");
368 if (fl == NULL) {
369 if (goobers) fprintf(stderr, "can't create demo file '%s'\n", buf);
370 return -1;
372 strcpy(sign, "AWD1");
373 if (fwrite(sign, 4, 1, fl) != 1) goto error;
374 if (writeDW(fl, curLevel) != 0) goto error;
375 if (writeUDW(fl, demo_m_w) != 0) goto error;
376 if (writeUDW(fl, demo_m_z) != 0) goto error;
377 if (writeDW(fl, demoPos) != 0) goto error;
378 if (writeBuf(fl, demoData, demoPos) != 0) goto error;
379 fclose(fl);
380 if (goobers) fprintf(stderr, "demo saved to file '%s'\n", buf);
381 return 0;
382 error:
383 fclose(fl);
384 unlink(buf);
385 if (goobers) fprintf(stderr, "can't write demo file '%s'\n", buf);
386 return -1;
390 static int demoLoad (void) {
391 FILE *fl;
392 char buf[128];
393 int size, level;
394 char sign[4];
396 demoClear();
397 sprintf(buf, "awish%02d.dmo", curLevel+1);
398 fl = fopen(buf, "rb");
399 if (fl == NULL) {
400 if (goobers) fprintf(stderr, "can't open demo file '%s'\n", buf);
401 return -1;
404 if (fread(sign, 4, 1, fl) != 1) goto error;
405 if (memcmp(sign, "AWD1", 4) != 0) goto error;
406 if (readDW(fl, &level) != 0) goto error;
407 if (readUDW(fl, &demo_m_w) != 0) goto error;
408 if (readUDW(fl, &demo_m_z) != 0) goto error;
409 if (readDW(fl, &size) != 0) goto error;
410 if (size < 1 || size > 1024*1024*16) goto error;
411 demoData = malloc(size);
412 if (demoData == NULL) goto error;
413 if (readBuf(fl, demoData, size) != 0) goto error;
414 demoSize = size;
415 demoPos = 0;
416 fclose(fl);
417 setSeedL(demo_m_w);
418 setSeedL(demo_m_z);
419 if (goobers) fprintf(stderr, "loaded demo file '%s'\n", buf);
421 return 0;
422 error:
423 fclose(fl);
424 if (goobers) fprintf(stderr, "can't load demo file '%s'\n", buf);
425 demoClear();
426 return -1;
430 static inline int isGameStopped (void) {
431 return
432 vmGVars[GVAR_GAME_STATE] == CONST_GAME_STATE_DEAD ||
433 vmGVars[GVAR_GAME_STATE] == CONST_GAME_STATE_COMPLETE;
437 static void demoCB (void) {
438 //if (vmGVars[GVAR_GAME_STATE] == CONST_GAME_STATE_STARTED) unloadMapScript();
440 if (vmGVars[GVAR_SCR_X] != oldScrX || vmGVars[GVAR_SCR_Y] != oldScrY) {
441 oldScrX = vmGVars[GVAR_SCR_X];
442 oldScrY = vmGVars[GVAR_SCR_Y];
443 redrawLevel = 1;
446 switch (demorecmode) {
447 case -666: // prepare to load demo
448 if (demoLoad() != 0) {
449 demorecmode = 0;
450 break;
452 demorecmode = -2;
453 // fallthru
454 case 666: // prepare to save demo
455 vmSetPC(0, CODE_ENTRY_GAME_RESTART_LEVEL);
456 vmGVars[GVAR_CUR_LEVEL] = curLevel;
457 vmGVars[GVAR_GAME_STATE] = -1; // invalid state
458 if (demorecmode > 0) {
459 demoClear();
460 demorecmode = 2;
462 break;
463 case -1: // demo replaying
464 case 1: // demo saving
465 if (isGameStopped()) {
466 // the level is over or prof is dead
467 if (demorecmode > 0) demoSave();
468 demoClear();
469 demorecmode = 0;
470 } else {
471 ((demorecmode < 0) ? demoGetFrameData : demoAddFrameData)();
473 break;
474 case -2: // waiting for 'game started' trigger
475 case 2: // waiting for 'game started' trigger
476 if (vmGVars[GVAR_GAME_STATE] >= 0) {
477 if (goobers) fprintf(stderr, "demo %s started...\n", demorecmode<0?"replaying":"recording");
478 if (demorecmode < 0) {
479 // replay
480 setSeedL(demo_m_w);
481 setSeedL(demo_m_z);
482 } else {
483 // record
484 demo_m_w = getSeedL();
485 demo_m_z = getSeedH();
487 demorecmode = (demorecmode < 0) ? -1 : 1;
489 break;
491 setGameKeysGVars();
495 ////////////////////////////////////////////////////////////////////////////////
496 static void frmGameKey (SDL_KeyboardEvent *key) {
497 if (key->type == SDL_KEYDOWN) {
498 switch (key->keysym.sym) {
499 case SDLK_ESCAPE:
500 case SDLK_SPACE:
501 case SDLK_RETURN:
502 if (vmPaused && inMinimap) {
503 inMinimap = 0;
504 doSave = 0;
505 vmPaused = 0;
506 redrawLevel = 1;
507 gamekeys[6] = 0;
509 break;
510 case SDLK_F12:
511 vmGVars[GVAR_KEY_QUIT] = 1;
512 break;
513 case SDLK_p:
514 if (vmPaused == 0) {
515 gamekeys[6] = 1;
516 } else {
517 if (inMinimap) {
518 inMinimap = 0;
519 doSave = 0;
520 vmPaused = 0;
521 redrawLevel = 1;
522 gamekeys[6] = 1;
525 break;
526 case SDLK_r:
527 if (vmPaused == 0) {
528 if (demorecmode != 0) { demoClear(); demorecmode = 0; }
529 if ((key->keysym.mod&(KMOD_CTRL)) != 0) {
530 gamekeys[7] = 1;
533 break;
534 case SDLK_s:
535 if (vmPaused == 0 && !demorecmode) {
536 if (goobers && (key->keysym.mod&(KMOD_CTRL)) != 0) { doSave = 1; vmPaused = 1; }
538 break;
539 case SDLK_l:
540 if (vmPaused == 0 && !demorecmode) {
541 if (goobers && (key->keysym.mod&(KMOD_CTRL)) != 0) { doSave = -1; vmPaused = 1; }
543 break;
544 case SDLK_d:
545 if (vmPaused == 0) {
546 if (goobers && (key->keysym.mod&(KMOD_CTRL)) != 0) {
547 if (!demorecmode) {
548 demorecmode = 666; // 'start saving'
549 } else {
550 demoSave();
551 demoClear();
552 demorecmode = 0;
556 break;
557 case SDLK_m:
558 if (vmPaused == 0) {
559 if (goobers && (key->keysym.mod&(KMOD_CTRL)) != 0) {
560 if (!demorecmode) {
561 demorecmode = -666; // 'start playing'
562 } else {
563 demoClear();
564 demorecmode = 0;
565 demoSetKeyState(0);
569 break;
570 default: ;
574 if (demorecmode == 0 || demorecmode == 1) {
575 switch (key->keysym.sym) {
576 case SDLK_LEFT: case SDLK_KP4: gamekeys[0] = (key->type == SDL_KEYDOWN); break;
577 case SDLK_RIGHT: case SDLK_KP6: gamekeys[1] = (key->type == SDL_KEYDOWN); break;
578 case SDLK_UP: case SDLK_KP8: gamekeys[2] = (key->type == SDL_KEYDOWN); break;
579 case SDLK_DOWN: case SDLK_KP2: gamekeys[3] = (key->type == SDL_KEYDOWN); break;
580 case SDLK_SPACE: case SDLK_KP0: gamekeys[4] = (key->type == SDL_KEYDOWN); break;
581 case SDLK_RETURN: case SDLK_KP_PERIOD: gamekeys[5] = (key->type == SDL_KEYDOWN); break;
582 default: ;
585 if (goobers && !demorecmode) {
586 switch (key->keysym.sym) {
587 case SDLK_f: gamekeys[8] = (key->type == SDL_KEYDOWN); break;
588 case SDLK_w: gamekeys[9] = (key->type == SDL_KEYDOWN); break;
589 case SDLK_KP_MINUS: gamekeys[10] = (key->type == SDL_KEYDOWN); break;
590 case SDLK_KP_PLUS: gamekeys[11] = (key->type == SDL_KEYDOWN); break;
592 case SDLK_F1: fkeys[1] = (key->type == SDL_KEYDOWN); break;
593 case SDLK_F2: fkeys[2] = (key->type == SDL_KEYDOWN); break;
594 case SDLK_F3: fkeys[3] = (key->type == SDL_KEYDOWN); break;
595 case SDLK_F4: fkeys[4] = (key->type == SDL_KEYDOWN); break;
596 case SDLK_F5: fkeys[5] = (key->type == SDL_KEYDOWN); break;
597 case SDLK_F6: fkeys[6] = (key->type == SDL_KEYDOWN); break;
598 case SDLK_F7: fkeys[7] = (key->type == SDL_KEYDOWN); break;
599 case SDLK_F8: fkeys[8] = (key->type == SDL_KEYDOWN); break;
600 case SDLK_F9: fkeys[9] = (key->type == SDL_KEYDOWN); break;
601 default: ;
608 ////////////////////////////////////////////////////////////////////////////////
609 static inline Uint8 fgTile (int x, int y) {
610 return (x >= 0 && y >= 0 && x < mapWidth && y < mapHeight) ? fmap[y*mapWidth+x] : 0;
614 static inline Uint8 bgTile (int x, int y) {
615 if (y == -666) return (x >= 0 && x < mapWidth) ? xflags[x] : 0;
616 return (x >= 0 && y >= 0 && x < mapWidth && y < mapHeight) ? bmap[y*mapWidth+x] : 0;
620 static inline void setFGTile (int x, int y, Uint8 tile) {
621 if (x >= 0 && y >= 0 && x < mapWidth && y < mapHeight) {
622 if (fmap[y*mapWidth+x] != tile) {
623 redrawLevel = 1;
624 fmap[y*mapWidth+x] = tile;
630 static inline void setBGTile (int x, int y, Uint8 tile) {
631 if (y == -666 && x >= 0 && x < mapWidth) xflags[x] = tile;
632 if (x >= 0 && y >= 0 && x < mapWidth && y < mapHeight) {
633 if (bmap[y*mapWidth+x] != tile) {
634 redrawLevel = 1;
635 bmap[y*mapWidth+x] = tile;
641 static int mapGet (int tid, int fg, int x, int y) {
642 if (fmap != NULL) return fg ? fgTile(x, y) : bgTile(x, y);
643 return 0;
647 static void mapSet (int tid, int fg, int x, int y, int tile) {
648 if (fmap != NULL) {
649 if (fg) setFGTile(x, y, tile); else setBGTile(x, y, tile);
654 ////////////////////////////////////////////////////////////////////////////////
655 // cliprect should be set
656 static void levelDrawMap (SDL_Surface *frame) {
657 SDL_Rect rc;
659 rc.x = VIS_X*2;
660 rc.y = VIS_Y*2;
661 rc.w = VIS_WIDTH*20*2;
662 rc.h = VIS_HEIGHT*10*2;
664 if (!inMinimap) {
665 int levelback = vmGVars[GVAR_LEVEL_BACKPIC];
666 int scrX = vmGVars[GVAR_SCR_X], scrY = vmGVars[GVAR_SCR_Y];
668 if (levelback < 1 || levelback > 7) levelback = 1;
669 blitSurface(frame, 0, 0, backs[levelback]);
671 SDL_SetClipRect(frame, &rc);
672 for (int dy = 0; dy < VIS_HEIGHT; ++dy) {
673 for (int dx = 0; dx < VIS_WIDTH; ++dx) {
674 int x = scrX+dx, y = scrY+dy;
675 Uint8 b = bgTile(x, y), f = fgTile(x, y);
677 if (b) blitSurface2x(frame, VIS_X+dx*20, VIS_Y+dy*10, banks[CONST_BANK_BG_TILES].spr[b-1][0]);
678 if (f) blitSurface2x(frame, VIS_X+dx*20, VIS_Y+dy*10, banks[CONST_BANK_FG_TILES].spr[f-1][0]);
681 } else {
682 blitSurface(frame, 0, 0, backs[1]);
683 SDL_SetClipRect(frame, &rc);
684 for (int dy = 0; dy < mapHeight; ++dy) {
685 for (int dx = 1; dx < mapWidth; ++dx) {
686 Uint8 b = bgTile(dx, dy), f = fgTile(dx, dy), t = 8;
688 if (f == 0) {
689 if (b >= 1 && b <= 8) t = b-1; else t = 8;
690 } else {
691 t = f+8;
693 if (banks[CONST_BANK_MAP_TILES].spr[t][0]) {
694 blitSurface2x(frame, VIS_X+dx*6, VIS_Y+dy*4, banks[CONST_BANK_MAP_TILES].spr[t][0]);
695 } else {
696 blitSurface2x(frame, VIS_X+dx*6, VIS_Y+dy*4, banks[CONST_BANK_MAP_TILES].spr[0][0]);
701 SDL_SetClipRect(frame, NULL);
702 redrawLevel = 0;
706 static void levelDrawSprites (SDL_Surface *frame) {
707 if (!inMinimap) {
708 int scrX = vmGVars[GVAR_SCR_X], scrY = vmGVars[GVAR_SCR_Y];
709 int py = vmGetTVar(0, TVAR_POS_Y)-scrY;
711 for (int cc = 0; cc <= 2; ++cc) {
712 for (int f = cc==1?1:0; f <= vmLastThread(); ++f) {
713 if (vmIsThreadAlive(f) && !vmIsSuspendedThread(f)) {
714 int b = vmGetTVar(f, TVAR_SPR_BANK);
715 int s = vmGetTVar(f, TVAR_SPR_NUM);
716 int d = vmGetTVar(f, TVAR_SPR_DIR);
718 if (d >= 0 && d <= 1 && s >= 0 && b >= 0 && b <= 255 && s < banks[b].count && banks[b].spr[s][d]) {
719 SDL_Surface *sf = banks[b].spr[s][d];
720 int x = vmGetTVar(f, TVAR_POS_X)-scrX;
721 int y = vmGetTVar(f, TVAR_POS_Y)-scrY;
722 int tx = vmGetTVar(f, TVAR_POS_TX);
723 int ty = vmGetTVar(f, TVAR_POS_TY);
724 int si = vmGetTVar(f, TVAR_SPR_ITEM);
726 if (cc == 0 && b == CONST_BANK_ITEMS) continue;
727 if (cc == 1 && b != CONST_BANK_ITEMS) continue;
728 if (cc == 2) {
729 if (b != CONST_BANK_PROF) {
730 if (b != CONST_BANK_ITEMS) continue;
731 if (y < py) continue;
734 if (b == CONST_BANK_IM_PROF || b == CONST_BANK_PROF) tx -= 5;
735 blitSurface2x(frame, VIS_X+x*20+tx, VIS_Y+y*10+ty-sf->h/2, sf);
736 if (si > 0 && si < banks[CONST_BANK_ITEMS].count) {
737 sf = banks[CONST_BANK_ITEMS].spr[si][0];
738 blitSurface2x(frame, VIS_X+x*20+tx, VIS_Y+y*10+ty-sf->h/2-24, sf);
744 } else {
745 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]);
746 for (int f = 1; f < VM_MAX_THREADS; ++f) {
747 if (vmIsThreadAlive(f) && !vmIsSuspendedThread(f)) {
748 int i = vmGetTVar(f, TVAR_ITEM_ID);
749 if (i > 0 && i <= 255) {
750 SDL_Surface *sf = banks[CONST_BANK_MAP_ITEMS].spr[i][0];
751 int x = vmGetTVar(f, TVAR_POS_X);
752 int y = vmGetTVar(f, TVAR_POS_Y);
754 blitSurface2x(frame, VIS_X+(x+0)*6, VIS_Y+(y-2)*4, sf);
762 static void levelDrawCode (SDL_Surface *frame) {
763 int pos = vmGVars[GVAR_LEVEL_CODE_OFS], len = vmGVars[GVAR_LEVEL_CODE_LEN], x;
764 char buf[128];
766 if (len < 1) return;
767 if (len > 255) len = 255;
768 if (pos < 0 || pos+len > vmCodeSize) return;
769 x = 320-len*8;
770 sprintf(buf, "%d", curLevel);
771 x -= strlen(buf)*8;
772 for (; len > 0; x += 8, ++pos, --len) drawChar(frame, vmCode[pos], x, 0, 3);
776 static void levelDrawItemName (SDL_Surface *frame) {
777 int pos = vmGVars[GVAR_ITEM_NAME_OFS], len = vmGVars[GVAR_ITEM_NAME_LEN], x = 0;
779 if (len < 0 || len > 32) len = 32;
780 if (pos >= 0) {
781 for (; len > 0 && pos < vmCodeSize && vmCode[pos]; x += 8, ++pos, --len) drawChar(frame, vmCode[pos], x, 0, 1);
782 } else {
783 pos = 0-(pos+1);
784 for (; len > 0 && pos < levelDataSize && levelData[pos]; x += 8, ++pos, --len) drawChar(frame, levelData[pos], x, 0, 1);
789 static void levelDrawMisc (SDL_Surface *frame) {
790 if (demorecmode < 0) {
791 if (SDL_GetTicks()%1000 < 500) drawChar(frame, 'R', 0, 8, 3);
792 } else if (demorecmode > 0) {
793 if (SDL_GetTicks()%1000 < 500) drawChar(frame, 'D', 0, 8, 3);
798 static void drawSpriteBank (SDL_Surface *frame, SpriteBank *bank) {
799 SDL_Rect dst;
800 int x = 0, y = 0, maxh = 0;
802 SDL_SetClipRect(frame, NULL);
803 dst.x = 0;
804 dst.y = 0;
805 dst.w = frame->w;
806 dst.h = frame->h;
807 SDL_FillRect(frame, &dst, palette[3]);
809 for (int num = 0; num < bank->count; ++num) {
810 SDL_Surface *s = bank->spr[num][0];
812 if (s != NULL) {
813 char buf[16];
814 int w = s->w/2;
816 sprintf(buf, "%d", num);
817 if (w < strlen(buf)*8+2) w = strlen(buf)*8+2;
818 if (x+w > 320) {
819 x = 0;
820 y += maxh;
821 maxh = 0;
823 if (maxh < s->h/2+1) maxh = s->h/2+1;
824 blitSurface2x(frame, x, y, s);
825 drawString(frame, buf, x, y, 1);
826 x += w+1;
832 static void frmGameDraw (SDL_Surface *frame) {
833 SDL_Rect rc;
835 if (vmPaused && doSave) {
836 if (doSave < 0) {
837 if (loadGame() != 0) fatal("can't load game, VM is inconsistent, dying.");
838 redrawLevel = 1;
839 } else {
840 saveGame();
842 doSave = 0;
843 vmPaused = inMinimap;
846 SDL_SetClipRect(frame, NULL);
848 if (curLevel >= 0) {
849 char buf[8];
851 if (redrawLevel) levelDrawMap(backbuf);
852 blitSurface(frame, 0, 0, backbuf);
854 drawString(frame, levelname, lnamex, 0, 0x76);
855 levelDrawItemName(frame);
856 levelDrawCode(frame);
857 levelDrawMisc(frame);
859 sprintf(buf, "%d", curLevel+1);
860 drawString(frame, buf, 320-strlen(buf)*8, 0, 1);
862 rc.x = VIS_X*2;
863 rc.y = VIS_Y*2;
864 rc.w = VIS_WIDTH*20*2;
865 rc.h = VIS_HEIGHT*10*2;
866 SDL_SetClipRect(frame, &rc);
868 levelDrawSprites(frame);
870 SDL_SetClipRect(frame, NULL);
871 } else {
872 SDL_Rect dst;
874 dst.x = 0;
875 dst.y = 0;
876 dst.w = frame->w;
877 dst.h = frame->h;
878 SDL_FillRect(frame, &dst, palette[0]);
881 #if 0
882 polymodStart();
885 polymodAddPoint(10, 200-10);
886 polymodAddPoint(320-10, 200-10);
887 polymodAddPoint(10, 10);
888 polymodAddPoint(320-10, 10);
891 polymodAddPoint(10, 10);
892 polymodAddPoint(320-10, 10);
893 polymodAddPoint(10, 200-10);
895 polymodAddPoint(202, 100);
896 polymodAddPoint(200, 110);
897 polymodAddPoint(196, 120);
898 polymodAddPoint(189, 129);
899 polymodAddPoint(181, 136);
900 polymodAddPoint(170, 140);
901 polymodAddPoint(160, 142);
902 polymodAddPoint(150, 140);
903 polymodAddPoint(140, 136);
904 polymodAddPoint(131, 129);
905 polymodAddPoint(124, 120);
906 polymodAddPoint(120, 110);
907 polymodAddPoint(118, 100);
908 polymodAddPoint(120, 90);
909 polymodAddPoint(124, 79);
910 polymodAddPoint(131, 71);
911 polymodAddPoint(139, 64);
912 polymodAddPoint(150, 60);
913 polymodAddPoint(160, 58);
914 polymodAddPoint(170, 60);
915 polymodAddPoint(181, 64);
916 polymodAddPoint(189, 71);
917 polymodAddPoint(196, 79);
918 polymodAddPoint(200, 90);
920 polymodEnd();
921 polymodFill(frame, 3, 50);
922 #endif
924 if (goobers) {
925 for (int f = 84; f <= 90; ++f) if (fkeys[f-83]) drawSpriteBank(frame, &banks[f]);
930 ////////////////////////////////////////////////////////////////////////////////
931 #define ROOM_SCRIPTS_MARK " room script labels starts here "
934 static void loadMapScript (ResFile *resfile) {
935 VMLabelInfo *l;
936 int csz;
938 l = vmLabelAddMark(ROOM_SCRIPTS_MARK);
939 l->value = vmCodeSize;
940 if ((csz = loadCodeFile(resfile, vmCodeSize, 94+curLevel)) > 1) {
941 vmCodeSize += csz;
946 static void unloadMapScript (void) {
947 VMLabelInfo *l, *ucl;
949 while ((l = vmFindMark(ROOM_SCRIPTS_MARK)) != NULL) {
950 ucl = vmFindLabel("entry_local_room_script_onunload");
951 if (ucl != NULL && ucl->type == LB_CODE) vmExecuteBSR(0, ucl->value, 0);
952 vmCodeSize = l->value;
953 vmFreeLabelsUntilMark(ROOM_SCRIPTS_MARK);
956 ucl = vmFindLabel("entry_room_onunload");
957 if (ucl != NULL && ucl->type == LB_CODE) vmExecuteBSR(0, ucl->value, 0);
961 ////////////////////////////////////////////////////////////////////////////////
962 static int loadLevelInternal (ResFile *resfile, int lidx) {
963 message[0] = 0;
964 unloadMapScript();
966 vmGVars[GVAR_VIS_WIDTH] = VIS_WIDTH;
967 vmGVars[GVAR_VIS_HEIGHT] = VIS_HEIGHT;
968 vmGVars[GVAR_ITEM_NAME_OFS] = 0;
969 vmGVars[GVAR_ITEM_NAME_LEN] = 0;
970 levelname[0] = 0;
972 if (levelData != NULL) free(levelData); levelData = NULL; levelDataSize = 0;
973 if (bmap != NULL) free(bmap); bmap = NULL;
974 if (fmap != NULL) free(fmap); fmap = NULL;
975 if (xflags != NULL) free(xflags); xflags = NULL;
977 curLevel = -1;
978 if (lidx < 0 || lidx > 73) return -1;
979 if ((levelData = loadResFile(resfile, lidx+9, &levelDataSize)) == NULL) return -1;
980 curLevel = lidx;
981 loadMapScript(resfile);
982 return levelDataSize;
986 ////////////////////////////////////////////////////////////////////////////////
987 static void activateMinimap (void) {
988 inMinimap = 1;
989 vmPaused = 1;
990 doSave = 0;
991 redrawLevel = 1;
995 ////////////////////////////////////////////////////////////////////////////////
996 static int gameRST (int tid, int opcode, int argc, int argv[], int *argp[]) {
997 if (argv[0] == CONST_FRST_MINIMAP) {
998 activateMinimap();
999 } else if (argv[0] == CONST_FRST_OPEN_LEVEL_FILE) {
1000 // levelnum
1001 //if (vmLoadArgs(tid, 2, argv, argp, argc, argv, argp) != 0) fatal("out of args");
1002 if (argc < 2) fatal("out of args in FRST_LOAD_LEVEL");
1003 vmGVars[GVAR_RST_RESULT] = loadLevelInternal(&resfile, argv[1]);
1004 //if (goobers) fprintf(stderr, "FRST_OPEN_LEVEL_FILE(%d): %d\n", argv[1], vmGVars[GVAR_RST_RESULT]);
1005 } else if (argv[0] == CONST_FRST_GET_LEVEL_DATA_SIZE) {
1006 vmGVars[GVAR_RST_RESULT] = levelDataSize;
1007 } else if (argv[0] == CONST_FRST_GET_LEVEL_LOADER) {
1008 VMLabelInfo *ucl = vmFindLabel("entry_local_room_loader");
1010 vmGVars[GVAR_RST_RESULT] = (ucl != NULL && ucl->type == LB_CODE) ? ucl->value : -1;
1011 } else if (argv[0] == CONST_FRST_GET_LEVEL_INITER) {
1012 VMLabelInfo *ucl = vmFindLabel("entry_local_room_onload");
1014 vmGVars[GVAR_RST_RESULT] = (ucl != NULL && ucl->type == LB_CODE) ? ucl->value : -1;
1015 } else if (argv[0] == CONST_FRST_GET_LEVEL_FILE_BYTE) {
1016 if (argc < 2) fatal("out of args in FRST_GET_LEVEL_FILE_BYTE");
1017 if (argv[1] < 0 || argv[1] >= levelDataSize) fatal("invalid offset in FRST_GET_LEVEL_FILE_BYTE: %d", argv[1]);
1018 vmGVars[GVAR_RST_RESULT] = levelData[argv[1]];
1019 } else if (argv[0] == CONST_FRST_GET_LEVEL_FILE_WORD) {
1020 if (argc < 2) fatal("out of args in FRST_GET_LEVEL_FILE_WORD");
1021 if (argv[1] < 0 || argv[1]+1 >= levelDataSize) fatal("invalid offset in FRST_GET_LEVEL_FILE_WORD: %d", argv[1]);
1022 vmGVars[GVAR_RST_RESULT] = levelData[argv[1]+1];
1023 vmGVars[GVAR_RST_RESULT] <<= 8;
1024 vmGVars[GVAR_RST_RESULT] |= levelData[argv[1]];
1025 if (vmGVars[GVAR_RST_RESULT] >= 32768) vmGVars[GVAR_RST_RESULT] -= 65536;
1026 } else if (argv[0] == CONST_FRST_SET_LEVEL_SIZE) {
1027 int w, h;
1029 if (argc != 3) fatal("out of args in FRST_SET_LEVEL_SIZE");
1030 w = argv[1];
1031 h = argv[2];
1032 if (w < 1 || w > 32700 || h < 1 || h > 32700) fatal("invalid dimensions in FRST_SET_LEVEL_SIZE: %dx%d", w, h);
1033 if (bmap != NULL) free(bmap); bmap = NULL;
1034 if (fmap != NULL) free(fmap); fmap = NULL;
1035 if (xflags != NULL) free(xflags); xflags = NULL;
1036 fmap = calloc(w*h, 1); if (fmap == NULL) fatal("out of memory in FRST_SET_LEVEL_SIZE");
1037 bmap = calloc(w*h, 1); if (bmap == NULL) fatal("out of memory in FRST_SET_LEVEL_SIZE");
1038 xflags = calloc(w, 1); if (xflags == NULL) fatal("out of memory in FRST_SET_LEVEL_SIZE");
1039 mapWidth = vmGVars[GVAR_MAP_WIDTH] = w;
1040 mapHeight = vmGVars[GVAR_MAP_HEIGHT] = h;
1041 oldScrX = 0;
1042 oldScrY = 0;
1043 redrawLevel = 1;
1044 } else if (argv[0] == CONST_FRST_SET_LEVEL_NAME) {
1045 int pos = 0, len = sizeof(levelname)-1, lp = 0;
1046 char buf[32];
1048 switch (argc) {
1049 case 1: fatal("out of args in FRST_SET_LEVEL_NAME");
1050 case 2: pos = argv[1]; break;
1051 case 3: pos = argv[1]; len = argv[2]; break;
1053 memset(levelname, 0, sizeof(levelname));
1054 if (len > sizeof(levelname)-1) len = sizeof(levelname)-1;
1055 if (pos > 0) {
1056 while (len > 0 && pos < vmCodeSize && vmCode[pos]) {
1057 levelname[lp++] = vmCode[pos++];
1058 --len;
1060 } else {
1061 pos = 0-(pos+1);
1062 while (len > 0 && pos < levelDataSize && levelData[pos]) {
1063 levelname[lp++] = levelData[pos++];
1064 --len;
1067 lnamex = (320-strlen(levelname)*8)/2;
1068 sprintf(buf, "%d", curLevel+1);
1069 while (lnamex+strlen(levelname)*8+strlen(buf)*8+6*8 > 320) lnamex -= 8;
1070 } else {
1071 if (gameRSTCB) return gameRSTCB(tid, opcode, argc, argv, argp);
1072 fatal("invalid RST: %d", argv[0]);
1074 return 0; // continue
1078 ////////////////////////////////////////////////////////////////////////////////
1079 void setMainLoopGame (void) {
1080 frameCB = frmGameDraw;
1081 keyCB = frmGameKey;
1082 gameRSTCB = gameRST;
1083 beforeVMCB = demoCB;
1084 vmMapGetCB = mapGet;
1085 vmMapSetCB = mapSet;
1087 setSeed(time(NULL));
1088 vmPaused = 0;
1089 inMinimap = 0;
1090 doSave = 0;
1091 redrawLevel = 1;
1092 //curLevel = -1;
1093 //demorecmode = 0;
1094 message[0] = 0;
1095 memset(fkeys, 0, sizeof(fkeys));
1096 memset(gamekeys, 0, sizeof(gamekeys));