// File for movement procs for atom/movable //////////////////////////////////////// // Here's where we rewrite how byond handles movement except slightly different // To be removed on step_ conversion // All this work to prevent a second bump /atom/movable/Move(atom/newloc, direct=0) set waitfor = FALSE //n o . = FALSE if(!newloc || newloc == loc) return if(!direct) direct = get_dir(src, newloc) setDir(direct) if(!loc.Exit(src, newloc)) return if(!newloc.Enter(src, src.loc)) return if (SEND_SIGNAL(src, COMSIG_MOVABLE_PRE_MOVE, newloc) & COMPONENT_MOVABLE_BLOCK_PRE_MOVE) return // Past this is the point of no return var/atom/oldloc = loc var/area/oldarea = get_area(oldloc) var/area/newarea = get_area(newloc) loc = newloc . = TRUE oldloc.Exited(src, newloc) if(oldarea != newarea) oldarea.Exited(src, newloc) for(var/i in oldloc) if(i == src) // Multi tile objects continue var/atom/movable/thing = i thing.Uncrossed(src) newloc.Entered(src, oldloc) if(oldarea != newarea) newarea.Entered(src, oldloc) for(var/i in loc) if(i == src) // Multi tile objects continue var/atom/movable/thing = i thing.Crossed(src) // //////////////////////////////////////// /atom/movable/Move(atom/newloc, direct) set waitfor = FALSE //n o var/atom/movable/pullee = pulling var/turf/T = loc if(!moving_from_pull) check_pulling() if(!loc || !newloc) return FALSE var/atom/oldloc = loc if(loc != newloc) if (!(direct & (direct - 1))) //Cardinal move . = ..() else //Diagonal move, split it into cardinal moves moving_diagonally = FIRST_DIAG_STEP var/first_step_dir // The `&& moving_diagonally` checks are so that a forceMove taking // place due to a Crossed, Bumped, etc. call will interrupt // the second half of the diagonal movement, or the second attempt // at a first half if step() fails because we hit something. if (direct & NORTH) if (direct & EAST) if (step(src, NORTH) && moving_diagonally) first_step_dir = NORTH moving_diagonally = SECOND_DIAG_STEP . = step(src, EAST) else if (moving_diagonally && step(src, EAST)) first_step_dir = EAST moving_diagonally = SECOND_DIAG_STEP . = step(src, NORTH) else if (direct & WEST) if (step(src, NORTH) && moving_diagonally) first_step_dir = NORTH moving_diagonally = SECOND_DIAG_STEP . = step(src, WEST) else if (moving_diagonally && step(src, WEST)) first_step_dir = WEST moving_diagonally = SECOND_DIAG_STEP . = step(src, NORTH) else if (direct & SOUTH) if (direct & EAST) if (step(src, SOUTH) && moving_diagonally) first_step_dir = SOUTH moving_diagonally = SECOND_DIAG_STEP . = step(src, EAST) else if (moving_diagonally && step(src, EAST)) first_step_dir = EAST moving_diagonally = SECOND_DIAG_STEP . = step(src, SOUTH) else if (direct & WEST) if (step(src, SOUTH) && moving_diagonally) first_step_dir = SOUTH moving_diagonally = SECOND_DIAG_STEP . = step(src, WEST) else if (moving_diagonally && step(src, WEST)) first_step_dir = WEST moving_diagonally = SECOND_DIAG_STEP . = step(src, SOUTH) if(moving_diagonally == SECOND_DIAG_STEP) if(!.) setDir(first_step_dir) else if (!inertia_moving) inertia_next_move = world.time + inertia_move_delay newtonian_move(direct) moving_diagonally = 0 return if(!loc || (loc == oldloc && oldloc != newloc)) last_move = NONE return if(.) last_move = direct setDir(direct) if(has_buckled_mobs() && !handle_buckled_mob_movement(loc,direct)) //movement failed due to buckled mob(s) return FALSE if(pulling && pulling == pullee && pulling != moving_from_pull) //we were pulling a thing and didn't lose it during our move. if(pulling.anchored) stop_pulling() else var/pull_dir = get_dir(src, pulling) //puller and pullee more than one tile away or in diagonal position if(get_dist(src, pulling) > 1 || (moving_diagonally != SECOND_DIAG_STEP && ((pull_dir - 1) & pull_dir))) pulling.moving_from_pull = src pulling.Move(T, get_dir(pulling, T)) //the pullee tries to reach our previous position pulling.moving_from_pull = null Moved(oldloc, direct) /atom/movable/proc/handle_buckled_mob_movement(newloc,direct) for(var/m in buckled_mobs) var/mob/living/buckled_mob = m if(!buckled_mob.Move(newloc, direct)) forceMove(buckled_mob.loc) last_move = buckled_mob.last_move inertia_dir = last_move buckled_mob.inertia_dir = last_move return FALSE return TRUE //Called after a successful Move(). By this point, we've already moved /atom/movable/proc/Moved(atom/OldLoc, Dir, Forced = FALSE) SEND_SIGNAL(src, COMSIG_MOVABLE_MOVED, OldLoc, Dir, Forced) if (!inertia_moving) inertia_next_move = world.time + inertia_move_delay newtonian_move(Dir) if (length(client_mobs_in_contents)) update_parallax_contents() return TRUE // Make sure you know what you're doing if you call this, this is intended to only be called by byond directly. // You probably want CanPass() /atom/movable/Cross(atom/movable/AM) . = TRUE SEND_SIGNAL(src, COMSIG_MOVABLE_CROSS, AM) return CanPass(AM, AM.loc, TRUE) //oldloc = old location on atom, inserted when forceMove is called and ONLY when forceMove is called! /atom/movable/Crossed(atom/movable/AM, oldloc) SEND_SIGNAL(src, COMSIG_MOVABLE_CROSSED, AM) /atom/movable/Uncross(atom/movable/AM, atom/newloc) . = ..() if(SEND_SIGNAL(src, COMSIG_MOVABLE_UNCROSS, AM) & COMPONENT_MOVABLE_BLOCK_UNCROSS) return FALSE if(isturf(newloc) && !CheckExit(AM, newloc)) return FALSE /atom/movable/Uncrossed(atom/movable/AM) SEND_SIGNAL(src, COMSIG_MOVABLE_UNCROSSED, AM) /atom/movable/Bump(atom/A) if(!A) CRASH("Bump was called with no argument.") SEND_SIGNAL(src, COMSIG_MOVABLE_BUMP, A) . = ..() if(!QDELETED(throwing)) throwing.hit_atom(A) . = TRUE if(QDELETED(A)) return A.Bumped(src) /atom/movable/proc/onTransitZ(old_z,new_z) SEND_SIGNAL(src, COMSIG_MOVABLE_Z_CHANGED, old_z, new_z) for (var/item in src) // Notify contents of Z-transition. This can be overridden IF we know the items contents do not care. var/atom/movable/AM = item AM.onTransitZ(old_z,new_z) /atom/movable/proc/setMovetype(newval) movement_type = newval ///////////// FORCED MOVEMENT ///////////// /atom/movable/proc/forceMove(atom/destination) . = FALSE if(destination) . = doMove(destination) else CRASH("No valid destination passed into forceMove") /atom/movable/proc/moveToNullspace() return doMove(null) /atom/movable/proc/doMove(atom/destination) . = FALSE if(destination) if(pulledby) pulledby.stop_pulling() var/atom/oldloc = loc var/same_loc = oldloc == destination var/area/old_area = get_area(oldloc) var/area/destarea = get_area(destination) loc = destination moving_diagonally = 0 if(!same_loc) if(oldloc) oldloc.Exited(src, destination) if(old_area && old_area != destarea) old_area.Exited(src, destination) for(var/atom/movable/AM in oldloc) AM.Uncrossed(src) var/turf/oldturf = get_turf(oldloc) var/turf/destturf = get_turf(destination) var/old_z = (oldturf ? oldturf.z : null) var/dest_z = (destturf ? destturf.z : null) if (old_z != dest_z) onTransitZ(old_z, dest_z) destination.Entered(src, oldloc) if(destarea && old_area != destarea) destarea.Entered(src, oldloc) for(var/atom/movable/AM in destination) if(AM == src) continue AM.Crossed(src, oldloc) Moved(oldloc, NONE, TRUE) . = TRUE //If no destination, move the atom into nullspace (don't do this unless you know what you're doing) else . = TRUE if (loc) var/atom/oldloc = loc var/area/old_area = get_area(oldloc) oldloc.Exited(src, null) if(old_area) old_area.Exited(src, null) loc = null //Called whenever an object moves and by mobs when they attempt to move themselves through space //And when an object or action applies a force on src, see newtonian_move() below //Return 0 to have src start/keep drifting in a no-grav area and 1 to stop/not start drifting //Mobs should return 1 if they should be able to move of their own volition, see client/Move() in mob_movement.dm //movement_dir == 0 when stopping or any dir when trying to move /atom/movable/proc/Process_Spacemove(movement_dir = 0) if(has_gravity(src)) return 1 if(pulledby) return 1 if(throwing) return 1 if(!isturf(loc)) return 1 if(locate(/obj/structure/lattice) in range(1, get_turf(src))) //Not realistic but makes pushing things in space easier return 1 return 0 /atom/movable/proc/newtonian_move(direction) //Only moves the object if it's under no gravity if(!loc || Process_Spacemove(0)) inertia_dir = 0 return 0 inertia_dir = direction if(!direction) return 1 inertia_last_loc = loc SSspacedrift.processing[src] = src return 1