diff --git a/code/__DEFINES/movement.dm b/code/__DEFINES/movement.dm index 473fed1619d9..2506e75736e9 100644 --- a/code/__DEFINES/movement.dm +++ b/code/__DEFINES/movement.dm @@ -58,3 +58,35 @@ #define PASS_FLAGS_FLAME (PASS_AROUND|PASS_UNDER|PASS_MOB_THRU|PASS_THROUGH|PASS_OVER_FIRE) #define PASS_FLAGS_SMOKE (PASS_HIGH_OVER|PASS_AROUND|PASS_UNDER|PASS_MOB_THRU|PASS_THROUGH) #define PASS_FLAGS_CRAWLER (PASS_TYPE_CRAWLER|PASS_UNDER) + +/// The minimum for glide_size to be clamped to. +#define MIN_GLIDE_SIZE 1 +/// The maximum for glide_size to be clamped to. +/// This shouldn't be higher than the icon size, and generally you shouldn't be changing this, but it's here just in case. +#define MAX_GLIDE_SIZE 32 + +/// Compensating for time dilation +GLOBAL_VAR_INIT(glide_size_multiplier, 1.0) + +///Broken down, here's what this does: +/// divides the world icon_size (32) by delay divided by ticklag to get the number of pixels something should be moving each tick. +/// The division result is given a min value of 1 to prevent obscenely slow glide sizes from being set +/// Then that's multiplied by the global glide size multiplier. 1.25 by default feels pretty close to spot on. This is just to try to get byond to behave. +/// The whole result is then clamped to within the range above. +/// Not very readable but it works +#define DELAY_TO_GLIDE_SIZE(delay) (clamp(((world.icon_size / max((delay) / world.tick_lag, 1)) * GLOB.glide_size_multiplier), MIN_GLIDE_SIZE, MAX_GLIDE_SIZE)) + +///True if the dir is north or south, false therwise +#define NSCOMPONENT(d) (d&(NORTH|SOUTH)) +///True if the dir is east/west, false otherwise +#define EWCOMPONENT(d) (d&(EAST|WEST)) +///Flips the dir for north/south directions +#define NSDIRFLIP(d) (d^(NORTH|SOUTH)) +///Flips the dir for east/west directions +#define EWDIRFLIP(d) (d^(EAST|WEST)) + +//Diagonal movement is split into two cardinal moves +/// The first step of the diagnonal movement +#define FIRST_DIAG_STEP 1 +/// The second step of the diagnonal movement +#define SECOND_DIAG_STEP 2 diff --git a/code/controllers/subsystem/time_track.dm b/code/controllers/subsystem/time_track.dm index a01c4a68755f..7b9e2363999a 100644 --- a/code/controllers/subsystem/time_track.dm +++ b/code/controllers/subsystem/time_track.dm @@ -80,6 +80,7 @@ SUBSYSTEM_DEF(time_track) time_dilation_avg_fast = MC_AVERAGE_FAST(time_dilation_avg_fast, time_dilation_current) time_dilation_avg = MC_AVERAGE(time_dilation_avg, time_dilation_avg_fast) time_dilation_avg_slow = MC_AVERAGE_SLOW(time_dilation_avg_slow, time_dilation_avg) + GLOB.glide_size_multiplier = (current_byondtime - last_tick_byond_time) / (current_realtime - last_tick_realtime) else first_run = FALSE last_tick_realtime = current_realtime diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index b8a901ccf321..4d43b47eac0e 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -1,5 +1,7 @@ /atom/movable layer = OBJ_LAYER + appearance_flags = TILE_BOUND|PIXEL_SCALE|LONG_GLIDE + glide_size = 8 var/last_move_dir = null var/anchored = FALSE var/drag_delay = 3 //delay (in deciseconds) added to mob's move_delay when pulling it. @@ -31,6 +33,11 @@ ///Highest-intensity light affecting us, which determines our visibility. var/affecting_dynamic_lumi = 0 + var/moving_diagonally + + /// Whether this atom should have its dir automatically changed when it moves. Setting this to FALSE allows for things such as directional windows to retain dir on moving without snowflake code all of the place. + var/set_dir_on_move = TRUE + //=========================================================================== /atom/movable/Destroy(force) for(var/atom/movable/I in contents) @@ -235,8 +242,9 @@ */ /atom/movable/proc/abstract_move(atom/new_loc) var/atom/old_loc = loc + var/direction = get_dir(old_loc, new_loc) loc = new_loc - Moved(old_loc) + Moved(old_loc, direction, TRUE) //called when a mob tries to breathe while inside us. /atom/movable/proc/handle_internal_lifeform(mob/lifeform_inside_me) @@ -355,3 +363,19 @@ set_light_range(range) set_light_power(power) set_light_color(color) + +/atom/movable/proc/set_glide_size(target = 8) + SEND_SIGNAL(src, COMSIG_MOVABLE_UPDATE_GLIDE_SIZE, target) + glide_size = target + +/mob/set_glide_size(target) + . = ..() + + if(pulling) + pulling.set_glide_size(target) + +/obj/set_glide_size(target) + . = ..() + + if(buckled_mob) + buckled_mob.set_glide_size(target) diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index a5a5ca40d8fb..dc8c0b1ab929 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -136,8 +136,6 @@ var/map_specific_decoration = FALSE /// color of the blood on us if there's any. var/blood_color = "" - /// taken from blood.dm - appearance_flags = KEEP_TOGETHER /// lets us know if the item is an objective or not var/is_objective = FALSE diff --git a/code/game/objects/structures/stool_bed_chair_nest/bed.dm b/code/game/objects/structures/stool_bed_chair_nest/bed.dm index 7979994915f4..de0de3f648a2 100644 --- a/code/game/objects/structures/stool_bed_chair_nest/bed.dm +++ b/code/game/objects/structures/stool_bed_chair_nest/bed.dm @@ -70,6 +70,12 @@ update_icon() +/obj/structure/bed/set_glide_size(target) + . = ..() + + if(buckled_bodybag) + buckled_bodybag.set_glide_size(target) + //Unsafe proc /obj/structure/bed/proc/do_buckle_bodybag(obj/structure/closet/bodybag/B, mob/user) B.visible_message(SPAN_NOTICE("[user] buckles [B] to [src]!")) @@ -115,11 +121,10 @@ return 0 /obj/structure/bed/proc/handle_buckled_bodybag_movement(NewLoc, direct) - if(!(direct & (direct - 1))) //Not diagonal move. the obj's diagonal move is split into two cardinal moves and those moves will handle the buckled bodybag's movement. - if(!buckled_bodybag.Move(NewLoc, direct)) - forceMove(buckled_bodybag.loc) - last_move_dir = buckled_bodybag.last_move_dir - return 0 + if(!buckled_bodybag.Move(NewLoc, direct)) + forceMove(buckled_bodybag.loc) + last_move_dir = buckled_bodybag.last_move_dir + return 0 return 1 /obj/structure/bed/roller/BlockedPassDirs(atom/movable/mover, target_dir) diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index 837610d5d7fe..b472480414c1 100644 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -206,7 +206,7 @@ return FALSE // if we are thrown, moved, dragged, or in any other way abused by code - check our diagonals - if(!mover.move_intentionally) + if(!mover.move_intentionally || (fdir == NORTHEAST || fdir == NORTHWEST || fdir == SOUTHEAST || fdir == SOUTHWEST)) // Check objects in adjacent turf EAST/WEST if(fd1 && fd1 != fdir) T = get_step(mover, fd1) diff --git a/code/modules/keybindings/bindings_atom.dm b/code/modules/keybindings/bindings_atom.dm index 82c3d4699f5c..bf9940776605 100644 --- a/code/modules/keybindings/bindings_atom.dm +++ b/code/modules/keybindings/bindings_atom.dm @@ -1,28 +1,22 @@ // You might be wondering why this isn't client level. If focus is null, we don't want you to move. // Only way to do that is to tie the behavior into the focus's keyLoop(). +// You might be wondering why this isn't client level. If focus is null, we don't want you to move. +// Only way to do that is to tie the behavior into the focus's keyLoop(). /atom/movable/keyLoop(client/user) var/movement_dir = NONE for(var/_key in user.keys_held) - if(user.movement_keys[_key]) - movement_dir = user.movement_keys[_key] - - if(!movement_dir) - return - - /* + movement_dir = movement_dir | user.movement_keys[_key] if(user.next_move_dir_add) movement_dir |= user.next_move_dir_add if(user.next_move_dir_sub) - movement_dir &= ~user.next_move_dir_sub*/ + movement_dir &= ~user.next_move_dir_sub // Sanity checks in case you hold left and right and up to make sure you only go up if((movement_dir & NORTH) && (movement_dir & SOUTH)) movement_dir &= ~(NORTH|SOUTH) if((movement_dir & EAST) && (movement_dir & WEST)) movement_dir &= ~(EAST|WEST) - movement_dir = turn(movement_dir, -dir2angle(user.dir)) //By doing this we ensure that our input direction is offset by the client (camera) direction - // This is called from Subsystem, and as such usr is unset. // You hopefully don't it but it might go against legacy code expectations. usr = user.mob @@ -30,4 +24,3 @@ keybind_face_direction(movement_dir) else user.Move(get_step(src, movement_dir), movement_dir) - diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm index 21a992693aa8..62fb4b0bf94c 100644 --- a/code/modules/mob/dead/observer/observer.dm +++ b/code/modules/mob/dead/observer/observer.dm @@ -435,26 +435,35 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp ghost.can_reenter_corpse = FALSE nest.ghost_of_buckled_mob = ghost -/mob/dead/observer/Move(atom/newloc, direct) +/mob/dead/observer/Move(atom/newloc, direct, glide_size_override = 32) following = null var/area/last_area = get_area(loc) if(updatedir) setDir(direct)//only update dir if we actually need it, so overlays won't spin on base sprites that don't have directions of their own + if(glide_size_override) + set_glide_size(glide_size_override) + if(newloc) abstract_move(newloc) else - abstract_move(get_turf(src)) //Get out of closets and such as a ghost + var/turf/destination = get_turf(src) + if((direct & NORTH) && y < world.maxy) - y++ + destination = get_step(destination, NORTH) + else if((direct & SOUTH) && y > 1) - y-- + destination = get_step(destination, SOUTH) + if((direct & EAST) && x < world.maxx) - x++ + destination = get_step(destination, EAST) + else if((direct & WEST) && x > 1) - x-- + destination = get_step(destination, WEST) + + abstract_move(destination) - var/turf/new_turf = locate(x, y, z) + var/turf/new_turf = get_turf(src) if(!new_turf) return diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm index 4416ba74fda3..1945ff39da7a 100644 --- a/code/modules/mob/living/carbon/human/human_defines.dm +++ b/code/modules/mob/living/carbon/human/human_defines.dm @@ -154,7 +154,6 @@ //Taken from update_icons var/list/overlays_standing[TOTAL_LAYERS] var/hardcore = FALSE //If TRUE, removes the body upon unrevivable death (for WO) - appearance_flags = KEEP_TOGETHER | TILE_BOUND throw_range = 4 // Humans can't be thrown that far var/datum/action/human_action/activable/selected_ability diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 059b50e4e9ee..7e1a176c2ebf 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -166,7 +166,7 @@ return -/mob/living/Move(NewLoc, direct) +/mob/living/Move(NewLoc, direct, glide_size_override) if (buckled && buckled.loc != NewLoc) //not updating position if (!buckled.anchored) return buckled.Move(NewLoc, direct) @@ -204,7 +204,7 @@ else if(get_dist(src, pulling) > 1 || ((pull_dir - 1) & pull_dir)) //puller and pullee more than one tile away or in diagonal position var/pulling_dir = get_dir(pulling, T) pulling.Move(T, pulling_dir) //the pullee tries to reach our previous position - if(pulling && get_dist(src, pulling) > 1) //the pullee couldn't keep up + if(pulling && get_dist(src, pulling) > 1 && !moving_diagonally) //the pullee couldn't keep up stop_pulling() else var/mob/living/pmob = pulling @@ -213,7 +213,7 @@ if(!(flags_atom & DIRLOCK)) setDir(turn(direct, 180)) //face the pullee - if(pulledby && get_dist(src, pulledby) > 1)//separated from our puller and not in the middle of a diagonal move. + if(pulledby && get_dist(src, pulledby) > 1 && !moving_diagonally)//separated from our puller and not in the middle of a diagonal move. pulledby.stop_pulling() if (s_active && !( s_active in contents ) && get_turf(s_active) != get_turf(src)) //check !( s_active in contents ) first so we hopefully don't have to call get_turf() so much. @@ -315,6 +315,9 @@ if(.) reset_view(destination) +#define SWAPPING 1 +#define PHASING 2 + /mob/living/Collide(atom/movable/AM) if(buckled || now_pushing) return @@ -371,13 +374,15 @@ return if(!L.buckled && !L.anchored) - var/mob_swap + var/mob_swap = NONE //the puller can always swap with its victim if on grab intent if(L.pulledby == src && a_intent == INTENT_GRAB) - mob_swap = 1 + mob_swap = SWAPPING //restrained people act if they were on 'help' intent to prevent a person being pulled from being separated from their puller else if((L.is_mob_restrained() || L.a_intent == INTENT_HELP) && (is_mob_restrained() || a_intent == INTENT_HELP)) - mob_swap = 1 + mob_swap = SWAPPING + if(moving_diagonally && (get_dir(src, L) in GLOB.cardinals) && get_step(src, dir).Enter(src, loc)) + mob_swap = PHASING if(mob_swap) //switch our position with L if(loc && !loc.Adjacent(L.loc)) @@ -389,8 +394,11 @@ L.add_temp_pass_flags(PASS_MOB_THRU) add_temp_pass_flags(PASS_MOB_THRU) - L.Move(oldloc) Move(oldLloc) + if(moving_diagonally) + moving_diagonally = FALSE + if(mob_swap == SWAPPING) + L.Move(oldloc) remove_temp_pass_flags(PASS_MOB_THRU) L.remove_temp_pass_flags(PASS_MOB_THRU) @@ -400,11 +408,14 @@ now_pushing = FALSE - if(!(L.status_flags & CANPUSH)) + if(!(L.status_flags & CANPUSH) || moving_diagonally) return ..() +#undef SWAPPING +#undef PHASING + /mob/living/launch_towards(datum/launch_metadata/LM) if(src) SEND_SIGNAL(src, COMSIG_MOB_MOVE_OR_LOOK, TRUE, dir, dir) diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm index 3e765e167ec1..25537aea3fdc 100644 --- a/code/modules/mob/mob_defines.dm +++ b/code/modules/mob/mob_defines.dm @@ -236,7 +236,6 @@ can_block_movement = TRUE - appearance_flags = TILE_BOUND var/mouse_icon = null ///the mob's tgui player panel diff --git a/code/modules/mob/mob_movement.dm b/code/modules/mob/mob_movement.dm index c3266982b20d..09617aa54026 100644 --- a/code/modules/mob/mob_movement.dm +++ b/code/modules/mob/mob_movement.dm @@ -101,6 +101,8 @@ next_move_dir_add = 0 next_move_dir_sub = 0 + var/old_move_delay = next_movement + next_movement = world.time + world.tick_lag if(!direct) @@ -124,7 +126,6 @@ return if(isobserver(mob)) //Ghosts are snowflakes unfortunately - next_movement = world.time + move_delay return mob.Move(n, direct) if(SEND_SIGNAL(mob, COMSIG_CLIENT_MOB_MOVE, n, direct) & COMPONENT_OVERRIDE_MOVE) @@ -197,8 +198,27 @@ if(mob.confused) mob.Move(get_step(mob, pick(cardinal))) else + + var/new_glide_size = DELAY_TO_GLIDE_SIZE(move_delay * ( (NSCOMPONENT(direct) && EWCOMPONENT(direct)) ? sqrt(2) : 1 ) ) + mob.set_glide_size(new_glide_size) // set it now in case of pulled objects + + //If the move was recent, count using old_move_delay + //We want fractional behavior and all + if(old_move_delay + world.tick_lag > world.time) + //Yes this makes smooth movement stutter if add_delay is too fractional + //Yes this is better then the alternative + next_movement = old_move_delay + else + next_movement = world.time + . = ..() + if((direct & (direct - 1)) && mob.loc == n) //moved diagonally successfully + move_delay *= sqrt(2) + + var/after_glide = DELAY_TO_GLIDE_SIZE(move_delay) + mob.set_glide_size(after_glide) + if (mob.tile_contents) mob.tile_contents = list() if(.) @@ -208,7 +228,7 @@ mob.update_clone() mob.move_intentionally = FALSE moving = FALSE - next_movement = world.time + move_delay + next_movement += move_delay return ///Process_Spacemove diff --git a/code/modules/movement/movement.dm b/code/modules/movement/movement.dm index da0c76cba9d5..193e3569ced8 100644 --- a/code/modules/movement/movement.dm +++ b/code/modules/movement/movement.dm @@ -55,7 +55,7 @@ return NO_BLOCKED_MOVEMENT -/atom/movable/Move(NewLoc, direct) +/atom/movable/Move(NewLoc, direct, glide_size_override) // If Move is not valid, exit if (SEND_SIGNAL(src, COMSIG_MOVABLE_PRE_MOVE, NewLoc) & COMPONENT_CANCEL_MOVE) return FALSE @@ -63,11 +63,68 @@ var/atom/oldloc = loc var/old_dir = dir - . = ..() + if(glide_size_override) + set_glide_size(glide_size_override) + + 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(!. && set_dir_on_move) + setDir(first_step_dir) + moving_diagonally = 0 + return + if (flags_atom & DIRLOCK) setDir(old_dir) else if(old_dir != direct) setDir(direct) + if(glide_size_override) + set_glide_size(glide_size_override) l_move_time = world.time if ((oldloc != loc && oldloc && oldloc.z == z)) last_move_dir = get_dir(oldloc, loc) diff --git a/code/modules/vehicles/multitile/multitile_movement.dm b/code/modules/vehicles/multitile/multitile_movement.dm index b5f308144707..b96ec656eb50 100644 --- a/code/modules/vehicles/multitile/multitile_movement.dm +++ b/code/modules/vehicles/multitile/multitile_movement.dm @@ -43,6 +43,12 @@ var/success = FALSE + if(direction & (direction - 1)) + if(direction & EAST) + direction &= ~(EAST) + if(direction & WEST) + direction &= ~(WEST) + if(dir == turn(direction, 180) || dir == direction) var/old_dir = dir success = try_move(direction) diff --git a/code/modules/vehicles/powerloader.dm b/code/modules/vehicles/powerloader.dm index 6992269c7566..b2247b17db10 100644 --- a/code/modules/vehicles/powerloader.dm +++ b/code/modules/vehicles/powerloader.dm @@ -47,12 +47,13 @@ return if(world.time > l_move_time + move_delay) if(dir != direction) - l_move_time = world.time setDir(direction) handle_rotation() - pick(playsound(src.loc, 'sound/mecha/powerloader_turn.ogg', 25, 1), playsound(src.loc, 'sound/mecha/powerloader_turn2.ogg', 25, 1)) + if(!(dir & (dir - 1))) // too much noise when moving diagonally, otherwise + pick(playsound(src.loc, 'sound/mecha/powerloader_turn.ogg', 25, 1), playsound(src.loc, 'sound/mecha/powerloader_turn2.ogg', 25, 1)) . = TRUE else + set_glide_size(DELAY_TO_GLIDE_SIZE(move_delay + 1)) . = step(src, direction) if(.) pick(playsound(loc, 'sound/mecha/powerloader_step.ogg', 25), playsound(loc, 'sound/mecha/powerloader_step2.ogg', 25)) @@ -60,13 +61,13 @@ /obj/vehicle/powerloader/handle_rotation() if(buckled_mob) buckled_mob.setDir(dir) - switch(dir) - if(EAST) - buckled_mob.pixel_x = 7 - if(WEST) - buckled_mob.pixel_x = -7 - else - buckled_mob.pixel_x = 0 + if(dir & EAST) + buckled_mob.pixel_x = 7 + return + if(dir & WEST) + buckled_mob.pixel_x = -7 + return + buckled_mob.pixel_x = 0 /obj/vehicle/powerloader/explode() new wreckage(loc) diff --git a/code/modules/vehicles/vehicle.dm b/code/modules/vehicles/vehicle.dm index 5192a6834050..14674af71750 100644 --- a/code/modules/vehicles/vehicle.dm +++ b/code/modules/vehicles/vehicle.dm @@ -4,7 +4,6 @@ layer = ABOVE_MOB_LAYER //so it sits above objects including mobs density = TRUE anchored = TRUE - animate_movement = 1 can_buckle = TRUE // The mobs that are in each position/seat of the vehicle