void fov_circle(fov_map_t* map, void* light_source,
                int x, int y, int max_radius) {
    int oct, r2;
    r2 = max_radius * max_radius;
    /* recursive shadow casting */
    for (oct=0; oct < 8; ++oct) {
        cast_light(map, light_source, y, x, 1, 1.0, 0.0, max_radius, r2,
                   mult[0][oct], mult[1][oct], mult[2][oct], mult[3][oct]);
    }
    map->apply(map, NULL, x, y);
}
static void cast_light(map_t *map,int cx, int cy,int row,float start, float end, int radius, int r2,
	int xx, int xy, int yx, int yy, int id, bool light_walls) {
	int j;
	float new_start=0.0f;
	if ( start < end ) return;
	for (j=row; j< radius+1; j++) {
		int dx=-j-1;
		int dy=-j;
		bool blocked=false;
		while ( dx <= 0 ) {
			int X,Y;
			dx++;
			X=cx+dx*xx+dy*xy;
			Y=cy+dx*yx+dy*yy;
			if ((unsigned)X < (unsigned)map->width && (unsigned)Y < (unsigned)map->height) {
				float l_slope,r_slope;
				int offset;
				offset=X+Y*map->width;
				l_slope=(dx-0.5f)/(dy+0.5f);
				r_slope=(dx+0.5f)/(dy-0.5f);
				if( start < r_slope ) continue;
				else if( end > l_slope ) break;
				if ( dx*dx+dy*dy <= r2
					&& (light_walls || map->cells[offset].transparent)) map->cells[offset].fov=1;
				if ( blocked ) {
					if (!map->cells[offset].transparent) {
						new_start=r_slope;
						continue;
					} else {
						blocked=false;
						start=new_start;
					}
				} else {
					if (!map->cells[offset].transparent && j < radius ) {
						blocked=true;
						cast_light(map,cx,cy,j+1,start,l_slope,radius,r2,xx,xy,yx,yy,id+1,light_walls);
						new_start=r_slope;
					}
				}
			}
		}
		if ( blocked ) break;
	}
}
static void cast_light(fov_map_t* map, void* light_source, int cx, int cy,
                       int row, float start, float end, int radius, int r2,
                       int xx, int xy, int yx, int yy) {
    int j;
    float new_start = 0.0f;
    if(start < end) return;
    for(j = row; j < radius + 1; j++) {
        int dx = -j - 1;
        int dy = -j;
        int blocked = 0;
        while(dx <= 0) {
            int X, Y;
            ++dx;
            X = cx + dx * xx + dy * xy;
            Y = cy + dx * yx + dy * yy;
            if(X < map->width && Y < map->height) {
                float l_slope, r_slope;
                l_slope = (dx - 0.5f) / (dy + 0.5f);
                r_slope = (dx + 0.5f) / (dy - 0.5f);
                if(start < r_slope) continue;
                else if( end > l_slope ) break;
                if(dx * dx + dy * dy <= r2) {
                    map->apply(map, NULL, X, Y);
                }
                if(blocked) {
                    if(map->blocked(map, X, Y)) {
                        new_start = r_slope;
                        continue;
                    } else {
                        blocked = 0;
                        start = new_start;
                    }
                } else if(map->blocked(map, X, Y) && j < radius ) {
                    blocked = 1;
                    cast_light(map, light_source, cx, cy, j + 1, start, l_slope,
                               radius, r2, xx, xy, yx, yy);
                    new_start = r_slope;
                }
            }
        }
        if(blocked) break;
    }
}
void TCOD_map_compute_fov_recursive_shadowcasting(TCOD_map_t map, int player_x, int player_y, int max_radius, bool light_walls) {
	int oct,c,r2;
	map_t *m = (map_t *)map;
	/* clean the map */
	for (c=m->nbcells-1; c >= 0; c--) {
		m->cells[c].fov=0;
	}
	if ( max_radius == 0 ) {
		int max_radius_x=m->width-player_x;
		int max_radius_y=m->height-player_y;
		max_radius_x=MAX(max_radius_x,player_x);
		max_radius_y=MAX(max_radius_y,player_y);
		max_radius = (int)(sqrt(max_radius_x*max_radius_x+max_radius_y*max_radius_y))+1;
	}
	r2=max_radius*max_radius;
	/* recursive shadow casting */
	for (oct=0; oct < 8; oct++) cast_light(m,player_x,player_y,1,1.0,0.0,max_radius,r2,
		mult[0][oct],mult[1][oct],mult[2][oct],mult[3][oct],0,light_walls);
	m->cells[player_x+player_y*m->width].fov=1;
}