Dynamic Lighting

by Forum_account
Dynamic Lighting
An easy way to add dynamic lighting to your game.
The day-night demo doesn't work if you toggle the mobs light- is this intentional?
FaintlySpoken wrote:
The day-night demo doesn't work if you toggle the mobs light- is this intentional?

That was not intentional. The reason it causes it to stop working is because the mob's light source doesn't just illuminate tiles, it forces nearby tiles to update their lighting as the time of day changes.

In the next update I'll take that verb out. If you want to see what it looks like without the mob casting any light, you can use the "decrease intensity" verb.
@forum_account in my game i am using a really big map (1000,1000) so i wanted to see if it would lag if i made the demo map big and it started to lag a lot is there a way to fix this so it doesn't lag(ps i used the shadow demo)
To initialize lighting, an object is created on every turf. You'd need a million objects to cover that map. It'll take a significant amount of time to create them all at once. You could find a way to split the map into "zones" and initialize lighting for each zone when a player gets near it. The best solution is to just use a smaller map (or multiple maps).
Cool lighting system. It does have one problem, which is that it may cause problems with client.eye changes, since only mobs update the surrounding tiles. It's nicer-looking than traditional DAL though.

I tried the fix for standard opacity, and it works well, but causes an odd sort of bias for north/east shadows as opposed to south/west ones. I may be able to fix that if I understand it a bit more.
Great stuff, but I'm having issues with implementing shading. For some reason, it seems that even if a mob isn't opaque, they cannot be the source of light. Even in your demo, toggling the light on/off does nothing.

I've tried making the mob non-opaque to solve it, but it still causes the same issue. Is this an intended effect?

I'm trying to make a spooky game where you walk around in caves and have a very limited view distance which is based on lighting, much like the original silent hill games. Would this be possible with your system?
Bravo1 wrote:
I'm trying to make a spooky game where you walk around in caves and have a very limited view distance which is based on lighting, much like the original silent hill games. Would this be possible with your system?

Absolutely.

I've tried making the mob non-opaque to solve it, but it still causes the same issue. Is this an intended effect?

No, that is a bug. It'll be fixed in the next update but in the meantime you can add this code at line 82 in light-source.dm:

loc = owner.loc

The surrounding bit of code should now be:

    proc
// this used to be called be an infinite loop that was local to
// the light object, but now there is a single infinite loop in
// the global lighting object that calls this proc.
loop()

// if the light is mobile (if it was attached to an atom of
// type /atom/movable), check to see if the owner has moved
if(mobile)
loc = owner.loc
// compute the owner's coordinates
var/opx = owner.x
var/opy = owner.y
Not sure if you're still checking on this thread, but I'm having some problems with the library. Everything works fine, but all objects with opacity are halfway lit, although no light source is nearby. Wat dis be?
In response to Shaoni
Shaoni wrote:
Not sure if you're still checking on this thread, but I'm having some problems with the library. Everything works fine, but all objects with opacity are halfway lit, although no light source is nearby. Wat dis be?

If you're using opacity, you can put this code in your project to shift the overlays back:

shading
pixel_x = 0
pixel_y = 0

light
lum(atom/a)
if(!radius)
return 0

var/d = (__x - a.x - 0.5) * (__x - a.x - 0.5) + (__y - a.y - 0.5) * (__y - a.y - 0.5)

if(d > radius_squared)
return 0

return cos(90 * sqrt(d) / radius) * intensity + ambient

The reason this is happening is because the overlays are offset 16 pixels. The illumination values are computed at the center of each turf (because it's easier to picture it that way), so the overlay to create the darkness has to have its corners at the centers of turfs, like this:

+---+---+
| | |<--- turfs
+---+---+
| | |
+---+---+

+---+---+
| +---+ |
+-| |<--- shadow overlay
| +---+ |
+---+---+

The code I provided shifts the overlays back and adjusts the lighting calculations so it still looks right.
In response to Forum_account
What's the difference between the shifted and non-shifted calculations, performance-and-appearance-wise? I'm not sure why you didn't do this to begin with, because the way it currently is makes opacity look horrible. (I mean, BYOND's opacity normally looks pretty bad, but this just adds strips of light too)
With the shifted objects the illumination value for a turf is the amount of light at the turf's center. Conceptually this makes more sense. You can adjust the calculation if you want to in-shift the overlays. Visually I'm not sure what difference it makes.
There actually is a difference using the non-shifted /shading objects. Now the center of darkness is at the top-right of a turf instead of the center of the turf, so you can light up the south and west edges of a lum=0 tile but not its north or east edges. That's a problem I think that lies in the icons, which would need to be re-generated in a different way.
You can fix the light-strips by setting the mob.sight |= SEE_PIXELS flag.

Also, Forum_account, you could try using this snippet to eliminate the need for squares to find the hypotenuse of a triangle. It's a quite accurate linear approximation and will work flawlessy for your needs.

proc/lum(atom/a)
if(!radius) return 0

var/dx = abs(__x - a.x)
var/dy = abs(__y - a.y)

var/dist
if(dx >= dy) dist = (0.934 * dx) + (0.427 * dy)
else dist = (0.934 * dy) + (0.427 * dx)

// if the turf is outside the radius the light doesn't illuminate it
if(dist > radius) return 0

// this creates a circle of light that non-linearly transitions between
// the value of the intensity var and zero.
return cos(90 * dist / radius) * intensity
Another few minor optimisations you could do are replacing
for(var/shading/s in changed)


with

for(var/i=1,i<=changed.len,i++)
var/shading/s = changed[i]


this is because for(var/type/A in changed) will perform a type-check on every element of the list. We know that all elements are of that type so this is not necessary, especially if we stop shading objects ever being deleted. If shading objects are deletable you could put an if(s == null){changed.Cut(i,i+1);} in there to prevent errors.
You might want to add a sanity check in the light.loop() to make sure the owner exists before you read values from its variables.
It looks like either the light generation or the shade placement can't do anything over 8 states. I'm getting "flipped edges" in most cases...

http://i.imgur.com/Rz3r4.png

Edit: this turned out to be my fault.
In response to Carnwennan
Carnwennan wrote:
You can fix the light-strips by setting the mob.sight |= SEE_PIXELS flag.

Also, Forum_account, you could try using this snippet to eliminate the need for squares to find the hypotenuse of a triangle. It's a quite accurate linear approximation and will work flawlessy for your needs.

> proc/lum(atom/a)
> if(!radius) return 0
>
> var/dx = abs(__x - a.x)
> var/dy = abs(__y - a.y)
>
> var/dist
> if(dx >= dy) dist = (0.934 * dx) + (0.427 * dy)
> else dist = (0.934 * dy) + (0.427 * dx)
>
> // if the turf is outside the radius the light doesn't illuminate it
> if(dist > radius) return 0
>
> // this creates a circle of light that non-linearly transitions between
> // the value of the intensity var and zero.
> return cos(90 * dist / radius) * intensity
>
>


This doesn't scale with different icon-sizes other than 32.
Also, your lights don't handle what should happen when they are deleted. I suggest:

light
Del()
off()
apply()
..()
Also, is this supposed to happen?

In the opacity demo, you use this to accomplish shadow casting:

effect() //original opacity
var/list/L = list()
for(var/shading/s in range(radius, src))
if(!isnull(L[s])) continue
if(s.x == x && s.y == y)
L[s] = lum(s)
continue
var/dx = (s.x - x)
var/dy = (s.y - y)
var/d = sqrt(dx * dx + dy * dy)
if(d > 0)
dx /= d
dy /= d
var/tx = x + dx + 0.5
var/ty = y + dy + 0.5
for(var/i = 1 to radius)
var/turf/t = locate(round(tx), round(ty), z)
if(!t) break
if(t.opaque) break
if(t.shading == s) break
if(!L[t.shading]) L[t.shading] = lum(t.shading)
tx += dx
ty += dy
return L


Unfortunately, if a turf gets "deleted" (turfs don't really get deleted, they just morph into another turf), the shading for that specific turf goes whacky...

I've tried just about everything I can think of and I can't fix it!

stale light 1


stale light 2


stale light 3


weird border

Also, as seen above, the opaque effect does something weird at the edge of the map.
Page: 1 2 3