#include <client_api.h> #include "grid.h" #include "ent.h" #include "parse.h" #include "hashmap.h" #include <ALLOC_IMPORTS.h> #include "level_imports.h" #include "constants.h" typedef enum { BG_SOLID = 1, BG_TOP = 2, } bg_flags; typedef struct { char tile[12]; pbm_t* pbm; uint8_t flags; } bg_t; bg_t bg[100]; int num_bg = 0; grid_t grid[COLS][ROWS]; __EXPORT__ 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; dir_entry_t* rootDir; size_t rootDirSize; bool in_root_dir(char* f) { for (int i = 0; i < rootDirSize; i++) { // + 1 to skip the opening / if (strcmp(rootDir[i].name, f + 1) == 0) return true; } return false; } ent_t player; #define OUTLINE 2 bool load_level(char* fname); void unload_level(); 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; } __EXPORT__ int setup(int a) { rootDir = f_list("/", &rootDirSize); for (int i = 0; i < rootDirSize; i++) { printf2("/%s : %d\n", rootDir[i].name, rootDir[i].type); } hashmap_create(32, &tile_tags); player.graphic = f_bitmap("/player.pbm"); set_outline(&player, OUTLINE, true, false); set_standard_sprite_off(&player); if (!load_level("level14")) { return 1; } return 0; } int remove_all_hashmap(void* const ctx, struct hashmap_element_s* const value) { return -1; } char* last_level = NULL; void* level_code = NULL; void unload_level() { level_update = NULL; level_entered_tile = NULL; level_exited_tile = NULL; if (level_code) unload_code(level_code); level_code = NULL; for (int i = 0; i < num_bg; i++) { free(bg[i].pbm); } num_bg = 0; hashmap_iterate_pairs(&tile_tags, remove_all_hashmap, NULL); if (last_level) { free(last_level); last_level = 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* level_name) { unsigned long now = micros(); unsigned long start = now; unsigned long then = now; printf3("load_level at %lu\n", now); unload_level(); then = now; now = micros(); printf2("unload_level() finished at %lu DLT: %lu\n", now, now - then); memset(grid, 0, sizeof(grid)); char filename[100]; sprintf(filename, "/%s.txt", level_name); size_t len; last_level = (char*)f_contents(filename, &len); if (!last_level) { printf("Couldn't find level: %s\n", filename); return false; } then = now; now = micros(); printf2("load_level() f_contents() finished at %lu DLT: %lu\n", now, now - then); 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", filename); 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]); now = micros(); pbm_t* n = in_root_dir(f) ? f_bitmap(f) : NULL; unsigned long then2 = now; now = micros(); printf2(" load_level() f_bitmap(%s) finished at %lu DLT: %lu\n", f, now, now - then2); if (n) { found = true; bg_t b; strcpy(b.tile, tile_def[0]); b.pbm = n; b.flags = flags; bg[num_bg] = b; num_bg++; printf("Tile %s registered as word %s\n", f, tile_def[0]); } else { sprintf(f, "/%s.pbp", tile_def[1]); now = micros(); size_t count = 0; pbm_t** ns = in_root_dir(f) ? f_bitmaps_packed(f, &count) : NULL; then2 = now; now = micros(); printf2(" load_level() f_bitmaps_packed(%s) (%d bitmaps) finished at %lu DLT: %lu\n", f, count, now, now - then2); if (count > 0) { found = true; for (int i = 0; i < count; i++) { char name[10]; sprintf(name, "%s%d", tile_def[0], i + 1); bg_t b; strcpy(b.tile, name); b.pbm = ns[i]; b.flags = flags; bg[num_bg] = b; num_bg++; printf("Tile %s(%d) registered as word %s\n", f, i, name); } free(ns); } else { int i = 1; sprintf(f, "/%s%d.pbm", tile_def[1], i); now = micros(); while (n = (in_root_dir(f) ? f_bitmap(f) : NULL)) { then2 = now; now = micros(); printf2(" load_level() f_bitmap(%s) finished at %lu DLT: %lu\n", f, now, now - then2); found = true; char name[10]; sprintf(name, "%s%d", tile_def[0], i); bg_t b; strcpy(b.tile, name); b.pbm = n; b.flags = flags; bg[num_bg] = b; num_bg++; printf("Tile %s registered as word %s\n", f, name); i++; sprintf(f, "/%s%d.pbm", tile_def[1], i); now = micros(); } } } 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); } } then = now; now = micros(); printf2("load_level() parsed tile defs finished at %lu DLT: %lu\n", now, now - then); 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; break; } } gx++; } } then = now; now = micros(); printf2("load_level() parse grid finished at %lu DLT: %lu\n", now, now - then); sprintf(filename, "/%s.bin", level_name); level_code = in_root_dir(filename) ? f_code(filename) : NULL; if (level_code) { LOAD_DYN(level_entered_tile) LOAD_DYN(level_exited_tile) LOAD_DYN(level_update) } then = now; now = micros(); printf3("load_level() f_code() finished at %lu DLT: %lu, total: %lu\n", now, now - then, now - start); reset_pos(&player, 0, 0); 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; } __EXPORT__ bool is_player(ent_t* ent) { return ent == &player; } __EXPORT__ grid_t* get_tile_by_tag(char* tag) { return (grid_t*)hashmap_get(&tile_tags, tag, strlen(tag)); } void entered_tile(ent_t* ent, int gx, int gy) { grid_t g = grid[gx][gy]; if (level_entered_tile) level_entered_tile(ent, gx, gy, g); } void exited_tile(ent_t* ent, int gx, int gy) { grid_t g = grid[gx][gy]; if (level_exited_tile) level_exited_tile(ent, gx, gy, g); } 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); char nextLevel[32] = ""; char nextLevelTag[32] = ""; bool in_door = false; __EXPORT__ int loop(int ms) { float t = ms / 1000.0f; if (nextLevel[0] != 0) { load_level(nextLevel); if (nextLevelTag[0] != 0) { grid_t* find = get_tile_by_tag(nextLevelTag); if (find) { int target_gx, target_gy; grid_pos(find, &target_gx, &target_gy); reset_pos(&player, target_gx * GRID, target_gy * GRID); } } nextLevel[0] = 0; nextLevelTag[0] = 0; } 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); if (level_update) level_update(t); in_door = false; d_clearMem(); 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; } __EXPORT__ bool door(grid_t g, char* t1, char* level, char* t2) { if (in_door) return false; if (g.tag && strcmp(g.tag, t1) == 0) { if (level) { in_door = true; strcpy(nextLevel, level); strcpy(nextLevelTag, t2); } else { grid_t* find = get_tile_by_tag(t2); if (find) { in_door = true; int target_gx, target_gy; grid_pos(find, &target_gx, &target_gy); player.x = target_gx * GRID; player.y = target_gy * GRID; } } } return in_door; } 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 (debug) printf("testX: %d => %d\n", testX, test); if (test) { ox_i = testX; if (ox_i == x_i) goX = false; if (debug) printf("ox_i = %d, goX = %d\n", ox_i, goX); } else { goX = false; if (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 (debug) printf("testY: %d => %d\n", testY, test); if (test) { oy_i = testY; if (oy_i == y_i) goY = false; if (debug) printf("oy_i = %d, goY = %d\n", oy_i, goY); } else { goY = false; if (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; }