#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 pbm_t* player; pbm_t* player_out; 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* x, int* y) { int index = (int)(g - &grid[0][0]); *x = index / ROWS; *y = index - (*x * ROWS); } struct hashmap_s tile_tags; #define OUTLINE 2 bool load_level(char* fname); void unload_bg(); int setup(int a) { hashmap_create(32, &tile_tags); player = f_bitmap("/player.pbm"); player_out = d_expand_bitmap(player, OUTLINE, true, false); 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 x = 0; int y = 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 (y < ROWS) { printf("Didn't read full grid, only %d,%d\n", x, y); return false; } break; } if (res == NL) { if (x < COLS) { printf("Couldn't read full row, only to index %d on line %d\n", x - 1, parse_line); return false; } x = 0; y++; } if (res == WORD) { if (x >= COLS || y >= ROWS) { printf("Grid is too big, reached %d,%d on line %d\n", x, y, 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, x, y); grid_t g = { i + 1, flags, tag }; if (tag) hashmap_put(&tile_tags, tag, strlen(tag), &grid[x][y]); grid[x][y] = g; } } x++; } } return 0; } float x = 0; float y = 0; int rot = 0; float speed = 80; bool flipX = false, flipY = false; void d_pbm_outline(int16_t x, int16_t y, pbm_t* pbm) { for (int i = x - 1; i <= x + 1; i++) { for (int j = y - 1; j <= y + 1; j++) { d_pbm(i, j, pbm, 0, 0, 0, 0, WHITE, TRANSPARENT, R_NONE, false, false); } } } 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 x, int y, int w, int h) { int minX = x / GRID; int maxX = (x + w - 1) / GRID; int minY = y / GRID; int maxY = (y + 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(x, y, w, h, i * GRID + 1, j * GRID + 1, GRID - 2, GRID - 2)) return false; } } } return true; } #define ENTER_GRID_PCT 0.2f void entered_tile(int tx, int ty) { grid_t g = grid[tx][ty]; if (g.tag) { void* find = NULL; if (strcmp(g.tag, "d1") == 0) { if ((find = hashmap_get(&tile_tags, "d2", 2)) != NULL) { int gx, gy; grid_pos((grid_t*)find, &gx, &gy); x = gx * GRID; y = gy * GRID; } } } } void exited_tile(int x, int y) { grid_t g = grid[x][y]; } int loop(int ms) { 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); } } } //d_fillRect(0, 0, SCREEN_W, SCREEN_H, BLACK); d_pbm( (uint16_t)roundf(x) - OUTLINE + PADX + SPRITEOFF, (uint16_t)roundf(y) - OUTLINE + PADY + SPRITEOFF, player_out, 0, 0, 0, 0, WHITE, TRANSPARENT, rot, flipX, flipY); d_pbm( (uint16_t)roundf(x) + PADX + SPRITEOFF, (uint16_t)roundf(y) + PADY + SPRITEOFF, player, 0, 0, 0, 0, BLACK, TRANSPARENT, rot, flipX, flipY); 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); } } } /* if (button_pressed(BUTTON_A)) { rot = (rot + 1) % 4; } if (button_pressed(BUTTON_B)) { flipX = !flipX; if (!flipX) { flipY = !flipY; } printf("Flip: %d %d\n", flipX, flipY); } */ float ox = x; float oy = y; int ox_i = (int)roundf(ox); int oy_i = (int)roundf(oy); float o_grid_x; float o_grid_y; float o_ingrid_x = modff((x + GRID * 0.5f) / GRID, &o_grid_x) - 0.5f; float o_ingrid_y = modff((y + GRID * 0.5f) / GRID, &o_grid_y) - 0.5f; if (fabsf(o_ingrid_x) > ENTER_GRID_PCT || fabsf(o_ingrid_y) > ENTER_GRID_PCT) { o_grid_x = -1; o_grid_y = -1; } float t = ms / 1000.0f; if (button_down(DPAD_RIGHT)) x += t * speed; if (button_down(DPAD_LEFT)) x -= t * speed; if (button_down(DPAD_DOWN)) y += t * speed; if (button_down(DPAD_UP)) y -= t * speed; if (button_pressed(BUTTON_A)) move_debug = !move_debug; if (x != ox || y != oy) { int x_i = (int)roundf(x); int y_i = (int)roundf(y); if (x > ox) x_i = (int)roundf(x + 0.5f); if (x < ox) x_i = (int)roundf(x - 0.5f); if (y > oy) y_i = (int)roundf(y + 0.5f); if (y < oy) y_i = (int)roundf(y - 0.5f); bool goX = ox_i != x_i; bool goY = oy_i != y_i; if (move_debug) printf("(%f,%f)->(%f,%f) I: (%d,%d)->(%d,%d) [%d, %d]\n", ox, oy, x, 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) x = ox_i; if (oy_i != y_i) y = oy_i; if (move_debug) printf("reached (%d,%d) on target of (%d,%d), x,y=(%f,%f)\n", ox_i, oy_i, x_i, y_i, x, y); if (move_debug) printf("\n====================\n\n"); float grid_x; float grid_y; float ingrid_x = modff((x + GRID * 0.5f) / GRID, &grid_x) - 0.5f; float ingrid_y = modff((y + GRID * 0.5f) / GRID, &grid_y) - 0.5f; if (fabsf(ingrid_x) > ENTER_GRID_PCT || fabsf(ingrid_y) > ENTER_GRID_PCT) { grid_x = -1; grid_y = -1; } if (grid_x != o_grid_x || grid_y != o_grid_y) { if (o_grid_x != -1) { exited_tile((int)o_grid_x, (int)o_grid_y); //printf("Exited: %f %f\n", o_grid_x, o_grid_y); } if (grid_x != -1) { entered_tile((int)grid_x, (int)grid_y); //printf("Entered: %f %f\n", grid_x, grid_y); } } } return 0; }