#include "level.h" #include "conf.h" #include "missile.h" #include "player.h" #include "polarity.h" #include "tile.h" #include "util.h" #include "vec.h" #include "visual_data.h" #include #include #include #include #include static struct Level level; struct LevelBin *user_level = NULL; extern bopti_image_t bimg_tileset; extern struct LevelBin kble_test, kble_burn, kble_bounce, kble_worm, kble_shake, kble_tilt, kble_cave, kble_headtrauma, kble_gimmick, kble_failure, kble_design, kble_safe, kble_harder; static const struct LevelBinNamed levels[] = { {&kble_test, "gentle introduction"}, {&kble_safe, "area 102"}, {&kble_harder, "harder the fall"}, {&kble_design, "this is my design"}, {&kble_failure, "insert a coin"}, {&kble_cave, "the light at the end of the tunnel"}, {&kble_gimmick, "sell your gimmick"}, {&kble_headtrauma, "minefield"}, {&kble_burn, "these are rare"}, {&kble_bounce, "deceptive road"}, {&kble_shake, "breaking into reality"}, {&kble_tilt, "tilted"}, {&kble_worm, "under your skin"}, {NULL, NULL}}; static void level_free(void); static void load(const struct LevelBin *); void level_init(struct Player *p) { level.data = NULL; level.visual_data = NULL; level.player = p; } void level_deinit(void) { level_free(); if (user_level != NULL) { free(user_level); user_level = NULL; } } void level_load(int id) { const struct LevelBin *const b = levels[id].bin; load(b); level.id = id; } void level_save_kble(const char *path) { const ssize_t fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644); unsigned char data[LEVELBIN_SIZE] = {0}; int i; data[0] = 0; data[1] = 1; data[3] = level.width; data[5] = level.height; i = level.size; while (i-- > 0) data[i + 6] = level.data[i]; if (fd >= 0) { write(fd, data, sizeof(data)); close(fd); } } void level_load_kble(const char *path) { const ssize_t fd = open(path, O_RDONLY); if (user_level == NULL) { user_level = malloc(LEVELBIN_SIZE); if (user_level == NULL) return; } if (fd >= 0) { size_t bytes; bytes = read(fd, user_level, LEVELBIN_SIZE); close(fd); if (bytes != LEVELBIN_SIZE) return; if (user_level->format != 0) return; if (user_level->chunk_size != 1) return; if (user_level->width != LEVEL_WIDTH) return; if (user_level->height != LEVEL_HEIGHT) return; } } void level_load_kble_followup(void) { if (user_level != NULL) { load(user_level); level_regen_visual_data(1); } } void level_reload(void) { level_load(level.id); } void level_next(void) { if (levels[level.id + 1].bin != NULL) level_load(level.id + 1); else level_reload(); } void level_regen_visual_data(int editing) { const int tileset_width = bimg_tileset.width / TILE_SIZE; int y = level.height; while (y-- > 0) { int x = level.width; while (x-- > 0) { const int i = x + y * level.width; const int tile = level.data[i]; struct VisualData *const vd = &level.visual_data[i]; vd->x = x * TILE_SIZE; vd->y = y * TILE_SIZE; vd->img_x = tile % tileset_width * TILE_SIZE; vd->img_y = (int)(tile / tileset_width) * TILE_SIZE; vd->visible = tile != 0; if (editing) continue; switch (tile) { case TILE_RED: case TILE_BURN_RED: vd->img_y += polarity() ? TILE_SIZE : 0; break; case TILE_BLUE: case TILE_BURN_BLUE: vd->img_y += polarity() ? 0 : TILE_SIZE; break; default: break; } } } } void level_draw(void) { int i = level.size; while (i-- > 0) { const struct VisualData *const vd = &level.visual_data[i]; if (vd->visible) { dsubimage(vd->x + DRAW_OFF_X, vd->y, &bimg_tileset, vd->img_x, vd->img_y, TILE_SIZE, TILE_SIZE, 0); } } } void level_draw_name(void) { dputs_outline(DWIDTH - 4, DHEIGHT - 2, DTEXT_RIGHT, DTEXT_BOTTOM, levels[level.id].name); } int level_get(int x, int y) { if (x < 0 || y < 0 || x >= level.width || y >= level.height) return TILE_OOB; return level.data[x + y * level.width]; } int level_get_px(int x, int y) { return level_get(x / TILE_SIZE, y / TILE_SIZE); } void level_set(int x, int y, int v) { if (x < 0 || y < 0 || x >= level.width || y >= level.height) return; level.data[x + y * level.width] = v; } void level_set_px(int x, int y, int v) { level_set(x / TILE_SIZE, y / TILE_SIZE, v); } struct Vec level_find(enum Tile t) { int i = level.size; while (i-- > 0 && level.data[i] != t) ; return (struct Vec){i % level.width, i / level.width}; } int level_count(enum Tile t) { int c = 0; int i = level.size; while (i-- > 0) c += level.data[i] == t; return c; } static void load(const struct LevelBin *b) { int i = b->width * b->height; level_free(); level.width = b->width; level.height = b->height; level.size = i; level.data = malloc(i); level.visual_data = malloc(i * sizeof(struct VisualData)); while (i-- > 0) level.data[i] = b->data[i]; polarity_reset(); level_regen_visual_data(0); player_spawn(level.player); /* missiles */ missile_manager_init(); i = level.size; while (i-- > 0) if (level.data[i] == TILE_MISSILE_LAUNCHER) missile_new(i % level.width * TILE_SIZE + TILE_SIZE / 2, (int)(i / level.width) * TILE_SIZE + TILE_SIZE / 2); } struct Vec level_dim(void) { return (struct Vec){level.width, level.height}; } static void level_free(void) { if (level.data != NULL) { free(level.data); level.data = NULL; } if (level.visual_data != NULL) { free(level.visual_data); level.visual_data = NULL; } }