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
51 SHAPE_FREE
= '0', /* 'air' */
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',
72 typedef struct stghit Q
;
78 unsigned char config
; /* maintest | sideconfig */
81 typedef struct stage stage
;
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";
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]){
130 printf("stage: problem with tile config\n");
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
){
151 static int min3(int a
, int b
, int c
){
159 static int max4(int a
, int b
, int c
, int d
){
168 static struct stghit
mkhit(int x
, int y
, int vx
, int vy
, int s
, int z
){
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
){
199 int xx
, yy
, vvx
, vvy
;
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
);
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
){
225 if(y
< x
/2) return nohit
;
227 dot
= SIN26(vx
) - COS26(vy
);
232 vy
- -COS26(2 * dot
),
237 static Q
mt_rtrap_floor(int x
, int y
, int vx
, int vy
){
241 static Q
mt_ltrap_ceil(int x
, int y
, int vx
, int vy
){
245 static Q
mt_rtrap_ceil(int x
, int y
, int vx
, int vy
){
249 static Q
mt_lslope_floor(int x
, int y
, int vx
, int vy
){
251 if(y
< x
/2 + L
/2) return nohit
;
252 z
= COS26(y
- x
/2 - L
/2);
253 dot
= SIN26(vx
) - COS26(vy
);
258 vy
- -COS26(2 * dot
),
263 static Q
mt_rslope_floor(int x
, int y
, int vx
, int vy
){
267 static Q
mt_lslope_ceil(int x
, int y
, int vx
, int vy
){
271 static Q
mt_rslope_ceil(int x
, int y
, int vx
, int vy
){
276 static Q
mt_triangle_ne(int x
, int y
, int vx
, int vy
){
280 static Q
mt_triangle_nw(int x
, int y
, int vx
, int vy
){
284 static Q
mt_triangle_se(int x
, int y
, int vx
, int vy
){
288 static Q
mt_triangle_sw(int x
, int y
, int vx
, int vy
){
290 if(y
< x
) return nohit
;
292 dot
= SIN45(vx
) - COS45(vy
);
297 vy
- -SIN45(2 * dot
),
303 static Q
nulltest(int x
, int y
, int vx
, int vy
){
308 static Q
general_test(
309 int x
, int y
, int vx
, int vy
,
310 test maintest
, test side1
, test side2
, test side3
314 Q hit0
= maintest(x
,y
,vx
,vy
);
316 if(!hit0
.yes
) return nohit
;
317 if(side3
== NULL
) sides
-= 1;
318 if(side2
== NULL
) sides
-= 1;
319 if(side1
== NULL
) sides
-= 1;
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
;
328 if(z0
< z1
) return hit0
;
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");
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");
348 printf("stage: general test is broken (code 1)\n");
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
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;
368 int W
= this_stage
->w
;
369 tile
* t
= this_stage
->tiles
+ tx
+ ty
*W
;
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]);
382 hit
.x
+= UUU(tx
* 16);
383 hit
.y
+= UUU(ty
* 16);
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
;
395 // struct stghit hit4, hit5, hit6, hit7;
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
;
429 /* end collision stuff */
436 static void initialize_tiles(int n
, tile
* tiles
){
439 tiles
[i
].shape
= SHAPE_FREE
;
446 static void fix_tile_sides(stage
* s
){
450 static unsigned char base_config(enum tile_shape 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);
479 static int load_stage(char* gfxpath
, char* stagepath
){
492 reader
* f
= loader_open(stagepath
);
493 char* filename
= path_ending(stagepath
);
497 stage
* s
= malloc(sizeof(stage
));
500 int x
, y
, ox
, oy
, fg
, bg
, shape
;
503 loader_scanline(f
, "%d %d %d %d", &w
, &h
, &ox
, &oy
);
505 loader_scanline(f
, "%s", buf
);
506 strcpy(gfx
, gfxpath
);
508 s
->bgimage
= load_bitmap(gfx
);
510 loader_scanline(f
, "%s", buf
);
511 strcpy(gfx
, gfxpath
);
513 s
->bgtiles
= load_bitmap(gfx
);
515 loader_scanline(f
, "%s", buf
);
516 strcpy(gfx
, gfxpath
);
518 s
->fgtiles
= load_bitmap(gfx
);
520 s
->tiles
= malloc(w
*h
*sizeof(tile
));
521 initialize_tiles(w
*h
, s
->tiles
);
526 strncpy(s
->id
, filename
, 256);
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
));
535 tptr
->config
= base_config(shape
);
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
563 char stagesdir
[256] = "";
564 char gfxpath
[256] = "";
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
);
580 printf("ERROR cant read dirs\n");
586 stagepath
= ptr
->item
;
587 if(load_stage(gfxpath
, stagepath
) < 0){
588 printf("ERROR cant load stage\n");
589 loader_freedirlist(dirs
);
595 loader_freedirlist(dirs
);
609 strcpy(zone_name
, "NO ZONE");
612 void switch_stage(string id
){
615 if(strcmp(id
, ptr
->id
) == 0){
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
;
629 int tw
= this_stage
->w
;
630 int th
= this_stage
->h
;
644 screen_dimensions(&W
, &H
);
646 extra_x
= (W
- 320)/16/2 + 2;
649 extra_y
= (H
- 240)/16/2 + 2;
657 if(io
> tw
-1) return;
658 if(jo
> th
-1) return;
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
;
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
){
685 int bw
, bh
, screen_w
, screen_h
;
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
++){
701 (i
+bgshift
)*bw
-cx
/parallax
,
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
);
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
;
753 printf("stage debug:\n\n");
754 printf("zone: %s\n\n", zone_name
);
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
);
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);