#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;
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;
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)
{
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)
{
unload_level();
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;
}
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]);
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++;
}
}
sprintf(filename, "/%s.bin", level_name);
level_code = f_code(filename);
if (level_code)
{
LOAD_DYN(level_entered_tile)
LOAD_DYN(level_exited_tile)
LOAD_DYN(level_update)
}
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 (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_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);
}
}
}
if (nextLevel[0] != 0)
{
unload_level();
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);
player.x = target_gx * GRID;
player.y = target_gy * GRID;
}
}
nextLevel[0] = 0;
nextLevelTag[0] = 0;
}
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 (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;
}