alot of code changed; first try to move level loader out of C engine; not working yet
[awish.git] / src / game.c
blob4d5dbfb5fec9f39fd93ccaa87510ceac86706a67
1 /*
2 * This program is free software: you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation, either version 3 of the License, or
5 * (at your option) any later version.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <time.h>
19 #include <unistd.h>
21 #include "game.h"
23 #include "resfile.h"
24 #include "video.h"
25 #include "mainloop.h"
26 #include "vm.h"
27 #include "gameglobals.h"
28 #include "title.h"
31 extern int goobers;
35 ////////////////////////////////////////////////////////////////////////////////
36 VMRSTCB gameRSTCB = NULL;
39 ////////////////////////////////////////////////////////////////////////////////
40 #define VIS_WIDTH (15)
41 #define VIS_HEIGHT (17)
42 #define VIS_X (10)
43 #define VIS_Y (20)
46 ////////////////////////////////////////////////////////////////////////////////
47 static int mapWidth;
48 static int mapHeight;
49 static int curLevel;
50 static char levelname[257];
51 static int lnamex;
52 static int levelback;
53 static Uint8 *levelData = NULL;
54 static int levelDataSize = 0;
55 static Uint8 *bmap = NULL, *fmap = NULL;
56 static Uint8 *xflags = NULL;
57 static int fkeys[10];
58 static int inMinimap;
60 static int doSave = 0;
62 static int demorecmode = 0; // 0: none; 1: recording; -1: playing
63 static int demoSize = 0;
64 static int demoPos = 0;
65 static Uint8 *demoData = NULL;
66 static Uint8 demoPrevKeyState = 0;
67 static int demoKeyStateRepeats = 0;
69 static char message[256];
70 //static Uint32 msgEndTime = 0;
72 static Uint32 demo_m_w = 0;
73 static Uint32 demo_m_z = 0;
76 ////////////////////////////////////////////////////////////////////////////////
77 static int gamekeys[12];
80 static void setGameKeysGVars (void) {
81 vmGVars[GVAR_KEY_LEFT] = gamekeys[0] ? 1 : 0;
82 vmGVars[GVAR_KEY_RIGHT] = gamekeys[1] ? 1 : 0;
83 vmGVars[GVAR_KEY_UP] = gamekeys[2] ? 1 : 0;
84 vmGVars[GVAR_KEY_DOWN] = gamekeys[3] ? 1 : 0;
85 vmGVars[GVAR_KEY_TAKE] = gamekeys[4] ? 1 : 0;
86 vmGVars[GVAR_KEY_USE] = gamekeys[5] ? 1 : 0;
88 vmGVars[GVAR_KEY_MINIMAP] = gamekeys[6] ? 1 : 0;
89 vmGVars[GVAR_KEY_RESTART] = gamekeys[7] ? 1 : 0;
91 vmGVars[GVAR_KEY_FALL_CHEAT] = gamekeys[8] ? 1 : 0;
92 vmGVars[GVAR_KEY_WALK_CHEAT] = gamekeys[9] ? 1 : 0;
93 vmGVars[GVAR_KEY_PLEV_CHEAT] = gamekeys[10] ? 1 : 0;
94 vmGVars[GVAR_KEY_NLEV_CHEAT] = gamekeys[11] ? 1 : 0;
98 ////////////////////////////////////////////////////////////////////////////////
99 #define SECRET (42)
100 //#define SECRET (0)
103 static int readBuf (FILE *fl, void *buf, int len) {
104 unsigned char *c = (unsigned char *)buf;
106 while (len-- > 0) {
107 unsigned char b;
109 if (fread(&b, 1, 1, fl) != 1) return -1;
110 b ^= SECRET;
111 *c++ = b;
113 return 0;
117 static int writeBuf (FILE *fl, const void *buf, int len) {
118 const unsigned char *c = (const unsigned char *)buf;
120 while (len-- > 0) {
121 unsigned char b = *c++;
123 b ^= SECRET;
124 if (fwrite(&b, 1, 1, fl) != 1) return -1;
126 return 0;
130 static int readDW (FILE *fl, int *v) {
131 int t[4];
133 for (int f = 0; f < 4; ++f) {
134 unsigned char b;
136 if (fread(&b, 1, 1, fl) != 1) return -1;
137 t[f] = b^SECRET;
140 if (v) {
141 *v = 0;
142 for (int f = 0; f < 4; ++f) *v |= t[f]<<(8*f);
144 return 0;
148 static int writeDW (FILE *fl, int v) {
149 for (int f = 0; f < 4; ++f) {
150 unsigned char b;
152 b = v&0xff;
153 b ^= SECRET;
154 if (fwrite(&b, 1, 1, fl) != 1) return -1;
155 v >>= 8;
157 return 0;
161 static int readUDW (FILE *fl, Uint32 *v) {
162 int t[4];
164 for (int f = 0; f < 4; ++f) {
165 unsigned char b;
167 if (fread(&b, 1, 1, fl) != 1) return -1;
168 t[f] = b^SECRET;
171 if (v) {
172 *v = 0;
173 for (int f = 0; f < 4; ++f) *v |= t[f]<<(8*f);
175 return 0;
179 static int writeUDW (FILE *fl, Uint32 v) {
180 for (int f = 0; f < 4; ++f) {
181 unsigned char b;
183 b = v&0xff;
184 b ^= SECRET;
185 if (fwrite(&b, 1, 1, fl) != 1) return -1;
186 v >>= 8;
188 return 0;
192 ////////////////////////////////////////////////////////////////////////////////
193 static const char *SAVE_GAME_SIGNATURE = "ASG0";
195 static int loadGame (void) {
196 FILE *fl = fopen("awish.sav", "rb");
198 if (fl != NULL) {
199 char sign[4];
201 if (fread(sign, 4, 1, fl) != 1) goto error;
202 if (memcmp(sign, SAVE_GAME_SIGNATURE, 4) != 0) goto error;
203 if (vmLoadState(fl) != 0) goto error;
204 if (readUDW(fl, &demo_m_w) != 0) goto error;
205 if (readUDW(fl, &demo_m_z) != 0) goto error;
206 if (readDW(fl, &inMinimap) != 0) goto error;
207 if (readDW(fl, &curLevel) != 0) goto error;
208 if (readBuf(fl, levelname, sizeof(levelname)) != 0) goto error;
209 if (readDW(fl, &lnamex) != 0) goto error;
210 if (readDW(fl, &levelback) != 0) goto error;
211 if (readBuf(fl, bmap, sizeof(bmap)) != 0) goto error;
212 if (readBuf(fl, fmap, sizeof(fmap)) != 0) goto error;
213 if (readBuf(fl, xflags, sizeof(xflags)) != 0) goto error;
214 fclose(fl);
215 setSeedL(demo_m_w);
216 setSeedL(demo_m_z);
217 return 0;
219 error:
220 fclose(fl);
221 if (goobers) fprintf(stderr, "error loading saved game\n");
222 return -1;
226 static int saveGame (void) {
227 FILE *fl = fopen("awish.sav", "wb");
229 if (fl != NULL) {
230 demo_m_w = getSeedL();
231 demo_m_z = getSeedH();
232 if (fwrite(SAVE_GAME_SIGNATURE, 4, 1, fl) != 1) goto error;
233 if (vmSaveState(fl) != 0) goto error;
234 if (writeUDW(fl, demo_m_w) != 0) goto error;
235 if (writeUDW(fl, demo_m_z) != 0) goto error;
236 if (writeDW(fl, inMinimap) != 0) goto error;
237 if (writeDW(fl, curLevel) != 0) goto error;
238 if (writeBuf(fl, levelname, sizeof(levelname)) != 0) goto error;
239 if (writeDW(fl, lnamex) != 0) goto error;
240 if (writeDW(fl, levelback) != 0) goto error;
241 if (writeBuf(fl, bmap, sizeof(bmap)) != 0) goto error;
242 if (writeBuf(fl, fmap, sizeof(fmap)) != 0) goto error;
243 if (writeBuf(fl, xflags, sizeof(xflags)) != 0) goto error;
244 fclose(fl);
245 return 0;
247 error:
248 fclose(fl);
249 unlink("awish.sav");
250 if (goobers) fprintf(stderr, "error saving game\n");
251 return -1;
255 ////////////////////////////////////////////////////////////////////////////////
256 static void demoClear (void) {
257 //demorecmode = 0;
258 demoSize = 0;
259 demoPos = 0;
260 demoPrevKeyState = 0;
261 demoKeyStateRepeats = 0;
262 if (demoData != NULL) free(demoData);
263 demoData = NULL;
267 static void demoAddByte (Uint8 b) {
268 if (demoPos+1 > demoSize && demoSize > 1024*1024*16) fatal("out of memory in demo recording!");
269 if (demoPos+1 > demoSize) {
270 int newsz = demoSize+1024*32;
271 Uint8 *nn = realloc(demoData, newsz);
273 if (!nn) fatal("out of memory in demo recording!");
274 demoData = nn;
275 demoSize = newsz;
277 demoData[demoPos++] = b;
281 static Uint8 demoGetKeyState (void) {
282 return
283 ((gamekeys[0]?1:0)<<0) |
284 ((gamekeys[1]?1:0)<<1) |
285 ((gamekeys[2]?1:0)<<2) |
286 ((gamekeys[3]?1:0)<<3) |
287 ((gamekeys[4]?1:0)<<4) |
288 ((gamekeys[5]?1:0)<<5);
292 static void demoSetKeyState (Uint8 kstate) {
293 gamekeys[0] = (kstate&(1<<0)) != 0;
294 gamekeys[1] = (kstate&(1<<1)) != 0;
295 gamekeys[2] = (kstate&(1<<2)) != 0;
296 gamekeys[3] = (kstate&(1<<3)) != 0;
297 gamekeys[4] = (kstate&(1<<4)) != 0;
298 gamekeys[5] = (kstate&(1<<5)) != 0;
302 static void demoAddFrameData (void) {
303 Uint8 kstate = demoGetKeyState();
305 if (demoKeyStateRepeats == 256) {
306 demoAddByte(demoKeyStateRepeats-1);
307 demoAddByte(demoPrevKeyState);
308 demoKeyStateRepeats = 0;
311 if (kstate != demoPrevKeyState) {
312 if (demoKeyStateRepeats > 0) {
313 demoAddByte(demoKeyStateRepeats-1);
314 demoAddByte(demoPrevKeyState);
316 demoPrevKeyState = kstate;
317 demoKeyStateRepeats = 1;
318 } else {
319 ++demoKeyStateRepeats;
324 static void demoGetFrameData (void) {
325 while (demoKeyStateRepeats < 1) {
326 if (demoPos+2 > demoSize) {
327 demoClear();
328 demorecmode = 0;
329 if (goobers) fprintf(stderr, "demo complete\n");
330 strcpy(message, "demo stopped");
331 return;
333 demoKeyStateRepeats = demoData[demoPos++];
334 ++demoKeyStateRepeats;
335 demoPrevKeyState = demoData[demoPos++];
337 --demoKeyStateRepeats;
339 demoSetKeyState(demoPrevKeyState);
343 static int demoSave (void) {
344 FILE *fl;
345 char buf[128];
346 char sign[5];
348 if (demoKeyStateRepeats > 0) {
349 demoAddByte(demoKeyStateRepeats-1);
350 demoAddByte(demoPrevKeyState);
353 sprintf(buf, "awish%02d.dmo", curLevel+1);
354 fl = fopen(buf, "wb");
355 if (fl == NULL) {
356 if (goobers) fprintf(stderr, "can't create demo file '%s'\n", buf);
357 return -1;
359 strcpy(sign, "AWD1");
360 if (fwrite(sign, 4, 1, fl) != 1) goto error;
361 if (writeDW(fl, curLevel) != 0) goto error;
362 if (writeUDW(fl, demo_m_w) != 0) goto error;
363 if (writeUDW(fl, demo_m_z) != 0) goto error;
364 if (writeDW(fl, demoPos) != 0) goto error;
365 if (writeBuf(fl, demoData, demoPos) != 0) goto error;
366 fclose(fl);
367 if (goobers) fprintf(stderr, "demo saved to file '%s'\n", buf);
368 return 0;
369 error:
370 fclose(fl);
371 unlink(buf);
372 if (goobers) fprintf(stderr, "can't write demo file '%s'\n", buf);
373 return -1;
377 static int demoLoad (void) {
378 FILE *fl;
379 char buf[128];
380 int size, level;
381 char sign[4];
383 demoClear();
384 sprintf(buf, "awish%02d.dmo", curLevel+1);
385 fl = fopen(buf, "rb");
386 if (fl == NULL) {
387 if (goobers) fprintf(stderr, "can't open demo file '%s'\n", buf);
388 return -1;
391 if (fread(sign, 4, 1, fl) != 1) goto error;
392 if (memcmp(sign, "AWD1", 4) != 0) goto error;
393 if (readDW(fl, &level) != 0) goto error;
394 if (readUDW(fl, &demo_m_w) != 0) goto error;
395 if (readUDW(fl, &demo_m_z) != 0) goto error;
396 if (readDW(fl, &size) != 0) goto error;
397 if (size < 1 || size > 1024*1024*16) goto error;
398 demoData = malloc(size);
399 if (demoData == NULL) goto error;
400 if (readBuf(fl, demoData, size) != 0) goto error;
401 demoSize = size;
402 demoPos = 0;
403 fclose(fl);
404 setSeedL(demo_m_w);
405 setSeedL(demo_m_z);
406 if (goobers) fprintf(stderr, "loaded demo file '%s'\n", buf);
408 return 0;
409 error:
410 fclose(fl);
411 if (goobers) fprintf(stderr, "can't load demo file '%s'\n", buf);
412 demoClear();
413 return -1;
417 static inline int isGameStopped (void) {
418 return
419 vmGVars[GVAR_GAME_STATE] == CONST_GAME_STATE_DEAD ||
420 vmGVars[GVAR_GAME_STATE] == CONST_GAME_STATE_COMPLETE;
424 static void demoCB (void) {
425 //if (vmGVars[GVAR_GAME_STATE] == CONST_GAME_STATE_STARTED) unloadMapScript();
427 switch (demorecmode) {
428 case -666: // prepare to load demo
429 if (demoLoad() != 0) {
430 demorecmode = 0;
431 break;
433 demorecmode = -2;
434 // fallthru
435 case 666: // prepare to save demo
436 vmSetPC(0, CODE_ENTRY_GAME_RESTART_LEVEL);
437 vmGVars[GVAR_CUR_LEVEL] = curLevel;
438 vmGVars[GVAR_GAME_STATE] = -1; // invalid state
439 if (demorecmode > 0) {
440 demoClear();
441 demorecmode = 2;
443 break;
444 case -1: // demo replaying
445 case 1: // demo saving
446 if (isGameStopped()) {
447 // the level is over or prof is dead
448 if (demorecmode > 0) demoSave();
449 demoClear();
450 demorecmode = 0;
451 } else {
452 ((demorecmode < 0) ? demoGetFrameData : demoAddFrameData)();
454 break;
455 case -2: // waiting for 'game started' trigger
456 case 2: // waiting for 'game started' trigger
457 if (vmGVars[GVAR_GAME_STATE] >= 0) {
458 if (goobers) fprintf(stderr, "demo %s started...\n", demorecmode<0?"replaying":"recording");
459 if (demorecmode < 0) {
460 // replay
461 setSeedL(demo_m_w);
462 setSeedL(demo_m_z);
463 } else {
464 // record
465 demo_m_w = getSeedL();
466 demo_m_z = getSeedH();
468 demorecmode = (demorecmode < 0) ? -1 : 1;
470 break;
472 setGameKeysGVars();
476 ////////////////////////////////////////////////////////////////////////////////
477 static void frmGameKey (SDL_KeyboardEvent *key) {
478 if (key->type == SDL_KEYDOWN) {
479 switch (key->keysym.sym) {
480 case SDLK_ESCAPE:
481 case SDLK_SPACE:
482 case SDLK_RETURN:
483 if (vmPaused && inMinimap) {
484 inMinimap = 0;
485 doSave = 0;
486 vmPaused = 0;
487 gamekeys[6] = 0;
489 break;
490 case SDLK_F12:
491 vmGVars[GVAR_KEY_QUIT] = 1;
492 break;
493 case SDLK_p:
494 if (vmPaused == 0) {
495 gamekeys[6] = 1;
496 } else {
497 if (inMinimap) {
498 inMinimap = 0;
499 doSave = 0;
500 vmPaused = 0;
501 gamekeys[6] = 1;
504 break;
505 case SDLK_r:
506 if (vmPaused == 0) {
507 if (demorecmode != 0) { demoClear(); demorecmode = 0; }
508 if ((key->keysym.mod&(KMOD_CTRL)) != 0) {
509 gamekeys[7] = 1;
512 break;
513 case SDLK_s:
514 if (vmPaused == 0 && !demorecmode) {
515 if (goobers && (key->keysym.mod&(KMOD_CTRL)) != 0) { doSave = 1; vmPaused = 1; }
517 break;
518 case SDLK_l:
519 if (vmPaused == 0 && !demorecmode) {
520 if (goobers && (key->keysym.mod&(KMOD_CTRL)) != 0) { doSave = -1; vmPaused = 1; }
522 break;
523 case SDLK_d:
524 if (vmPaused == 0) {
525 if (goobers && (key->keysym.mod&(KMOD_CTRL)) != 0) {
526 if (!demorecmode) {
527 demorecmode = 666; // 'start saving'
528 } else {
529 demoSave();
530 demoClear();
531 demorecmode = 0;
535 break;
536 case SDLK_m:
537 if (vmPaused == 0) {
538 if (goobers && (key->keysym.mod&(KMOD_CTRL)) != 0) {
539 if (!demorecmode) {
540 demorecmode = -666; // 'start playing'
541 } else {
542 demoClear();
543 demorecmode = 0;
544 demoSetKeyState(0);
548 break;
549 default: ;
553 if (demorecmode == 0 || demorecmode == 1) {
554 switch (key->keysym.sym) {
555 case SDLK_LEFT: case SDLK_KP4: gamekeys[0] = (key->type == SDL_KEYDOWN); break;
556 case SDLK_RIGHT: case SDLK_KP6: gamekeys[1] = (key->type == SDL_KEYDOWN); break;
557 case SDLK_UP: case SDLK_KP8: gamekeys[2] = (key->type == SDL_KEYDOWN); break;
558 case SDLK_DOWN: case SDLK_KP2: gamekeys[3] = (key->type == SDL_KEYDOWN); break;
559 case SDLK_SPACE: case SDLK_KP0: gamekeys[4] = (key->type == SDL_KEYDOWN); break;
560 case SDLK_RETURN: case SDLK_KP_PERIOD: gamekeys[5] = (key->type == SDL_KEYDOWN); break;
561 default: ;
564 if (goobers && !demorecmode) {
565 switch (key->keysym.sym) {
566 case SDLK_f: gamekeys[8] = (key->type == SDL_KEYDOWN); break;
567 case SDLK_w: gamekeys[9] = (key->type == SDL_KEYDOWN); break;
568 case SDLK_KP_MINUS: gamekeys[10] = (key->type == SDL_KEYDOWN); break;
569 case SDLK_KP_PLUS: gamekeys[11] = (key->type == SDL_KEYDOWN); break;
571 case SDLK_F1: fkeys[1] = (key->type == SDL_KEYDOWN); break;
572 case SDLK_F2: fkeys[2] = (key->type == SDL_KEYDOWN); break;
573 case SDLK_F3: fkeys[3] = (key->type == SDL_KEYDOWN); break;
574 case SDLK_F4: fkeys[4] = (key->type == SDL_KEYDOWN); break;
575 case SDLK_F5: fkeys[5] = (key->type == SDL_KEYDOWN); break;
576 case SDLK_F6: fkeys[6] = (key->type == SDL_KEYDOWN); break;
577 case SDLK_F7: fkeys[7] = (key->type == SDL_KEYDOWN); break;
578 case SDLK_F8: fkeys[8] = (key->type == SDL_KEYDOWN); break;
579 case SDLK_F9: fkeys[9] = (key->type == SDL_KEYDOWN); break;
580 default: ;
587 ////////////////////////////////////////////////////////////////////////////////
588 static inline Uint8 fgTile (int x, int y) {
589 return (x >= 0 && y >= 0 && x < mapWidth && y < mapHeight) ? fmap[y*mapWidth+x] : 0;
593 static inline Uint8 bgTile (int x, int y) {
594 if (y == -666) return (x >= 0 && x < mapWidth) ? xflags[x] : 0;
595 return (x >= 0 && y >= 0 && x < mapWidth && y < mapHeight) ? bmap[y*mapWidth+x] : 0;
599 static inline void setFGTile (int x, int y, Uint8 tile) {
600 if (x >= 0 && y >= 0 && x < mapWidth && y < mapHeight) fmap[y*mapWidth+x] = tile;
604 static inline void setBGTile (int x, int y, Uint8 tile) {
605 if (y == -666 && x >= 0 && x < mapWidth) xflags[x] = tile;
606 if (x >= 0 && y >= 0 && x < mapWidth && y < mapHeight) bmap[y*mapWidth+x] = tile;
610 static int mapGet (int tid, int fg, int x, int y) {
611 if (fmap != NULL) return fg ? fgTile(x, y) : bgTile(x, y);
612 return 0;
616 static void mapSet (int tid, int fg, int x, int y, int tile) {
617 if (fmap != NULL) {
618 if (fg) setFGTile(x, y, tile); else setBGTile(x, y, tile);
623 ////////////////////////////////////////////////////////////////////////////////
624 // cliprect should be set
625 static void levelDrawMap (SDL_Surface *frame) {
626 if (!inMinimap) {
627 int scrX = vmGVars[GVAR_SCR_X], scrY = vmGVars[GVAR_SCR_Y];
629 for (int dy = 0; dy < VIS_HEIGHT; ++dy) {
630 for (int dx = 0; dx < VIS_WIDTH; ++dx) {
631 int x = scrX+dx, y = scrY+dy;
632 Uint8 b = bgTile(x, y), f = fgTile(x, y);
634 if (b) blitSurface2x(frame, VIS_X+dx*20, VIS_Y+dy*10, banks[CONST_BANK_BG_TILES].spr[b-1][0]);
635 if (f) blitSurface2x(frame, VIS_X+dx*20, VIS_Y+dy*10, banks[CONST_BANK_FG_TILES].spr[f-1][0]);
638 } else {
639 for (int dy = 0; dy < mapHeight; ++dy) {
640 for (int dx = 1; dx < mapWidth; ++dx) {
641 Uint8 b = bgTile(dx, dy), f = fgTile(dx, dy), t = 8;
643 if (f == 0) {
644 if (b >= 1 && b <= 8) t = b-1; else t = 8;
645 } else {
646 t = f+8;
648 if (banks[CONST_BANK_MAP_TILES].spr[t][0]) {
649 blitSurface2x(frame, VIS_X+dx*6, VIS_Y+dy*4, banks[CONST_BANK_MAP_TILES].spr[t][0]);
650 } else {
651 blitSurface2x(frame, VIS_X+dx*6, VIS_Y+dy*4, banks[CONST_BANK_MAP_TILES].spr[0][0]);
659 static void levelDrawSprites (SDL_Surface *frame) {
660 if (!inMinimap) {
661 int scrX = vmGVars[GVAR_SCR_X], scrY = vmGVars[GVAR_SCR_Y];
662 int py = vmGetTVar(0, TVAR_POS_Y)-scrY;
664 for (int cc = 0; cc <= 2; ++cc) {
665 for (int f = cc==1?1:0; f <= vmLastThread(); ++f) {
666 if (vmIsThreadAlive(f) && !vmIsSuspendedThread(f)) {
667 int b = vmGetTVar(f, TVAR_SPR_BANK);
668 int s = vmGetTVar(f, TVAR_SPR_NUM);
669 int d = vmGetTVar(f, TVAR_SPR_DIR);
671 if (d >= 0 && d <= 1 && s >= 0 && b >= 0 && b <= 255 && s < banks[b].count && banks[b].spr[s][d]) {
672 SDL_Surface *sf = banks[b].spr[s][d];
673 int x = vmGetTVar(f, TVAR_POS_X)-scrX;
674 int y = vmGetTVar(f, TVAR_POS_Y)-scrY;
675 int tx = vmGetTVar(f, TVAR_POS_TX);
676 int ty = vmGetTVar(f, TVAR_POS_TY);
677 int si = vmGetTVar(f, TVAR_SPR_ITEM);
679 if (cc == 0 && b == CONST_BANK_ITEMS) continue;
680 if (cc == 1 && b != CONST_BANK_ITEMS) continue;
681 if (cc == 2) {
682 if (b != CONST_BANK_PROF) {
683 if (b != CONST_BANK_ITEMS) continue;
684 if (y < py) continue;
687 if (b == CONST_BANK_IM_PROF || b == CONST_BANK_PROF) tx -= 5;
688 blitSurface2x(frame, VIS_X+x*20+tx, VIS_Y+y*10+ty-sf->h/2, sf);
689 if (si > 0 && si < banks[CONST_BANK_ITEMS].count) {
690 sf = banks[CONST_BANK_ITEMS].spr[si][0];
691 blitSurface2x(frame, VIS_X+x*20+tx, VIS_Y+y*10+ty-sf->h/2-24, sf);
697 } else {
698 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]);
699 for (int f = 1; f < VM_MAX_THREADS; ++f) {
700 if (vmIsThreadAlive(f) && !vmIsSuspendedThread(f)) {
701 int i = vmGetTVar(f, TVAR_ITEM_ID);
702 if (i > 0 && i <= 255) {
703 SDL_Surface *sf = banks[CONST_BANK_MAP_ITEMS].spr[i][0];
704 int x = vmGetTVar(f, TVAR_POS_X);
705 int y = vmGetTVar(f, TVAR_POS_Y);
707 blitSurface2x(frame, VIS_X+(x+0)*6, VIS_Y+(y-2)*4, sf);
715 static void levelDrawCode (SDL_Surface *frame) {
716 int pos = vmGVars[GVAR_LEVEL_CODE_OFS], len = vmGVars[GVAR_LEVEL_CODE_LEN], x;
717 char buf[128];
719 if (len > 255) len = 255;
720 if (pos < 0 || len < 1 || pos+len > vmCodeSize) return;
721 x = 320-len*8;
722 sprintf(buf, "%d", curLevel);
723 x -= strlen(buf)*8;
724 for (; len > 0; x += 8, ++pos, --len) drawChar(frame, vmCode[pos], x, 0, 3);
728 static void levelDrawItemName (SDL_Surface *frame) {
729 int pos = vmGVars[GVAR_ITEM_NAME_OFS], len = vmGVars[GVAR_ITEM_NAME_LEN], x = 0;
731 if (pos < 0) return;
732 if (len < 0 || len > 32) len = 32;
733 for (; len > 0 && pos < vmCodeSize && vmCode[pos]; x += 8, ++pos, --len) drawChar(frame, vmCode[pos], x, 0, 3);
737 static void levelDrawMisc (SDL_Surface *frame) {
738 if (demorecmode < 0) {
739 if (SDL_GetTicks()%1000 < 500) drawChar(frame, 'R', 0, 8, 3);
740 } else if (demorecmode > 0) {
741 if (SDL_GetTicks()%1000 < 500) drawChar(frame, 'D', 0, 8, 3);
746 static void drawSpriteBank (SDL_Surface *frame, SpriteBank *bank) {
747 SDL_Rect dst;
748 int x = 0, y = 0, maxh = 0;
750 SDL_SetClipRect(frame, NULL);
751 dst.x = 0;
752 dst.y = 0;
753 dst.w = frame->w;
754 dst.h = frame->h;
755 SDL_FillRect(frame, &dst, palette[3]);
757 for (int num = 0; num < bank->count; ++num) {
758 SDL_Surface *s = bank->spr[num][0];
760 if (s != NULL) {
761 char buf[16];
762 int w = s->w/2;
764 sprintf(buf, "%d", num);
765 if (w < strlen(buf)*8+2) w = strlen(buf)*8+2;
766 if (x+w > 320) {
767 x = 0;
768 y += maxh;
769 maxh = 0;
771 if (maxh < s->h/2+1) maxh = s->h/2+1;
772 blitSurface2x(frame, x, y, s);
773 drawString(frame, buf, x, y, 1);
774 x += w+1;
780 static void frmGameDraw (SDL_Surface *frame) {
781 SDL_Rect rc;
783 if (vmPaused && doSave) {
784 if (doSave < 0) {
785 if (loadGame() != 0) fatal("can't load game, VM is inconsistent, dying.");
786 } else {
787 saveGame();
789 doSave = 0;
790 vmPaused = inMinimap;
793 SDL_SetClipRect(frame, NULL);
795 if (curLevel >= 0) {
796 char buf[8];
797 //int pi;
799 if (!inMinimap) {
800 int levelback = vmGVars[GVAR_LEVEL_BACKPIC];
802 if (levelback < 1 || levelback > 7) levelback = 1;
803 blitSurface(frame, 0, 0, backs[levelback]);
804 } else {
805 blitSurface(frame, 0, 0, backs[1]);
807 drawString(frame, levelname, lnamex, 0, 0x76);
808 levelDrawItemName(frame);
809 levelDrawCode(frame);
810 levelDrawMisc(frame);
813 pi = vmGVars[GVAR_PROF_ITEM];
814 if (vmIsThreadAlive(pi) && vmIsSuspendedThread(pi)) {
815 pi = vmGetTVar(pi, TVAR_ITEM_ID);
816 if (pi > 0 && pi < 11) {
817 drawString(frame, itemNames[pi], 0, 0, 1);
822 sprintf(buf, "%d", curLevel+1);
823 drawString(frame, buf, 320-strlen(buf)*8, 0, 1);
825 rc.x = VIS_X*2;
826 rc.y = VIS_Y*2;
827 rc.w = VIS_WIDTH*20*2;
828 rc.h = VIS_HEIGHT*10*2;
829 SDL_SetClipRect(frame, &rc);
831 levelDrawMap(frame);
832 levelDrawSprites(frame);
833 } else {
834 SDL_Rect dst;
836 dst.x = 0;
837 dst.y = 0;
838 dst.w = frame->w;
839 dst.h = frame->h;
840 SDL_FillRect(frame, &dst, palette[0]);
843 SDL_SetClipRect(frame, NULL);
844 levelDrawMisc(frame);
846 if (goobers) {
847 for (int f = 84; f <= 90; ++f) if (fkeys[f-83]) drawSpriteBank(frame, &banks[f]);
852 ////////////////////////////////////////////////////////////////////////////////
853 #define ROOM_SCRIPTS_MARK " room script labels starts here "
856 static void loadMapScript (ResFile *resfile) {
857 VMLabelInfo *l;
858 int csz;
860 l = vmLabelAddMark(ROOM_SCRIPTS_MARK);
861 l->value = vmCodeSize;
862 if ((csz = loadCodeFile(resfile, vmCodeSize, 94+curLevel)) > 1) {
863 vmCodeSize += csz;
868 static void unloadMapScript (void) {
869 VMLabelInfo *l, *ucl;
871 while ((l = vmFindMark(ROOM_SCRIPTS_MARK)) != NULL) {
872 ucl = vmFindLabel("entry_local_room_script_onunload");
873 if (ucl != NULL && ucl->type == LB_CODE) vmExecuteBSR(0, ucl->value, 0);
874 vmCodeSize = l->value;
875 vmFreeLabelsUntilMark(ROOM_SCRIPTS_MARK);
878 ucl = vmFindLabel("entry_room_onunload");
879 if (ucl != NULL && ucl->type == LB_CODE) vmExecuteBSR(0, ucl->value, 0);
883 ////////////////////////////////////////////////////////////////////////////////
884 static int loadLevelInternal (ResFile *resfile, int lidx) {
885 message[0] = 0;
886 unloadMapScript();
888 vmGVars[GVAR_VIS_WIDTH] = VIS_WIDTH;
889 vmGVars[GVAR_VIS_HEIGHT] = VIS_HEIGHT;
890 vmGVars[GVAR_ITEM_NAME_OFS] = 0;
891 vmGVars[GVAR_ITEM_NAME_LEN] = 0;
892 levelname[0] = 0;
893 levelback = 1;
895 if (levelData != NULL) free(levelData); levelData = NULL; levelDataSize = 0;
896 if (bmap != NULL) free(bmap); bmap = NULL;
897 if (fmap != NULL) free(fmap); fmap = NULL;
898 if (xflags != NULL) free(xflags); xflags = NULL;
900 curLevel = -1;
901 if (lidx < 0 || lidx > 73) return -1;
902 if ((levelData = loadResFile(resfile, lidx+9, &levelDataSize)) == NULL) return -1;
903 curLevel = lidx;
904 loadMapScript(resfile);
905 return levelDataSize;
909 ////////////////////////////////////////////////////////////////////////////////
910 static void activateMinimap (void) {
911 inMinimap = 1;
912 vmPaused = 1;
913 doSave = 0;
917 ////////////////////////////////////////////////////////////////////////////////
918 static int gameRST (int tid, int opcode, int argc, int argv[], int *argp[]) {
919 if (argv[0] == CONST_FRST_MINIMAP) {
920 activateMinimap();
921 } else if (argv[0] == CONST_FRST_OPEN_LEVEL_FILE) {
922 // levelnum
923 //if (vmLoadArgs(tid, 2, argv, argp, argc, argv, argp) != 0) fatal("out of args");
924 if (argc < 2) fatal("out of args in FRST_LOAD_LEVEL");
925 vmGVars[GVAR_RST_RESULT] = loadLevelInternal(&resfile, argv[1]);
926 if (goobers) fprintf(stderr, "FRST_OPEN_LEVEL_FILE(%d): %d\n", argv[1], vmGVars[GVAR_RST_RESULT]);
927 } else if (argv[0] == CONST_FRST_GET_LEVEL_DATA_SIZE) {
928 vmGVars[GVAR_RST_RESULT] = levelDataSize;
929 } else if (argv[0] == CONST_FRST_GET_LEVEL_LOADER) {
930 VMLabelInfo *ucl = vmFindLabel("entry_local_room_loader");
932 vmGVars[GVAR_RST_RESULT] = (ucl != NULL && ucl->type == LB_CODE) ? ucl->value : -1;
933 } else if (argv[0] == CONST_FRST_GET_LEVEL_INITER) {
934 VMLabelInfo *ucl = vmFindLabel("entry_local_room_onload");
936 vmGVars[GVAR_RST_RESULT] = (ucl != NULL && ucl->type == LB_CODE) ? ucl->value : -1;
937 } else if (argv[0] == CONST_FRST_GET_LEVEL_FILE_BYTE) {
938 if (argc < 2) fatal("out of args in FRST_GET_LEVEL_FILE_BYTE");
939 if (argv[1] < 0 || argv[1] >= levelDataSize) fatal("invalid offset in FRST_GET_LEVEL_FILE_BYTE: %d", argv[1]);
940 vmGVars[GVAR_RST_RESULT] = levelData[argv[1]];
941 } else if (argv[0] == CONST_FRST_GET_LEVEL_FILE_WORD) {
942 if (argc < 2) fatal("out of args in FRST_GET_LEVEL_FILE_WORD");
943 if (argv[1] < 0 || argv[1]+1 >= levelDataSize) fatal("invalid offset in FRST_GET_LEVEL_FILE_WORD: %d", argv[1]);
944 vmGVars[GVAR_RST_RESULT] = levelData[argv[1]+1];
945 vmGVars[GVAR_RST_RESULT] <<= 8;
946 vmGVars[GVAR_RST_RESULT] |= levelData[argv[1]];
947 if (vmGVars[GVAR_RST_RESULT] >= 32768) vmGVars[GVAR_RST_RESULT] -= 65536;
948 } else if (argv[0] == CONST_FRST_SET_LEVEL_SIZE) {
949 if (argc != 3) fatal("out of args in FRST_SET_LEVEL_SIZE");
950 if (argv[1] < 1 || argv[1] > 32700 || argv[2] < 1 || argv[2] > 32700) fatal("invalid dimensions in FRST_SET_LEVEL_SIZE: %dx%d", argv[1], argv[2]);
951 if (bmap != NULL) free(bmap); bmap = NULL;
952 if (fmap != NULL) free(fmap); fmap = NULL;
953 if (xflags != NULL) free(xflags); xflags = NULL;
954 fmap = calloc(1, argv[1]*argv[2]); if (fmap == NULL) fatal("out of memory in FRST_SET_LEVEL_SIZE");
955 bmap = calloc(1, argv[1]*argv[2]); if (bmap == NULL) fatal("out of memory in FRST_SET_LEVEL_SIZE");
956 xflags = calloc(1, argv[1]); if (xflags == NULL) fatal("out of memory in FRST_SET_LEVEL_SIZE");
957 vmGVars[GVAR_MAP_WIDTH] = argv[1];
958 vmGVars[GVAR_MAP_HEIGHT] = argv[2];
959 } else if (argv[0] == CONST_FRST_SET_LEVEL_NAME) {
960 int pos = 0, len = sizeof(levelname)-1, lp = 0;
961 char buf[32];
963 switch (argc) {
964 case 1: fatal("out of args in FRST_SET_LEVEL_NAME");
965 case 2: pos = argv[1]; break;
966 case 3: pos = argv[1]; len = argv[2]; break;
968 memset(levelname, 0, sizeof(levelname));
969 if (len > sizeof(levelname)-1) len = sizeof(levelname)-1;
970 if (pos > 0) {
971 while (len > 0 && pos < vmCodeSize && vmCode[pos]) {
972 levelname[lp++] = vmCode[pos++];
973 --len;
975 } else {
976 pos = 0-pos+1;
977 while (len > 0 && pos < levelDataSize && levelData[pos]) {
978 levelname[lp++] = levelData[pos++];
979 --len;
982 lnamex = (320-strlen(levelname)*8)/2;
983 sprintf(buf, "%d", curLevel+1);
984 while (lnamex+strlen(levelname)*8+strlen(buf)*8+6*8 > 320) lnamex -= 8;
985 } else {
986 if (gameRSTCB) return gameRSTCB(tid, opcode, argc, argv, argp);
987 fatal("invalid RST: %d", argv[0]);
989 return 0; // continue
993 ////////////////////////////////////////////////////////////////////////////////
994 void setMainLoopGame (void) {
995 frameCB = frmGameDraw;
996 keyCB = frmGameKey;
997 gameRSTCB = gameRST;
998 beforeVMCB = demoCB;
999 vmMapGetCB = mapGet;
1000 vmMapSetCB = mapSet;
1002 setSeed(time(NULL));
1003 vmPaused = 0;
1004 inMinimap = 0;
1005 doSave = 0;
1006 //curLevel = -1;
1007 //demorecmode = 0;
1008 message[0] = 0;
1009 memset(fkeys, 0, sizeof(fkeys));
1010 memset(gamekeys, 0, sizeof(gamekeys));