diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..4a6a890 --- /dev/null +++ b/.clang-format @@ -0,0 +1,94 @@ +BasedOnStyle: LLVM +AccessModifierOffset: -4 +AlignAfterOpenBracket: DontAlign +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignConsecutiveMacros: false +AlignEscapedNewlines: Right +AlignTrailingComments: true +AlignOperands: true +AlignTrailingComments: true +AllowAllArgumentsOnNextLine: false +AllowAllConstructorInitializersOnNextLine: false +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: true +AllowShortFunctionsOnASingleLine: None +AllowShortIfStatementsOnASingleLine: Never +AllowShortLambdasOnASingleLine: None +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: true +AlwaysBreakTemplateDeclarations: Yes +BinPackArguments: false +BinPackParameters: false +BraceWrapping: + AfterCaseLabel: false + AfterClass: true + AfterControlStatement: false + AfterEnum: true + AfterFunction: true + AfterNamespace: true + AfterStruct: true + AfterUnion: true + AfterExternBlock: true + BeforeCatch: false + BeforeElse: false + IndentBraces: false + SplitEmptyFunction: false + SplitEmptyRecord: false + SplitEmptyNamespace: false +BreakBeforeBinaryOperators: All +BreakBeforeBraces: Custom +BreakBeforeTernaryOperators: true +BreakConstructorInitializers: AfterColon +BreakInheritanceList: AfterColon +BreakStringLiterals: true +ColumnLimit: 120 +CompactNamespaces: true +ConstructorInitializerAllOnOneLineOrOnePerLine: true +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: false +DerivePointerAlignment: false +DisableFormat: false +ExperimentalAutoDetectBinPacking: true +FixNamespaceComments: false +IncludeBlocks: Regroup +IncludeCategories: + - Regex: '".*"' + Priority: 1 + - Regex: '<[a-zA-Z0-9.]*>' + Priority: 3 + - Regex: '.*' + Priority: 2 +IndentCaseLabels: false +IndentPPDirectives: BeforeHash +IndentWidth: 4 +IndentWrappedFunctionNames: false +KeepEmptyLinesAtTheStartOfBlocks: false +Language: Cpp +MaxEmptyLinesToKeep: 2 +NamespaceIndentation: Inner +PointerAlignment: Right +ReflowComments: true +SortIncludes: true +SortUsingDeclarations : true +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: false +SpaceBeforeAssignmentOperators: true +SpaceBeforeCpp11BracedList: true +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Auto +TabWidth: 4 +UseTab: ForContinuationAndIndentation \ No newline at end of file diff --git a/.gitignore b/.gitignore index 6a4818f..9cb6e41 100644 --- a/.gitignore +++ b/.gitignore @@ -2,5 +2,5 @@ a.out debug_log tags -bin/torch -bin/debug +bin +.vs diff --git a/src/color.c b/src/color.c index 1373d35..ff67bc9 100644 --- a/src/color.c +++ b/src/color.c @@ -4,7 +4,7 @@ struct color color_add(struct color c, struct color a) { - return (struct color){ + return (struct color) { min(c.r + a.r, 255), min(c.g + a.g, 255), min(c.b + a.b, 255), @@ -13,7 +13,7 @@ struct color color_add(struct color c, struct color a) struct color color_multiply_by(struct color c, float m) { - return (struct color){ + return (struct color) { min(c.r * m, 255), min(c.g * m, 255), min(c.b * m, 255), diff --git a/src/demo.c b/src/demo.c index 7074f2d..a2097e6 100644 --- a/src/demo.c +++ b/src/demo.c @@ -1,12 +1,12 @@ #include "torch.h" #include "ui.h" -#include -#include #include #include #include +#include #include +#include struct floor *cur_floor = &floors[0]; @@ -15,15 +15,15 @@ void demo_place_snake(int y, int x); void demo_floor_load_map(const char *filename) { -// FILE *mapfp = fopen(filename, "r"); + // FILE *mapfp = fopen(filename, "r"); floor_map_generate(&floors[0], CAVE); /* Make this not shit please. */ for (size_t line = 0; line < MAP_LINES; ++line) { for (size_t col = 0; col < MAP_COLS; ++col) { -// fscanf(mapfp, "%c", &demo_floor.map[line][col].token); + // fscanf(mapfp, "%c", &demo_floor.map[line][col].token); floors[0].map[line][col].light = 0; - floors[0].map[line][col].color = (struct color){ 51, 51, 51 }; + floors[0].map[line][col].color = (struct color) { 51, 51, 51 }; #if 0 if (demo_floor.map[line][col].token != '.') { @@ -32,10 +32,10 @@ void demo_floor_load_map(const char *filename) } #endif } -// (void)fgetc(mapfp); + // (void)fgetc(mapfp); } -// fclose(mapfp); + // fclose(mapfp); for (int i = 0; i < 20; ++i) { int y = rand() % MAP_LINES; @@ -46,7 +46,8 @@ void demo_floor_load_map(const char *filename) int drawn_to[MAP_LINES][MAP_COLS] = { 0 }; -struct light_info { +struct light_info +{ tile_map *map; float bright; int y, x; @@ -67,7 +68,7 @@ void cast_light_at(struct tile *tile, int y, int x, void *context) (*info->map)[y][x].light += info->bright; } else { (*info->map)[y][x].light += dlight; -// assert((*info->map)[y][x].light > 0); + // assert((*info->map)[y][x].light > 0); /* = info->r * dlight + tile.dcolor */ (*info->map)[y][x].dcolor = color_add(color_multiply_by(info->color, dlight), tile->dcolor); } @@ -75,8 +76,6 @@ void cast_light_at(struct tile *tile, int y, int x, void *context) def_entity_fn(demo_player_update) { - int y = this->posy; - int x = this->posx; float bright = player_lantern_on && player_fuel > 0 ? 0.5f : 0.1f; if (player_lantern_on) { if (player_fuel > 0) { @@ -85,41 +84,50 @@ def_entity_fn(demo_player_update) player_lantern_on = false; } } - //cast_light(this->floor->map, y, x, 6, 0.3f, this->r, this->g, this->b); - int radius = sqrt(1.f / (1.f / 255)); - raycast_at(this->floor, y, x, radius, &cast_light_at, - &(struct light_info) { - .map = &this->floor->map, - .bright = bright, .y = y, .x = x, - .color = this->color, - }); + + // cast_light(this->floor->map, y, x, 6, 0.3f, this->r, this->g, this->b); + + raycast_at(&(struct raycast_params) { + .callback = &cast_light_at, + .context = &(struct light_info) { .map = &this->floor->map, + .bright = bright, + .y = this->posy, + .x = this->posx, + .color = this->color }, + .floor = this->floor, + .y = this->posy, + .x = this->posx, + .radius = sqrt(255.0f), + }); + memset(drawn_to, 0, (sizeof(drawn_to[0][0]) * MAP_LINES * MAP_COLS)); } def_entity_fn(demo_torch_update) { - int y = (rand() % 3 - 1); - int x = (rand() % 3 - 1); + // entity_move_pos_rel(this, y, x); -// entity_move_pos_rel(this, y, x); + // cast_light(this->floor->map, y, x, 6, 1.f, this->r, this->g, this->b); - y = this->posy; - x = this->posx; - //cast_light(this->floor->map, y, x, 6, 1.f, this->r, this->g, this->b); - int radius = sqrt(1.f / (1.f / 255)); - raycast_at(this->floor, y, x, radius, &cast_light_at, - &(struct light_info) { + raycast_at(&(struct raycast_params) { + .callback = &cast_light_at, + .context = &(struct light_info) { .map = &this->floor->map, - .bright = 1.f, .y = y, .x = x, + .bright = 1.0f, + .y = this->posy, + .x = this->posx, .color = this->color, - }); + }, + .floor = this->floor, + .y = this->posy, + .x = this->posx, + .radius = sqrt(255.0f) + }); memset(drawn_to, 0, (sizeof(drawn_to[0][0]) * MAP_LINES * MAP_COLS)); } def_entity_fn(demo_torch_destroy) -{ - -} +{} void demo_add_entities(void) { @@ -168,7 +176,9 @@ struct entity demo_new_torch(int y, int x) { struct entity torch = { .color = { - .r = 0xe2, .g = 0x58, .b = 0x22, + .r = 0xe2, + .g = 0x58, + .b = 0x22, }, .token = '!', .posy = y, .posx = x, @@ -196,9 +206,9 @@ def_input_key_fn(place_torch) case 'k': y--; break; case 'l': x++; break; } - struct entity torch = demo_new_torch(y, x); - struct entity *t = malloc(sizeof(torch)); - memcpy(t, &torch, sizeof(torch)); + + struct entity *t = malloc(sizeof(struct entity)); + *t = demo_new_torch(y, x); return floor_add_entity(cur_floor, t); } @@ -210,9 +220,8 @@ def_input_key_fn(demo_get_fuel) void demo_place_snake(int y, int x) { - struct entity snake = demo_new_snake(y, x); - struct entity *s = malloc(sizeof(snake)); - memcpy(s, &snake, sizeof(snake)); + struct entity *s = malloc(sizeof(struct entity)); + *s = demo_new_snake(y, x); floor_add_entity(&floors[0], s); } @@ -246,7 +255,9 @@ struct entity demo_new_snake(int y, int x) { struct entity snake = { .color = { - .r = 0xa, .g = 0xCA, .b = 0xa, + .r = 0xa, + .g = 0xCA, + .b = 0xa, }, .token = 's', .posy = y, .posx = x, @@ -281,7 +292,7 @@ static void raycast_octant_at(tile_map map, int y, int x, int radius, int row, static int transforms[4][8] = { {1, 0, 0, -1, -1, 0, 0, 1}, {0, 1, -1, 0, 0, -1, 1, 0}, - {0, 1, 1, 0, 0, -1, -1, 0}, + {0, 1, 1, 0, 0, -1, -1, 0}, {1, 0, 0, 1, -1, 0, 0, -1}, }; diff --git a/src/draw.c b/src/draw.c index 895eb5d..5f0b8b2 100644 --- a/src/draw.c +++ b/src/draw.c @@ -1,8 +1,8 @@ #include "torch.h" #include "ui.h" -#include #include +#include #if 0 static int visibility[MAP_LINES][MAP_COLS] = { 0 }; @@ -30,23 +30,31 @@ void draw_map(void) uint8_t b = min(tile.b * tile.light + tile.db, 255); if (visibility[viewy + line][viewx + col]) { - ui_draw_at(line, col, (struct ui_cell){ + ui_draw_at(line, col, (struct ui_cell) { .codepoint = { [0] = tile.token }, .fg = { - .r = r, .g = g, .b = b, + .r = r, + .g = g, + .b = b, }, .bg = { - .r = 0, .g = 0, .b = 0, + .r = 0, + .g = 0, + .b = 0, }, }); } else { ui_draw_at(line, col, (struct ui_cell) { .codepoint = " ", .fg = { - .r = 0, .g = 0, .b = 0, + .r = 0, + .g = 0, + .b = 0, }, .bg = { - .r = 0, .g = 0, .b = 0, + .r = 0, + .g = 0, + .b = 0, }, }); } @@ -77,18 +85,19 @@ void draw_entities(void) if (visibility[pos->posy][pos->posx]) { ui_draw_at(line, col, (struct ui_cell) { .codepoint = { [0] = pos->token }, - .fg = { - .r = r, .g = g, .b = b, + .fg = { + .r = r, .g = g, .b = b, }, .bg = { .r = 0, .g = 0, .b = 0, }, }); - } else { + } + else { ui_draw_at(line, col, (struct ui_cell) { .codepoint = " ", - .fg = { - .r = 0, .g = 0, .b = 0, + .fg = { + .r = 0, .g = 0, .b = 0, }, .bg = { .r = 0, .g = 0, .b = 0, @@ -101,7 +110,8 @@ void draw_entities(void) } #endif -struct draw_info { +struct draw_info +{ int view_lines, view_cols; }; @@ -111,19 +121,23 @@ void draw_thing(struct tile *tile, int y, int x, void *context) int line = y - clamp(player.posy - info->view_lines / 2, 0, MAP_LINES - info->view_lines); int col = x - clamp(player.posx - info->view_cols / 2, 0, MAP_COLS - info->view_cols); if (tile->entity) { - ui_draw_at(line, col, (struct ui_cell){ - .codepoint = { [0] = tile->entity->token }, - /* = tile.entity->color * tile.light + tile.dcolor */ - .fg = color_add(color_multiply_by(tile->entity->color, tile->light), tile->dcolor), - .bg = { 0, 0, 0 }, - }); + ui_draw_at(line, + col, + (struct ui_cell) { + .codepoint = { [0] = tile->entity->token }, + /* = tile.entity->color * tile.light + tile.dcolor */ + .fg = color_add(color_multiply_by(tile->entity->color, tile->light), tile->dcolor), + .bg = { 0, 0, 0 }, + }); } else { - ui_draw_at(line, col, (struct ui_cell){ - .codepoint = { [0] = tile->token }, - /* = tile.color * tile.light + tile.dcolor */ - .fg = color_add(color_multiply_by(tile->color, tile->light), tile->dcolor), - .bg = { 0, 0, 0 }, - }); + ui_draw_at(line, + col, + (struct ui_cell) { + .codepoint = { [0] = tile->token }, + /* = tile.color * tile.light + tile.dcolor */ + .fg = color_add(color_multiply_by(tile->color, tile->light), tile->dcolor), + .bg = { 0, 0, 0 }, + }); } } @@ -131,14 +145,23 @@ void draw_shit(void) { int view_lines, view_cols; ui_dimensions(&view_lines, &view_cols); - raycast_at(cur_floor, player.posy, player.posx, max(view_lines, view_cols) / 2, &draw_thing, &(struct draw_info) { - view_lines, view_cols - }); + + raycast_at(&(struct raycast_params) { + .callback = &draw_thing, + .context = &(struct draw_info) { + .view_lines = view_lines, + .view_cols = view_cols + }, + .floor = cur_floor, + .y = player.posy, + .x = player.posx, + .radius = max(view_lines, view_cols) / 2 }); size_t needed = snprintf(NULL, 0, "%3d %3d", player_fuel, player_torches) + 1; char *buf = malloc(needed); sprintf(buf, "%3d %3d", player_fuel, player_torches); - ui_draw_str_at(1, 0, buf, (struct ui_cell){ .fg = { 0xe2, 0x58, 0x22 } }); -// ui_draw_at(2, 0, (struct ui_cell){ .codepoint = { [0] = player_fuel }, .fg = { 77, 26, 128 }, }); -// ui_draw_at(2, 2, (struct ui_cell){ .codepoint = { [0] = player_torches }, .fg = { 77, 26, 128 }, }); + ui_draw_str_at(1, 0, buf, (struct ui_cell) { .fg = { 0xe2, 0x58, 0x22 } }); + + // ui_draw_at(2, 0, (struct ui_cell){ .codepoint = { [0] = player_fuel }, .fg = { 77, 26, 128 }, }); + // ui_draw_at(2, 2, (struct ui_cell){ .codepoint = { [0] = player_torches }, .fg = { 77, 26, 128 }, }); } diff --git a/src/floor.c b/src/floor.c index c2ea73c..34a91ed 100644 --- a/src/floor.c +++ b/src/floor.c @@ -1,5 +1,5 @@ -#include "torch.h" #include "list.h" +#include "torch.h" #include #include @@ -7,8 +7,7 @@ typedef int cell; typedef cell cell_grid[MAP_LINES][MAP_COLS]; -#define cell_grid_for_each_cell(pos, grid) \ - for (pos = *grid; pos != back(grid); ++pos) +#define cell_grid_for_each_cell(pos, grid) for (pos = *grid; pos != back(grid); ++pos) static void intern_populate_grid(cell_grid grid, float rate); static void intern_iterate_grid(cell_grid grid, int birth, int survive); @@ -26,20 +25,20 @@ void floor_map_generate(struct floor *floor, enum floor_type type) intern_iterate_grid(grid, 4, 5); } - intern_floor_write_grid(floor, grid); + intern_floor_write_grid(floor, grid); } static void intern_populate_grid(cell_grid grid, float rate) { -/* - cell *pos; - cell_grid_for_each_cell(pos, grid) { - if (rand() % 100 / 100.f < rate) - *pos = 1; - else - *pos = 0; - } -*/ + /* + cell *pos; + cell_grid_for_each_cell(pos, grid) { + if (rand() % 100 / 100.f < rate) + *pos = 1; + else + *pos = 0; + } + */ for (int y = 0; y < MAP_LINES; ++y) { for (int x = 0; x < MAP_COLS; ++x) { if (rand() % 100 / 100.f < rate) @@ -65,7 +64,7 @@ static void intern_iterate_grid(cell_grid grid, int birth, int survive) } } -// memcpy(new, grid, sizeof(new)); + // memcpy(new, grid, sizeof(new)); for (int y = 0; y < MAP_LINES; ++y) for (int x = 0; x < MAP_COLS; ++x) { grid[y][x] = new[y][x]; @@ -75,13 +74,13 @@ static void intern_iterate_grid(cell_grid grid, int birth, int survive) static int intern_cell_alive_neighbours(cell_grid grid, int y, int x) { int alive = 0; -/* - for (int i = -1; i < 2; ++i) - for (int j = -1; j < 2; ++j) { - if (i != 0 && j != 0 && grid[y + i][x + j]) - alive++; - } -*/ + /* + for (int i = -1; i < 2; ++i) + for (int j = -1; j < 2; ++j) { + if (i != 0 && j != 0 && grid[y + i][x + j]) + alive++; + } + */ intern_cell_grid_at(grid, y - 1, x - 1) ? alive++ : 0; intern_cell_grid_at(grid, y - 1, x) ? alive++ : 0; @@ -91,7 +90,7 @@ static int intern_cell_alive_neighbours(cell_grid grid, int y, int x) intern_cell_grid_at(grid, y + 1, x - 1) ? alive++ : 0; intern_cell_grid_at(grid, y + 1, x) ? alive++ : 0; intern_cell_grid_at(grid, y + 1, x + 1) ? alive++ : 0; - + return alive; } @@ -107,7 +106,8 @@ static void intern_floor_write_grid(struct floor *floor, cell_grid grid) { struct tile *t; cell *c = *grid; - floor_for_each_tile(t, floor) { + floor_for_each_tile(t, floor) + { if (*c) { t->token = '#'; t->walk = 0; @@ -124,7 +124,7 @@ struct tile floor_map_at(struct floor *floor, int y, int x) if (floor_map_in_bounds(y, x)) return (floor->map)[y][x]; else - return (struct tile){ .token = ' ' }; + return (struct tile) { .token = ' ' }; } int floor_map_in_bounds(int y, int x) @@ -135,7 +135,8 @@ int floor_map_in_bounds(int y, int x) void floor_map_clear_lights(void) { struct tile *pos; - floor_for_each_tile(pos, cur_floor) { + floor_for_each_tile(pos, cur_floor) + { pos->light = 0; pos->dcolor.r = 0; pos->dcolor.g = 0; @@ -156,13 +157,14 @@ int floor_add_entity(struct floor *floor, struct entity *entity) void floor_update_entities(struct floor *floor) { struct entity *pos; - list_for_each_entry(pos, &floor->entities, list) { + list_for_each_entry(pos, &floor->entities, list) + { if (pos->update) pos->update(pos); } } -bool tile_blocks_light(struct tile tile) +bool tile_blocks_light(const struct tile *tile) { - return !tile.walk || (tile.entity ? tile.entity->blocks_light : tile.entity); + return !tile->walk || (tile->entity && tile->entity->blocks_light); } diff --git a/src/player.c b/src/player.c index feeb412..d392e52 100644 --- a/src/player.c +++ b/src/player.c @@ -1,14 +1,17 @@ -#include "torch.h" #include "list.h" +#include "torch.h" #include struct entity player = { .color = { - .r = 151, .g = 151, .b = 151, + .r = 151, + .g = 151, + .b = 151, }, .token = '@', - .posy = 13, .posx = 14, + .posy = 13, + .posx = 14, .update = demo_player_update, .destroy = NULL, .list = LIST_HEAD_INIT(player.list), diff --git a/src/raycast.c b/src/raycast.c index 37ceaaf..893e5c2 100644 --- a/src/raycast.c +++ b/src/raycast.c @@ -1,140 +1,278 @@ #include "torch.h" -#include #include +#include -static void raycast_octant_at(struct floor *floor, int y, int x, int radius, - int row, float start_slope, float end_slope, int octant, - raycast_callback_fn callback, void *context); +/* + * Fetches the selected tile, calls back, and returns + * the selected tile. + * + * preconditions: + * - raycast->floor points to a valid floor + * - raycast->callback points to a valid function + * - (x, y) denotes a tile which is in the floor bounds + * + * postconditions: + * - raycast->callback is called with the corresponding tile + * - the result is a pointer to that tile + */ +static struct tile *get_tile(const struct raycast_params *raycast, int x, int y) +{ + struct tile *tile = &raycast->floor->map[y][x]; + raycast->callback(tile, y, x, raycast->context); + return tile; +} -static void transform_point_by_octant(int y, int x, int dy, int dx, int octant, - int *ay, int *ax); +/* + * preconditions: + * - l and r point to valid integers + * + * postconditions: + * - the values of *l and *r are swapped + */ +void swapi(int *l, int *r) +{ + int t = *l; + *l = *r; + *r = t; +} -/* Hides hard-coded initial values for raycast_octant_at() */ -#define RAYCAST_OCTANT_AT(map, y, x, radius, octant, callback, context) do { \ - raycast_octant_at(map, y, x, radius, 1, 1.0, 0.0, octant, \ - callback, context); \ - } while (0); +struct point +{ + int x; + int y; +}; -/* Uses Björn Bergström's Recursive Shadowcasting Algorithm, which - divides the map into eight congruent triangular octants, and calculates each - octant individually. */ -void raycast_at(struct floor *floor, int y, int x, int radius, - raycast_callback_fn callback, void *context) +/* + * Transforms zeroth octant coordinates into other octants' coordinates. + * + * Diagram of octant layout: + * + * \ 7 | 5 / + * 6 \ | / 4 + * - - - - - + * 2 / | \ 0 + * / 3 | 1 \ + * + * If first octant bit is set, flip across the diagonal. + * If second octant bit is set, flip across the vertical. + * If third octant bit is set, flip across the horizontal. + * + * wide contract + */ +static struct point transform_point_to_octant(int x, int y, int dx, int dy, int octant_number) { - /* raycast_octant_at() does not cast on the origin (y, x). */ - callback(&floor->map[y][x], y, x, context); + if (octant_number & 1) { + swapi(&dx, &dy); + } + if (octant_number & 2) { + dx = -dx; + } + if (octant_number & 4) { + dy = -dy; + } - for (int octant = 0; octant < 8; ++octant) - RAYCAST_OCTANT_AT(floor, y, x, radius, octant, callback, context); + return (struct point) { x + dx, y + dy }; } -/* Iterates through each tile, left to right, of each row of a given octant, - recursing in each gap between blocking tiles, except for the last one. - - 7 --....-- - = visible tile --....-- - 6 --...-- . = hidden tile --...-- - 5 --##-- # = blocking tile --!#-- ! = function recurses here - 4 ----- ----- - 3 ---- ---- - 2 --- --- - 1 -- -- - @ @ = the centre @ - - Note: the algorithm uses an unorthodox understanding of coordinate gradients, - calculating them as dx / dy rather than dy / dx. Consequently, as a line - approaches the bottom left, its slope approaches infinity, and as a line - approaches the top right, its slope approaches zero. */ -static void raycast_octant_at(struct floor *floor, int y, int x, int radius, - int row, float start_slope, float end_slope, int octant, - raycast_callback_fn callback, void *context) +/* + * Transforms the coordinates according to the octant, + * fetches the selected tile, calls back, and returns + * the selected tile. + * + * preconditions: + * - raycast->floor points to a valid floor + * - raycast->callback is a valid function pointer + * - (dx, dy) denotes a tile which is in the floor bounds + * + * postconditions: + * - raycast->callback is called with the corresponding tile + * - the result is a pointer to that tile + */ +static struct tile *get_transformed_tile(const struct raycast_params *raycast, int dx, int dy, int octant) { - if (start_slope < end_slope) - return; - - /* This function is called recursively on each gap in blocking tiles - except for the last one. next_start_slope contains the slope of the - top right corner of the rightmost blocking tile. */ - float next_start_slope = start_slope; - for (; row <= radius; ++row) { - /* Doesn't do anything. Here for clarity. */ - start_slope = next_start_slope; - /* Contains whether or not the last tile blocked light. Used - to determine when to recurse. */ - bool blocked = false; - /* Contains the vector of the current tile from the centre. */ - int dx, dy; - /* This implementation pretends its always calculating the - zeroth octant, then applies a transformation. */ - for (dx = -row, dy = -row; dx <= 0; ++dx) { - /* Slope of the bottom left and top right corners - of the current tile from the centre, respectively */ - float bl_slope = (dx - 0.5) / (dy + 0.5); - float tr_slope = (dx + 0.5) / (dy - 0.5); - /* Accounts for mathematical edgecases. */ - if (start_slope < tr_slope) { - continue; - } else if (end_slope > bl_slope) { - break; - } + struct point r = transform_point_to_octant(raycast->x, raycast->y, dx, dy, octant); + return get_tile(raycast, r.x, r.y); +} + +struct index_interval +{ + int begin; + int end; +}; + +/* + * Figures the range of y coordinates we need to iterate over for + * the given slopes. Clamps this range to fit in floor bounds. + * + * wide contract + */ +struct index_interval make_dy_interval(float slope_begin, float slope_end, int dx, int dy_max) +{ + struct index_interval dy_interval = { + slope_begin * (dx - 0.5f) + 0.5f, + slope_end * (dx + 0.5f) - 0.5f + }; + dy_interval.begin = min(dy_interval.begin, dy_max); + dy_interval.end = min(dy_interval.end, dy_max); + return dy_interval; +} + +struct octant_params +{ + int number; + int dx_max; + int dy_max; +}; + +/* + * Accepts slopes corresponding a gap between blocking tiles. + * Iterates through columns from left to right, then through each + * tile, top to bottom, of the gap, searching for deeper gaps. + * Recurses in each gap except those containing the last tile + * of the column. Iterates into gaps containing the last tile. + * Exits if there is no such gap. + * + * Diagram of zeroth octant: + * + * dx 01234567 dy + * + * @------- 0 `-` = visible tile + * ------- 1 `.` = hidden tile + * ---!.. 2 `#` = blocking tile + * --#.. 3 `@` = centre + * --.. 4 `!` = blocking tile where function recurses + * --. 5 + * -- 6 + * - 7 + * + * The algorithm always works in the coordinates of the zeroth octant, + * then applies a transformation to obtain the actual coordinate of the + * selected tile. + * + * As a column is iterated: + * + * If the previous tile blocks light, but the current tile does not, we have + * encountered the beginning of a gap. If the previous tile allows light, but + * the current tile does not, we have encountered the end of the gap, and we recurse. + * + * Each time we encounter any such bound, we calculate the slope of the associated + * point on the tile. Slopes for beginnings of gaps correspond to bottom left points + * of tiles. Slopes for ends of gaps correspond to top right points of tiles. + * Those slopes are used in next recursion or iteration. + * + * Diagram of begin and end slopes: + * + * @xxxxxx + * xxxxxxxxx ####### + * xxx xxxxxx####### begin + * xxx ------- + * xxx ------- + * xxx------- + * xxx---- + * ---xxx- end + * ####### + * ####### + * + * preconditions: + * - raycast->floor points to a valid floor + * - raycast->callback points to a valid function + * - (raycast->x, raycast->y) denotes a tile which is in the floor bounds + * - octant->dx_min <= octant->dx_max + * - start_slope < end_slope + * - the coordinates and bounds in raycast and octant denote a set of tiles + * which are in the floor bounds + * + * postconditions: + * - raycast->callback is called at least once for each tile in line of sight + * within the octant and octant->dx_max + */ +static void raycast_octant_at(const struct raycast_params *raycast, + const struct octant_params *octant, + int dx, + float slope_begin, + float slope_end) +{ + bool blocked = false; + for (; dx <= octant->dx_max && !blocked; ++dx) { + struct index_interval dy_interval = make_dy_interval(slope_begin, slope_end, dx, octant->dy_max); + + int dy = dy_interval.begin; + + struct tile *tile = get_transformed_tile(raycast, dx, dy, octant->number); - /* Contains the actual coordinate of the current tile. */ - int ay, ax; - transform_point_by_octant(y, x, dy, dx, octant, &ay, &ax); - if (!floor_map_in_bounds(ay, ax)) - continue; - - if (dx * dx + dy * dy < radius * radius) - callback(&floor->map[ay][ax], ay, ax, context); - - struct tile tile = floor_map_at(floor, ay, ax); - if (blocked) { - if (tile_blocks_light(tile)) { - float tr_slope = (dx + 0.5) / (dy - 0.5); - next_start_slope = tr_slope; - continue; - } else { /* Still blocked. */ - blocked = false; - start_slope = next_start_slope; - } - } else if (tile_blocks_light(tile)) { + blocked = tile_blocks_light(tile); + + for (++dy; dy <= dy_interval.end; ++dy) { + tile = get_transformed_tile(raycast, dx, dy, octant->number); + + if (blocked && !tile_blocks_light(tile)) { + blocked = false; + slope_begin = (dy - 0.5f) / (dx - 0.5f); + } else if (!blocked && tile_blocks_light(tile)) { blocked = true; - float tr_slope = (dx + 0.5) / (dy - 0.5); - next_start_slope = tr_slope; - float bl_slope = (dx - 0.5) / (dy + 0.5); - /* Recurse! */ - raycast_octant_at(floor, y, x, radius, row + 1, - start_slope, bl_slope, octant, callback, - context); + raycast_octant_at(raycast, octant, dx + 1, slope_begin, (dy - 0.5f) / (dx + 0.5f)); } } - if (blocked) { /* If the last tile of the row blocks light. */ - break; - } } } -/* raycast_octant_at() pretends its always calculating the zeroth octant. - transform_point_by_octant() applies the transformation. */ -static void transform_point_by_octant(int y, int x, int dy, int dx, int octant, - int *ay, int *ax) +/* Uses a modified version ofBjörn Bergström's Recursive Shadowcasting Algorithm, + * which divides the map into eight congruent triangular octants, and calculates + * each octant individually. + * + * Casts on the origin, then kicks off the recursive functions for each octant. + * + * preconditions: + * - raycast->floor points to a valid floor + * - raycast->callback points to a valid function + * - (raycast->x, raycast->y) denotes a tile which is in the floor bounds + * - the coordinates and bounds in raycast and octant denote a set of tiles + * which are in the floor bounds + * + * postconditions: + * - raycast->callback is called at least once for each tile in raycast->radius + * and line of sight + * + * notes: + * - may call back for cells outside the requested radius. + * - may call back multiple times for cells along axes and diagonals. + */ +void raycast_at(const struct raycast_params *raycast) { - /* Notice that either yy and xx are non-zero, or yx and xy are non-zero. - When yy and xx are zero, yx and xy swap r and x respectively. - When yx and xy are zero, y and x flip along the axis respectively. */ - static const int transforms[8][2][2] = { - /* yy yx xx xy */ - { { 1, 0}, { 1, 0} }, - { { 0, 1}, { 0, 1} }, - { { 0, 1}, { 0, -1} }, - { { 1, 0}, {-1, 0} }, - { {-1, 0}, {-1, 0} }, - { { 0, -1}, { 0, -1} }, - { { 0, -1}, { 0, 1} }, - { {-1, 0}, { 1, 0} }, + get_tile(raycast, raycast->x, raycast->y); + + const int dx_max[] = { + min(raycast->radius, MAP_COLS - raycast->x - 1), + min(raycast->radius, MAP_LINES - raycast->y - 1), + min(raycast->radius, raycast->x), + min(raycast->radius, MAP_LINES - raycast->y - 1), + min(raycast->radius, MAP_COLS - raycast->x - 1), + min(raycast->radius, raycast->y), + min(raycast->radius, raycast->x), + min(raycast->radius, raycast->y), }; - const int (*t)[2] = transforms[octant]; - *ay = y + dy * t[0][0] + dx * t[0][1]; - *ax = x + dx * t[1][0] + dy * t[1][1]; + const int dy_max[] = { + MAP_LINES - raycast->y - 1, + MAP_COLS - raycast->x - 1, + MAP_LINES - raycast->y - 1, + raycast->x, + raycast->y, + MAP_COLS - raycast->x - 1, + raycast->y, + raycast->x, + }; + + for (int octant = 0; octant < 8; octant++) { + raycast_octant_at(raycast, + &(struct octant_params) { + .number = octant, + .dx_max = dx_max[octant], + .dy_max = dy_max[octant] + }, + 1, + 0.0f, + 1.0f); + } } diff --git a/src/torch.c b/src/torch.c index e0377ad..54398f5 100644 --- a/src/torch.c +++ b/src/torch.c @@ -1,16 +1,17 @@ #include "torch.h" + #include "ui.h" #include +#include #include #include -#include #ifdef DEBUG FILE *debug_log; #endif -int main(void) +int main() { srand(time(NULL)); #ifdef DEBUG diff --git a/src/torch.h b/src/torch.h index 97f5a5e..06c44aa 100644 --- a/src/torch.h +++ b/src/torch.h @@ -3,11 +3,12 @@ #include "list.h" -#include -#include #include +#include +#include +#include -#define back(arr) (arr[sizeof(arr)/sizeof(*arr)]) +#define back(arr) (arr[sizeof(arr) / sizeof(*arr)]) #define min(a, b) (a < b ? a : b) #define max(a, b) (a > b ? a : b) #define clamp(v, lo, hi) ((v < lo) ? lo : (hi < v) ? hi : v) @@ -23,7 +24,8 @@ typedef def_input_key_fn(input_key_fn); extern input_key_fn *input_keymap[]; /* Color */ -struct color { +struct color +{ uint8_t r, g, b; }; @@ -38,7 +40,8 @@ struct entity; #define def_entity_fn(name) void name(struct entity *this) typedef def_entity_fn(entity_fn); -struct entity { +struct entity +{ struct color color; char token; int posy, posx; @@ -69,7 +72,8 @@ def_input_key_fn(player_move_downright); def_input_key_fn(player_toggle_lantern); /* Floor */ -struct tile { +struct tile +{ struct color color; char token; float light; @@ -78,15 +82,16 @@ struct tile { uint walk : 1; }; -bool tile_blocks_light(struct tile); +bool tile_blocks_light(struct tile const *); #define MAP_LINES 100 -#define MAP_COLS 100 +#define MAP_COLS 100 typedef struct tile tile_map[MAP_LINES][MAP_COLS]; typedef struct list_head entity_list; -struct floor { +struct floor +{ tile_map map; entity_list entities; }; @@ -95,24 +100,24 @@ extern struct floor floors[]; extern struct floor *cur_floor; -enum floor_type { +enum floor_type +{ CAVE, }; struct tile floor_map_at(struct floor *floor, int y, int x); -int floor_map_in_bounds(int y, int x); -void floor_map_clear_lights(void); -void floor_map_generate(struct floor *floor, enum floor_type type); +int floor_map_in_bounds(int y, int x); +void floor_map_clear_lights(void); +void floor_map_generate(struct floor *floor, enum floor_type type); -int floor_add_entity(struct floor *floor, struct entity *entity); +int floor_add_entity(struct floor *floor, struct entity *entity); void floor_update_entities(struct floor *floor); -#define floor_for_each_tile(pos, floor) \ - for (pos = *floor->map; pos != back(floor->map); ++pos) +#define floor_for_each_tile(pos, floor) for (pos = *floor->map; pos != back(floor->map); ++pos) /* Draw */ #define VIEW_LINES 23 -#define VIEW_COLS 79 +#define VIEW_COLS 79 #if 0 void draw_map(void); @@ -134,9 +139,20 @@ def_input_key_fn(demo_get_fuel); extern struct floor demo_floor; /* Raycast */ -typedef void raycast_callback_fn(struct tile *tile, int y, int x, void *context); -void raycast_at(struct floor *floor, int y, int x, int radius, - raycast_callback_fn callback, void *context); +typedef void raycast_callback_fn(struct tile *tile, int x, int y, void *context); + +struct raycast_params +{ + struct floor *floor; + int x; + int y; + int radius; + raycast_callback_fn *callback; + void *context; +}; + +void raycast_at(const struct raycast_params *params); +void raycast(const struct raycast_params *params); #endif diff --git a/src/ui.h b/src/ui.h index 80bf258..b44be93 100644 --- a/src/ui.h +++ b/src/ui.h @@ -3,22 +3,24 @@ #include "torch.h" -#include #include +#include void ui_init(void); void ui_quit(void); void ui_dimensions(int *lines, int *cols); -struct ui_cell { +struct ui_cell +{ char codepoint[5]; - struct color fg, bg; - unsigned int bold : 1, - italic : 1, - under_ : 1, - blink : 1, - reverse_vid : 1, - strikethru : 1; + struct color fg; + struct color bg; + unsigned int bold : 1; + unsigned int italic : 1; + unsigned int under_ : 1; + unsigned int blink : 1; + unsigned int reverse_vid : 1; + unsigned int strikethru : 1; }; void ui_draw_at(int line, int col, struct ui_cell cell); @@ -28,18 +30,22 @@ void ui_flush(void); /* Hacky garbage. */ void ui_draw_str_at(int line, int col, const char *str, struct ui_cell attr); -enum ui_event_type { +enum ui_event_type +{ UI_EVENT_KEY, }; -enum ui_event_key { +enum ui_event_key +{ UI_EVENT_KEY_a = 'a', UI_EVENT_KEY_A = 'A', }; -struct ui_event { - enum ui_event_type type; - union { +struct ui_event +{ + enum ui_event_type type; + union + { long key; }; }; diff --git a/src/ui/sdl.c b/src/ui/sdl.c index e69de29..8b13789 100644 --- a/src/ui/sdl.c +++ b/src/ui/sdl.c @@ -0,0 +1 @@ + diff --git a/src/ui/term.c b/src/ui/term.c index 2400b9b..9b44b4b 100644 --- a/src/ui/term.c +++ b/src/ui/term.c @@ -1,20 +1,22 @@ -#include "ui.h" #include "termkey.h" #include "torch.h" +#include "ui.h" #include -#include + #include #include +#include #include #include -#include +#include #define CSI "\e[" #define ALTERNATE_SCREEN "?1049h" #define NORMAL_SCREEN "?1049l" #define HIDE_CURSOR "?25l" #define SHOW_CURSOR "?25h" + static void get_terminal_size(); static void handle_resize(int signal); @@ -221,7 +223,7 @@ void ui_flush(void) } if (this.strikethru != last.strikethru) { - sgr_params[nparams++] = this.strikethru? 9 : 29; + sgr_params[nparams++] = this.strikethru ? 9 : 29; sgrlen += this.strikethru ? 1 : 2; } @@ -229,10 +231,10 @@ void ui_flush(void) char *iter = sgr_buf; iter += sprintf(iter, "\e["); - for (int i = 0; i < nparams-1; i++) + for (int i = 0; i < nparams - 1; i++) iter += sprintf(iter, "%d;", sgr_params[i]); if (nparams > 0) // no semicolon for last parameter - iter += sprintf(iter, "%d", sgr_params[nparams-1]); + iter += sprintf(iter, "%d", sgr_params[nparams - 1]); sprintf(iter, "m"); } else sgr_buf[0] = '\0'; @@ -268,31 +270,30 @@ void ui_draw_str_at(int line, int col, const char *str, struct ui_cell attr) struct ui_event ui_poll_event(void) { - bool cont = true; + bool cont = true; TermKeyKey key; struct ui_event event; - - while (cont) switch (termkey_waitkey(termkey_instance, &key)) { - case TERMKEY_RES_KEY: - switch (key.type) { - case TERMKEY_TYPE_UNICODE: - event.key = key.code.codepoint; - cont = false; - break; - default: + + while (cont) + switch (termkey_waitkey(termkey_instance, &key)) { + case TERMKEY_RES_KEY: + switch (key.type) { + case TERMKEY_TYPE_UNICODE: + event.key = key.code.codepoint; + cont = false; + break; + default: break; + } break; + default: break; } - break; - default: - break; - } return event; } #ifdef TEST -#include + #include int main(void) { @@ -312,7 +313,7 @@ int main(void) }); assert(strcmp(ui_buffer[lines - 1][0].codepoint, "L") == 0); - ui_draw_str_at(3, 3, "Test", (struct ui_cell){.fg = {19, 103, 76}}); + ui_draw_str_at(3, 3, "Test", (struct ui_cell) { .fg = { 19, 103, 76 } }); ui_flush(); struct ui_event event = ui_poll_event();