Added entities. Rewrote stage, half 'working'.
[cantaveria.git] / stage.c
blob3667bb934740c9cac5a6969126ea915fe36e206a
1 /*
2 Cantaveria - action adventure platform game
3 Copyright (C) 2009 2010 Evan Rinehart
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to
18 The Free Software Foundation, Inc.
19 51 Franklin Street, Fifth Floor
20 Boston, MA 02110-1301, USA
25 stage module is responsible for modelling the static world.
26 it has three main functions
27 * load zone files into world
28 * draw world background and foreground layers
29 * calculate if and where a moving rectangle will collide with world
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <ctype.h>
38 #include <limits.h>
40 #include <util.h>
41 #include <list.h>
42 #include <loader.h>
43 #include <graphics.h>
44 #include <video.h>
46 #include <stage.h>
50 enum tile_shape {
51 SHAPE_FREE = '0', /* 'air' */
52 SHAPE_SQUARE = 'M',
54 SHAPE_TRI_NE = 'a',
55 SHAPE_TRI_NW = 'b',
56 SHAPE_TRI_SE = 'c',
57 SHAPE_TRI_SW = 'd',
59 SHAPE_LTRAP_FLOOR = '1', /* trapezoid */
60 SHAPE_RTRAP_FLOOR = '2',
61 SHAPE_LSLOPE_FLOOR = '3',
62 SHAPE_RSLOPE_FLOOR = '4',
63 SHAPE_LTRAP_CEIL = '5',
64 SHAPE_RTRAP_CEIL = '6',
65 SHAPE_LSLOPE_CEIL = '7',
66 SHAPE_RSLOPE_CEIL = '8',
68 SHAPE_HALF_FLOOR = 'm',
69 SHAPE_HALF_CEIL = 'w'
72 typedef struct stghit Q;
74 typedef struct {
75 unsigned char bg;
76 unsigned char fg;
77 unsigned char shape;
78 unsigned char config; /* maintest | sideconfig */
79 } tile;
81 typedef struct stage stage;
82 struct stage {
83 char id[256];
84 int w, h;
85 tile* tiles;
86 int bgimage;
87 int bgtiles;
88 int fgtiles;
89 int ox, oy;
90 stage* next;
93 typedef Q (*test)(int x, int y, int vx, int vy);
95 static test maintests[16];
96 static test sidetests[4];
98 char zone_name[256] = "NO ZONE";
99 stage* stages = NULL;
100 stage* this_stage = NULL;
102 #define SIN26(X) ((X) * 458 / 1024)
103 #define COS26(X) ((X) * 916 / 1024)
104 #define SIN45(X) ((X) * 724 / 1024)
105 #define COS45(X) ((X) * 724 / 1024)
106 #define UUU(X) ((X) * 1024)
107 #define DDD(X) ((X) / 1024)
109 static const int L = UUU(16);
110 static const struct stghit nohit = {0,0,0,0,0,0,0};
113 static unsigned char pack_config(int maintest, int sideconfig){
114 return (maintest << 4) | sideconfig;
117 static void unpack_config(unsigned char config, int* mtout, int* scout){
118 *scout = config & 0x0f;
119 *mtout = (config & 0xf0) >> 4;
122 static void get_sidetests(int sideconfig, test tests[3]){
123 int i = 0;
124 int sc = sideconfig;
125 tests[0] = NULL;
126 tests[1] = NULL;
127 tests[2] = NULL;
129 if(sc==0xf) {
130 printf("stage: problem with tile config\n");
131 return;
134 if(sc&1) tests[i++]=sidetests[0];
135 if(sc&2) tests[i++]=sidetests[1];
136 if(sc&4) tests[i++]=sidetests[2];
137 if(sc&8) tests[i++]=sidetests[3];
142 static int min4(int a, int b, int c, int d){
143 int m = INT_MAX;
144 if(a < m) m = a;
145 if(b < m) m = b;
146 if(c < m) m = c;
147 if(d < m) m = d;
148 return m;
151 static int min3(int a, int b, int c){
152 int m = INT_MAX;
153 if(a < m) m = a;
154 if(b < m) m = b;
155 if(c < m) m = c;
156 return m;
159 static int max4(int a, int b, int c, int d){
160 int m = INT_MIN;
161 if(a > m) m = a;
162 if(b > m) m = b;
163 if(c > m) m = c;
164 if(d > m) m = d;
165 return m;
168 static struct stghit mkhit(int x, int y, int vx, int vy, int s, int z){
169 struct stghit hit;
170 hit.yes = 1;
171 hit.x = x;
172 hit.y = y;
173 hit.vx = vx;
174 hit.vy = vy;
175 hit.surface = s;
176 hit.depth = z;
177 return hit;
181 static Q st_left(int x, int y, int vx, int vy){
182 return mkhit(-1,y,-vx,vy,1,x);
184 static Q st_right(int x, int y, int vx, int vy){
185 return mkhit(L,y,-vx,vy,1,L-x);
187 static Q st_top(int x, int y, int vx, int vy){
188 return mkhit(x,-1,vx,-vy,0,y);
190 static Q st_bottom(int x, int y, int vx, int vy){
191 return mkhit(x,L,vx,-vy,2,L-y);
197 static Q mt_square(int x, int y, int vx, int vy){
198 int z1,z2,z3,z4,z;
199 int xx, yy, vvx, vvy;
200 z1 = x;
201 z2 = y;
202 z3 = L - x;
203 z4 = L - y;
204 z = min4(z1,z2,z3,z4);
205 if(z == z1) return mkhit(-1,y,-vx,vy,1,z);
206 if(z == z2) return mkhit(x,-1,vx,-vy,0,z);
207 if(z == z3) return mkhit(L,y,-vx,vy,1,z);
208 if(z == z4) return mkhit(x,L,vx,-vy,2,z);
209 return nohit;
212 static Q mt_half_floor(int x, int y, int vx, int vy){
213 if(y < L/2) return nohit;
214 else return mkhit(x,L/2,vx,-vy,0,y-L/2);
217 static Q mt_half_ceil(int x, int y, int vx, int vy){
218 if(y > L/2) return nohit;
219 else return mkhit(x,L/2,vx,-vy,0,L/2-y);
223 static Q mt_ltrap_floor(int x, int y, int vx, int vy){
224 int dot, z;
225 if(y < x/2) return nohit;
226 z = COS26(y - x/2);
227 dot = SIN26(vx) - COS26(vy);
228 return mkhit(
229 x + SIN26(z),
230 y - COS26(z),
231 vx - SIN26(2 * dot),
232 vy - -COS26(2 * dot),
237 static Q mt_rtrap_floor(int x, int y, int vx, int vy){
238 return nohit;
241 static Q mt_ltrap_ceil(int x, int y, int vx, int vy){
242 return nohit;
245 static Q mt_rtrap_ceil(int x, int y, int vx, int vy){
246 return nohit;
249 static Q mt_lslope_floor(int x, int y, int vx, int vy){
250 int dot, z;
251 if(y < x/2 + L/2) return nohit;
252 z = COS26(y - x/2 - L/2);
253 dot = SIN26(vx) - COS26(vy);
254 return mkhit(
255 x + SIN26(z),
256 y - COS26(z),
257 vx - SIN26(2 * dot),
258 vy - -COS26(2 * dot),
263 static Q mt_rslope_floor(int x, int y, int vx, int vy){
264 return nohit;
267 static Q mt_lslope_ceil(int x, int y, int vx, int vy){
268 return nohit;
271 static Q mt_rslope_ceil(int x, int y, int vx, int vy){
272 return nohit;
276 static Q mt_triangle_ne(int x, int y, int vx, int vy){
277 return nohit;
280 static Q mt_triangle_nw(int x, int y, int vx, int vy){
281 return nohit;
284 static Q mt_triangle_se(int x, int y, int vx, int vy){
285 return nohit;
288 static Q mt_triangle_sw(int x, int y, int vx, int vy){
289 int z,dot;
290 if(y < x) return nohit;
291 z = COS45(y - x);
292 dot = SIN45(vx) - COS45(vy);
293 return mkhit(
294 x + COS45(z),
295 y - SIN45(z),
296 vx - COS45(2 * dot),
297 vy - -SIN45(2 * dot),
303 static Q nulltest(int x, int y, int vx, int vy){
304 return nohit;
308 static Q general_test(
309 int x, int y, int vx, int vy,
310 test maintest, test side1, test side2, test side3
312 int sides = 3;
313 int z0,z1,z2,z3,z;
314 Q hit0 = maintest(x,y,vx,vy);
315 Q hit1, hit2, hit3;
316 if(!hit0.yes) return nohit;
317 if(side3 == NULL) sides -= 1;
318 if(side2 == NULL) sides -= 1;
319 if(side1 == NULL) sides -= 1;
321 z0 = hit0.depth;
322 if(side1) {hit1 = side1(x,y,vx,vy); z1 = hit1.depth;}
323 if(side2) {hit2 = side2(x,y,vx,vy); z2 = hit2.depth;}
324 if(side3) {hit3 = side3(x,y,vx,vy); z3 = hit3.depth;}
326 if(sides == 0) return hit0;
327 if(sides == 1){
328 if(z0 < z1) return hit0;
329 else return hit1;
331 if(sides == 2){
332 z = min3(z0,z1,z2);
333 if(z==z0) return hit0;
334 if(z==z1) return hit1;
335 if(z==z2) return hit2;
336 printf("stage: general test is broken (code 2)\n");
337 return nohit;
339 if(sides == 3){
340 z = min4(z0,z1,z2,z3);
341 if(z==z0) return hit0;
342 if(z==z1) return hit1;
343 if(z==z2) return hit2;
344 if(z==z3) return hit3;
345 printf("stage: general test is broken (code 0)\n");
346 return nohit;
348 printf("stage: general test is broken (code 1)\n");
349 return nohit;
355 test a single moving point for collision with the stage.
356 if a collision occurs the result will contain the collision
357 point, a new velocity, penetration depth, and surface type.
358 this data can be used in several ways by the client code.
359 testing moving rectangles is expected to use several calls.
360 this model has problems with sharp acute corners, so levels
361 should avoid them.
363 struct stghit stage_collide(int x, int y, int vx, int vy){
364 int tx = DDD(x) / 16;
365 int ty = DDD(y) / 16;
366 int x_ = x % L;
367 int y_ = y % L;
368 int W = this_stage->w;
369 tile* t = this_stage->tiles + tx + ty*W;
370 int mt;
371 int sc;
372 test sides[3];
373 Q hit;
375 unpack_config(t->config, &mt, &sc);
376 if(maintests[mt] == NULL) mt = 1;
377 get_sidetests(sc, sides);
378 hit = general_test(x_, y_, vx, vy, maintests[mt],
379 sides[0], sides[1], sides[2]);
381 if(hit.yes){
382 hit.x += UUU(tx * 16);
383 hit.y += UUU(ty * 16);
385 return hit;
389 /* i believe this only works for rectangles strictly smaller than
390 * a tile. 'works' means level should avoid sharp points.
392 struct stghit stage_collide_rect(int x, int y, int w, int h, int vx, int vy){
393 struct stghit hit0, hit1, hit2, hit3;
394 int z0,z1,z2,z3,z;
395 // struct stghit hit4, hit5, hit6, hit7;
396 // int z4,z5,z6,z7;
398 hit0 = stage_collide(x,y,vx,vy);
399 hit1 = stage_collide(x+w,y,vx,vy);
400 hit2 = stage_collide(x,y+h,vx,vy);
401 hit3 = stage_collide(x+w,y+h,vx,vy);
403 z0 = hit0.yes ? hit0.depth : INT_MIN;
404 z1 = hit1.yes ? hit1.depth : INT_MIN;
405 z2 = hit2.yes ? hit2.depth : INT_MIN;
406 z3 = hit3.yes ? hit3.depth : INT_MIN;
408 z = max4(z0,z1,z2,z3);
410 if(z == z0) return hit0;
411 if(z == z1){
412 hit1.x -= w;
413 return hit1;
415 if(z == z2){
416 hit2.y -= h;
417 return hit2;
419 if(z == z3){
420 hit3.x -= w;
421 hit3.y -= h;
422 return hit3;
425 return nohit;
429 /* end collision stuff */
436 static void initialize_tiles(int n, tile* tiles){
437 int i;
438 for(i=0; i<n; i++){
439 tiles[i].shape = SHAPE_FREE;
440 tiles[i].fg = 0;
441 tiles[i].bg = 0;
442 tiles[i].config = 0;
446 static void fix_tile_sides(stage* s){
450 static unsigned char base_config(enum tile_shape sh){
451 /* 2
452 * 1 4
455 switch(sh){
456 case SHAPE_FREE: return pack_config(0, 0);
457 case SHAPE_SQUARE: return pack_config(1, 0);
459 case SHAPE_TRI_NE: return pack_config(2, 2|4);
460 case SHAPE_TRI_NW: return pack_config(3, 1|2);
461 case SHAPE_TRI_SE: return pack_config(4, 4|8);
462 case SHAPE_TRI_SW: return pack_config(5, 1|8);
464 case SHAPE_LTRAP_FLOOR: return pack_config(6, 1|8|4);
465 case SHAPE_RTRAP_FLOOR: return pack_config(7, 1|8|4);
466 case SHAPE_LSLOPE_FLOOR: return pack_config(8, 1|8);
467 case SHAPE_RSLOPE_FLOOR: return pack_config(9, 8|4);
468 case SHAPE_LTRAP_CEIL: return pack_config(10, 1|2|4);
469 case SHAPE_RTRAP_CEIL: return pack_config(11, 1|2|4);
470 case SHAPE_LSLOPE_CEIL: return pack_config(12, 1|2);
471 case SHAPE_RSLOPE_CEIL: return pack_config(13, 2|4);
473 case SHAPE_HALF_FLOOR: return pack_config(14, 1|8|4);
474 case SHAPE_HALF_CEIL: return pack_config(15, 1|2|4);
475 default: return 0;
479 static int load_stage(char* gfxpath, char* stagepath){
481 [stage file]
482 width height
483 bgimage
484 dectiles
485 bgtiles
486 fgtiles
487 x y fg bg shape
488 x y fg bg shape
492 reader* f = loader_open(stagepath);
493 char* filename = path_ending(stagepath);
494 char buf[256];
495 char gfx[256];
496 tile* tptr;
497 stage* s = malloc(sizeof(stage));
498 int w = 20;
499 int h = 15;
500 int x, y, ox, oy, fg, bg, shape;
501 int i, j;
503 loader_scanline(f, "%d %d %d %d", &w, &h, &ox, &oy);
505 loader_scanline(f, "%s", buf);
506 strcpy(gfx, gfxpath);
507 strcat(gfx, buf);
508 s->bgimage = load_bitmap(gfx);
510 loader_scanline(f, "%s", buf);
511 strcpy(gfx, gfxpath);
512 strcat(gfx, buf);
513 s->bgtiles = load_bitmap(gfx);
515 loader_scanline(f, "%s", buf);
516 strcpy(gfx, gfxpath);
517 strcat(gfx, buf);
518 s->fgtiles = load_bitmap(gfx);
520 s->tiles = malloc(w*h*sizeof(tile));
521 initialize_tiles(w*h, s->tiles);
522 s->w = w;
523 s->h = h;
524 s->ox = ox;
525 s->oy = oy;
526 strncpy(s->id, filename, 256);
527 s->id[255] = '\0';
529 while(!loader_feof(f)){
530 loader_scanline(f, "%d %d %d %d %c", &x, &y, &fg, &bg, &shape);
531 tptr = s->tiles + (x+ox) + (s->w * (y+oy));
532 tptr->fg = fg;
533 tptr->bg = bg;
534 tptr->shape = shape;
535 tptr->config = base_config(shape);
538 fix_tile_sides(s);
540 s->next = stages;
541 stages = s;
543 loader_close(f);
544 return 0;
547 /***/
550 int load_zone(string name){
552 loading a zone from a file
553 a zone consists of one or more stages
554 each stage has its own tileset graphics and background
555 it also has a large array of tiles
557 the stage files are in zones/zonename/
558 the name of the file will be used as the id
561 list* dirs;
562 list* ptr;
563 char stagesdir[256] = "";
564 char gfxpath[256] = "";
565 char* stagepath;
567 strcat(stagesdir, "zones/");
568 strcat(stagesdir, name);
569 strcat(stagesdir, "/stages/");
571 strcat(gfxpath, "zones/");
572 strcat(gfxpath, name);
573 strcat(gfxpath, "/gfx/");
575 strncpy(zone_name, name, 256);
576 zone_name[255] = '\0';
578 dirs = loader_readdir((string)stagesdir);
579 if(dirs == NULL){
580 printf("ERROR cant read dirs\n");
581 return -1;
584 ptr = dirs->next;
585 while(ptr){
586 stagepath = ptr->item;
587 if(load_stage(gfxpath, stagepath) < 0){
588 printf("ERROR cant load stage\n");
589 loader_freedirlist(dirs);
590 return -1;
592 ptr = ptr->next;
595 loader_freedirlist(dirs);
596 return 0;
599 void unload_zone(){
600 stage* ptr = stages;
601 stage* prev;
602 while(ptr){
603 free(ptr->tiles);
604 prev = ptr;
605 ptr = ptr->next;
606 free(prev);
609 strcpy(zone_name, "NO ZONE");
612 void switch_stage(string id){
613 stage* ptr = stages;
614 while(ptr){
615 if(strcmp(id, ptr->id) == 0){
616 this_stage = ptr;
617 return;
619 ptr = ptr->next;
622 printf("ERROR stage not found\n");
626 static void draw_tiles(int gfx, char layer, int cx, int cy){
627 tile* tbase = this_stage->tiles;
628 tile* t;
629 int tw = this_stage->w;
630 int th = this_stage->h;
631 int id;
632 int gx, gy;
634 int i;
635 int j;
636 int io = cx/16;
637 int jo = cy/16;
638 int i_ = io + 20;
639 int j_ = jo + 15;
641 int extra_x = 0;
642 int extra_y = 0;
643 int W, H;
644 screen_dimensions(&W, &H);
645 if(W>320){
646 extra_x = (W - 320)/16/2 + 2;
648 if(H>240){
649 extra_y = (H - 240)/16/2 + 2;
652 io -= extra_x;
653 jo -= extra_y;
654 i_ += extra_x;
655 j_ += extra_y+1;
657 if(io > tw-1) return;
658 if(jo > th-1) return;
660 if(io < 0) io = 0;
661 if(jo < 0) jo = 0;
662 if(i_ > tw) i_ = tw;
663 if(j_ > th) j_ = th;
665 for(j=jo; j<j_; j++){
666 for(i=io; i<i_; i++){
667 t = tbase + i + tw*j;
668 if(layer=='b') id = t->bg;
669 else if(layer=='f') id = t->fg;
670 else id = 0;
671 gx = 16*(id%16);
672 gy = 16*(id/16);
673 draw_gfx(gfx, i*16-cx, j*16-cy, gx, gy, 16, 16);
679 cx, cy is the camera position
680 x, y i think is the offset from 0,0 you should shift
681 w, h i think is the width and height of the screen
683 void stage_draw_bg(int cx, int cy){
684 int i;
685 int bw, bh, screen_w, screen_h;
686 int bgimage;
687 int bgshift;
688 int parallax = 2;
690 if(this_stage == NULL) return;
692 /* draw background vertically centered
693 tile horizontally at fraction of camera x */
694 bgimage = this_stage->bgimage;
695 gfx_dimensions(bgimage, &bw, &bh);
696 screen_dimensions(&screen_w, &screen_h);
697 bgshift = cx/parallax/bw;
698 for(i=-1; i<(screen_w/bw)+2; i++){
699 draw_gfx_raw(
700 bgimage,
701 (i+bgshift)*bw-cx/parallax,
702 (screen_h-bh)/2,
703 0, 0, bw, bh
707 draw_tiles(this_stage->bgtiles, 'b', cx, cy);
710 void stage_draw_fg(int cx, int cy){
711 draw_tiles(this_stage->bgtiles, 'f', cx, cy);
715 void stage_init(){
716 int i;
717 for(i=0; i<14; i++){
718 maintests[i] = NULL;
721 sidetests[0] = st_left;
722 sidetests[1] = st_right;
723 sidetests[2] = st_top;
724 sidetests[3] = st_bottom;
726 maintests[0] = nulltest;
727 maintests[1] = mt_square;
728 maintests[2] = mt_triangle_ne;
729 maintests[3] = mt_triangle_nw;
730 maintests[4] = mt_triangle_se;
731 maintests[5] = mt_triangle_sw;
732 maintests[6] = mt_ltrap_floor;
733 maintests[7] = mt_rtrap_floor;
734 maintests[8] = mt_lslope_floor;
735 maintests[9] = mt_rslope_floor;
736 maintests[10] = mt_ltrap_ceil;
737 maintests[11] = mt_rtrap_ceil;
738 maintests[12] = mt_lslope_ceil;
739 maintests[13] = mt_rslope_ceil;
740 maintests[14] = mt_half_floor;
741 maintests[15] = mt_half_ceil;
746 /*** debug ***/
748 void stage_debug(){
749 stage* ptr = stages;
750 int i, j;
751 char c;
753 printf("stage debug:\n\n");
754 printf("zone: %s\n\n", zone_name);
756 while(ptr){
757 printf("stage: %s\n", ptr->id);
758 printf("size: %dx%d\n", ptr->w, ptr->h);
759 printf("origin: (%d, %d)\n", ptr->ox, ptr->oy);
760 printf("bgimage: %d\n", ptr->bgimage);
761 printf("bgtiles: %d\n", ptr->bgtiles);
762 printf("fgtiles: %d\n", ptr->fgtiles);
763 printf("tiles:\n");
765 for(j=0; j<ptr->h; j++){
766 for(i=0; i<ptr->w; i++){
767 c = (ptr->tiles + i + ptr->w*j)->fg;
769 if(isprint(c)) printf("%d", c);
770 else printf("?");*/
771 printf("[%02x]", c);
773 printf("\n");
777 printf("\n");
778 ptr = ptr->next;