ID:166520
 
I'm trying to soft-code a view()-like visibility, opacity and line of sight system.

I could only dig up a handful of info on the view() function. I have read (from reputable sources) that view() is:
client side, hard-coded, and uses an edge detection algorithm.

What I'm wondering is, does anyone know how this algorithm works as far as line of sight? IOW, is it a raycasting engine, a shadowcasting engine, what?

I've looked at several LOS demos on http://roguelikedevelopment.org/php/category/ showCategory.php?path=development/&category=LOS
But, I'm afraid I'm still at a loss to describe view()'s behavior.

Anyone?
mob/verb/view8()
for(var/mob/m in block(locate(x-8,y-8,z),locate(x+8,y+8,z))
m << "[usr] talks!"


? =P
In response to Sad Lonely Geek
Sad Lonely Geek wrote:
> mob/verb/view8()
> for(var/mob/m in block(locate(x-8,y-8,z),locate(x+8,y+8,z))
> m << "[usr] talks!"
>

? =P

Uhhh...welllllllllll...I meant in regards to visibility and line of sight.
I'll edit my post to make that clearer.
view(range,ref) = everyone that [ref] can see within [range] is in this list.
oview(range,ref) = everyone that [ref] can see within [range], excluding [ref] and it's contents.
viewers(range,ref) = everyone that can see [ref] in [range] from [ref] (if say a spy near [ref] has invisibility=1 the spy would be included on the list, and can eavesdrop in on conversations).
oviewers(range,ref) = everyone that can see [ref] in [range] from [ref] minus [ref] and it's contents.

hearers(range,ref) = everyone that can hear [ref]. If someone is blinded (mob.sight=2) or it's dark outside (luminosity=0), people are still included in this list even though they would not be included on the lists above because they'd be incapable of seeing [ref].
ohearers(range,ref) = same as above, minus [ref] and it's contents.

For a say verb, hearers() is recommended, as well as for sending sound()s.
For a visual effect (like displaying an /image object), viewers() is recommended.
In response to Android Data
Android Data wrote:
view(range,ref) ...

Thanks, but...
Again, I understand the syntax, usage, and use of these functions. What I'm looking for is some information on the system used to determine if an atom or turf is in the ref's line of sight.

...v. ...1. ...v.
..... ..1.. .....
..@.. ..@.. ..@..
..... ..... ..2..
..#.. ..#.. ..#..
..i.. ..i.. ..i..
where
(.) = turf/opacity = 0 (floor)
(#) = turf/opacity = 1 (wall)
(@) = ref atom
(v) = visible mob or atom
(i) = invisible mob or atom
(1) = raycast to visible mob, not blocked by opaque turf
(2) = ray toward not visible mob, blocked by opaque wall


If we're using a raycasting engine in view(), are rays drawn simply from the center spot to the outer edge of view range, or are they drawn to ALL the turfs in view range.
If view isn't using a raycasting engine, what sort of algorithm is it using?
I don't know much about those systems, the raycasting and the shadow-somethingorother, but line of sight detection shouldn't be too difficult.

Use the line formula y=mx+b to loop through all turfs on a line from reference to target. At each turf, check to see if it or anything in it would have any reason to block line of sight, and if it does return 0. If you safely get past that loop, then you can assume the one object is in line of sight of the other.
In response to Loduwijk
Loduwijk wrote:
Use the line formula y=mx+b to loop through all turfs on a line from reference to target. At each turf, check to see if it or anything in it would have any reason to block line of sight, and if it does return 0. If you safely get past that loop, then you can assume the one object is in line of sight of the other.

Alright! That's exactly the subject I'm trying to get to!
A little more explanation:
Right now, I'm using a modified Bresenham's line drawing routine to cast rays (it works using a similar formula as y=mx+b, as you suggested). The rays project from the reference object outward to the edge of the viewing area, testing all turfs that the line touches for opacity. This gets me an approximation of the visual effect of the built in view(). However, there are differences. My raycasting yields less visible tiles 'behind' an opaque turf than Dreamseeker's.
It almost seems as though DS's casts a second ray from where the first is interrupted by the opaque tile, giving the appearence of a mob's view to see around a tile (especially from larger distances between the viewer and the opaque turf).
Anyone know for sure?
In response to TheMonkeyDidIt
I'm not sure how the built-in view function does it, but I wouldn't worry too much unless you are using its built-in sight variables in combination with your own system such that it doesn't catch an object even though the player can see it.

Still, I've seen plenty of professional games that suffer from some slight viewing problems, where you can see an object but can't interact with it or you can but you still can't see it yet. Why that would be in such games I don't know, since it's not like they would be combining two seperate viewing systems like you are, but I've seen it happen.
In response to Loduwijk
Loduwijk wrote:
... such that it doesn't catch an object even though the player can see it.

That's what I was concerned about.
Well, we'll see what happens.

Still, I've seen plenty of professional games that suffer from some slight viewing problems...

True enough.
I suppose I'll move on to other problems and use what I have. Not good to become so focused on one aspect at the expense of others.
Thanks for replying, tho!
In response to TheMonkeyDidIt
It didn't come to me while I was writing that last post, but something just hit me - something that should have been obvious.

If you are using it in combination with the viewing system that is already in place, then why don't you just check to see if the object is in the other object's view with the view function?

I can understand why you might want your own function to handle objects farther away than the built-in view function allows for, but if it's close enough, why not just do a simple if(O in view())? Then it all works out.
In response to Loduwijk
Loduwijk wrote:
It didn't come to me while I was writing that last post, but something just hit me - ...

I'm definately going to use the built-in view for some things (I haven't been able to get a great way of adding verbs to a client from an atom or turf at a distance without it (set src in view(10)), as that verb setting won't allow a custom proc).
But with the display map in iso8 I'm using, the viewing distance is, for most of the shown map, more than 10 tiles. View() won't do more than 10 at the moment, so for opacity and line of sight on far tiles, I've got to write my own.

If view could do more than a radius of 10, could be overridden or customized, and could be replaced in set src = view(), than I'd always use that as it's a gajillion times faster than what I could write.

I think I'm onto something, but it will be fairly convoluted, which seems to be my M.O.
Thanks, Loduwijk!
Psst, if you get something that's working nicely, would you mind posting the code? I'm trying to get over a client.eye's visibility problem (where you have to create your own visibility procedure). So I'll appreciate it if you can help me out :).
In response to TheMonkeyDidIt
I thought that you could specify a range and it could go up to twice the view setting. Maybe that was just the range/orange function.
In response to Crashed
Here's something from one of my own personal libraries that has a bunch of stuff I use a lot or that I think could be useful.
point
var
x = 0
y = 0
z = 0
New(x, y, z)
src.x = x
src.y = y
src.z = z
proc
p(x, y, z)
//returns a new point object with coordinates x, y, z
if(istype(x, /point)||istype(x, /atom))
var/point/p = x
x = p.x
y = p.y
z = p.z
return new /point(x, y, z)
locatep(point/p)
//locatep(p) mimics functionality of locate(x, y, z) where x, y, z are p.x, p.y, p.z
return locate(p.x, p.y, p.z)

line
var
point
p1; p2
New(point/p1, point/p2)
src.p1 = p1
src.p2 = p2
proc
m()
//returns the slope
return (p2.y-p1.y)/(p2.x-p1.x)
y_intercept()
//returns the y of the y-intercept point
return p1.y-p1.x*m()
online(point/p)
//returns 1 if point p is on this line, 0 otherwise
if(p.y == m()*p.x + y_intercept()) return 1
return 0
parallel(line/L)
//returns 1 if line L is parallel to this line, 0 otherwise
return (m() == L.m())
perpendicular(line/L)
//returns 1 if line L is perpendicular to this line, 0 otherwise
return (m() == -1/L.m())
xpoint(y)
//returns the x value of the point that corresponds to the y value given
return (y-y_intercept())/m()
ypoint(x)
//returns the y value of the point that corresponds to the x value given
return m()*x + y_intercept()
get_points()
//returns a list of points on the line from p1 to p2
list/L[0]
var/m = m()
if(m <= 1)
for(var/index = p1.x to p2.x)
L += p(index, ypoint(index))
else
for(var/index = p1.y to p2.y)
L += p(xpoint(index), index)
return L

This library of mine I took it out of is something that I just started recently when I decided it was silly to keep reprogramming the same things for half the projects I do. The line part is something I have never actually used but which I thought would be useful for possible future applications, and as such I haven't actually tested that code; but it should work fine unless I'm overlooking some silly mistake.

Either way, that can be used to easily create a line of site function.
atom/InSight(atom/target)
var/line/L = new(p(src), p(target))
var/list/turf_line = L.get_points()
for(var/index = 1 to turf_line.len)
turf_line[index] = locatep(turf_line[index])
//the rest is easy common sense stuff that anybody can do
//just use this list of turfs however you want to calculate line of site. I'll give an example
turf_line.Remove(loc)
for(var/turf/T in turf_line)
if(T.opacity) return 0
for(var/atom/movable/M in T.contents)
if(M.opacity) return 0
return 1

As you can see, InSight returns 0 if the object is not in the src's sight, or 1 if it is.

You could modify this to keep track of the sightedness of objects it checks in the for loop on its way to target so that you can just check a list first and get a speed boost so that you can call InSight for every object in view with insignificant processing.
In response to Loduwijk
Loduwijk wrote:
Here's something from one of my own personal libraries that has a bunch of stuff I use a lot or that I think could be useful.

Wow! Amazing stuff!
Thanks, Loduwijk!

But, I didn't get you anything...
Wait! (reaches into an old sock stuffed under the bed). Here:

proc/ray(var/x1,var/y1,var/x2,var/y2,var/z,var/icon/i)

…var/temp
…var/steep = FALSE

…if( (abs(y2 - y1)) > (abs(x2 - x1)) )
……temp = x1; x1 = y1; y1 = temp
……temp = x2; x2 = y2; y2 = temp
……steep = TRUE

…var/deltax = x2 - x1
…var/deltay = abs(y2 - y1)

…var/error = 0
…var/y = y1

…var/ystep
…if(y1 < y2) ystep = 1
…else ystep = -1

…if(x1 > x2)
……for(var/x=x1; x>=x2; x--)

………if(steep)
…………if(!plot(y,x,z,i)) return
………else
……………if(!plot(x,y,z,i)) return

………error -= deltay
………if((error + error) <= deltax)
…………y += ystep
…………error -= deltax

…else
……for(var/x=x1; x<=x2; x++)

………if(steep)
…………if(!plot(y,x,z,i))return
………else
…………if(!plot(x,y,z,i))return

………error += deltay
………if((error + error) >= deltax)
…………y += ystep
…………error -= deltax


(The elipses (…) are intended to be an easy find and replace for tab...something I'm tryin' out but don't know if it works that well)

It's a ray proc that will reliably (so far) draw a line of plotted points from x1,y1 to x2,y2. Based on Bresenham's algorithm, it differs only in that some implementations will flip x1,y1 with x2,y2 and so, the line will sometimes start at the endpoint, whereas this will always begin at the starting coords.
Depending on what you make proc/plot(x,y,z) do, it might be useful to you or anyone who stumbles on it.

Thanks for the help, Loduwijk.
In response to TheMonkeyDidIt
TheMonkeyDidIt wrote:
> ………if(steep)
> …………if(!plot(y,x,z,i)) return
> …………else
> ……………if(!plot(x,y,z,i)) return
>


Are you sure the indentation there is right? From the looks of things, it seems like that else should belong to the first if there, not the second.

(The elipses (…) are intended to be an easy find and replace for tab...something I'm tryin' out but don't know if it works that well)

I found it a bit odd at first, but quickly got used to it. I think we need some symbal like the one you can make show up in dream maker instead of the tab, that double arrow thing. Just a normal "> " would probably work too. I think I've seen some people do that on the forum here before, and since you said part of your reasoning was in easy find/replace, "> " would have the same ease for that.

If I want tabs bad enough in here though, I usually just copy or cut a tab and then hit ctrl+v to tab in the browser.
In response to Loduwijk
Loduwijk wrote:
Are you sure the indentation there is right? From the looks of things, it seems like that else should belong to the first if there, not the second.

Yeppers. Got an error there and now edited to correct.
Thanks!

... "> " would have the same ease for that.

True! The only reason I used an extended like the elipses was to avoid any characters actually used in coding.

If I want tabs bad enough in here though, I usually just copy or cut a tab and then hit ctrl+v to tab in the browser.

Yeah...if that's the worst problem of my day, I'm doing pretty damn well. I do find, tho, that A LOT of my errors (compile or run-time (and I get a lot)) come from copying and pasting from another section I've written.
In response to TheMonkeyDidIt
TheMonkeyDidIt wrote:
(The elipses (…) are intended to be an easy find and replace for tab...something I'm tryin' out but don't know if it works that well)

Please don't do that. It's fairly difficult to read, and now certain people are emulating it in a worse way. Much better is to simply replace the tabs with 2 or 4 spaces each.

Lummox JR
In response to Lummox JR
Lummox JR wrote:
Please don't do that. It's fairly difficult to read, and now certain people are emulating it in a worse way. Much better is to simply replace the tabs with 2 or 4 spaces each.

Lummox JR

Hmmmm...I tried to choose the most unobtrusive symbol I could find, and one that wasn't used in any code. I'm not sure it would be catching on if there wasn't a call for something of this nature besides the spaces.

I find the 2 or 4 spaces to be often more trouble when either doing a find and replace or manually shift-tab/tabbing.

Can you offer a suggestion between the two options?