Better lighting and a circle algorithm that works more reliably.
This commit is contained in:
parent
03fe9b3d01
commit
35f2defc11
9 changed files with 97 additions and 87 deletions
|
@ -1,32 +1,32 @@
|
||||||
{
|
{
|
||||||
"WALL_TILE": {
|
"WALL_TILE": {
|
||||||
"foreground": [230, 20, 0],
|
"foreground": [230, 20, 0],
|
||||||
"background": [230, 20, 0],
|
"background": [230, 20, 2],
|
||||||
"display": "\ua5b8"
|
"display": "\ua5b8"
|
||||||
},
|
},
|
||||||
"FLOOR_TILE": {
|
"FLOOR_TILE": {
|
||||||
"foreground": [80, 100, 0],
|
"foreground": [80, 100, 0],
|
||||||
"background": [30, 20, 0],
|
"background": [30, 20, 2],
|
||||||
"display":"\u2849"
|
"display":"\u2849"
|
||||||
},
|
},
|
||||||
"PLAYER_TILE": {
|
"PLAYER_TILE": {
|
||||||
"foreground": [255, 200, 0],
|
"foreground": [255, 200, 0],
|
||||||
"background": [30, 20, 0],
|
"background": [30, 20, 2],
|
||||||
"display":"\ua66b"
|
"display":"\ua66b"
|
||||||
},
|
},
|
||||||
"ENEMY_TILE": {
|
"ENEMY_TILE": {
|
||||||
"foreground": [255, 200, 0],
|
"foreground": [255, 200, 0],
|
||||||
"background": [30, 20, 0],
|
"background": [30, 20, 2],
|
||||||
"display":"\u1d5c"
|
"display":"\u1d5c"
|
||||||
},
|
},
|
||||||
"BG_TILE": {
|
"BG_TILE": {
|
||||||
"foreground": [230, 20, 0],
|
"foreground": [230, 20, 0],
|
||||||
"background": [230, 20, 0],
|
"background": [230, 20, 2],
|
||||||
"display":"█"
|
"display":"█"
|
||||||
},
|
},
|
||||||
"WATER_TILE": {
|
"WATER_TILE": {
|
||||||
"foreground": [132, 200, 0],
|
"foreground": [132, 200, 0],
|
||||||
"background": [147, 220, 0],
|
"background": [147, 220, 2],
|
||||||
"display":"\u224b"
|
"display":"\u224b"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
40
lights.cpp
40
lights.cpp
|
@ -5,18 +5,50 @@
|
||||||
using std::vector;
|
using std::vector;
|
||||||
|
|
||||||
namespace lighting {
|
namespace lighting {
|
||||||
void LightRender::render_light(LightSource source, Point at) {
|
|
||||||
Point min, max;
|
|
||||||
clear_light_target(at);
|
|
||||||
vector<Point> has_light;
|
|
||||||
|
|
||||||
|
void LightRender::render_circle_light(LightSource source, Point at, PointList &has_light) {
|
||||||
|
for(matrix::circle it{at, source.distance + 1}; it.next();) {
|
||||||
|
for(int x = it.left; x < it.right; x++) {
|
||||||
|
if(matrix::inbounds($paths.$paths, x, it.y) &&
|
||||||
|
$paths.$paths[it.y][x] != WALL_PATH_LIMIT)
|
||||||
|
{
|
||||||
|
$lightmap[it.y][x] = light_level(source.strength, x, it.y);
|
||||||
|
has_light.push_back({(size_t)x, (size_t)it.y});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LightRender::render_compass_light(LightSource source, Point at, PointList &has_light) {
|
||||||
|
for(matrix::compass it{$lightmap, at.x, at.y}; it.next();) {
|
||||||
|
if($paths.$paths[it.y][it.x] != WALL_PATH_LIMIT) {
|
||||||
|
$lightmap[it.y][it.x] = light_level(source.strength, it.x, it.y);
|
||||||
|
has_light.push_back({it.x, it.y});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LightRender::render_square_light(LightSource source, Point at, PointList &has_light) {
|
||||||
for(matrix::in_box it{$lightmap, at.x, at.y, (size_t)source.distance}; it.next();) {
|
for(matrix::in_box it{$lightmap, at.x, at.y, (size_t)source.distance}; it.next();) {
|
||||||
if($paths.$paths[it.y][it.x] != WALL_PATH_LIMIT) {
|
if($paths.$paths[it.y][it.x] != WALL_PATH_LIMIT) {
|
||||||
$lightmap[it.y][it.x] = light_level(source.strength, it.x, it.y);
|
$lightmap[it.y][it.x] = light_level(source.strength, it.x, it.y);
|
||||||
has_light.push_back({it.x, it.y});
|
has_light.push_back({it.x, it.y});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LightRender::render_light(LightSource source, Point at) {
|
||||||
|
Point min, max;
|
||||||
|
clear_light_target(at);
|
||||||
|
PointList has_light;
|
||||||
|
|
||||||
|
if(source.distance == 0) {
|
||||||
|
render_compass_light(source, at, has_light);
|
||||||
|
} else if(source.distance == 1) {
|
||||||
|
render_square_light(source, at, has_light);
|
||||||
|
} else {
|
||||||
|
render_circle_light(source, at, has_light);
|
||||||
|
}
|
||||||
|
|
||||||
const int wall_light = source.strength + WALL_LIGHT_LEVEL;
|
const int wall_light = source.strength + WALL_LIGHT_LEVEL;
|
||||||
for(auto point : has_light) {
|
for(auto point : has_light) {
|
||||||
|
|
19
lights.hpp
19
lights.hpp
|
@ -13,20 +13,20 @@ namespace lighting {
|
||||||
int distance = 1; // higher is farther, in squares
|
int distance = 1; // higher is farther, in squares
|
||||||
};
|
};
|
||||||
|
|
||||||
const int MIN = 40;
|
const int MIN = 50;
|
||||||
const int MAX = 220;
|
const int MAX = 170;
|
||||||
const int MID = 140;
|
const int MID = 130;
|
||||||
|
|
||||||
const std::array<int, 10> LEVELS{
|
const std::array<int, 10> LEVELS{
|
||||||
MAX,
|
MAX,
|
||||||
200,
|
|
||||||
180,
|
|
||||||
160,
|
160,
|
||||||
|
150,
|
||||||
|
140,
|
||||||
MID,
|
MID,
|
||||||
120,
|
120,
|
||||||
100,
|
110,
|
||||||
80,
|
90,
|
||||||
60,
|
70,
|
||||||
MIN,
|
MIN,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -51,6 +51,9 @@ namespace lighting {
|
||||||
void light_box(LightSource source, Point from, Point &min_out, Point &max_out);
|
void light_box(LightSource source, Point from, Point &min_out, Point &max_out);
|
||||||
int light_level(int level, size_t x, size_t y);
|
int light_level(int level, size_t x, size_t y);
|
||||||
void render_light(LightSource source, Point at);
|
void render_light(LightSource source, Point at);
|
||||||
|
void render_square_light(LightSource source, Point at, PointList &has_light);
|
||||||
|
void render_compass_light(LightSource source, Point at, PointList &has_light);
|
||||||
|
void render_circle_light(LightSource source, Point at, PointList &has_light);
|
||||||
Matrix &lighting() { return $lightmap; }
|
Matrix &lighting() { return $lightmap; }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
4
main.cpp
4
main.cpp
|
@ -46,7 +46,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, {7,1});
|
world.set<LightSource>(enemy2, {7,0});
|
||||||
|
|
||||||
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)});
|
||||||
|
@ -55,7 +55,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, {2,3});
|
world.set<LightSource>(wall_torch, {3,4});
|
||||||
world.set<Tile>(wall_torch, {"☀"});
|
world.set<Tile>(wall_torch, {"☀"});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
48
matrix.cpp
48
matrix.cpp
|
@ -183,48 +183,18 @@ namespace matrix {
|
||||||
circle::circle(Point center, int radius) :
|
circle::circle(Point center, int radius) :
|
||||||
center(center), radius(radius)
|
center(center), radius(radius)
|
||||||
{
|
{
|
||||||
xi = 0;
|
top = max(center.y - radius, size_t(0));
|
||||||
yi = radius;
|
bottom = center.y + radius;
|
||||||
m = 5 - 4 * radius;
|
y = top;
|
||||||
step = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void circle::update() {
|
|
||||||
if(m > 0) {
|
|
||||||
yi--;
|
|
||||||
m -= 8 * yi;
|
|
||||||
}
|
|
||||||
xi++;
|
|
||||||
m += 8 * xi + 4;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool circle::next() {
|
bool circle::next() {
|
||||||
if(xi <= yi) {
|
y++;
|
||||||
switch(step % 4) {
|
if(y <= bottom) {
|
||||||
case 0:
|
dy = y - center.y;
|
||||||
x0 = center.x - xi;
|
dx = floor(sqrt(radius * radius - dy * dy));
|
||||||
y = center.y - yi;
|
left = center.x - dx;
|
||||||
x1 = center.x + xi;
|
right = center.x + dx;
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
x0 = center.x - yi;
|
|
||||||
y = center.y - xi;
|
|
||||||
x1 = center.x + yi;
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
x0 = center.x - yi;
|
|
||||||
y = center.y + xi;
|
|
||||||
x1 = center.x + yi;
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
x0 = center.x - xi;
|
|
||||||
y = center.y + yi;
|
|
||||||
x1 = center.x + xi;
|
|
||||||
update();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
step++;
|
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
|
|
14
matrix.hpp
14
matrix.hpp
|
@ -120,13 +120,13 @@ namespace matrix {
|
||||||
struct circle {
|
struct circle {
|
||||||
Point center;
|
Point center;
|
||||||
int radius = 0;
|
int radius = 0;
|
||||||
int xi = 0;
|
int y = 0;
|
||||||
int yi = 0;
|
int dx = 0;
|
||||||
int m = 0;
|
int dy = 0;
|
||||||
int step = 0;
|
int left = 0;
|
||||||
int x0;
|
int right = 0;
|
||||||
int x1;
|
int top = 0;
|
||||||
int y;
|
int bottom = 0;
|
||||||
|
|
||||||
circle(Point center, int radius);
|
circle(Point center, int radius);
|
||||||
void update();
|
void update();
|
||||||
|
|
13
systems.cpp
13
systems.cpp
|
@ -189,20 +189,19 @@ void System::draw_map(DinkyECS::World &world, Map &game_map, const Matrix &light
|
||||||
for(size_t x = 0; x < end_x; ++x) {
|
for(size_t x = 0; x < end_x; ++x) {
|
||||||
const TileCell& tile = tiles.at(start.x+x, start.y+y);
|
const TileCell& tile = tiles.at(start.x+x, start.y+y);
|
||||||
int light_value = debug.LIGHT ? 160 : lighting[start.y+y][start.x+x];
|
int light_value = debug.LIGHT ? 160 : lighting[start.y+y][start.x+x];
|
||||||
|
int dnum = debug.PATHS ? paths[start.y+y][start.x+x] : WALL_PATH_LIMIT;
|
||||||
|
|
||||||
if(debug.PATHS) {
|
if(debug.PATHS && dnum != WALL_PATH_LIMIT) {
|
||||||
int dnum = paths[start.y+y][start.x+x];
|
string num = dnum > 15 ? "*" : format("{:x}", dnum);
|
||||||
string num = format("{:x}", dnum);
|
|
||||||
num = num.size() > 2 ? "*" : num;
|
|
||||||
|
|
||||||
canvas.DrawText(x * 2, y * 4, num, [dnum, light_value](auto &pixel) {
|
canvas.DrawText(x * 2, y * 4, num, [dnum, light_value](auto &pixel) {
|
||||||
pixel.foreground_color = Color::HSV(dnum * 20, 150, 200);
|
pixel.foreground_color = Color::HSV(dnum * 20, 150, 200);
|
||||||
pixel.background_color = Color::HSV(30, 20, light_value / 5);
|
pixel.background_color = Color::HSV(30, 20, light_value / 2);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
canvas.DrawText(x * 2, y * 4, tile.display, [tile, light_value](auto &pixel) {
|
canvas.DrawText(x * 2, y * 4, tile.display, [tile, light_value](auto &pixel) {
|
||||||
pixel.foreground_color = Color::HSV(tile.fg_h, tile.fg_s, light_value);
|
pixel.foreground_color = Color::HSV(tile.fg_h, tile.fg_s, light_value - tile.fg_v);
|
||||||
pixel.background_color = Color::HSV(tile.bg_h, tile.bg_s, light_value / 2);
|
pixel.background_color = Color::HSV(tile.bg_h, tile.bg_s, light_value / tile.bg_v);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -226,22 +226,28 @@ TEST_CASE("prototype line algorithm", "[matrix:line]") {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("prototype circle algorithm", "[matrix:circle]") {
|
TEST_CASE("prototype circle algorithm", "[matrix:circle]") {
|
||||||
|
for(int count = 0; count < 20; count++) {
|
||||||
size_t width = Random::uniform<size_t>(10, 13);
|
size_t width = Random::uniform<size_t>(10, 13);
|
||||||
size_t height = Random::uniform<size_t>(10, 15);
|
size_t height = Random::uniform<size_t>(10, 15);
|
||||||
|
int pos_mod = Random::uniform<int>(-3,3);
|
||||||
Map map(width,height);
|
Map map(width,height);
|
||||||
// create a target for the paths
|
// create a target for the paths
|
||||||
Point start{.x=map.width() / 2, .y=map.height()/2};
|
Point start{.x=map.width() / 2 + pos_mod, .y=map.height()/2 + pos_mod};
|
||||||
|
|
||||||
for(int radius = 2; radius < 5; radius++) {
|
for(int radius = 2; radius < 10; radius++) {
|
||||||
// use an empty map
|
// use an empty map
|
||||||
Matrix result = map.walls();
|
Matrix result = map.walls();
|
||||||
|
|
||||||
for(matrix::circle it{start, radius}; it.next();) {
|
for(matrix::circle it{start, radius}; it.next();) {
|
||||||
for(int i = it.x0; i < it.x1; i++) {
|
for(int x = it.left; x < it.right; x++) {
|
||||||
result[it.y][i] += 1;
|
// println("top={}, bottom={}, center.y={}, dy={}, left={}, right={}, x={}, y={}", it.top, it.bottom, it.center.y, it.dy, it.left, it.right, x, it.y);
|
||||||
|
if(matrix::inbounds(result, x, it.y)) {
|
||||||
|
result[it.y][x] += 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// matrix::dump("RESULT AFTER CIRCLE", result, start.x, start.y);
|
// matrix::dump("RESULT AFTER CIRCLE", result, start.x, start.y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -143,7 +143,7 @@ void WorldBuilder::generate() {
|
||||||
|
|
||||||
Point center = $map.place_entity(1);
|
Point center = $map.place_entity(1);
|
||||||
for(matrix::circle it{center, 3}; it.next();) {
|
for(matrix::circle it{center, 3}; it.next();) {
|
||||||
for(int x = it.x0; x < it.x1; x++) {
|
for(int x = it.left; x < it.right; x++) {
|
||||||
if($map.inmap(x, it.y) && !$map.iswall(x, it.y)) {
|
if($map.inmap(x, it.y) && !$map.iswall(x, it.y)) {
|
||||||
$map.$tiles.set_tile(x, it.y, "WATER_TILE");
|
$map.$tiles.set_tile(x, it.y, "WATER_TILE");
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue