fixes to resource unpacker and resource builder
[awish.git] / src / game.c
blob969467e4729a2249beb2e51c05ea9212b8587bf7
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 <unistd.h>
20 #include "game.h"
22 #include "resfile.h"
23 #include "video.h"
24 #include "mainloop.h"
25 #include "vm.h"
26 #include "gameglobals.h"
27 #include "title.h"
30 extern int goobers;
34 ////////////////////////////////////////////////////////////////////////////////
35 #define MAP_WIDTH (44)
36 #define MAP_HEIGHT (41)
37 #define VIS_WIDTH (15)
38 #define VIS_HEIGHT (17)
39 #define VIS_X (10)
40 #define VIS_Y (20)
43 ////////////////////////////////////////////////////////////////////////////////
44 static const char *itemNames[] = {
45 "",
46 "key",
47 "fan",
48 "dynamite",
49 "drill",
50 "umbrella",
51 "monster",
52 "rocket",
53 "apple",
54 "vial",
55 "gloves"
59 enum {
60 ITEM_NOTHING,
61 ITEM_KEY,
62 ITEM_FAN,
63 ITEM_DYNAMITE,
64 ITEM_DRILL,
65 ITEM_UMBRELLA,
66 ITEM_HARRY,
67 ITEM_ROCKET,
68 ITEM_APPLE,
69 ITEM_VIAL,
70 ITEM_GLOVES,
71 ITEM_MAX
75 typedef struct {
76 int type; // 0: inactive
77 int x, y; // in map
78 int frame; // animation frame
79 int fframe, lframe; // first and last anim frame
80 int animated;
81 int tid;
82 } Item;
85 ////////////////////////////////////////////////////////////////////////////////
86 static int curLevel;
87 static char levelname[257];
88 static int lnamex;
89 static int levelback;
90 static Uint8 bmap[MAP_HEIGHT][MAP_WIDTH];
91 static Uint8 fmap[MAP_HEIGHT][MAP_WIDTH];
92 static Uint8 xflags[MAP_WIDTH];
93 static int fkeys[10];
94 static int inMinimap;
96 static int doSave = 0;
99 ////////////////////////////////////////////////////////////////////////////////
100 static void frmGameKey (SDL_KeyboardEvent *key) {
101 if (key->type == SDL_KEYDOWN) {
102 switch (key->keysym.sym) {
103 case SDLK_ESCAPE:
104 if (inMinimap) {
105 inMinimap = 0;
106 doSave = 0;
107 vmPaused = 0;
108 vmGVars[GVAR_KEY_MINIMAP] = 0;
110 break;
111 case SDLK_F12:
112 vmGVars[GVAR_KEY_QUIT] = 1;
113 break;
114 case SDLK_p:
115 vmGVars[GVAR_KEY_MINIMAP] = 1;
116 break;
117 case SDLK_r:
118 if ((key->keysym.mod&(KMOD_CTRL)) != 0) vmGVars[GVAR_KEY_RESTART] = 1;
119 break;
120 case SDLK_s:
121 if (goobers && (key->keysym.mod&(KMOD_CTRL)) != 0) { doSave = 1; vmPaused = 1; }
122 break;
123 case SDLK_l:
124 if (goobers && (key->keysym.mod&(KMOD_CTRL)) != 0) { doSave = -1; vmPaused = 1; }
125 break;
126 default: ;
130 switch (key->keysym.sym) {
131 case SDLK_LEFT: case SDLK_KP4: vmGVars[GVAR_KEY_LEFT] = (key->type == SDL_KEYDOWN); break;
132 case SDLK_RIGHT: case SDLK_KP6: vmGVars[GVAR_KEY_RIGHT] = (key->type == SDL_KEYDOWN); break;
133 case SDLK_UP: case SDLK_KP8: vmGVars[GVAR_KEY_UP] = (key->type == SDL_KEYDOWN); break;
134 case SDLK_DOWN: case SDLK_KP2: vmGVars[GVAR_KEY_DOWN] = (key->type == SDL_KEYDOWN); break;
135 case SDLK_SPACE: case SDLK_KP0: vmGVars[GVAR_KEY_TAKE] = (key->type == SDL_KEYDOWN); break;
136 case SDLK_RETURN: case SDLK_KP_PERIOD: vmGVars[GVAR_KEY_USE] = (key->type == SDL_KEYDOWN); break;
137 default: ;
140 if (goobers) {
141 switch (key->keysym.sym) {
142 case SDLK_f: vmGVars[GVAR_KEY_FALL_CHEAT] = (key->type == SDL_KEYDOWN); break;
143 case SDLK_w: vmGVars[GVAR_KEY_WALK_CHEAT] = (key->type == SDL_KEYDOWN); break;
144 case SDLK_KP_MINUS: vmGVars[GVAR_KEY_PLEV_CHEAT] = (key->type == SDL_KEYDOWN); break;
145 case SDLK_KP_PLUS: vmGVars[GVAR_KEY_NLEV_CHEAT] = (key->type == SDL_KEYDOWN); break;
147 case SDLK_F1: fkeys[1] = (key->type == SDL_KEYDOWN); break;
148 case SDLK_F2: fkeys[2] = (key->type == SDL_KEYDOWN); break;
149 case SDLK_F3: fkeys[3] = (key->type == SDL_KEYDOWN); break;
150 case SDLK_F4: fkeys[4] = (key->type == SDL_KEYDOWN); break;
151 case SDLK_F5: fkeys[5] = (key->type == SDL_KEYDOWN); break;
152 case SDLK_F6: fkeys[6] = (key->type == SDL_KEYDOWN); break;
153 case SDLK_F7: fkeys[7] = (key->type == SDL_KEYDOWN); break;
154 case SDLK_F8: fkeys[8] = (key->type == SDL_KEYDOWN); break;
155 case SDLK_F9: fkeys[9] = (key->type == SDL_KEYDOWN); break;
156 default: ;
162 ////////////////////////////////////////////////////////////////////////////////
163 static int loadLevelInternal (ResFile *resfile, int lidx) {
164 Uint8 *lvl;
165 int sz, res = -1, pos;
166 static Item items[256];
167 static int itemCount;
168 static int scrX, scrY;
169 static int profX, profY;
170 char buf[64];
172 vmGVars[GVAR_MAP_WIDTH] = MAP_WIDTH;
173 vmGVars[GVAR_MAP_HEIGHT] = MAP_HEIGHT;
174 vmGVars[GVAR_VIS_WIDTH] = VIS_WIDTH;
175 vmGVars[GVAR_VIS_HEIGHT] = VIS_HEIGHT;
177 if ((lvl = loadResFile(resfile, lidx+9, &sz)) == NULL) return -1;
178 if (sz < 3700) goto quit;
179 if (lvl[0] > 25) goto quit;
180 memset(levelname, 0, sizeof(levelname));
181 if (lvl[0] > 0) memcpy(levelname, lvl+1, lvl[0]);
182 lnamex = (320-strlen(levelname)*8)/2;
183 sprintf(buf, "%d", lidx+1);
184 while (lnamex+strlen(levelname)*8+strlen(buf)*8+6*8 > 320) lnamex -= 8;
185 levelback = lvl[26];
186 if (levelback < 1 || levelback > 7) levelback = 1;
187 profX = lvl[27];
188 profY = lvl[28];
189 vmSetTVar(0, TVAR_POS_X, profX);
190 vmSetTVar(0, TVAR_POS_Y, profY);
191 scrX = lvl[29];
192 scrY = lvl[30]; if (scrY > 0) --scrY;
193 vmGVars[GVAR_SCR_X] = scrX;
194 vmGVars[GVAR_SCR_Y] = scrY;
195 pos = 31;
196 memset(bmap, 0, sizeof(bmap));
197 memset(fmap, 0, sizeof(fmap));
198 memset(xflags, 0, sizeof(xflags));
199 for (int x = 1; x < MAP_WIDTH; ++x) {
200 for (int y = 0; y < MAP_HEIGHT; ++y) {
201 bmap[y][x] = lvl[pos++];
204 for (int x = 1; x < MAP_WIDTH; ++x) {
205 for (int y = 0; y < MAP_HEIGHT; ++y) {
206 fmap[y][x] = lvl[pos++];
208 pos += 3; // skip unknown bytes
211 itemCount = lvl[pos++]+1; // at least one item is always here
212 for (int f = 0; f < itemCount; ++f) {
213 int tid;
215 items[f].x = lvl[pos++];
216 items[f].y = lvl[pos++];
217 items[f].fframe = lvl[pos++];
218 items[f].lframe = lvl[pos++];
219 items[f].frame = lvl[pos++];
220 items[f].animated = lvl[pos++];
221 if (!items[f].animated) items[f].frame = items[f].fframe;
222 ++pos; // 0xff
223 pos += 4; // dunno
224 items[f].type = lvl[pos++];
225 ++pos; // dunno
226 // spawn thread
227 tid = vmNewThread(CODE_ENTRY_ITEM);
228 if (tid < 0) goto quit;
229 vmSetTVar(tid, TVAR_ITEM_ID, items[f].type);
230 vmSetTVar(tid, TVAR_ANIMATED, items[f].animated);
231 vmSetTVar(tid, TVAR_AFIRST_FRAME, items[f].fframe);
232 vmSetTVar(tid, TVAR_ALAST_FRAME, items[f].lframe);
233 vmSetTVar(tid, TVAR_AFRAME, items[f].frame);
234 if (items[f].type == 6) vmSetTVar(tid, TVAR_AFRAME, lvl[pos-6]);
235 vmSetTVar(tid, TVAR_POS_X, items[f].x);
236 vmSetTVar(tid, TVAR_POS_Y, items[f].y);
237 vmSetTVar(tid, TVAR_POS_TX, 0);
238 vmSetTVar(tid, TVAR_POS_TY, 0);
239 vmSetTVar(tid, TVAR_SPR_BANK, CONST_BANK_ITEMS);
240 vmSetTVar(tid, TVAR_SPR_NUM, items[f].frame);
241 vmSetTVar(tid, TVAR_SPR_DIR, 0);
243 curLevel = lidx;
244 inMinimap = 0;
245 doSave = 0;
246 res = 0;
247 quit:
248 free(lvl);
249 return res;
253 ////////////////////////////////////////////////////////////////////////////////
254 static inline Uint8 fgTile (int x, int y) {
255 return (x >= 0 && y >= 0 && x < MAP_WIDTH && y < MAP_HEIGHT) ? fmap[y][x]&0x0f : 0;
259 static inline Uint8 bgTile (int x, int y) {
260 if (y == -666) return (x >= 0 && x < MAP_WIDTH) ? xflags[x] : 0;
261 return (x >= 0 && y >= 0 && x < MAP_WIDTH && y < MAP_HEIGHT) ? bmap[y][x]&0x0f : 0;
265 static int mapGet (int tid, int fg, int x, int y) {
266 return fg ? fgTile(x, y) : bgTile(x, y);
270 static inline void setFGTile (int x, int y, Uint8 tile) {
271 if (x >= 0 && y >= 0 && x < MAP_WIDTH && y < MAP_HEIGHT) fmap[y][x] = (fmap[y][x]&0xf0)|(tile&0x0f);
275 static inline void setBGTile (int x, int y, Uint8 tile) {
276 if (y == -666 && x >= 0 && x < MAP_WIDTH) xflags[x] = tile;
277 if (x >= 0 && y >= 0 && x < MAP_WIDTH && y < MAP_HEIGHT) bmap[y][x] = (bmap[y][x]&0xf0)|(tile&0x0f);
281 static void mapSet (int tid, int fg, int x, int y, int tile) {
282 if (fg) setFGTile(x, y, tile); else setBGTile(x, y, tile);
286 ////////////////////////////////////////////////////////////////////////////////
287 // cliprect should be set
288 static void levelDrawMap (SDL_Surface *frame) {
289 if (!inMinimap) {
290 int scrX = vmGVars[GVAR_SCR_X], scrY = vmGVars[GVAR_SCR_Y];
292 for (int dy = 0; dy < VIS_HEIGHT; ++dy) {
293 for (int dx = 0; dx < VIS_WIDTH; ++dx) {
294 int x = scrX+dx, y = scrY+dy;
295 Uint8 b = bgTile(x, y), f = fgTile(x, y);
297 if (b) blitSurface2x(frame, VIS_X+dx*20, VIS_Y+dy*10, banks[CONST_BANK_BG_TILES].spr[b-1][0]);
298 if (f) blitSurface2x(frame, VIS_X+dx*20, VIS_Y+dy*10, banks[CONST_BANK_FG_TILES].spr[f-1][0]);
301 } else {
302 for (int dy = 0; dy < MAP_HEIGHT; ++dy) {
303 for (int dx = 1; dx < MAP_WIDTH; ++dx) {
304 Uint8 b = bgTile(dx, dy), f = fgTile(dx, dy), t = 8;
306 if (f == 0) {
307 if (b >= 1 && b <= 8) t = b-1; else t = 8;
308 } else {
309 t = f+8;
311 if (banks[CONST_BANK_MAP_TILES].spr[t][0]) {
312 blitSurface2x(frame, VIS_X+dx*6, VIS_Y+dy*4, banks[CONST_BANK_MAP_TILES].spr[t][0]);
320 static void levelDrawSprites (SDL_Surface *frame) {
321 if (!inMinimap) {
322 int scrX = vmGVars[GVAR_SCR_X], scrY = vmGVars[GVAR_SCR_Y];
323 int py = vmGetTVar(0, TVAR_POS_Y)-scrY;
325 for (int cc = 0; cc <= 2; ++cc) {
326 for (int f = cc==1?1:0; f <= vmLastThread(); ++f) {
327 if (vmIsThreadAlive(f) && !vmIsSuspendedThread(f)) {
328 int b = vmGetTVar(f, TVAR_SPR_BANK);
329 int s = vmGetTVar(f, TVAR_SPR_NUM);
330 int d = vmGetTVar(f, TVAR_SPR_DIR);
332 if (d >= 0 && d <= 1 && s >= 0 && b >= 0 && b <= 255 && s < banks[b].count && banks[b].spr[s][d]) {
333 SDL_Surface *sf = banks[b].spr[s][d];
334 int x = vmGetTVar(f, TVAR_POS_X)-scrX;
335 int y = vmGetTVar(f, TVAR_POS_Y)-scrY;
336 int tx = vmGetTVar(f, TVAR_POS_TX);
337 int ty = vmGetTVar(f, TVAR_POS_TY);
338 int si = vmGetTVar(f, TVAR_SPR_ITEM);
340 if (cc == 0 && b == CONST_BANK_ITEMS) continue;
341 if (cc == 1 && b != CONST_BANK_ITEMS) continue;
342 if (cc == 2) {
343 if (b != CONST_BANK_PROF) {
344 if (b != CONST_BANK_ITEMS) continue;
345 if (y < py) continue;
348 if (b == CONST_BANK_IM_PROF || b == CONST_BANK_PROF) tx -= 5;
349 blitSurface2x(frame, VIS_X+x*20+tx, VIS_Y+y*10+ty-sf->h/2, sf);
350 if (si > 0 && si < banks[CONST_BANK_ITEMS].count) {
351 sf = banks[CONST_BANK_ITEMS].spr[si][0];
352 blitSurface2x(frame, VIS_X+x*20+tx, VIS_Y+y*10+ty-sf->h/2-24, sf);
358 } else {
359 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]);
360 for (int f = 1; f < VM_MAX_THREADS; ++f) {
361 if (vmIsThreadAlive(f) && !vmIsSuspendedThread(f)) {
362 int i = vmGetTVar(f, TVAR_ITEM_ID);
363 if (i > 0 && i < ITEM_MAX) {
364 SDL_Surface *sf = banks[CONST_BANK_MAP_ITEMS].spr[i][0];
365 int x = vmGetTVar(f, TVAR_POS_X);
366 int y = vmGetTVar(f, TVAR_POS_Y);
368 blitSurface2x(frame, VIS_X+(x+0)*6, VIS_Y+(y-2)*4, sf);
376 static void levelDrawCode (SDL_Surface *frame) {
377 int pos = vmGVars[GVAR_LEVEL_CODE_OFS], len = vmGVars[GVAR_LEVEL_CODE_LEN], x;
378 char buf[128];
380 if (pos < 0 || len < 1 || pos+len > vmCodeSize) return;
381 x = 320-len*8;
382 sprintf(buf, "%d", curLevel);
383 x -= strlen(buf)*8;
384 for (; len > 0; x += 8, ++pos, --len) drawChar(frame, vmCode[pos], x, 0, 3);
388 static void drawSpriteBank (SDL_Surface *frame, SpriteBank *bank) {
389 SDL_Rect dst;
390 int x = 0, y = 0, maxh = 0;
392 SDL_SetClipRect(frame, NULL);
393 dst.x = 0;
394 dst.y = 0;
395 dst.w = frame->w;
396 dst.h = frame->h;
397 SDL_FillRect(frame, &dst, palette[3]);
399 for (int num = 0; num < bank->count; ++num) {
400 SDL_Surface *s = bank->spr[num][0];
402 if (s != NULL) {
403 char buf[16];
404 int w = s->w/2;
406 sprintf(buf, "%d", num);
407 if (w < strlen(buf)*8+2) w = strlen(buf)*8+2;
408 if (x+w > 320) {
409 x = 0;
410 y += maxh;
411 maxh = 0;
413 if (maxh < s->h/2+1) maxh = s->h/2+1;
414 blitSurface2x(frame, x, y, s);
415 drawString(frame, buf, x, y, 1);
416 x += w+1;
422 static int readBuf (FILE *fl, void *buf, int len) {
423 unsigned char *c = (unsigned char *)buf;
425 while (len-- > 0) {
426 unsigned char b;
428 if (fread(&b, 1, 1, fl) != 1) return -1;
429 b ^= 42;
430 *c++ = b;
432 return 0;
436 static int writeBuf (FILE *fl, const void *buf, int len) {
437 const unsigned char *c = (const unsigned char *)buf;
439 while (len-- > 0) {
440 unsigned char b = *c++;
442 b ^= 42;
443 if (fwrite(&b, 1, 1, fl) != 1) return -1;
445 return 0;
449 static int readDW (FILE *fl, int *v) {
450 int t[4];
452 for (int f = 0; f < 4; ++f) {
453 unsigned char b;
455 if (fread(&b, 1, 1, fl) != 1) return -1;
456 t[f] = b^42;
459 if (v) {
460 *v = 0;
461 for (int f = 0; f < 4; ++f) *v |= t[f]<<(8*f);
463 return 0;
467 static int writeDW (FILE *fl, int v) {
468 for (int f = 0; f < 4; ++f) {
469 unsigned char b;
471 b = v&0xff;
472 b ^= 42;
473 if (fwrite(&b, 1, 1, fl) != 1) return -1;
474 v >>= 8;
476 return 0;
480 static void frmGameDraw (SDL_Surface *frame) {
481 SDL_Rect rc;
482 char buf[8];
483 int pi;
485 if (vmPaused && doSave) {
486 FILE *fl;
488 if (doSave < 0) {
489 // load
490 fl = fopen("awish.sav", "rb");
491 if (fl != NULL) {
492 int ok = 0;
494 if (vmLoadState(fl) == 0) {
495 if (readDW(fl, &inMinimap) != 0) goto stop_loading;
496 if (readDW(fl, &curLevel) != 0) goto stop_loading;
497 if (readBuf(fl, levelname, sizeof(levelname)) != 0) goto stop_loading;
498 if (readDW(fl, &lnamex) != 0) goto stop_loading;
499 if (readDW(fl, &levelback) != 0) goto stop_loading;
500 if (readBuf(fl, bmap, sizeof(bmap)) != 0) goto stop_loading;
501 if (readBuf(fl, fmap, sizeof(fmap)) != 0) goto stop_loading;
502 if (readBuf(fl, xflags, sizeof(xflags)) != 0) goto stop_loading;
503 ok = 1;
505 stop_loading:
506 fclose(fl);
507 if (!ok) {
508 vmPaused = 0;
509 if (vmInitialize() != 0) fatal("can't init VM");
510 vmSetPC(0, CODE_ENTRY_TITLE);
511 if (vmExecuteBSR(0, CODE_ENTRY_MAIN_INIT, 0) != 0) fatal("can't initialize game");
512 setMainLoopTitle();
513 frameCB = NULL;
514 keyCB = NULL;
515 return;
518 } else {
519 // save
520 fl = fopen("awish.sav", "wb");
521 if (fl != NULL) {
522 int ok = 0;
524 if (vmSaveState(fl) == 0) {
525 if (writeDW(fl, inMinimap) != 0) goto stop_saving;
526 if (writeDW(fl, curLevel) != 0) goto stop_saving;
527 if (writeBuf(fl, levelname, sizeof(levelname)) != 0) goto stop_saving;
528 if (writeDW(fl, lnamex) != 0) goto stop_saving;
529 if (writeDW(fl, levelback) != 0) goto stop_saving;
530 if (writeBuf(fl, bmap, sizeof(bmap)) != 0) goto stop_saving;
531 if (writeBuf(fl, fmap, sizeof(fmap)) != 0) goto stop_saving;
532 if (writeBuf(fl, xflags, sizeof(xflags)) != 0) goto stop_saving;
533 ok = 1;
535 stop_saving:
536 fclose(fl);
537 if (!ok) unlink("awish.sav");
540 doSave = 0;
541 vmPaused = inMinimap;
544 SDL_SetClipRect(frame, NULL);
546 if (!inMinimap) {
547 blitSurface(frame, 0, 0, backs[levelback]);
548 } else {
549 blitSurface(frame, 0, 0, backs[1]);
551 drawString(frame, levelname, lnamex, 0, 0x76);
552 levelDrawCode(frame);
554 pi = vmGVars[GVAR_PROF_ITEM];
555 if (vmIsThreadAlive(pi) && vmIsSuspendedThread(pi)) {
556 pi = vmGetTVar(pi, TVAR_ITEM_ID);
557 if (pi > 0 && pi < 11) {
558 drawString(frame, itemNames[pi], 0, 0, 1);
562 sprintf(buf, "%d", curLevel+1);
563 drawString(frame, buf, 320-strlen(buf)*8, 0, 1);
565 rc.x = VIS_X*2;
566 rc.y = VIS_Y*2;
567 rc.w = VIS_WIDTH*20*2;
568 rc.h = VIS_HEIGHT*10*2;
569 SDL_SetClipRect(frame, &rc);
571 levelDrawMap(frame);
572 levelDrawSprites(frame);
574 if (goobers) {
575 for (int f = 84; f <= 90; ++f) if (fkeys[f-83]) drawSpriteBank(frame, &banks[f]);
580 ////////////////////////////////////////////////////////////////////////////////
581 int loadLevel (int lidx) {
582 if (lidx < 0 || lidx > 73) return -1;
583 return loadLevelInternal(&resfile, lidx);
587 void activateMinimap (void) {
588 inMinimap = 1;
589 vmPaused = 1;
590 doSave = 0;
594 ////////////////////////////////////////////////////////////////////////////////
595 void setMainLoopGame (void) {
596 frameCB = frmGameDraw;
597 keyCB = frmGameKey;
598 vmMapGetCB = mapGet;
599 vmMapSetCB = mapSet;
601 vmPaused = 0;
602 inMinimap = 0;
603 doSave = 0;
604 curLevel = -1;
605 memset(fkeys, 0, sizeof(fkeys));