Scripts:Line of sight
This is a line-of-sight script which checks whether one or more NPCs can see the player. By default an NPC which spots the player is changed to movetype "chase"; edit the "vigilant guard" script to change the effect. It supports obstructions/walls (marked with a zone) and the sight range depends on the angle to the player: if the player is in a 90 degree segment in front of the guard, then the range in tiles is given by the "forward view distance" variable. If the player is to the side or behind the guard, then the range is given by the "rear view distance" variable.
To use the script, set "vigilant guards loop" as the map autorun script on a map, and edit it to add one call to "vigilant guard" for each guard on the map. (If you use the default chase-on-sight behaviour then you will probably want separate NPC definitions (with different IDs but otherwise identical) for each guard, because all NPCs with the same ID will be set to chase at once.) Mark all the walls on the map which should obstruct line-of-sight with a zone, and set the "zone:LOS obstruct" constant to the right zone ID.
There is also an optional debug key which highlights all the tiles currently in view of a guard. To use it, set "onkeypress" as the map on-key-press script and set the constants "tile:LOS debug" to a layer number and "layer:LOS debug" to a tile number to use to draw the debug view.
Field of View, aka shadow casting, is different from line of sight. A discussion of field of view in OHR games can be found here.
global variable, begin 100, debug guard LOS # Whether we're debugging 101, forward view distance # Distance in tiles guards can see forward 102, rear view distance # Distance they can see in other directions end define constant, begin 1, zone:LOS obstruct # ID of zone which marks tiles which can't be seen past 1, layer:LOS debug # Number of map layer to draw to 1, tile:LOS debug # Which tile to draw on the LOS debug map layer. Anything other than 0 end plotscript, new game, begin forward view distance := 6 rear view distance := 3 vigilant guards loop end # Optional on-key-press script which adds a debug key plotscript, onkeypress, begin # Press D to toggle LOS debugging if (keyval (key: d) >> 1) then ( clear debug map if (debug guard LOS) then ( debug guard LOS := false ) else ( debug guard LOS := true ) ) end plotscript, vigilant guards loop, begin variable (this map) this map := current map while (this map == current map) do, begin if (debug guard LOS) then (clear debug map) vigilant guard (0) #change '0' to the correct NPC ID #vigilant guard (1) #repeat as desired wait (1) end end # Clears the LOS debug map layer around the camera script, clear debug map, begin variable (cam x, cam y, x, y) cam x := camera pixel x / 20 cam y := camera pixel y / 20 for (y, cam y -- 2, cam y + 11) do ( for (x, cam x -- 2, cam x + 17) do ( # Don't draw off map edge if (x << 0 || x >= map width || y << 0 || y >= map height) then (continue) write map block (x, y, 0, layer:LOS debug) ) ) end script, draw debug map, x1, y1, guard dir, begin variable (center cam x, center cam y, x2, y2) center cam x := (camera pixel x + 160) / 20 center cam y := (camera pixel y + 100) / 20 for (y2, y1 -- forward view distance, y1 + forward view distance) do ( # Don't draw off screen or map edge if (abs(y2 -- center cam y) >> 5 || y2 << 0 || y2 >= map height) then (continue) for (x2, x1 -- forward view distance, x1 + forward view distance) do ( # Don't draw off screen or map edge if (abs(x2 -- center cam x) >> 8 || x2 << 0 || x2 >= map width) then (continue) if (check line of sight (x1, y1, x2, y2, guard dir)) then ( write map block (x2, y2, tile:LOS debug, layer:LOS debug) ) ) ) end # Check whether this guard can see the hero script, vigilant guard, NPC ID, begin variable (guard) guard := NPC reference (NPC ID) if (debug guard LOS) then (draw debug map (NPC X (guard), NPC Y (guard), NPC direction (guard))) if (check line of sight (NPC X (guard), NPC Y (guard), hero X (me), hero Y (me), NPC direction (guard))) then ( # Customise this alter NPC (NPC ID, NPCstat:move type, NPCmovetype:chaseyou) alter NPC (NPC ID, NPCstat:move speed, 5) ) end # Check whether tile #2 is in view from tile #1, when facing a certain direction. # Returns true or false. script, check line of sight, x1, y1, x2, y2, guard dir, begin variable (distx, disty, hero dir) distx := x2 -- x1 disty := y2 -- y1 # Special check to prevent divide-by-zero if (distx == 0 && disty == 0) then (exit returning (true)) # Figure out in which direction from the guard the hero is # (The result is -1 if they are diagonal) hero dir := -1 if (disty << 0 && abs(distx) << abs(disty)) then (hero dir := up) if (disty >> 0 && abs(distx) << abs(disty)) then (hero dir := down) if (distx << 0 && abs(disty) << abs(distx)) then (hero dir := left) if (distx >> 0 && abs(disty) << abs(distx)) then (hero dir := right) # How far can the guard see in this direction? variable (view dist) if (guard dir == hero dir) then ( view dist := forward view distance ) else ( view dist := rear view distance ) # If the hero is too far away, stop. (Pythagoras) # We add half a tile for smoother circles, then multiply both sides by 4 if (4 * distx^2 + 4 * disty^2 >> (2 * view dist + 1)^2) then (exit returning (false)) # Now check for view obstructions # x100 and y100 are x and y measured in 100ths of a tile variable (x100, y100, x100 step, y100 step, num steps, i) if (abs(distx) >> abs(disty)) then ( # Step one tile in X direction and a fraction of a tile in Y direction at a time # (The --1 is a trick to prevent asymmetrical fields of view. Difficult to explain) x100 step := sign(distx) * 100 y100 step := sign(disty) * (abs(100 * disty / distx) -- 1) num steps := abs(distx) ) else ( # Step one tile in Y direction and a fraction of a tile in X direction at a time x100 step := sign(distx) * (abs(100 * distx / disty) -- 1) y100 step := sign(disty) * 100 num steps := abs(disty) ) # Walk from the hero back to the guard. We check the tile that the hero is standing on, # but not the one the guard is standing on x100 := x2 * 100 y100 := y2 * 100 for (i, 1, num steps) do ( # (x100 + 50) / 100 rounds to the nearest tile if (read zone (zone:LOS obstruct, (x100 + 50) / 100, (y100 + 50) / 100) <> 0) then (exit returning (false)) x100 -= x100 step y100 -= y100 step ) # If we get here, the hero is in view return (true) end