Many changes. Stage collision algorithm.
[cantaveria.git] / backend.c
blob073fc85e5cefd970e999dfcac417ec34c0925e1b
1 /*
2 Cantaveria - action adventure platform game
3 Copyright (C) 2009 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
23 #include <math.h>
25 #include <SDL/SDL.h>
26 #include <SDL/SDL_opengl.h>
28 #include "util.h"
29 #include "backend.h"
30 //#include "game.h"
31 #include "loader.h"
32 #include "sound.h"
34 SDL_Surface* video;
35 int gl_flag = 0;
36 int fullscreen = 0;
37 int W, H;
38 int end = 0;
40 struct handler handler;
43 void set_handler(struct handler h){
44 handler = h;
47 void end_program(){
48 end = 1;
50 int ended(){
51 return end;
54 int since(){
55 static int last = 0;
56 int now = SDL_GetTicks();
57 int diff = now - last;
58 last = now;
59 return diff;
62 void delay(int ms){
63 SDL_Delay(ms);
67 int keymap[32];
68 int butmap[MAX_PLAYERS][8];
69 int joymap[MAX_PLAYERS];
70 int alphanum_enable = 0;
72 int keynum(int name){
73 return keymap[name];
76 int butnum(int joy, int name){
77 return butmap[joy][name];
82 int joy2player(int joy){
83 for(int i=0; i<MAX_PLAYERS; i++){
84 if(joymap[i] == joy) return i;
86 return -1;
89 int key2name(int sym){
90 for(int i=0; i<14; i++){
91 if(keymap[i] == sym) return i;
93 return -1;
96 int button2name(int player, int but){
97 if(player < 0) return -1;
98 for(int i=0; i<8; i++){
99 if(butmap[i][player] == but) return i;
101 return -1;
107 void backend_quit(){
108 printf("sdl: quit\n");
109 SDL_LockAudio();
110 SDL_CloseAudio();
111 SDL_Quit();
118 void enable_alphanum(int yn){
119 SDL_EnableUNICODE(yn);
120 alphanum_enable = yn;
123 void input(){
125 SDL_Event e;
126 int name;
127 int player;
128 Uint16 uni16;
130 while(SDL_PollEvent(&e) != 0){
131 switch(e.type){
132 case SDL_KEYDOWN:
133 uni16 = e.key.keysym.unicode;
134 if(uni16 != 0 && alphanum_enable){
135 //handle uni16
137 else{
138 name = key2name(e.key.keysym.sym);
139 if(name > -1) handler.keydown(name);
141 break;
142 case SDL_KEYUP:
143 name = key2name(e.key.keysym.sym);
144 if(name > -1) handler.keyup(name);
145 break;
146 case SDL_JOYBUTTONDOWN:
147 player = joy2player(e.jbutton.which);
148 name = button2name(player, e.jbutton.button);
149 if(name > -1) handler.joypress(player, name);
150 break;
151 case SDL_JOYBUTTONUP:
152 player = joy2player(e.jbutton.which);
153 name = button2name(player, e.jbutton.button);
154 if(name > -1) handler.joyrelease(e.jbutton.which, name);
155 break;
156 case SDL_JOYAXISMOTION:
157 player = joy2player(e.jaxis.which);
158 if(player < 0) break;
159 if(e.jaxis.axis == 0){
160 handler.joymovex(player, e.jaxis.value);
162 else if(e.jaxis.axis == 1){
163 handler.joymovey(player, e.jaxis.value);
165 break;
166 case SDL_QUIT:
167 end_program();
168 break;
173 void control(int type, int par1, int par2){
175 switch(type){
176 case KEYDOWN:
177 handler.keydown(par1);
178 break;
179 case KEYUP:
180 handler.keyup(par1);
181 break;
182 case JOYMOVEX:
183 handler.joymovex(par1, par2);
184 break;
185 case JOYMOVEY:
186 handler.joymovey(par1, par2);
187 break;
188 case JOYPRESS:
189 handler.joypress(par1, par2);
190 break;
191 case JOYRELEASE:
192 handler.joyrelease(par1, par2);
193 break;
198 struct {
199 char* filename;
200 SDL_Surface* surf;
201 GLuint texture;
202 int w;
203 int h;
204 } gfx[MAX_GFX];
205 int gfx_count = 0;
207 int screen_w = 320;
208 int screen_h = 240;
209 int screen_offset_x = 0;
210 int screen_offset_y = 0;
215 /********************/
216 /* drawing routines */
217 /********************/
219 void draw_gfx_raw(int gfxid, int x, int y, int X, int Y, int W, int H){
220 if(x > screen_w || y > screen_h || x+W < 0 || y+H < 0) return;
221 if(!gl_flag){
222 SDL_Surface* surf = gfx[gfxid].surf;
223 SDL_Rect r1 = {X,Y,W,H};
224 SDL_Rect r2 = {x,y,W,H};
225 SDL_BlitSurface(surf,&r1,video,&r2);
227 else{
228 double w = gfx[gfxid].w;
229 double h = gfx[gfxid].h;
230 double X0 = ((double)X)/w;
231 double X1 = X0 + ((double)W)/w;
232 double Y0 = ((double)Y)/h;
233 double Y1 = Y0 + ((double)H)/h;
235 glBindTexture( GL_TEXTURE_2D, gfx[gfxid].texture );
236 glBegin( GL_QUADS );
237 glTexCoord2d(X0,Y0);
238 glVertex3f(x,y,0);
240 glTexCoord2d(X1,Y0);
241 glVertex3f(x+W,y,0);
243 glTexCoord2d(X1,Y1);
244 glVertex3f(x+W,y+H,0);
246 glTexCoord2d(X0,Y1);
247 glVertex3f(x,y+H,0);
248 glEnd();
252 void draw_gfx(int gfxid, int x, int y, int X, int Y, int W, int H){
253 draw_gfx_raw(gfxid, x+screen_offset_x, y+screen_offset_y, X, Y, W, H);
256 void clear_video(){
257 if(!gl_flag){
258 SDL_FillRect(video, 0, 0);
260 else{
261 glClearColor(0.0,0.0,0.0,0.0);
262 glClear( GL_COLOR_BUFFER_BIT );
266 void update_video(){
267 if(!gl_flag){
268 SDL_UpdateRect(video,0,0,0,0);
270 else{
271 SDL_GL_SwapBuffers();
278 /********************/
279 /* graphics loading */
280 /********************/
282 SDL_Surface* SDL_NewSurface(int w, int h){
283 char prefix[32] = "SDL_NewSurface";
284 SDL_Surface* tmp = SDL_CreateRGBSurface(SDL_SRCCOLORKEY,w,h,32,0,0,0,0);
285 if(!tmp){
286 out_of_memory(prefix);
289 SDL_Surface* surf = SDL_DisplayFormat(tmp);
290 if(!surf){
291 out_of_memory(prefix);
294 SDL_FreeSurface(tmp);
296 SDL_FillRect(surf,0,0x00ffffff);
298 return surf;
301 SDL_Surface* load_pixmap(char* filename){
302 char path[256] = "gfx/";
303 strmcat(path, filename, 256);
305 reader* rd = loader_open(path);
306 if(!rd){
307 return NULL;
310 unsigned char header[18];
311 loader_read(rd, header, 18);
313 int w = header[12] + (header[13]<<8);
314 int h = header[14] + (header[15]<<8);
315 int bpp = header[16];
316 printf("load_pixmap: %s has bpp=%d\n",filename, bpp);
317 SDL_Surface* surf = SDL_CreateRGBSurface(0,w,h,bpp,
318 0x00ff0000,0x0000ff00,0x000000ff,0xff000000);
319 if(!surf){
320 out_of_memory("load_pixmap");
322 loader_read(rd, surf->pixels, w*(bpp/8)*h);
323 loader_close(rd);
325 return surf;
328 int load_gfx(char* filename){
329 if(gfx_count == MAX_GFX){
330 fatal_error("load_gfx: cannot load any more than %d graphics\n",MAX_GFX);
333 printf("loading %s\n",filename);
336 for(int i=0; i<gfx_count; i++){/*check for already loaded gfx*/
337 if(strcmp(gfx[i].filename, filename)==0){
338 return i;
342 SDL_Surface* src = load_pixmap(filename);
343 if(!src){
344 fatal_error("load_gfx: failed to load %s\n",filename);
347 if(!gl_flag){
348 SDL_Surface* surf = SDL_DisplayFormatAlpha(src);
349 SDL_SetAlpha(surf, 0, 0);
351 Uint32 key = SDL_MapRGB(surf->format, (COLOR_KEY&0xff0000)>>16,
352 (COLOR_KEY&0x00ff00)>>8,
353 (COLOR_KEY&0x0000ff)>>0);
354 SDL_SetColorKey(surf, SDL_SRCCOLORKEY, key);
356 SDL_FreeSurface(src);
359 gfx[gfx_count].filename = strxcpy(filename);
360 gfx[gfx_count].surf = surf;
361 gfx[gfx_count].w = surf->w;
362 gfx[gfx_count].h = surf->h;
364 else {
365 GLuint texture;
367 //SDL_Surface* conv = SDL_CreateRGBSurface(0, src->w, src->h, 32,
368 // 0xff<<16,0xff<<8,0xff<<0,0);
370 SDL_Surface* conv = SDL_DisplayFormatAlpha(src);
371 //SDL_SetAlpha(conv, 0, 0);
372 //SDL_BlitSurface(src, NULL, conv, NULL);
373 //printf("bpp = %d\n",conv->format->BitsPerPixel);
374 int N = 0;
375 int M = 3;
376 Uint8* conv_bytes = conv->pixels;
377 Uint32 key = SDL_MapRGB(src->format,(COLOR_KEY&0xff0000)>>16,
378 (COLOR_KEY&0x00ff00)>>8,
379 (COLOR_KEY&0x0000ff)>>0);
380 for(int i=0; i<src->w; i++){
381 for(int j=0; j<src->h; j++){
383 //if(1){printf("M==%d totalbytes=%d\n",M,src->w*src->h*4);}
385 Uint32 pixel = *((Uint32*)(src->pixels+N));
386 conv_bytes[M] = pixel==key ? SDL_ALPHA_TRANSPARENT : SDL_ALPHA_OPAQUE;
387 N += src->format->BytesPerPixel;
388 M += 4;
392 glGenTextures( 1, &texture );
393 glBindTexture( GL_TEXTURE_2D, texture );
395 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
396 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
397 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
398 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
400 glTexImage2D( GL_TEXTURE_2D, 0, 4, conv->w, conv->h, 0,
401 GL_BGRA, GL_UNSIGNED_BYTE, conv->pixels );
403 gfx[gfx_count].filename = strxcpy(filename);
404 gfx[gfx_count].texture = texture;
405 gfx[gfx_count].w = src->w;
406 gfx[gfx_count].h = src->h;
408 SDL_FreeSurface(conv);
409 SDL_FreeSurface(src);
412 return gfx_count++;
415 int gfx_width(int gfxid){
416 return gfx[gfxid].w;
419 int gfx_height(int gfxid){
420 return gfx[gfxid].h;
425 float* lout;
426 float* rout;
427 int buffer_size;
429 extern void process_audio(float lout[], float rout[], int len);
431 void audio_callback(void *userdata, Uint8 *stream, int len){
432 Sint16* out = (Sint16*)stream;
434 process_audio(lout, rout, buffer_size);
436 for(int i=0; i<buffer_size; i++){
437 out[i*2 ] = (Sint16)(lout[i]*32767);
438 out[i*2 + 1] = (Sint16)(rout[i]*32767);
445 /******************/
446 /* initialization */
447 /******************/
449 void sdl_init(){
450 if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK)==-1){
451 report_error("sdl: %s\n",SDL_GetError());
452 exit(-1);
456 void parse_options(int argc, char* argv[], int* fullscreen, int* gl_flag){
458 for(int i=0; i<argc; i++){
459 if(!strcmp(argv[i], "-gl")){
460 *gl_flag = 1;
461 continue;
463 if(!strcmp(argv[i], "-f")){
464 *fullscreen = 1;
466 if(!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")){
467 printf("options:\n");
468 printf(" -gl use opengl video mode\n");
469 printf(" -f use fullscreen video mode\n");
470 printf(" -h print this help\n");
471 printf(" -v print version info\n");
472 exit(0);
474 if(!strcmp(argv[i], "-v")){
475 printf("cantaveria (v%d.%d)\n",VERSION_MAJOR,VERSION_MINOR);
476 printf("Copyright 2009 Evan Rinehart\n\n");
478 printf("This program is distributed under the terms of the GNU General\n"
479 "Public License (v2) and comes with ABSOLUTELY NO WARRANTY.\n\n");
481 printf("Send questions, comments, and bugs to evanrinehart@gmail.com\n\n");
483 printf("Send money to:\n");
484 printf("1850 Claiborne St\n");
485 printf("Mandeville, LA 70448\n");
486 printf("United States of America\n\n");
488 printf("Thanks! :)\n");
489 exit(0);
494 void load_keymap(){
496 keymap[ESCAPE_KEY] = SDLK_ESCAPE;
497 keymap[PAUSE_KEY] = SDLK_PAUSE;
499 keymap[LEFT_KEY] = SDLK_LEFT;
500 keymap[RIGHT_KEY] = SDLK_RIGHT;
501 keymap[UP_KEY] = SDLK_UP;
502 keymap[DOWN_KEY] = SDLK_DOWN;
504 keymap[FIRE_KEY] = SDLK_z;
505 keymap[JUMP_KEY] = SDLK_x;
506 keymap[INVENTORY_KEY] = SDLK_a;
507 keymap[SPECIAL_KEY] = SDLK_s;
509 keymap[L_KEY] = SDLK_q;
510 keymap[R_KEY] = SDLK_w;
511 keymap[START_KEY] = SDLK_RETURN;
512 keymap[SELECT_KEY] = SDLK_TAB;
515 keymap[ESCAPE_KEY] = SDLK_ESCAPE;
516 keymap[PAUSE_KEY] = SDLK_PAUSE;
518 keymap[LEFT_KEY] = SDLK_a;
519 keymap[RIGHT_KEY] = SDLK_d;
520 keymap[UP_KEY] = SDLK_w;
521 keymap[DOWN_KEY] = SDLK_s;
523 keymap[FIRE_KEY] = SDLK_j;
524 keymap[JUMP_KEY] = SDLK_k;
525 keymap[INVENTORY_KEY] = SDLK_i;
526 keymap[SPECIAL_KEY] = SDLK_l;
528 keymap[L_KEY] = SDLK_q;
529 keymap[R_KEY] = SDLK_e;
530 keymap[START_KEY] = SDLK_u;
531 keymap[SELECT_KEY] = SDLK_o;
534 void setup_joysticks(){
535 int N = SDL_NumJoysticks();
536 printf("sdl: detected %d joysticks\n",N);
537 for(int i=0; i<N; i++){
538 if(SDL_JoystickOpen(i)){
539 printf(" joy%d: %s\n",i,SDL_JoystickName(i));
541 else{
542 printf(" joy%d: %s (failed to open)\n",i,SDL_JoystickName(i));
548 void setup_video(){
549 int flags = 0;
551 SDL_WM_SetCaption("cantaveria","cantaveria");
552 SDL_ShowCursor(SDL_DISABLE);
553 const SDL_VideoInfo* vinfo = SDL_GetVideoInfo();
556 if(fullscreen && gl_flag){
557 W = vinfo->current_w;
558 H = vinfo->current_h;
560 else if(gl_flag){
561 //W = 320;
562 //H = 240;
563 W = 640;
564 H = 480;
565 //W = 960;
566 //H = 720;
568 else if(fullscreen){
569 W = 320;
570 H = 240;
572 else{
573 W = 320;
574 H = 240;
577 if(gl_flag){
578 SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
579 //SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1);
580 SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, 1);
581 flags |= SDL_OPENGL;
584 if(fullscreen){
585 flags |= SDL_FULLSCREEN;
590 video = SDL_SetVideoMode(W,H,32,flags);
591 if(video == NULL){
592 report_error("sdl: %s\n",SDL_GetError());
593 exit(-1);
597 if(gl_flag){
598 glEnable(GL_BLEND);
599 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
601 glEnable( GL_TEXTURE_2D );
602 glClearColor( 0.0f, 0.0f, 0.0f, 0.0f );
604 glViewport( 0, 0, W, H );
606 glClear( GL_COLOR_BUFFER_BIT );
608 glMatrixMode( GL_PROJECTION );
609 glLoadIdentity();
610 if(fullscreen){
611 //glOrtho(0.0f, 240*aspect, 240, 0.0f, -1.0f, 1.0f);
612 //glOrtho(0.0f, 1280.0/3, 800.0/3, 0.0f, -1.0f, 1.0f);
613 int min = 9999;
614 int n = 0;
615 for(int i=1; i<10; i++){
616 if(abs(H/i - 240) < min){ min = H/i - 240; n = i; }
618 double new_w = ((double)W)/n;
619 double new_h = ((double)H)/n;
620 screen_offset_x = (new_w-320)/2;
621 screen_offset_y = (new_h-240)/2;
622 glOrtho(0.0f, new_w, new_h, 0.0f, -1.0f, 1.0f);
623 screen_w = new_w;
624 screen_h = new_h;
626 else{
627 glOrtho(0.0f, 320, 240, 0.0f, -1.0f, 1.0f);
628 screen_w = 320;
629 screen_h = 240;
632 glMatrixMode( GL_MODELVIEW );
633 glLoadIdentity();
635 else{
636 screen_w = 320;
637 screen_h = 240;
640 printf("video:\n");
641 printf(" resolution: %d x %d %s\n",W,H,fullscreen?"fullscreen":"windowed");
642 printf(" pixel dimensions: %d x %d\n",screen_w,screen_h);
643 printf(" aspect ratio: %g\n",((double)W)/H);
644 printf(" opengl: %s\n",gl_flag?"yes":"no");
645 printf(" x-offset: %d\n",screen_offset_x);
646 printf(" y-offset: %d\n",screen_offset_y);
647 printf(" video on\n");
651 void setup_audio(){
653 SDL_AudioSpec audio;
654 audio.freq = SAMPLE_RATE;
655 audio.format = AUDIO_S16;
656 audio.channels = 2;
657 audio.samples = BUFFER_SIZE;
658 audio.callback = audio_callback;
660 SDL_AudioSpec gotten;
662 if(SDL_OpenAudio(&audio, &gotten)<0){
663 report_error("sdl: cannot open audio (%s)\n", SDL_GetError());
664 exit(-1);
667 printf("audio:\n");
668 printf(" sample rate = %d\n",gotten.freq);
669 printf(" channels = %d\n",gotten.channels);
670 printf(" samples = %d\n",gotten.samples);
671 printf(" format = %d\n",gotten.format);
673 if(gotten.format != AUDIO_S16){
674 printf(" WARNING: audio format not AUDIO_S16 :(\n");
677 lout = xmalloc(gotten.samples*sizeof(float));
678 rout = xmalloc(gotten.samples*sizeof(float));
679 buffer_size = gotten.samples;
681 printf(" sound on\n");
682 SDL_PauseAudio(0);
687 void backend_init(int argc, char* argv[]){
689 sdl_init();
690 parse_options(argc, argv, &fullscreen, &gl_flag);
691 load_keymap();
692 setup_joysticks();
693 setup_video();
694 setup_audio();
695 srand(RANDOM_SEED);