Skip to content

Commit

Permalink
Movement delay & smoothness refactor. (#2294)
Browse files Browse the repository at this point in the history
* Movement delay & smoothness refactor.

1: kills tick compensation. It was a broken system that could best be summed up as "old coders threw random formulas at movement code until they got a movement speed they were happy with".
2: Movement now uses glide size to be SMOOTH AS HELL.
3: client FPS had to be disabled due to point 2, because of https://secure.byond.com/forum/?post=2241289

Still WiP and probably pretty broken.

* Improvements.

* Many many fixes.

* Properly disable fps preference.

* Movement loop!

Implements a movement loop. Instead of the client repeatedly sending movement commands, the server keeps track of the client's movement buttons and sends the movement commands itself.

This helps with responsiveness, and also makes diagonal movement possible (enabled only for ghosts)

* Actually give ghosts movement delays.

* Review comments.

* Review comment
  • Loading branch information
PJB3005 authored and NanakoAC committed Nov 9, 2018
1 parent 8591e38 commit ff1af3d
Show file tree
Hide file tree
Showing 85 changed files with 957 additions and 460 deletions.
6 changes: 6 additions & 0 deletions cev_eris.dme
Expand Up @@ -44,6 +44,7 @@
#include "code\__defines\misc.dm"
#include "code\__defines\mob_stats.dm"
#include "code\__defines\mobs.dm"
#include "code\__defines\movement.dm"
#include "code\__defines\process_scheduler.dm"
#include "code\__defines\qdel.dm"
#include "code\__defines\reagents.dm"
Expand Down Expand Up @@ -75,6 +76,7 @@
#include "code\_helpers\areas.dm"
#include "code\_helpers\atmospherics.dm"
#include "code\_helpers\atom_movables.dm"
#include "code\_helpers\delays.dm"
#include "code\_helpers\files.dm"
#include "code\_helpers\functional.dm"
#include "code\_helpers\game.dm"
Expand Down Expand Up @@ -1186,6 +1188,10 @@
#include "code\modules\client\preferences_savefile.dm"
#include "code\modules\client\preferences_spawnpoints.dm"
#include "code\modules\client\ui_style.dm"
#include "code\modules\client\edge_sliding\move_loop.dm"
#include "code\modules\client\edge_sliding\keystate\1constants.dm"
#include "code\modules\client\edge_sliding\keystate\1definitions.dm"
#include "code\modules\client\edge_sliding\keystate\KeyState.dm"
#include "code\modules\client\preference_setup\preference_setup.dm"
#include "code\modules\client\preference_setup\preview.dm"
#include "code\modules\client\preference_setup\antagonism\01_candidacy.dm"
Expand Down
5 changes: 3 additions & 2 deletions code/ZAS/Airflow.dm
Expand Up @@ -177,8 +177,9 @@ obj/item/check_airflow_movable(n)
if(!istype(loc, /turf))
return
step_towards(src, src.airflow_dest)
if(ismob(src) && src:client)
src:client:move_delay = world.time + vsc.airflow_mob_slowdown
if(ismob(src))
var/mob/m = src
m.setMoveCooldown(vsc.airflow_mob_slowdown)
airflow_dest = null
airflow_speed = 0
airflow_time = 0
Expand Down
9 changes: 9 additions & 0 deletions code/__defines/movement.dm
@@ -0,0 +1,9 @@
#define MOVE_DELAY_BASE 1.1

//Glidesize
#define FRACTIONAL_GLIDESIZES 1
#ifdef FRACTIONAL_GLIDESIZES
#define DELAY2GLIDESIZE(delay) (world.icon_size / max(Ceiling(delay / world.tick_lag), 1))
#else
#define DELAY2GLIDESIZE(delay) (Ceiling(world.icon_size / max(Ceiling(delay / world.tick_lag), 1)))
#endif
32 changes: 32 additions & 0 deletions code/_helpers/delays.dm
@@ -0,0 +1,32 @@
//////////////////////////////////
// /vg/ MODULARIZED DELAYS - by N3X15
//////////////////////////////////

// This number's kinda arbitrary but whatever.
#define DELAY_CONTROLLER_DEFAULT_MAX 10000
#define DELAY_CONTROLLER_DEFAULT_MIN 0.3

// Reduces duplicated code by quite a bit.
/datum/delay_controller
// Delay clamps (for adminbus, effects)
var/min_delay = DELAY_CONTROLLER_DEFAULT_MIN
var/max_delay = DELAY_CONTROLLER_DEFAULT_MAX

var/next_allowed = 0

/datum/delay_controller/New(var/min=DELAY_CONTROLLER_DEFAULT_MIN, var/max=DELAY_CONTROLLER_DEFAULT_MAX)
min_delay = min
max_delay = max

/datum/delay_controller/proc/setDelay(var/delay)
next_allowed = world.time + Clamp(delay, min_delay, max_delay)

/datum/delay_controller/proc/setDelayMin(var/delay)
next_allowed = max(world.time + Clamp(delay, min_delay, max_delay), next_allowed)

/datum/delay_controller/proc/addDelay(var/delay)
var/current_delay = max(0, next_allowed - world.time)
setDelay(current_delay + delay)

/datum/delay_controller/proc/isBlocked()
return next_allowed > world.time
4 changes: 2 additions & 2 deletions code/_onclick/hud/parallax.dm
Expand Up @@ -25,12 +25,12 @@
/mob
var/obj/parallax/parallax

/mob/Move()
/mob/Move(NewLoc, Dir = 0, step_x = 0, step_y = 0, var/glide_size_override = 0)
. = ..()
if(. && parallax)
parallax.update()

/mob/forceMove()
/mob/forceMove(atom/destination, var/special_event, glide_size_override=0)
. = ..()
if(. && parallax)
parallax.update()
Expand Down
8 changes: 2 additions & 6 deletions code/controllers/configuration.dm
Expand Up @@ -43,10 +43,9 @@ var/list/storyteller_cache = list()
var/protect_roles_from_antagonist = 0// If security and such can be traitor/cult/other
var/allow_Metadata = 0 // Metadata is supported.
var/popup_admin_pm = 0 //adminPMs to non-admins show in a pop-up 'reply' window when set to 1.
var/fps = 20
var/fps = 30
var/tick_limit_mc_init = TICK_LIMIT_MC_INIT_DEFAULT //SSinitialization throttling
var/Ticklag = 0.9
var/Tickcomp = 0
var/Ticklag = 0.3
var/socket_talk = 0 // use socket_talk to communicate with other processes
var/list/resource_urls = null
var/antag_hud_allowed = 0 // Ghosts can turn on Antagovision to see a HUD of who is the bad guys this round.
Expand Down Expand Up @@ -519,9 +518,6 @@ var/list/storyteller_cache = list()
if("socket_talk")
socket_talk = text2num(value)

if("tickcomp")
Tickcomp = 1

if("humans_need_surnames")
humans_need_surnames = 1

Expand Down
4 changes: 2 additions & 2 deletions code/datums/observation/moved.dm
Expand Up @@ -39,13 +39,13 @@ GLOBAL_DATUM_INIT(moved_event, /decl/observ/moved, new)
GLOB.moved_event.unregister(src, am, /atom/movable/proc/recursive_move)

// Entered() typically lifts the moved event, but in the case of null-space we'll have to handle it.
/atom/movable/Move()
/atom/movable/Move(NewLoc, Dir = 0, step_x = 0, step_y = 0, var/glide_size_override = 0)
var/old_loc = loc
. = ..()
if(. && !loc)
GLOB.moved_event.raise_event(src, old_loc, null)

/atom/movable/forceMove(atom/destination)
/atom/movable/forceMove(atom/destination, var/special_event, glide_size_override=0)
var/old_loc = loc
. = ..()
if(. && !loc)
Expand Down
2 changes: 1 addition & 1 deletion code/game/atoms.dm
@@ -1,7 +1,7 @@
/atom
layer = TURF_LAYER
plane = GAME_PLANE
appearance_flags = TILE_BOUND|PIXEL_SCALE
appearance_flags = TILE_BOUND|PIXEL_SCALE|LONG_GLIDE
var/level = 2
var/flags = 0
var/list/fingerprints
Expand Down
76 changes: 75 additions & 1 deletion code/game/atoms_movable.dm
Expand Up @@ -51,10 +51,13 @@
/atom/movable/proc/entered_with_container(var/atom/old_loc)
return

/atom/movable/proc/forceMove(atom/destination, var/special_event)
/atom/movable/proc/forceMove(atom/destination, var/special_event, glide_size_override=0)
if(loc == destination)
return 0

if (glide_size_override)
set_glide_size(glide_size_override)

var/is_origin_turf = isturf(loc)
var/is_destination_turf = isturf(destination)
// It is a new area if:
Expand Down Expand Up @@ -296,3 +299,74 @@
return null
return text2num(pickweight(candidates))


/atom/movable/proc/set_glide_size(glide_size_override = 0, var/min = 0.9, var/max = world.icon_size/2)
if (!glide_size_override || glide_size_override > max)
glide_size = 0
else
glide_size = max(min, glide_size_override)

for (var/atom/movable/AM in contents)
AM.set_glide_size(glide_size, min, max)


//This proc should never be overridden elsewhere at /atom/movable to keep directions sane.
// Spoiler alert: it is, in moved.dm
/atom/movable/Move(NewLoc, Dir = 0, step_x = 0, step_y = 0, var/glide_size_override = 0)
if (glide_size_override > 0)
set_glide_size(glide_size_override)

// To prevent issues, diagonal movements are broken up into two cardinal movements.

// Is this a diagonal movement?
if (Dir & (Dir - 1))
if (Dir & NORTH)
if (Dir & EAST)
// Pretty simple really, try to move north -> east, else try east -> north
// Pretty much exactly the same for all the other cases here.
if (step(src, NORTH))
step(src, EAST)
else
if (step(src, EAST))
step(src, NORTH)
else
if (Dir & WEST)
if (step(src, NORTH))
step(src, WEST)
else
if (step(src, WEST))
step(src, NORTH)
else
if (Dir & SOUTH)
if (Dir & EAST)
if (step(src, SOUTH))
step(src, EAST)
else
if (step(src, EAST))
step(src, SOUTH)
else
if (Dir & WEST)
if (step(src, SOUTH))
step(src, WEST)
else
if (step(src, WEST))
step(src, SOUTH)
else
var/atom/A = src.loc

var/olddir = dir //we can't override this without sacrificing the rest of movable/New()
. = ..()
if(Dir != olddir)
dir = olddir
set_dir(Dir)

src.move_speed = world.time - src.l_move_time
src.l_move_time = world.time
src.m_flag = 1
if ((A != src.loc && A && A.z == src.z))
src.last_move = get_dir(A, src.loc)

// Wrapper of step() that also sets glide size to a specific value.
/proc/step_glide(var/atom/movable/am, var/dir, var/glide_size_override)
am.set_glide_size(glide_size_override)
return step(am, dir)
2 changes: 1 addition & 1 deletion code/game/gamemodes/events/meteors.dm
Expand Up @@ -268,7 +268,7 @@
z_original = z


/obj/effect/meteor/Move()
/obj/effect/meteor/Move(NewLoc, Dir = 0, step_x = 0, step_y = 0, var/glide_size_override = 0)
. = ..() //process movement...
move_count++
if(loc == dest)
Expand Down
6 changes: 3 additions & 3 deletions code/game/machinery/CableLayer.dm
Expand Up @@ -14,9 +14,9 @@
cable.amount = 100
..()

/obj/machinery/cablelayer/Move(new_turf,M_Dir)
..()
layCable(new_turf,M_Dir)
/obj/machinery/cablelayer/Move(NewLoc, Dir = 0, step_x = 0, step_y = 0, var/glide_size_override = 0)
. = ..()
layCable(NewLoc, Dir)

/obj/machinery/cablelayer/attack_hand(mob/user as mob)
if(!cable&&!on)
Expand Down
2 changes: 1 addition & 1 deletion code/game/machinery/doors/door.dm
Expand Up @@ -486,7 +486,7 @@
else
source.thermal_conductivity = initial(source.thermal_conductivity)

/obj/machinery/door/Move(new_loc, new_dir)
/obj/machinery/door/Move(NewLoc, Dir = 0, step_x = 0, step_y = 0, var/glide_size_override = 0)
//update_nearby_tiles()
. = ..()
if(width > 1)
Expand Down
2 changes: 1 addition & 1 deletion code/game/machinery/doors/multi_tile.dm
Expand Up @@ -6,7 +6,7 @@
..()
SetBounds()

/obj/machinery/door/airlock/multi_tile/Move()
/obj/machinery/door/airlock/multi_tile/Move(NewLoc, Dir = 0, step_x = 0, step_y = 0, var/glide_size_override = 0)
. = ..()
SetBounds()

Expand Down
6 changes: 3 additions & 3 deletions code/game/machinery/floorlayer.dm
Expand Up @@ -13,8 +13,8 @@
T = new/obj/item/stack/tile/floor(src)
..()

/obj/machinery/floorlayer/Move(new_turf,M_Dir)
..()
/obj/machinery/floorlayer/Move(NewLoc, Dir = 0, step_x = 0, step_y = 0, var/glide_size_override = 0)
. = ..()

if(on)
if(mode["dismantle"])
Expand All @@ -27,7 +27,7 @@
CollectTiles(old_turf)


old_turf = new_turf
old_turf = NewLoc

/obj/machinery/floorlayer/attack_hand(mob/user as mob)
on=!on
Expand Down
5 changes: 2 additions & 3 deletions code/game/machinery/pipe/construction.dm
Expand Up @@ -344,8 +344,8 @@ Buildable meters
//src.pipe_set_dir(get_pipe_dir())
return

/obj/item/pipe/Move()
..()
/obj/item/pipe/Move(NewLoc, Dir = 0, step_x = 0, step_y = 0, var/glide_size_override = 0)
. = ..()
if ((pipe_type in list (PIPE_SIMPLE_BENT, PIPE_SUPPLY_BENT, PIPE_SCRUBBERS_BENT, PIPE_HE_BENT, PIPE_INSULATED_BENT)) \
&& (src.dir in cardinal))
src.set_dir(src.dir|turn(src.dir, 90))
Expand All @@ -354,7 +354,6 @@ Buildable meters
set_dir(1)
else if(dir==8)
set_dir(4)
return

// returns all pipe's endpoints

Expand Down
10 changes: 5 additions & 5 deletions code/game/machinery/pipe/pipelayer.dm
Expand Up @@ -19,15 +19,15 @@
W = new(src)
..()

/obj/machinery/pipelayer/Move(new_turf,M_Dir)
..()
/obj/machinery/pipelayer/Move(NewLoc, Dir = 0, step_x = 0, step_y = 0, var/glide_size_override = 0)
. = ..()

if(on && a_dis)
dismantleFloor(old_turf)
layPipe(old_turf,M_Dir,old_dir)
layPipe(old_turf,Dir,old_dir)

old_turf = new_turf
old_dir = turn(M_Dir,180)
old_turf = NewLoc
old_dir = turn(Dir,180)

/obj/machinery/pipelayer/attack_hand(mob/user as mob)
if(!metal&&!on)
Expand Down
4 changes: 2 additions & 2 deletions code/game/machinery/teleporter.dm
Expand Up @@ -396,9 +396,9 @@
src.range--
return

/obj/effect/laser/Move()
/obj/effect/laser/Move(NewLoc, Dir = 0, step_x = 0, step_y = 0, var/glide_size_override = 0)
src.range--
return
return ..()

/atom/proc/laserhit(L as obj)
return 1
2 changes: 2 additions & 0 deletions code/game/mecha/combat/marauder.dm
Expand Up @@ -105,10 +105,12 @@
var/tmp_step_energy_drain = step_energy_drain
var/move_result = 0
if(internal_damage&MECHA_INT_CONTROL_LOST)
set_glide_size(DELAY2GLIDESIZE(step_in))
move_result = mechsteprand()
else if(src.dir!=direction)
move_result = mechturn(direction)
else
set_glide_size(DELAY2GLIDESIZE(step_in))
move_result = mechstep(direction)
if(move_result)
if(istype(src.loc, /turf/space))
Expand Down
5 changes: 3 additions & 2 deletions code/game/mecha/combat/phazon.dm
Expand Up @@ -35,9 +35,10 @@
if(can_move)
can_move = 0
flick("phazon-phase", src)
src.loc = get_step(src,src.dir)
var/delay = step_in*3
src.forceMove(get_step(src,src.dir), glide_size_override=DELAY2GLIDESIZE(delay))
src.use_power(phasing_energy_drain)
sleep(step_in*3)
sleep(delay)
can_move = 1
else
. = ..()
Expand Down

0 comments on commit ff1af3d

Please sign in to comment.