Page 1 of 1

Pseudo-dynamic Lighting?

Posted: Sat Apr 21, 2012 6:17 am
by BMR
Ok, first off, I know that it's not really dynamic lighting, but as I couldn't think of a better term, I'll stick with "pseudo-dynamic lighting".

(Shame on you BMR, you're an English teacher, you should be able to think of better words...)

Ah, and most of the graphics/tilesets and whatnot have been done already, but there is some tweaking that needs to be done which is pretty obvious with the lighting tilesets.

Anywho, a bit of background information before I get to what I'm trying to accomplish.

I have a sample room, like so:

Image

There are four torches, none of which are lit. When one torch is lit, I WRITE MAP BLOCK it to look like this:

Image

The red line shows the boundary of the tiles that changed.

The second torch would look like this:

Image

Same applies, but the blue line shows the boundary this time.

When both are lit, I get this:

Image

The blue and red boundaries now intersect.

Because the space that each torch changes is always the same, I can map each area to a Zone for each torch or whatever light-source I want. And because the limit for Zones is pretty high, this shouldn't be a problem with larger maps, though I'm keeping maps relatively small to keep computations down.

Now, on to what I want to accomplish.

It is a pain to have to specify which tile goes where with a bunch of READ and WRITE map blocks. I was thinking if it was feasible (I'm pretty sure it's possible, but feasibility is another question) to write a script that figures out, by itself, which tile should go where.

For example, with Zone 01, the script would pretty much, in pseudo code, go:

Code: Select all

LOOK AT TILE X, Y
LOOK AT SURROUNDING TILES
IF( SURROUNDING TILES( NW==FALSE, NN==TRUE, NE==FALSE, etc...) THEN(
WRITE Z BLOCK TO X, Y
)
Or something like that. Because each type of tile is only used in certain places, based on which tiles are either part of or not part of the zone, the script can then figure out which type of tile to use. For example, if a tile in a zone has the following:

NW==FALSE
NN==TRUE
NE==TRUE
WW==FALSE
EE==TRUE
SW==FALSE
SS==TRUE
SE==TRUE

Then I know I need this tile:

Image

Which is only used on the western side of the blobs of light that light-sources generate.

Further, I can have the script check if there are any Zones above or below it that overlap. Each Zone would then have a Global Variable attached to it to check if the light source is either ON or OFF. Because the script checks if other Zones are ON or OFF, it can then figure out how to merge the two. Pretty much all of my light-border tiles have been made and can be connected in any configuration to create any shape, and because when each is used is static, the script can then figure out what the place where and when to do it.


The problem I'm having is, how do I figure out the state of the surrounding tiles without having to use a large and convoluted IF, AND, THEN statement? I've semi-gotten it working, but it's extremely messy and I can't quite figure out a simple and elegant solution, if such even exists that is.

One thing I thought of was creating another script that returns a string, something like: "01101010" which would be a representation of the block above. Problem is, I can't quite figure out how to do this.

Help, anyone? Would be very much appreciated.

Posted: Sat Apr 21, 2012 2:20 pm
by Master K
I don't have a sweet clue.
But I gotta say, those are amazing dungeon graphics.

Posted: Sat Apr 21, 2012 5:11 pm
by Pepsi Ranger
I may be reading too deeply into your post, but it looks to me like you may be overcomplicating things a little. The point of zones is to cut down on the need for excessive write map block commands (among other uses).

Consider this: you can stack up to 15 zones on a single tile. Although zone comparison support is a little weak, there are cheap ways of figuring out where the zones overlap (I've talked to TMC about this and I think he's updated the zone editor slightly to allow basic editing in view mode just so that you can see where another zone's boundaries are located, but it requires at least one active zone tile to work, so it requires experimentation to really use it well). Instead of trying to figure out where tiles overlap, just create a new zone that outlines overlapping areas, and then turn it on if both overlapping zones are active.

It would look something like this:

Code: Select all

script, Adjust Lights, begin

#zone 1 = eastern torch (approximate range x30-x50/y30-y50)
#zone 2 = southern torch (approximate range x20-x35/y40-y55)
#zone 3 = overlapping section where east and south meet (approximate range x30-x35/y40-y50)
#Note: ranges are not exact, but it doesn't matter
#tag 2 = "east switch"
#tag 3 = "south switch"
#tag 4 = "overlapping light"

variable (x,y)
#toggle eastern light on
if (checktag (2)) then(
 for (y, 30, 50) do(
  for (x, 30, 50) do(
   if (read zone (1, x, y)) then(
    write map block (x, y, light tile, ground layer)
   )
  )
 )
)
#toggle eastern light off
if (checktag (2)==false) then(
 for (y, 30, 50) do(
  for (x, 30, 50) do(
   if (read zone (1, x, y)) then(
    write map block (x, y, dark tile, ground layer)
   )
  )
 )
)
#toggle southern light on
if (checktag (3)) then(
 for (y, 40, 55) do(
  for (x, 20, 35) do(
   if (read zone (2, x, y)) then(
    write map block (x, y, light tile, ground layer)
   )
  )
 )
)
#toggle southern light off
if (checktag (3)==false) then(
 for (y, 40, 55) do(
  for (x, 20, 35) do(
   if (read zone (2, x, y)) then(
    write map block (x, y, dark tile, ground layer)
   )
  )
 )
)
#toggle overlap light

if ((checktag (2)), and, (checktag (3))) then(
set tag (4, ON)
)

if ((checktag (2)==false), or, (checktag (3)==false)) then(
set tag (4, OFF)
)

#toggle overlap on

if (checktag (4)) then(
 for (y, 40, 50) do(
  for (x, 30, 35) do(
   if (read zone (3, x, y)) then(
    write map block (x, y, advanced light tile, ground layer)
   )
  )
 )
)
#toggle overlap off
if (checktag (4)==false) then(
 for (y, 40, 50) do(
  for (x, 30, 35) do(
   if (read zone (3, x, y)) then(
    write map block (x, y, dark tile, ground layer)
   )
#this will turn off the edges of valid lights, so you'll want to reset those
   if (checktag (2)) then(
    if (read zone (1, x, y)) then(
     write map block (x, y, light tile, ground layer)
    )
   )
   if (checktag (3)) then(
    if (read zone (2, x, y)) then(
     write map block (x, y, light tile, ground layer)
    )
   )
  )
 )
)
end
You'll notice that the tile search range doesn't have to be exact, since the zone itself will determine whether the specific coordinate is valid for your lighting need. All you have to do is draw the zone, and then use the script to toggle what the zone does -- either turn on or turn off.

Obviously, "light tile" and "dark tile" require the numbers corresponding to the specified tile from your tileset, and the "ground layer" refers to the layer number. "Advanced light tile" refers to the overlapping light tile.

You'll want to make sure to include conditions that reset the edges of your valid single lights if the overlapping light returns false, since the script will turn them all dark initially (which you'll want because you don't want invalid bleed over into the darkened zone).

I have two HamsterSpeak articles about zone usage that can further help you make better use of zones:

Zoned In #1:

http://superwalrusland.com/ohr/issue49/index.html

Zoned In #2:

http://superwalrusland.com/ohr/issue54/index.html

Hope this helps.

Posted: Sat Apr 21, 2012 10:26 pm
by msw188
Yeah, having a separate zone for overlaps might be a bit helpful, but it doesn't change the fact that you will need a rather involved script to do this (Pepsi's is already somewhat involved, and doesn't even include the 'hard' part).

I tried to think of a way to simplify your calculations, but if you can't guarantee any conditions on your lit areas (convexity would be nice), then you really do have to check all of the boundary conditions you're talking about. Putting these conditions into one 'binary string' might make it seem more tidily organized, but it won't really simplify any of the calculations. The only other option might be to have several zones per torch, each zone defining the type of lighting tile that will be used on that tile (this will necessitate the existence of a wholly separate zone for intersections).

The problem with Pepsi's script as written is that it doesn't address the main problem of the difference between boundary-tiles versus interior-tiles within the same zone. That's where most of the complicated computation occurs, it seems to me. Also, if you're planning on having, say, a few hundred of these torches throughout the game, writing the scripts this way feels a bit demoralizing when it feels as though there should be a clever general script that should be able to handle all possible torches and all possible overlaps.

Posted: Sat Apr 21, 2012 11:54 pm
by Mogri
If I were trying this, I would do something like this:

- Mark all tiles within a certain radius of torches
- Fill in all of the marked ground tiles with lighted ground tiles
- For each ground tile adjacent to a lighted tile, figure out which edge tile it should be

The last step is the tricky one. You want to do something like this for each applicable tile:

Code: Select all

variable(surround)
surround := 0
if (tile to the right is lit) then (surround += east wall)
if (tile to the left is lit) then (surround += west wall)
if (tile above is lit) then (surround += north wall)
if (tile below is lit) then (surround += west wall)
write map block(x, y, surround)
You will need to set up your tileset to adjust to this, but it is probably the easiest method.

Posted: Sun Apr 22, 2012 3:07 am
by msw188
Hm, this idea seems promising. For each torch, have one zone for 'interior' tiles and a different zone for 'border' tiles. Then the lighting script could go something like the following:
1. Check through all torches' "interior tile" zones and light them up if the torch is lit (relatively easy to do with one script - see below).
2. Check through all torches' "border tile" zones and IF THE TILE IS NOT ALREADY LIT UP, light it up using Mogri's calculation (again, relatively easy to do with one script - see below).

Key #1 - by lighting up all interiors first, step two doesn't have to worry about which torch was lit in intersecting cases; the IF THE TILE IS ALREADY LIT UP check can be based on the maptile and not on checking for a bunch of different possible zones

Key #2 - If you use NPCs with pre-arranged ID's for your torches, the single script can check the location of the torch and so narrow down the tiles to check for the given zone

Pseudo script illustrating Key #2

Code: Select all

variable(Xmin,Xmax,Ymin,Ymax)
#for torch #1, say, that has NPC ID 101
if(torch1 is lit),then
      begin
Xmin:=npcX(101)--(max general torch radius)
Xmax:=npcX(101)+(max general torch radius)
#similar for y's
for(x going from xMin to xMax), do
 begin
  for(y going from yMin to yMax), do
   begin
    if tile at x,y is in interior zone for torch1, writemap it as being lit up
   end
 end
       end
#REPEAT FOR ALL TORCHES (this can be made one giant for loop running through torch numbers
#you just have to keep a consistent setup from map to map where zone 3, say, ALWAYS
#corresponds to torch #3 which ALWAYS corresponds to NPC 103, and so forth
#then maybe there are no even numbered torches, so even number zones can be the border-zones)

wait(1)

#do the exact same thing, except in the inner most if line:
    if tile at x,y is in border zone for torch1,
    AND, if tile at x,y is not already a lit up maptile,
    then writemap it using Mogri's technique
Does this help? This might not be particularly pretty, but it seems like the most efficient possible solution if you're planning on having such torches in a wide variety of situations.

Posted: Sun Apr 22, 2012 3:17 am
by Gizmog
Could you use step on NPCs and tags? It seems the whole thing could be done very efficiently if each torch turned on a tag which in turn made some npcs visible.

Posted: Sun Apr 22, 2012 2:09 pm
by msw188
Using NPCs would be problematic if this could happen anywhere other NPCs could walk, which seems likely.

That said, if there is no chance of blocking other NPCs, yeah using step-on NPCs for the different lighted tiles is a great idea. Then stack them at intersections, making sure that the NPC who is the lighting when both Torch1 AND Torch2 are lit is "on top" of the NPCs for just Torch1 and just Torch2.

Posted: Sun Apr 22, 2012 2:40 pm
by BMR
Hmm, crikey but this is more complicated than I originally thought it would be... I'll try and throw something together by the end of the week incorporating everyone's suggestions and I'll post the results of what I get then. Thanks everybody!

Posted: Mon Apr 23, 2012 6:24 am
by TMC
BMR wrote:Hmm, crikey but this is more complicated than I originally thought it would be...
It's no worse than what you described in your first post: you already had it mostly worked out. Either create one zone per torch if you want each lit area to have a hand made shape, as you originally planned, or use Moogle's suggestion of just lighting nearby tiles automatically. Then use the idea from Moogle's snippet for calculating the correct tile.

msw's suggestion of having separate interior and exterior zones is an optimisation, but one which I'm confident won't be necessary if you do everything else efficiently enough. More important is that you only update an area around each torch, rather than trying to update the whole map.

Anyway, Moogle's snippet isn't enough, because you've created enough tile variations that you really do need to check the surrounding 8 tiles instead of just the adjacent 4. In the naïve way, that would require 256 tiles. But many combinations are impossible. For example (apparently), for a lit tile to have unlit tiles on both left and right.

Here's the formula which I would try first (80 tiles in total):

Code: Select all

variable(surround, corner)
surround := 0
if (tile to the right is lit) then (surround += east wall)
if (tile to the left is lit) then (surround += west wall)
if (tile above is lit) then (surround += north wall)
if (tile below is lit) then (surround += south wall)

#corner conditions
corner := 0
if (tile to top left is unlit) then (corner := 16)
if (tile to top right is unlit) then (corner := 32)
if (tile to bottom left is unlit) then (corner := 48)
if (tile to bottom right is unlit) then (corner := 64)

write map block(x, y, FIRST TILE + surround + corner, LAYER)
Of those 80, lots of tiles will be duplicate and lots will be impossible. For example, with unlit tiles south and west, lit tiles north and east: use the same tile regardless of the extra corner conditions. Therefore, place a column of 5 identical tiles on your tileset.

But that's just a suggestion.