// File: pathing.dm
// 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:
// http://en.wikipedia.org/wiki/A*_search_algorithm
Path
var
turf/destination
mob/mover
list/tiles
list/closed
list/fringe
list/parent
turf/current
list/f_score
list/g_score
list/h_score
limit = 0
New(mob/m, turf/t)
mover = m
destination = t
compute()
proc
// 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).
add(turf/t)
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
if(tentative_is_better)
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.
heuristic(turf/t)
return distance(t, destination)
compute()
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
while(fringe.len)
// if there's a limit to how many turfs you'll check
if(limit)
// and if you've reached that limit, stop
if(closed.len >= limit)
break
// 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
break
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)
if(t)
if(t.density)
in_air = 0
else
in_air = 1
add(t)
// 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)
if(in_air)
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.
else
t = locate(current.x - 1, current.y, current.z)
add(t)
t = locate(current.x + 1, current.y, current.z)
add(t)
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.
if(!found_path)
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()
while(t)
tiles.Insert(1, t)
t = parent[t]
mob
var
Path/path
turf/destination
turf/next_step
turf/__last_jump_loc
__jump_delay = 0
__stuck_counter = 0
proc
// 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.
follow_path()
// If the mob is following a planned path...
if(path)
if(loc == path.destination)
stop()
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)
if(!path)
return 0
next_step = null
while(next_step == null)
if(!path.tiles.len)
stop()
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)
if(on_ground)
path.tiles.Cut(1,2)
next_step = null
__stuck_counter = 0
else
path.tiles.Cut(1,2)
next_step = null
__stuck_counter = 0
if(!next_step)
stop()
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)
move(RIGHT)
dir = RIGHT
else if(px + pwidth > next_step.px + next_step.pwidth)
move(LEFT)
dir = LEFT
else
slow_down()
if(next_step.y > y)
if(can_jump())
__last_jump_loc = loc
__jump_delay = 30
jump()
// if the mob is moving towards a destination...
else if(destination)
if(loc == destination)
stop()
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)
move(RIGHT)
dir = EAST
else if(px + pwidth > destination.px + destination.pwidth)
move(LEFT)
dir = WEST
else
slow_down()
else
return 0
proc
// calling the stop proc will halt any movement that was
// triggered by a call to move_to or move_towards.
stop()
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.
move_towards(turf/t)
stop()
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.
move_to(turf/t)
stop()
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)
if(path)
next_step = path.tiles[1]
return 1
else
stop()
return 0
// File: pixel-movement.dm
// 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.
turf
var
ladder = 0
atom
// 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.
var
// 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
New()
..()
if(icon_width == -1)
world.set_icon_size()
if(pwidth == -1)
pwidth = icon_width
if(pheight == -1)
pheight = icon_height
if(pleft == 0 && pright == 0)
pleft = pheight
pright = pheight
else
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
else
px = icon_width * x
py = icon_height * y
proc
// 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.
height(qx,qy,qw,qh)
if(pright > pleft)
. = min(icon_height, qx + qw - px)
. = py + pleft + (.) * (pright - pleft) / pwidth
. = min(., py + pright)
else
. = max(0, qx - px)
if(. > px + pwidth) return -100
. = py + pleft + (.) * (pright - pleft) / pwidth
. = min(., py + pleft)
. = round(.)
#ifdef STEPPED_ON
stepped_on(mob/m)
stepping_on(mob/m, time)
stepped_off(mob/m)
#endif
atom/movable
step_size = 1
New()
..()
bound_width = pwidth
bound_height = pheight
mob
animate_movement = 0
// movable atoms can have velocities
var
vel_x = 0
vel_y = 0
#ifdef STEPPED_ON
list/bottom = list()
was_on_ground = 0
#endif
proc
// by default you can only bump into dense turfs and platforms
can_bump(atom/a)
// we need to handle the scaffold differently whether it's a ramp or not.
if(a.scaffold)
if(dropped) return 0
// If it's not a ramp...
if(a.pleft == a.pright)
if(py >= a.py + a.pheight)
return 1
// if it is a ramp...
else
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
else
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
#ifdef LIBRARY_DEBUG
if(trace) trace.event("[world.time]: start pixel_move: dpx = [dpx], dpy = [dpy]")
#endif
// 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 collision.dm)
check_collision(a)
// 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
#ifdef LIBRARY_DEBUG
if(trace) trace.event("[world.time]: end pixel_move: dpx = [dpx], dpy = [dpy], move = [move_x], [move_y]")
#endif
// 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))
if(can_bump(a))
bump(a, RIGHT)
else if(bumped & LEFT)
for(var/atom/a in left(1))
if(can_bump(a))
bump(a, LEFT)
if(bumped & UP)
for(var/atom/a in top(1))
if(can_bump(a))
bump(a, UP)
else if(bumped & DOWN)
for(var/atom/a in bottom(1))
if(can_bump(a))
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)
#ifdef LIBRARY_DEBUG
if(trace) trace.event("[world.time]: start set_pos: nx = [nx], ny = [ny], map_z = [map_z]")
#endif
// 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
else
loc = a.loc
return set_pos(a.px + (a.pwidth - pwidth) / 2, a.py + (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)
if(moved)
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)
if(new_loc)
new_loc.Entered(src)
// 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
if(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)
if(!loc)
if(SIDESCROLLER_DEBUG)
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
if(client)
set_camera()
client.pixel_x = camera.px - px
client.pixel_y = camera.py - py
// -- position the /Background objects --
// set_background is a proc the developer can override to
// define custom background logic.
set_background()
// 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 + bg.py
if(bg.repeat & REPEAT_X)
while(bx < -bg.width * 1.5)
bx += bg.width
while(bx > bg.width * -0.5)
bx -= bg.width
else
..()
if(bg.repeat & REPEAT_Y)
while(by < -bg.height * 1.5)
by += bg.height
while(by > bg.height * -0.5)
by -= bg.height
else
..()
bg.object.pixel_x = bx + 16
bg.object.pixel_y = by + 16
// -- Done with /Background objects --
#ifdef STEPPED_ON
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))
continue
else
if(py != a.py + a.pheight)
continue
_bottom += a
for(var/atom/a in _bottom)
if(a in bottom)
bottom[a] += 1
a.stepping_on(src, bottom[a])
else
bottom[a] = 1
a.stepped_on(src)
for(var/atom/a in bottom)
if(!(a in _bottom))
bottom -= a
a.stepped_off(src)
was_on_ground = on_ground
#endif
last_x = x
last_y = y
last_z = z
#ifdef LIBRARY_DEBUG
if(trace) trace.event("[world.time]: end set_pos: nx = [nx], ny = [ny], map_z = [map_z]")
#endif
mob/character
var
HP=0
MaxHP=0
Str=0
Def=0
minato
HP=90
MaxHP=90
Str=7
Def=2
deidara
HP=100
MaxHP=100
Str=6
Def=1
grimmjow
HP=110
MaxHP=110
Str=6
Def=3
proc
Deathcheck()
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 it:pathing.dm:230:error: on_ground: undefined var
pathing.dm:254:error: RIGHT: undefined var
pathing.dm:254:error: move: undefined proc
pathing.dm:255:error: RIGHT: undefined var
pathing.dm:257:error: LEFT: undefined var
pathing.dm:257:error: move: undefined proc
pathing.dm:258:error: LEFT: undefined var
pathing.dm:260:error: slow_down: undefined proc
pathing.dm:263:error: can_jump: undefined proc
pathing.dm:266:error: jump: undefined proc
pathing.dm:278:error: RIGHT: undefined var
pathing.dm:278:error: move: undefined proc
pathing.dm:281:error: LEFT: undefined var
pathing.dm:281:error: move: undefined proc
pathing.dm:284:error: slow_down: undefined proc
pixel-movement.dm:200:error: on_ground: undefined var
pixel-movement.dm:229:error: on_left: undefined var
pixel-movement.dm:232:error: LEFT: undefined var
pixel-movement.dm:233:error: on_right: undefined var
pixel-movement.dm:236:error: RIGHT: undefined var
pixel-movement.dm:238:error: on_ground: undefined var
pixel-movement.dm:242:error: on_ceiling: undefined var
pixel-movement.dm:267:error: bottom: undefined proc
pixel-movement.dm:291:error: RIGHT: undefined var
pixel-movement.dm:293:error: LEFT: undefined var
pixel-movement.dm:302:error: RIGHT: undefined var
pixel-movement.dm:303:error: right: undefined proc
pixel-movement.dm:305:error: RIGHT: undefined var
pixel-movement.dm:305:error: bump: undefined proc
pixel-movement.dm:306:error: LEFT: undefined var
pixel-movement.dm:307:error: left: undefined proc
pixel-movement.dm:309:error: LEFT: undefined var
pixel-movement.dm:309:error: bump: undefined proc
pixel-movement.dm:312:error: top: undefined proc
pixel-movement.dm:314:error: bump: undefined proc
pixel-movement.dm:316:error: bottom: undefined proc
pixel-movement.dm:318:error: bump: undefined proc
pixel-movement.dm:452:error: last_x: undefined var
pixel-movement.dm:453:error: last_y: undefined var
pixel-movement.dm:454:error: last_z: undefined var
keyboard.dm:61:error: drop: undefined proc
world.dm:34:error: m.check_loc: undefined proc
world.dm:35:error: 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?
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)