Game::RaycastFOV - raycast field-of-view and related routines
use Game::RaycastFOV qw( bypair circle line cached_circle swing_circle raycast shadowcast ); # mostly internal utility routine bypair( { my ($x,$y) = @_; ... } $x1, $y1, $x2, $y2, ... ); # Bresenham in XS circle( { my ($cx,$cy) = @_; ... } $x, $y, $radius ); line( { my ($lx,$ly) = @_; ... } $x, $y, $x1, $y1 ); # fast, slower circle constructions cached_circle( { my ($cx,$cy) ... } $x, $y, $radius ); swing_circle( { my ($cx,$cy) ... } $x, $y, $radius, $swing ); # complicated, see docs and examples raycast( \&circle, sub { ... }, $x, $y, ... ); shadowcast( ... );
This module contains various subroutines that perform fast calculation of lines and circles; these in turn help with Field Of View (FOV) calculations. Raycasting and shadowcasting FOV calls are provided.
Speed is favored over error checking; the XS code may not work for large integer values; etc.
.#.## .##.##### # #.##..##... #. .##.#.##.#... #. #####..#.#### # #. .#.#.#.###.##.. #.#.## ####....#.##... #....# ##...#.@#T...## #.@# #..#.###....#.# ###.. .##.#####..#... #.. .##...####.## ##.# ....#.###.#.. . ###.###.#.. .######.# ....#
Will our plucky hero stumble into that Troll unseen? Tune in next week!
Utility function for slicing up an arbitrary list pairwise. Sort of like pairwise of List::Util only in a void context, and that returning the value -1 from the callback subroutine will abort the processing of subsequent items.
pairwise
-1
Like bypair but does not include code to abort processing the list.
Since v1.01.
This routine looks up the radius in the %circle_points variable (which is available for export and can be modified as need be) to obtain a pre-computed list of circle points (calculated by swing_circle) that are fed to the callback as is done for the circle call.
%circle_points
Will silently do nothing if the radius is not found in the cache. This is by design so that cached_circle is fast.
The cached points might (but are unlikely to) change without notice; calling code if paranoid should set specific sets of points to use or require a specific version of this module.
Bresenham circle. Note that this may not produce a completely filled-in FOV at various radius.
Since version 2.02 only unique points are passed to the callback.
Bresenham line. Returning the value -1 from the callback subroutine will abort the processing of the line at the given point.
Given a circle-fn such as circle or swing_circle and the center of a circle given by x and y, the raycast calls line between x,y and the points returned by the circle function; line in turn will call the user-supplied point-fn to handle what should happen at each raycasted point. Additional arguments ... will be passed to the circle-fn following x and y.
"EXAMPLES" may be of more help than the above text.
Performs a shadowcast FOV calculation of the given radius around the point x, y. Callbacks:
blockcb is called with newx, newy (the point shadowcasting has reached), deltax, and deltay (the delta from the origin for the point). It return a boolean indicating whether that coordinate is blocked on the level map (e.g. by a wall, a large monster, or maybe the angle from the starting point is no good, etc).
The deltax and deltay values are only passed in module version 2.02 or higher.
litcb is called with newx, newy, deltax, and deltay and should do whatever needs to be done to present that point as visible.
radiuscb is passed deltax, deltay, and radius and must return true if the deltas are within the radius. This allows for different FOV shapes. The delta values could be negative so will need to be run through abs or ** 2 to determine the distance.
abs
** 2
The callbacks may be called with points outside of a level map.
Finds points around the given radius by rotating a ray by swing radians starting from start-angle and ending at max-angle. Smaller swing values will result in a more complete circle at the cost of additional CPU and memory use. Each unique point is passed to the callback function:
sub_circle( sub { my ($newx, $newy) = @_; ... }, ... );
Has limited to no error checking; the caller should ensure that the swing value is positive, etc.
Since version 2.02.
Calls sub_circle with a starting angle of 0 and a max angle of pi * 2.
0
pi * 2
Prior to version 2.02 used distinct code.
See also the eg/ directory of this module's distribution.
eg/
https://thrig.me/src/ministry-of-silly-vaults.git has a FOV subdirectory with example scripts.
use Game::RaycastFOV qw(circle raycast swing_circle); use Math::Trig 'deg2rad'; # to only draw within the map area our $MAX_X = 79; our $MAX_Y = 23; # assuming a rows/columns array-of-arrays with characters our @map = ( ... ); # something that updates the @map sub plot { ... } # where the FOV happens and how big it is my ($x, $y, $radius) = ...; raycast( \&circle, sub { my ($lx, $ly) = @_; # whoops line has wandered outside of map return -1 if $lx < 0 or $lx > $MAX_X or $ly < 0 or $ly > $MAX_Y; # may instead build up a string to print to terminal my $ch = $map[$ly][$lx]; plot($lx, $ly, $ch); # abort the line if FOV is blocked return -1 if $ch eq '#'; }, $x, $y, $radius ); # or instead using swing_circle raycast( \&swing_circle, sub { my ($lx, $ly) = @_; return -1 if $lx < 0 or $lx > $MAX_X or $ly < 0 or $ly > $MAX_Y; my $ch = $map[$ly][$lx]; plot($lx, $ly, $ch); return -1 if $ch eq '#'; }, $x, $y, $radius, deg2rad(5) # different arguments! );
The plot routine may need to cache whether something has been printed to the given cell as raycast likes to revisit cells a lot, especially those close to the origin that are clear of FOV-blocking obstacles.
None known. Raycast is problematic, but that's probably baked into to algorithm, which is why other methods of FOV were invented.
Game::Xomb uses modified code from this module.
NetHack::FOV
https://thrig.me/src/ministry-of-silly-vaults.git
There are other FOV algorithms and implementations to be found on the Internet.
This software is Copyright (c) 2020 by Jeremy Mates.
This is free software, licensed under:
The (three-clause) BSD License
To install Game::RaycastFOV, copy and paste the appropriate command in to your terminal.
cpanm
cpanm Game::RaycastFOV
CPAN shell
perl -MCPAN -e shell install Game::RaycastFOV
For more information on module installation, please visit the detailed CPAN module installation guide.