#include <fov.h>
#include <stdlib.h>
#include <libtcod_int.h>
#include <libtcod_utility.h>
double * start_angle = NULL;
double * end_angle = NULL;
int allocated = 0;
void TCOD_map_compute_fov_restrictive_shadowcasting_quadrant (map_t *m, int player_x, int player_y, int max_radius, bool light_walls, int dx, int dy) {
{
int iteration = 1;
bool done = false;
int total_obstacles = 0;
int obstacles_in_last_line = 0;
double min_angle = 0.0;
int x;
int y;
y = player_y+dy;
if (y < 0 || y >= m->height) {
done = true;
}
while (!done) {
double slopes_per_cell = 1.0 / (double)(iteration);
double half_slopes = slopes_per_cell * 0.5;
int processed_cell = (int)((min_angle + half_slopes) / slopes_per_cell);
int minx = MAX(0, player_x - iteration);
int maxx = MIN(m->width - 1, player_x + iteration);
done = true;
for (x = player_x + (processed_cell * dx); x >= minx && x <= maxx; x+=dx) {
int c = x + (y * m->width);
bool visible = true;
bool extended = false;
double centre_slope = (double)processed_cell * slopes_per_cell;
double start_slope = centre_slope - half_slopes;
double end_slope = centre_slope + half_slopes;
if (obstacles_in_last_line > 0) {
if (
!(
m->cells[c-(m->width * dy)].fov &&
m->cells[c-(m->width * dy)].transparent
) &&
!(
m->cells[c-(m->width * dy) - dx].fov &&
m->cells[c-(m->width * dy) - dx].transparent
)
) {
visible = false;
} else {
int idx;
for (idx = 0; idx < obstacles_in_last_line && visible; ++idx) {
if (
start_slope <= end_angle[idx] &&
end_slope >= start_angle[idx]
) {
if (m->cells[c].transparent) {
if (
centre_slope > start_angle[idx] &&
centre_slope < end_angle[idx]
) {
visible = false;
}
} else {
if (
start_slope >= start_angle[idx] &&
end_slope <= end_angle[idx]
) {
visible = false;
} else {
start_angle[idx] = MIN(start_angle[idx], start_slope);
end_angle[idx] = MAX(end_angle[idx], end_slope);
extended = true;
}
}
}
}
}
}
if (visible) {
done = false;
m->cells[c].fov = true;
if (!m->cells[c].transparent) {
if (min_angle >= start_slope) {
min_angle = end_slope;
if (processed_cell == iteration) {
done = true;
}
} else if (!extended) {
start_angle[total_obstacles] = start_slope;
end_angle[total_obstacles++] = end_slope;
}
if (!light_walls) {
m->cells[c].fov = false;
}
}
}
processed_cell++;
}
if (iteration == max_radius) {
done = true;
}
iteration++;
obstacles_in_last_line = total_obstacles;
y += dy;
if (y < 0 || y >= m->height) {
done = true;
}
}
}
{
int iteration = 1;
bool done = false;
int total_obstacles = 0;
int obstacles_in_last_line = 0;
double min_angle = 0.0;
int x;
int y;
x = player_x+dx;
if (x < 0 || x >= m->width) {
done = true;
}
while (!done) {
double slopes_per_cell = 1.0 / (double)(iteration);
double half_slopes = slopes_per_cell * 0.5;
int processed_cell = (int)((min_angle + half_slopes) / slopes_per_cell);
int miny = MAX(0, player_y - iteration);
int maxy = MIN(m->height - 1, player_y + iteration);
done = true;
for (y = player_y + (processed_cell * dy); y >= miny && y <= maxy; y += dy) {
int c = x + (y * m->width);
bool visible = true;
bool extended = false;
double centre_slope = (double)processed_cell * slopes_per_cell;
double start_slope = centre_slope - half_slopes;
double end_slope = centre_slope + half_slopes;
if (obstacles_in_last_line > 0) {
if (
!(
m->cells[c-dx].fov &&
m->cells[c-dx].transparent
) &&
!(
m->cells[c-(m->width * dy) - dx].fov &&
m->cells[c-(m->width * dy) - dx].transparent
)
) {
visible = false;
} else {
int idx;
for (idx = 0; idx < obstacles_in_last_line && visible; ++idx) {
if (
start_slope <= end_angle[idx] &&
end_slope >= start_angle[idx]
) {
if (m->cells[c].transparent) {
if (
centre_slope > start_angle[idx] &&
centre_slope < end_angle[idx]
) {
visible = false;
}
} else {
if (
start_slope >= start_angle[idx] &&
end_slope <= end_angle[idx]
) {
visible = false;
} else {
start_angle[idx] = MIN(start_angle[idx], start_slope);
end_angle[idx] = MAX(end_angle[idx], end_slope);
extended = true;
}
}
++idx;
}
}
}
}
if (visible) {
done = false;
m->cells[c].fov = true;
if (!m->cells[c].transparent) {
if (min_angle >= start_slope) {
min_angle = end_slope;
if (processed_cell == iteration) {
done = true;
}
} else if (!extended) {
start_angle[total_obstacles] = start_slope;
end_angle[total_obstacles++] = end_slope;
}
if (!light_walls) {
m->cells[c].fov = false;
}
}
}
processed_cell++;
}
if (iteration == max_radius) {
done = true;
}
iteration++;
obstacles_in_last_line = total_obstacles;
x += dx;
if (x < 0 || x >= m->width) {
done = true;
}
}
}
}
void TCOD_map_compute_fov_restrictive_shadowcasting(TCOD_map_t map, int player_x, int player_y, int max_radius, bool light_walls) {
map_t *m = (map_t *)map;
int max_obstacles;
int c;
for(c = m->nbcells - 1; c >= 0; c--) {
m->cells[c].fov = false;
}
max_obstacles = m->nbcells / 7;
if (max_obstacles > allocated) {
allocated = max_obstacles;
if (start_angle != NULL) free(start_angle);
if (end_angle != NULL) free(end_angle);
start_angle = (double*)calloc(max_obstacles, sizeof(double));
end_angle = (double*)calloc(max_obstacles, sizeof(double));
}
m->cells[player_x+(player_y*m->width)].fov = true;
TCOD_map_compute_fov_restrictive_shadowcasting_quadrant (m, player_x, player_y, max_radius, light_walls, 1, 1);
TCOD_map_compute_fov_restrictive_shadowcasting_quadrant (m, player_x, player_y, max_radius, light_walls, 1, -1);
TCOD_map_compute_fov_restrictive_shadowcasting_quadrant (m, player_x, player_y, max_radius, light_walls, -1, 1);
TCOD_map_compute_fov_restrictive_shadowcasting_quadrant (m, player_x, player_y, max_radius, light_walls, -1, -1);
}