ID:1572154
 
(See the best response by DarkCampainger.)
First off let me just say that this is my first post, so if I am doing something wrong please let me know (be harsh if you have to or else I might never learn).

ps. for those not interested in a life story skip the next paragraph

Okay, so I have been coding with dream maker for a while now, but never did anything really large scale until now. The only problem is I think I bit off a little more than I can chew. I have always been searching the forums constantly for answers to all my coding problems and have found it to be incredibly useful, but for this one I am just completely stumped. For all of you still reading thank you and here's my problem:

I am aware that when you place one turf on top of another in the map editor, there is not actually two turfs during runtime. From what I have read only the topmost layer is actually there and all of the previous layers are stored within its underlays list. That's all well and good but the problem that I am having now is accessing those underlays. For all of you questioning why I might be doing that it is because I am trying to generate my own minimap, and am trying to decide between whether or not to base it off of Caution or Zaltron's tutorial, but no matter which one I choose this will still be a problem. I tried placing their libraries within my game and it basically whites out whatever was behind the upmost turf and my only assumption is that its because (in cautions tutorial), it is only grabbing the icon of the turf and not its underlays as well.

At first I jumped right in and tried looping through the turfs underlays and blending them to the new icon I had created to represent the turf on the minimap, but that didn't do anything. Since then I have tried overlays and even contents to no avail.

Finally I tried some debugging code to see what the problem was and I used the following:
verb
checkTerrain()
var/turf/t = locate(src.x, src.y, src.z)
var/icon/saveTurf = icon(t.icon, t.icon_state)
for(var/icon/i in t.overlays)
usr << "Let their be overlays"
saveTurf.Blend(i, ICON_OVERLAY)
for(var/icon/i in t.underlays)
usr << "Let their be underlays"
saveTurf.Blend(i, ICON_OVERLAY)
for(var/atom/i in t.contents)
usr << "Let their be content of Type: [i.type]"
usr << browse_rsc(saveTurf, "Turfed")
usr << browse("<img src=Turfed>", "window=test")


With the above verb I would simply walk over the turf I wanted to examine and a window would appear displaying the turfs icon

I placed the messages to the user so that I could see if the loops were being run through at all, and their in lies the problem. Whenever I tried to run it, the loops weren't being entered at all. Therefore I assumed that their must not be anything in the underlays (even when I tried to stand over turfs that I had more placed more than one turf on in the map editor).

Stranger than that I tried this next code:
verb
checkTerrain()
var/turf/t = locate(src.x, src.y, src.z)
var/icon/saveTurf = icon(t.icon, t.icon_state)
for(var/i in t.overlays)
usr << "Let their be overlays"
//saveTurf.Blend(i, ICON_OVERLAY)
for(var/i in t.underlays)
usr << "Let their be underlays"
//saveTurf.Blend(i, ICON_OVERLAY)
for(var/atom/i in t.contents)
usr << "Let their be content of Type: [i.type]"
usr << browse_rsc(saveTurf, "Turfed")
usr << browse("<img src=Turfed>", "window=test")


the only difference is that instead of for(var/icon/i in whatever)
its for(var/i in whatever). Basically I am declaring a basic variable rather than an icon one. The results that I found were that the underlays loop was being entered suddenly. So now I am more confused than ever. Any thoughts?

Also if I there is a better way to generate minimaps and all I am doing here is just reinventing the wheel, please let me know.

Problem description: In a nutshell, how do I access the turfs underneath the top most turf that have been placed in the map using the map editor.

Thank you all in advance
According to documentation, the underlays and overlays lists are "special" and individual elements cannot be accessed directly. So, how do I overcome this limitation?

atom
var/list
my_underlays = new
my_overlays = new

proc
add_overlay(x)
my_overlays += x
update_overlays()

add_underlay(x)
my_underlays += x
update_underlays()

rem_overlay(x)
my_overlays -= x
update_overlays()

rem_underlay(x)
my_underlays -= x
update_underlays()

update_overlays(x)
overlays = my_overlays

update_underlays(x)
underlays = my_underlays


Keep in mind, since we are now using normal lists instead of the built in special ones, we can access individual elements. Now you can do with this list anything you could do with the normal underlays and overlays lists. Just remember to "update" the internal underlays and overlays lists when you are done modifying the wrapper version.

Feel free to expand on it and make it more robust/secure/clean/whatever.
In response to Koshigia
Okay, that makes sense. The under and overlays lists are "special" and therefore cannot be accessed one element at a time. Thank you very much for that, but I still have one problem and that is that I don't have a list of the underlays prior.

Let me rephrase that. I am trying to access the underlays of the turf generated by the map editor, so I don't know what the underlays are as I did not put them there myself, it was made by the map editor after I placed one turf on top of another.

Now I have taken that concept you gave me and tried a few different things by modifying my debugging code into the following:
verb
checkTerrain()
var/turf/t = locate(src.x, src.y, src.z)
var/icon/saveTurf = icon(t.icon, t.icon_state)
var/list/l = t.underlays
for(var/i in l)
usr << "Let their be underlays"

The above worked correctly in that the list was filled but when I tried this:

verb
checkTerrain()
var/turf/t = locate(src.x, src.y, src.z)
var/icon/saveTurf = icon(t.icon, t.icon_state)
var/list/icon/l = t.underlays
for(var/icon/i in l)
usr << "Let their be underlays"


or even this

verb
checkTerrain()
var/turf/t = locate(src.x, src.y, src.z)
var/icon/saveTurf = icon(t.icon, t.icon_state)
var/list/l = t.underlays
for(var/icon/i in l)
usr << "Let their be underlays"


I cannot find an icon in the list.
So I tried turning whatever it was I am finding in the list into an icon as follows:
verb
checkTerrain()
var/turf/t = locate(src.x, src.y, src.z)
var/icon/saveTurf = icon(t.icon, t.icon_state)
var/list/l = t.underlays
for(var/i in l)
usr << "Let their be underlays"
var/icon/pic = icon(i)
saveTurf.Blend(pic, ICON_OVERLAY)
usr << browse_rsc(saveTurf, "Turfed")
usr << browse("<img src=Turfed>", "window=test")

In the above it enters the loop after storing whatever it found in "i", but errors out when I try to turn it into an icon.

With that being the case I am at a loss as to how to get the icon out of the turfs underlays, after it was generated by the map editor. As an additional note, if it helps, I don't need to take the icons out of the underlays element by element. I just need an icon representing the entire underlayer, whether I have to make it piece by piece myself or take it as a whole. The reason for this is that if I wanted to make an image of the turf as is, I would only be able to grab the icon of the surface most turf and not anything beneath it.

Thank you once again for your response, it was very enlightening.
From this point, I am unsure where to go. It was precisely this reason why I turned to in-game map editing/saving/loading. There's a gap where what you can do from the map editor doesn't quite line up with what you can do during run-time.

My suggestion would be to avoid using the underlays/overlays via map-generation until runtime. An alternative that doesn't completely rid of the map-maker would be to give a variable that is read at runtime to generate the overlays/underlays dynamically such as with the first example I provided. However, that could be tedious.

There's still a chance that someone else knows more about it than me, however.
In response to Koshigia
Hmmm, that's what I was afraid of. So it seems unless someone else knows different, my only options are to either construct an icon complete with for example, both the "flowers" and the "grass", or to place nodes from which I can spawn them at run time, or have the New() proc somehow generate a list of underlays for the turf at runtime... How annoying. Thank you once again, you've been of great help, I think I will hold off for a bit first just to see if anyone else has anything they wish to add before I dive into the endeavour.
another option would be to make flowers and such into objs.

If you still wish flowers to be turfs, then another option still would be to create a special type of object such as...

obj/remap
var/my_remap_type

New()
..()
//handle placing the new turf here with underlay/overlay handling. Be sure to use overlay/underlay wrapper system.
del(src) //delete the temporary remap object after the turfs are placed.


flower
my_remap_type = /turf/flower


This would create a bit of CPU overhead at startup, but would avoid tedious work while allowing you use of the map maker WHILE putting turfs in proper working order for the overlay/underlay wrapper
In response to Koshigia
That could work. You said that it might cause some CPU overhead at start up though, would that just be a startup problem or would it affect the CPU overhead for the entire game? Basically I'm wondering if having these as objects instead of turfs in abundance would cause lag.
I don't imagine much, and it will only be at startup. The objects are only going to exist long enough to alter the turfs/underlays/etc, then should delete themselves as they are no longer necessary
Best response
underlays and overlays are stored as a special internal "appearance" type. That's why when you tried filtering the for() loop with the /icon type, you didn't find anything.

You can't access the appearance type directly, but you can get at it's variables with the colon operator or by pretending it's a /image. Here's an example proc that will dump the contents of an overlay/underlay list by using the colon operator:
mob/verb/Test()
src << PrintOverlays(src.overlays)

proc/PrintOverlays(list/overlays)
var/output = "<hr>"
output += "\noverlays.len = [overlays.len]"
var/i = 1
for(var/a in overlays)
output += "\noverlays\[[i]] = {"
if(!a)
output += "\n <null>"
else
output += "\n \icon[a]"
output += "\n icon = '[a:icon]' (\ref[a:icon])"
output += "\n icon_state = \"[a:icon_state]\""
output += "\n dir = [a:dir]"
output += "\n layer = [a:layer]"
output += "\n suffix = \"[a:suffix]\""
output += "\n name = \"[a:name]\""
output += "\n text = \"[a:text]\""
output += "\n pixel_x = [a:pixel_x]"
output += "\n pixel_y = [a:pixel_y]"
output += "\n pixel_z = [a:pixel_z]"
output += "\n maptext = \"[a:maptext]\""
output += "\n maptext_width = [a:maptext_width]"
output += "\n maptext_height = [a:maptext_height]"
output += "\n blend_mode = [a:blend_mode]"
output += "\n alpha = [a:alpha]"
output += "\n color = \"[a:color]\""
output += "\n override = [a:override]"
var/matrix/m = a:transform
if(m)
if(m.a == 1 && m.d == 0 && m.b == 0 && m.e == 1 && m.c == 0 && m.f == 0)
output += "\n transform = [m] (\ref[m]) (identity)"
else
output += "\n transform = [m] (\ref[m])"
output += "\n \t\[ \[ [m.a]\t[m.d]\t0 \]"
output += "\n \t \[ [m.b]\t[m.e]\t0 \]"
output += "\n \t \[ [m.c]\t[m.f]\t1 \] \]"
output += "\n}"
i++
output += "\n<hr>"

return output


You might also be interested in this library:
GetFlatIcon
Thanks DC!
In response to DarkCampainger
Thanks DC, you are a life saver. I was able to use the method that you provided to reconstruct icons with their underlays. Although with that being said I seem to have encountered another problem. I am not sure if I should put this under another thread or not so if that would be more appropriate please let me know.

Anyway's, as I have stated before this all started when I began trying to generate a minimap using libraries by Caution and Zaltron. Well I decided that Cautions might be more appropriate for my situation, the only problem is that I am having difficulties when it comes to big icons. Here's what I have so far:

mob/proc
mapgrab(list/l,scale=10) // list/l is something similar to view(), range(), oview(), or orange(); if there is no list, then it will be assumed
// that we are checking for the entire world on the user's z-level.
// scale is the scale of the map. This can be anything from 1 to anything;
// However, scale should be noted that it creates a 32x32 icon multiplied by width and height, and then each by scale.
// So, a scale of 1 would create a 32x32 icon, 10 would be 320x320, etc.
var/icon/newIcon = new ('icon_blank.dmi') // create the blank icon. this icon file is necessary for the program to work.
if(l) // if there is a list
var/minx=src.x // define the minimum x, y and maximum x, y
var/miny=src.y
var/maxx=src.x
var/maxy=src.y
for(var/turf/map in l)
if(map.x <= minx)
minx = map.x //if the turfs x is lower than the minx, make it the new minx (same concept applies for the rest of this section
else
if(map.x >= maxx)
maxx = map.x
if(map.y <= miny)
miny = map.y
else
if(map.y >= maxy)
maxy = map.y
newIcon.Scale((maxx-minx+1)*scale,(maxy-miny+1)*scale) // scale it accordingly; basically, find the width/height and add 1
//(in case the width would equal 0)
for(var/turf/map in l) // check every turf in view(), range(), etc.
var/icon/tempIcon = new (icon(map.icon,map.icon_state)) // create a temporary icon of the atom
for(var/t in map.underlays)
var/icon/tIcon = icon(t:icon, t:icon_state)
tempIcon.Blend(tIcon, ICON_UNDERLAY)
tempIcon.Scale((tempIcon.Width()/32)*scale,(tempIcon.Height()/32)*scale) // Scale it accordingly // scale it
newIcon.Blend(tempIcon,ICON_OVERLAY,(((map.x*scale)-(minx*scale)+1)),(((map.y*scale)-(miny*scale)+1))) // Blend our blank icon with the information from
for(var/atom/atomMap in map)
var/icon/tempIconAtom = new (icon(atomMap.icon,atomMap.icon_state,dir=atomMap.dir)) // create a temporary icon of the atom
tempIconAtom.Scale(scale,scale) // Scale it accordingly
newIcon.Blend(tempIconAtom,ICON_OVERLAY,(((map.x*scale)-(minx*scale)+1)),(((map.y*scale)-(miny*scale)+1)))
else
newIcon.Scale(world.maxx*scale,world.maxy*scale)
for(var/turf/map in block(locate(1,1,src.z),locate(world.maxx,world.maxy,src.z)))
var/icon/tempIcon = new (icon(map.icon,map.icon_state)) // create a temporary icon of the atom
for(var/t in map.underlays)
tempIcon.Blend(t:icon, ICON_UNDERLAY)
tempIcon.Scale(scale,scale) // Scale it accordingly
newIcon.Blend(tempIcon,ICON_OVERLAY,((map.x*scale)-(1*scale)+1),((map.y*scale)-(1*scale)+1))
for(var/atom/atomMap in map)
var/icon/tempIconAtom = new (icon(atomMap.icon,atomMap.icon_state,dir=atomMap.dir)) // create a temporary icon of the atom
tempIconAtom.Scale(scale,scale) // Scale it accordingly
newIcon.Blend(tempIconAtom,ICON_OVERLAY,((map.x*scale)-(1*scale)+1),((map.y*scale)-(1*scale)+1))
return newIcon


Cautions original library can be downloaded from here: Caution's Create a Minimap

I have incorporated a loop that checks for and blends the turfs underlays prior to adding it to the map and that works terrific so thank you once again. But the problem that I am having specifically is with my roads. Here is a screenshot of the output:

Minimap Screen Shot

This problem began when I incorporated icon.Width() and icon.Height() in the scaling of the icons. Perhaps a bit more information would help:

My world icon size is 32x32
The scale is 10 when the proc is called
the list being placed in is a view(100) of the map
The icon size for my roads are 128x128 - if it helps here is a screenshot of their icon file:
Roads Icon File
My code for the scaling of the icons as you can see takes and divides the width and height of the icon by 32, thus giving me how many times bigger the icons are compared to normal icons. In this case it would give me 4. It works for the trees on the map but just not the roads, it seems to me like they are almost interfering with one another. If it helps more here is what the map looks like without altering the scale so you can see where I have placed the road turfs:
Minimap Without Icon Scaling

Thank you once again for all of your help, and just let me know if I I am placing this question in the wrong area, and sorry in advanced if my posts are too lengthy.
No worries, your post is fine.

The issue you're probably running into is that your road turfs still actually only take up a single 32x32 tile, despite having a 128x128 icon. The road turf actually exists at the bottom-left of the icon, and then there are 15 other turfs that are just covered up by the road icon. However, your loop is still going through those 15 "covered up" turfs, and drawing their green icon over your road.

The simplest solution to this problem, assuming that you don't use negative pixel offsets, is to just iterate through the turfs from the top-right to the bottom-left.

To do that, you'll have to switch to using block() for generating your turf list (view() orders turfs by distance from the center, whereas block() orders them bottom-left to top-right), and then just iterate through it backwards.

// Replace view(100) with block() so the turfs are ordered by their coordinates
var/mapRange = 100
var/turf/bottomLeft = locate(max(1, src.x - mapRange), max(1, src.y - mapRange), src.z)
var/turf/topRight = locate(min(world.maxx, src.x + mapRange), min(world.maxy, src.y + mapRange), src.z)
mapgrab(block(bottomLeft, topRight))


mob/proc
mapgrab(list/l,scale=10)

// ...

// Loop through the list in reverse
for(var/i = l.len to 1)
var/turf/map = l[i]

// ...


If you need negative pixel offsets... then things start to get interesting :)

Even after that, you'll still have an issue on the left and bottom edges of the minimap, because the road turf with the icon to cover that area is outside of the block. The easiest way to work around that is to extend your minimap area by your "largest" icon overlap (ie you would need 3 extra tiles on the left and bottom side to always include the turf with the road icon), and then just crop it down afterwards.
In response to DarkCampainger
DC you've done it again! Thank you sooo much. Here I am completely stumped as to what's going on but when you explain it, it just makes so much sense. Thanks again for everything your idea of iterating through a block backwards worked like a charm. Also, although I am not using negative pixel offsets, thank you because it was very enlightening to see how you could accommodate for it. This is my first thread on Byond and I've already learnt so much.