ID:153002
 

The red represent ramps going up/over the ones marked with blue. What would be the best way of doing this without teleporting the mob to a cut out bottom part somewhere else?
I think Garthor made a demo of this.
In response to N1ghtW1ng
Turf Elevation dosent show how to make a path under a path.
This is possible, though not trivial. I've never seen anyone actually do it in a game... or even in a demo, for that matter.

You'll have to mess around with layers to get things to display on top of each other. You'll need a variable on all movable atoms representing their elevation; you'll also need to work out a way of representing the elevations of terrain that exist in the world. Using a list on each turf is one way, but remember that there's a limit of 65,000 lists, so this may not be practical. You could mess around with (non-predefined) bitflags, though this approach is limited in the variations of elevation you can represent:

1 (20) - Elev. 0
2 (21) - Elev. 1
4 (22) - Elev. 2
8 (23) - Elev. 3
...and so on.

Then you'll need to override movement procs to (A) restrict large changes in elevation (so people can't climb over high walls or onto bridges); (B) let people move between different elevations, and adjust their layer and elevation vars if necessary; and (C) make dense mobs/objs on different elevations not impede movement.

I recommend overriding Enter() for A and C, and either Entered() or Exited() for B.

It might be useful to get a "dithered" version of movable atoms' icons, and add them as overlays with a very high layer. That way players will still be able to see mobs and objs that are underneath bridges and such.

A full-blown elevation system would be much more complicated; say, one that allows people to be inside a building while other people are standing on the roof - the people inside shouldn't be able to see the people on the roof, and vice versa. Also, the roof should disappear for players inside the building, and for players looking in through the doorway. Once you start getting that complicated, you have to mess around with /images a lot and have customised versions of procs for just about everything. It's theoretically doable, but not pretty.
In response to Crispy
Actually, Crispy, one of the very first demos I looked at had to do with exactly this (well, not exactly this, but can be modified to do this).
In response to Crispy
Crispy wrote:
A full-blown elevation system would be much more complicated; say, one that allows people to be inside a building while other people are standing on the roof - the people inside shouldn't be able to see the people on the roof, and vice versa. Also, the roof should disappear for players inside the building, and for players looking in through the doorway. Once you start getting that complicated, you have to mess around with /images a lot and have customised versions of procs for just about everything. It's theoretically doable, but not pretty.

Meh, which is why I suggested a while ago about maybe adding another "dimension" for elevation. But, I guess I hadn't explained it all that well back then - no one really got much of what I was saying. >_>
In response to Teh Governator
Wasn't there a BYONDscape demo released that dealed with something like this? I.e. seeing people on different Z-layers? There'd be alot of mapwork with using such a system for this, but it would be doable.
In response to Crashed
Really? Show meh! =)
In response to Crispy
Crispy wrote:
This is possible, though not trivial. I've never seen anyone actually do it in a game... or even in a demo, for that matter.

Lummox made one. :p
Elly, Arty(I think Arty was there), and I were there, so were tons of other people. He hasn't released it yet. He may not release it, or he may. Who knows?
In response to Hell Ramen
Oh yeah, I think I remember that. There were stairs and stuff and puddles of green stuff on the ground >_>. I liked how if you bumped into another player you switched places with them. I could make people dance with me!
In response to Artekia
Artekia wrote:
Oh yeah, I think I remember that. There were stairs and stuff and puddles of green stuff on the ground >_>. I liked how if you bumped into another player you switched places with them. I could make people dance with me!

Yeah, we kept dancing with Lummy. :o
In response to Crispy
Crispy wrote:
Really? Show meh! =)

Ebonshadow also has something similar. So, go bug him.
In response to SuperAntx
SuperAntx wrote:
Turf Elevation dosent show how to make a path under a path.

Combine the concepts of Turf Elevation and the Roof lib. It isn't really all that hard just an annoyingly large amount of work. Just make sure you have the patience to try it or you'll end up smashing your keyboard.
datum/turf_parts
var
icon/I
elevation=1
turf
var/list/elevations=list(1)
//set elevations to /datum/turf_parts paths to begin\
with if you want them set at compile-time

New()
elevate()
proc/elevate()
set background = 1
var/datum/icon_datum //the highest one
for(var/datum/E in elevations)
if(isnum(E))continue
elevations.Remove(E)
if(ispath(E))E=new E
if(!E.elevation in elevations)elevations+=(E.elevation)
if(!icon_datum||E.elevation>icon_datum.elevation)
icon_datum=E
if(icon_datum)
if(icon_datum.I)icon=icon_datum.I
layer=icon_datum.elevation*3
Enter(atom/movable/A)
//has to be altered so you only stay in your elevation
if(density&&A.density)return 0
var/change=0
for(var/elevation in elevations)
switch(elevation)
if(A.elevation){change=2;break}
if(A.elevation-1)change|=1
if(A.elevation+1)change|=4
if(!change)return 0
if(change==2)change=A.elevation
else if(change&1)change=A.elevation-1
else if(change&4)change=A.elevation+1
for(var/atom/movable/M in contents)
if(M.elevation==change)
if(M.density)return 0
.=..()
if(.)
A.elevation=change
A.layer=change*3
if(istype(A,/mob))layer+=2
else layer+=1

The elevations list for turfs should contain numbers in it at runtime, except when being altered at runtime. Though, when being altered, the elevate function should be called immediately afterwords.

The elevate function converts /datum/turf_parts paths and actual instances of the type into usable elevations and changes the turf's icon if appropriate (that is, if the elevation is the highest).

You can use turf_parts as a subtype of /obj and just place them on the map for easier use if you don't think you'll have more than the object limit's worth of them. Using /datum/turf_parts in this way should allow you to have about 60,000+ elevated turfs on the map, wheras using /obj/turf_parts would limit you further, especially if you wanted to have multiple elevations accessible per turf.

As for its use...
datum/turf_parts
mountain
base
elevation=2
I='mountain_base.dmi'
mid
elevation=3
I='mountain_mid.dmi'
peak
elevation=4
I='mountain_peak.dmi'
cave
elevation=1
I='cave.dmi'
turf
mountain
base
base_w_cave
elevations=list(/datum/turf_parts/mountain/base,\
/datum/turf_parts/mountain/cave)
mid
elevations=list(/datum/turf_parts/mountain/mid,\
/datum/turf_parts/mountain/cave)
peak
elevations=list(/datum/turf_parts/mountain/peak,\
/datum/turf_parts/mountain/cave)

In that example, you can put an entrance to the cave at any outlying base parts of the mountain and it extends inward under the entire thing.

Of course, I doubt anybody wants to go through all that trouble of typing out tons of types, especially for something more complex than that simple mountain. The implementation of the way you actually add the elevations can be improved, as I noted before with changing it to mappable objects; but the underlying principle is the same.

Also, you can write functions that do much more complicated elevation handling combined with the first snippet as well. That is why I put the guts in the elevate function, so you could call it at will. That also means that you can change the map easily in the middle of the game too, having a changable landscape.
mob/verb
construct_bridge()
var/obj/resources/O=locate()in contents
if(!O)return 0
var/turf/T=get_step(src,dir)
if((elevation in T.elevations)||\
(elevation-1 in T.elevations))
return 0
var/datum/turf_parts/D=new
D.elevation=elevation;D.I='bridge.dmi'
T.elevations+=D;T.elevate()

datum/turf_parts
tower
I='stairs.dmi'
world/New()
var
datum/turf_parts/tower/D
turf/T=locate(5,5,1)
dir=5
for(var/index = 1 to 17) //raise 5 for higher stairs
D=new;D.elevation=index
D.I=icon(D.I,"",dir)
T.elevations+=D
T.elevate()
T=get_step(T,dir)
dir=turn(dir,90)

That second one should be sufficient to make steps spiraling up a tower, going around twice. You could alter it to loop through 1 to 7, making most all the ones in the same circular route the same elevation instead, then jumping up at the next circuit (which might make it look better for the next suggestion).

You could also have it show the icons as overlays for all the elevations available and offset them (and movable objects there at that elevation) by a few pixels to make it appear as though the different elevations are actually at different heights. This would also make it easier to scale tall objects as you could see what you are doing even when not at the top.

If you use the above height perception idea, you might also want to consider only showing those things which are at your elevation or one higher instead of all of them right up to the topmost. This way you don't have towering objects obstructing the view of everything else. Of course, you probably want to show all the elevations lower than you, or possibly down to a few lower and then cut the view off below that in darkness.

I haven't tested any of those code snippets, just wrote them up in the browser here. I'll have to test them when I get the opportunity to make sure they work, as there are bound to be a few bugs to work out. I think it should be alright for the most part though.
In response to Crispy
I've got a library in the works to do this, except it doesn't handle things like visibility at different elevations. I basically had to acknowledge that full control over that is impossible.

The system uses bitflags a lot like you suggested. Any levels separated by 2 elevations or more are considered impassable. Each turf has a base elevation, and the bit flags define elevations above that. Bit 0 is base+2, bit 1 is base+3, and so on up to bit 15 (base+17).

The main problem has been that creating a map to use these features is difficult. I have yet to determine a good way to easily create these maps; perhaps something that could read a multi-tiered .dmp file would help.

Another problem is finding a way to include the necessary autojoin features in the library. My current demo is using kind of a hacked-up solution to that.

Lummox JR
In response to Lummox JR
Cool! Sounds interesting.

Lummox JR wrote:
I've got a library in the works to do this, except it doesn't handle things like visibility at different elevations. I basically had to acknowledge that full control over that is impossible.

Yeah... it's one of those rare cases where it's easier to just write a tile-based engine in some other language, I think.

The main problem has been that creating a map to use these features is difficult. I have yet to determine a good way to easily create these maps; perhaps something that could read a multi-tiered .dmp file would help.

Yeah - probably the easiest way would be to create a DMP file with multiple Z levels. (Or use a "map editor" world - I have a library for making map editors in the works, though it's not particularly fancy.) A space where there is "nothing" would be most easily represented by, say, a /turf/nothing type. Or just /turf. Whatever it was, it'd have to be the world/turf default. Then you'd use a utility to read the DMP file in and write it back out in some useful format. SwapMaps, perhaps.

Though I don't anticipate that being particularly fast or convenient to use...
In response to Crispy
Crispy wrote:
Though I don't anticipate that being particularly fast or convenient to use...

Fortunately you could make it alter everything once into a different format that is faster then discard the uncompressed map. It's just a one-time deal.
turf/var/elevation=0
world/New()
spawn()compress()
proc/compress()
var/turf
T
T1=locate(1,1,1)
T2=locate(world.maxx,world.maxy,index)
for(var/index = 1 to world.maxz)
for(var/turf/E in block(T1,T2))
if(T.type==/turf)continue
T=locate(E.x,E.y,index)
T.elevation|=2**index
world.maxz=1
save_dmp()
In response to Loduwijk
Sure, but you have to do that every single time you make a change to the map, which is potentially slow if you're continually tweaking your map. Making it smart so it only "compresses" changed bits would be an improvement, but it's still an extra level of complexity in the map editing process.

Oh, and I think you're looping a bit too much in your snippet, but I get the idea. =)