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 7 days 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 {
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 "render.hpp"
#include "save.hpp"
#include "lights.hpp"
#include "ftxui/screen/terminal.hpp" // for SetColorSupport, Color, TrueColor
#include <filesystem>
#include <fcntl.h>
@ -16,6 +17,7 @@
#endif
using namespace ftxui;
using lighting::LightSource;
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<Tile>(player.entity, {config.PLAYER_TILE});
world.set<Inventory>(player.entity, {5});
world.set<LightSource>(player.entity, {50});
world.set<LightSource>(player.entity, {5,2});
auto enemy = world.entity();
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<Combat>(enemy2, {20, 10});
world.set<Tile>(enemy2, {"*"});
world.set<LightSource>(enemy2, {100});
world.set<LightSource>(enemy2, {6,1});
auto gold = world.entity();
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();
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, {"!"});
}

@ -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) :
$limit(1000),
$width(width),
$height(height),
$input_map(height, MatrixRow(width, 1)),
$walls(height, MatrixRow(width, INV_WALL)),
$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),
$walls(walls_map)
{
$width = $walls[0].size();
$height = $walls.size();
}
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) {
dbc::pre("x out of bounds", origin_x < width());
dbc::pre("y out of bounds", origin_y < height());
dbc::pre("w out of bounds", w <= width());
dbc::pre("h out of bounds", h <= height());
dbc::pre("x out of bounds", origin_x < $width);
dbc::pre("y out of bounds", origin_y < $height);
dbc::pre("w out of bounds", w <= $width);
dbc::pre("h out of bounds", h <= $height);
for(size_t y = origin_y; y < origin_y + h; ++y) {
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) {
return x < width() && y < height();
return x < $width && y < $height;
}
void Map::set_door(Room &room, int value) {
@ -304,8 +308,8 @@ void Map::generate() {
Room root{
.x = 0,
.y = 0,
.width = width(),
.height = height()
.width = $width,
.height = $height
};
partition_map(root, 10);
@ -329,8 +333,8 @@ void Map::generate() {
walk(src.exit, target.entry);
clear_target(target.entry);
for(size_t y = 0; y < height(); ++y) {
for(size_t x = 0; x < width(); ++x) {
for(size_t y = 0; y < $height; ++y) {
for(size_t x = 0; x < $width; ++x) {
$walls[y][x] = !$walls[y][x];
}
}
@ -345,3 +349,84 @@ void Map::dump() {
dump_map("WALLS", $walls);
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 "point.hpp"
#include "tser.hpp"
#include "lights.hpp"
#define INV_WALL 0
#define INV_SPACE 1
#define WALL_VALUE 1
#define SPACE_VALUE 0
using lighting::LightSource;
struct Room {
size_t x = 0;
size_t y = 0;
@ -33,10 +36,12 @@ void add_neighbors(Matrix &closed, size_t j, size_t i);
class Map {
public:
int $limit;
size_t $width;
size_t $height;
Matrix $input_map;
Matrix $walls;
Matrix $paths;
Matrix $lighting; // BUG: this is not the place
Matrix $lightmap;
std::vector<Room> $rooms;
Map(Matrix input_map, Matrix walls_map, int limit);
@ -48,58 +53,42 @@ public:
Map(Map &map) = delete;
Matrix& paths() { return $paths; }
Matrix& lighting() { return $lighting; }
Matrix& lighting() { return $lightmap; }
Matrix& input_map() { return $input_map; }
Matrix& walls() { return $walls; }
int limit() { return $limit; }
size_t width() { return $walls[0].size(); }
size_t height() { return $walls.size(); }
size_t width() { return $width; }
size_t height() { return $height; }
int distance(Point to) { return $paths[to.y][to.x]; }
Room &room(size_t at) {
return $rooms[at];
}
size_t room_count() {
return $rooms.size();
}
Room &room(size_t at) { return $rooms[at]; }
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 add_door(Room &room);
bool inmap(size_t x, size_t y);
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);
bool can_move(Point move_to);
void generate();
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 partition_map(Room &cur, int depth);
void set_target(const Point &at, int value=0);
void clear_target(const Point &at);
bool walk(Point &src, Point &target);
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) {
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);
Point map_to_camera(const Point &loc, const Point &cam_orig);
Point center_camera(const Point &around, size_t view_x, size_t view_y);
void reset_light();
void set_light_target(const Point &at, int value=0);
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.
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 dump();
};

@ -1,9 +1,9 @@
TODAY'S GOAL:
* Neighbors needs a rewrite
* Neighbors algo isn't using greater parameter
* Refine the lighting to support multiple lights.
* Think up an enemy system.
* Revisit map generation.
* Create a index based light system and a unit test for it.
TODO:
* 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.
* 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.

@ -8,6 +8,7 @@
#include "ftxui/screen/color.hpp"
#include "ftxui/screen/terminal.hpp" // for SetColorSupport, Color, TrueColor
#include "dbc.hpp"
#include "lights.hpp"
const bool DEBUG_MAP=false;
@ -15,68 +16,21 @@ using std::string;
using namespace fmt;
using namespace components;
using ftxui::Color;
const int LIGHT_MIN = 20;
const int LIGHT_MAX = 160;
using lighting::LightSource;
void System::lighting(DinkyECS::World &world, Map &game_map, Player &player) {
using std::min, std::max, std::clamp;
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;
}
}
game_map.reset_light();
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();
auto &paths = game_map.paths();
game_map.path_light();
world.query<Position, LightSource>([&](const auto &ent, auto &position, auto &lightsource) {
game_map.clear_target(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});
}
}
}
}
game_map.render_light(lightsource, position.location);
});
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) {
@ -184,7 +138,7 @@ void System::collision(DinkyECS::World &world, Player &player) {
world.send<Events::GUI>(Events::GUI::LOOT, entity, loot);
inventory.gold += loot.amount;
light.strength = 100;
light.strength = 3;
collider.remove(loot_pos.location);
} else {
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) {
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.background_color = Color::HSV(230, 20, light_value / 2);
} else {

Loading…
Cancel
Save