ID:1620724
 
I hear you guys repeating this mantra over and over again: "BYOND's map editor is trash.". And while it could stand for improvement, it's really only as bad as you make it. For starters, just about all of you have been taught to use it wrong.

Today's lesson is going to feed into next week's lesson, for the first two-parter of the series. Today, we're going to learn about the map editor, and why polymorphism isn't a database.


Know your role:



Of course, these descriptions are all a bit of a laugh really, but you'll notice that these four roles are going to be what you and your team tend to fall into. Some teams will have members that take multiple roles, and some team members won't really have a role on this list. Of course, as an hobbyist, these roles aren't all-important.

Based on these roles, a logical structure for how your maps should work starts to emerge.

Let's take a look at the structure here:



Basically, what happens, is your designer becomes a central point of contact between each of the three specializations that actually work on the project itself.

The Designer's job is to ensure that the right bits get to the right people. If the programmers need art to test a feature, the designer gets that art. If the artists need a tool to make their art life easier, the designer figures out the requirements and gives them to the programmers. If the environment artist needs the programmers to fix an issue with a tile prototype, that goes through the designer.

I'm not saying these groups should never speak to one another, as synergy is good, but you should never have a situation where the environment artists need to know about programming to do their job, and the programmers know about art to do their job.

If you have set your project up correctly, these three disciplines should maintain their separation until they are combined by the designer for the final product.

What does this have to do with mapping, you may ask? Well, let's look at how BYOND projects tend to do it to explain why this is relevant.

How to fail:


This is a screenshot of Zeta's map. The map itself isn't the worst I've ever seen, but it's certainly not the best. My problem, is primarily within the structure of the object tree itself.

Zeta's code was leaked, ironically by a mapper. Notice how there are pages and pages of objects in a big line down the object tree? That makes it borderline impossible to actually create a map, due to a lack of structure and organization.

Not only that, the way the objects themselves are defined is just plain scary bad!

Let's take a look at what's wrong:

turf
grass
icon = 'turfs.dmi'
icon_state = "grass" //Summer Time
//icon_state = "snow" //Winter Time
density = 0
buildingtop
icon = 'turfs.dmi'
icon_state = "buildingtop"
density = 1
Bridge1
icon = 'turfs7.dmi'
icon_state = "bridge1"
density = 0
Bridge2
icon = 'turfs7.dmi'
icon_state = "bridge2"
density = 0
Bridge3
icon = 'turfs7.dmi'
icon_state = "bridge3"
density = 0
Bridge4
icon = 'turfs7.dmi'
icon_state = "bridge4"
density = 0
Bridge
icon = 'turfs7.dmi'
icon_state = "bridge"
Wall
icon = 'turfs7.dmi'
icon_state = "wall"
density = 1
Wall1
icon = 'turfs7.dmi'
icon_state = "wall1"
density = 1
Wall2
icon = 'turfs7.dmi'
icon_state = "wall2"
density = 1
Wall3
icon = 'turfs7.dmi'
icon_state = "wall3"
density = 1
Wall4
icon = 'turfs7.dmi'
icon_state = "wall4"
density = 1
Wall5
icon = 'turfs7.dmi'
icon_state = "wall5"
density = 1
Wall6
icon = 'turfs7.dmi'
icon_state = "wall6"
density = 1
Wall7
icon = 'turfs7.dmi'
icon_state = "wall7"
density = 1
Cliff
icon = 'turfs7.dmi'
icon_state = "cliff"
density = 1


Don't see anything wrong with this? That's because Zeta was where this bad habit started to spread. None of these turfs need to be hard-coded. None of these icons need to be hard-coded. They should be soft-coded. Why?

Look back up to the diagram I drew of project structure. The mapper should not need to know how to write code in order to use the art assets. Even simple copy-pasta like this is bad practice, as it unnecessarily muddies your project's structure and hinders your environment artists from doing their job.


Polymorphism is not a database

Let's talk about polymorphism for a minute. Polymorphism is a fundamental programming concept that is best described by the common rhetorical phrase: "All letters are symbols, but not all symbols are letters."

Something can be a sort of thing, but have its own properties differentiating itself from another. For instance, a squirrel is an ermine, and has all the properties of an ermine. A weasel is also an ermine, also having all the properties of an ermine, but not having all the properties of a squirrel.



Since the weasel and squirrel are both types of Ermine, we can say with certainty, that either a squirrel or a weasel are ermines, and behave similarly to ermines.

The same is true in programming. In the above bad example, grass, buildingtop, Bridge1, Bridge2, Bridge3, etc. are all types of /turf. The only way in which they differ is in the value of their variables.

This alone is not enough to justify the creation of a new type within the source code. Having the variables is enough to justify the type, but changing them is not. New behavior would, on the other hand, justify having these different types. Let's say we have a type of turf that when walked on, will hurt the player. That should be a different type from the rest of the turfs that don't do that. Let's add on to that, by pretending we also need a turf that will kill the player when they step on it, and one that will teleport the player to another location when they step on it:

turf
hurtplayer
var
damage = 10
Entered(atom/movable/o)
if(istype(o,/mob/player))
var/mob/player/p = o
p.TakeDamage(src,damage)
killplayer
Entered(atom/movable/o)
if(istype(o,/mob/player))
var/mob/player/p = o
p.Die(src)
teleporter
var
relocate_x
relocate_y
relocate_z
Entered(atom/movable/o)
if(istype(o,/mob/player))
if(!p.HasFlag("teleporting"))
var/mob/player/p = o
p.AddFlag("teleporting")
p.Move(locate(relocate_x,relocate_y,relocate_z))
p.RemoveFlag("teleporting")


As you can see, we have three types of turf now. If you want to have different turfs that cause different amounts of damage, or different turfs that teleport you different places, you should use the instance editor, and not hard-code the variable changes without good reason. I will go over the instance editor next.


The neglected instance editor:

The instance editor is a powerful little tool. It allows you to create variations of object types on the map simply by changing variables.

Go into the map editor, and click on the type in the object tree you wish to create an instance of. Now right click on any instance in the object subpanel to bring up the context menu. Click on New "Instance..."



This will bring up the instance editor subwindow, which will allow you to specify variables for this instance. Only what is needed is maintained by the project, so next time you compile, any instances you have created that don't have at least one painted somewhere on the map will disappear from the object panel. This usually discourages people from using this feature, but don't give up on it!

As you can see in my map, I have several dozen variations of this one type all stored under one type. That might seem somewhat disorganized to you, but we'll go over that later.

I have over 75 different turfs on my map without a single one being hard-coded. There is no code specifying the variable changes in my project. I can change the graphics on the fly without having to browse the code, and any environment artists can do the same without knowing a lick of code. Not only that, the programmers don't have to even know the art for the project exists, because all of the project's art was specified in the instance editor on the map itself!


Use an atlas, ya dingus!

So now, you've got hundreds of different turfs all crammed into one little 200 pixel wide context box, right? Now it's just gonna be such a pain to sort through it and find all your turfs, right? Wrong. Don't bother with the left half of the map editor's interface at all. There's literally no need.

What you want to do, is paint an atlas on your map somewhere, that you can drag around with you while you work. Here's mine:



This little atlas makes life a lot easier. Plus, this handy shortcut: Press Ctrl+shift+left click on an object, or a tile to make that object the active object you will be painting with. This is what our atlas is great for. Drag it around with you, and use it to select your pieces from.


A few additional tips:

Right clicking an instance in the object panel will allow you to edit all instances of that modified type on the map at once. Right clicking an instance in the map will allow you to edit that sole instance.

If you create a new instance with the same variables as another instance, it will not create a new type, just change the existing type to the one that already exists, or select the old type.

Ctrl+Click allows you to overwrite all turfs at that location with the currently selected turf.


What just a few hours of work looks like using this technique:


Happy mapping!
Designer LF MORE DPS Complete game 5 MAN MODE NO NINJAS



1+ post
Tbh, even from other people I learned to hard code each icon/turf/whatever. But the instances is a little something unheard of by me, defiantly seems easier than having 17 icons on the side different by just icon_state.

Not sure if im being oblivious or...but do new instances stay around even after closing the project? They seem a bit like ghost icons in a way...
In response to Plemith
Plemith wrote:
Tbh, even from other people I learned to hard code each icon/turf/whatever. But the instances is a little something unheard of by me, defiantly seems easier than having 17 icons on the side different by just icon_state.

Not sure if im being oblivious or...but do new instances stay around even after closing the project? They seem a bit like ghost icons in a way...

As I mentioned, they stick around as long as there's at least one on the map. Try using the atlas trick I mentioned. It helps you keep track of what you need.
In response to Plemith
They do.

I'll say, using the map editor as described by Ter takes some getting used to, but it is much more efficient.

Edit: Also, nice read.
Next week's topic has been selected. It's going to cover polymorphism and datasets. This is more or less a part 2 to this one, however, as it further demonstrates that polymorphism isn't a database.
Keep it up Ter. I love reading your posts.
Another possibly stupid question xD:

Say for instance you had a turf that was grass and one was lava or something, you want the lava to trigger damage, but its the ONLY icon you have out of, lets say, 10 that does that.

Back in the code, lets say you make it like in the example
> turf
> hurtplayer
> var
> damage = 10
>

Would you have to go through and disable the hurtplayer on every icon besides the lava, or is it possible to make it more "automatic".

no idea if I explained this right...
You'd probably have to specify it in the Entered() proc:
        Entered(atom/movable/o)
if(src.icon_state == "lava" && istype(o,/mob/player))
var/mob/player/p = o
p.TakeDamage(src,damage)
+Yay

These are great reads. I feel like I am all roles currently, Lol.
        Entered(atom/movable/o)
if(src.icon_state == "lava" && istype(o,/mob/player))
var/mob/player/p = o
p.TakeDamage(src,damage)
return 1

Not a programmer but uh, add "return 1" if you want that turf to be actually entered.
In response to Plemith
Generate the instances for the grass and other non-damaging icons under just /turf. Create only the lava instance using the /turf/hurtplayer type.

Going back and setting damage to zero for all the other icons is unnecessary, and checking the name of the icon_state in the code kind of defeats the whole purpose of this tutorial ;)
In response to Baird Techie
That's not really needed in this case, as Entered() is called after Enter() (which is when you'd want to return the parent value).
In response to LordAndrew
LordAndrew wrote:
That's not really needed in this case, as Entered() is called after Enter() (which is when you'd want to return the parent value).

my brain
Fantastic! An article that should be considered required reading by people new to using Dream Makers map editor.
In response to Kidpaddle45
Kidpaddle45 wrote:
You'd probably have to specify it in the Entered() proc:
>         Entered(atom/movable/o)
> if(src.icon_state == "lava" && istype(o,/mob/player))
> var/mob/player/p = o
> p.TakeDamage(src,damage)
>


No. Your code should not work like this. It's exactly the kind of thinking that makes me call BYOND logic backward.

Again, you should embed behavior at the necessary levels.

If the turf doesn't hurt you when you step on it, there's no reason to check if it should hurt you. This is what polymorphism is for, ensuring that different behavior is differentiated between types.

DarkCampainger wrote:
Generate the instances for the grass and other non-damaging icons under just /turf. Create only the lava instance using the /turf/hurtplayer type.

Going back and setting damage to zero for all the other icons is unnecessary, and checking the name of the icon_state in the code kind of defeats the whole purpose of this tutorial ;)

Spot on, DC.
Still being spanked for Nbolts and Zeta habits in 2014 shame. Now that you put it this way it reminds me of other engines map editiors now.
I'm glad someone took the time to label out proper mapping techniques! I agree that the instance editor is a great tool that just isn't used like it should be. We should hope for an update that allows a separate window for your atlas, however, so you're not constantly selecting and moving blocks on the map itself, you can have a proper pallet for your design work!

Great post, Ter. Let's see more!
In response to Solomn Architect
Solomn Architect wrote:
I'm glad someone took the time to label out proper mapping techniques! I agree that the instance editor is a great tool that just isn't used like it should be. We should hope for an update that allows a separate window for your atlas, however, so you're not constantly selecting and moving blocks on the map itself, you can have a proper pallet for your design work!

Great post, Ter. Let's see more!

Thanks for the overwhelming positivity on this one guys!

I'll definitely be back for more this sunday.
Wouldn't it help for compile (or is it update?) to not clear unused instances?
Page: 1 2 3