#include <stdlib.h>
#include "libtcod.h"
#include "libtcod_int.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,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), 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 && m->cells[c].fov == false) {
int idx = 0;
if (visible && (m->cells[c-(m->width*dy)].fov == false || !m->cells[c-(m->width*dy)].transparent) && (x-dx >= 0 && x-dx < m->width && (m->cells[c-(m->width*dy)-dx].fov == false || !m->cells[c-(m->width*dy)-dx].transparent))) visible = false;
else while(visible && idx < obstacles_in_last_line) {
if (start_angle[idx] > end_slope || end_angle[idx] < start_slope) {
++idx;
}
else {
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) {
m->cells[c].fov = true;
done = false;
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,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), 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 && m->cells[c].fov == false) {
int idx = 0;
if (visible && (m->cells[c-dx].fov == false || !m->cells[c-dx].transparent) && (y-dy >= 0 && y-dy < m->height && (m->cells[c-(m->width*dy)-dx].fov == false || !m->cells[c-(m->width*dy)-dx].transparent))) visible = false;
else while(visible && idx < obstacles_in_last_line) {
if (start_angle[idx] > end_slope || end_angle[idx] < start_slope) {
++idx;
}
else {
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) {
m->cells[c].fov = true;
done = false;
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 c;
int max_obstacles;
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);
}