awasm: new code file format; fixes for (g|t)vars
[awish.git] / src / game.c
blob838fc270ee873638c08d144ba0faa11aaad4d85d
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 static void frmMouse (int x, int y, int buttons) {
615 vmGVars[GVAR_MOUSE_X] = x;
616 vmGVars[GVAR_MOUSE_Y] = y;
617 vmGVars[GVAR_MOUSE_BUTTONS] = buttons;
621 ////////////////////////////////////////////////////////////////////////////////
622 static inline Uint8 fgTile (int x, int y) {
623 return (x >= 0 && y >= 0 && x < mapWidth && y < mapHeight) ? fmap[y*mapWidth+x] : 0;
627 static inline Uint8 bgTile (int x, int y) {
628 if (y == -666) return (x >= 0 && x < mapWidth) ? xflags[x] : 0;
629 return (x >= 0 && y >= 0 && x < mapWidth && y < mapHeight) ? bmap[y*mapWidth+x] : 0;
633 static inline void setFGTile (int x, int y, Uint8 tile) {
634 if (x >= 0 && y >= 0 && x < mapWidth && y < mapHeight) {
635 if (fmap[y*mapWidth+x] != tile) {
636 redrawLevel = 1;
637 fmap[y*mapWidth+x] = tile;
643 static inline void setBGTile (int x, int y, Uint8 tile) {
644 if (y == -666 && x >= 0 && x < mapWidth) xflags[x] = tile;
645 if (x >= 0 && y >= 0 && x < mapWidth && y < mapHeight) {
646 if (bmap[y*mapWidth+x] != tile) {
647 redrawLevel = 1;
648 bmap[y*mapWidth+x] = tile;
654 static int mapGet (int tid, int fg, int x, int y) {
655 if (fmap != NULL) return fg ? fgTile(x, y) : bgTile(x, y);
656 return 0;
660 static void mapSet (int tid, int fg, int x, int y, int tile) {
661 if (fmap != NULL) {
662 if (fg) setFGTile(x, y, tile); else setBGTile(x, y, tile);
667 ////////////////////////////////////////////////////////////////////////////////
668 // cliprect should be set
669 static void levelDrawMap (SDL_Surface *frame) {
670 SDL_Rect rc;
672 rc.x = VIS_X*2;
673 rc.y = VIS_Y*2;
674 rc.w = VIS_WIDTH*20*2;
675 rc.h = VIS_HEIGHT*10*2;
677 if (!inMinimap) {
678 int levelback = vmGVars[GVAR_LEVEL_BACKPIC];
679 int scrX = vmGVars[GVAR_SCR_X], scrY = vmGVars[GVAR_SCR_Y];
681 if (levelback < 1 || levelback > 7) levelback = 1;
682 blitSurface(frame, 0, 0, backs[levelback]);
684 SDL_SetClipRect(frame, &rc);
685 for (int dy = 0; dy < VIS_HEIGHT; ++dy) {
686 for (int dx = 0; dx < VIS_WIDTH; ++dx) {
687 int x = scrX+dx, y = scrY+dy;
688 Uint8 b = bgTile(x, y), f = fgTile(x, y);
690 if (b) blitSurface2x(frame, VIS_X+dx*20, VIS_Y+dy*10, banks[CONST_BANK_BG_TILES].spr[b-1][0]);
691 if (f) blitSurface2x(frame, VIS_X+dx*20, VIS_Y+dy*10, banks[CONST_BANK_FG_TILES].spr[f-1][0]);
694 } else {
695 blitSurface(frame, 0, 0, backs[1]);
696 SDL_SetClipRect(frame, &rc);
697 for (int dy = 0; dy < mapHeight; ++dy) {
698 for (int dx = 1; dx < mapWidth; ++dx) {
699 Uint8 b = bgTile(dx, dy), f = fgTile(dx, dy), t = 8;
701 if (f == 0) {
702 if (b >= 1 && b <= 8) t = b-1; else t = 8;
703 } else {
704 t = f+8;
706 if (banks[CONST_BANK_MAP_TILES].spr[t][0]) {
707 blitSurface2x(frame, VIS_X+dx*6, VIS_Y+dy*4, banks[CONST_BANK_MAP_TILES].spr[t][0]);
708 } else {
709 blitSurface2x(frame, VIS_X+dx*6, VIS_Y+dy*4, banks[CONST_BANK_MAP_TILES].spr[0][0]);
714 SDL_SetClipRect(frame, NULL);
715 redrawLevel = 0;
719 static void levelDrawSprites (SDL_Surface *frame) {
720 if (!inMinimap) {
721 int scrX = vmGVars[GVAR_SCR_X], scrY = vmGVars[GVAR_SCR_Y];
722 int py = vmGetTVar(0, TVAR_POS_Y)-scrY;
724 for (int cc = 0; cc <= 2; ++cc) {
725 for (int f = cc==1?1:0; f <= vmLastThread(); ++f) {
726 if (vmIsThreadAlive(f) && !vmIsSuspendedThread(f)) {
727 int b = vmGetTVar(f, TVAR_SPR_BANK);
728 int s = vmGetTVar(f, TVAR_SPR_NUM);
729 int d = vmGetTVar(f, TVAR_SPR_DIR);
731 if (d >= 0 && d <= 1 && s >= 0 && b >= 0 && b <= 255 && s < banks[b].count && banks[b].spr[s][d]) {
732 SDL_Surface *sf = banks[b].spr[s][d];
733 int x = vmGetTVar(f, TVAR_POS_X)-scrX;
734 int y = vmGetTVar(f, TVAR_POS_Y)-scrY;
735 int tx = vmGetTVar(f, TVAR_POS_TX);
736 int ty = vmGetTVar(f, TVAR_POS_TY);
737 int si = vmGetTVar(f, TVAR_SPR_ITEM);
739 if (cc == 0 && b == CONST_BANK_ITEMS) continue;
740 if (cc == 1 && b != CONST_BANK_ITEMS) continue;
741 if (cc == 2) {
742 if (b != CONST_BANK_PROF) {
743 if (b != CONST_BANK_ITEMS) continue;
744 if (y < py) continue;
747 if (b == CONST_BANK_IM_PROF || b == CONST_BANK_PROF) tx -= 5;
748 blitSurface2x(frame, VIS_X+x*20+tx, VIS_Y+y*10+ty-sf->h/2, sf);
749 if (si > 0 && si < banks[CONST_BANK_ITEMS].count) {
750 sf = banks[CONST_BANK_ITEMS].spr[si][0];
751 blitSurface2x(frame, VIS_X+x*20+tx, VIS_Y+y*10+ty-sf->h/2-24, sf);
757 } else {
758 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]);
759 for (int f = 1; f < VM_MAX_THREADS; ++f) {
760 if (vmIsThreadAlive(f) && !vmIsSuspendedThread(f)) {
761 int i = vmGetTVar(f, TVAR_ITEM_ID);
762 if (i > 0 && i <= 255) {
763 SDL_Surface *sf = banks[CONST_BANK_MAP_ITEMS].spr[i][0];
764 int x = vmGetTVar(f, TVAR_POS_X);
765 int y = vmGetTVar(f, TVAR_POS_Y);
767 blitSurface2x(frame, VIS_X+(x+0)*6, VIS_Y+(y-2)*4, sf);
775 static void levelDrawName (SDL_Surface *frame) {
776 drawString(frame, levelname, lnamex, 0, 0x76);
780 static void levelDrawNumber (SDL_Surface *frame) {
781 char buf[8];
783 sprintf(buf, "%d", curLevel+1);
784 drawString(frame, buf, 320-strlen(buf)*8, 0, 1);
788 static void levelDrawCode (SDL_Surface *frame) {
789 char buf[8];
791 sprintf(buf, "%d", curLevel+1);
792 drawString(frame, levelcode, 320-strlen(levelcode)*8-strlen(buf)*8, 0, 3);
796 static void levelDrawItemName (SDL_Surface *frame) {
797 drawString(frame, itemname, 0, 0, 1);
801 static void levelDrawMisc (SDL_Surface *frame) {
802 if (demorecmode < 0) {
803 if (SDL_GetTicks()%1000 < 500) drawChar(frame, 'R', 0, 8, 3);
804 } else if (demorecmode > 0) {
805 if (SDL_GetTicks()%1000 < 500) drawChar(frame, 'D', 0, 8, 3);
810 static void drawSpriteBank (SDL_Surface *frame, SpriteBank *bank) {
811 SDL_Rect dst;
812 int x = 0, y = 0, maxh = 0;
814 SDL_SetClipRect(frame, NULL);
815 dst.x = 0;
816 dst.y = 0;
817 dst.w = frame->w;
818 dst.h = frame->h;
819 SDL_FillRect(frame, &dst, palette[3]);
821 for (int num = 0; num < bank->count; ++num) {
822 SDL_Surface *s = bank->spr[num][0];
824 if (s != NULL) {
825 char buf[16];
826 int w = s->w/2;
828 if (gamekeys[4] || gamekeys[5]) {
829 // take or use
830 sprintf(buf, "%d", num);
831 if (w < strlen(buf)*8+2) w = strlen(buf)*8+2;
833 if (x+w > 320) {
834 x = 0;
835 y += maxh;
836 maxh = 0;
838 if (maxh < s->h/2+1) maxh = s->h/2+1;
839 blitSurface2x(frame, x, y, s);
840 if (gamekeys[4] || gamekeys[5]) drawString(frame, buf, x, y, 1);
841 x += w+1;
847 static void drawPalette (SDL_Surface *frame) {
848 SDL_Rect dst;
850 SDL_SetClipRect(frame, NULL);
851 dst.x = 0;
852 dst.y = 0;
853 dst.w = frame->w;
854 dst.h = frame->h;
855 SDL_FillRect(frame, &dst, palette[0]);
857 for (int y = 0; y < 16; ++y) {
858 for (int x = 0; x < 16; ++x) {
859 dst.x = x*20;
860 dst.y = y*20;
861 dst.w = 18;
862 dst.h = 18;
863 SDL_FillRect(frame, &dst, palette[y*16+x]);
864 if (gamekeys[4] || gamekeys[5]) {
865 char buf[16];
867 sprintf(buf, "%d", y*16+x);
868 drawString(frame, buf, x*20, y*20, 1);
875 static void frmGameDraw (SDL_Surface *frame) {
876 SDL_Rect rc;
878 if (vmPaused && doSave) {
879 if (doSave < 0) {
880 if (loadGame() != 0) fatal("can't load game, VM is inconsistent, dying.");
881 redrawLevel = 1;
882 } else {
883 saveGame();
885 doSave = 0;
886 vmPaused = inMinimap;
889 SDL_SetClipRect(frame, NULL);
891 if (curLevel >= 0) {
892 if (redrawLevel) {
893 levelDrawMap(backbuf);
894 levelDrawName(backbuf);
895 levelDrawNumber(backbuf);
896 levelDrawCode(backbuf);
897 levelDrawItemName(backbuf);
899 blitSurface(frame, 0, 0, backbuf);
900 levelDrawMisc(frame);
902 rc.x = VIS_X*2;
903 rc.y = VIS_Y*2;
904 rc.w = VIS_WIDTH*20*2;
905 rc.h = VIS_HEIGHT*10*2;
906 SDL_SetClipRect(frame, &rc);
907 levelDrawSprites(frame);
908 SDL_SetClipRect(frame, NULL);
909 } else {
910 rc.x = 0;
911 rc.y = 0;
912 rc.w = frame->w;
913 rc.h = frame->h;
914 SDL_FillRect(frame, &rc, palette[0]);
917 #if 0
918 static int angle = 0;
919 static int mult = 1;
920 static int multdir = 1;
922 if (!fkeys[9]) {
923 angle = (angle+1)%360;
924 mult += multdir;
925 if (mult < 2 || mult > 50) multdir = -multdir;
926 //fprintf(stderr, "a=%d\n", angle);
927 } else {
928 polyDump = fkeys[8];
930 polymodStart(320/2, 200/2, (mult+20)*POLYFIX_BASE/50, angle);
932 #if 0
933 polymodAddPoint(-20, -20);
934 polymodAddPoint(20, -20);
935 polymodAddPoint(20, 20);
936 polymodAddPoint(-20, 20);
937 #endif
938 #if 0
939 polymodAddPoint(-10, -10);
940 polymodAddPoint(20, -10);
941 polymodAddPoint(-10, 20);
942 polymodAddPoint(20, 20);
943 #endif
944 #if 1
945 polymodAddPoint(36, 0);
946 polymodAddPoint(46, 12);
947 polymodAddPoint(31, 17);
948 polymodAddPoint(33, 33);
949 polymodAddPoint(18, 31);
950 polymodAddPoint(12, 46);
951 polymodAddPoint(0, 36);
952 polymodAddPoint(-12, 46);
953 polymodAddPoint(-17, 31);
954 polymodAddPoint(-33, 33);
955 polymodAddPoint(-31, 17);
956 polymodAddPoint(-46, 12);
957 polymodAddPoint(-36, 0);
958 polymodAddPoint(-46, -12);
959 polymodAddPoint(-31, -18);
960 polymodAddPoint(-33, -33);
961 polymodAddPoint(-18, -31);
962 polymodAddPoint(-12, -46);
963 polymodAddPoint(0, -36);
964 polymodAddPoint(12, -46);
965 polymodAddPoint(18, -31);
966 polymodAddPoint(33, -33);
967 polymodAddPoint(31, -18);
968 polymodAddPoint(46, -12);
969 #endif
970 #if 0
971 polymodAddPoint(42, 0);
972 polymodAddPoint(40, 10);
973 polymodAddPoint(36, 20);
974 polymodAddPoint(29, 29);
975 polymodAddPoint(21, 36);
976 polymodAddPoint(10, 40);
977 polymodAddPoint(0, 42);
978 polymodAddPoint(-10, 40);
979 polymodAddPoint(-20, 36);
980 polymodAddPoint(-29, 29);
981 polymodAddPoint(-36, 20);
982 polymodAddPoint(-40, 10);
983 polymodAddPoint(-42, 0);
984 polymodAddPoint(-40, -10);
985 polymodAddPoint(-36, -21);
986 polymodAddPoint(-29, -29);
987 polymodAddPoint(-21, -36);
988 polymodAddPoint(-10, -40);
989 polymodAddPoint(0, -42);
990 polymodAddPoint(10, -40);
991 polymodAddPoint(21, -36);
992 polymodAddPoint(29, -29);
993 polymodAddPoint(36, -21);
994 polymodAddPoint(40, -10);
995 #endif
997 polymodEnd();
998 polymodFill(frame, 3, 100);
999 #endif
1001 if (goobers) {
1002 for (int f = 84; f <= 90; ++f) if (fkeys[f-83]) drawSpriteBank(frame, &banks[f]);
1003 if (fkeys[8]) drawPalette(frame);
1005 if (vmGVars[GVAR_MOUSE_HIDDEN] == 0) {
1006 int cur = vmGVars[GVAR_MOUSE_CURSOR];
1008 if (cur >= 0 && cur < banks[256].count) {
1009 blitSurface2x(frame, vmGVars[GVAR_MOUSE_X], vmGVars[GVAR_MOUSE_Y], banks[256].spr[cur][0]);
1015 ////////////////////////////////////////////////////////////////////////////////
1016 #define ROOM_SCRIPTS_MARK " room script labels starts here "
1019 static void loadMapScript (ResFile *resfile) {
1020 VMLabelInfo *l;
1021 int csz;
1023 l = vmLabelAddMark(ROOM_SCRIPTS_MARK);
1024 l->value = vmCodeSize;
1025 if ((csz = loadCodeFile(resfile, vmCodeSize, 94+curLevel, 1)) > 1) {
1026 vmCodeSize += csz;
1031 static void unloadMapScript (void) {
1032 VMLabelInfo *l, *ucl;
1034 while ((l = vmFindMark(ROOM_SCRIPTS_MARK)) != NULL) {
1035 ucl = vmFindLabel("entry_local_room_script_onunload");
1036 if (ucl != NULL && ucl->type == LB_CODE) vmExecuteBSR(0, ucl->value, 0);
1037 vmCodeSize = l->value;
1038 vmFreeLabelsUntilMark(ROOM_SCRIPTS_MARK);
1041 ucl = vmFindLabel("entry_goobers_room_onunload");
1042 if (ucl != NULL && ucl->type == LB_CODE) vmExecuteBSR(0, ucl->value, 0);
1044 ucl = vmFindLabel("entry_room_onunload");
1045 if (ucl != NULL && ucl->type == LB_CODE) vmExecuteBSR(0, ucl->value, 0);
1049 ////////////////////////////////////////////////////////////////////////////////
1050 static int loadLevelInternal (ResFile *resfile, int lidx) {
1051 message[0] = 0;
1052 unloadMapScript();
1054 vmGVars[GVAR_VIS_WIDTH] = VIS_WIDTH;
1055 vmGVars[GVAR_VIS_HEIGHT] = VIS_HEIGHT;
1056 levelname[0] = levelcode[0] = itemname[0] = 0;
1058 if (levelData != NULL) free(levelData); levelData = NULL; levelDataSize = 0;
1059 if (bmap != NULL) free(bmap); bmap = NULL;
1060 if (fmap != NULL) free(fmap); fmap = NULL;
1061 if (xflags != NULL) free(xflags); xflags = NULL;
1063 curLevel = -1;
1064 if (lidx < 0 || lidx > 73) return -1;
1065 if ((levelData = loadResFile(resfile, lidx+9, &levelDataSize)) == NULL) return -1;
1066 curLevel = lidx;
1067 loadMapScript(resfile);
1068 return levelDataSize;
1072 ////////////////////////////////////////////////////////////////////////////////
1073 static void activateMinimap (void) {
1074 inMinimap = 1;
1075 vmPaused = 1;
1076 doSave = 0;
1077 redrawLevel = 1;
1081 ////////////////////////////////////////////////////////////////////////////////
1082 static void getVMString (char *dest, int destsize, int argc, int argv[]) {
1083 if (destsize > 1) {
1084 int pos = 0, len = destsize-1, lp = 0;
1086 switch (argc) {
1087 case 1: fatal("out of args in FRST_SET_LEVEL_NAME");
1088 case 2: pos = argv[1]; break;
1089 case 3: pos = argv[1]; len = argv[2]; break;
1091 memset(dest, 0, destsize);
1092 if (len < 0) len = destsize-1;
1093 if (len > destsize-1) len = destsize-1;
1094 if (pos >= 0) {
1095 while (len > 0 && pos < vmCodeSize && vmCode[pos]) {
1096 dest[lp++] = vmCode[pos++];
1097 --len;
1099 } else {
1100 pos = 0-(pos+1);
1101 while (len > 0 && pos < levelDataSize && levelData[pos]) {
1102 dest[lp++] = levelData[pos++];
1103 --len;
1106 } else if (destsize == 1) {
1107 dest[0] = 0;
1112 static int gameRST (int tid, int opcode, int argc, int argv[], int *argp[]) {
1113 if (argv[0] == CONST_FRST_MINIMAP) {
1114 activateMinimap();
1115 } else if (argv[0] == CONST_FRST_OPEN_LEVEL_FILE) {
1116 // levelnum
1117 //if (vmLoadArgs(tid, 2, argv, argp, argc, argv, argp) != 0) fatal("out of args");
1118 if (argc < 2) fatal("out of args in FRST_LOAD_LEVEL");
1119 vmGVars[GVAR_RST_RESULT] = loadLevelInternal(&resfile, argv[1]);
1120 //if (goobers) fprintf(stderr, "FRST_OPEN_LEVEL_FILE(%d): %d\n", argv[1], vmGVars[GVAR_RST_RESULT]);
1121 } else if (argv[0] == CONST_FRST_GET_LEVEL_DATA_SIZE) {
1122 vmGVars[GVAR_RST_RESULT] = levelDataSize;
1123 } else if (argv[0] == CONST_FRST_GET_LEVEL_LOADER) {
1124 VMLabelInfo *ucl = vmFindLabel("entry_local_room_loader");
1126 vmGVars[GVAR_RST_RESULT] = (ucl != NULL && ucl->type == LB_CODE) ? ucl->value : -1;
1127 } else if (argv[0] == CONST_FRST_GET_LEVEL_INITER) {
1128 VMLabelInfo *ucl = vmFindLabel("entry_local_room_onload");
1130 vmGVars[GVAR_RST_RESULT] = (ucl != NULL && ucl->type == LB_CODE) ? ucl->value : -1;
1131 } else if (argv[0] == CONST_FRST_GET_GOOBERS_INITER) {
1132 VMLabelInfo *ucl = vmFindLabel("entry_goobers_game_init");
1134 vmGVars[GVAR_RST_RESULT] = (ucl != NULL && ucl->type == LB_CODE) ? ucl->value : -1;
1135 } else if (argv[0] == CONST_FRST_GET_GOOBERS_ONLOAD) {
1136 VMLabelInfo *ucl = vmFindLabel("entry_goobers_room_onload");
1138 vmGVars[GVAR_RST_RESULT] = (ucl != NULL && ucl->type == LB_CODE) ? ucl->value : -1;
1139 } else if (argv[0] == CONST_FRST_GET_LEVEL_FILE_BYTE) {
1140 if (argc < 2) fatal("out of args in FRST_GET_LEVEL_FILE_BYTE");
1141 if (argv[1] < 0 || argv[1] >= levelDataSize) fatal("invalid offset in FRST_GET_LEVEL_FILE_BYTE: %d", argv[1]);
1142 vmGVars[GVAR_RST_RESULT] = levelData[argv[1]];
1143 } else if (argv[0] == CONST_FRST_GET_LEVEL_FILE_WORD) {
1144 if (argc < 2) fatal("out of args in FRST_GET_LEVEL_FILE_WORD");
1145 if (argv[1] < 0 || argv[1]+1 >= levelDataSize) fatal("invalid offset in FRST_GET_LEVEL_FILE_WORD: %d", argv[1]);
1146 vmGVars[GVAR_RST_RESULT] = levelData[argv[1]+1];
1147 vmGVars[GVAR_RST_RESULT] <<= 8;
1148 vmGVars[GVAR_RST_RESULT] |= levelData[argv[1]];
1149 if (vmGVars[GVAR_RST_RESULT] >= 32768) vmGVars[GVAR_RST_RESULT] -= 65536;
1150 } else if (argv[0] == CONST_FRST_SET_LEVEL_SIZE) {
1151 int w, h;
1153 if (argc != 3) fatal("out of args in FRST_SET_LEVEL_SIZE");
1154 w = argv[1];
1155 h = argv[2];
1156 if (w < 1 || w > 32700 || h < 1 || h > 32700) fatal("invalid dimensions in FRST_SET_LEVEL_SIZE: %dx%d", w, h);
1157 if (bmap != NULL) free(bmap); bmap = NULL;
1158 if (fmap != NULL) free(fmap); fmap = NULL;
1159 if (xflags != NULL) free(xflags); xflags = NULL;
1160 fmap = calloc(w*h, 1); if (fmap == NULL) fatal("out of memory in FRST_SET_LEVEL_SIZE");
1161 bmap = calloc(w*h, 1); if (bmap == NULL) fatal("out of memory in FRST_SET_LEVEL_SIZE");
1162 xflags = calloc(w, 1); if (xflags == NULL) fatal("out of memory in FRST_SET_LEVEL_SIZE");
1163 mapWidth = vmGVars[GVAR_MAP_WIDTH] = w;
1164 mapHeight = vmGVars[GVAR_MAP_HEIGHT] = h;
1165 oldScrX = 0;
1166 oldScrY = 0;
1167 redrawLevel = 1;
1168 } else if (argv[0] == CONST_FRST_SET_LEVEL_NAME) {
1169 char buf[32];
1171 getVMString(levelname, sizeof(levelname), argc, argv);
1172 lnamex = (320-strlen(levelname)*8)/2;
1173 sprintf(buf, "%d", curLevel+1);
1174 while (lnamex+strlen(levelname)*8+strlen(buf)*8+6*8 > 320) lnamex -= 8;
1175 redrawLevel = 1;
1176 } else if (argv[0] == CONST_FRST_SET_LEVEL_CODE) {
1177 getVMString(levelcode, sizeof(levelcode), argc, argv);
1178 redrawLevel = 1;
1179 } else if (argv[0] == CONST_FRST_SET_ITEM_NAME) {
1180 getVMString(itemname, sizeof(itemname), argc, argv);
1181 redrawLevel = 1;
1182 } else {
1183 if (gameRSTCB) return gameRSTCB(tid, opcode, argc, argv, argp);
1184 fatal("invalid RST: %d", argv[0]);
1186 return 0; // continue
1190 ////////////////////////////////////////////////////////////////////////////////
1191 void setMainLoopGame (void) {
1192 frameCB = frmGameDraw;
1193 keyCB = frmGameKey;
1194 mouseCB = frmMouse;
1195 gameRSTCB = gameRST;
1196 beforeVMCB = demoCB;
1197 vmMapGetCB = mapGet;
1198 vmMapSetCB = mapSet;
1200 setSeed(time(NULL));
1201 vmPaused = 0;
1202 inMinimap = 0;
1203 doSave = 0;
1204 redrawLevel = 1;
1205 levelname[0] = levelcode[0] = itemname[0] = 0;
1206 //curLevel = -1;
1207 //demorecmode = 0;
1208 message[0] = 0;
1209 memset(fkeys, 0, sizeof(fkeys));
1210 memset(gamekeys, 0, sizeof(gamekeys));