#include #include #include #include #include #include "aoi.h" /********** * macros * **********/ #define aoi_log(...) do {\ fprintf(stderr,"[%s:%i] ",__FILE__,__LINE__);\ fprintf(stderr,__VA_ARGS__);\ fputc('\n',stderr);\ } while (0) #define aoi_error(...) do {\ aoi_log(__VA_ARGS__);\ exit(1);\ } while (0) #define aoi_assert(x) if (!(x)) aoi_error("assertion failed '%s'", #x) /********* * state * *********/ ALLEGRO_DISPLAY* display = NULL; ALLEGRO_BITMAP* sheet = NULL; /******************* * input functions * *******************/ //bitmask for input (current frame and previous frame) uint16_t buttons = 0, buttonsprev = 0; int aoi_btn(uint8_t btn) { return !!(buttons & (1 << btn)); } typedef struct { int kb_keycode; int js_button; } aoi_input_keymap_t; struct { uint8_t n; //how many input buttons defined, max 16 (16 bit bitmask) ALLEGRO_JOYSTICK* js; //joystick aoi_input_keymap_t keymaps[16]; //keymaps for each input button } aoi_input_config; //init input config variable void aoi_input_init(int n, ALLEGRO_JOYSTICK* js, aoi_input_keymap_t* keymaps) { for (int i = 0; i < n; i++) { aoi_input_config.keymaps[i] = keymaps[i]; } aoi_input_config.n = n; aoi_input_config.js = js; } //parse input config string and init input config void aoi_input_load(char* cfg) { /*cfg of the format: (@joystick name;)kKEY_CODE,jJOYSTICK_BUTTON;...; */ char js_name[200]; int js_name_idx = 0; char c; aoi_input_keymap_t keymaps[16]; uint8_t keymap = 0; while ((c = *cfg++)) { switch (c) { case '@': { while (((c = *cfg++) != ';') && c) js_name[js_name_idx++] = c; js_name[js_name_idx] = 0; } break; case 'k': case 'j': { char t = c; int n = -1; char s[10]; int i = 0; c = *cfg++; while (c && c != ';' && c != ',') { s[i++] = c; c = *cfg++; } //aoi_log("END %c", c); sscanf(s, "%d", &n); if (t == 'k') keymaps[keymap].kb_keycode = n; else keymaps[keymap].js_button = n; if (c == ';' || !c) keymap++; } break; } } int max = al_get_num_joysticks(); ALLEGRO_JOYSTICK* js = NULL; for (int i = 0; i < max; i++) { js = al_get_joystick(i); if (js) { const char* name = al_get_joystick_name(js); if (strcmp(name,js_name) == 0) { aoi_log("loaded joystick '%s' (index %i)", name, i); break; } al_release_joystick(js); js = NULL; } } aoi_input_init(keymap, js, keymaps); } void aoi_input_update() { buttonsprev = buttons; buttons = 0; ALLEGRO_JOYSTICK_STATE jst; if (aoi_input_config.js) al_get_joystick_state(aoi_input_config.js, &jst); ALLEGRO_KEYBOARD_STATE kst; al_get_keyboard_state(&kst); for (int i = 0; i < aoi_input_config.n; i++) { aoi_input_keymap_t map = aoi_input_config.keymaps[i]; if (al_key_down(&kst, map.kb_keycode) || (aoi_input_config.js && jst.button[i])) buttons |= 1 << i; } } /********************** * graphics functions * **********************/ void aoi_cls(aoi_color_t color) { al_clear_to_color((ALLEGRO_COLOR) color); } const int TW_ = AOI_TILE_WIDTH; const int TH_ = AOI_TILE_HEIGHT; //draw sprite from spritesheet void aoi_spr(uint8_t spr, int x, int y, uint8_t flip) { al_draw_bitmap_region(sheet, TW_*(spr&0xF), TH_*(spr>>4), TW_, TH_, x, y, flip); } //same but with extra options void aoi_sprEX(uint8_t spr, int x, int y, uint8_t flip, float scale, float rot, int w, int h) { al_draw_tinted_scaled_rotated_bitmap_region( sheet, TW_*(spr&0xF), TH_*(spr>>4), TW_*w, TH_*h, al_map_rgb(255,255,255), 0, 0, x, y, scale, scale, rot, flip ); } void aoi_line(int x0, int y0, int x1, int y1, aoi_color_t color) { al_draw_line(x0, y0, x1, y1, (ALLEGRO_COLOR) color, -1); } void aoi_rect(int x, int y, int w, int h, uint8_t filled, aoi_color_t color) { if (filled) al_draw_filled_rectangle(x, y, x+w, y+h, (ALLEGRO_COLOR) color); else al_draw_rectangle(x, y, x+w, y+h, (ALLEGRO_COLOR) color, -1); } /***************** * map functions * *****************/ typedef struct { uint8_t width, height; //dimensions in tiles uint8_t* data; } aoi_map_t; aoi_map_t* map = NULL; #define mget(x,y) (map->data[x+y*map->width]) uint8_t aoi_mget(uint8_t x, uint8_t y) { return (map && x < map->width && y < map->height && x > 0 && y > 0) ? mget(x,y) : 0; } #define mset(x,y,t) (map->data[x+y*map->width] = t) void aoi_mset(uint8_t x, uint8_t y, uint8_t tile) { if (map && x < map->width && y < map->height && x >= 0 && y >= 0) mset(x, y, tile); } //draws map void aoi_map(int x, int y) { uint16_t siz = map->width*map->height; for (int i = 0; i <= siz; i++) aoi_spr(map->data[i], TW_*(i%map->width) + x, TH_*((int)i/map->height) + y, 0); } /********************* * loading functions * *********************/ int aoi_load_spritesheet(char* path) { return (sheet = al_load_bitmap(path)) != NULL; } int aoi_load_map(char* path) { ALLEGRO_FILE* f = al_fopen(path, "r"); if (!f) return 0; //read once to get dimensions int rows = 0, columns = 1; int c = 0; while ((c = al_fgetc(f)) != EOF) { if (!rows && c == ',') columns++; else if (c == '\n') rows++; } //aoi_log("%ix%i", columns, rows); map = malloc(sizeof(aoi_map_t)); map->width = columns, map->height = rows; map->data = malloc(map->width*map->height); //read again to get data al_fseek(f, 0, ALLEGRO_SEEK_SET); int n = 0; char s[6]; int s_idx = 0; int i = 0; while ((c = al_fgetc(f)) != EOF) { if (c == ',') { s[s_idx] = 0; s_idx = 0; sscanf(s, "%d", &n); map->data[i++] = (n < 0 ? 0 : n); n = 0; } else if (c == '\n'); else if (s_idx < sizeof(s)) s[s_idx++] = c; } al_fclose(f); return 1; } void aoi_destroy_maps() { free(map->data); free(map); } /***************** * main function * *****************/ int aoi_loop(void); int main(int argc, char** argv) { aoi_log("starting up"); al_init(); al_init_image_addon(); al_install_keyboard(); al_install_joystick(); aoi_assert(display = al_create_display(AOI_INTERNAL_WIDTH, AOI_INTERNAL_HEIGHT)); aoi_assert(aoi_load_spritesheet(AOI_SPRITESHEET_PATH)); //TODO load from run-time/compile-time(for defaults) config file aoi_input_load("@mayflash limited MAYFLASH GameCube Controller Adapter;k26,j0;"); aoi_load_map("map.csv"); int running = 1; unsigned int frames = 0; double init_time = al_get_time(); while (running) { double t0 = al_get_time(); aoi_input_update(); running = !aoi_loop(); al_flip_display(); double delta = al_get_time() - t0; if (delta < 1/AOI_FPS) { al_rest(1/AOI_FPS-delta); } //else aoi_log("lag frame (%u)", frames); frames++; } double t = al_get_time(); aoi_log("%i frames in %f seconds: avg = %fFPS", frames, init_time, frames/(t-init_time)); aoi_log("quitting"); al_destroy_display(display); al_destroy_bitmap(sheet); aoi_destroy_maps(); return 0; }