(See the best response by Rotem12.)
// File:
// Library: Forum_account.Sidescroller
// Author: Forum_account
// Contents:
// This file contains pathfinding features. It defines the
// mob.move_to and mob.move_towards procs which are similar
// to DM's built-in walk_to and walk_towards procs.

// The /Path datum is used to contain all A* pathfinding code.
// You never need to access this directly, mob.move_to handles
// all the details.
// The implementation is almost directly copied from:



limit = 0

New(mob/m, turf/t)
mover = m
destination = t


// The add proc is called to add a node to the fringe. If the node already
// exists in the fringe, it's g() and h() values are updated (if appropriate).
if(!t) return
if(t.density) return
if(t in closed) return 0

var/tentative_g_score = g_score[current] + distance(current, t)
var/tentative_is_better = 0

if(!(t in fringe))
fringe += t
tentative_is_better = 1
else if(tentative_g_score < g_score[t])
tentative_is_better = 1

parent[t] = current

g_score[t] = tentative_g_score
h_score[t] = heuristic(t)
f_score[t] = g_score[t] + h_score[t]

return 1

// this proc controls the distance metric used by the search algorithm.
distance(turf/a, turf/b)
return abs(a.x - b.x) + abs(a.y - b.y)

// the heuristic is simply the distance from the turf to the destination.
return distance(t, destination)


closed = list()
fringe = list(mover.loc)
parent = list()

f_score = list()
g_score = list()
h_score = list()

g_score[mover.loc] = 0
h_score[mover.loc] = heuristic(mover.loc)
f_score[mover.loc] = h_score[mover.loc]

parent[mover.loc] = null

var/found_path = 0


// if there's a limit to how many turfs you'll check
// and if you've reached that limit, stop
if(closed.len >= limit)

// find the node with the lowest f-score
current = fringe[1]

for(var/turf/t in fringe)
if(f_score[t] < f_score[current])
current = t

// If this node is the destination, we're done.
if(current == destination)
found_path = 1

fringe -= current
closed += current

// The in_air flag tells us if the current tile is in the air, or
// on the ground (there's a dense tile below it). This flags determines
// who the current node's neighbors are -- if it's on the ground the
// mob can reach nodes by walking to the side or by jumping, if the node
// if in the air the mob can only reach nodes by falling.
var/in_air = 1

var/turf/t = locate(current.x, current.y - 1, current.z)
in_air = 0
in_air = 1


// if the current tile is "in the air", only check the tiles you
// can fall to (down+left and down+right, down was already added)
t = locate(current.x - 1, current.y, current.z)
if(t && !t.density)
add(locate(current.x - 1, current.y - 1, current.z))

t = locate(current.x + 1, current.y, current.z)
if(t && !t.density)
add(locate(current.x + 1, current.y - 1, current.z))

// if the current tile is on the ground, check the tiles to the
// sides and tiles you can jump to.
t = locate(current.x - 1, current.y, current.z)

t = locate(current.x + 1, current.y, current.z)

t = locate(current.x, current.y, current.z)

var/turf/up = locate(current.x, current.y + 1, current.z)
if(t && !t.density && up && !up.density)

if(add(locate(current.x - 1, current.y + 1, current.z)))
add(locate(current.x - 2, current.y + 1, current.z))

if(add(locate(current.x + 1, current.y + 1, current.z)))
add(locate(current.x + 2, current.y + 1, current.z))

// At this point we're outside of the loop and we've
// either found a path or exhausted all options.

del src

// at this point we know a path exists, we just have to identify it.
// we use the "parent" list to trace the path.
var/turf/t = destination
tiles = list()

tiles.Insert(1, t)
t = parent[t]


__jump_delay = 0

__stuck_counter = 0

// The follow_path proc is called from the mob's default movement proc if
// either path or destination is set to a non-null value (in other words,
// follow_path is called if move_to or move_towards was called). Because
// this proc is called from movement, all we need to do is determine what
// calls to move, jump, or climb (not implemented yet) should be made.

// If the mob is following a planned path...

if(loc == path.destination)
return 1

// check to see if the mob is "stuck", the stuck counter
// is reset to zero when the mob advances a tile. If the
// mob spends 50 ticks at the same tile, they're "stuck".
__stuck_counter += 1

if(__stuck_counter > 50)
__stuck_counter = 0
path = new(src, path.destination)

return 0

next_step = null
while(next_step == null)
return 0

next_step = path.tiles[1]

if(loc == next_step)
var/turf/under = locate(next_step.x, next_step.y - 1, next_step.z)

// if the next_step has a dense turf below it, that means its a tile
// you need to step on before we'll remove it from the list. If it's
// up in the air we'll remove it from the list if you just touch a
// small part of it.
if(under && under.density)
next_step = null
__stuck_counter = 0

next_step = null
__stuck_counter = 0

return 0

// move towards the next step

// Previously, these if statements had compared your x
// to the next_step's x. This could cause you to get stuck
// standing on the edge of an object when your next_step
// was below you.
// In the testing I've done, this corrects the problem and
// improves path following overall.
if(px < next_step.px)
dir = RIGHT
else if(px + pwidth > next_step.px + next_step.pwidth)
dir = LEFT

if(next_step.y > y)
__last_jump_loc = loc
__jump_delay = 30

// if the mob is moving towards a destination...
else if(destination)

if(loc == destination)
return 1

// I made the same changes to these if statements as the ones
// in the case for following a path.
if(px < destination.px)
dir = EAST
else if(px + pwidth > destination.px + destination.pwidth)
dir = WEST

return 0

// calling the stop proc will halt any movement that was
// triggered by a call to move_to or move_towards.
destination = null
next_step = null
path = null
__stuck_counter = 0

// This is the sidescroller equivalent of DM's built-in walk_towards
// proc. The behavior takes some obstacles into account (it'll try to
// jump over obstacles) but it doesn't plan a path. It's CPU usage is
// lower than move_to but the behavior may not be sufficiently smart.


if(istype(t, /atom/movable))
t = t.loc

// calling move_towards(null) will stop the current movement but
// not trigger a new one, so it's just like calling stop().
if(!t) return 0

destination = t

return 1

// move_to is the sidescroller equivalent of DM's built-in walk_to proc.
// It uses the A* algorithm to plan a path to the destination and the
// follow_path proc handles the details of following the path.


if(istype(t, /atom/movable))
t = t.loc

// calling move_to(null) will stop the current movement but
// not trigger a new one, so it's just like calling stop().
if(!t) return 0

var/turf/a = locate(t.x, t.y - 1, t.z)
if(!a.density && !a.scaffold)
a = locate(t.x, t.y - 2, t.z)
if(!a.density && !a.scaffold)
return 0

// Because we're creating a new path we can reset this counter.
__stuck_counter = 0

path = new(src, t)

next_step = path.tiles[1]
return 1
return 0

// File:
// Library: Forum_account.Sidescroller
// Author: Forum_account
// Contents:
// This file contains the code to handle the actual
// pixel movement (the pixel_move proc). It also has
// some related functions.

ladder = 0

// we need these vars for turfs and mobs at least. We probably don't
// need them for areas, but it's easy to define them for all atoms.
// px/py is your position on the map in pixels
px = -1
py = -1

// the fractional parts of your movements
_px = 0
_py = 0

// pwidth/pheight determine the dimensions of your bounding box
pwidth = -1
pheight = -1

// used for sloped objects, pleft is the height of the object's
// left side and pright is the height of its right side.
pleft = 0
pright = 0

// ceiling_ramp = 0

// offset_x/y are used to offset the object's icon to make the
// image appear within the bounding box.
// use pixel_x and pixel_y instead now.
// offset_x = 0
// offset_y = 0

// This replaces the "platform" var for turfs. Set scaffold = 1
// to create objects that you can walk in front of and stand on
// top of.
scaffold = 0

// This is used to define properties of the object that get
// stored in the mob's on_ground, on_left, on_right, and
// on_ceiling vars.
flags = 0

// These are flags for individual sides of the atom.
flags_right = 0
flags_left = 0
flags_bottom = 0
flags_top = 0


if(icon_width == -1)

if(pwidth == -1)
pwidth = icon_width

if(pheight == -1)
pheight = icon_height

if(pleft == 0 && pright == 0)
pleft = pheight
pright = pheight
pheight = max(pleft, pright)

if(x && y)
if(istype(src, /atom/movable))
var/atom/movable/m = src
px = icon_width * x + m.step_x
py = icon_height * y + m.step_y
px = icon_width * x
py = icon_height * y

// Calculates the height of a sloped tile for a given bounding
// box. The height is the largest py value that the slope has
// underneath the specified bounding box. You can also think of
// the height as being the y-value of the point where contact
// would first be made if you moved the bounding box straight down.
if(pright > pleft)
. = min(icon_height, qx + qw - px)
. = py + pleft + (.) * (pright - pleft) / pwidth
. = min(., py + pright)
. = max(0, qx - px)
if(. > px + pwidth) return -100
. = py + pleft + (.) * (pright - pleft) / pwidth
. = min(., py + pleft)

. = round(.)

stepping_on(mob/m, time)

step_size = 1
bound_width = pwidth
bound_height = pheight

animate_movement = 0

// movable atoms can have velocities
vel_x = 0
vel_y = 0

list/bottom = list()
was_on_ground = 0

// by default you can only bump into dense turfs and platforms

// we need to handle the scaffold differently whether it's a ramp or not.
if(dropped) return 0

// If it's not a ramp...
if(a.pleft == a.pright)
if(py >= + a.pheight)
return 1

// if it is a ramp...
if(py >= a.height(px, py, pwidth, pheight))
return 1

// you can always bump into dense turfs, even if you're not dense
else if(isturf(a))
return a.density

// you can only bump into dense mobs/objs if you're dense too
return a.density && density

return 0

// The pixel_move proc moves a mob by (dpx, dpy) pixels. If this move is invalid (because a
// dense atom is in your way) the move may be adjusted so that you don't end up inside that atom.
pixel_move(dpx, dpy)

bound_width = pwidth
bound_height = pheight

// find the integer part of your move
var/ipx = round(abs(dpx)) * ((dpx < 0) ? -1 : 1)
var/ipy = round(abs(dpy)) * ((dpy < 0) ? -1 : 1)

// accumulate the fractional parts of the move
_px += (dpx - ipx)
_py += (dpy - ipy)

// ignore the fractional parts
dpx = ipx
dpy = ipy

// increment the move if the fractions have added up
while(_px > 0.5)
_px -= 1
dpx += 1
while(_px < -0.5)
_px += 1
dpx -= 1

while(_py > 0.5)
_py -= 1
dpy += 1
while(_py < -0.5)
_py += 1
dpy -= 1

if(trace) trace.event("[world.time]: start pixel_move: dpx = [dpx], dpy = [dpy]")

// We'll use this var later to check if we should "stick" to a ramp below us.
// The reason we declare the variable here is because the value of dpy might
// change in this proc but we want to use its initial value.
var/stick_to_ramp = on_ground && (dpy <= 0)

move_x = dpx
move_y = dpy

var/bumped = 0

// if you're touching a wall and are trying to move in that
// direction, we set the bump flag but pre-emptively update
// the move_x or move_y var so you don't attempt the move.
// this is done to avoid some glitches, for example:
// +---+
// |mob|
// +---+---+---+---+
// |###|###|###|###|
// +---+---+---+---+
// ^
// |
// if the mob tries to move over 4 pixels and down 4 pixels,
// they'll end up bumping the left side of the indicated turf
// which will set move_x = 0, then they'll bump the top of a
// turf and set their move_y = 0 - the end result is no movement.
// these changes avoid this behavior by pre-emptively setting
// move_y = 0 in the situation shown above. the bump flag is
// set so the bump() proc is still called.
if(on_left && move_x < 0)
move_x = 0
dpx = 0
bumped |= LEFT
else if(on_right && move_x > 0)
move_x = 0
dpx = 0
bumped |= RIGHT

if(on_ground && move_y < 0)
move_y = 0
dpy = 0
bumped |= DOWN
else if(on_ceiling && move_y > 0)
move_y = 0
dpy = 0
bumped |= UP

for(var/atom/a in obounds(src, move_x, move_y, abs(move_x), abs(move_y)))

// if we're not trying to move anymore, we can stop checking for collisions.
if(move_x == 0 && move_y == 0) break

// We use the src object's can_bump proc to determine what it can
// collide with. We might have more complex rules than just "dense
// objects collide with dense objects". For example, you might want
// bullets and other projectiles to pass through walls that players
// cannot.
if(!can_bump(a)) continue

// if we can bump it, check for collisions (see

// stick_to_ramp will be true if you were on the ground before performing this
// move and if you're not moving upwards (if you're moving upwards you shouldn't
// stick to the ground).
if(stick_to_ramp && move_y <= 0)
// check all turfs within 8 pixels of your bottom (hehe)...
for(var/turf/t in bottom(8))
// only check turfs that you can bump and are ramps
if(!can_bump(t)) continue
if(t.pleft == t.pright) continue

// t.height gives you the height of the top of the turf based on your mob.
// You can think of it as, "if your mob fell straight down, at what height
// would you hit the ramp". That's the heigh that t.height returns.
var/h = t.height(px + move_x,py + move_y, pwidth, pheight)

// by setting dpy to h - py, we're making you move down just enough that
// you'll end up on the ramp.
move_y = h - py

if(trace) trace.event("[world.time]: end pixel_move: dpx = [dpx], dpy = [dpy], move = [move_x], [move_y]")

// at this point we've clipped your move against all nearby tiles, so the move
// is a valid one at this point (both might be zero) so we can perform it.
set_pos(px + move_x, py + move_y)

// if the resulting move was shorter than the attempted move, a bump occurred
if(dpx > 0 && move_x < dpx)
bumped |= RIGHT
else if(dpx < 0 && move_x > dpx)
bumped |= LEFT

if(dpy > 0 && move_y < dpy)
bumped |= UP
else if(dpy < 0 && move_y > dpy)
bumped |= DOWN

// if any bump flags were set, call the bump proc for all atoms you're touching
// in the flagged directions
if(bumped & RIGHT)
for(var/atom/a in right(1))
bump(a, RIGHT)
else if(bumped & LEFT)
for(var/atom/a in left(1))
bump(a, LEFT)

if(bumped & UP)
for(var/atom/a in top(1))
bump(a, UP)
else if(bumped & DOWN)
for(var/atom/a in bottom(1))
bump(a, DOWN)

return bumped ? 0 : 1

// set_pos now takes your new px and py values as parameters.
set_pos(nx, ny, map_z = -1)

if(trace) trace.event("[world.time]: start set_pos: nx = [nx], ny = [ny], map_z = [map_z]")

// if the first argument is an atom, set the position
// of src to be centered on the atom.
if(istype(nx, /atom))
var/atom/a = nx

if(istype(a, /turf))
loc = a
loc = a.loc

return set_pos(a.px + (a.pwidth - pwidth) / 2, + (a.pheight - pheight) / 2, a.z)

if(map_z == -1) map_z = z

var/moved = (nx != px || ny != py || map_z != z)

px = round(nx)
py = round(ny)

var/tx = round((px + pwidth / 2) / icon_width)
var/ty = round((py + pheight / 2) / icon_height)

var/turf/old_loc = loc
var/turf/new_loc = locate(tx, ty, map_z)

if(new_loc != old_loc)
var/area/old_area = old_loc:loc
Move(new_loc, dir)

// In case Move failed we need to update your loc anyway.
// If you want to prevent movement, don't do it through Move()
loc = new_loc

var/area/new_area = new_loc.loc

if(old_area != new_area)
if(old_area) old_area.Exited(src)
if(new_area) new_area.Entered(src)

CRASH("The atom [src] is not on the map. Objects may \"fall off\" the map if the perimeter of the map does not contain dense turfs.")

step_x = px - x * icon_width
step_y = py - y * icon_height

client.pixel_x = camera.px - px
client.pixel_y = - py

// -- position the /Background objects --
// set_background is a proc the developer can override to
// define custom background logic.

// position all the backgrounds accordingly.
for(var/Background/bg in backgrounds)

if(!(bg.image in client.images))
client.images += bg.image

bg.object.loc = loc

// I should also make this take camera position into account
var/bx = step_x + client.pixel_x + bg.px
var/by = step_y + client.pixel_y +

if(bg.repeat & REPEAT_X)
while(bx < -bg.width * 1.5)
bx += bg.width
while(bx > bg.width * -0.5)
bx -= bg.width

if(bg.repeat & REPEAT_Y)
while(by < -bg.height * 1.5)
by += bg.height
while(by > bg.height * -0.5)
by -= bg.height

bg.object.pixel_x = bx + 16
bg.object.pixel_y = by + 16
// -- Done with /Background objects --

if(moved || on_ground || was_on_ground)

var/list/_bottom = list()
for(var/atom/a in obounds(src, 0, -1, 0, -pheight + 1))
if(!can_bump(a)) continue

if(a.pleft != a.pright)
if(py != a.height(px,py,pwidth,pheight))
if(py != + a.pheight)

_bottom += a

for(var/atom/a in _bottom)
if(a in bottom)
bottom[a] += 1
a.stepping_on(src, bottom[a])
bottom[a] = 1

for(var/atom/a in bottom)
if(!(a in _bottom))
bottom -= a
was_on_ground = on_ground

last_x = x
last_y = y
last_z = z

if(trace) trace.event("[world.time]: end set_pos: nx = [nx], ny = [ny], map_z = [map_z]")





if(HP <= 0)
view() << "[src] dies!"

Problem description:

The file on pixel-movement and pathing was working fine in my project before i had to make classes in order to give my characters each different stats so i made all my classes under mob/character and now im getting all these errors because of the on_ground: undefined var RIGHT: undefined var move: undefined proc RIGHT: undefined var LEFT: undefined var move: undefined proc LEFT: undefined var slow_down: undefined proc can_jump: undefined proc jump: undefined proc RIGHT: undefined var move: undefined proc LEFT: undefined var move: undefined proc slow_down: undefined proc on_ground: undefined var on_left: undefined var LEFT: undefined var on_right: undefined var RIGHT: undefined var on_ground: undefined var on_ceiling: undefined var bottom: undefined proc RIGHT: undefined var LEFT: undefined var RIGHT: undefined var right: undefined proc RIGHT: undefined var bump: undefined proc LEFT: undefined var left: undefined proc LEFT: undefined var bump: undefined proc top: undefined proc bump: undefined proc bottom: undefined proc bump: undefined proc last_x: undefined var last_y: undefined var last_z: undefined var drop: undefined proc m.check_loc: undefined proc m.movement: undefined proc

Official.dmb - 43 errors, 0 warnings (double-click on an error to jump to it)

would i have to define the vars and procs under my mob/character? or is there another easier way to do it?
Best response
No, anything defined under /mob should apply to /mob/character.

I only have 1 question, where are those things defined in first place?
I followed first few errors but I didn't find a definition for move() or RIGHT or LEFT or slow_jump etc.

It makes me wonder whether you're missing a file from the library.
(Check it's ticked and everything, it might be a simple silly issue)
I hope you are not copy-pasting the library into your project.

In case you don't know, you can include the library into your project by checking it in your file tree under the Lib folder.

Make sure that you have the latest version of the Sidescroller library and other libraries that it uses to avoid problems.
i didnt know u could include the library like that .-. thx for something new. and i it was just a simple silly issue....i didnt have the movement file ticked... THX ANYWAY GUYS
Hmmm How would i go about adding the macros to my characters from sidescroller, cause as of now the macro for jump doesnt work for me. And i was wondering if its possible to have the same key be used as a different macro for different characters? for example having "A" let one character jump while letting "A" for another character be the attack macro.
If I recall correctly, Forum_account uses his keyboard library alongside the sidescroller.

Check this library:
You probably are already using it.

It should be possible to add/change macros or have different macros for different characters.
the keyboard library is included in my project and all the directional keys work. but the main one that i cant figure out is the jump one. I could make a jump verb for each of my characters but i dont know how. could anyone show me an example of what a jump verb should be like? or link me to somewhere that has it other than sidescroller.
Apparently it doesn't use the keyboard library.

If you want to change the jump verb key you can just do:
mob/verb/change_jump_macro(key as text)
controls.jump = key // assigns jump macro to a certain key

By default, controls.jump is assigned to "space" by the library but it can be any other key.

For example, if we want to have one person with attack verb on "A" and another with jump verb on "A", we'd assign a normal macro for one of them and set controls.jump for the other.
i checked the keyboard library and it does say it defaults to space but jumping wont work for some reason. and i checked the macros on the interface and space isnt one of them, would i need to add it there and make the verb jump? or is it already established with the keyboard library?
It's done at and in sidescroller.

Sidescroller does not use the keyboard library.

You won't need to add a macro for jump at the interface just set controls.jump to the key you want.
I tried it using ur format but it doesnt work. instead it creates an ingame command that says "change jump macro".
My format was an example, a verb.

You can just put that line on Login() or client/New() or wherever you want:
controls.jump = "A" // or client.controls.jump

Of course it doesn't have to be "A", it can be any key.
i did it like this:
world<<"[src] has Logged In"
controls.jump = "X"

and i dont get any errors or anything, but it still wont jump :/ .
Try small x
world<<"[src] has Logged In"
controls.jump = "x"
doesnt work :/ i'm always having errors with macros. for some reason even a simple attack macro wont work for me. heres the attack verb, could u just help me make an attack one and then id work from there? i already an attack macro to the macro list in my interface but it still wont work for somereason.

heres the verb if it will help :

for(var/mob/character/m in get_step(src, dir))
world << m
view(m)<<"[src] hit [m] for [Damage] Damage!"
Use DM tags.

// for this to work, simply put <dm> above your code
// then space it where you want it
// and then place <dm> below your code (with a / infront of the d in <dm>

And boom, it's so much easier to read.
for(var/mob/character/m in get_step(src, dir))
world << m
view(m)<<"[src] hit [m] for [Damage] Damage!"
Whenever you say you get errors, it is best to say what errors you get for us to narrow down the problem. There are countless possible errors. Copy-pasting the error message helps a lot. Please keep this in mind.
forgive me for that, i used the wrong word there. i dont get any errors, the macros just dont work.
Page: 1 2 3