#include <client_api.h> #include "parse.h" #include "hashmap.h" #define GRID 14 #define COLS 12 #define ROWS 10 #define PADX 0 #define PADY 2 #define SPRITEOFF -1 typedef enum { BG_SOLID = 1, BG_TOP = 2, } bg_flags; typedef struct { char* tile; pbm_t* pbm; uint8_t flags; } bg_t; bg_t bg[100]; int num_bg = 0; typedef enum { GRID_FLIPX = 1, GRID_FLIPY = 2, } grid_flags; typedef struct { uint8_t tile; grid_flags flags; char* tag; } grid_t; grid_t grid[COLS][ROWS]; void grid_pos(grid_t* g, int* gx, int* gy) { int index = (int)(g - &grid[0][0]); *gx = index / ROWS; *gy = index - (*gx * ROWS); } struct hashmap_s tile_tags; typedef struct { // last frame coords float ox; float oy; // current coords float x; float y; pbm_t* graphic; pbm_t* outline; int off_x; int off_y; int outline_pixels; } ent_t; ent_t player; #define OUTLINE 2 bool load_level(char* fname); void unload_bg(); void set_outline(ent_t* ent, int pixels, bool expand, bool dither) { ent->outline = d_expand_bitmap(ent->graphic, pixels, expand, dither); ent->outline_pixels = expand ? pixels : 0; } void set_standard_sprite_off(ent_t* ent) { ent->off_x = ent->off_y = SPRITEOFF; } void reset_pos(ent_t* ent, int x, int y) { ent->x = ent->ox = x; ent->y = ent->oy = y; } int setup(int a) { hashmap_create(32, &tile_tags); player.graphic = f_bitmap("/player.pbm"); set_outline(&player, OUTLINE, true, false); set_standard_sprite_off(&player); reset_pos(&player, 0, 0); if (!load_level("/level14.txt")) { return 1; } return 0; } int remove_all_hashmap(void* const ctx, struct hashmap_element_s* const value) { return -1; } char* last_level = NULL; void unload_bg() { for (int i = 0; i < num_bg; i++) { free(bg[i].pbm); } num_bg = 0; if (last_level) { free(last_level); last_level = NULL; } hashmap_iterate_pairs(&tile_tags, remove_all_hashmap, NULL); } #define SOLID 1 #define TOP 2 bool is_space(char c) { return isspace(c); } bool is_comma_or_space(char c) { return c == ',' || is_space(c); } bool load_level(char* fname) { unload_bg(); memset(grid, 0, sizeof(grid)); size_t len; last_level = (char*)f_contents(fname, &len); if (!last_level) { printf("Couldn't find level: %s\n", fname); return false; } reset_parse = true; // name, file, [flags] char* tile_def[] = { NULL, NULL, NULL }; while (true) { int res = parse(last_level, len, is_space, false); //printf("Parse res: %d\n", res); if (res == DONE) { printf("No tile grid given in level %s\n", fname); return false; } int not_null = 0; for (; not_null < 3; not_null++) { if (!tile_def[not_null]) break; } if (res == NL) { if (not_null < 2) { printf("Invalid tile def on line %d\n", parse_line - 1); return false; } if (!tile_def[2]) tile_def[2] = "NULL"; printf("Parsed tile %d %s %s %s\n", not_null, tile_def[0], tile_def[1], tile_def[2]); bg_flags flags = 0; if (not_null >= 3) { size_t flag_len = strlen(tile_def[2]); for (int i = 0; i < flag_len; i++) { if (tile_def[2][i] == 's') flags |= BG_SOLID; if (tile_def[2][i] == 't') flags |= BG_TOP; } } bool found = false; char f[100]; sprintf(f, "/%s.pbm", tile_def[1]); pbm_t* n = f_bitmap(f); if (n) { found = true; bg_t b = { tile_def[0], n, flags }; bg[num_bg++] = b; printf("Tile %s registered as word %s\n", f, tile_def[0]); } else { int i = 1; sprintf(f, "/%s%d.pbm", tile_def[1], i); while (n = f_bitmap(f)) { found = true; char name[10]; sprintf(name, "%s%d", tile_def[0], i); bg_t b = { malloc(strlen(name) + 1), n, flags }; strcpy(b.tile, name); bg[num_bg++] = b; printf("Tile %s registered as word %s\n", f, name); i++; sprintf(f, "/%s%d.pbm", tile_def[1], i); } } if (!found) { printf("Not found: graphic with name %s\n", tile_def[1]); } memset(tile_def, 0, sizeof(tile_def)); continue; } if (res == BREAK) break; if (not_null < 3) { tile_def[not_null++] = parse_word; } else { printf("Too many words on line %d\n", parse_line); } } int gx = 0; int gy = 0; while (true) { int res = parse(last_level, len, is_comma_or_space, false); //printf("Parse res: %d\n", res); if (res == DONE || res == BREAK) { if (gy < ROWS) { printf("Didn't read full grid, only %d,%d\n", gx, gy); return false; } break; } if (res == NL) { if (gx < COLS) { printf("Couldn't read full row, only to index %d on line %d\n", gx - 1, parse_line); return false; } gx = 0; gy++; } if (res == WORD) { if (gx >= COLS || gy >= ROWS) { printf("Grid is too big, reached %d,%d on line %d\n", gx, gy, parse_line); return false; } char* prob = NULL; char* tag = NULL; grid_flags flags = 0; for (int i = 0; i < 2; i++) { if (parse_word[0] == '-') { flags |= GRID_FLIPX; parse_word++; } else if (parse_word[0] == '!') { flags |= GRID_FLIPY; parse_word++; } } char* last_word = parse_word; size_t l = strlen(last_word); for (int i = 0; i < l; i++) { if (last_word[i] == '.') { last_word[i] = 0; prob = last_word + (i + 1); last_word = prob; l = strlen(prob); break; } } for (int i = 0; i < l; i++) { if (last_word[i] == ':') { last_word[i] = 0; tag = last_word + (i + 1); last_word = tag; l = strlen(tag); break; } } //printf("Searching %s\n", parse_word); for (int i = 0; i < num_bg; i++) { //printf(" Check %s\n", bg[i].tile); if (strcmp(bg[i].tile, parse_word) == 0) { if (prob) { int p = max(0, min(9, atoi(prob))); if (random(1, 10) > p) continue; } //printf("Tile %d on %d,%d\n", i, gx, gy); grid_t g = { i + 1, flags, tag }; if (tag) hashmap_put(&tile_tags, tag, strlen(tag), &grid[gx][gy]); grid[gx][gy] = g; } } gx++; } } return 0; } float speed = 80; bool move_debug = false; bool r_intersect(int x1, int y1, int w1, int h1, int x2, int y2, int w2, int h2) { if (x1 >= x2 + w2 || x1 + w2 <= x2 || y1 >= y2 + h2 || y1 + h2 <= y2) return false; if (move_debug) printf("INTERSECT [%d,%d + %d,%d] [%d,%d + %d,%d]\n", x1, y1, w1, h1, x2, y2, w2, h2); return true; } bool r_free(int px, int py, int w, int h) { int minX = px / GRID; int maxX = (px + w - 1) / GRID; int minY = py / GRID; int maxY = (py + h - 1) / GRID; for (int i = max(0, minX); i <= min(COLS - 1, maxX); i++) { for (int j = max(0, minY); j <= min(ROWS - 1, maxY); j++) { if (grid[i][j].tile != 0 && (bg[grid[i][j].tile - 1].flags & SOLID)) { if (r_intersect(px, py, w, h, i * GRID + 1, j * GRID + 1, GRID - 2, GRID - 2)) return false; } } } return true; } bool in_door = false; void entered_tile(ent_t* ent, int gx, int gy) { grid_t g = grid[gx][gy]; if (g.tag) { if (ent == &player) { void* find = NULL; if (strcmp(g.tag, "d1") == 0) { if (!in_door && (find = hashmap_get(&tile_tags, "d2", 2)) != NULL) { int target_gx, target_gy; grid_pos((grid_t*)find, &target_gx, &target_gy); ent->x = target_gx * GRID; ent->y = target_gy * GRID; in_door = true; } } if (strcmp(g.tag, "d2") == 0) { if (!in_door && (find = hashmap_get(&tile_tags, "d1", 2)) != NULL) { int target_gx, target_gy; grid_pos((grid_t*)find, &target_gx, &target_gy); ent->x = target_gx * GRID; ent->y = target_gy * GRID; in_door = true; } } } } } void exited_tile(ent_t* ent, int gx, int gy) { grid_t g = grid[gx][gy]; } void render_ent(ent_t* ent) { if (ent->outline) { d_pbm( (uint16_t)roundf(ent->x) - ent->outline_pixels + PADX + ent->off_x, (uint16_t)roundf(ent->y) - ent->outline_pixels + PADY + ent->off_y, ent->outline, 0, 0, 0, 0, WHITE, TRANSPARENT, R_NONE, false, false); } d_pbm( (uint16_t)roundf(ent->x) + PADX + ent->off_x, (uint16_t)roundf(ent->y) + PADY + ent->off_y, ent->graphic, 0, 0, 0, 0, BLACK, TRANSPARENT, R_NONE, false, false); } void update_ent_movement(ent_t* ent, bool debug); int loop(int ms) { float t = ms / 1000.0f; if (button_down(DPAD_RIGHT)) player.x += t * speed; if (button_down(DPAD_LEFT)) player.x -= t * speed; if (button_down(DPAD_DOWN)) player.y += t * speed; if (button_down(DPAD_UP)) player.y -= t * speed; if (button_pressed(BUTTON_A)) move_debug = !move_debug; update_ent_movement(&player, move_debug); in_door = false; d_clear(); for (int i = 0; i < COLS; i++) { for (int j = 0; j < ROWS; j++) { if (grid[i][j].tile != 0 && !(bg[grid[i][j].tile - 1].flags & TOP)) { d_pbm( PADX + i * GRID + SPRITEOFF, PADY + j * GRID + SPRITEOFF, bg[grid[i][j].tile - 1].pbm, 0,0,0,0, BLACK, TRANSPARENT, R_NONE, grid[i][j].flags & GRID_FLIPX, grid[i][j].flags & GRID_FLIPY); } } } render_ent(&player); for (int i = 0; i < COLS; i++) { for (int j = 0; j < ROWS; j++) { if (grid[i][j].tile != 0 && (bg[grid[i][j].tile - 1].flags & TOP)) { d_fillRect( PADX + i * GRID, PADY + j * GRID, GRID, GRID, WHITE ); } } } for (int i = 0; i < COLS; i++) { for (int j = 0; j < ROWS; j++) { if (grid[i][j].tile != 0 && (bg[grid[i][j].tile - 1].flags & TOP)) { d_pbm( PADX + i * GRID + SPRITEOFF, PADY + j * GRID + SPRITEOFF, bg[grid[i][j].tile - 1].pbm, 0,0,0,0, BLACK, TRANSPARENT, R_NONE, grid[i][j].flags & GRID_FLIPX, grid[i][j].flags & GRID_FLIPY); } } } return 0; } #define ENTER_GRID_PCT 0.2f void get_close_grid(float x, float y, int* gx, int* gy, float* pct_x, float* pct_y) { float grid_x, grid_y; *pct_x = modff((x + GRID * 0.5f) / GRID, &grid_x) - 0.5f; *pct_y = modff((y + GRID * 0.5f) / GRID, &grid_y) - 0.5f; if (fabsf(*pct_x) > ENTER_GRID_PCT || fabsf(*pct_y) > ENTER_GRID_PCT) { grid_x = -1; grid_y = -1; } *gx = (int)grid_x; *gy = (int)grid_y; } void update_ent_movement(ent_t* ent, bool debug) { int ox_i = (int)roundf(ent->ox); int oy_i = (int)roundf(ent->oy); if (ent->x != ent->ox || ent->y != ent->oy) { int x_i = (int)roundf(ent->x); int y_i = (int)roundf(ent->y); if (ent->x > ent->ox) x_i = (int)roundf(ent->x + 0.5f); if (ent->x < ent->ox) x_i = (int)roundf(ent->x - 0.5f); if (ent->y > ent->oy) y_i = (int)roundf(ent->y + 0.5f); if (ent->y < ent->oy) y_i = (int)roundf(ent->y - 0.5f); bool goX = ox_i != x_i; bool goY = oy_i != y_i; if (debug) printf("(%f,%f)->(%f,%f) I: (%d,%d)->(%d,%d) [%d, %d]\n", ent->ox, ent->oy, ent->x, ent->y, ox_i, oy_i, x_i, y_i, goX, goY); while (goX || goY) { if (goX) { int testX = ox_i < x_i ? ox_i + 1 : ox_i - 1; bool test = r_free(testX + 1, oy_i + 1, GRID - 2, GRID - 2); if (move_debug) printf("testX: %d => %d\n", testX, test); if (test) { ox_i = testX; if (ox_i == x_i) goX = false; if (move_debug) printf("ox_i = %d, goX = %d\n", ox_i, goX); } else { goX = false; if (move_debug) printf("goX = false\n"); } } if (goY) { int testY = oy_i < y_i ? oy_i + 1 : oy_i - 1; bool test = r_free(ox_i + 1, testY + 1, GRID - 2, GRID - 2); if (move_debug) printf("testY: %d => %d\n", testY, test); if (test) { oy_i = testY; if (oy_i == y_i) goY = false; if (move_debug) printf("oy_i = %d, goY = %d\n", oy_i, goY); } else { goY = false; if (move_debug) printf("goY = false\n"); } } } if (ox_i != x_i) ent->x = ox_i; if (oy_i != y_i) ent->y = oy_i; if (debug) printf("reached (%d,%d) on target of (%d,%d), x,y=(%f,%f)\n", ox_i, oy_i, x_i, y_i, ent->x, ent->y); if (debug) printf("\n====================\n\n"); int o_grid_x, o_grid_y; float o_ingrid_x, o_ingrid_y; int grid_x, grid_y; float ingrid_x, ingrid_y; get_close_grid(ent->ox, ent->oy, &o_grid_x, &o_grid_y, &o_ingrid_x, &o_ingrid_y); int tries = 10; for (; tries > 0; tries--) { get_close_grid(ent->x, ent->y, &grid_x, &grid_y, &ingrid_x, &ingrid_y); if (grid_x == o_grid_x && grid_y == o_grid_y) { break; } else { if (o_grid_x != -1) { exited_tile(ent, o_grid_x, o_grid_y); //printf("Exited: %f %f\n", o_grid_x, o_grid_y); } if (grid_x != -1) { entered_tile(ent, grid_x, grid_y); //printf("Entered: %f %f\n", grid_x, grid_y); } o_grid_x = grid_x; o_grid_y = grid_y; } } if (tries == 0) printf("RAN OUT OF TILE CHECK TRIES\n"); } ent->ox = ent->x; ent->oy = ent->y; }