aoi/aoi.c

320 lines
6.8 KiB
C

#include <stdio.h>
#include <stdint.h>
#include <allegro5/allegro.h>
#include <allegro5/allegro_image.h>
#include <allegro5/allegro_primitives.h>
#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;
}