Applies to:Dream Maker
Status: Open

Issue hasn't been assigned a status value.
Blahh Another one. Like my Last post its about the Maps and stuff. Right now its taking a while to map because i add the edges or the sides to the turfs. So i was wondering, wouldn't it be a good thing if like you can code .dmi s' as Tile Sets and such so that when you place down the main turf it creates the other stuffs for you. I'm not sure if this is possible but i'm talking kind of like in Rpg Maker Vx.
It would be great to have some way to make mapping edges less time consuming and tedious, but I don't think there is a good way BYOND can do this. I think there are some programming options developers can use, but I'm not really familiar with them.

Would be nice though. It's the one thing I hate the most about mapping.
Are you talking about built in auto-tile? If so, I offered Tom money to put this in officially supported and got no reply.
Ah, That's a bummer. It'd be really nice if they added it.
It's entirely possible to do this with soft-code, in fact, it's more reliable if you do it with soft-code; that way it's designed exactly how you need it to be designed.
Yeah, about a month after sending that email I had a working version that was very fast. It's not too hard to do, albeit a slight learning curve for new programmers though. It would be nice if this was built into byond in such a way that it worked in the map editor, though as opposed to calculating on world start up.
Would you like to request a unique system in autotile own editor, which worked equally to RPG MAKER XP, leaving the edges always beautiful, the maps become more elegant and armoniosos, as the photo below.

Autotiles images are in it, but the editors make beauties.

Here's some relevant code for super fast auto-join / auto-tile system:

It works with 47 state joins, which is outlined in Lummox JR's article here: LummoxJR?command=view_post&post=36068

This is the number of states that both RPG Maker VX and XP use. If you really really want the RPG Maker XP style of autojoins, you'll need to write or obtain a utility that knows the internal cutting algorithm because Lummox JR's Icon Cutter doesn't quite support it fully (it can only cut up VX style after you've put it into a specific 5 state format).
Indeed, the way the tiles are done in that image are slightly different from what IconCutter would do. Specifically, the left image shows a "0" state that seems to only apply to closed ends, that is when two outer corners are next to each other, or to itself. It uses longer corners for all other cases. I actually rather like the idea of using longer corners, although these particular ones have too square a look.

IconCutter's algorithm is very simple: It identifies each corner of the icon in question as an outer corner, inner corner, horizontal or vertical edge, or fully connected. Then it takes the 1/4 icon corner from the source icon that corresponds to that shape. In this way it can work with a minimal number of icons: For 47-state joining, you only need the states for 0 (all outer), 17 (north/south), 68 (east/west), 85 (all inner), and 255 (all connected). For 161-state joins, it needs an additional 187 (north/south, with connected corners) and 238 (east/west with connected corners).

The icon in the example image has the following states, in reading order: 0, -, 85, 28, 124, 112, 31, 255, 241, 7, 199, 193. The - state is because the tile that's entirely grass would belong to a different tile type besides the water; it's only useful as a frame of reference here, and would not be included in a cutter.

One thing I'd be willing to consider based on this is a more advanced version of IconCutter. For instance, given the tileset represented here, much more aesthetically pleasing cuts could be made. For instance, state 20 is an outer corner in the northwest, and an inner in the southeast; it could take three sections of state 28, and only one of state 85. Additionally, since this tileset has no 17 or 68 intrinsically, those could be built based on the edges given here (31 and 241 to build 17, 124 and 199 to build 68).

Anyone can build this, so I'd encourage others to try. Here's the gist of the algorithm: Analyze the states in the source based on their state numbers. (For simplicity, let's assume that the left image has been broken up into the states I mentioned, leaving out the all-grass tile.) For a given join mode to be possible, each corner has to have at least one icon having each possible corner type. (47-state joins have 5 corner types; 16-state have 4; 161-state have 7.) Once that's been established, the remaining states can be built by looking at the corner types involved, taking a best-match approach. The source icon that has the most consecutive matches (in clockwise or counterclockwise) for corner types is chosen, and those matching corners are taken. The remaining corners are chosen the same way. So state 20 is built by looking at the source and noticing 28 is the best match. State 28 is OHCV, from northwest clockwise, and state 20 is OHIV; with wraparound, the OH-V pattern is common to both, so state 28's northwest, northeast, and southwest corners are all copied. The remaining southeast corner is taken from the best match, state 85 (IIII).
Because we're talking about actually doing something now that lummox has replied, here's my contribution on the subject...

This shows you how to cut up the XP style joins and turn them into a useable icon (supports animation too). I spent a really long time getting this to work / figuring it out. The code is pretty bad but who cares when its for a utility that is outputting a single icon once ever. The code uses some common icon functions I've written as well:

Written by: FIREking

#define TRANSPARENT_PIXEL "#357272"

proc/xGetPixel(x, y)
//uses standard coordinate system instead of BYOND's coordinate system
return src.GetPixel(x, src.Height() + 1 - y)

proc/SetPixel(pixel, x, y)
//wrapper function
src.DrawBox(pixel, x, y)

proc/xSetPixel(pixel, x, y)
//uses standard coordinate system instead of BYOND's coordinate system
src.DrawBox(pixel, x, src.Height() + 1 - y)

proc/tileCrop(x, y, w, h, unit = 32)
//allows you to crop the icon using tile units instead of pixel units
Crop(((x - 1) * unit) + 1, ((y - 1) * unit) + 1, (((x - 1) * unit) + (w * unit)) + 1, (((y - 1) * unit) + (h * unit)) + 1)

proc/DrawTo(icon/i, sx, sy, dx, dy, w, h)
for(var/ix = sx to sx + w step 1)
for(var/iy = sy to sy + h step 1)
var/pixel = src.GetPixel(ix, iy)
i.SetPixel(pixel, ix - sx + dx, iy - sy + dy)

//save the icon to disk
//if no path is supplied, a dialog will ask for filename and where to save
//otherwise, save to path
usr.icon = src
CRASH("Invalid usr in icon.Save()")


fcopy(file(usr.icon), path)
usr << ftp(file(usr.icon), "untitled.dmi")

proc/newicon(w, h)
//initializes a new icon of any size
//requires blank.dmi that contains a blank unlabled 32x32 icon_state
var/icon/ic = icon('blank.dmi')
ic.Scale(w, h)
return ic

proc/merge_icon(icon/a, icon/b)
//returns the result of merging a and b
var/icon/ic = new(a)
var/icon/m = icon(b, moving = 1)
var/icon/n = icon(b, moving = 0)
for(var/s in icon_states(m))
if(s != "")
ic.Insert(icon(m, s), s, moving = 1)
for(var/s in icon_states(n))
if(s != "")
ic.Insert(icon(n, s), s, moving = 0)
return ic

//removes any icon states that are not labeled
var/icon/ic = new
var/icon/m = icon(a, moving = 1)
var/icon/n = icon(a, moving = 0)
for(var/s in icon_states(m))
if(s != "")
ic.Insert(icon(m, s), s, moving = 1)
for(var/s in icon_states(n))
if(s != "")
ic.Insert(icon(n, s), s, moving = 0)
return ic

XP style
Written by: FIREking

var/AUTO_INDEX = list(
list(27,28,33,34), list(5,28,33,34), list(27,6,33,34), list(5,6,33,34),
list(27,28,33,12), list(5,28,33,12), list(27,6,33,12), list(5,6,33,12),
list(27,28,11,34), list(5,28,11,34), list(27,6,11,34), list(5,6,11,34),
list(27,28,11,12), list(5,28,11,12), list(27,6,11,12), list(5,6,11,12),
list(25,26,31,32), list(25,6,31,32), list(25,26,31,12), list(25,6,31,12),
list(15,16,21,22), list(15,16,21,12), list(15,16,11,22), list(15,16,11,12),
list(29,30,35,36), list(29,30,11,36), list(5,30,35,36), list(5,30,11,36),
list(39,40,45,46), list(5,40,45,46), list(39,6,45,46), list(5,6,45,46),
list(25,30,31,36), list(15,16,45,46), list(13,14,19,20), list(13,14,19,12),
list(17,18,23,24), list(17,18,11,24), list(41,42,47,48), list(5,42,47,48),
list(37,38,43,44), list(37,6,43,44), list(13,18,19,24), list(13,14,43,44),
list(37,42,43,48), list(17,18,47,48), list(13,18,43,48), list(13,18,43,48)

var/list/state_conversion = list(
"47" = 0,
"46" = 0,
"45" = 64,
"44" = 1,
"43" = 4,
"42" = 16,
"41" = 5,
"40" = 7,
"39" = 65,
"38" = 193,
"37" = 80,
"36" = 112,
"35" = 20,
"34" = 28,
"33" = 68,
"32" = 17,
"31" = 69,
"30" = 197,
"29" = 71,
"28" = 199,
"27" = 81,
"26" = 113,
"25" = 209,
"24" = 241,
"23" = 84,
"22" = 92,
"21" = 116,
"20" = 124,
"19" = 21,
"18" = 23,
"17" = 29,
"16" = 31,
"15" = 85,
"14" = 213,
"13" = 87,
"12" = 215,
"11" = 93,
"10" = 221,
"9" = 95,
"8" = 223,
"7" = 117,
"6" = 245,
"5" = 119,
"4" = 247,
"3" = 125,
"2" = 253,
"1" = 127,
"0" = 255)

return "[state_conversion["[index]"]]"

proc/format_autotile_xp(icon/bitmap, transpixel)
//takes a 96x128 source graphic and converts it to a 47 state icon for use with autojoin

if(!isicon(bitmap)) CRASH("icon/bitmap is not an icon!")

var/icon/master = newicon(32, 32)

for(var/frame = 0 to bitmap.Width()-1 step 96)
var/icon/template = newicon(256, 192)
for(var/i = 0; i < 6; i++)
for(var/j = 0; j < 8; j++)

for(var/e in AUTO_INDEX[(8*i+j) + 1]) //grab the four block positions for this tile
var/number = e - 1

var/x = 16 * round(number % 6)
var/y = 16 * round(number / 6)

var/px = round(32 * j + x % 32)
var/py = round(32 * i + y % 32)

//print the tile
for(var/ix = x; ix < x + 16; ix++)
for(var/iy = y; iy < y + 16; iy++)

var/pixel = bitmap.xGetPixel(ix + frame + 1, iy + 1)
if(!transpixel || (transpixel && pixel != transpixel))
template.xSetPixel(pixel, px + (ix - x) + 1, py + (iy - y) + 1)

var/index = 8*i+j
var/icon/tile = newicon(32, 32)
var/sx = 32 * round(index % 8)
var/sy = 32 * round(index / 8)

//print each state from template into a separate tile icon, insert that icon state into master icon
for(var/ix = sx; ix < sx + 32; ix++)
for(var/iy = sy; iy < sy + 32; iy++)
var/pixel = template.xGetPixel(ix + 1, iy + 1)
tile.xSetPixel(pixel, ix - sx + 1, iy - sy + 1)

master.Insert(tile, get_ljr_state(index), SOUTH, (frame / 96) + 1, 0, 2)

if(index == 47)
master.Insert(tile, "")

return master
Seems like your state conversion could be done as a simpler non-associative list of numbers, just using state_conversion[index+1] to grab the state.

As far as setting pixels, you're probably way better off just making a copy of the source icon, using DrawBox() to null out the corners you don't want, and blending it into your result icon state. It's faster and vastly simplifies the code.
In response to Lummox JR
I didn't think of that (for the numbers), and if that tidbit about DrawBox was listed in the documentation (or perhaps made more clear), I would have totally have done that. Thanks for the response!

why. why would you even need this. it's so easy as is even i can do it.
In response to Vrocaan
The discussion is (is supposed to be) about having autojoin built-in to the map-editor.

And if that was implemented, what type of state joins should be supported? if so, which ones, and how? Questions the BYOND developers would have to answer carefully...
It's a neat idea, and would improve mapping for many people, but I'm not sure how well it could be implemented on a built-in level.

For instance: You'd need vars that defined not only what type of autojoining was supported, but what constituted a join. (Although the latter could be something like a common type match.) Overriding the icon_state would be impossible for these atoms, as it would be driven by the autojoin. And should the autojoining also happen at runtime?

Underlays would need to obey the joining rules, too, and would need to be considered in the joining. Presently, there's a bug in which the icon_state of the underlay isn't respected if it's not the original choice of that type path. It's an old bug, but it relates to this too. Runtime autojoining with this method would be made much more difficult, because underlays don't preserve any type information at runtime; they're merely Appearances.

All that said, though, I agree on the huge value of something like this for mappers.
I think 47 state joins would be a great "hey let's just pick one and support it" like you've done in the past with sound, maptext, font loading... etc.

How about an appearance object that has no logic to process? You can draw it on the map, and it doesn't overwrite turfs or objs or atoms or anything else. You'd just have /autojoin per tile treat it like turfs. Allow for ctrl+click style edits so it supports underlays just like turfs, have it function in the same way at run time, maybe a flag in New denotes when to clear instead of continuing to add underlays. Developers could be left with their own devices of dealing with it at runtime by being able to access the /autojoin object of the loc in question and going from there. The main thing you'd gain from this is the mapping support.

I do want to say that this idea sorta starts to seem outdated when you bring up the subject of dynamic maps, or chunks.... real time map editing vs compile time map editing, etc. It would be very easy to make a better map editor that works it's logic at runtime; it could support and save auto-joining. We're just lacking something in similar functionality with the dream maker map editor.

I really think it would be great if it could some how just set icon_states much similar to the way we currently handle autotiles. Just make it a new tool in the map editor, and when you're using it, its just changing icon states as you paint (this is how rpg maker map editor works anyway). Then provide maybe a built in way to create / import / export the auto-tiles into your icon editor.

For run-time, you could just provide a few helpful procs that work really similar to the way we handle autojoins in code. The system I made is pretty broad, supports matches and exclusions, directional connections, and is very fast. Its mentioned elsewhere in this thread.
It seems to me that if we support this in the map editor, then for consistency we'd want some runtime support for it as well. Which means, broadly speaking, we'd probably want a KISS approach.

For instance, if we had atom/icon_join, that could be set to 16, 47, 82, 161, or 255. All atoms of that type would join with all other atoms that share a common ancestor with the same value of icon_join. (E.g., if /turf/wall/icon_join==47, then it would join with /turf/wall/cracked and vice-versa. Likewise /turf/wall/cracked would join with /turf/wall/turret.) For convenience, it would probably be necessary at runtime to store the ancestor type in a hidden var kept in the Appearance. The icon_join and the hidden var needn't be sent to the client, as the server can take care of the icon_state manipulation itself. All that has to be done at runtime then is check for cases where a turf's Appearance changes to a different join ancestor, in which case it and the eight turfs around it would need to be updated.

Internally this can be handled with two vars; externally it'd be only one var, which is pretty simple as far as users are concerned. There would be obvious cases where this kind of helpful feature wouldn't work, like when the joins are based on more than just type, but on the whole I think this could be pretty useful.
There are times when you want an auto-tile to connect to something other than a type or path type match; for example when a dithered floor connects to a "SOUTH" wall because in 3/4ths perspective in interior areas, you won't see the bottom where the floor meets the edge of the wall. This is because the standing wall is tall enough to cover it. It looks like this:

This is directional matching, where I match to the SOUTH direction. Situations like this would beg for a different method of determining when a match is made.

An example of where the map editor functions differently than the real-time is placing a new turf. In the map editor, it applies the previous turf as image in the underlays list... at run-time the new turf is placed without the underlays...

It would be great to have a built-in proc that returns a 1 or a zero based on a passed-in atom. Returning 1 means its a match, returning 0 means it isn't a match

icon_join = 47
return 1
return 0
In response to FIREking
You make an interesting point, though given those circumstances I'd think you could just alter the icons so SOUTH was always connected. That'd work really well for any case except, say, a cliff edge, but the cliff edge icon could always belong to the south tile rather than this one.

Making this proc-based would be incompatible with any compile-time solution, which I think is what 99% of mappers who'd use this at all would want. If the joining format truly needs a proc, my feeling is that joining is not suited to built-in automation.
In response to Lummox JR
Modifying the icon_states to always match SOUTH is one way it could work, but there are also situations where you do need that south dithering to occur in other match states not mentioned here.

I think I agree with you though, as much as having built-in auto-tiling would be really great if there was some divine implementation; it doesn't mean we have to use it. And for complex cases like this, you could still roll your own. But having something bare-minimum is still pretty useful, especially to mappers (Eternia has a huge problem with this!).

I would be more interested in a built-in map saving and loading feature for developers; I would generate all of my auto-joins at run time and then save them directly to the map file that gets compiled (and write logic to stop generating them if icon_state differs from the default). My saves would work because each turf's icon state is the only information that describes the state of the autotile anyway!

So in the big picture, we need map saving and loading a little more importantly than map-editor auto-tiling... because that feature would be so much more powerful.
Page: 1 2