ID:2081539
 
Code:
/turf/traintracks/icon = 'track.dmi'

/mob/train/
icon = 'train.dmi'
verb/Board()
set src in view(1)
usr.loc = src
usr.client.eye = src
usr.client.perspective = EYE_PERSPECTIVE
usr.see_invisible = 1
usr.On_Train = 1
spawn() train_movement()

/atom/movable/proc/train_movement()
var/turf/traintracks/destination = null
for(var/turf/traintracks/t in oview(1))
if(get_dir(src, t) != turn(dir, 180))
destination = t
if(destination)
dir = get_dir(src, destination)
loc = destination
return 1
return 0


Problem description: I'm trying to create a train that players can enter and have it move along tracks during runtime. I have this code here to attempt to do that, but the problem is that the train doesn't move after I click Board(). Where'd I go wrong?

Your best bet is probably adding debugging text to see where the process is failing, right off the bat I'd say you can probably define train_movement() under whatever type the train is and not /atom/movable.

From a quick glance though, I can see a few things, you can probably use get_step(src,src.dir) instead of that loop and direction check, that will return the turf in that direction. You can then check the type using istype() to make sure it's a track.

However, if you were to make the train tracks into objects (probably should, so you can easily add/remove them at runtime and even move them if needed) you could do something like.

var/obj/traintrack/found_track = locate() in get_dir(src,src.dir)
if(found_track)
Move(found_track.loc)


You'll notice I called Move() to move the train and not set the loc directly, this will allow things such as Enter()/Entered() to be called and collision checks and the sort to be executed. You can check the return value of Move() to see if the movement was successful or not (eg: if(Move(location)).

You'd want to alter the train's direction on corners and whatnot before the next movement takes place, this will also allow more complex track setups without pieces adjacent to each other conflicting.

Keep in mind this was all written off the top of my head to give you an idea of what direction to head with the system and it's not "just going to work" obviously.
Hm okay. Just a couple questions so I can wrap my head around this:

1. What's the functional difference between writing
var/obj/traintrack/found_track

and
obj/traintrack/var/found_track
?

2. What exactly does get_step(Ref,Dir) do? The F1 says it finds the position of the step. Does that mean it finds how many steps it takes to get to location of "Ref"? If not, what is the difference between the get_step() and get_loc() proc?
var/obj/traintrack/found_track is a variable that holds an obj/traintrack for you to reference.

obj/traintrack/var/found_track is a variable that would be held by all objects of type obj/traintrack. You would not declare it inside a proc, as it would be a fundamental and permanent part of the object's architecture.

In get_step, "ref" is the object that would be doing the stepping, and dir is a direction. If ref is at 1,1,1, get_step(ref,NORTH) returns the turf at 1,2,1.
In response to Hedgemistress
Hedgemistress wrote:
var/obj/traintrack/found_track is a variable that holds an obj/traintrack for you to reference.

obj/traintrack/var/found_track is a variable that would be held by all objects of type obj/traintrack. You would not declare it inside a proc, as it would be a fundamental and permanent part of the object's architecture.

In get_step, "ref" is the object that would be doing the stepping, and dir is a direction. If ref is at 1,1,1, get_step(ref,NORTH) returns the turf at 1,2,1.

Thanks for the var explanation. That put it in a slightly different perspective than how I was looking at it.

To clarify, get_step is the proc you want to use when you're trying to make one object or mob move on top of another object? Specifically for trying to make this moving train work.
No, get_step does not move anything, and it only references one object. Look at its arguments: ref and dir. Ref is one atom, anything in the world with a location. Dir is a direction. You use it when you know what you're dealing with, and you want to know what is exactly one step in a given direction from it.

I think you might wish to read Nadrew's reply in more detail, as he's explaining how get_step could be used to pare down your loops, but he's also giving you an alternative that may be better.

One thing I can tell you about your code: oview() actually has two arguments, the radius to be checked and the central viewpoint, or the thing that's doing the oviewing. If you don't specify who's viewing, it defaults to usr. Now, the proc you defined is being called by a verb, so the usr is the mob that did the verb.

Now, if I'm following your code, the usr for that proc is inside the object at the time that oview() happens. There's nothing actually in oview() of the usr except the object the mob is inside. You would need to specify that the object itself is the viewer by making it the second argument.

What you're trying to do here isn't actually that complicated. What is complicated is trying to figure out how to do it when you don't know what the pieces they're trying to fit together do, or how or why they do it.
Haaaa.. Thanks for all that Hedgemistress. You're right, I don't know what all the pieces are. I'm trying to relearn the little I knew while trying to avoid asking anyone to do my work for me. I've been trying a bunch of different things but they didn't work.

But anyway this is as far as my understanding takes me. I attempted Nadrew's method but I couldn't get it to work. Here's what I went back to. How do I finesse this damn thing?
/obj/vehicle/train/
icon = 'train.dmi'
verb/Board()
set src in view(1)
usr.loc = src
usr.client.eye = src
usr.client.perspective = EYE_PERSPECTIVE
usr.see_invisible = 1
usr.On_Train = 1
spawn() FireUpTrains()

/obj/traintracks/
icon = 'track.dmi'
var/destination = 0

/atom/movable/proc/FireUpTrains()
var/turf/traintracks/destination = null
for(var/obj/traintracks/t in oview(1, src))
if(get_dir(src, t) != turn(dir, 180)) //line 80
var/destination = t //line 81
if(destination) //line 82
dir = get_dir(src, destination) //line 83
loc = destination //line 84
return 1 //line 85
return 0


My errors are as follows:
Train System.dm:84:error: destination: undefined type: destination
Train System.dm:85:error: destination: undefined type: destination
Train System.dm:86:error: destination: undefined type: destination
Train System.dm:83:error: destination: duplicate definition
Train System.dm:80:error: destination: previous definition
I suspect you've added or changed some lines between when you generated those errors and when you copied this, or you've labeled the lines wrong, because that's not where the errors are.

In this line:

var/turf/traintracks/destination = null


you have declared a variable called "destination". Notably you are saying that this destination is going to be a turf of the type traintracks. Does such a turf type exist? In the code as you've posted, traintracks is an obj. This may be where your "undefined type" errors are coming in. Any non-blue words between var/ and /variable_name are strictly defining the type of thing the variable will hold. It must match a type of thing in the game.

Anyway, up there you define a var called destination.

Here:

 var/destination = t


you are defining another var called destination.

Look at those error messages: "duplicate definition" and "previous definition". Any time you see those, you know you've used the same variable name twice. You don't have to avoid re-using variable names that only exist within a loop, or only within the scope of a proc. But since the first definition of destination exists in the main body of your proc, it still exists when we get to the second.

"Undefined type" is likely because there's no such thing as a /turf/traintracks/, so the compiler doesn't know what a turf/traintracks/destination is.

Now, looking at your code, I don't think you even meant to define a second variable, because then you wouldn't be doing anything with the first. So your problem here is that you're sticking a "var/" in front of "destination = t" for no other reason than it looks good. "var/" is used only for declaration. var/ is what you put when you are telling the code, for the first and only time, "This variable exists and I'm going to use it."

Now, let's talk about the rest of your code.

You've got that confusion between objs and turfs something fierce. You're looking for var/obj/traintrack/t, but storing it in var/turf/traintrack/destination. And in your movement code, you are treating destination like a turf, telling it that the train's loc should = destination, which is an obj.

Objs are objects. They are not tiles on the map, even if the obj is. If this code could be compiled and ran, it would move your train off the map and stuff it inside the track.

You don't want to move the train to the traintracks, because for an obj or mob, that means inside. You want to move the train to the place where the traintracks are. Now, listen. You know the var you should be referencing here. You use it in this code. "loc" on what you've labeled line 84 is actually src.loc, with "src." being implicit/default.

How would you refer to the same thing, but for the object contained in the variable "destination"?

You've got one more technical problem in your code that I can see.

if(get_dir(src, t) != turn(dir, 180))


This is to make sure the train only goes forward and not backwards, yes? But picture, if you will, the train is going east or west and hits an elbow that turns it north. Alright. It's going west and it's got the track behind it, the bend in the track in front of it... and the northbound track to the northwest. What's going to happen? It will detect the track in front of it, but then it will detect the track that's northwest of it. That's not a 180 degree turn from it, so it will accept it as a destination, and so it'll skip the elbow.

On other directions, it gets worse. If it's going southeast or southwest, then as soon as it clears the bend it will start detecting the track it left before the bend as the final destination.

I think what you really want is to make sure it's straight or a 90 degree turn or a -90 degree turn.


And this is before we get into issues with having two tracks running side by side. Your trains would literally be jumping tracks.

Really, though, the more I look at this and the more I think Nadrew was right and you really want to do this with get_step. The best way to avoid confusion about which piece of track in oview(1) to follow would be to check one at a time.

I'm going to do something I'll probably regret and give you some code. If you do not actually take the time to get to know what this code means and how and why it works, I will not help you any further. Your responsibility is to not just copy this code but read through it, line by line. If you don't understand what a line does and why it works, it is poison and it will kill your project, maybe not now but somewhere down the line. Look it up in the reference. Read what it says. Code is nothing but instructions for the computer. If you don't know what the instructions you're giving a computer mean, that's how you get a robot uprising.

/atom/movable/proc/FireUpTrains()
for (var/obj/traintracks/destination in get_step(src,dir)) //we don't need a separate var for "t". If there's something that fits in the traintrack-shaped hole we dug in the turf one step from src in dir, it will trigger the loop and we'll deal with it there, inside the loop.
loc = destination.loc //Put us in the same place as the track.
//gonna get a little tricky right now.
if (dir != destination.dir)
dir = turn(destination.dir - destination.dir&dir,180) //I'll explain below.


The key to this is that you have to have the train pointed the right direction to begin with, because each time it executes it will be checking the square right in front of the train and only that square for track. (That is what get_step does.)

Okay, now the dir changing, that's getting a little complicated, and it depends on you making your train track icons in a certain way. I'm assuming that you are naming the corner pieces after the direction their corner is pointing, not the direction they head in. For instance, the one that looks like L would be the southwest corner.

So if you're a train going into that L corner, you're either heading south or you're heading west, right? If you're west, you want to be going north. If you're heading south, you want to be heading east.

So what we need is a line of code that says "take whatever direction I'm not heading, and reverse it." Right?

So, working backwards, the way you find out what part of the destination's direction is shared by the train is you put an & between them. This is the "bitwise and". It's a mathematical operator that returns the portion of two bitflag values that overlap. SOUTHWEST is equal to SOUTH+WEST. If the track is SOUTHWEST and I'm going SOUTH, our & is SOUTH. Subtract our & from the track's dir and you get WEST. I want to be going the opposite of that, so we turn it 180 degrees.

Notice that in my explanation of that weird line at the end, I'm working my way inside out and backwards. That's the way you have to think when assembling such a line. Start by definining in precise terms, to yourself, exactly what you want to do, then figure out how to couch the instruction to do so to the computer.

I think this solves all the problems with your code except for one: if you get this working, the train will move exactly one square in its initial direction and stop. This really needs to be a loop.
Hedgemistress, I'm sure all that was an easy task do to but I appreciate you helping me out.

1. Yes I mistakenly moved a thing or two around before compiling.
2. I'm an idiot for not catching the undefined type error. They're fixed now.
3. I understand now that aspect of variable declaration where I didn't need that 2nd declaration. I placed it where I did purely to see if it would work.
4. I now know/remember moving an obj to a mob or obj moves it into it's contents and not the coordinate or tile it's on.
5. As for the 180 turn, that was there for me to fix later. My main objective was to get the train and the train segments behind it to move along objs (here, the traintracks).
6. I didn't think to use a math concept to have an obj(train) orient itself face another obj (traintracks), but understand that last line.
7. I always make efforts to understand code someone shows me, which is why it's taken me so long to respond to this respond because I try and solve the problem with every bit of help I can get. Unfortunately my progamming skills are garbage right now so pointing out my mistakes and seeing commented examples has helped me best.


My problem now *sigh* is this warning from this code.
/obj/vehicle/train/
icon = 'train.dmi'
verb/Board()
set src in view(1)
usr.loc = src
usr.client.eye = src
usr.client.perspective = EYE_PERSPECTIVE
usr.see_invisible = 1
usr.On_Train = 1
FireUpTrains(/obj/vehicle/train in world) //line 71. I've tried putting src as an argument, I've tried
//putting /obj/vehicle/train and an argument -- doesn't get the train moving


/obj/traintracks/
icon = 'track.dmi'


/atom/movable/proc/FireUpTrains()
for (var/obj/traintracks/destination = locate() in get_step(src,dir)) //line 80. get_step() checks for the tiles infront(dir) of the train. Src refers to what/who used the FireUpTrains() proc
if(destination)
world << "Destination track found." //Debug line here
Move(destination.loc) //Put us in the same place as the track.
else
world << "Destination track not found" //Debug line here
if (dir != destination.dir) //How the trains turn
dir = turn(destination.dir - destination.dir&dir,180)


Error message is:
Train System.dm:80:warning: destination: variable defined but not used

I don't see how it isn't used. I downloaded SS13 and looked through a couple hundred lines and found out it doesn't use locate() as an argument inside a for loop like I tried doing. So I assume that's where I may be wrong?

Another thing is this: The 'src' in get_step(src, dir) refers to the thing that called the proc, right? In this case, the Board() verb runs the FireUpTrains() proc at the end, so does 'src' in get_step(src, dir) refer to the obj that Board() belongs to (/obj/vehicle/train)? Or does it refer to the player that clicks the Board() verb? Sorry if that sounds confusing.