ID:2186744
 
This causes any mobs in a player's "blind spot" to be masked by a null variable and essentially render them invisible to the player unless the player re-orients themself toward the mob and calls the Blindspot and CanSee procs to refresh their new "blind spots" and what they can then see.

mob

var/blindspots = list()
var/complex_blind = 0
var/masked_from[0]



proc/RefreshView()
Blindspots(1)
for (var/mob/M in view())
CanSee(M)

proc/RefreshViewers()
for(var/mob/P in view())
P.CanSee(src)

proc/Blindspots(recalc)
if (recalc)
blindspots = list()
if (!client && !complex_blind)
//NPCs don't need big blindspot calculation for now but have that var there just in case
if (dir == SOUTH)
blindspots += locate(x, y + 1, z)
if (dir == NORTH)
blindspots += locate(x, y - 1, z)
if (dir == EAST)
blindspots += locate(x - 1, y, z)
if (dir == WEST)
blindspots += locate(x + 1, y, z)
return
var/p
var/newy = y
var/newx = x
var/xref = x
var/yref = y
var/loops = 0
if (dir == SOUTH)
for(p=0, p<6, p++)
newy = newy + 1
blindspots += locate(newx, newy, z)
if (loops)
for(p=0, p<loops, p++)
newx = newx + 1
blindspots += locate(newx, newy, z)
newx = xref
for(p=0, p<loops, p++)
newx = newx - 1
blindspots += locate(newx, newy, z)
newx = xref
loops++
if (dir == NORTH)
for(p=0, p<6, p++) //p<howevermany tiles the screen extends from the player
newy = newy - 1
blindspots += locate(newx, newy, z)
if (loops)
for(p=0, p<loops, p++)
newx = newx + 1
blindspots += locate(newx, newy, z)
newx = xref
for(p=0, p<loops, p++) // The tiles immediately in the player's rear peripheral aren't made a 'blind spot' this way
newx = newx - 1
blindspots += locate(newx, newy, z)
newx = xref
loops++
if (dir == EAST)
for(p=0, p<6, p++)
newx = newx - 1
blindspots += locate(newx, newy, z)
if (loops)
for(p=0, p<loops, p++)
newy = newy + 1
blindspots += locate(newx, newy, z)
newy = yref
for(p=0, p<loops, p++)
newy = newy - 1
blindspots += locate(newx, newy, z)
newy = yref
loops++
if (dir == WEST)
for(p=0, p<6, p++)
newx = newx + 1
blindspots += locate(newx, newy, z)
if (loops)
for(p=0, p<loops, p++)
newy = newy + 1
blindspots += locate(newx, newy, z)
newy = yref
for(p=0, p<loops, p++)
newy = newy - 1
blindspots += locate(newx, newy, z)
newy = yref
loops++



proc/CanSee(mob/M)
var/T
if (src.key in M.masked_from)
for (T in blindspots)
if (M.loc == T)
return 0
if (client)
del M.masked_from[src.key]
M.masked_from -= src.key
return 1
else
for (T in blindspots)
if (M.loc == T)
if (client)
var/image/mask = image(null, loc = M)
M.masked_from[src.key] = mask
mask.override = 1
src << mask
return 0


If (client) code in saves on a little bit of CPU if the player is disconnected or an NPC. Feel free to critique and offer feedback on how this could be made more efficient or useful if you think you see a way it could.
Send the mask images to garbage collection instead of using hard delete.

Looks clean though.
CanSee is called on mobs when they move into a new tile in my game, so the hard delete is there if they move out of the blind spot.

EDIT: Rather, RefreshViewers is called.
In response to Gooobai
I'm pretty sure you can still garbage collect there. If I'm not mistaken, only a single client ever sees the image -- which means you only have to remove the image from said single client when you go to remove references and null its loc.
In response to FKI
Images don't need their loc set to null since they don't actually go in their loc's contents.
In response to Kaiochao
Kaiochao wrote:
Images don't need their loc set to null since they don't actually go in their loc's contents.

True. Admittedly it's a habit of mine to clear any/all references at that time, as a sort of safety net.
In response to FKI
I guess the best way to ensure garbage collection is to destroy the world?

The garbage collection of some object A requires that there be no references to A, not that A can't have any references to other objects. It can be confusing sometimes.

Back on-topic though, I wonder what the difference in performance would be if you were to check visibility for mobs individually rather than generate a list of invisible locs all the time. Like, if I wanted to check if an object was in my field of view, I would probably start by using a single vector dot product, which would be cleaner, shorter, support pixel movement, and work in 360 degrees, but requires some basic vector math knowledge.
switch() is something you could use to improve performance.

edit: CanSee() , var/image/mask , that variable can be defined outside of the for() for better performance.
Is it possible to use pixel movement while retaining the tile based movement if for the sake of smoother animations or would that not have an effect?

Also, I don't know vector math, but there's no reason I can't learn. Do you have any good resources you could point me to to start?
In response to Gooobai
I second this, I've been meaning to ask about that for some time but I'll drop it here to make it easier on the helpers.
In response to Gooobai
Gooobai wrote:
Is it possible to use pixel movement while retaining the tile based movement if for the sake of smoother animations or would that not have an effect?

You can abuse pixel_x & pixel_y to achieve pixel like movement for things while sticking with tile-based movement.