polymod is working
[awish.git] / src / game.c
blob81d7ffb8918151829cce4f6e98770002c5c7a6af
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 char levelcode[257];
55 static char itemname[257];
56 static int lnamex;
57 static Uint8 *levelData = NULL;
58 static int levelDataSize = 0;
59 static Uint8 *bmap = NULL, *fmap = NULL;
60 static Uint8 *xflags = NULL;
61 static int inMinimap;
63 static int doSave = 0;
65 static int demorecmode = 0; // 0: none; 1: recording; -1: playing
66 static int demoSize = 0;
67 static int demoPos = 0;
68 static Uint8 *demoData = NULL;
69 static Uint8 demoPrevKeyState = 0;
70 static int demoKeyStateRepeats = 0;
72 static char message[256];
73 //static Uint32 msgEndTime = 0;
75 static Uint32 demo_m_w = 0;
76 static Uint32 demo_m_z = 0;
79 ////////////////////////////////////////////////////////////////////////////////
80 static int gamekeys[12];
81 static int fkeys[10];
84 static void setGameKeysGVars (void) {
85 vmGVars[GVAR_KEY_LEFT] = gamekeys[0] ? 1 : 0;
86 vmGVars[GVAR_KEY_RIGHT] = gamekeys[1] ? 1 : 0;
87 vmGVars[GVAR_KEY_UP] = gamekeys[2] ? 1 : 0;
88 vmGVars[GVAR_KEY_DOWN] = gamekeys[3] ? 1 : 0;
89 vmGVars[GVAR_KEY_TAKE] = gamekeys[4] ? 1 : 0;
90 vmGVars[GVAR_KEY_USE] = gamekeys[5] ? 1 : 0;
92 vmGVars[GVAR_KEY_MINIMAP] = gamekeys[6] ? 1 : 0;
93 vmGVars[GVAR_KEY_RESTART] = gamekeys[7] ? 1 : 0;
95 vmGVars[GVAR_KEY_FALL_CHEAT] = gamekeys[8] ? 1 : 0;
96 vmGVars[GVAR_KEY_WALK_CHEAT] = gamekeys[9] ? 1 : 0;
97 vmGVars[GVAR_KEY_PLEV_CHEAT] = gamekeys[10] ? 1 : 0;
98 vmGVars[GVAR_KEY_NLEV_CHEAT] = gamekeys[11] ? 1 : 0;
102 ////////////////////////////////////////////////////////////////////////////////
103 #define SECRET (42)
104 //#define SECRET (0)
107 static int readBuf (FILE *fl, void *buf, int len) {
108 unsigned char *c = (unsigned char *)buf;
110 while (len-- > 0) {
111 unsigned char b;
113 if (fread(&b, 1, 1, fl) != 1) return -1;
114 b ^= SECRET;
115 *c++ = b;
117 return 0;
121 static int writeBuf (FILE *fl, const void *buf, int len) {
122 const unsigned char *c = (const unsigned char *)buf;
124 while (len-- > 0) {
125 unsigned char b = *c++;
127 b ^= SECRET;
128 if (fwrite(&b, 1, 1, fl) != 1) return -1;
130 return 0;
134 static int readDW (FILE *fl, int *v) {
135 int t[4];
137 for (int f = 0; f < 4; ++f) {
138 unsigned char b;
140 if (fread(&b, 1, 1, fl) != 1) return -1;
141 t[f] = b^SECRET;
144 if (v) {
145 *v = 0;
146 for (int f = 0; f < 4; ++f) *v |= t[f]<<(8*f);
148 return 0;
152 static int writeDW (FILE *fl, int v) {
153 for (int f = 0; f < 4; ++f) {
154 unsigned char b;
156 b = v&0xff;
157 b ^= SECRET;
158 if (fwrite(&b, 1, 1, fl) != 1) return -1;
159 v >>= 8;
161 return 0;
165 static int readUDW (FILE *fl, Uint32 *v) {
166 int t[4];
168 for (int f = 0; f < 4; ++f) {
169 unsigned char b;
171 if (fread(&b, 1, 1, fl) != 1) return -1;
172 t[f] = b^SECRET;
175 if (v) {
176 *v = 0;
177 for (int f = 0; f < 4; ++f) *v |= t[f]<<(8*f);
179 return 0;
183 static int writeUDW (FILE *fl, Uint32 v) {
184 for (int f = 0; f < 4; ++f) {
185 unsigned char b;
187 b = v&0xff;
188 b ^= SECRET;
189 if (fwrite(&b, 1, 1, fl) != 1) return -1;
190 v >>= 8;
192 return 0;
196 ////////////////////////////////////////////////////////////////////////////////
197 static const char *SAVE_GAME_SIGNATURE = "ASG1";
200 static int saveGame (void) {
201 FILE *fl = fopen("awish.sav", "wb");
203 if (fl != NULL) {
204 demo_m_w = getSeedL();
205 demo_m_z = getSeedH();
206 if (fwrite(SAVE_GAME_SIGNATURE, 4, 1, fl) != 1) goto error;
207 if (vmSaveState(fl) != 0) goto error;
208 if (writeDW(fl, mapWidth) != 0) goto error;
209 if (writeDW(fl, mapHeight) != 0) goto error;
210 if (writeUDW(fl, demo_m_w) != 0) goto error;
211 if (writeUDW(fl, demo_m_z) != 0) goto error;
212 if (writeDW(fl, inMinimap) != 0) goto error;
213 if (writeDW(fl, curLevel) != 0) goto error;
214 if (writeBuf(fl, levelname, sizeof(levelname)) != 0) goto error;
215 if (writeDW(fl, lnamex) != 0) goto error;
216 if (writeBuf(fl, levelcode, sizeof(levelcode)) != 0) goto error;
217 if (writeBuf(fl, itemname, sizeof(itemname)) != 0) goto error;
218 if (writeBuf(fl, bmap, mapWidth*mapHeight) != 0) goto error;
219 if (writeBuf(fl, fmap, mapWidth*mapHeight) != 0) goto error;
220 if (writeBuf(fl, xflags, mapWidth) != 0) goto error;
221 fclose(fl);
222 return 0;
224 error:
225 fclose(fl);
226 unlink("awish.sav");
227 if (goobers) fprintf(stderr, "error saving game\n");
228 return -1;
232 static int loadGame (void) {
233 FILE *fl = fopen("awish.sav", "rb");
235 if (fl != NULL) {
236 char sign[4];
238 if (fread(sign, 4, 1, fl) != 1) goto error;
239 if (memcmp(sign, SAVE_GAME_SIGNATURE, 4) != 0) goto error;
240 if (vmLoadState(fl) != 0) goto error;
241 if (readDW(fl, &mapWidth) != 0) goto error;
242 if (readDW(fl, &mapHeight) != 0) goto error;
243 if (readUDW(fl, &demo_m_w) != 0) goto error;
244 if (readUDW(fl, &demo_m_z) != 0) goto error;
245 if (readDW(fl, &inMinimap) != 0) goto error;
246 if (readDW(fl, &curLevel) != 0) goto error;
247 if (readBuf(fl, levelname, sizeof(levelname)) != 0) goto error;
248 if (readDW(fl, &lnamex) != 0) goto error;
249 if (readBuf(fl, levelcode, sizeof(levelcode)) != 0) goto error;
250 if (readBuf(fl, itemname, sizeof(itemname)) != 0) goto error;
251 if (bmap != NULL) free(bmap); bmap = NULL;
252 if (fmap != NULL) free(fmap); fmap = NULL;
253 if (xflags != NULL) free(xflags); xflags = NULL;
254 fmap = calloc(mapWidth*mapHeight, 1); if (fmap == NULL) fatal("out of memory in loadGame");
255 bmap = calloc(mapWidth*mapHeight, 1); if (bmap == NULL) fatal("out of memory in loadGame");
256 xflags = calloc(mapWidth, 1); if (xflags == NULL) fatal("out of memory in loadGame");
257 if (readBuf(fl, bmap, mapWidth*mapHeight) != 0) goto error;
258 if (readBuf(fl, fmap, mapWidth*mapHeight) != 0) goto error;
259 if (readBuf(fl, xflags, mapWidth) != 0) goto error;
260 fclose(fl);
261 setSeedL(demo_m_w);
262 setSeedL(demo_m_z);
263 redrawLevel = 1;
264 oldScrX = oldScrY = -1;
265 return 0;
267 error:
268 fclose(fl);
269 if (goobers) fprintf(stderr, "error loading saved game\n");
270 return -1;
274 ////////////////////////////////////////////////////////////////////////////////
275 static void demoClear (void) {
276 //demorecmode = 0;
277 demoSize = 0;
278 demoPos = 0;
279 demoPrevKeyState = 0;
280 demoKeyStateRepeats = 0;
281 if (demoData != NULL) free(demoData);
282 demoData = NULL;
286 static void demoAddByte (Uint8 b) {
287 if (demoPos+1 > demoSize && demoSize > 1024*1024*16) fatal("out of memory in demo recording!");
288 if (demoPos+1 > demoSize) {
289 int newsz = demoSize+1024*32;
290 Uint8 *nn = realloc(demoData, newsz);
292 if (!nn) fatal("out of memory in demo recording!");
293 demoData = nn;
294 demoSize = newsz;
296 demoData[demoPos++] = b;
300 static Uint8 demoGetKeyState (void) {
301 return
302 ((gamekeys[0]?1:0)<<0) |
303 ((gamekeys[1]?1:0)<<1) |
304 ((gamekeys[2]?1:0)<<2) |
305 ((gamekeys[3]?1:0)<<3) |
306 ((gamekeys[4]?1:0)<<4) |
307 ((gamekeys[5]?1:0)<<5);
311 static void demoSetKeyState (Uint8 kstate) {
312 gamekeys[0] = (kstate&(1<<0)) != 0;
313 gamekeys[1] = (kstate&(1<<1)) != 0;
314 gamekeys[2] = (kstate&(1<<2)) != 0;
315 gamekeys[3] = (kstate&(1<<3)) != 0;
316 gamekeys[4] = (kstate&(1<<4)) != 0;
317 gamekeys[5] = (kstate&(1<<5)) != 0;
321 static void demoAddFrameData (void) {
322 Uint8 kstate = demoGetKeyState();
324 if (demoKeyStateRepeats == 256) {
325 demoAddByte(demoKeyStateRepeats-1);
326 demoAddByte(demoPrevKeyState);
327 demoKeyStateRepeats = 0;
330 if (kstate != demoPrevKeyState) {
331 if (demoKeyStateRepeats > 0) {
332 demoAddByte(demoKeyStateRepeats-1);
333 demoAddByte(demoPrevKeyState);
335 demoPrevKeyState = kstate;
336 demoKeyStateRepeats = 1;
337 } else {
338 ++demoKeyStateRepeats;
343 static void demoGetFrameData (void) {
344 while (demoKeyStateRepeats < 1) {
345 if (demoPos+2 > demoSize) {
346 demoClear();
347 demorecmode = 0;
348 if (goobers) fprintf(stderr, "demo complete\n");
349 strcpy(message, "demo stopped");
350 return;
352 demoKeyStateRepeats = demoData[demoPos++];
353 ++demoKeyStateRepeats;
354 demoPrevKeyState = demoData[demoPos++];
356 --demoKeyStateRepeats;
358 demoSetKeyState(demoPrevKeyState);
362 static int demoSave (void) {
363 FILE *fl;
364 char buf[128];
365 char sign[5];
367 if (demoKeyStateRepeats > 0) {
368 demoAddByte(demoKeyStateRepeats-1);
369 demoAddByte(demoPrevKeyState);
372 sprintf(buf, "awish%02d.dmo", curLevel+1);
373 fl = fopen(buf, "wb");
374 if (fl == NULL) {
375 if (goobers) fprintf(stderr, "can't create demo file '%s'\n", buf);
376 return -1;
378 strcpy(sign, "AWD1");
379 if (fwrite(sign, 4, 1, fl) != 1) goto error;
380 if (writeDW(fl, curLevel) != 0) goto error;
381 if (writeUDW(fl, demo_m_w) != 0) goto error;
382 if (writeUDW(fl, demo_m_z) != 0) goto error;
383 if (writeDW(fl, demoPos) != 0) goto error;
384 if (writeBuf(fl, demoData, demoPos) != 0) goto error;
385 fclose(fl);
386 if (goobers) fprintf(stderr, "demo saved to file '%s'\n", buf);
387 return 0;
388 error:
389 fclose(fl);
390 unlink(buf);
391 if (goobers) fprintf(stderr, "can't write demo file '%s'\n", buf);
392 return -1;
396 static int demoLoad (void) {
397 FILE *fl;
398 char buf[128];
399 int size, level;
400 char sign[4];
402 demoClear();
403 sprintf(buf, "awish%02d.dmo", curLevel+1);
404 fl = fopen(buf, "rb");
405 if (fl == NULL) {
406 if (goobers) fprintf(stderr, "can't open demo file '%s'\n", buf);
407 return -1;
410 if (fread(sign, 4, 1, fl) != 1) goto error;
411 if (memcmp(sign, "AWD1", 4) != 0) goto error;
412 if (readDW(fl, &level) != 0) goto error;
413 if (readUDW(fl, &demo_m_w) != 0) goto error;
414 if (readUDW(fl, &demo_m_z) != 0) goto error;
415 if (readDW(fl, &size) != 0) goto error;
416 if (size < 1 || size > 1024*1024*16) goto error;
417 demoData = malloc(size);
418 if (demoData == NULL) goto error;
419 if (readBuf(fl, demoData, size) != 0) goto error;
420 demoSize = size;
421 demoPos = 0;
422 fclose(fl);
423 setSeedL(demo_m_w);
424 setSeedL(demo_m_z);
425 if (goobers) fprintf(stderr, "loaded demo file '%s'\n", buf);
427 return 0;
428 error:
429 fclose(fl);
430 if (goobers) fprintf(stderr, "can't load demo file '%s'\n", buf);
431 demoClear();
432 return -1;
436 static inline int isGameStopped (void) {
437 return
438 vmGVars[GVAR_GAME_STATE] == CONST_GAME_STATE_DEAD ||
439 vmGVars[GVAR_GAME_STATE] == CONST_GAME_STATE_COMPLETE;
443 static void demoCB (void) {
444 //if (vmGVars[GVAR_GAME_STATE] == CONST_GAME_STATE_STARTED) unloadMapScript();
446 if (vmGVars[GVAR_SCR_X] != oldScrX || vmGVars[GVAR_SCR_Y] != oldScrY) {
447 oldScrX = vmGVars[GVAR_SCR_X];
448 oldScrY = vmGVars[GVAR_SCR_Y];
449 redrawLevel = 1;
452 switch (demorecmode) {
453 case -666: // prepare to load demo
454 if (demoLoad() != 0) {
455 demorecmode = 0;
456 break;
458 demorecmode = -2;
459 // fallthru
460 case 666: // prepare to save demo
461 vmSetPC(0, CODE_ENTRY_GAME_RESTART_LEVEL);
462 vmGVars[GVAR_CUR_LEVEL] = curLevel;
463 vmGVars[GVAR_GAME_STATE] = -1; // invalid state
464 if (demorecmode > 0) {
465 demoClear();
466 demorecmode = 2;
468 break;
469 case -1: // demo replaying
470 case 1: // demo saving
471 if (isGameStopped()) {
472 // the level is over or prof is dead
473 if (demorecmode > 0) demoSave();
474 demoClear();
475 demorecmode = 0;
476 } else {
477 ((demorecmode < 0) ? demoGetFrameData : demoAddFrameData)();
479 break;
480 case -2: // waiting for 'game started' trigger
481 case 2: // waiting for 'game started' trigger
482 if (vmGVars[GVAR_GAME_STATE] >= 0) {
483 if (goobers) fprintf(stderr, "demo %s started...\n", demorecmode<0?"replaying":"recording");
484 if (demorecmode < 0) {
485 // replay
486 setSeedL(demo_m_w);
487 setSeedL(demo_m_z);
488 } else {
489 // record
490 demo_m_w = getSeedL();
491 demo_m_z = getSeedH();
493 demorecmode = (demorecmode < 0) ? -1 : 1;
495 break;
497 setGameKeysGVars();
501 ////////////////////////////////////////////////////////////////////////////////
502 static void frmGameKey (SDL_KeyboardEvent *key) {
503 if (key->type == SDL_KEYDOWN) {
504 switch (key->keysym.sym) {
505 case SDLK_ESCAPE:
506 case SDLK_SPACE:
507 case SDLK_RETURN:
508 if (vmPaused && inMinimap) {
509 inMinimap = 0;
510 doSave = 0;
511 vmPaused = 0;
512 redrawLevel = 1;
513 gamekeys[6] = 0;
515 break;
516 case SDLK_F12:
517 vmGVars[GVAR_KEY_QUIT] = 1;
518 break;
519 case SDLK_p:
520 if (vmPaused == 0) {
521 gamekeys[6] = 1;
522 } else {
523 if (inMinimap) {
524 inMinimap = 0;
525 doSave = 0;
526 vmPaused = 0;
527 redrawLevel = 1;
528 gamekeys[6] = 1;
531 break;
532 case SDLK_r:
533 if (vmPaused == 0) {
534 if (demorecmode != 0) { demoClear(); demorecmode = 0; }
535 if ((key->keysym.mod&(KMOD_CTRL)) != 0) {
536 gamekeys[7] = 1;
539 break;
540 case SDLK_s:
541 if (vmPaused == 0 && !demorecmode) {
542 if (goobers && (key->keysym.mod&(KMOD_CTRL)) != 0) { doSave = 1; vmPaused = 1; }
544 break;
545 case SDLK_l:
546 if (vmPaused == 0 && !demorecmode) {
547 if (goobers && (key->keysym.mod&(KMOD_CTRL)) != 0) { doSave = -1; vmPaused = 1; }
549 break;
550 case SDLK_d:
551 if (vmPaused == 0) {
552 if (goobers && (key->keysym.mod&(KMOD_CTRL)) != 0) {
553 if (!demorecmode) {
554 demorecmode = 666; // 'start saving'
555 } else {
556 demoSave();
557 demoClear();
558 demorecmode = 0;
562 break;
563 case SDLK_m:
564 if (vmPaused == 0) {
565 if (goobers && (key->keysym.mod&(KMOD_CTRL)) != 0) {
566 if (!demorecmode) {
567 demorecmode = -666; // 'start playing'
568 } else {
569 demoClear();
570 demorecmode = 0;
571 demoSetKeyState(0);
575 break;
576 default: ;
580 if (demorecmode == 0 || demorecmode == 1) {
581 switch (key->keysym.sym) {
582 case SDLK_LEFT: case SDLK_KP4: gamekeys[0] = (key->type == SDL_KEYDOWN); break;
583 case SDLK_RIGHT: case SDLK_KP6: gamekeys[1] = (key->type == SDL_KEYDOWN); break;
584 case SDLK_UP: case SDLK_KP8: gamekeys[2] = (key->type == SDL_KEYDOWN); break;
585 case SDLK_DOWN: case SDLK_KP2: gamekeys[3] = (key->type == SDL_KEYDOWN); break;
586 case SDLK_SPACE: case SDLK_KP0: gamekeys[4] = (key->type == SDL_KEYDOWN); break;
587 case SDLK_RETURN: case SDLK_KP_PERIOD: gamekeys[5] = (key->type == SDL_KEYDOWN); break;
588 default: ;
591 if (goobers && !demorecmode) {
592 switch (key->keysym.sym) {
593 case SDLK_f: gamekeys[8] = (key->type == SDL_KEYDOWN); break;
594 case SDLK_w: gamekeys[9] = (key->type == SDL_KEYDOWN); break;
595 case SDLK_KP_MINUS: gamekeys[10] = (key->type == SDL_KEYDOWN); break;
596 case SDLK_KP_PLUS: gamekeys[11] = (key->type == SDL_KEYDOWN); break;
598 case SDLK_F1: fkeys[1] = (key->type == SDL_KEYDOWN); break;
599 case SDLK_F2: fkeys[2] = (key->type == SDL_KEYDOWN); break;
600 case SDLK_F3: fkeys[3] = (key->type == SDL_KEYDOWN); break;
601 case SDLK_F4: fkeys[4] = (key->type == SDL_KEYDOWN); break;
602 case SDLK_F5: fkeys[5] = (key->type == SDL_KEYDOWN); break;
603 case SDLK_F6: fkeys[6] = (key->type == SDL_KEYDOWN); break;
604 case SDLK_F7: fkeys[7] = (key->type == SDL_KEYDOWN); break;
605 case SDLK_F8: fkeys[8] = (key->type == SDL_KEYDOWN); break;
606 case SDLK_F9: fkeys[9] = (key->type == SDL_KEYDOWN); break;
607 default: ;
614 ////////////////////////////////////////////////////////////////////////////////
615 static inline Uint8 fgTile (int x, int y) {
616 return (x >= 0 && y >= 0 && x < mapWidth && y < mapHeight) ? fmap[y*mapWidth+x] : 0;
620 static inline Uint8 bgTile (int x, int y) {
621 if (y == -666) return (x >= 0 && x < mapWidth) ? xflags[x] : 0;
622 return (x >= 0 && y >= 0 && x < mapWidth && y < mapHeight) ? bmap[y*mapWidth+x] : 0;
626 static inline void setFGTile (int x, int y, Uint8 tile) {
627 if (x >= 0 && y >= 0 && x < mapWidth && y < mapHeight) {
628 if (fmap[y*mapWidth+x] != tile) {
629 redrawLevel = 1;
630 fmap[y*mapWidth+x] = tile;
636 static inline void setBGTile (int x, int y, Uint8 tile) {
637 if (y == -666 && x >= 0 && x < mapWidth) xflags[x] = tile;
638 if (x >= 0 && y >= 0 && x < mapWidth && y < mapHeight) {
639 if (bmap[y*mapWidth+x] != tile) {
640 redrawLevel = 1;
641 bmap[y*mapWidth+x] = tile;
647 static int mapGet (int tid, int fg, int x, int y) {
648 if (fmap != NULL) return fg ? fgTile(x, y) : bgTile(x, y);
649 return 0;
653 static void mapSet (int tid, int fg, int x, int y, int tile) {
654 if (fmap != NULL) {
655 if (fg) setFGTile(x, y, tile); else setBGTile(x, y, tile);
660 ////////////////////////////////////////////////////////////////////////////////
661 // cliprect should be set
662 static void levelDrawMap (SDL_Surface *frame) {
663 SDL_Rect rc;
665 rc.x = VIS_X*2;
666 rc.y = VIS_Y*2;
667 rc.w = VIS_WIDTH*20*2;
668 rc.h = VIS_HEIGHT*10*2;
670 if (!inMinimap) {
671 int levelback = vmGVars[GVAR_LEVEL_BACKPIC];
672 int scrX = vmGVars[GVAR_SCR_X], scrY = vmGVars[GVAR_SCR_Y];
674 if (levelback < 1 || levelback > 7) levelback = 1;
675 blitSurface(frame, 0, 0, backs[levelback]);
677 SDL_SetClipRect(frame, &rc);
678 for (int dy = 0; dy < VIS_HEIGHT; ++dy) {
679 for (int dx = 0; dx < VIS_WIDTH; ++dx) {
680 int x = scrX+dx, y = scrY+dy;
681 Uint8 b = bgTile(x, y), f = fgTile(x, y);
683 if (b) blitSurface2x(frame, VIS_X+dx*20, VIS_Y+dy*10, banks[CONST_BANK_BG_TILES].spr[b-1][0]);
684 if (f) blitSurface2x(frame, VIS_X+dx*20, VIS_Y+dy*10, banks[CONST_BANK_FG_TILES].spr[f-1][0]);
687 } else {
688 blitSurface(frame, 0, 0, backs[1]);
689 SDL_SetClipRect(frame, &rc);
690 for (int dy = 0; dy < mapHeight; ++dy) {
691 for (int dx = 1; dx < mapWidth; ++dx) {
692 Uint8 b = bgTile(dx, dy), f = fgTile(dx, dy), t = 8;
694 if (f == 0) {
695 if (b >= 1 && b <= 8) t = b-1; else t = 8;
696 } else {
697 t = f+8;
699 if (banks[CONST_BANK_MAP_TILES].spr[t][0]) {
700 blitSurface2x(frame, VIS_X+dx*6, VIS_Y+dy*4, banks[CONST_BANK_MAP_TILES].spr[t][0]);
701 } else {
702 blitSurface2x(frame, VIS_X+dx*6, VIS_Y+dy*4, banks[CONST_BANK_MAP_TILES].spr[0][0]);
707 SDL_SetClipRect(frame, NULL);
708 redrawLevel = 0;
712 static void levelDrawSprites (SDL_Surface *frame) {
713 if (!inMinimap) {
714 int scrX = vmGVars[GVAR_SCR_X], scrY = vmGVars[GVAR_SCR_Y];
715 int py = vmGetTVar(0, TVAR_POS_Y)-scrY;
717 for (int cc = 0; cc <= 2; ++cc) {
718 for (int f = cc==1?1:0; f <= vmLastThread(); ++f) {
719 if (vmIsThreadAlive(f) && !vmIsSuspendedThread(f)) {
720 int b = vmGetTVar(f, TVAR_SPR_BANK);
721 int s = vmGetTVar(f, TVAR_SPR_NUM);
722 int d = vmGetTVar(f, TVAR_SPR_DIR);
724 if (d >= 0 && d <= 1 && s >= 0 && b >= 0 && b <= 255 && s < banks[b].count && banks[b].spr[s][d]) {
725 SDL_Surface *sf = banks[b].spr[s][d];
726 int x = vmGetTVar(f, TVAR_POS_X)-scrX;
727 int y = vmGetTVar(f, TVAR_POS_Y)-scrY;
728 int tx = vmGetTVar(f, TVAR_POS_TX);
729 int ty = vmGetTVar(f, TVAR_POS_TY);
730 int si = vmGetTVar(f, TVAR_SPR_ITEM);
732 if (cc == 0 && b == CONST_BANK_ITEMS) continue;
733 if (cc == 1 && b != CONST_BANK_ITEMS) continue;
734 if (cc == 2) {
735 if (b != CONST_BANK_PROF) {
736 if (b != CONST_BANK_ITEMS) continue;
737 if (y < py) continue;
740 if (b == CONST_BANK_IM_PROF || b == CONST_BANK_PROF) tx -= 5;
741 blitSurface2x(frame, VIS_X+x*20+tx, VIS_Y+y*10+ty-sf->h/2, sf);
742 if (si > 0 && si < banks[CONST_BANK_ITEMS].count) {
743 sf = banks[CONST_BANK_ITEMS].spr[si][0];
744 blitSurface2x(frame, VIS_X+x*20+tx, VIS_Y+y*10+ty-sf->h/2-24, sf);
750 } else {
751 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]);
752 for (int f = 1; f < VM_MAX_THREADS; ++f) {
753 if (vmIsThreadAlive(f) && !vmIsSuspendedThread(f)) {
754 int i = vmGetTVar(f, TVAR_ITEM_ID);
755 if (i > 0 && i <= 255) {
756 SDL_Surface *sf = banks[CONST_BANK_MAP_ITEMS].spr[i][0];
757 int x = vmGetTVar(f, TVAR_POS_X);
758 int y = vmGetTVar(f, TVAR_POS_Y);
760 blitSurface2x(frame, VIS_X+(x+0)*6, VIS_Y+(y-2)*4, sf);
768 static void levelDrawName (SDL_Surface *frame) {
769 drawString(frame, levelname, lnamex, 0, 0x76);
773 static void levelDrawNumber (SDL_Surface *frame) {
774 char buf[8];
776 sprintf(buf, "%d", curLevel+1);
777 drawString(frame, buf, 320-strlen(buf)*8, 0, 1);
781 static void levelDrawCode (SDL_Surface *frame) {
782 char buf[8];
784 sprintf(buf, "%d", curLevel+1);
785 drawString(frame, levelcode, 320-strlen(levelcode)*8-strlen(buf)*8, 0, 3);
789 static void levelDrawItemName (SDL_Surface *frame) {
790 drawString(frame, itemname, 0, 0, 1);
794 static void levelDrawMisc (SDL_Surface *frame) {
795 if (demorecmode < 0) {
796 if (SDL_GetTicks()%1000 < 500) drawChar(frame, 'R', 0, 8, 3);
797 } else if (demorecmode > 0) {
798 if (SDL_GetTicks()%1000 < 500) drawChar(frame, 'D', 0, 8, 3);
803 static void drawSpriteBank (SDL_Surface *frame, SpriteBank *bank) {
804 SDL_Rect dst;
805 int x = 0, y = 0, maxh = 0;
807 SDL_SetClipRect(frame, NULL);
808 dst.x = 0;
809 dst.y = 0;
810 dst.w = frame->w;
811 dst.h = frame->h;
812 SDL_FillRect(frame, &dst, palette[3]);
814 for (int num = 0; num < bank->count; ++num) {
815 SDL_Surface *s = bank->spr[num][0];
817 if (s != NULL) {
818 char buf[16];
819 int w = s->w/2;
821 sprintf(buf, "%d", num);
822 if (w < strlen(buf)*8+2) w = strlen(buf)*8+2;
823 if (x+w > 320) {
824 x = 0;
825 y += maxh;
826 maxh = 0;
828 if (maxh < s->h/2+1) maxh = s->h/2+1;
829 blitSurface2x(frame, x, y, s);
830 drawString(frame, buf, x, y, 1);
831 x += w+1;
837 static void frmGameDraw (SDL_Surface *frame) {
838 SDL_Rect rc;
840 if (vmPaused && doSave) {
841 if (doSave < 0) {
842 if (loadGame() != 0) fatal("can't load game, VM is inconsistent, dying.");
843 redrawLevel = 1;
844 } else {
845 saveGame();
847 doSave = 0;
848 vmPaused = inMinimap;
851 SDL_SetClipRect(frame, NULL);
853 if (curLevel >= 0) {
854 if (redrawLevel) {
855 levelDrawMap(backbuf);
856 levelDrawName(backbuf);
857 levelDrawNumber(backbuf);
858 levelDrawCode(backbuf);
859 levelDrawItemName(backbuf);
861 blitSurface(frame, 0, 0, backbuf);
862 levelDrawMisc(frame);
864 rc.x = VIS_X*2;
865 rc.y = VIS_Y*2;
866 rc.w = VIS_WIDTH*20*2;
867 rc.h = VIS_HEIGHT*10*2;
868 SDL_SetClipRect(frame, &rc);
869 levelDrawSprites(frame);
870 SDL_SetClipRect(frame, NULL);
871 } else {
872 rc.x = 0;
873 rc.y = 0;
874 rc.w = frame->w;
875 rc.h = frame->h;
876 SDL_FillRect(frame, &rc, palette[0]);
879 #if 1
880 static int angle = 0;
882 if (!fkeys[9]) {
883 angle = (angle+1)%360;
884 //fprintf(stderr, "a=%d\n", angle);
885 } else {
886 polyDump = fkeys[8];
888 polymodStart(320/2, 200/2, 1*POLYFIX_BASE, angle);
890 #if 0
891 polymodAddPoint(-20, -20);
892 polymodAddPoint(20, -20);
893 polymodAddPoint(20, 20);
894 polymodAddPoint(-20, 20);
895 #endif
896 #if 0
897 polymodAddPoint(-10, -10);
898 polymodAddPoint(20, -10);
899 polymodAddPoint(-10, 20);
900 polymodAddPoint(20, 20);
901 #endif
902 #if 1
903 polymodAddPoint(36, 0);
904 polymodAddPoint(46, 12);
905 polymodAddPoint(31, 17);
906 polymodAddPoint(33, 33);
907 polymodAddPoint(18, 31);
908 polymodAddPoint(12, 46);
909 polymodAddPoint(0, 36);
910 polymodAddPoint(-12, 46);
911 polymodAddPoint(-17, 31);
912 polymodAddPoint(-33, 33);
913 polymodAddPoint(-31, 17);
914 polymodAddPoint(-46, 12);
915 polymodAddPoint(-36, 0);
916 polymodAddPoint(-46, -12);
917 polymodAddPoint(-31, -18);
918 polymodAddPoint(-33, -33);
919 polymodAddPoint(-18, -31);
920 polymodAddPoint(-12, -46);
921 polymodAddPoint(0, -36);
922 polymodAddPoint(12, -46);
923 polymodAddPoint(18, -31);
924 polymodAddPoint(33, -33);
925 polymodAddPoint(31, -18);
926 polymodAddPoint(46, -12);
927 #endif
928 #if 0
929 polymodAddPoint(42, 0);
930 polymodAddPoint(40, 10);
931 polymodAddPoint(36, 20);
932 polymodAddPoint(29, 29);
933 polymodAddPoint(21, 36);
934 polymodAddPoint(10, 40);
935 polymodAddPoint(0, 42);
936 polymodAddPoint(-10, 40);
937 polymodAddPoint(-20, 36);
938 polymodAddPoint(-29, 29);
939 polymodAddPoint(-36, 20);
940 polymodAddPoint(-40, 10);
941 polymodAddPoint(-42, 0);
942 polymodAddPoint(-40, -10);
943 polymodAddPoint(-36, -21);
944 polymodAddPoint(-29, -29);
945 polymodAddPoint(-21, -36);
946 polymodAddPoint(-10, -40);
947 polymodAddPoint(0, -42);
948 polymodAddPoint(10, -40);
949 polymodAddPoint(21, -36);
950 polymodAddPoint(29, -29);
951 polymodAddPoint(36, -21);
952 polymodAddPoint(40, -10);
953 #endif
955 polymodEnd();
956 polymodFill(frame, 3, 100);
957 #endif
959 if (goobers) {
960 for (int f = 84; f <= 90; ++f) if (fkeys[f-83]) drawSpriteBank(frame, &banks[f]);
965 ////////////////////////////////////////////////////////////////////////////////
966 #define ROOM_SCRIPTS_MARK " room script labels starts here "
969 static void loadMapScript (ResFile *resfile) {
970 VMLabelInfo *l;
971 int csz;
973 l = vmLabelAddMark(ROOM_SCRIPTS_MARK);
974 l->value = vmCodeSize;
975 if ((csz = loadCodeFile(resfile, vmCodeSize, 94+curLevel)) > 1) {
976 vmCodeSize += csz;
981 static void unloadMapScript (void) {
982 VMLabelInfo *l, *ucl;
984 while ((l = vmFindMark(ROOM_SCRIPTS_MARK)) != NULL) {
985 ucl = vmFindLabel("entry_local_room_script_onunload");
986 if (ucl != NULL && ucl->type == LB_CODE) vmExecuteBSR(0, ucl->value, 0);
987 vmCodeSize = l->value;
988 vmFreeLabelsUntilMark(ROOM_SCRIPTS_MARK);
991 ucl = vmFindLabel("entry_room_onunload");
992 if (ucl != NULL && ucl->type == LB_CODE) vmExecuteBSR(0, ucl->value, 0);
996 ////////////////////////////////////////////////////////////////////////////////
997 static int loadLevelInternal (ResFile *resfile, int lidx) {
998 message[0] = 0;
999 unloadMapScript();
1001 vmGVars[GVAR_VIS_WIDTH] = VIS_WIDTH;
1002 vmGVars[GVAR_VIS_HEIGHT] = VIS_HEIGHT;
1003 vmGVars[GVAR_ITEM_NAME_OFS] = 0;
1004 vmGVars[GVAR_ITEM_NAME_LEN] = 0;
1005 levelname[0] = levelcode[0] = itemname[0] = 0;
1007 if (levelData != NULL) free(levelData); levelData = NULL; levelDataSize = 0;
1008 if (bmap != NULL) free(bmap); bmap = NULL;
1009 if (fmap != NULL) free(fmap); fmap = NULL;
1010 if (xflags != NULL) free(xflags); xflags = NULL;
1012 curLevel = -1;
1013 if (lidx < 0 || lidx > 73) return -1;
1014 if ((levelData = loadResFile(resfile, lidx+9, &levelDataSize)) == NULL) return -1;
1015 curLevel = lidx;
1016 loadMapScript(resfile);
1017 return levelDataSize;
1021 ////////////////////////////////////////////////////////////////////////////////
1022 static void activateMinimap (void) {
1023 inMinimap = 1;
1024 vmPaused = 1;
1025 doSave = 0;
1026 redrawLevel = 1;
1030 ////////////////////////////////////////////////////////////////////////////////
1031 static void getVMString (char *dest, int destsize, int argc, int argv[]) {
1032 if (destsize > 1) {
1033 int pos = 0, len = destsize-1, lp = 0;
1035 switch (argc) {
1036 case 1: fatal("out of args in FRST_SET_LEVEL_NAME");
1037 case 2: pos = argv[1]; break;
1038 case 3: pos = argv[1]; len = argv[2]; break;
1040 memset(dest, 0, destsize);
1041 if (len < 0) len = destsize-1;
1042 if (len > destsize-1) len = destsize-1;
1043 if (pos >= 0) {
1044 while (len > 0 && pos < vmCodeSize && vmCode[pos]) {
1045 dest[lp++] = vmCode[pos++];
1046 --len;
1048 } else {
1049 pos = 0-(pos+1);
1050 while (len > 0 && pos < levelDataSize && levelData[pos]) {
1051 dest[lp++] = levelData[pos++];
1052 --len;
1055 } else if (destsize == 1) {
1056 dest[0] = 0;
1061 static int gameRST (int tid, int opcode, int argc, int argv[], int *argp[]) {
1062 if (argv[0] == CONST_FRST_MINIMAP) {
1063 activateMinimap();
1064 } else if (argv[0] == CONST_FRST_OPEN_LEVEL_FILE) {
1065 // levelnum
1066 //if (vmLoadArgs(tid, 2, argv, argp, argc, argv, argp) != 0) fatal("out of args");
1067 if (argc < 2) fatal("out of args in FRST_LOAD_LEVEL");
1068 vmGVars[GVAR_RST_RESULT] = loadLevelInternal(&resfile, argv[1]);
1069 //if (goobers) fprintf(stderr, "FRST_OPEN_LEVEL_FILE(%d): %d\n", argv[1], vmGVars[GVAR_RST_RESULT]);
1070 } else if (argv[0] == CONST_FRST_GET_LEVEL_DATA_SIZE) {
1071 vmGVars[GVAR_RST_RESULT] = levelDataSize;
1072 } else if (argv[0] == CONST_FRST_GET_LEVEL_LOADER) {
1073 VMLabelInfo *ucl = vmFindLabel("entry_local_room_loader");
1075 vmGVars[GVAR_RST_RESULT] = (ucl != NULL && ucl->type == LB_CODE) ? ucl->value : -1;
1076 } else if (argv[0] == CONST_FRST_GET_LEVEL_INITER) {
1077 VMLabelInfo *ucl = vmFindLabel("entry_local_room_onload");
1079 vmGVars[GVAR_RST_RESULT] = (ucl != NULL && ucl->type == LB_CODE) ? ucl->value : -1;
1080 } else if (argv[0] == CONST_FRST_GET_LEVEL_FILE_BYTE) {
1081 if (argc < 2) fatal("out of args in FRST_GET_LEVEL_FILE_BYTE");
1082 if (argv[1] < 0 || argv[1] >= levelDataSize) fatal("invalid offset in FRST_GET_LEVEL_FILE_BYTE: %d", argv[1]);
1083 vmGVars[GVAR_RST_RESULT] = levelData[argv[1]];
1084 } else if (argv[0] == CONST_FRST_GET_LEVEL_FILE_WORD) {
1085 if (argc < 2) fatal("out of args in FRST_GET_LEVEL_FILE_WORD");
1086 if (argv[1] < 0 || argv[1]+1 >= levelDataSize) fatal("invalid offset in FRST_GET_LEVEL_FILE_WORD: %d", argv[1]);
1087 vmGVars[GVAR_RST_RESULT] = levelData[argv[1]+1];
1088 vmGVars[GVAR_RST_RESULT] <<= 8;
1089 vmGVars[GVAR_RST_RESULT] |= levelData[argv[1]];
1090 if (vmGVars[GVAR_RST_RESULT] >= 32768) vmGVars[GVAR_RST_RESULT] -= 65536;
1091 } else if (argv[0] == CONST_FRST_SET_LEVEL_SIZE) {
1092 int w, h;
1094 if (argc != 3) fatal("out of args in FRST_SET_LEVEL_SIZE");
1095 w = argv[1];
1096 h = argv[2];
1097 if (w < 1 || w > 32700 || h < 1 || h > 32700) fatal("invalid dimensions in FRST_SET_LEVEL_SIZE: %dx%d", w, h);
1098 if (bmap != NULL) free(bmap); bmap = NULL;
1099 if (fmap != NULL) free(fmap); fmap = NULL;
1100 if (xflags != NULL) free(xflags); xflags = NULL;
1101 fmap = calloc(w*h, 1); if (fmap == NULL) fatal("out of memory in FRST_SET_LEVEL_SIZE");
1102 bmap = calloc(w*h, 1); if (bmap == NULL) fatal("out of memory in FRST_SET_LEVEL_SIZE");
1103 xflags = calloc(w, 1); if (xflags == NULL) fatal("out of memory in FRST_SET_LEVEL_SIZE");
1104 mapWidth = vmGVars[GVAR_MAP_WIDTH] = w;
1105 mapHeight = vmGVars[GVAR_MAP_HEIGHT] = h;
1106 oldScrX = 0;
1107 oldScrY = 0;
1108 redrawLevel = 1;
1109 } else if (argv[0] == CONST_FRST_SET_LEVEL_NAME) {
1110 char buf[32];
1112 getVMString(levelname, sizeof(levelname), argc, argv);
1113 lnamex = (320-strlen(levelname)*8)/2;
1114 sprintf(buf, "%d", curLevel+1);
1115 while (lnamex+strlen(levelname)*8+strlen(buf)*8+6*8 > 320) lnamex -= 8;
1116 redrawLevel = 1;
1117 } else if (argv[0] == CONST_FRST_SET_LEVEL_CODE) {
1118 getVMString(levelcode, sizeof(levelcode), argc, argv);
1119 redrawLevel = 1;
1120 } else if (argv[0] == CONST_FRST_SET_ITEM_NAME) {
1121 getVMString(itemname, sizeof(itemname), argc, argv);
1122 redrawLevel = 1;
1123 } else {
1124 if (gameRSTCB) return gameRSTCB(tid, opcode, argc, argv, argp);
1125 fatal("invalid RST: %d", argv[0]);
1127 return 0; // continue
1131 ////////////////////////////////////////////////////////////////////////////////
1132 void setMainLoopGame (void) {
1133 frameCB = frmGameDraw;
1134 keyCB = frmGameKey;
1135 gameRSTCB = gameRST;
1136 beforeVMCB = demoCB;
1137 vmMapGetCB = mapGet;
1138 vmMapSetCB = mapSet;
1140 setSeed(time(NULL));
1141 vmPaused = 0;
1142 inMinimap = 0;
1143 doSave = 0;
1144 redrawLevel = 1;
1145 levelname[0] = levelcode[0] = itemname[0] = 0;
1146 //curLevel = -1;
1147 //demorecmode = 0;
1148 message[0] = 0;
1149 memset(fkeys, 0, sizeof(fkeys));
1150 memset(gamekeys, 0, sizeof(gamekeys));