diff --git a/code/__DEFINES/move_force.dm b/code/__DEFINES/move_force.dm
new file mode 100644
index 00000000000..750821bd898
--- /dev/null
+++ b/code/__DEFINES/move_force.dm
@@ -0,0 +1,19 @@
+//Defaults
+#define MOVE_FORCE_DEFAULT 1000
+#define MOVE_RESIST_DEFAULT 1000
+#define PULL_FORCE_DEFAULT 1000
+
+ //Factors/modifiers
+#define MOVE_FORCE_PULL_RATIO 1 //Same move force to pull objects
+#define MOVE_FORCE_PUSH_RATIO 1 //Same move force to normally push
+#define MOVE_FORCE_FORCEPUSH_RATIO 2 //2x move force to forcefully push
+#define MOVE_FORCE_CRUSH_RATIO 3 //3x move force to do things like crush objects
+#define MOVE_FORCE_THROW_RATIO 1 //Same force throw as resist to throw objects
+#define MOVE_FORCE_OVERPOWERING MOVE_FORCE_DEFAULT * MOVE_FORCE_CRUSH_RATIO * 10
+#define MOVE_FORCE_EXTREMELY_STRONG MOVE_FORCE_DEFAULT * MOVE_FORCE_CRUSH_RATIO * 3
+#define MOVE_FORCE_VERY_STRONG (MOVE_FORCE_DEFAULT * MOVE_FORCE_CRUSH_RATIO) - 1
+#define MOVE_FORCE_STRONG MOVE_FORCE_DEFAULT * 2
+#define MOVE_FORCE_NORMAL MOVE_FORCE_DEFAULT
+#define MOVE_FORCE_WEAK MOVE_FORCE_DEFAULT / 2
+#define MOVE_FORCE_VERY_WEAK (MOVE_FORCE_DEFAULT / MOVE_FORCE_CRUSH_RATIO) + 1
+#define MOVE_FORCE_EXTREMELY_WEAK MOVE_FORCE_DEFAULT / (MOVE_FORCE_CRUSH_RATIO * 3)
\ No newline at end of file
diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm
index 6ce486bdc05..44522f89e8e 100644
--- a/code/__HELPERS/unsorted.dm
+++ b/code/__HELPERS/unsorted.dm
@@ -1495,7 +1495,7 @@ var/mob/dview/dview_mob = new
invisibility = 101
density = 0
- anchored = 1
+ move_resist = INFINITY
simulated = 0
see_in_dark = 1e6
diff --git a/code/_onclick/click.dm b/code/_onclick/click.dm
index 8ce2177751c..7b700ad4c6d 100644
--- a/code/_onclick/click.dm
+++ b/code/_onclick/click.dm
@@ -285,12 +285,12 @@
/mob/proc/CtrlClickOn(var/atom/A)
A.CtrlClick(src)
return
-/atom/proc/CtrlClick(var/mob/user)
- return
-/atom/movable/CtrlClick(var/mob/user)
- if(Adjacent(user))
- user.start_pulling(src)
+/atom/proc/CtrlClick(mob/user)
+ SEND_SIGNAL(src, COMSIG_CLICK_CTRL, user)
+ var/mob/living/ML = user
+ if(istype(ML))
+ ML.pulled(src)
/*
Alt click
diff --git a/code/controllers/subsystem/throwing.dm b/code/controllers/subsystem/throwing.dm
index b74e3a1a6c9..fb32991b42b 100644
--- a/code/controllers/subsystem/throwing.dm
+++ b/code/controllers/subsystem/throwing.dm
@@ -53,6 +53,7 @@ SUBSYSTEM_DEF(throwing)
var/dist_y
var/dx
var/dy
+ var/force = MOVE_FORCE_DEFAULT
var/pure_diagonal
var/diagonal_error
var/datum/callback/callback
diff --git a/code/game/atoms.dm b/code/game/atoms.dm
index 30b901875b3..58197cbadf1 100644
--- a/code/game/atoms.dm
+++ b/code/game/atoms.dm
@@ -86,7 +86,7 @@
if(opacity && isturf(loc))
var/turf/T = loc
T.has_opaque_atom = TRUE // No need to recalculate it in this case, it's guranteed to be on afterwards anyways.
-
+
if(loc)
loc.InitializedOn(src) // Used for poolcontroller / pool to improve performance greatly. However it also open up path to other usage of observer pattern on turfs.
@@ -324,7 +324,7 @@
// Atoms that return TRUE prevent RPDs placing any kind of pipes on their turf.
return FALSE
-/atom/proc/hitby(atom/movable/AM, skipcatch, hitpush, blocked)
+/atom/proc/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum)
if(density && !has_gravity(AM)) //thrown stuff bounces off dense stuff in no grav, unless the thrown stuff ends up inside what it hit(embedding, bola, etc...).
addtimer(CALLBACK(src, .proc/hitby_react, AM), 2)
diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm
index a5d2366c46c..46b86a05ecc 100644
--- a/code/game/atoms_movable.dm
+++ b/code/game/atoms_movable.dm
@@ -3,6 +3,9 @@
appearance_flags = TILE_BOUND
var/last_move = null
var/anchored = 0
+ var/move_resist = MOVE_RESIST_DEFAULT
+ var/move_force = MOVE_FORCE_DEFAULT
+ var/pull_force = PULL_FORCE_DEFAULT
// var/elevation = 2 - not used anywhere
var/move_speed = 10
var/l_move_time = 1
@@ -13,6 +16,8 @@
var/no_spin_thrown = 0
var/moved_recently = 0
var/mob/pulledby = null
+ var/atom/movable/pulling
+ var/canmove = 1
var/inertia_dir = 0
var/atom/inertia_last_loc
@@ -55,6 +60,70 @@
pulledby = null
return ..()
+/atom/movable/proc/start_pulling(atom/movable/AM, state, force = move_force, supress_message = FALSE)
+ if(QDELETED(AM))
+ return FALSE
+ if(!(AM.can_be_pulled(src, state, force)))
+ return FALSE
+
+ // If we're pulling something then drop what we're currently pulling and pull this instead.
+ if(pulling)
+ if(state == 0)
+ stop_pulling()
+ return FALSE
+ // Are we trying to pull something we are already pulling? Then enter grab cycle and end.
+ if(AM == pulling)
+ if(isliving(AM))
+ var/mob/living/AMob = AM
+ AMob.grabbedby(src)
+ return TRUE
+ stop_pulling()
+ if(AM.pulledby)
+ add_attack_logs(AM, AM.pulledby, "pulled from", ATKLOG_ALMOSTALL)
+ AM.pulledby.stop_pulling() //an object can't be pulled by two mobs at once.
+ pulling = AM
+ AM.pulledby = src
+ if(ismob(AM))
+ var/mob/M = AM
+ add_attack_logs(src, M, "passively grabbed", ATKLOG_ALMOSTALL)
+ if(!supress_message)
+ visible_message("[src] has grabbed [M] passively!")
+ return TRUE
+
+/atom/movable/proc/stop_pulling()
+ if(pulling)
+ pulling.pulledby = null
+ var/mob/living/ex_pulled = pulling
+ pulling = null
+ if(isliving(ex_pulled))
+ var/mob/living/L = ex_pulled
+ L.update_canmove()// mob gets up if it was lyng down in a chokehold
+
+/atom/movable/proc/check_pulling()
+ if(pulling)
+ var/atom/movable/pullee = pulling
+ if(pullee && get_dist(src, pullee) > 1)
+ stop_pulling()
+ return
+ if(!isturf(loc))
+ stop_pulling()
+ return
+ if(pullee && !isturf(pullee.loc) && pullee.loc != loc) //to be removed once all code that changes an object's loc uses forceMove().
+ log_game("DEBUG:[src]'s pull on [pullee] wasn't broken despite [pullee] being in [pullee.loc]. Pull stopped manually.")
+ stop_pulling()
+ return
+ if(pulling.anchored)
+ stop_pulling()
+ return
+
+/atom/movable/proc/can_be_pulled(user, grab_state, force)
+ if(src == user || !isturf(loc))
+ return FALSE
+ if(anchored || throwing)
+ return FALSE
+ if(force < (move_resist * MOVE_FORCE_PULL_RATIO))
+ return FALSE
+ return TRUE
// Used in shuttle movement and AI eye stuff.
// Primarily used to notify objects being moved by a shuttle/bluespace fuckup.
/atom/movable/proc/setLoc(var/T, var/teleported=0)
@@ -218,12 +287,12 @@
if(!QDELETED(hit_atom))
return hit_atom.hitby(src)
-/atom/movable/hitby(atom/movable/AM, skipcatch, hitpush = 1, blocked)
- if(!anchored && hitpush)
+/atom/movable/hitby(atom/movable/AM, skipcatch, hitpush = 1, blocked, datum/thrownthing/throwingdatum)
+ if(!anchored && hitpush && (!throwingdatum || (throwingdatum.force >= (move_resist * MOVE_FORCE_PUSH_RATIO))))
step(src, AM.dir)
..()
-/atom/movable/proc/throw_at(atom/target, range, speed, mob/thrower, spin = TRUE, diagonals_first = FALSE, var/datum/callback/callback)
+/atom/movable/proc/throw_at(atom/target, range, speed, mob/thrower, spin = TRUE, diagonals_first = FALSE, datum/callback/callback, force = INFINITY)
if(!target || (flags & NODROP) || speed <= 0)
return 0
@@ -330,6 +399,22 @@
return 0
return 1
+/atom/movable/proc/force_pushed(atom/movable/pusher, force = MOVE_FORCE_DEFAULT, direction)
+ return FALSE
+
+/atom/movable/proc/force_push(atom/movable/AM, force = move_force, direction, silent = FALSE)
+ . = AM.force_pushed(src, force, direction)
+ if(!silent && .)
+ visible_message("[src] forcefully pushes against [AM]!", "You forcefully push against [AM]!")
+
+/atom/movable/proc/move_crush(atom/movable/AM, force = move_force, direction, silent = FALSE)
+ . = AM.move_crushed(src, force, direction)
+ if(!silent && .)
+ visible_message("[src] crushes past [AM]!", "You crush [AM]!")
+
+/atom/movable/proc/move_crushed(atom/movable/pusher, force = MOVE_FORCE_DEFAULT, direction)
+ return FALSE
+
/atom/movable/CanPass(atom/movable/mover, turf/target, height=1.5)
if(buckled_mob == mover)
return 1
diff --git a/code/game/gamemodes/miniantags/bot_swarm/swarmer.dm b/code/game/gamemodes/miniantags/bot_swarm/swarmer.dm
index 9a8ead8b289..82a018d9902 100644
--- a/code/game/gamemodes/miniantags/bot_swarm/swarmer.dm
+++ b/code/game/gamemodes/miniantags/bot_swarm/swarmer.dm
@@ -311,7 +311,7 @@
to_chat(S, "Destroying this machine while it is occupied would result in biological and sentient resources to be harmed. Aborting.")
return
..()
-
+
/mob/living/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
S.DisperseTarget(src)
diff --git a/code/game/gamemodes/miniantags/revenant/revenant.dm b/code/game/gamemodes/miniantags/revenant/revenant.dm
index fe7d02488d6..45402fe5fc9 100644
--- a/code/game/gamemodes/miniantags/revenant/revenant.dm
+++ b/code/game/gamemodes/miniantags/revenant/revenant.dm
@@ -32,7 +32,7 @@
wander = 0
density = 0
flying = 1
- anchored = 1
+ move_resist = INFINITY
mob_size = MOB_SIZE_TINY
pass_flags = PASSTABLE | PASSGRILLE | PASSMOB
speed = 1
@@ -130,7 +130,7 @@
else
message_admins("Revenant was created but has no mind. Put a ghost inside, or a poll will be made in one minute.")
addtimer(CALLBACK(src, .proc/setupOrDelete), 1 MINUTES)
-
+
/mob/living/simple_animal/revenant/proc/setupOrDelete()
if(mind)
giveObjectivesandGoals()
diff --git a/code/game/gamemodes/miniantags/slaughter/bloodcrawl.dm b/code/game/gamemodes/miniantags/slaughter/bloodcrawl.dm
index 51f3d277598..d769c2f6807 100644
--- a/code/game/gamemodes/miniantags/slaughter/bloodcrawl.dm
+++ b/code/game/gamemodes/miniantags/slaughter/bloodcrawl.dm
@@ -154,7 +154,6 @@
name = "odd blood"
icon = 'icons/effects/effects.dmi'
icon_state = "nothing"
- var/canmove = 1
density = 0
anchored = 1
invisibility = 60
diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm
index 8d9b17ef7c2..902b0af771f 100644
--- a/code/game/machinery/doors/airlock.dm
+++ b/code/game/machinery/doors/airlock.dm
@@ -43,6 +43,7 @@ var/list/airlock_overlays = list()
name = "airlock"
icon = 'icons/obj/doors/airlocks/station/public.dmi'
icon_state = "closed"
+ anchored = 1
max_integrity = 300
integrity_failure = 70
damage_deflection = AIRLOCK_DAMAGE_DEFLECTION_N
diff --git a/code/game/machinery/magnet.dm b/code/game/machinery/magnet.dm
index 6a11662fc33..3af76895428 100644
--- a/code/game/machinery/magnet.dm
+++ b/code/game/machinery/magnet.dm
@@ -22,7 +22,7 @@
var/code = 0 // frequency code, they should be different unless you have a group of magnets working together or something
var/turf/center // the center of magnetic attraction
var/on = 0
- var/pulling = 0
+ var/magpulling = 0
// x, y modifiers to the center turf; (0, 0) is centered on the magnet, whereas (1, -1) is one tile right, one tile down
var/center_x = 0
@@ -154,10 +154,10 @@
/obj/machinery/magnetic_module/proc/magnetic_process() // proc that actually does the pulling
- if(pulling) return
+ if(magpulling) return
while(on)
- pulling = 1
+ magpulling = 1
center = locate(x+center_x, y+center_y, z)
if(center)
for(var/obj/M in orange(magnetic_field, center))
@@ -171,7 +171,7 @@
use_power(electricity_level * 5)
sleep(13 - electricity_level)
- pulling = 0
+ magpulling = 0
/obj/machinery/magnetic_controller
name = "Magnetic Control Console"
diff --git a/code/game/machinery/shieldgen.dm b/code/game/machinery/shieldgen.dm
index b655635ba1a..de3088ae60d 100644
--- a/code/game/machinery/shieldgen.dm
+++ b/code/game/machinery/shieldgen.dm
@@ -154,6 +154,7 @@
active = 1
update_icon()
+ anchored = 1
for(var/turf/target_tile in range(2, src))
if(istype(target_tile,/turf/space) && !(locate(/obj/machinery/shield) in target_tile))
@@ -166,6 +167,7 @@
active = 0
update_icon()
+ anchored = 0
for(var/obj/machinery/shield/shield_tile in deployed_shields)
qdel(shield_tile)
diff --git a/code/game/objects/effects/effects.dm b/code/game/objects/effects/effects.dm
index 5836136401e..a08e7fac021 100644
--- a/code/game/objects/effects/effects.dm
+++ b/code/game/objects/effects/effects.dm
@@ -6,6 +6,7 @@
icon = 'icons/effects/effects.dmi'
burn_state = LAVA_PROOF | FIRE_PROOF
resistance_flags = INDESTRUCTIBLE
+ anchored = 1
can_be_hit = FALSE
/obj/effect/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir)
diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm
index e3812286714..cd50f079b0c 100644
--- a/code/game/objects/items.dm
+++ b/code/game/objects/items.dm
@@ -532,10 +532,10 @@ var/global/image/fire_overlay = image("icon" = 'icons/goonstation/effects/fire.d
itempush = 0 // too light to push anything
return A.hitby(src, 0, itempush)
-/obj/item/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback)
+/obj/item/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback, force)
thrownby = thrower
callback = CALLBACK(src, .proc/after_throw, callback) //replace their callback with our own
- . = ..(target, range, speed, thrower, spin, diagonals_first, callback)
+ . = ..(target, range, speed, thrower, spin, diagonals_first, callback, force)
/obj/item/proc/after_throw(datum/callback/callback)
if(callback) //call the original callback
diff --git a/code/game/objects/obj_defense.dm b/code/game/objects/obj_defense.dm
index 572ff92c4fa..3ab8de69f7f 100644
--- a/code/game/objects/obj_defense.dm
+++ b/code/game/objects/obj_defense.dm
@@ -87,6 +87,17 @@
return TRUE
return FALSE
+/obj/force_pushed(atom/movable/pusher, force = MOVE_FORCE_DEFAULT, direction)
+ return TRUE
+
+/obj/move_crushed(atom/movable/pusher, force = MOVE_FORCE_DEFAULT, direction)
+ collision_damage(pusher, force, direction)
+ return TRUE
+
+/obj/proc/collision_damage(atom/movable/pusher, force = MOVE_FORCE_DEFAULT, direction)
+ var/amt = max(0, ((force - (move_resist * MOVE_FORCE_CRUSH_RATIO)) / (move_resist * MOVE_FORCE_CRUSH_RATIO)) * 10)
+ take_damage(amt, BRUTE)
+
/obj/blob_act(obj/structure/blob/B)
if(isturf(loc))
var/turf/T = loc
diff --git a/code/modules/mob/camera/camera.dm b/code/modules/mob/camera/camera.dm
index 38b5e81a87b..a5bfcb02e4b 100644
--- a/code/modules/mob/camera/camera.dm
+++ b/code/modules/mob/camera/camera.dm
@@ -3,7 +3,8 @@
/mob/camera
name = "camera mob"
density = 0
- anchored = 1
+ move_force = INFINITY
+ move_resist = INFINITY
status_flags = GODMODE // You can't damage it.
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
see_in_dark = 7
diff --git a/code/modules/mob/dead/death.dm b/code/modules/mob/dead/death.dm
index 48a04a834c7..704a61abbba 100644
--- a/code/modules/mob/dead/death.dm
+++ b/code/modules/mob/dead/death.dm
@@ -3,3 +3,6 @@
/mob/dead/gib() //ghosts can't be gibbed.
return
+
+/mob/dead
+ move_resist = INFINITY
\ No newline at end of file
diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm
index 7e583216475..cb2bc013154 100644
--- a/code/modules/mob/dead/observer/observer.dm
+++ b/code/modules/mob/dead/observer/observer.dm
@@ -13,7 +13,7 @@ var/list/image/ghost_darkness_images = list() //this is a list of images for thi
density = 0
canmove = 0
alpha = 127
- anchored = 1 // don't get pushed around
+ move_resist = INFINITY // don't get pushed around
invisibility = INVISIBILITY_OBSERVER
var/can_reenter_corpse
var/bootime = 0
@@ -706,6 +706,6 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
set name = "Mob spawners menu"
set desc = "See all currently available ghost spawners"
set category = "Ghost"
-
+
var/datum/spawners_menu/menu = new /datum/spawners_menu(src)
menu.ui_interact(src)
diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm
index e9b09ba1f2b..ca791c9e400 100644
--- a/code/modules/mob/living/carbon/carbon.dm
+++ b/code/modules/mob/living/carbon/carbon.dm
@@ -574,7 +574,7 @@ var/list/ventcrawl_machinery = list(/obj/machinery/atmospherics/unary/vent_pump,
if(thrown_thing)
visible_message("[src] has thrown [thrown_thing].")
newtonian_move(get_dir(target, src))
- thrown_thing.throw_at(target, thrown_thing.throw_range, thrown_thing.throw_speed, src)
+ thrown_thing.throw_at(target, thrown_thing.throw_range, thrown_thing.throw_speed, src, null, null, null, move_force)
/mob/living/carbon/can_use_hands()
if(handcuffed)
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index bf49d5b2598..62b9cf6cd65 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -50,7 +50,7 @@
if(ObjBump(A))
return
if(istype(A, /atom/movable))
- if(PushAM(A))
+ if(PushAM(A, move_force))
return
//Called when we bump into a mob
@@ -127,32 +127,41 @@
return
//Called when we want to push an atom/movable
-/mob/living/proc/PushAM(atom/movable/AM)
+/mob/living/proc/PushAM(atom/movable/AM, force = move_force)
if(now_pushing)
- return 1
+ return TRUE
if(moving_diagonally) // no pushing during diagonal moves
- return 1
+ return TRUE
if(!client && (mob_size < MOB_SIZE_SMALL))
return
- if(!AM.anchored)
- now_pushing = 1
- var/t = get_dir(src, AM)
- if(istype(AM, /obj/structure/window))
- var/obj/structure/window/W = AM
- if(W.fulltile)
- for(var/obj/structure/window/win in get_step(W, t))
- now_pushing = 0
- return
- if(pulling == AM)
- stop_pulling()
- var/current_dir
- if(isliving(AM))
- current_dir = AM.dir
- step(AM, t)
- if(current_dir)
- AM.setDir(current_dir)
- now_pushing = 0
-
+ now_pushing = TRUE
+ var/t = get_dir(src, AM)
+ var/push_anchored = FALSE
+ if((AM.move_resist * MOVE_FORCE_CRUSH_RATIO) <= force)
+ if(move_crush(AM, move_force, t))
+ push_anchored = TRUE
+ if((AM.move_resist * MOVE_FORCE_FORCEPUSH_RATIO) <= force) //trigger move_crush and/or force_push regardless of if we can push it normally
+ if(force_push(AM, move_force, t, push_anchored))
+ push_anchored = TRUE
+ if((AM.anchored && !push_anchored) || (force < (AM.move_resist * MOVE_FORCE_PUSH_RATIO)))
+ now_pushing = FALSE
+ return
+ if(istype(AM, /obj/structure/window))
+ var/obj/structure/window/W = AM
+ if(W.fulltile)
+ for(var/obj/structure/window/win in get_step(W,t))
+ now_pushing = FALSE
+ return
+ if(pulling == AM)
+ stop_pulling()
+ var/current_dir
+ if(isliving(AM))
+ current_dir = AM.dir
+ if(step(AM, t))
+ step(src, t)
+ if(current_dir)
+ AM.setDir(current_dir)
+ now_pushing = FALSE
/mob/living/Stat()
. = ..()
@@ -187,9 +196,20 @@
set name = "Pull"
set category = "Object"
- if(AM.Adjacent(src))
+ if(istype(AM) && Adjacent(AM))
start_pulling(AM)
- return
+ else
+ stop_pulling()
+
+/mob/living/stop_pulling()
+ ..()
+ if(pullin)
+ pullin.update_icon(src)
+
+/mob/living/verb/stop_pulling1()
+ set name = "Stop Pulling"
+ set category = "IC"
+ stop_pulling()
//same as above
/mob/living/pointed(atom/A as mob|obj|turf in view())
@@ -852,6 +872,37 @@
return 0
return 1
+/mob/living/start_pulling(atom/movable/AM, state, force = pull_force, supress_message = FALSE)
+ if(!AM || !src)
+ return FALSE
+ if(!(AM.can_be_pulled(src, state, force)))
+ return FALSE
+ if(incapacitated())
+ return
+ // If we're pulling something then drop what we're currently pulling and pull this instead.
+ AM.add_fingerprint(src)
+ if(pulling)
+ if(AM == pulling)// Are we trying to pull something we are already pulling? Then just stop here, no need to continue.
+ return
+ stop_pulling()
+ if(AM.pulledby)
+ visible_message("[src] has pulled [AM] from [AM.pulledby]'s grip.")
+ AM.pulledby.stop_pulling() //an object can't be pulled by two mobs at once.
+ pulling = AM
+ AM.pulledby = src
+ if(pullin)
+ pullin.update_icon(src)
+ if(ismob(AM))
+ var/mob/M = AM
+ if(!iscarbon(src))
+ M.LAssailant = null
+ else
+ M.LAssailant = usr
+
+/mob/living/proc/check_pull()
+ if(pulling && !(pulling in orange(1)))
+ stop_pulling()
+
/mob/living/proc/get_taste_sensitivity()
return 1
diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm
index f766a1f53ce..cfe35b58983 100644
--- a/code/modules/mob/living/living_defense.dm
+++ b/code/modules/mob/living/living_defense.dm
@@ -73,7 +73,7 @@
return 0
//this proc handles being hit by a thrown atom
-/mob/living/hitby(atom/movable/AM, skipcatch, hitpush = 1, blocked = 0)//Standardization and logging -Sieve
+/mob/living/hitby(atom/movable/AM, skipcatch, hitpush = 1, blocked = 0, datum/thrownthing/throwingdatum)//Standardization and logging -Sieve
if(istype(AM, /obj/item))
var/obj/item/I = AM
var/zone = ran_zone("chest", 65)//Hits a random part of the body, geared towards the chest
@@ -229,7 +229,7 @@
// End BS12 momentum-transfer code.
-/mob/living/proc/grabbedby(mob/living/carbon/user, supress_message = 0)
+/mob/living/proc/grabbedby(mob/living/carbon/user, supress_message = FALSE)
if(user == src || anchored)
return 0
if(!(status_flags & CANPUSH))
diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm
index 2f9ad47399b..239fec3b246 100644
--- a/code/modules/mob/living/silicon/ai/ai.dm
+++ b/code/modules/mob/living/silicon/ai/ai.dm
@@ -38,7 +38,7 @@ var/list/ai_verbs_default = list(
name = "AI"
icon = 'icons/mob/AI.dmi'//
icon_state = "ai"
- anchored = 1 // -- TLE
+ move_resist = MOVE_FORCE_VERY_STRONG
density = 1
status_flags = CANSTUN|CANPARALYSE|CANPUSH
mob_size = MOB_SIZE_LARGE
@@ -533,9 +533,14 @@ var/list/ai_verbs_default = list(
if(!isturf(loc)) // if their location isn't a turf
return // stop
- anchored = !anchored // Toggles the anchor
+ var/is_anchored = FALSE
+ if(move_resist == MOVE_FORCE_VERY_STRONG)
+ move_resist = MOVE_FORCE_VERY_STRONG
+ else
+ is_anchored = TRUE
+ move_resist = MOVE_FORCE_NORMAL
- to_chat(src, "[anchored ? "You are now anchored." : "You are now unanchored."]")
+ to_chat(src, "[is_anchored ? "You are now anchored." : "You are now unanchored."]")
/mob/living/silicon/ai/update_canmove()
return FALSE
diff --git a/code/modules/mob/living/silicon/pai/pai.dm b/code/modules/mob/living/silicon/pai/pai.dm
index eeaa920c83a..e7028ea4422 100644
--- a/code/modules/mob/living/silicon/pai/pai.dm
+++ b/code/modules/mob/living/silicon/pai/pai.dm
@@ -517,12 +517,8 @@
/mob/living/silicon/pai/Bumped()
return
-/mob/living/silicon/pai/start_pulling(var/atom/movable/AM)
- if(stat || sleeping || paralysis || weakened)
- return
- if(istype(AM,/obj/item))
- to_chat(src, "You are far too small to pull anything!")
- return
+/mob/living/silicon/pai/start_pulling(atom/movable/AM, state, force = move_force, supress_message = FALSE)
+ return FALSE
/mob/living/silicon/pai/update_canmove(delay_action_updates = 0)
. = ..()
diff --git a/code/modules/mob/living/simple_animal/bot/mulebot.dm b/code/modules/mob/living/simple_animal/bot/mulebot.dm
index 103e443d7e7..49a3b2718db 100644
--- a/code/modules/mob/living/simple_animal/bot/mulebot.dm
+++ b/code/modules/mob/living/simple_animal/bot/mulebot.dm
@@ -11,8 +11,8 @@
desc = "A Multiple Utility Load Effector bot."
icon_state = "mulebot0"
density = 1
- anchored = 1
- animate_movement=1
+ move_resist = MOVE_FORCE_STRONG
+ animate_movement = 1
health = 50
maxHealth = 50
damage_coeff = list(BRUTE = 0.5, BURN = 0.7, TOX = 0, CLONE = 0, STAMINA = 0, OXY = 0)
diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm
index 6a6e44a5852..870b3bf62cc 100644
--- a/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm
@@ -38,7 +38,9 @@
var/elimination = 0
var/anger_modifier = 0
var/obj/item/gps/internal_gps
- anchored = TRUE
+ move_force = MOVE_FORCE_OVERPOWERING
+ move_resist = MOVE_FORCE_OVERPOWERING
+ pull_force = MOVE_FORCE_OVERPOWERING
mob_size = MOB_SIZE_LARGE
layer = MOB_LAYER + 0.5 //Looks weird with them slipping under mineral walls and cameras and shit otherwise
mouse_opacity = MOUSE_OPACITY_OPAQUE // Easier to click on in melee, they're giant targets anyway
diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs.dm
index 39799d6e493..0f37482b3e2 100644
--- a/code/modules/mob/living/simple_animal/hostile/mining_mobs.dm
+++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs.dm
@@ -136,7 +136,9 @@
throw_message = "does nothing to the rocky hide of the"
aggro_vision_range = 9
idle_vision_range = 5
- anchored = 1 //Stays anchored until death as to be unpullable
+ move_force = MOVE_FORCE_VERY_STRONG
+ move_resist = MOVE_FORCE_VERY_STRONG
+ pull_force = MOVE_FORCE_VERY_STRONG
var/pre_attack = 0
loot = list(/obj/item/asteroid/goliath_hide{layer = 4.1})
@@ -152,7 +154,9 @@
icon_state = "Goliath_preattack"
/mob/living/simple_animal/hostile/asteroid/goliath/revive()
- anchored = 1
+ move_force = MOVE_FORCE_VERY_STRONG
+ move_resist = MOVE_FORCE_VERY_STRONG
+ pull_force = MOVE_FORCE_VERY_STRONG
..()
/mob/living/simple_animal/hostile/asteroid/goliath/OpenFire()
diff --git a/code/modules/mob/living/simple_animal/hostile/statue.dm b/code/modules/mob/living/simple_animal/hostile/statue.dm
index 88326747247..b293c1de974 100644
--- a/code/modules/mob/living/simple_animal/hostile/statue.dm
+++ b/code/modules/mob/living/simple_animal/hostile/statue.dm
@@ -42,7 +42,9 @@
see_invisible = SEE_INVISIBLE_OBSERVER_NOLIGHTING
sight = SEE_SELF|SEE_MOBS|SEE_OBJS|SEE_TURFS
- anchored = 1
+ move_force = MOVE_FORCE_EXTREMELY_STRONG
+ move_resist = MOVE_FORCE_EXTREMELY_STRONG
+ pull_force = MOVE_FORCE_EXTREMELY_STRONG
status_flags = GODMODE // Cannot push also
var/cannot_be_seen = 1
diff --git a/code/modules/mob/living/simple_animal/spawner.dm b/code/modules/mob/living/simple_animal/spawner.dm
index 22b5fb705a9..7a17dfb202b 100644
--- a/code/modules/mob/living/simple_animal/spawner.dm
+++ b/code/modules/mob/living/simple_animal/spawner.dm
@@ -10,7 +10,7 @@
var/mob_type = /mob/living/simple_animal/hostile/carp
var/spawn_text = "emerges from"
status_flags = 0
- anchored = 1
+ move_resist = MOVE_FORCE_VERY_STRONG
AIStatus = AI_OFF
a_intent = INTENT_HARM
stop_automated_movement = 1
diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm
index bfe9ac7484a..3de413ef8a1 100644
--- a/code/modules/mob/mob_defines.dm
+++ b/code/modules/mob/mob_defines.dm
@@ -35,7 +35,6 @@
var/obj/machinery/machine = null
var/other_mobs = null
var/memory = ""
- var/atom/movable/pulling = null
var/next_move = null
var/notransform = null //Carbon
var/other = 0.0
@@ -48,7 +47,6 @@
var/bhunger = 0 //Carbon
var/lying = 0
var/lying_prev = 0
- var/canmove = 1
var/lastpuke = 0
var/unacidable = 0
var/can_strip = 1
diff --git a/code/modules/mob/new_player/new_player.dm b/code/modules/mob/new_player/new_player.dm
index 6d636f0328e..ec57d9d6200 100644
--- a/code/modules/mob/new_player/new_player.dm
+++ b/code/modules/mob/new_player/new_player.dm
@@ -12,8 +12,6 @@
stat = 2
canmove = 0
- anchored = 1 // don't get pushed around
-
/mob/new_player/New()
GLOB.mob_list += src
diff --git a/code/modules/mob/pulling.dm b/code/modules/mob/pulling.dm
deleted file mode 100644
index f5cb9eecbe2..00000000000
--- a/code/modules/mob/pulling.dm
+++ /dev/null
@@ -1,46 +0,0 @@
-//this and stop_pulling really ought to be /mob/living procs
-/mob/proc/start_pulling(atom/movable/AM)
- if(src == AM) // Trying to pull yourself is a shortcut to stop pulling
- stop_pulling()
- return
- if(!AM || !isturf(AM.loc)) //if there's no object or the object being pulled is inside something: abort!
- return
- if(incapacitated())
- return
- if(!(AM.anchored))
- AM.add_fingerprint(src)
-
- // If we're pulling something then drop what we're currently pulling and pull this instead.
- if(pulling)
- // Are we trying to pull something we are already pulling? Then just stop here, no need to continue.
- if(AM == pulling)
- return
- stop_pulling()
- if(AM.pulledby)
- visible_message("[src] has pulled [AM] from [AM.pulledby]'s grip.")
- AM.pulledby.stop_pulling() //an object can't be pulled by two mobs at once.
-
- pulling = AM
- AM.pulledby = src
- if(pullin)
- pullin.update_icon(src)
- if(ismob(AM))
- var/mob/M = AM
- if(!iscarbon(src))
- M.LAssailant = null
- else
- M.LAssailant = usr
-
-/mob/verb/stop_pulling()
- set name = "Stop Pulling"
- set category = "IC"
-
- if(pulling)
- pulling.pulledby = null
- pulling = null
- if(pullin)
- pullin.update_icon(src)
-
-/mob/living/proc/check_pull()
- if(pulling && !(pulling in orange(1)))
- stop_pulling()
diff --git a/code/modules/recycling/conveyor2.dm b/code/modules/recycling/conveyor2.dm
index 64c201a319f..6e1fbb8a842 100644
--- a/code/modules/recycling/conveyor2.dm
+++ b/code/modules/recycling/conveyor2.dm
@@ -16,6 +16,7 @@ GLOBAL_LIST_INIT(conveyor_switches, list())
desc = "It's a conveyor belt, commonly used to transport large numbers of items elsewhere quite quickly."
layer = CONVEYOR_LAYER // so they appear under stuff but not below stuff like vents
anchored = TRUE
+ move_force = MOVE_FORCE_DEFAULT
var/operating = FALSE //NB: this can be TRUE while the belt doesn't go
var/forwards // The direction the conveyor sends you in
var/backwards // hopefully self-explanatory
@@ -183,9 +184,12 @@ GLOBAL_LIST_INIT(conveyor_switches, list())
..()
/obj/machinery/conveyor/proc/move_thing(atom/movable/AM)
+ if(move_force < (AM.move_resist))
+ return FALSE
if(!AM.anchored && AM.loc == loc)
step(AM, forwards)
+
/obj/machinery/conveyor/proc/can_conveyor_run()
if(stat & BROKEN)
return FALSE
diff --git a/paradise.dme b/paradise.dme
index a2e96d73ddf..f04d4909f96 100644
--- a/paradise.dme
+++ b/paradise.dme
@@ -44,6 +44,7 @@
#include "code\__DEFINES\MC.dm"
#include "code\__DEFINES\misc.dm"
#include "code\__DEFINES\mobs.dm"
+#include "code\__DEFINES\move_force.dm"
#include "code\__DEFINES\pda.dm"
#include "code\__DEFINES\pipes.dm"
#include "code\__DEFINES\preferences.dm"
@@ -1611,7 +1612,6 @@
#include "code\modules\mob\mob_helpers.dm"
#include "code\modules\mob\mob_movement.dm"
#include "code\modules\mob\mob_transformation_simple.dm"
-#include "code\modules\mob\pulling.dm"
#include "code\modules\mob\say.dm"
#include "code\modules\mob\status_procs.dm"
#include "code\modules\mob\transform_procs.dm"