Refactor the light calculations to be part of map instead of spread out all over. Still need to bring back lighting on walls and also pathing for enemies is currently busted.

main
Zed A. Shaw 5 months ago
parent 0e8a2e520a
commit 54fa1a23ce
  1. 4
      components.hpp
  2. 30
      lights.hpp
  3. 8
      main.cpp
  4. 105
      map.cpp
  5. 65
      map.hpp
  6. 4
      status.txt
  7. 62
      systems.cpp

@ -48,8 +48,4 @@ namespace components {
struct EnemyConfig { struct EnemyConfig {
int HEARING_DISTANCE; int HEARING_DISTANCE;
}; };
struct LightSource {
int strength = 100;
};
} }

@ -0,0 +1,30 @@
#pragma once
#include <array>
#include "dbc.hpp"
#include "point.hpp"
#include <algorithm>
#include "map.hpp"
namespace lighting {
struct LightSource {
int strength = 0; // lower is better
int distance = 1; // higher is farther, in squares
};
const int MIN = 40;
const int MAX = 220;
const std::array<int, 10> LEVELS{
MAX,
200,
180,
160,
140,
120,
100,
80,
60,
MIN,
};
}

@ -7,6 +7,7 @@
#include "collider.hpp" #include "collider.hpp"
#include "render.hpp" #include "render.hpp"
#include "save.hpp" #include "save.hpp"
#include "lights.hpp"
#include "ftxui/screen/terminal.hpp" // for SetColorSupport, Color, TrueColor #include "ftxui/screen/terminal.hpp" // for SetColorSupport, Color, TrueColor
#include <filesystem> #include <filesystem>
#include <fcntl.h> #include <fcntl.h>
@ -16,6 +17,7 @@
#endif #endif
using namespace ftxui; using namespace ftxui;
using lighting::LightSource;
namespace fs = std::filesystem; namespace fs = std::filesystem;
/* /*
@ -33,7 +35,7 @@ void configure_world(DinkyECS::World &world, Map &game_map) {
world.set<Combat>(player.entity, {100, 10}); world.set<Combat>(player.entity, {100, 10});
world.set<Tile>(player.entity, {config.PLAYER_TILE}); world.set<Tile>(player.entity, {config.PLAYER_TILE});
world.set<Inventory>(player.entity, {5}); world.set<Inventory>(player.entity, {5});
world.set<LightSource>(player.entity, {50}); world.set<LightSource>(player.entity, {5,2});
auto enemy = world.entity(); auto enemy = world.entity();
world.set<Position>(enemy, {game_map.place_entity(1)}); world.set<Position>(enemy, {game_map.place_entity(1)});
@ -46,7 +48,7 @@ void configure_world(DinkyECS::World &world, Map &game_map) {
world.set<Motion>(enemy2, {0,0}); world.set<Motion>(enemy2, {0,0});
world.set<Combat>(enemy2, {20, 10}); world.set<Combat>(enemy2, {20, 10});
world.set<Tile>(enemy2, {"*"}); world.set<Tile>(enemy2, {"*"});
world.set<LightSource>(enemy2, {100}); world.set<LightSource>(enemy2, {6,1});
auto gold = world.entity(); auto gold = world.entity();
world.set<Position>(gold, {game_map.place_entity(3)}); world.set<Position>(gold, {game_map.place_entity(3)});
@ -56,7 +58,7 @@ void configure_world(DinkyECS::World &world, Map &game_map) {
auto wall_torch = world.entity(); auto wall_torch = world.entity();
world.set<Position>(wall_torch, {game_map.place_entity(4)}); world.set<Position>(wall_torch, {game_map.place_entity(4)});
world.set<LightSource>(wall_torch, {200}); world.set<LightSource>(wall_torch, {2,3});
world.set<Tile>(wall_torch, {"!"}); world.set<Tile>(wall_torch, {"!"});
} }

@ -45,10 +45,12 @@ inline void add_neighbors(PointList &neighbors, Matrix &closed, size_t y, size_t
*/ */
Map::Map(size_t width, size_t height) : Map::Map(size_t width, size_t height) :
$limit(1000), $limit(1000),
$width(width),
$height(height),
$input_map(height, MatrixRow(width, 1)), $input_map(height, MatrixRow(width, 1)),
$walls(height, MatrixRow(width, INV_WALL)), $walls(height, MatrixRow(width, INV_WALL)),
$paths(height, MatrixRow(width, 1)), $paths(height, MatrixRow(width, 1)),
$lighting(height, MatrixRow(width, 0)) $lightmap(height, MatrixRow(width, 0))
{ {
} }
@ -58,6 +60,8 @@ Map::Map(Matrix input_map, Matrix walls_map, int limit) :
$input_map(input_map), $input_map(input_map),
$walls(walls_map) $walls(walls_map)
{ {
$width = $walls[0].size();
$height = $walls.size();
} }
void Map::make_paths() { void Map::make_paths() {
@ -108,10 +112,10 @@ void Map::make_paths() {
} }
void Map::make_room(size_t origin_x, size_t origin_y, size_t w, size_t h) { void Map::make_room(size_t origin_x, size_t origin_y, size_t w, size_t h) {
dbc::pre("x out of bounds", origin_x < width()); dbc::pre("x out of bounds", origin_x < $width);
dbc::pre("y out of bounds", origin_y < height()); dbc::pre("y out of bounds", origin_y < $height);
dbc::pre("w out of bounds", w <= width()); dbc::pre("w out of bounds", w <= $width);
dbc::pre("h out of bounds", h <= height()); dbc::pre("h out of bounds", h <= $height);
for(size_t y = origin_y; y < origin_y + h; ++y) { for(size_t y = origin_y; y < origin_y + h; ++y) {
dbc::check(y < $walls.size(), "y is out of bounds"); dbc::check(y < $walls.size(), "y is out of bounds");
@ -215,7 +219,7 @@ bool Map::neighbors(Point &out, bool greater) {
} }
bool Map::inmap(size_t x, size_t y) { bool Map::inmap(size_t x, size_t y) {
return x < width() && y < height(); return x < $width && y < $height;
} }
void Map::set_door(Room &room, int value) { void Map::set_door(Room &room, int value) {
@ -304,8 +308,8 @@ void Map::generate() {
Room root{ Room root{
.x = 0, .x = 0,
.y = 0, .y = 0,
.width = width(), .width = $width,
.height = height() .height = $height
}; };
partition_map(root, 10); partition_map(root, 10);
@ -329,8 +333,8 @@ void Map::generate() {
walk(src.exit, target.entry); walk(src.exit, target.entry);
clear_target(target.entry); clear_target(target.entry);
for(size_t y = 0; y < height(); ++y) { for(size_t y = 0; y < $height; ++y) {
for(size_t x = 0; x < width(); ++x) { for(size_t x = 0; x < $width; ++x) {
$walls[y][x] = !$walls[y][x]; $walls[y][x] = !$walls[y][x];
} }
} }
@ -345,3 +349,84 @@ void Map::dump() {
dump_map("WALLS", $walls); dump_map("WALLS", $walls);
dump_map("INPUT", $input_map); dump_map("INPUT", $input_map);
} }
bool Map::can_move(Point move_to) {
return inmap(move_to.x, move_to.y) &&
!iswall(move_to.x, move_to.y);
}
Point Map::map_to_camera(const Point &loc, const Point &cam_orig) {
return {loc.x - cam_orig.x, loc.y - cam_orig.y};
}
Point Map::center_camera(const Point &around, size_t view_x, size_t view_y) {
int high_x = int(width() - view_x);
int high_y = int(height() - view_y);
int center_x = int(around.x - view_x / 2);
int center_y = int(around.y - view_y / 2);
// BUG: is clamp really the best thing here? this seems wrong.
size_t start_x = high_x > 0 ? std::clamp(center_x, 0, high_x) : 0;
size_t start_y = high_y > 0 ? std::clamp(center_y, 0, high_y) : 0;
return {start_x, start_y};
}
void Map::reset_light() {
for(auto &row : $lightmap) {
for(size_t i = 0; i < row.size(); i++) {
row[i] = lighting::MIN;
}
}
}
void Map::set_light_target(const Point &at, int value) {
set_target(at, value);
}
void Map::path_light() {
make_paths();
}
void Map::light_box(LightSource source, Point from, Point &min_out, Point &max_out) {
using std::min, std::max;
min_out.x = max(int(from.x), source.distance) - source.distance;
max_out.x = min(from.x + source.distance, width() - 1);
min_out.y = max(int(from.y), source.distance) - source.distance;
max_out.y = min(from.y + source.distance, width() - 1);
}
int Map::light_level(int level, size_t x, size_t y) {
size_t at = level + $paths[y][x];
int cur_level = $lightmap[y][x];
int new_level = at < lighting::LEVELS.size() ? lighting::LEVELS[at] : lighting::MIN;
return cur_level < new_level ? new_level : cur_level;
}
void Map::render_light(LightSource source, Point at) {
Point min, max;
light_box(source, at, min, max);
for(size_t x = min.x; x <= max.x; ++x) {
for(size_t y = min.y; y <= max.y; ++y) {
$lightmap[y][x] = light_level(source.strength, x, y);
}
}
/*
const int UNPATH = game_map.limit();
for(auto point : has_light) {
for(int i = -1; i <= 1; i++) {
for(int j = -1; j <= 1; j++) {
if(!game_map.inmap(point.x+i, point.y+j)) continue;
if(paths[point.y+j][point.x+i] == UNPATH) {
lightmap[point.y+j][point.x+i] = lighting::MAX;
}
}
}
}
*/
}

@ -7,12 +7,15 @@
#include <fmt/core.h> #include <fmt/core.h>
#include "point.hpp" #include "point.hpp"
#include "tser.hpp" #include "tser.hpp"
#include "lights.hpp"
#define INV_WALL 0 #define INV_WALL 0
#define INV_SPACE 1 #define INV_SPACE 1
#define WALL_VALUE 1 #define WALL_VALUE 1
#define SPACE_VALUE 0 #define SPACE_VALUE 0
using lighting::LightSource;
struct Room { struct Room {
size_t x = 0; size_t x = 0;
size_t y = 0; size_t y = 0;
@ -33,10 +36,12 @@ void add_neighbors(Matrix &closed, size_t j, size_t i);
class Map { class Map {
public: public:
int $limit; int $limit;
size_t $width;
size_t $height;
Matrix $input_map; Matrix $input_map;
Matrix $walls; Matrix $walls;
Matrix $paths; Matrix $paths;
Matrix $lighting; // BUG: this is not the place Matrix $lightmap;
std::vector<Room> $rooms; std::vector<Room> $rooms;
Map(Matrix input_map, Matrix walls_map, int limit); Map(Matrix input_map, Matrix walls_map, int limit);
@ -48,58 +53,42 @@ public:
Map(Map &map) = delete; Map(Map &map) = delete;
Matrix& paths() { return $paths; } Matrix& paths() { return $paths; }
Matrix& lighting() { return $lighting; } Matrix& lighting() { return $lightmap; }
Matrix& input_map() { return $input_map; } Matrix& input_map() { return $input_map; }
Matrix& walls() { return $walls; } Matrix& walls() { return $walls; }
int limit() { return $limit; } int limit() { return $limit; }
size_t width() { return $walls[0].size(); } size_t width() { return $width; }
size_t height() { return $walls.size(); } size_t height() { return $height; }
int distance(Point to) { return $paths[to.y][to.x]; } int distance(Point to) { return $paths[to.y][to.x]; }
Room &room(size_t at) {
return $rooms[at];
}
size_t room_count() { Room &room(size_t at) { return $rooms[at]; }
return $rooms.size(); size_t room_count() { return $rooms.size(); }
}
void partition_map(Room &cur, int depth);
void make_room(size_t origin_y, size_t origin_x, size_t width, size_t height); void make_room(size_t origin_y, size_t origin_x, size_t width, size_t height);
void add_door(Room &room); void add_door(Room &room);
bool inmap(size_t x, size_t y); bool can_move(Point move_to);
bool iswall(size_t x, size_t y);
bool can_move(Point move_to) {
return inmap(move_to.x, move_to.y) &&
!iswall(move_to.x, move_to.y);
}
bool neighbors(Point &out, bool up);
void generate(); void generate();
void place_rooms(Room &root); void place_rooms(Room &root);
Point place_entity(size_t room_index);
bool neighbors(Point &out, bool up);
bool inmap(size_t x, size_t y);
bool iswall(size_t x, size_t y);
void make_paths(); void make_paths();
void partition_map(Room &cur, int depth);
void set_target(const Point &at, int value=0); void set_target(const Point &at, int value=0);
void clear_target(const Point &at); void clear_target(const Point &at);
bool walk(Point &src, Point &target); bool walk(Point &src, Point &target);
void set_door(Room &room, int value); void set_door(Room &room, int value);
void dump();
Point place_entity(size_t room_index);
Point map_to_camera(const Point &loc, const Point &cam_orig) {
return {loc.x - cam_orig.x, loc.y - cam_orig.y};
}
Point center_camera(const Point &around, size_t view_x, size_t view_y) { Point map_to_camera(const Point &loc, const Point &cam_orig);
int high_x = int(width() - view_x); Point center_camera(const Point &around, size_t view_x, size_t view_y);
int high_y = int(height() - view_y); void reset_light();
int center_x = int(around.x - view_x / 2); void set_light_target(const Point &at, int value=0);
int center_y = int(around.y - view_y / 2); void path_light();
void light_box(LightSource source, Point from, Point &min_out, Point &max_out);
int light_level(int level, size_t x, size_t y);
void render_light(LightSource source, Point at);
// BUG: is clamp really the best thing here? this seems wrong. void dump();
size_t start_x = high_x > 0 ? std::clamp(center_x, 0, high_x) : 0;
size_t start_y = high_y > 0 ? std::clamp(center_y, 0, high_y) : 0;
return {start_x, start_y};
}
}; };

@ -1,9 +1,9 @@
TODAY'S GOAL: TODAY'S GOAL:
* Neighbors needs a rewrite * Neighbors needs a rewrite
* Neighbors algo isn't using greater parameter * Neighbors algo isn't using greater parameter
* Refine the lighting to support multiple lights.
* Think up an enemy system. * Think up an enemy system.
* Revisit map generation. * Revisit map generation.
* Create a index based light system and a unit test for it.
TODO: TODO:
* Write a method for renderer that can translate coordinates. * Write a method for renderer that can translate coordinates.
@ -16,7 +16,7 @@ TODO:
* Probably a system for mapping collision types to sound effects, rather than having the GUI do it. * Probably a system for mapping collision types to sound effects, rather than having the GUI do it.
* Write a test that generates a ton of maps then confirms there's a path from one room to every other room? * Write a test that generates a ton of maps then confirms there's a path from one room to every other room?
* Lua integration? * Lua integration
* check out SoLoud. * check out SoLoud.

@ -8,6 +8,7 @@
#include "ftxui/screen/color.hpp" #include "ftxui/screen/color.hpp"
#include "ftxui/screen/terminal.hpp" // for SetColorSupport, Color, TrueColor #include "ftxui/screen/terminal.hpp" // for SetColorSupport, Color, TrueColor
#include "dbc.hpp" #include "dbc.hpp"
#include "lights.hpp"
const bool DEBUG_MAP=false; const bool DEBUG_MAP=false;
@ -15,68 +16,21 @@ using std::string;
using namespace fmt; using namespace fmt;
using namespace components; using namespace components;
using ftxui::Color; using ftxui::Color;
using lighting::LightSource;
const int LIGHT_MIN = 20;
const int LIGHT_MAX = 160;
void System::lighting(DinkyECS::World &world, Map &game_map, Player &player) { void System::lighting(DinkyECS::World &world, Map &game_map, Player &player) {
using std::min, std::max, std::clamp; game_map.reset_light();
auto &lighting = game_map.lighting();
std::vector<Point> has_light;
for(auto &row : lighting) {
for(size_t i = 0; i < row.size(); i++) {
row[i] = LIGHT_MIN;
}
}
world.query<Position, LightSource>([&](const auto &ent, auto &position, auto &lightsource) { world.query<Position, LightSource>([&](const auto &ent, auto &position, auto &lightsource) {
game_map.set_target(position.location); game_map.set_light_target(position.location);
}); });
game_map.make_paths(); game_map.path_light();
auto &paths = game_map.paths();
world.query<Position, LightSource>([&](const auto &ent, auto &position, auto &lightsource) { world.query<Position, LightSource>([&](const auto &ent, auto &position, auto &lightsource) {
game_map.clear_target(position.location); game_map.render_light(lightsource, position.location);
int strength = 255 - lightsource.strength;
size_t dist = size_t((float(lightsource.strength) / 255.0) * 3) + 1;
size_t min_x = max(position.location.x, dist) - dist;
size_t max_x = min(position.location.x + dist, game_map.width() - 1);
size_t min_y = max(position.location.y, dist) - dist;
size_t max_y = min(position.location.y + dist, game_map.height() - 1);
for(size_t x = min_x; x <= max_x; ++x) {
for(size_t y = min_y; y <= max_y; ++y) {
int dnum = paths[y][x];
int light = std::clamp(255 - (strength * dnum), LIGHT_MIN, LIGHT_MAX);
if(lighting[y][x] < light) {
lighting[y][x] = light;
if(light > LIGHT_MIN) {
has_light.push_back({x, y});
}
}
}
}
}); });
const int UNPATH = game_map.limit();
for(auto point : has_light) {
for(int i = -1; i <= 1; i++) {
for(int j = -1; j <= 1; j++) {
if(!game_map.inmap(point.x+i, point.y+j)) continue;
if(paths[point.y+j][point.x+i] == UNPATH) {
lighting[point.y+j][point.x+i] = LIGHT_MAX;
}
}
}
}
} }
void System::enemy_pathing(DinkyECS::World &world, Map &game_map, Player &player) { void System::enemy_pathing(DinkyECS::World &world, Map &game_map, Player &player) {
@ -184,7 +138,7 @@ void System::collision(DinkyECS::World &world, Player &player) {
world.send<Events::GUI>(Events::GUI::LOOT, entity, loot); world.send<Events::GUI>(Events::GUI::LOOT, entity, loot);
inventory.gold += loot.amount; inventory.gold += loot.amount;
light.strength = 100; light.strength = 3;
collider.remove(loot_pos.location); collider.remove(loot_pos.location);
} else { } else {
println("UNKNOWN COLLISION TYPE {}", entity); println("UNKNOWN COLLISION TYPE {}", entity);
@ -232,7 +186,7 @@ void System::draw_map(DinkyECS::World &world, Map &game_map, ftxui::Canvas &canv
if(tile == config.WALL_TILE) { if(tile == config.WALL_TILE) {
canvas.DrawText(x * 2, y * 4, config.WALL_TILE, [light_value](auto &pixel) { canvas.DrawText(x * 2, y * 4, config.WALL_TILE, [light_value](auto &pixel) {
if(light_value > LIGHT_MIN) { if(light_value > lighting::MIN) {
pixel.foreground_color = Color::HSV(230, 20, 10); pixel.foreground_color = Color::HSV(230, 20, 10);
pixel.background_color = Color::HSV(230, 20, light_value / 2); pixel.background_color = Color::HSV(230, 20, light_value / 2);
} else { } else {

Loading…
Cancel
Save