diff --git a/_maps/RandomRuins/LavaRuins/lavaland_surface_ash_walker1.dmm b/_maps/RandomRuins/LavaRuins/lavaland_surface_ash_walker1.dmm
index ed5b07168f..1b4b6a5d43 100644
--- a/_maps/RandomRuins/LavaRuins/lavaland_surface_ash_walker1.dmm
+++ b/_maps/RandomRuins/LavaRuins/lavaland_surface_ash_walker1.dmm
@@ -266,7 +266,7 @@
/turf/open/indestructible/boss,
/area/ruin/unpowered/ash_walkers)
"aH" = (
-/mob/living/simple_animal/hostile/spawner/lavaland/ash_walker,
+/obj/structure/lavaland/ash_walker,
/turf/open/lava/smooth{
initial_gas_mix = "o2=14;n2=23;TEMP=300"
},
diff --git a/_maps/RandomZLevels/VR/snowdin_VR.dmm b/_maps/RandomZLevels/VR/snowdin_VR.dmm
index 8f6eda450c..0e67b7227c 100644
--- a/_maps/RandomZLevels/VR/snowdin_VR.dmm
+++ b/_maps/RandomZLevels/VR/snowdin_VR.dmm
@@ -3669,7 +3669,7 @@
/area/awaymission/snowdin/cave/cavern)
"im" = (
/obj/effect/decal/cleanable/blood/old,
-/mob/living/simple_animal/hostile/spawner/nether{
+/obj/structure/spawner/nether{
max_mobs = 5
},
/turf/open/floor/engine/cult{
@@ -8272,7 +8272,7 @@
/turf/open/floor/engine/cult,
/area/awaymission/snowdin/post/cavern2)
"sb" = (
-/mob/living/simple_animal/hostile/spawner/nether{
+/obj/structure/spawner/nether{
max_mobs = 4;
name = "weak netherworld link"
},
@@ -10173,7 +10173,7 @@
/obj/structure/cable/yellow{
icon_state = "2-8"
},
-/mob/living/simple_animal/hostile/spawner/nether{
+/obj/structure/spawner/nether{
max_mobs = 4;
name = "weak netherworld link"
},
diff --git a/_maps/RandomZLevels/caves.dmm b/_maps/RandomZLevels/caves.dmm
index a25808d933..66ab3e60a8 100644
--- a/_maps/RandomZLevels/caves.dmm
+++ b/_maps/RandomZLevels/caves.dmm
@@ -103,7 +103,7 @@
},
/area/awaymission/caves/BMP_asteroid/level_four)
"at" = (
-/mob/living/simple_animal/hostile/spawner/skeleton,
+/obj/structure/spawner/skeleton,
/turf/open/floor/plating/asteroid/basalt/lava{
initial_gas_mix = "n2=23;o2=14"
},
@@ -164,7 +164,7 @@
},
/area/awaymission/caves/BMP_asteroid/level_four)
"aB" = (
-/mob/living/simple_animal/hostile/spawner/skeleton,
+/obj/structure/spawner/skeleton,
/turf/open/floor/engine/cult{
initial_gas_mix = "n2=23;o2=14"
},
@@ -374,7 +374,7 @@
},
/area/awaymission/caves/BMP_asteroid/level_three)
"be" = (
-/mob/living/simple_animal/hostile/spawner/mining/goliath,
+/obj/structure/spawner/mining/goliath,
/turf/open/floor/plating/asteroid/basalt{
initial_gas_mix = "n2=23;o2=14"
},
@@ -504,7 +504,7 @@
/area/awaymission/caves/BMP_asteroid/level_three)
"bw" = (
/obj/effect/decal/cleanable/blood,
-/mob/living/simple_animal/hostile/spawner/skeleton,
+/obj/structure/spawner/skeleton,
/turf/open/floor/engine/cult{
initial_gas_mix = "n2=23;o2=14"
},
@@ -930,7 +930,7 @@
/turf/closed/wall,
/area/awaymission/caves/BMP_asteroid/level_two)
"cL" = (
-/mob/living/simple_animal/hostile/spawner/mining/basilisk,
+/obj/structure/spawner/mining/basilisk,
/turf/open/floor/plating/asteroid/basalt{
initial_gas_mix = "n2=23;o2=14"
},
@@ -1644,7 +1644,7 @@
/turf/open/floor/plasteel,
/area/awaymission/caves/listeningpost)
"fb" = (
-/mob/living/simple_animal/hostile/spawner/mining/hivelord,
+/obj/structure/spawner/mining/hivelord,
/turf/open/floor/plating/asteroid/basalt{
initial_gas_mix = "n2=23;o2=14"
},
@@ -2133,7 +2133,7 @@
/turf/open/floor/plasteel/recharge_floor,
/area/awaymission/caves/BMP_asteroid)
"gD" = (
-/mob/living/simple_animal/hostile/spawner/mining/hivelord,
+/obj/structure/spawner/mining/hivelord,
/turf/open/floor/plating/asteroid/basalt{
initial_gas_mix = "n2=23;o2=14"
},
@@ -2206,7 +2206,7 @@
},
/area/awaymission/caves/BMP_asteroid)
"gQ" = (
-/mob/living/simple_animal/hostile/spawner/mining/basilisk,
+/obj/structure/spawner/mining/basilisk,
/turf/open/floor/plating/asteroid/basalt{
initial_gas_mix = "n2=23;o2=14"
},
diff --git a/_maps/RandomZLevels/snowdin.dmm b/_maps/RandomZLevels/snowdin.dmm
index 525c8fcf7b..13171fddcc 100644
--- a/_maps/RandomZLevels/snowdin.dmm
+++ b/_maps/RandomZLevels/snowdin.dmm
@@ -3664,7 +3664,7 @@
/area/awaymission/snowdin/cave/cavern)
"im" = (
/obj/effect/decal/cleanable/blood/old,
-/mob/living/simple_animal/hostile/spawner/nether{
+/obj/structure/spawner/nether{
max_mobs = 5
},
/turf/open/floor/engine/cult{
@@ -8327,7 +8327,7 @@
/turf/open/floor/engine/cult,
/area/awaymission/snowdin/post/cavern2)
"sb" = (
-/mob/living/simple_animal/hostile/spawner/nether{
+/obj/structure/spawner/nether{
max_mobs = 4;
name = "weak netherworld link"
},
@@ -10237,7 +10237,7 @@
/obj/structure/cable/yellow{
icon_state = "2-8"
},
-/mob/living/simple_animal/hostile/spawner/nether{
+/obj/structure/spawner/nether{
max_mobs = 4;
name = "weak netherworld link"
},
diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm
index bccf1f28dd..5ca0c99a63 100644
--- a/code/__DEFINES/misc.dm
+++ b/code/__DEFINES/misc.dm
@@ -25,30 +25,6 @@ Will print: "/mob/living/carbon/human/death" (you can optionally embed it in a s
#define NOT_IMPLEMENTED "NOT_IMPLEMENTED"
-#define MIDNIGHT_ROLLOVER 864000 //number of deciseconds in a day
-
-#define JANUARY 1
-#define FEBRUARY 2
-#define MARCH 3
-#define APRIL 4
-#define MAY 5
-#define JUNE 6
-#define JULY 7
-#define AUGUST 8
-#define SEPTEMBER 9
-#define OCTOBER 10
-#define NOVEMBER 11
-#define DECEMBER 12
-
-//Select holiday names -- If you test for a holiday in the code, make the holiday's name a define and test for that instead
-#define NEW_YEAR "New Year"
-#define VALENTINES "Valentine's Day"
-#define APRIL_FOOLS "April Fool's Day"
-#define EASTER "Easter"
-#define HALLOWEEN "Halloween"
-#define CHRISTMAS "Christmas"
-#define FESTIVE_SEASON "Festive Season"
-
//Human Overlays Indexes/////////
//LOTS OF CIT CHANGES HERE. BE CAREFUL WHEN UPSTREAM ADDS MORE LAYERS
#define MUTATIONS_LAYER 31 //mutations. Tk headglows, cold resistance glow, etc
diff --git a/code/__DEFINES/move_force.dm b/code/__DEFINES/move_force.dm
new file mode 100644
index 0000000000..ec31388c72
--- /dev/null
+++ b/code/__DEFINES/move_force.dm
@@ -0,0 +1,20 @@
+//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/__DEFINES/time.dm b/code/__DEFINES/time.dm
index f13f13510b..e1fff1879e 100644
--- a/code/__DEFINES/time.dm
+++ b/code/__DEFINES/time.dm
@@ -1,3 +1,27 @@
+#define MIDNIGHT_ROLLOVER 864000 //number of deciseconds in a day
+
+#define JANUARY 1
+#define FEBRUARY 2
+#define MARCH 3
+#define APRIL 4
+#define MAY 5
+#define JUNE 6
+#define JULY 7
+#define AUGUST 8
+#define SEPTEMBER 9
+#define OCTOBER 10
+#define NOVEMBER 11
+#define DECEMBER 12
+
+//Select holiday names -- If you test for a holiday in the code, make the holiday's name a define and test for that instead
+#define NEW_YEAR "New Year"
+#define VALENTINES "Valentine's Day"
+#define APRIL_FOOLS "April Fool's Day"
+#define EASTER "Easter"
+#define HALLOWEEN "Halloween"
+#define CHRISTMAS "Christmas"
+#define FESTIVE_SEASON "Festive Season"
+
/*
Days of the week to make it easier to reference them.
diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm
index 3c8d62ce3a..da74045418 100644
--- a/code/__HELPERS/unsorted.dm
+++ b/code/__HELPERS/unsorted.dm
@@ -1291,7 +1291,7 @@ GLOBAL_DATUM_INIT(dview_mob, /mob/dview, new)
invisibility = 101
density = FALSE
see_in_dark = 1e6
- anchored = TRUE
+ move_resist = INFINITY
var/ready_to_die = FALSE
/mob/dview/Initialize() //Properly prevents this mob from gaining huds or joining any global lists
diff --git a/code/controllers/subsystem/throwing.dm b/code/controllers/subsystem/throwing.dm
index 77ad712b99..bf79cbda34 100644
--- a/code/controllers/subsystem/throwing.dm
+++ b/code/controllers/subsystem/throwing.dm
@@ -54,6 +54,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/datums/components/spawner.dm b/code/datums/components/spawner.dm
new file mode 100644
index 0000000000..dc46603b1e
--- /dev/null
+++ b/code/datums/components/spawner.dm
@@ -0,0 +1,49 @@
+/datum/component/spawner
+ var/mob_types = list(/mob/living/simple_animal/hostile/carp)
+ var/spawn_time = 300 //30 seconds default
+ var/list/spawned_mobs = list()
+ var/spawn_delay = 0
+ var/max_mobs = 5
+ var/spawn_text = "emerges from"
+ var/list/faction = list("mining")
+
+/datum/component/spawner/Initialize(_mob_types, _spawn_time, _faction, _spawn_text, _max_mobs)
+ if(_spawn_time)
+ spawn_time=_spawn_time
+ if(_mob_types)
+ mob_types=_mob_types
+ if(_faction)
+ faction=_faction
+ if(_spawn_text)
+ spawn_text=_spawn_text
+ if(_max_mobs)
+ max_mobs=_max_mobs
+
+ RegisterSignal(parent, list(COMSIG_PARENT_QDELETED), .proc/stop_spawning)
+ START_PROCESSING(SSprocessing, src)
+
+/datum/component/spawner/process()
+ try_spawn_mob()
+
+
+/datum/component/spawner/proc/stop_spawning(force, hint)
+ STOP_PROCESSING(SSprocessing, src)
+ for(var/mob/living/simple_animal/L in spawned_mobs)
+ if(L.nest == src)
+ L.nest = null
+ spawned_mobs = null
+
+/datum/component/spawner/proc/try_spawn_mob()
+ var/atom/P = parent
+ if(spawned_mobs.len >= max_mobs)
+ return 0
+ if(spawn_delay > world.time)
+ return 0
+ spawn_delay = world.time + spawn_time
+ var/chosen_mob_type = pick(mob_types)
+ var/mob/living/simple_animal/L = new chosen_mob_type(P.loc)
+ L.flags_1 |= (P.flags_1 & ADMIN_SPAWNED_1)
+ spawned_mobs += L
+ L.nest = src
+ L.faction = src.faction
+ P.visible_message("[L] [spawn_text] [P].")
\ No newline at end of file
diff --git a/code/datums/martial/cqc.dm b/code/datums/martial/cqc.dm
index c7644997ee..390122bc81 100644
--- a/code/datums/martial/cqc.dm
+++ b/code/datums/martial/cqc.dm
@@ -127,7 +127,7 @@
if(A.grab_state >= GRAB_AGGRESSIVE)
D.grabbedby(A, 1)
else
- A.start_pulling(D, 1)
+ A.start_pulling(D, supress_message = TRUE)
if(A.pulling)
D.stop_pulling()
log_combat(A, D, "grabbed", addition="aggressively")
diff --git a/code/datums/martial/psychotic_brawl.dm b/code/datums/martial/psychotic_brawl.dm
index 34301516dc..50e3f7d334 100644
--- a/code/datums/martial/psychotic_brawl.dm
+++ b/code/datums/martial/psychotic_brawl.dm
@@ -25,7 +25,7 @@
if(A.grab_state >= GRAB_AGGRESSIVE)
D.grabbedby(A, 1)
else
- A.start_pulling(D, 1)
+ A.start_pulling(D, supress_message = TRUE)
if(A.pulling)
D.drop_all_held_items()
D.stop_pulling()
diff --git a/code/datums/martial/sleeping_carp.dm b/code/datums/martial/sleeping_carp.dm
index 801e8c8c7a..933d96b179 100644
--- a/code/datums/martial/sleeping_carp.dm
+++ b/code/datums/martial/sleeping_carp.dm
@@ -108,7 +108,7 @@
if(A.grab_state >= GRAB_AGGRESSIVE)
D.grabbedby(A, 1)
else
- A.start_pulling(D, 1)
+ A.start_pulling(D, supress_message = TRUE)
if(A.pulling)
D.drop_all_held_items()
D.stop_pulling()
diff --git a/code/game/atoms.dm b/code/game/atoms.dm
index 7c9dc3f4fe..e41b0986cb 100644
--- a/code/game/atoms.dm
+++ b/code/game/atoms.dm
@@ -313,7 +313,7 @@
SEND_SIGNAL(src, COMSIG_ATOM_FIRE_ACT, exposed_temperature, exposed_volume)
return
-/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 83762240bc..61cea6945e 100644
--- a/code/game/atoms_movable.dm
+++ b/code/game/atoms_movable.dm
@@ -3,6 +3,9 @@
var/last_move = null
var/last_move_time = 0
var/anchored = FALSE
+ var/move_resist = MOVE_RESIST_DEFAULT
+ var/move_force = MOVE_FORCE_DEFAULT
+ var/pull_force = PULL_FORCE_DEFAULT
var/datum/thrownthing/throwing = null
var/throw_speed = 2 //How many tiles to move per ds when being thrown. Float values are fully supported
var/throw_range = 7
@@ -71,20 +74,20 @@
return FALSE
return ..()
-/atom/movable/proc/start_pulling(atom/movable/AM,gs)
+/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)))
+ 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(gs==0)
+ 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)
- grab_state = gs
+ grab_state = state
if(istype(AM,/mob/living))
var/mob/living/AMob = AM
AMob.grabbedby(src)
@@ -95,11 +98,12 @@
AM.pulledby.stop_pulling() //an object can't be pulled by two mobs at once.
pulling = AM
AM.pulledby = src
- grab_state = gs
+ grab_state = state
if(ismob(AM))
var/mob/M = AM
log_combat(src, M, "grabbed", addition="passive grab")
- visible_message("[src] has grabbed [M] passively!")
+ if(!supress_message)
+ visible_message("[src] has grabbed [M] passively!")
return TRUE
/atom/movable/proc/stop_pulling()
@@ -461,17 +465,19 @@
/atom/movable/proc/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
set waitfor = 0
SEND_SIGNAL(src, COMSIG_MOVABLE_IMPACT, hit_atom, throwingdatum)
- return hit_atom.hitby(src)
+ return hit_atom.hitby(src, throwingdatum=throwingdatum)
-/atom/movable/hitby(atom/movable/AM, skipcatch, hitpush = TRUE, blocked)
- if(!anchored && hitpush)
+/atom/movable/hitby(atom/movable/AM, skipcatch, hitpush = TRUE, blocked, datum/thrownthing/throwingdatum)
+ if(!anchored && hitpush && (!throwingdatum || (throwingdatum.force >= (move_resist * MOVE_FORCE_PUSH_RATIO))))
step(src, AM.dir)
..()
-/atom/movable/proc/safe_throw_at(atom/target, range, speed, mob/thrower, spin=TRUE, diagonals_first = FALSE, var/datum/callback/callback)
- return throw_at(target, range, speed, thrower, spin, diagonals_first, callback)
+/atom/movable/proc/safe_throw_at(atom/target, range, speed, mob/thrower, spin = TRUE, diagonals_first = FALSE, datum/callback/callback, force = INFINITY)
+ if((force < (move_resist * MOVE_FORCE_THROW_RATIO)) || (move_resist == INFINITY))
+ return
+ return throw_at(target, range, speed, thrower, spin, diagonals_first, callback, force)
-/atom/movable/proc/throw_at(atom/target, range, speed, mob/thrower, spin=TRUE, diagonals_first = FALSE, var/datum/callback/callback) //If this returns FALSE then callback will not be called.
+/atom/movable/proc/throw_at(atom/target, range, speed, mob/thrower, spin = TRUE, diagonals_first = FALSE, datum/callback/callback, force = INFINITY) //If this returns FALSE then callback will not be called.
. = FALSE
if (!target || speed <= 0)
return
@@ -517,6 +523,7 @@
TT.speed = speed
TT.thrower = thrower
TT.diagonals_first = diagonals_first
+ TT.force = force
TT.callback = callback
var/dist_x = abs(target.x - src.x)
@@ -565,6 +572,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)
if(mover in buckled_mobs)
return 1
@@ -784,14 +807,15 @@
/atom/movable/proc/get_cell()
return
-/atom/movable/proc/can_be_pulled(user)
+/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
-
/obj/item/proc/do_pickup_animation(atom/target)
set waitfor = FALSE
if(!istype(loc, /turf))
diff --git a/code/game/gamemodes/clown_ops/clown_weapons.dm b/code/game/gamemodes/clown_ops/clown_weapons.dm
index 9d1a3b650c..60ae5685ec 100644
--- a/code/game/gamemodes/clown_ops/clown_weapons.dm
+++ b/code/game/gamemodes/clown_ops/clown_weapons.dm
@@ -145,9 +145,9 @@
C.throw_mode_on() //so they can catch it on the return.
return ..()
-/obj/item/shield/energy/bananium/throw_impact(atom/hit_atom)
+/obj/item/shield/energy/bananium/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
if(active)
- var/caught = hit_atom.hitby(src, 0, 0)
+ var/caught = hit_atom.hitby(src, FALSE, FALSE, throwingdatum=throwingdatum)
if(iscarbon(hit_atom) && !caught)//if they are a carbon and they didn't catch it
GET_COMPONENT(slipper, /datum/component/slippery)
slipper.Slip(hit_atom)
diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm
index a2da7de29a..15b3c66c8b 100644
--- a/code/game/machinery/doors/door.dm
+++ b/code/game/machinery/doors/door.dm
@@ -5,6 +5,7 @@
icon_state = "door1"
opacity = 1
density = TRUE
+ move_resist = MOVE_FORCE_VERY_STRONG
layer = OPEN_DOOR_LAYER
power_channel = ENVIRON
max_integrity = 350
diff --git a/code/game/machinery/shieldgen.dm b/code/game/machinery/shieldgen.dm
index fc6577a4f1..16016b8e18 100644
--- a/code/game/machinery/shieldgen.dm
+++ b/code/game/machinery/shieldgen.dm
@@ -4,6 +4,7 @@
icon = 'icons/effects/effects.dmi'
icon_state = "shield-old"
density = TRUE
+ move_resist = INFINITY
opacity = 0
anchored = TRUE
resistance_flags = LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
@@ -93,6 +94,7 @@
/obj/machinery/shieldgen/proc/shields_up()
active = TRUE
update_icon()
+ move_resist = INFINITY
for(var/turf/target_tile in range(shield_range, src))
if(isspaceturf(target_tile) && !(locate(/obj/structure/emergency_shield) in target_tile))
@@ -101,6 +103,7 @@
/obj/machinery/shieldgen/proc/shields_down()
active = FALSE
+ move_resist = initial(move_resist)
update_icon()
QDEL_LIST(deployed_shields)
diff --git a/code/game/mecha/equipment/tools/other_tools.dm b/code/game/mecha/equipment/tools/other_tools.dm
index a8ba9850ff..07ecdec508 100644
--- a/code/game/mecha/equipment/tools/other_tools.dm
+++ b/code/game/mecha/equipment/tools/other_tools.dm
@@ -84,7 +84,7 @@
switch(mode)
if(1)
if(!locked)
- if(!istype(target) || target.anchored)
+ if(!istype(target) || target.anchored || target.move_resist >= MOVE_FORCE_EXTREMELY_STRON)
occupant_message("Unable to lock on [target]")
return
locked = target
@@ -110,7 +110,7 @@
else
atoms = orange(3, target)
for(var/atom/movable/A in atoms)
- if(A.anchored)
+ if(A.anchored || A.move_resist >= MOVE_FORCE_EXTREMELY_STRONG)
continue
spawn(0)
var/iter = 5-get_dist(A,target)
diff --git a/code/game/mecha/equipment/weapons/weapons.dm b/code/game/mecha/equipment/weapons/weapons.dm
index 9f8f3ef742..5d4d0e5899 100644
--- a/code/game/mecha/equipment/weapons/weapons.dm
+++ b/code/game/mecha/equipment/weapons/weapons.dm
@@ -436,7 +436,7 @@
throwforce = 35
icon_state = "punching_glove"
-/obj/item/punching_glove/throw_impact(atom/hit_atom)
+/obj/item/punching_glove/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
if(!..())
if(ismovableatom(hit_atom))
var/atom/movable/AM = hit_atom
diff --git a/code/game/mecha/mecha_defense.dm b/code/game/mecha/mecha_defense.dm
index 62a62b569d..90cd5df79c 100644
--- a/code/game/mecha/mecha_defense.dm
+++ b/code/game/mecha/mecha_defense.dm
@@ -108,8 +108,8 @@
/obj/mecha/attack_tk()
return
-/obj/mecha/hitby(atom/movable/A as mob|obj) //wrapper
- log_message("Hit by [A].", color="red")
+/obj/mecha/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum) //wrapper
+ log_message("Hit by [AM].", color="red")
. = ..()
diff --git a/code/game/objects/effects/effects.dm b/code/game/objects/effects/effects.dm
index 410923fe25..ccde9c2254 100644
--- a/code/game/objects/effects/effects.dm
+++ b/code/game/objects/effects/effects.dm
@@ -4,6 +4,7 @@
/obj/effect
icon = 'icons/effects/effects.dmi'
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF | FREEZE_PROOF
+ move_resist = INFINITY
obj_flags = 0
/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 6a706fede9..542c3249ac 100644
--- a/code/game/objects/items.dm
+++ b/code/game/objects/items.dm
@@ -544,21 +544,21 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE)
else
return
-/obj/item/throw_impact(atom/A, datum/thrownthing/throwingdatum)
- if(A && !QDELETED(A))
- SEND_SIGNAL(src, COMSIG_MOVABLE_IMPACT, A, throwingdatum)
- if(is_hot() && isliving(A))
- var/mob/living/L = A
+/obj/item/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
+ if(hit_atom && !QDELETED(hit_atom))
+ SEND_SIGNAL(src, COMSIG_MOVABLE_IMPACT, hit_atom, throwingdatum)
+ if(is_hot() && isliving(hit_atom))
+ var/mob/living/L = hit_atom
L.IgniteMob()
var/itempush = 1
if(w_class < 4)
itempush = 0 //too light to push anything
- return A.hitby(src, 0, itempush)
+ return hit_atom.hitby(src, 0, itempush, throwingdatum=throwingdatum)
-/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
@@ -649,7 +649,7 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE)
else
. = ""
-/obj/item/hitby(atom/movable/AM)
+/obj/item/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum)
return
/obj/item/attack_hulk(mob/living/carbon/human/user)
diff --git a/code/game/objects/items/dice.dm b/code/game/objects/items/dice.dm
index cb0d4ec4ac..c6af6246bf 100644
--- a/code/game/objects/items/dice.dm
+++ b/code/game/objects/items/dice.dm
@@ -160,7 +160,7 @@
/obj/item/dice/attack_self(mob/user)
diceroll(user)
-/obj/item/dice/throw_impact(atom/target)
+/obj/item/dice/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
diceroll(thrownby)
. = ..()
diff --git a/code/game/objects/items/handcuffs.dm b/code/game/objects/items/handcuffs.dm
index 246dd77684..d27ab8231b 100644
--- a/code/game/objects/items/handcuffs.dm
+++ b/code/game/objects/items/handcuffs.dm
@@ -340,7 +340,7 @@
return
playsound(src.loc,'sound/weapons/bolathrow.ogg', 75, 1)
-/obj/item/restraints/legcuffs/bola/throw_impact(atom/hit_atom)
+/obj/item/restraints/legcuffs/bola/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
if(..() || !iscarbon(hit_atom))//if it gets caught or the target can't be cuffed,
return//abort
var/mob/living/carbon/C = hit_atom
@@ -368,7 +368,7 @@
w_class = WEIGHT_CLASS_SMALL
breakouttime = 60
-/obj/item/restraints/legcuffs/bola/energy/throw_impact(atom/hit_atom)
+/obj/item/restraints/legcuffs/bola/energy/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
if(iscarbon(hit_atom))
var/obj/item/restraints/legcuffs/beartrap/B = new /obj/item/restraints/legcuffs/beartrap/energy/cyborg(get_turf(hit_atom))
B.Crossed(hit_atom)
diff --git a/code/game/objects/items/melee/misc.dm b/code/game/objects/items/melee/misc.dm
index 10b84917bb..7494eb8e1c 100644
--- a/code/game/objects/items/melee/misc.dm
+++ b/code/game/objects/items/melee/misc.dm
@@ -332,13 +332,13 @@
if(proximity_flag)
consume_everything(target)
-/obj/item/melee/supermatter_sword/throw_impact(target)
+/obj/item/melee/supermatter_sword/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
..()
- if(ismob(target))
- var/mob/M
+ if(ismob(hit_atom))
+ var/mob/M = hit_atom
if(src.loc == M)
M.dropItemToGround(src)
- consume_everything(target)
+ consume_everything(hit_atom)
/obj/item/melee/supermatter_sword/pickup(user)
..()
diff --git a/code/game/objects/items/singularityhammer.dm b/code/game/objects/items/singularityhammer.dm
index b6559c9091..1c20fcfacc 100644
--- a/code/game/objects/items/singularityhammer.dm
+++ b/code/game/objects/items/singularityhammer.dm
@@ -105,10 +105,10 @@
playsound(src.loc, "sparks", 50, 1)
shock(M)
-/obj/item/twohanded/mjollnir/throw_impact(atom/target)
+/obj/item/twohanded/mjollnir/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
. = ..()
- if(isliving(target))
- shock(target)
+ if(isliving(hit_atom))
+ shock(hit_atom)
/obj/item/twohanded/mjollnir/update_icon() //Currently only here to fuck with the on-mob icons.
icon_state = "mjollnir[wielded]"
diff --git a/code/game/objects/items/stacks/bscrystal.dm b/code/game/objects/items/stacks/bscrystal.dm
index 49a735af9c..2d494ca186 100644
--- a/code/game/objects/items/stacks/bscrystal.dm
+++ b/code/game/objects/items/stacks/bscrystal.dm
@@ -35,7 +35,7 @@
/obj/item/stack/ore/bluespace_crystal/proc/blink_mob(mob/living/L)
do_teleport(L, get_turf(L), blink_range, asoundin = 'sound/effects/phasein.ogg', channel = TELEPORT_CHANNEL_BLUESPACE)
-/obj/item/stack/ore/bluespace_crystal/throw_impact(atom/hit_atom)
+/obj/item/stack/ore/bluespace_crystal/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
if(!..()) // not caught in mid-air
visible_message("[src] fizzles and disappears upon impact!")
var/turf/T = get_turf(hit_atom)
diff --git a/code/game/objects/items/stacks/stack.dm b/code/game/objects/items/stacks/stack.dm
index 155d1f1643..ea9440830d 100644
--- a/code/game/objects/items/stacks/stack.dm
+++ b/code/game/objects/items/stacks/stack.dm
@@ -335,7 +335,7 @@
merge(o)
. = ..()
-/obj/item/stack/hitby(atom/movable/AM, skip, hitpush)
+/obj/item/stack/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum)
if(istype(AM, merge_type))
merge(AM)
. = ..()
diff --git a/code/game/objects/items/stunbaton.dm b/code/game/objects/items/stunbaton.dm
index bfe630ba01..81956f147c 100644
--- a/code/game/objects/items/stunbaton.dm
+++ b/code/game/objects/items/stunbaton.dm
@@ -40,7 +40,7 @@
cell = new preload_cell_type(src)
update_icon()
-/obj/item/melee/baton/throw_impact(atom/hit_atom)
+/obj/item/melee/baton/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
..()
//Only mob/living types have stun handling
if(status && prob(throw_hit_chance) && iscarbon(hit_atom))
diff --git a/code/game/objects/items/theft_tools.dm b/code/game/objects/items/theft_tools.dm
index b99be7e988..243787dcb4 100644
--- a/code/game/objects/items/theft_tools.dm
+++ b/code/game/objects/items/theft_tools.dm
@@ -235,7 +235,7 @@
if(proximity && ismovableatom(O) && O != sliver)
Consume(O, user)
-/obj/item/hemostat/supermatter/throw_impact(atom/hit_atom) // no instakill supermatter javelins
+/obj/item/hemostat/supermatter/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum) // no instakill supermatter javelins
if(sliver)
sliver.forceMove(loc)
to_chat(usr, "\The [sliver] falls out of \the [src] as you throw them.")
diff --git a/code/game/objects/items/toys.dm b/code/game/objects/items/toys.dm
index 46fabea8b0..2e4c45fa96 100644
--- a/code/game/objects/items/toys.dm
+++ b/code/game/objects/items/toys.dm
@@ -84,7 +84,7 @@
else
return ..()
-/obj/item/toy/balloon/throw_impact(atom/hit_atom)
+/obj/item/toy/balloon/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
if(!..()) //was it caught by a mob?
balloon_burst(hit_atom)
@@ -384,7 +384,7 @@
/obj/item/toy/snappop/fire_act(exposed_temperature, exposed_volume)
pop_burst()
-/obj/item/toy/snappop/throw_impact(atom/hit_atom)
+/obj/item/toy/snappop/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
if(!..())
pop_burst()
@@ -1006,7 +1006,7 @@
icon_state = "minimeteor"
w_class = WEIGHT_CLASS_SMALL
-/obj/item/toy/minimeteor/throw_impact(atom/hit_atom)
+/obj/item/toy/minimeteor/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
if(!..())
playsound(src, 'sound/effects/meteorimpact.ogg', 40, 1)
for(var/mob/M in urange(10, src))
@@ -1055,7 +1055,7 @@
if(user.dropItemToGround(src))
throw_at(target, throw_range, throw_speed)
-/obj/item/toy/snowball/throw_impact(atom/hit_atom)
+/obj/item/toy/snowball/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
if(!..())
playsound(src, 'sound/effects/pop.ogg', 20, 1)
qdel(src)
diff --git a/code/game/objects/obj_defense.dm b/code/game/objects/obj_defense.dm
index 70a05a8d40..2d99f0e073 100644
--- a/code/game/objects/obj_defense.dm
+++ b/code/game/objects/obj_defense.dm
@@ -46,7 +46,7 @@
if(BURN)
playsound(src.loc, 'sound/items/welder.ogg', 100, 1)
-/obj/hitby(atom/movable/AM)
+/obj/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum)
..()
var/throwdamage = AM.throwforce
if(isobj(AM))
@@ -126,6 +126,17 @@
if(. && !play_soundeffect)
playsound(src, 'sound/effects/meteorimpact.ogg', 100, 1)
+/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/attack_slime(mob/living/simple_animal/slime/user)
if(!user.is_adult)
return
diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm
index 6dd8a43130..cd19ceda50 100644
--- a/code/game/objects/objs.dm
+++ b/code/game/objects/objs.dm
@@ -80,7 +80,7 @@
SEND_SIGNAL(src, COMSIG_OBJ_SETANCHORED, anchorvalue)
anchored = anchorvalue
-/obj/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback)
+/obj/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback, force)
..()
if(obj_flags & FROZEN)
visible_message("[src] shatters into a million pieces!")
diff --git a/code/game/objects/structures/ghost_role_spawners.dm b/code/game/objects/structures/ghost_role_spawners.dm
index b820e93c7b..e71a2720ae 100644
--- a/code/game/objects/structures/ghost_role_spawners.dm
+++ b/code/game/objects/structures/ghost_role_spawners.dm
@@ -44,6 +44,7 @@
roundstart = FALSE
death = FALSE
anchored = FALSE
+ move_resist = MOVE_FORCE_NORMAL
density = FALSE
flavour_text = "You are an ash walker. Your tribe worships the Necropolis. The wastes are sacred ground, its monsters a blessed bounty. You would never leave its beautiful expanse. \
You have seen lights in the distance... they foreshadow the arrival of outsiders that seek to tear apart the Necropolis and its domain. Fresh sacrifices for your nest."
@@ -118,6 +119,7 @@
roundstart = FALSE
death = FALSE
anchored = FALSE
+ move_resist = MOVE_FORCE_NORMAL
density = FALSE
var/has_owner = FALSE
var/can_transfer = TRUE //if golems can switch bodies to this new shell
diff --git a/code/game/objects/structures/grille.dm b/code/game/objects/structures/grille.dm
index 00d1690d86..460059ffbf 100644
--- a/code/game/objects/structures/grille.dm
+++ b/code/game/objects/structures/grille.dm
@@ -258,7 +258,7 @@
take_damage(1, BURN, 0, 0)
..()
-/obj/structure/grille/hitby(AM as mob|obj)
+/obj/structure/grille/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum)
if(isobj(AM))
if(prob(50) && anchored && !broken)
var/obj/O = AM
diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/necropolis_tendril.dm b/code/game/objects/structures/lavaland/necropolis_tendril.dm
similarity index 71%
rename from code/modules/mob/living/simple_animal/hostile/mining_mobs/necropolis_tendril.dm
rename to code/game/objects/structures/lavaland/necropolis_tendril.dm
index e3c81b2639..b1e9a81087 100644
--- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/necropolis_tendril.dm
+++ b/code/game/objects/structures/lavaland/necropolis_tendril.dm
@@ -1,39 +1,32 @@
//Necropolis Tendrils, which spawn lavaland monsters and break into a chasm when killed
-/obj/effect/light_emitter/tendril
- set_luminosity = 4
- set_cap = 2.5
- light_color = LIGHT_COLOR_LAVA
-
-/mob/living/simple_animal/hostile/spawner/lavaland
+/obj/structure/spawner/lavaland
name = "necropolis tendril"
desc = "A vile tendril of corruption, originating deep underground. Terrible monsters are pouring out of it."
+
icon = 'icons/mob/nest.dmi'
icon_state = "tendril"
- icon_living = "tendril"
- icon_dead = "tendril"
+
faction = list("mining")
- weather_immunities = list("lava","ash")
- health = 250
- maxHealth = 250
max_mobs = 3
- spawn_time = 300 //30 seconds default
+ max_integrity = 250
mob_types = list(/mob/living/simple_animal/hostile/asteroid/basilisk/watcher/tendril)
- spawn_text = "emerges from"
- atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 5, "min_n2" = 0, "max_n2" = 0)
- minbodytemp = 0
- maxbodytemp = INFINITY
- loot = list(/obj/effect/collapse, /obj/structure/closet/crate/necropolis/tendril)
- del_on_death = 1
+
+ move_resist=INFINITY // just killing it tears a massive hole in the ground, let's not move it
+ anchored = TRUE
+ resistance_flags = FIRE_PROOF | LAVA_PROOF
+
var/gps = null
var/obj/effect/light_emitter/tendril/emitted_light
-/mob/living/simple_animal/hostile/spawner/lavaland/goliath
+
+/obj/structure/spawner/lavaland/goliath
mob_types = list(/mob/living/simple_animal/hostile/asteroid/goliath/beast/tendril)
-/mob/living/simple_animal/hostile/spawner/lavaland/legion
+/obj/structure/spawner/lavaland/legion
mob_types = list(/mob/living/simple_animal/hostile/asteroid/hivelord/legion/tendril)
-/mob/living/simple_animal/hostile/spawner/lavaland/Initialize()
+GLOBAL_LIST_INIT(tendrils, list())
+/obj/structure/spawner/lavaland/Initialize()
. = ..()
emitted_light = new(loc)
for(var/F in RANGE_TURFS(1, src))
@@ -41,18 +34,19 @@
var/turf/closed/mineral/M = F
M.ScrapeAway(null, CHANGETURF_IGNORE_AIR)
gps = new /obj/item/gps/internal(src)
+ GLOB.tendrils += src
-/mob/living/simple_animal/hostile/spawner/lavaland/Destroy()
- QDEL_NULL(emitted_light)
- QDEL_NULL(gps)
+/obj/structure/spawner/lavaland/deconstruct(disassembled)
+ new /obj/effect/collapse(loc)
+ new /obj/structure/closet/crate/necropolis/tendril(loc)
return ..()
-/mob/living/simple_animal/hostile/spawner/lavaland/death()
+
+/obj/structure/spawner/lavaland/Destroy()
var/last_tendril = TRUE
- for(var/mob/living/simple_animal/hostile/spawner/lavaland/other in GLOB.mob_living_list)
- if(other != src)
- last_tendril = FALSE
- break
+ if(GLOB.tendrils.len>1)
+ last_tendril = FALSE
+
if(last_tendril && !(flags_1 & ADMIN_SPAWNED_1))
if(SSmedals.hub_enabled)
for(var/mob/living/L in view(7,src))
@@ -60,7 +54,15 @@
continue
SSmedals.UnlockMedal("[BOSS_MEDAL_TENDRIL] [ALL_KILL_MEDAL]", L.client)
SSmedals.SetScore(TENDRIL_CLEAR_SCORE, L.client, 1)
- ..()
+ GLOB.tendrils -= src
+ QDEL_NULL(emitted_light)
+ QDEL_NULL(gps)
+ return ..()
+
+/obj/effect/light_emitter/tendril
+ set_luminosity = 4
+ set_cap = 2.5
+ light_color = LIGHT_COLOR_LAVA
/obj/effect/collapse
name = "collapsing necropolis tendril"
@@ -92,4 +94,4 @@
for(var/turf/T in range(2,src))
if(!T.density)
T.TerraformTurf(/turf/open/chasm/lavaland, /turf/open/chasm/lavaland)
- qdel(src)
+ qdel(src)
\ No newline at end of file
diff --git a/code/game/objects/structures/spawner.dm b/code/game/objects/structures/spawner.dm
new file mode 100644
index 0000000000..e67ef7af60
--- /dev/null
+++ b/code/game/objects/structures/spawner.dm
@@ -0,0 +1,75 @@
+/obj/structure/spawner
+ name = "monster nest"
+ icon = 'icons/mob/animal.dmi'
+ icon_state = "hole"
+ max_integrity = 100
+
+ move_resist = MOVE_FORCE_EXTREMELY_STRONG
+ anchored = TRUE
+ density = TRUE
+
+ var/max_mobs = 5
+ var/spawn_time = 300 //30 seconds default
+ var/mob_types = list(/mob/living/simple_animal/hostile/carp)
+ var/spawn_text = "emerges from"
+ var/faction = list("hostile")
+
+/obj/structure/spawner/Initialize()
+ . = ..()
+ AddComponent(/datum/component/spawner, mob_types, spawn_time, faction, spawn_text, max_mobs)
+
+/obj/structure/spawner/syndicate
+ name = "warp beacon"
+ icon = 'icons/obj/device.dmi'
+ icon_state = "syndbeacon"
+ spawn_text = "warps in from"
+ mob_types = list(/mob/living/simple_animal/hostile/syndicate/ranged)
+ faction = list(ROLE_SYNDICATE)
+
+/obj/structure/spawner/skeleton
+ name = "bone pit"
+ desc = "A pit full of bones, and some still seem to be moving..."
+ icon_state = "hole"
+ icon = 'icons/mob/nest.dmi'
+ max_integrity = 150
+ max_mobs = 15
+ spawn_time = 150
+ mob_types = list(/mob/living/simple_animal/hostile/skeleton)
+ spawn_text = "climbs out of"
+ faction = list("skeleton")
+
+/obj/structure/spawner/mining
+ name = "monster den"
+ desc = "A hole dug into the ground, harboring all kinds of monsters found within most caves or mining asteroids."
+ icon_state = "hole"
+ max_integrity = 200
+ max_mobs = 3
+ icon = 'icons/mob/nest.dmi'
+ spawn_text = "crawls out of"
+ mob_types = list(/mob/living/simple_animal/hostile/asteroid/goldgrub, /mob/living/simple_animal/hostile/asteroid/goliath, /mob/living/simple_animal/hostile/asteroid/hivelord, /mob/living/simple_animal/hostile/asteroid/basilisk, /mob/living/simple_animal/hostile/asteroid/fugu)
+ faction = list("mining")
+
+/obj/structure/spawner/mining/goldgrub
+ name = "goldgrub den"
+ desc = "A den housing a nest of goldgrubs, annoying but arguably much better than anything else you'll find in a nest."
+ mob_types = list(/mob/living/simple_animal/hostile/asteroid/goldgrub)
+
+/obj/structure/spawner/mining/goliath
+ name = "goliath den"
+ desc = "A den housing a nest of goliaths, oh god why?"
+ mob_types = list(/mob/living/simple_animal/hostile/asteroid/goliath)
+
+/obj/structure/spawner/mining/hivelord
+ name = "hivelord den"
+ desc = "A den housing a nest of hivelords."
+ mob_types = list(/mob/living/simple_animal/hostile/asteroid/hivelord)
+
+/obj/structure/spawner/mining/basilisk
+ name = "basilisk den"
+ desc = "A den housing a nest of basilisks, bring a coat."
+ mob_types = list(/mob/living/simple_animal/hostile/asteroid/basilisk)
+
+/obj/structure/spawner/mining/wumborian
+ name = "wumborian fugu den"
+ desc = "A den housing a nest of wumborian fugus, how do they all even fit in there?"
+ mob_types = list(/mob/living/simple_animal/hostile/asteroid/fugu)
\ No newline at end of file
diff --git a/code/game/turfs/simulated/floor/plating/asteroid.dm b/code/game/turfs/simulated/floor/plating/asteroid.dm
index d9966ee55c..d46c2363e3 100644
--- a/code/game/turfs/simulated/floor/plating/asteroid.dm
+++ b/code/game/turfs/simulated/floor/plating/asteroid.dm
@@ -155,9 +155,9 @@
has_data = TRUE
/turf/open/floor/plating/asteroid/airless/cave/volcanic
- mob_spawn_list = list(/mob/living/simple_animal/hostile/asteroid/goliath/beast/random = 50, /mob/living/simple_animal/hostile/spawner/lavaland/goliath = 3, \
- /mob/living/simple_animal/hostile/asteroid/basilisk/watcher/random = 40, /mob/living/simple_animal/hostile/spawner/lavaland = 2, \
- /mob/living/simple_animal/hostile/asteroid/hivelord/legion/random = 30, /mob/living/simple_animal/hostile/spawner/lavaland/legion = 3, \
+ mob_spawn_list = list(/mob/living/simple_animal/hostile/asteroid/goliath/beast/random = 50, /obj/structure/spawner/lavaland/goliath = 3, \
+ /mob/living/simple_animal/hostile/asteroid/basilisk/watcher/random = 40, /obj/structure/spawner/lavaland = 2, \
+ /mob/living/simple_animal/hostile/asteroid/hivelord/legion/random = 30, /obj/structure/spawner/lavaland/legion = 3, \
SPAWN_MEGAFAUNA = 6, /mob/living/simple_animal/hostile/asteroid/goldgrub = 10)
data_having_type = /turf/open/floor/plating/asteroid/airless/cave/volcanic/has_data
@@ -278,7 +278,7 @@
return //if there's a megafauna within standard view don't spawn anything at all
if(ispath(randumb, /mob/living/simple_animal/hostile/asteroid) || istype(H, /mob/living/simple_animal/hostile/asteroid))
return //if the random is a standard mob, avoid spawning if there's another one within 12 tiles
- if((ispath(randumb, /mob/living/simple_animal/hostile/spawner/lavaland) || istype(H, /mob/living/simple_animal/hostile/spawner/lavaland)) && get_dist(src, H) <= 2)
+ if((ispath(randumb, /obj/structure/spawner/lavaland) || istype(H, /obj/structure/spawner/lavaland)) && get_dist(src, H) <= 2)
return //prevents tendrils spawning in each other's collapse range
new randumb(T)
diff --git a/code/game/turfs/simulated/lava.dm b/code/game/turfs/simulated/lava.dm
index e24736ecf3..6a5a8aee3c 100644
--- a/code/game/turfs/simulated/lava.dm
+++ b/code/game/turfs/simulated/lava.dm
@@ -36,7 +36,7 @@
if(burn_stuff(AM))
START_PROCESSING(SSobj, src)
-/turf/open/lava/hitby(atom/movable/AM)
+/turf/open/lava/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum)
if(burn_stuff(AM))
START_PROCESSING(SSobj, src)
diff --git a/code/game/turfs/simulated/wall/misc_walls.dm b/code/game/turfs/simulated/wall/misc_walls.dm
index 8b63d60939..d445be91cb 100644
--- a/code/game/turfs/simulated/wall/misc_walls.dm
+++ b/code/game/turfs/simulated/wall/misc_walls.dm
@@ -25,7 +25,7 @@
if(stored_pulling)
stored_pulling.setDir(get_dir(stored_pulling.loc, newloc))
stored_pulling.forceMove(src)
- H.start_pulling(stored_pulling, TRUE)
+ H.start_pulling(stored_pulling, supress_message = TRUE)
/turf/closed/wall/mineral/cult/ratvar_act()
. = ..()
diff --git a/code/modules/antagonists/clockcult/clock_items/clock_weapons/ratvarian_spear.dm b/code/modules/antagonists/clockcult/clock_items/clock_weapons/ratvarian_spear.dm
index 3ad6684725..a004714e5e 100644
--- a/code/modules/antagonists/clockcult/clock_items/clock_weapons/ratvarian_spear.dm
+++ b/code/modules/antagonists/clockcult/clock_items/clock_weapons/ratvarian_spear.dm
@@ -45,10 +45,10 @@
bonus_damage *= 3 //total 30 damage on cultists, 50 with ratvar
GLOB.clockwork_vitality += target.adjustFireLoss(bonus_damage) //adds the damage done to existing vitality
-/obj/item/clockwork/weapon/ratvarian_spear/throw_impact(atom/target)
- var/turf/T = get_turf(target)
- if(isliving(target))
- var/mob/living/L = target
+/obj/item/clockwork/weapon/ratvarian_spear/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
+ var/turf/T = get_turf(hit_atom)
+ if(isliving(hit_atom))
+ var/mob/living/L = hit_atom
if(is_servant_of_ratvar(L))
if(L.put_in_active_hand(src))
L.visible_message("[L] catches [src] out of the air!")
diff --git a/code/modules/antagonists/cult/cult_items.dm b/code/modules/antagonists/cult/cult_items.dm
index 10759afcd0..445e4c9302 100644
--- a/code/modules/antagonists/cult/cult_items.dm
+++ b/code/modules/antagonists/cult/cult_items.dm
@@ -264,7 +264,7 @@
to_chat(user, "The bola seems to take on a life of its own!")
throw_impact(user)
-/obj/item/restraints/legcuffs/bola/cult/throw_impact(atom/hit_atom)
+/obj/item/restraints/legcuffs/bola/cult/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
if(iscultist(hit_atom))
return
. = ..()
@@ -689,10 +689,10 @@
/obj/item/twohanded/cult_spear/update_icon()
icon_state = "bloodspear[wielded]"
-/obj/item/twohanded/cult_spear/throw_impact(atom/target)
- var/turf/T = get_turf(target)
- if(isliving(target))
- var/mob/living/L = target
+/obj/item/twohanded/cult_spear/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
+ var/turf/T = get_turf(hit_atom)
+ if(isliving(hit_atom))
+ var/mob/living/L = hit_atom
if(iscultist(L))
playsound(src, 'sound/weapons/throwtap.ogg', 50)
if(L.put_in_active_hand(src))
@@ -983,11 +983,11 @@
return TRUE
return FALSE
-/obj/item/shield/mirror/throw_impact(atom/target, throwingdatum)
- var/turf/T = get_turf(target)
+/obj/item/shield/mirror/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
+ var/turf/T = get_turf(hit_atom)
var/datum/thrownthing/D = throwingdatum
- if(isliving(target))
- var/mob/living/L = target
+ if(isliving(hit_atom))
+ var/mob/living/L = hit_atom
if(iscultist(L))
playsound(src, 'sound/weapons/throwtap.ogg', 50)
if(L.put_in_active_hand(src))
diff --git a/code/modules/antagonists/revenant/revenant.dm b/code/modules/antagonists/revenant/revenant.dm
index f380fa68e9..66eab69f39 100644
--- a/code/modules/antagonists/revenant/revenant.dm
+++ b/code/modules/antagonists/revenant/revenant.dm
@@ -43,7 +43,7 @@
wander = FALSE
density = FALSE
movement_type = FLYING
- anchored = TRUE
+ move_resist = MOVE_FORCE_OVERPOWERING
mob_size = MOB_SIZE_TINY
pass_flags = PASSTABLE | PASSGRILLE | PASSMOB
speed = 1
@@ -360,7 +360,7 @@
user.dropItemToGround(src)
scatter()
-/obj/item/ectoplasm/revenant/throw_impact(atom/hit_atom)
+/obj/item/ectoplasm/revenant/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
..()
if(inert)
return
diff --git a/code/modules/assembly/infrared.dm b/code/modules/assembly/infrared.dm
index 07f31a6678..e9855bce46 100644
--- a/code/modules/assembly/infrared.dm
+++ b/code/modules/assembly/infrared.dm
@@ -142,7 +142,7 @@
. = ..()
olddir = dir
-/obj/item/assembly/infra/throw_impact()
+/obj/item/assembly/infra/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
. = ..()
if(!olddir)
return
diff --git a/code/modules/assembly/mousetrap.dm b/code/modules/assembly/mousetrap.dm
index a2a9fb0105..9889a9e2c6 100644
--- a/code/modules/assembly/mousetrap.dm
+++ b/code/modules/assembly/mousetrap.dm
@@ -130,10 +130,10 @@
return FALSE
-/obj/item/assembly/mousetrap/hitby(A as mob|obj)
+/obj/item/assembly/mousetrap/hitby(atom/hit_atom, skipcatch = FALSE, hitpush = TRUE, blocked = FALSE, datum/thrownthing/throwingdatum)
if(!armed)
return ..()
- visible_message("[src] is triggered by [A].")
+ visible_message("[src] is triggered by [hit_atom].")
triggered(null)
diff --git a/code/modules/atmospherics/environmental/LINDA_turf_tile.dm b/code/modules/atmospherics/environmental/LINDA_turf_tile.dm
index ea555c9489..3967fc39b5 100644
--- a/code/modules/atmospherics/environmental/LINDA_turf_tile.dm
+++ b/code/modules/atmospherics/environmental/LINDA_turf_tile.dm
@@ -266,12 +266,13 @@
/atom/movable/proc/experience_pressure_difference(pressure_difference, direction, pressure_resistance_prob_delta = 0)
var/const/PROBABILITY_OFFSET = 25
var/const/PROBABILITY_BASE_PRECENT = 75
+ var/max_force = sqrt(pressure_difference)*(MOVE_FORCE_DEFAULT / 5)
set waitfor = 0
var/move_prob = 100
if (pressure_resistance > 0)
move_prob = (pressure_difference/pressure_resistance*PROBABILITY_BASE_PRECENT)-PROBABILITY_OFFSET
move_prob += pressure_resistance_prob_delta
- if (move_prob > PROBABILITY_OFFSET && prob(move_prob))
+ if (move_prob > PROBABILITY_OFFSET && prob(move_prob) && (move_resist != INFINITY) && (!anchored && (max_force >= (move_resist * MOVE_FORCE_PUSH_RATIO))) || (anchored && (max_force >= (move_resist * MOVE_FORCE_FORCEPUSH_RATIO))))
step(src, direction)
last_high_pressure_movement_air_cycle = SSair.times_fired
diff --git a/code/modules/atmospherics/machinery/atmosmachinery.dm b/code/modules/atmospherics/machinery/atmosmachinery.dm
index 7d095869cb..c9b5eab18e 100644
--- a/code/modules/atmospherics/machinery/atmosmachinery.dm
+++ b/code/modules/atmospherics/machinery/atmosmachinery.dm
@@ -14,6 +14,7 @@ Pipelines + Other Objects -> Pipe network
/obj/machinery/atmospherics
anchored = TRUE
+ move_resist = INFINITY //Moving a connected machine without actually doing the normal (dis)connection things will probably cause a LOT of issues.
idle_power_usage = 0
active_power_usage = 0
power_channel = ENVIRON
diff --git a/code/modules/events/immovable_rod.dm b/code/modules/events/immovable_rod.dm
index e041d566e9..27ca16fd3c 100644
--- a/code/modules/events/immovable_rod.dm
+++ b/code/modules/events/immovable_rod.dm
@@ -43,6 +43,9 @@ In my current plan for it, 'solid' will be defined as anything with density == 1
icon = 'icons/obj/objects.dmi'
icon_state = "immrod"
throwforce = 100
+ move_force = INFINITY
+ move_resist = INFINITY
+ pull_force = INFINITY
density = TRUE
anchored = TRUE
var/mob/living/wizard
diff --git a/code/modules/flufftext/Hallucination.dm b/code/modules/flufftext/Hallucination.dm
index bac2e0bb65..b9f355a8cc 100644
--- a/code/modules/flufftext/Hallucination.dm
+++ b/code/modules/flufftext/Hallucination.dm
@@ -213,9 +213,9 @@ GLOBAL_LIST_INIT(hallucination_list, list(
. = ..()
name = "alien hunter ([rand(1, 1000)])"
-/obj/effect/hallucination/simple/xeno/throw_impact(A)
+/obj/effect/hallucination/simple/xeno/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
update_icon("alienh_pounce")
- if(A == target && target.stat!=DEAD)
+ if(hit_atom == target && target.stat!=DEAD)
target.Knockdown(100)
target.visible_message("[target] flails around wildly.","[name] pounces on you!")
diff --git a/code/modules/food_and_drinks/drinks/drinks.dm b/code/modules/food_and_drinks/drinks/drinks.dm
index 7b1d8be85e..40489eba9b 100644
--- a/code/modules/food_and_drinks/drinks/drinks.dm
+++ b/code/modules/food_and_drinks/drinks/drinks.dm
@@ -101,10 +101,10 @@
to_chat(user, "You heat [name] with [I]!")
..()
-/obj/item/reagent_containers/food/drinks/throw_impact(atom/target, datum/thrownthing/throwinfo)
+/obj/item/reagent_containers/food/drinks/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
. = ..()
if(!.) //if the bottle wasn't caught
- smash(target, throwinfo.thrower, TRUE)
+ smash(hit_atom, throwingdatum?.thrower, TRUE)
/obj/item/reagent_containers/food/drinks/proc/smash(atom/target, mob/thrower, ranged = FALSE)
if(!isGlass)
diff --git a/code/modules/food_and_drinks/drinks/drinks/bottle.dm b/code/modules/food_and_drinks/drinks/drinks/bottle.dm
index e72b624ae5..16878bcf75 100644
--- a/code/modules/food_and_drinks/drinks/drinks/bottle.dm
+++ b/code/modules/food_and_drinks/drinks/drinks/bottle.dm
@@ -442,7 +442,7 @@
isGlass = FALSE
return
-/obj/item/reagent_containers/food/drinks/bottle/molotov/throw_impact(atom/target,datum/thrownthing/throwdata)
+/obj/item/reagent_containers/food/drinks/bottle/molotov/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
var/firestarter = 0
for(var/datum/reagent/R in reagents.reagent_list)
for(var/A in accelerants)
@@ -450,8 +450,8 @@
firestarter = 1
break
if(firestarter && active)
- target.fire_act()
- new /obj/effect/hotspot(get_turf(target))
+ hit_atom.fire_act()
+ new /obj/effect/hotspot(get_turf(hit_atom))
..()
/obj/item/reagent_containers/food/drinks/bottle/molotov/attackby(obj/item/I, mob/user, params)
diff --git a/code/modules/food_and_drinks/food/snacks_egg.dm b/code/modules/food_and_drinks/food/snacks_egg.dm
index 510783479e..fdc02c96e4 100644
--- a/code/modules/food_and_drinks/food/snacks_egg.dm
+++ b/code/modules/food_and_drinks/food/snacks_egg.dm
@@ -31,7 +31,7 @@
var/color = mix_color_from_reagents(reagents.reagent_list)
add_atom_colour(color, FIXED_COLOUR_PRIORITY)
-/obj/item/reagent_containers/food/snacks/egg/throw_impact(atom/hit_atom)
+/obj/item/reagent_containers/food/snacks/egg/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
if(!..()) //was it caught by a mob?
var/turf/T = get_turf(hit_atom)
new/obj/effect/decal/cleanable/egg_smudge(T)
diff --git a/code/modules/food_and_drinks/food/snacks_other.dm b/code/modules/food_and_drinks/food/snacks_other.dm
index ad0824dd79..3a60e991a9 100644
--- a/code/modules/food_and_drinks/food/snacks_other.dm
+++ b/code/modules/food_and_drinks/food/snacks_other.dm
@@ -439,8 +439,8 @@
head.color = C
add_overlay(head)
-/obj/item/reagent_containers/food/snacks/lollipop/throw_impact(atom/A)
- ..(A)
+/obj/item/reagent_containers/food/snacks/lollipop/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
+ ..(hit_atom)
throw_speed = 1
throwforce = 0
diff --git a/code/modules/food_and_drinks/food/snacks_pie.dm b/code/modules/food_and_drinks/food/snacks_pie.dm
index bc66d353e1..f32ebbb3a0 100644
--- a/code/modules/food_and_drinks/food/snacks_pie.dm
+++ b/code/modules/food_and_drinks/food/snacks_pie.dm
@@ -29,7 +29,7 @@
foodtype = GRAIN | DAIRY | SUGAR
var/stunning = TRUE
-/obj/item/reagent_containers/food/snacks/pie/cream/throw_impact(atom/hit_atom)
+/obj/item/reagent_containers/food/snacks/pie/cream/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
. = ..()
if(!.) //if we're not being caught
splat(hit_atom)
diff --git a/code/modules/food_and_drinks/pizzabox.dm b/code/modules/food_and_drinks/pizzabox.dm
index d608a73560..58f7985291 100644
--- a/code/modules/food_and_drinks/pizzabox.dm
+++ b/code/modules/food_and_drinks/pizzabox.dm
@@ -231,7 +231,7 @@
if(boxes.len >= 3 && prob(25 * boxes.len))
disperse_pizzas()
-/obj/item/pizzabox/throw_impact(atom/movable/AM)
+/obj/item/pizzabox/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
if(boxes.len >= 2 && prob(20 * boxes.len))
disperse_pizzas()
diff --git a/code/modules/holiday/halloween/jacqueen.dm b/code/modules/holiday/halloween/jacqueen.dm
index b2f69df2e4..b033755650 100644
--- a/code/modules/holiday/halloween/jacqueen.dm
+++ b/code/modules/holiday/halloween/jacqueen.dm
@@ -422,7 +422,7 @@
reagents.add_reagent(R, 30)
name = "[R] Potion"
-/obj/item/reagent_containers/potion_container/throw_impact(atom/target)
+/obj/item/reagent_containers/potion_container/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
..()
sleep(20)
var/datum/effect_system/smoke_spread/chem/s = new()
diff --git a/code/modules/holodeck/items.dm b/code/modules/holodeck/items.dm
index 5c143f7a25..54c940dfbc 100644
--- a/code/modules/holodeck/items.dm
+++ b/code/modules/holodeck/items.dm
@@ -80,7 +80,7 @@
item_state = "dodgeball"
desc = "Used for playing the most violent and degrading of childhood games."
-/obj/item/toy/beach_ball/holoball/dodgeball/throw_impact(atom/hit_atom)
+/obj/item/toy/beach_ball/holoball/dodgeball/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
..()
if((ishuman(hit_atom)))
var/mob/living/carbon/M = hit_atom
@@ -123,7 +123,7 @@
else
..()
-/obj/structure/holohoop/hitby(atom/movable/AM)
+/obj/structure/holohoop/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum)
if (isitem(AM) && !istype(AM,/obj/item/projectile))
if(prob(50))
AM.forceMove(get_turf(src))
diff --git a/code/modules/hydroponics/grown.dm b/code/modules/hydroponics/grown.dm
index 096c7b5b2b..5cc7fcc57a 100644
--- a/code/modules/hydroponics/grown.dm
+++ b/code/modules/hydroponics/grown.dm
@@ -91,7 +91,7 @@
squash(user)
..()
-/obj/item/reagent_containers/food/snacks/grown/throw_impact(atom/hit_atom)
+/obj/item/reagent_containers/food/snacks/grown/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
if(!..()) //was it caught by a mob?
if(seed)
for(var/datum/plant_gene/trait/T in seed.genes)
diff --git a/code/modules/hydroponics/growninedible.dm b/code/modules/hydroponics/growninedible.dm
index c0e3beaf79..3ef6c1472f 100644
--- a/code/modules/hydroponics/growninedible.dm
+++ b/code/modules/hydroponics/growninedible.dm
@@ -48,7 +48,7 @@
return 1
return 0
-/obj/item/grown/throw_impact(atom/hit_atom)
+/obj/item/grown/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
if(!..()) //was it caught by a mob?
if(seed)
for(var/datum/plant_gene/trait/T in seed.genes)
diff --git a/code/modules/mining/fulton.dm b/code/modules/mining/fulton.dm
index f97d3612a8..af8860c02b 100644
--- a/code/modules/mining/fulton.dm
+++ b/code/modules/mining/fulton.dm
@@ -11,6 +11,7 @@ GLOBAL_LIST_EMPTY(total_extraction_beacons)
var/uses_left = 3
var/can_use_indoors
var/safe_for_living_creatures = 1
+ var/max_force_fulton = MOVE_FORCE_STRONG
/obj/item/extraction_pack/examine()
. = ..()
@@ -57,7 +58,7 @@ GLOBAL_LIST_EMPTY(total_extraction_beacons)
return
if(!isturf(A.loc)) // no extracting stuff inside other stuff
return
- if(A.anchored)
+ if(A.anchored || (A.move_resist > max_force_fulton))
return
to_chat(user, "You start attaching the pack to [A]...")
if(do_after(user,50,target=A))
diff --git a/code/modules/mining/ores_coins.dm b/code/modules/mining/ores_coins.dm
index 69361e8685..1ce735b0b8 100644
--- a/code/modules/mining/ores_coins.dm
+++ b/code/modules/mining/ores_coins.dm
@@ -99,7 +99,7 @@ GLOBAL_LIST_INIT(sand_recipes, list(\
recipes = GLOB.sand_recipes
. = ..()
-/obj/item/stack/ore/glass/throw_impact(atom/hit_atom)
+/obj/item/stack/ore/glass/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
if(..() || !ishuman(hit_atom))
return
var/mob/living/carbon/human/C = hit_atom
diff --git a/code/modules/mob/camera/camera.dm b/code/modules/mob/camera/camera.dm
index ee89167f29..dffaf9e4e7 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 = FALSE
- anchored = TRUE
+ 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/dead.dm b/code/modules/mob/dead/dead.dm
index 35dbe0828f..213a6fd6db 100644
--- a/code/modules/mob/dead/dead.dm
+++ b/code/modules/mob/dead/dead.dm
@@ -4,6 +4,7 @@ INITIALIZE_IMMEDIATE(/mob/dead)
/mob/dead
sight = SEE_TURFS | SEE_MOBS | SEE_OBJS | SEE_SELF
+ move_resist = INFINITY
throwforce = 0
/mob/dead/Initialize()
diff --git a/code/modules/mob/dead/new_player/new_player.dm b/code/modules/mob/dead/new_player/new_player.dm
index 591fc650b4..54c09184a3 100644
--- a/code/modules/mob/dead/new_player/new_player.dm
+++ b/code/modules/mob/dead/new_player/new_player.dm
@@ -12,8 +12,6 @@
stat = DEAD
canmove = FALSE
- anchored = TRUE // don't get pushed around
-
var/mob/living/new_character //for instant transfer once the round is set up
//Used to make sure someone doesn't get spammed with messages if they're ineligible for roles
@@ -151,7 +149,7 @@
message_admins(msg)
to_chat(usr, "The round is either not ready, or has already finished...")
return
-
+
if(!GLOB.enter_allowed)
to_chat(usr, "There is an administrative lock on entering the game!")
return
diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm
index d599d55886..e6a57f3180 100644
--- a/code/modules/mob/dead/observer/observer.dm
+++ b/code/modules/mob/dead/observer/observer.dm
@@ -12,7 +12,6 @@ GLOBAL_VAR_INIT(observer_default_invisibility, INVISIBILITY_OBSERVER)
stat = DEAD
density = FALSE
canmove = 0
- anchored = TRUE // don't get pushed around
see_invisible = SEE_INVISIBLE_OBSERVER
see_in_dark = 100
invisibility = INVISIBILITY_OBSERVER
@@ -619,10 +618,6 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
to_chat(src, "This creature is too powerful for you to possess!")
return 0
- if(istype (target, /mob/living/simple_animal/hostile/spawner))
- to_chat(src, "This isn't really a creature, now is it!")
- return 0
-
if(!can_reenter_round)
to_chat(src, "You are unable to re-enter the round.")
return FALSE
diff --git a/code/modules/mob/living/carbon/alien/alien_defense.dm b/code/modules/mob/living/carbon/alien/alien_defense.dm
index b9e27c0637..daffca106f 100644
--- a/code/modules/mob/living/carbon/alien/alien_defense.dm
+++ b/code/modules/mob/living/carbon/alien/alien_defense.dm
@@ -5,7 +5,7 @@
/mob/living/carbon/alien/get_ear_protection()
return 2 //no ears
-/mob/living/carbon/alien/hitby(atom/movable/AM, skipcatch, hitpush)
+/mob/living/carbon/alien/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum)
..(AM, skipcatch = TRUE, hitpush = FALSE)
diff --git a/code/modules/mob/living/carbon/alien/humanoid/caste/hunter.dm b/code/modules/mob/living/carbon/alien/humanoid/caste/hunter.dm
index fe682b5c99..c75718e65c 100644
--- a/code/modules/mob/living/carbon/alien/humanoid/caste/hunter.dm
+++ b/code/modules/mob/living/carbon/alien/humanoid/caste/hunter.dm
@@ -54,18 +54,18 @@
weather_immunities -= "lava"
update_icons()
-/mob/living/carbon/alien/humanoid/hunter/throw_impact(atom/A)
+/mob/living/carbon/alien/humanoid/hunter/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
if(!leaping)
return ..()
pounce_cooldown = world.time + pounce_cooldown_time
- if(A)
- if(isliving(A))
- var/mob/living/L = A
+ if(hit_atom)
+ if(isliving(hit_atom))
+ var/mob/living/L = hit_atom
var/blocked = FALSE
- if(ishuman(A))
- var/mob/living/carbon/human/H = A
+ if(ishuman(hit_atom))
+ var/mob/living/carbon/human/H = hit_atom
if(H.check_shields(src, 0, "the [name]", attack_type = LEAP_ATTACK))
blocked = TRUE
if(!blocked)
@@ -77,8 +77,8 @@
Knockdown(40, 1, 1)
toggle_leap(0)
- else if(A.density && !A.CanPass(src))
- visible_message("[src] smashes into [A]!", "[src] smashes into [A]!")
+ else if(hit_atom.density && !hit_atom.CanPass(src))
+ visible_message("[src] smashes into [hit_atom]!", "[src] smashes into [hit_atom]!")
Knockdown(40, 1, 1)
if(leaping)
diff --git a/code/modules/mob/living/carbon/alien/larva/larva.dm b/code/modules/mob/living/carbon/alien/larva/larva.dm
index e1e079cbae..3b150a2264 100644
--- a/code/modules/mob/living/carbon/alien/larva/larva.dm
+++ b/code/modules/mob/living/carbon/alien/larva/larva.dm
@@ -57,7 +57,7 @@
/mob/living/carbon/alien/larva/toggle_throw_mode()
return
-/mob/living/carbon/alien/larva/start_pulling()
+/mob/living/carbon/alien/larva/start_pulling(atom/movable/AM, state, force = move_force, supress_message = FALSE)
return
/mob/living/carbon/alien/larva/stripPanelUnequip(obj/item/what, mob/who)
diff --git a/code/modules/mob/living/carbon/alien/special/facehugger.dm b/code/modules/mob/living/carbon/alien/special/facehugger.dm
index e66d70f492..6b04233464 100644
--- a/code/modules/mob/living/carbon/alien/special/facehugger.dm
+++ b/code/modules/mob/living/carbon/alien/special/facehugger.dm
@@ -115,7 +115,7 @@
if(icon_state == "[initial(icon_state)]_thrown")
icon_state = "[initial(icon_state)]"
-/obj/item/clothing/mask/facehugger/throw_impact(atom/hit_atom)
+/obj/item/clothing/mask/facehugger/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
..()
if(stat == CONSCIOUS)
icon_state = "[initial(icon_state)]"
diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm
index 263edf42c2..a81545fbe5 100644
--- a/code/modules/mob/living/carbon/carbon.dm
+++ b/code/modules/mob/living/carbon/carbon.dm
@@ -94,15 +94,13 @@
return 1
return ..()
-/mob/living/carbon/throw_impact(atom/hit_atom, throwingdatum)
+/mob/living/carbon/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
. = ..()
var/hurt = TRUE
- if(istype(throwingdatum, /datum/thrownthing))
- var/datum/thrownthing/D = throwingdatum
- if(iscyborg(D.thrower))
- var/mob/living/silicon/robot/R = D.thrower
- if(!R.emagged)
- hurt = FALSE
+ if(throwingdatum?.thrower && iscyborg(throwingdatum.thrower))
+ var/mob/living/silicon/robot/R = throwingdatum.thrower
+ if(!R.emagged)
+ hurt = FALSE
if(hit_atom.density && isturf(hit_atom))
if(hurt)
Knockdown(20)
@@ -160,7 +158,7 @@
//END OF CIT CHANGES
var/atom/movable/thrown_thing
- var/obj/item/I = src.get_active_held_item()
+ var/obj/item/I = get_active_held_item()
if(!I)
if(pulling && isliving(pulling) && grab_state >= GRAB_AGGRESSIVE)
@@ -188,11 +186,11 @@
if(thrown_thing)
visible_message("[src] has thrown [thrown_thing].")
- src.log_message("has thrown [thrown_thing]", LOG_ATTACK)
+ log_message("has thrown [thrown_thing]", LOG_ATTACK)
do_attack_animation(target, no_effect = 1)
playsound(loc, 'sound/weapons/punchmiss.ogg', 50, 1, -1)
newtonian_move(get_dir(target, src))
- thrown_thing.throw_at(target, thrown_thing.throw_range, thrown_thing.throw_speed, src)
+ thrown_thing.safe_throw_at(target, thrown_thing.throw_range, thrown_thing.throw_speed, src, null, null, null, move_force)
/mob/living/carbon/restrained(ignore_grab)
. = (handcuffed || (!ignore_grab && pulledby && pulledby.grab_state >= GRAB_AGGRESSIVE))
@@ -448,7 +446,7 @@
I.throw_at(target,I.throw_range,I.throw_speed,src)
if(61 to 90) //throw it down to the floor
var/turf/target = get_turf(loc)
- I.throw_at(target,I.throw_range,I.throw_speed,src)
+ I.safe_throw_at(target,I.throw_range,I.throw_speed,src, force = move_force)
/mob/living/carbon/Stat()
..()
diff --git a/code/modules/mob/living/carbon/carbon_defense.dm b/code/modules/mob/living/carbon/carbon_defense.dm
index bc64c8303b..626923a003 100644
--- a/code/modules/mob/living/carbon/carbon_defense.dm
+++ b/code/modules/mob/living/carbon/carbon_defense.dm
@@ -58,7 +58,7 @@
return
return TRUE
-/mob/living/carbon/hitby(atom/movable/AM, skipcatch, hitpush = TRUE, blocked = FALSE)
+/mob/living/carbon/hitby(atom/movable/AM, skipcatch, hitpush = TRUE, blocked = FALSE, datum/thrownthing/throwingdatum)
if(!skipcatch) //ugly, but easy
if(can_catch_item())
if(istype(AM, /obj/item))
diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm
index a44779e05e..3333459dca 100644
--- a/code/modules/mob/living/carbon/human/human_defense.dm
+++ b/code/modules/mob/living/carbon/human/human_defense.dm
@@ -124,7 +124,7 @@
return TRUE
return FALSE
-/mob/living/carbon/human/hitby(atom/movable/AM, skipcatch = FALSE, hitpush = TRUE, blocked = FALSE)
+/mob/living/carbon/human/hitby(atom/movable/AM, skipcatch = FALSE, hitpush = TRUE, blocked = FALSE, datum/thrownthing/throwingdatum)
if(dna && dna.species)
var/spec_return = dna.species.spec_hitby(AM, src)
if(spec_return)
diff --git a/code/modules/mob/living/carbon/monkey/combat.dm b/code/modules/mob/living/carbon/monkey/combat.dm
index 25bc243f07..180c638448 100644
--- a/code/modules/mob/living/carbon/monkey/combat.dm
+++ b/code/modules/mob/living/carbon/monkey/combat.dm
@@ -384,7 +384,7 @@
retaliate(Proj.firer)
..()
-/mob/living/carbon/monkey/hitby(atom/movable/AM, skipcatch = FALSE, hitpush = TRUE, blocked = FALSE)
+/mob/living/carbon/monkey/hitby(atom/movable/AM, skipcatch = FALSE, hitpush = TRUE, blocked = FALSE, datum/thrownthing/throwingdatum)
if(istype(AM, /obj/item))
var/obj/item/I = AM
if(I.throwforce < src.health && I.thrownby && ishuman(I.thrownby))
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index 812733ebe2..5afd2adace 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -59,7 +59,7 @@
return
if(ismovableatom(A))
var/atom/movable/AM = A
- if(PushAM(AM))
+ if(PushAM(AM, move_force))
return
/mob/living/Bumped(atom/movable/AM)
@@ -202,33 +202,43 @@
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/start_pulling(atom/movable/AM, supress_message = 0)
+/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)))
diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm
index 9d04f288cd..fe35f1bf57 100644
--- a/code/modules/mob/living/living_defense.dm
+++ b/code/modules/mob/living/living_defense.dm
@@ -55,7 +55,7 @@
else
return 0
-/mob/living/hitby(atom/movable/AM, skipcatch, hitpush = TRUE, blocked = FALSE)
+/mob/living/hitby(atom/movable/AM, skipcatch, hitpush = TRUE, blocked = FALSE, datum/thrownthing/throwingdatum)
if(istype(AM, /obj/item))
var/obj/item/I = AM
var/zone = ran_zone(BODY_ZONE_CHEST, 65)//Hits a random part of the body, geared towards the chest
@@ -120,7 +120,7 @@
adjust_fire_stacks(3)
IgniteMob()
-/mob/living/proc/grabbedby(mob/living/carbon/user, supress_message = 0)
+/mob/living/proc/grabbedby(mob/living/carbon/user, supress_message = FALSE)
if(user == anchored || !isturf(user.loc))
return FALSE
@@ -146,7 +146,7 @@
return FALSE
if(!user.pulling || user.pulling != src)
- user.start_pulling(src, supress_message)
+ user.start_pulling(src, supress_message = supress_message)
return
if(!(status_flags & CANPUSH) || HAS_TRAIT(src, TRAIT_PUSHIMMUNE))
diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm
index 1b1eb1a805..551d9f4a99 100644
--- a/code/modules/mob/living/silicon/ai/ai.dm
+++ b/code/modules/mob/living/silicon/ai/ai.dm
@@ -16,7 +16,7 @@
name = "AI"
icon = 'icons/mob/ai.dmi'
icon_state = "ai"
- anchored = TRUE
+ move_resist = MOVE_FORCE_VERY_STRONG
density = TRUE
canmove = FALSE
status_flags = CANSTUN|CANPUSH
@@ -318,9 +318,14 @@
return // stop
if(incapacitated())
return
- 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, "You are now [anchored ? "" : "un"]anchored.")
+ to_chat(src, "You are now [is_anchored ? "" : "un"]anchored.")
// the message in the [] will change depending whether or not the AI is anchored
/mob/living/silicon/ai/update_canmove() //If the AI dies, mobs won't go through it anymore
diff --git a/code/modules/mob/living/silicon/ai/death.dm b/code/modules/mob/living/silicon/ai/death.dm
index 301d2bd218..319d94611b 100644
--- a/code/modules/mob/living/silicon/ai/death.dm
+++ b/code/modules/mob/living/silicon/ai/death.dm
@@ -14,7 +14,7 @@
cameraFollow = null
- anchored = FALSE //unbolt floorbolts
+ move_resist = MOVE_FORCE_NORMAL
update_canmove()
if(eyeobj)
eyeobj.setLoc(get_turf(src))
diff --git a/code/modules/mob/living/silicon/pai/pai_shell.dm b/code/modules/mob/living/silicon/pai/pai_shell.dm
index 164a3e7389..ecb80961cb 100644
--- a/code/modules/mob/living/silicon/pai/pai_shell.dm
+++ b/code/modules/mob/living/silicon/pai/pai_shell.dm
@@ -99,7 +99,7 @@
if(loc != card)
visible_message("[src] [rest? "lays down for a moment..." : "perks up from the ground"]")
-/mob/living/silicon/pai/start_pulling(atom/movable/AM)
+/mob/living/silicon/pai/start_pulling(atom/movable/AM, state, force = move_force, supress_message = FALSE)
return FALSE
/mob/living/silicon/pai/proc/toggle_integrated_light()
diff --git a/code/modules/mob/living/simple_animal/bot/honkbot.dm b/code/modules/mob/living/simple_animal/bot/honkbot.dm
index 1c19cd82a1..62667c0e58 100644
--- a/code/modules/mob/living/simple_animal/bot/honkbot.dm
+++ b/code/modules/mob/living/simple_animal/bot/honkbot.dm
@@ -155,7 +155,7 @@ Maintenance panel panel is [open ? "opened" : "closed"]"},
bike_horn(A)
-/mob/living/simple_animal/bot/honkbot/hitby(atom/movable/AM, skipcatch = 0, hitpush = 1, blocked = 0)
+/mob/living/simple_animal/bot/honkbot/hitby(atom/movable/AM, skipcatch = FALSE, hitpush = TRUE, blocked = FALSE, datum/thrownthing/throwingdatum)
if(istype(AM, /obj/item))
playsound(src, honksound, 50, TRUE, -1)
var/obj/item/I = AM
diff --git a/code/modules/mob/living/simple_animal/bot/mulebot.dm b/code/modules/mob/living/simple_animal/bot/mulebot.dm
index 1bc7493684..66bfd64a7d 100644
--- a/code/modules/mob/living/simple_animal/bot/mulebot.dm
+++ b/code/modules/mob/living/simple_animal/bot/mulebot.dm
@@ -13,8 +13,8 @@
desc = "A Multiple Utility Load Effector bot."
icon_state = "mulebot0"
density = TRUE
- anchored = TRUE
- 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/bot/secbot.dm b/code/modules/mob/living/simple_animal/bot/secbot.dm
index f22139ac22..fe02339403 100644
--- a/code/modules/mob/living/simple_animal/bot/secbot.dm
+++ b/code/modules/mob/living/simple_animal/bot/secbot.dm
@@ -221,7 +221,7 @@ Auto Patrol: []"},
..()
-/mob/living/simple_animal/bot/secbot/hitby(atom/movable/AM, skipcatch = FALSE, hitpush = TRUE, blocked = FALSE)
+/mob/living/simple_animal/bot/secbot/hitby(atom/movable/AM, skipcatch = FALSE, hitpush = TRUE, blocked = FALSE, datum/thrownthing/throwingdatum)
if(istype(AM, /obj/item))
var/obj/item/I = AM
if(I.throwforce < src.health && I.thrownby && ishuman(I.thrownby))
diff --git a/code/modules/mob/living/simple_animal/constructs.dm b/code/modules/mob/living/simple_animal/constructs.dm
index 3cc8822d02..ea7f5fe5ae 100644
--- a/code/modules/mob/living/simple_animal/constructs.dm
+++ b/code/modules/mob/living/simple_animal/constructs.dm
@@ -338,7 +338,7 @@
stored_pulling.forceMove(loc)
forceMove(AM)
if(stored_pulling)
- start_pulling(stored_pulling, TRUE) //drag anything we're pulling through the wall with us by magic
+ start_pulling(stored_pulling, supress_message = TRUE) //drag anything we're pulling through the wall with us by magic
/mob/living/simple_animal/hostile/construct/harvester/AttackingTarget()
if(iscarbon(target))
diff --git a/code/modules/mob/living/simple_animal/guardian/types/charger.dm b/code/modules/mob/living/simple_animal/guardian/types/charger.dm
index 3ece5d4e27..184da11d02 100644
--- a/code/modules/mob/living/simple_animal/guardian/types/charger.dm
+++ b/code/modules/mob/living/simple_animal/guardian/types/charger.dm
@@ -44,18 +44,18 @@
if(!charging)
..()
-/mob/living/simple_animal/hostile/guardian/charger/throw_impact(atom/A)
+/mob/living/simple_animal/hostile/guardian/charger/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
if(!charging)
return ..()
- else if(A)
- if(isliving(A) && A != summoner)
- var/mob/living/L = A
+ else if(hit_atom)
+ if(isliving(hit_atom) && hit_atom != summoner)
+ var/mob/living/L = hit_atom
var/blocked = FALSE
- if(hasmatchingsummoner(A)) //if the summoner matches don't hurt them
+ if(hasmatchingsummoner(hit_atom)) //if the summoner matches don't hurt them
blocked = TRUE
- if(ishuman(A))
- var/mob/living/carbon/human/H = A
+ if(ishuman(hit_atom))
+ var/mob/living/carbon/human/H = hit_atom
if(H.check_shields(src, 90, "[name]", attack_type = THROWN_PROJECTILE_ATTACK))
blocked = TRUE
if(!blocked)
diff --git a/code/modules/mob/living/simple_animal/hostile/jungle/leaper.dm b/code/modules/mob/living/simple_animal/hostile/jungle/leaper.dm
index d1e8f1f49e..72ae49eb0d 100644
--- a/code/modules/mob/living/simple_animal/hostile/jungle/leaper.dm
+++ b/code/modules/mob/living/simple_animal/hostile/jungle/leaper.dm
@@ -249,7 +249,7 @@
/mob/living/simple_animal/hostile/jungle/leaper/Goto()
return
-/mob/living/simple_animal/hostile/jungle/leaper/throw_impact()
+/mob/living/simple_animal/hostile/jungle/leaper/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
return
/mob/living/simple_animal/hostile/jungle/leaper/update_icons()
diff --git a/code/modules/mob/living/simple_animal/hostile/jungle/mook.dm b/code/modules/mob/living/simple_animal/hostile/jungle/mook.dm
index 29f48c7295..b786637592 100644
--- a/code/modules/mob/living/simple_animal/hostile/jungle/mook.dm
+++ b/code/modules/mob/living/simple_animal/hostile/jungle/mook.dm
@@ -147,7 +147,7 @@
update_icons()
Goto(target, move_to_delay, minimum_distance)
-/mob/living/simple_animal/hostile/jungle/mook/throw_impact(atom/hit_atom, throwingdatum)
+/mob/living/simple_animal/hostile/jungle/mook/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
. = ..()
if(isliving(hit_atom) && attack_state == MOOK_ATTACK_ACTIVE)
var/mob/living/L = hit_atom
diff --git a/code/modules/mob/living/simple_animal/hostile/jungle/seedling.dm b/code/modules/mob/living/simple_animal/hostile/jungle/seedling.dm
index 1a894734d8..8a1cfb7125 100644
--- a/code/modules/mob/living/simple_animal/hostile/jungle/seedling.dm
+++ b/code/modules/mob/living/simple_animal/hostile/jungle/seedling.dm
@@ -29,7 +29,7 @@
projectilesound = 'sound/weapons/pierce.ogg'
robust_searching = TRUE
stat_attack = UNCONSCIOUS
- anchored = TRUE
+ move_resist = MOVE_FORCE_EXTREMELY_STRONG
var/combatant_state = SEEDLING_STATE_NEUTRAL
var/obj/seedling_weakpoint/weak_point
var/mob/living/beam_debuff_target
diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm
index 45745bd84e..83abde2fca 100644
--- a/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm
@@ -158,12 +158,12 @@ Difficulty: Hard
DestroySurroundings()
..()
-/mob/living/simple_animal/hostile/megafauna/bubblegum/throw_impact(atom/A)
+/mob/living/simple_animal/hostile/megafauna/bubblegum/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
if(!charging)
return ..()
- else if(isliving(A))
- var/mob/living/L = A
+ else if(isliving(hit_atom))
+ var/mob/living/L = hit_atom
L.visible_message("[src] slams into [L]!", "[src] slams into you!")
L.apply_damage(40, BRUTE)
playsound(get_turf(L), 'sound/effects/meteorimpact.ogg', 100, 1)
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 d62f59cdd7..2ac8dd80c0 100644
--- a/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm
@@ -21,7 +21,9 @@
maxbodytemp = INFINITY
vision_range = 4
aggro_vision_range = 15
- anchored = TRUE
+ move_force = MOVE_FORCE_OVERPOWERING
+ move_resist = MOVE_FORCE_OVERPOWERING
+ pull_force = MOVE_FORCE_OVERPOWERING
mob_size = MOB_SIZE_LARGE
layer = LARGE_MOB_LAYER //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/megafauna/swarmer.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/swarmer.dm
index 786d7a9775..c7d90f9556 100644
--- a/code/modules/mob/living/simple_animal/hostile/megafauna/swarmer.dm
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/swarmer.dm
@@ -54,7 +54,6 @@ GLOBAL_LIST_INIT(AISwarmerCapsByType, list(/mob/living/simple_animal/hostile/swa
weather_immunities = list("lava","ash")
stop_automated_movement = TRUE
wander = FALSE
- anchored = TRUE
layer = BELOW_MOB_LAYER
AIStatus = AI_OFF
var/swarmer_spawn_cooldown = 0
diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/goliath.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/goliath.dm
index 15f2a0a834..c17e98a7db 100644
--- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/goliath.dm
+++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/goliath.dm
@@ -27,7 +27,9 @@
throw_message = "does nothing to the rocky hide of the"
vision_range = 4
aggro_vision_range = 7
- anchored = TRUE //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
var/pre_attack_icon = "Goliath_preattack"
loot = list(/obj/item/stack/sheet/animalhide/goliath_hide)
@@ -51,7 +53,9 @@
. = 1
/mob/living/simple_animal/hostile/asteroid/goliath/death(gibbed)
- anchored = FALSE
+ move_force = MOVE_FORCE_DEFAULT
+ move_resist = MOVE_RESIST_DEFAULT
+ pull_force = PULL_FORCE_DEFAULT
..(gibbed)
/mob/living/simple_animal/hostile/asteroid/goliath/OpenFire()
diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/hivelord.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/hivelord.dm
index 82f85766c2..9e398ce0da 100644
--- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/hivelord.dm
+++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/hivelord.dm
@@ -217,7 +217,7 @@
can_infest_dead = TRUE
//Legion that spawns Legions
-/mob/living/simple_animal/hostile/spawner/legion
+/mob/living/simple_animal/hostile/big_legion
name = "legion"
desc = "One of many."
icon = 'icons/mob/lavaland/64x64megafauna.dmi'
@@ -226,10 +226,6 @@
icon_dead = "legion"
health = 450
maxHealth = 450
- max_mobs = 3
- spawn_time = 200
- spawn_text = "peels itself off from"
- mob_types = list(/mob/living/simple_animal/hostile/asteroid/hivelord/legion)
melee_damage_lower = 20
melee_damage_upper = 20
anchored = FALSE
@@ -252,6 +248,10 @@
see_in_dark = 8
lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE
+/mob/living/simple_animal/hostile/big_legion/Initialize()
+ .=..()
+ AddComponent(/datum/component/spawner, list(/mob/living/simple_animal/hostile/asteroid/hivelord/legion), 200, faction, "peels itself off from", 3)
+
//Tendril-spawned Legion remains, the charred skeletons of those whose bodies sank into laval or fell into chasms.
/obj/effect/mob_spawn/human/corpse/charredskeleton
name = "charred skeletal remains"
diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/mining_mobs.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/mining_mobs.dm
index 05dec578bd..c040931979 100644
--- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/mining_mobs.dm
+++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/mining_mobs.dm
@@ -45,7 +45,7 @@
visible_message("[P] has a reduced effect on [src]!")
..()
-/mob/living/simple_animal/hostile/asteroid/hitby(atom/movable/AM)//No floor tiling them to death, wiseguy
+/mob/living/simple_animal/hostile/asteroid/hitby(atom/movable/AM, skipcatch = FALSE, hitpush = TRUE, blocked = FALSE, datum/thrownthing/throwingdatum)//No floor tiling them to death, wiseguy
if(istype(AM, /obj/item))
var/obj/item/T = AM
if(!stat)
diff --git a/code/modules/mob/living/simple_animal/hostile/mushroom.dm b/code/modules/mob/living/simple_animal/hostile/mushroom.dm
index dbaa8ab5fa..67a2282abc 100644
--- a/code/modules/mob/living/simple_animal/hostile/mushroom.dm
+++ b/code/modules/mob/living/simple_animal/hostile/mushroom.dm
@@ -170,7 +170,7 @@
if(M.a_intent == INTENT_HARM)
Bruise()
-/mob/living/simple_animal/hostile/mushroom/hitby(atom/movable/AM)
+/mob/living/simple_animal/hostile/mushroom/hitby(atom/movable/AM, skipcatch = FALSE, hitpush = TRUE, blocked = FALSE, datum/thrownthing/throwingdatum)
..()
if(istype(AM, /obj/item))
var/obj/item/T = AM
diff --git a/code/modules/mob/living/simple_animal/hostile/netherworld.dm b/code/modules/mob/living/simple_animal/hostile/netherworld.dm
index 8210fd6490..d144ad272b 100644
--- a/code/modules/mob/living/simple_animal/hostile/netherworld.dm
+++ b/code/modules/mob/living/simple_animal/hostile/netherworld.dm
@@ -65,33 +65,30 @@
attacktext = "punches"
deathmessage = "falls apart into a fine dust."
-/mob/living/simple_animal/hostile/spawner/nether
+/obj/structure/spawner/nether
name = "netherworld link"
desc = "A direct link to another dimension full of creatures not very happy to see you. Entering the link would be a very bad idea."
icon_state = "nether"
- icon_living = "nether"
- health = 50
- maxHealth = 50
+ max_integrity = 50
spawn_time = 600 //1 minute
max_mobs = 15
- mob_biotypes = list(MOB_INORGANIC)
icon = 'icons/mob/nest.dmi'
spawn_text = "crawls through"
mob_types = list(/mob/living/simple_animal/hostile/netherworld/migo, /mob/living/simple_animal/hostile/netherworld, /mob/living/simple_animal/hostile/netherworld/blankbody)
- atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
faction = list("nether")
- deathmessage = "shatters into oblivion."
- del_on_death = TRUE
-/mob/living/simple_animal/hostile/spawner/nether/attack_hand(mob/user)
+/obj/structure/spawner/nether/Initialize()
+ .=..()
+ START_PROCESSING(SSprocessing, src)
+
+/obj/structure/spawner/nether/attack_hand(mob/user)
user.visible_message("[user] is violently pulled into the link!", \
"Touching the portal, you are quickly pulled through into a world of unimaginable horror!")
contents.Add(user)
-/mob/living/simple_animal/hostile/spawner/nether/Life()
- ..()
- var/list/C = src.get_contents()
- for(var/mob/living/M in C)
+
+/obj/structure/spawner/nether/process()
+ for(var/mob/living/M in contents)
if(M)
playsound(src, 'sound/magic/demon_consume.ogg', 50, 1)
M.adjustBruteLoss(60)
diff --git a/code/modules/mob/living/simple_animal/hostile/statue.dm b/code/modules/mob/living/simple_animal/hostile/statue.dm
index 804989e71e..f38d77cd90 100644
--- a/code/modules/mob/living/simple_animal/hostile/statue.dm
+++ b/code/modules/mob/living/simple_animal/hostile/statue.dm
@@ -43,7 +43,10 @@
search_objects = 1 // So that it can see through walls
sight = SEE_SELF|SEE_MOBS|SEE_OBJS|SEE_TURFS
- anchored = TRUE
+
+ move_force = MOVE_FORCE_EXTREMELY_STRONG
+ move_resist = MOVE_FORCE_EXTREMELY_STRONG
+ pull_force = MOVE_FORCE_EXTREMELY_STRONG
var/cannot_be_seen = 1
var/mob/living/creator = null
diff --git a/code/modules/mob/living/simple_animal/simple_animal.dm b/code/modules/mob/living/simple_animal/simple_animal.dm
index 1f81899d8a..3f72022d24 100644
--- a/code/modules/mob/living/simple_animal/simple_animal.dm
+++ b/code/modules/mob/living/simple_animal/simple_animal.dm
@@ -65,7 +65,7 @@
var/buffed = 0 //In the event that you want to have a buffing effect on the mob, but don't want it to stack with other effects, any outside force that applies a buff to a simple mob should at least set this to 1, so we have something to check against
var/gold_core_spawnable = NO_SPAWN //If the mob can be spawned with a gold slime core. HOSTILE_SPAWN are spawned with plasma, FRIENDLY_SPAWN are spawned with blood
- var/mob/living/simple_animal/hostile/spawner/nest
+ var/datum/component/spawner/nest
var/sentience_type = SENTIENCE_ORGANIC // Sentience type, for slime potions
diff --git a/code/modules/mob/living/simple_animal/slime/slime.dm b/code/modules/mob/living/simple_animal/slime/slime.dm
index 2001c61e12..655933a0bd 100644
--- a/code/modules/mob/living/simple_animal/slime/slime.dm
+++ b/code/modules/mob/living/simple_animal/slime/slime.dm
@@ -61,7 +61,7 @@
var/mood = "" // To show its face
var/mutator_used = FALSE //So you can't shove a dozen mutators into a single slime
var/force_stasis = FALSE
-
+
do_footstep = TRUE
var/static/regex/slime_name_regex = new("\\w+ (baby|adult) slime \\(\\d+\\)")
@@ -245,7 +245,7 @@
/mob/living/simple_animal/slime/doUnEquip(obj/item/W)
return
-/mob/living/simple_animal/slime/start_pulling(atom/movable/AM)
+/mob/living/simple_animal/slime/start_pulling(atom/movable/AM, state, force = move_force, supress_message = FALSE)
return
/mob/living/simple_animal/slime/attack_ui(slot)
diff --git a/code/modules/mob/living/simple_animal/spawner.dm b/code/modules/mob/living/simple_animal/spawner.dm
deleted file mode 100644
index 0b97a940e4..0000000000
--- a/code/modules/mob/living/simple_animal/spawner.dm
+++ /dev/null
@@ -1,113 +0,0 @@
-/mob/living/simple_animal/hostile/spawner
- name = "monster nest"
- icon = 'icons/mob/animal.dmi'
- health = 100
- maxHealth = 100
- gender = NEUTER
- var/list/spawned_mobs = list()
- var/max_mobs = 5
- var/spawn_delay = 0
- var/spawn_time = 300 //30 seconds default
- var/mob_types = list(/mob/living/simple_animal/hostile/carp)
- var/spawn_text = "emerges from"
- status_flags = 0
- anchored = TRUE
- AIStatus = AI_OFF
- a_intent = INTENT_HARM
- stop_automated_movement = 1
- wander = 0
- atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 5, "min_n2" = 0, "max_n2" = 0)
- minbodytemp = 0
- maxbodytemp = 350
- layer = BELOW_MOB_LAYER
- sentience_type = SENTIENCE_BOSS
-
-
-/mob/living/simple_animal/hostile/spawner/Destroy()
- for(var/mob/living/simple_animal/L in spawned_mobs)
- if(L.nest == src)
- L.nest = null
- spawned_mobs = null
- return ..()
-
-/mob/living/simple_animal/hostile/spawner/Life()
- . = ..()
- if(!.) // dead
- return
- spawn_mob()
-
-/mob/living/simple_animal/hostile/spawner/proc/spawn_mob()
- if(spawned_mobs.len >= max_mobs)
- return 0
- if(spawn_delay > world.time)
- return 0
- spawn_delay = world.time + spawn_time
- var/chosen_mob_type = pick(mob_types)
- var/mob/living/simple_animal/L = new chosen_mob_type(src.loc)
- L.flags_1 |= (flags_1 & ADMIN_SPAWNED_1) //If we were admin spawned, lets have our children count as that as well.
- spawned_mobs += L
- L.nest = src
- L.faction = src.faction
- visible_message("[L] [spawn_text] [src].")
-
-/mob/living/simple_animal/hostile/spawner/syndicate
- name = "warp beacon"
- icon = 'icons/obj/device.dmi'
- icon_state = "syndbeacon"
- spawn_text = "warps in from"
- mob_types = list(/mob/living/simple_animal/hostile/syndicate/ranged)
- faction = list(ROLE_SYNDICATE)
-
-/mob/living/simple_animal/hostile/spawner/skeleton
- name = "bone pit"
- desc = "A pit full of bones, and some still seem to be moving..."
- icon_state = "hole"
- icon_living = "hole"
- icon = 'icons/mob/nest.dmi'
- health = 150
- maxHealth = 150
- max_mobs = 15
- spawn_time = 150
- mob_types = list(/mob/living/simple_animal/hostile/skeleton)
- spawn_text = "climbs out of"
- atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
- faction = list("skeleton")
-
-/mob/living/simple_animal/hostile/spawner/mining
- name = "monster den"
- desc = "A hole dug into the ground, harboring all kinds of monsters found within most caves or mining asteroids."
- icon_state = "hole"
- icon_living = "hole"
- health = 200
- maxHealth = 200
- max_mobs = 3
- icon = 'icons/mob/nest.dmi'
- spawn_text = "crawls out of"
- mob_types = list(/mob/living/simple_animal/hostile/asteroid/goldgrub, /mob/living/simple_animal/hostile/asteroid/goliath, /mob/living/simple_animal/hostile/asteroid/hivelord, /mob/living/simple_animal/hostile/asteroid/basilisk, /mob/living/simple_animal/hostile/asteroid/fugu)
- atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
- faction = list("mining")
-
-/mob/living/simple_animal/hostile/spawner/mining/goldgrub
- name = "goldgrub den"
- desc = "A den housing a nest of goldgrubs, annoying but arguably much better than anything else you'll find in a nest."
- mob_types = list(/mob/living/simple_animal/hostile/asteroid/goldgrub)
-
-/mob/living/simple_animal/hostile/spawner/mining/goliath
- name = "goliath den"
- desc = "A den housing a nest of goliaths, oh god why?"
- mob_types = list(/mob/living/simple_animal/hostile/asteroid/goliath)
-
-/mob/living/simple_animal/hostile/spawner/mining/hivelord
- name = "hivelord den"
- desc = "A den housing a nest of hivelords."
- mob_types = list(/mob/living/simple_animal/hostile/asteroid/hivelord)
-
-/mob/living/simple_animal/hostile/spawner/mining/basilisk
- name = "basilisk den"
- desc = "A den housing a nest of basilisks, bring a coat."
- mob_types = list(/mob/living/simple_animal/hostile/asteroid/basilisk)
-
-/mob/living/simple_animal/hostile/spawner/mining/wumborian
- name = "wumborian fugu den"
- desc = "A den housing a nest of wumborian fugus, how do they all even fit in there?"
- mob_types = list(/mob/living/simple_animal/hostile/asteroid/fugu)
diff --git a/code/modules/mob/mob_movement.dm b/code/modules/mob/mob_movement.dm
index 82e2b11f8e..85d8f94deb 100644
--- a/code/modules/mob/mob_movement.dm
+++ b/code/modules/mob/mob_movement.dm
@@ -108,8 +108,7 @@
if((direct & (direct - 1)) && mob.loc == n) //moved diagonally successfully
add_delay *= 2
- if(mob.loc != oldloc)
- move_delay += add_delay
+ move_delay += add_delay
if(.) // If mob is null here, we deserve the runtime
if(mob.throwing)
mob.throwing.finalize(FALSE)
diff --git a/code/modules/ninja/energy_katana.dm b/code/modules/ninja/energy_katana.dm
index 97c41faf12..e6d53d914f 100644
--- a/code/modules/ninja/energy_katana.dm
+++ b/code/modules/ninja/energy_katana.dm
@@ -56,7 +56,7 @@
//If we hit the Ninja who owns this Katana, they catch it.
//Works for if the Ninja throws it or it throws itself or someone tries
//To throw it at the ninja
-/obj/item/energy_katana/throw_impact(atom/hit_atom)
+/obj/item/energy_katana/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
if(ishuman(hit_atom))
var/mob/living/carbon/human/H = hit_atom
if(istype(H.wear_suit, /obj/item/clothing/suit/space/space_ninja))
diff --git a/code/modules/paperwork/paperplane.dm b/code/modules/paperwork/paperplane.dm
index 4b08ccf608..a896eb9034 100644
--- a/code/modules/paperwork/paperplane.dm
+++ b/code/modules/paperwork/paperplane.dm
@@ -97,7 +97,7 @@
/obj/item/paperplane/throw_at(atom/target, range, speed, mob/thrower, spin=FALSE, diagonals_first = FALSE, datum/callback/callback)
. = ..(target, range, speed, thrower, FALSE, diagonals_first, callback)
-/obj/item/paperplane/throw_impact(atom/hit_atom)
+/obj/item/paperplane/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
if(iscarbon(hit_atom))
var/mob/living/carbon/C = hit_atom
if(C.can_catch_item(TRUE))
diff --git a/code/modules/power/gravitygenerator.dm b/code/modules/power/gravitygenerator.dm
index 4d8cdaa778..6cedab99a4 100644
--- a/code/modules/power/gravitygenerator.dm
+++ b/code/modules/power/gravitygenerator.dm
@@ -23,6 +23,7 @@ GLOBAL_LIST_EMPTY(gravity_generators) // We will keep track of this by adding ne
desc = "A device which produces a graviton field when set up."
icon = 'icons/obj/machines/gravity_generator.dmi'
density = TRUE
+ move_resist = INFINITY
use_power = NO_POWER_USE
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
var/sprite_number = 0
diff --git a/code/modules/power/lighting.dm b/code/modules/power/lighting.dm
index 8d99cbb485..f995993ea2 100644
--- a/code/modules/power/lighting.dm
+++ b/code/modules/power/lighting.dm
@@ -758,7 +758,7 @@
/obj/item/light/bulb/broken
status = LIGHT_BROKEN
-/obj/item/light/throw_impact(atom/hit_atom)
+/obj/item/light/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
if(!..()) //not caught by a mob
shatter()
diff --git a/code/modules/power/singularity/containment_field.dm b/code/modules/power/singularity/containment_field.dm
index 3b0f7e7f6f..491be4a83d 100644
--- a/code/modules/power/singularity/containment_field.dm
+++ b/code/modules/power/singularity/containment_field.dm
@@ -6,6 +6,7 @@
icon = 'icons/obj/singularity.dmi'
icon_state = "Contain_F"
density = FALSE
+ move_resist = INFINITY
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
use_power = NO_POWER_USE
interaction_flags_atom = NONE
diff --git a/code/modules/power/singularity/field_generator.dm b/code/modules/power/singularity/field_generator.dm
index 6be89b43f4..cc6adb6b36 100644
--- a/code/modules/power/singularity/field_generator.dm
+++ b/code/modules/power/singularity/field_generator.dm
@@ -245,6 +245,7 @@ field_generator power level display
if(state != FG_WELDED || !anchored)
turn_off()
return
+ move_resist = INFINITY
spawn(1)
setup_field(1)
spawn(2)
@@ -335,6 +336,7 @@ field_generator power level display
message_admins("A singulo exists and a containment field has failed at [ADMIN_VERBOSEJMP(T)].")
investigate_log("has failed whilst a singulo exists at [AREACOORD(T)].", INVESTIGATE_SINGULO)
O.last_warning = world.time
+ move_resist = initial(move_resist)
/obj/machinery/field/generator/shock(mob/living/user)
if(fields.len)
diff --git a/code/modules/power/singularity/singularity.dm b/code/modules/power/singularity/singularity.dm
index 35710f8d61..dbd16b8c08 100644
--- a/code/modules/power/singularity/singularity.dm
+++ b/code/modules/power/singularity/singularity.dm
@@ -7,6 +7,7 @@
icon_state = "singularity_s1"
anchored = TRUE
density = TRUE
+ move_resist = INFINITY
layer = MASSIVE_OBJ_LAYER
light_range = 6
appearance_flags = 0
diff --git a/code/modules/procedural_mapping/mapGenerators/lavaland.dm b/code/modules/procedural_mapping/mapGenerators/lavaland.dm
index e6d7b00e8d..9e50b3ae95 100644
--- a/code/modules/procedural_mapping/mapGenerators/lavaland.dm
+++ b/code/modules/procedural_mapping/mapGenerators/lavaland.dm
@@ -16,9 +16,9 @@
/datum/mapGeneratorModule/splatterLayer/lavalandTendrils
spawnableTurfs = list()
- spawnableAtoms = list(/mob/living/simple_animal/hostile/spawner/lavaland = 5,
- /mob/living/simple_animal/hostile/spawner/lavaland/legion = 5,
- /mob/living/simple_animal/hostile/spawner/lavaland/goliath = 5)
+ spawnableAtoms = list(/obj/structure/spawner/lavaland = 5,
+ /obj/structure/spawner/lavaland/legion = 5,
+ /obj/structure/spawner/lavaland/goliath = 5)
/datum/mapGenerator/lavaland/ground_only
modules = list(/datum/mapGeneratorModule/bottomLayer/lavaland_default)
diff --git a/code/modules/projectiles/ammunition/_ammunition.dm b/code/modules/projectiles/ammunition/_ammunition.dm
index 87fdbc65b0..2a7d61f268 100644
--- a/code/modules/projectiles/ammunition/_ammunition.dm
+++ b/code/modules/projectiles/ammunition/_ammunition.dm
@@ -65,7 +65,7 @@
else
return ..()
-/obj/item/ammo_casing/throw_impact(atom/A)
+/obj/item/ammo_casing/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
if(heavy_metal)
bounce_away(FALSE, NONE)
. = ..()
diff --git a/code/modules/reagents/reagent_containers.dm b/code/modules/reagents/reagent_containers.dm
index b3f9591f64..26bbb20e6a 100644
--- a/code/modules/reagents/reagent_containers.dm
+++ b/code/modules/reagents/reagent_containers.dm
@@ -78,9 +78,9 @@
reagents.expose_temperature(exposed_temperature)
..()
-/obj/item/reagent_containers/throw_impact(atom/target)
+/obj/item/reagent_containers/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
. = ..()
- SplashReagents(target, TRUE)
+ SplashReagents(hit_atom, TRUE)
/obj/item/reagent_containers/proc/bartender_check(atom/target)
. = FALSE
diff --git a/code/modules/recycling/disposal/bin.dm b/code/modules/recycling/disposal/bin.dm
index a4a6a7d473..7bddb46d50 100644
--- a/code/modules/recycling/disposal/bin.dm
+++ b/code/modules/recycling/disposal/bin.dm
@@ -355,7 +355,7 @@
ui.soft_update_fields()
-/obj/machinery/disposal/bin/hitby(atom/movable/AM)
+/obj/machinery/disposal/bin/hitby(atom/movable/AM, skipcatch = FALSE, hitpush = TRUE, blocked = FALSE, datum/thrownthing/throwingdatum)
if(isitem(AM) && AM.CanEnterDisposals())
if(prob(75))
AM.forceMove(src)
diff --git a/code/modules/ruins/objects_and_mobs/ash_walker_den.dm b/code/modules/ruins/objects_and_mobs/ash_walker_den.dm
index 5425ddbdc4..2401fc63a0 100644
--- a/code/modules/ruins/objects_and_mobs/ash_walker_den.dm
+++ b/code/modules/ruins/objects_and_mobs/ash_walker_den.dm
@@ -1,26 +1,31 @@
#define ASH_WALKER_SPAWN_THRESHOLD 2
//The ash walker den consumes corpses or unconscious mobs to create ash walker eggs. For more info on those, check ghost_role_spawners.dm
-/mob/living/simple_animal/hostile/spawner/lavaland/ash_walker
+/obj/structure/lavaland/ash_walker
name = "necropolis tendril nest"
desc = "A vile tendril of corruption. It's surrounded by a nest of rapidly growing eggs..."
+ icon = 'icons/mob/nest.dmi'
icon_state = "ash_walker_nest"
- icon_living = "ash_walker_nest"
- icon_dead = "ash_walker_nest"
- faction = list("ashwalker")
- health = 200
- maxHealth = 200
- loot = list(/obj/effect/collapse)
+ move_resist=INFINITY // just killing it tears a massive hole in the ground, let's not move it
+ anchored = TRUE
+ density = TRUE
+ resistance_flags = FIRE_PROOF | LAVA_PROOF
+ max_integrity = 200
+ var/faction = list("ashwalker")
var/meat_counter = 6
-/mob/living/simple_animal/hostile/spawner/lavaland/ash_walker/death()
+/obj/structure/lavaland/ash_walker/Initialize()
+ .=..()
+ START_PROCESSING(SSprocessing, src)
+
+/obj/structure/lavaland/ash_walker/deconstruct(disassembled)
new /obj/item/assembly/signaler/anomaly (get_step(loc, pick(GLOB.alldirs)))
- return ..()
+ new /obj/effect/collapse(loc)
-/mob/living/simple_animal/hostile/spawner/lavaland/ash_walker/Life()
+/obj/structure/lavaland/ash_walker/process()
consume()
- return ..()
+ spawn_mob()
-/mob/living/simple_animal/hostile/spawner/lavaland/ash_walker/proc/consume()
+/obj/structure/lavaland/ash_walker/proc/consume()
for(var/mob/living/H in view(src, 1)) //Only for corpse right next to/on same tile
if(H.stat)
visible_message("Serrated tendrils eagerly pull [H] to [src], tearing the body apart as its blood seeps over the eggs.")
@@ -33,9 +38,9 @@
else
meat_counter++
H.gib()
- adjustHealth(-maxHealth * 0.05)//restores 5% hp of tendril
+ obj_integrity = min(obj_integrity + max_integrity*0.05,max_integrity)//restores 5% hp of tendril
-/mob/living/simple_animal/hostile/spawner/lavaland/ash_walker/spawn_mob()
+/obj/structure/lavaland/ash_walker/proc/spawn_mob()
if(meat_counter >= ASH_WALKER_SPAWN_THRESHOLD)
new /obj/effect/mob_spawn/human/ash_walker(get_step(loc, pick(GLOB.alldirs)))
visible_message("One of the eggs swells to an unnatural size and tumbles free. It's ready to hatch!")
diff --git a/code/modules/shuttle/on_move.dm b/code/modules/shuttle/on_move.dm
index dd2d7483e3..91243e04b7 100644
--- a/code/modules/shuttle/on_move.dm
+++ b/code/modules/shuttle/on_move.dm
@@ -131,7 +131,7 @@ All ShuttleMove procs go here
var/range = throw_force * 10
range = CEILING(rand(range-(range*0.1), range+(range*0.1)), 10)/10
var/speed = range/5
- throw_at(target, range, speed)
+ safe_throw_at(target, range, speed)
/////////////////////////////////////////////////////////////////////////////////////
diff --git a/code/modules/spells/spell_types/conjure.dm b/code/modules/spells/spell_types/conjure.dm
index 3ebded7487..2b7be7b4d7 100644
--- a/code/modules/spells/spell_types/conjure.dm
+++ b/code/modules/spells/spell_types/conjure.dm
@@ -63,7 +63,7 @@
clothes_req = FALSE
charge_max = 600
cooldown_min = 200
- summon_type = list(/mob/living/simple_animal/hostile/spawner/nether)
+ summon_type = list(/obj/structure/spawner/nether)
summon_amt = 1
range = 1
cast_sound = 'sound/weapons/marauder.ogg'
diff --git a/code/modules/spells/spell_types/wizard.dm b/code/modules/spells/spell_types/wizard.dm
index c4d2c34a71..005a52a382 100644
--- a/code/modules/spells/spell_types/wizard.dm
+++ b/code/modules/spells/spell_types/wizard.dm
@@ -363,7 +363,7 @@
icon_state = "snappop"
w_class = WEIGHT_CLASS_TINY
-/obj/item/spellpacket/lightningbolt/throw_impact(atom/hit_atom)
+/obj/item/spellpacket/lightningbolt/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
if(!..())
if(isliving(hit_atom))
var/mob/living/M = hit_atom
diff --git a/code/modules/surgery/bodyparts/bodyparts.dm b/code/modules/surgery/bodyparts/bodyparts.dm
index dffb7bf355..52261eac4e 100644
--- a/code/modules/surgery/bodyparts/bodyparts.dm
+++ b/code/modules/surgery/bodyparts/bodyparts.dm
@@ -116,7 +116,7 @@
else
return ..()
-/obj/item/bodypart/throw_impact(atom/hit_atom)
+/obj/item/bodypart/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
..()
if(status != BODYPART_ROBOTIC)
playsound(get_turf(src), 'sound/misc/splort.ogg', 50, 1, -1)
diff --git a/code/modules/unit_tests/_unit_tests.dm b/code/modules/unit_tests/_unit_tests.dm
index 8da5d7c113..66f3d4f544 100644
--- a/code/modules/unit_tests/_unit_tests.dm
+++ b/code/modules/unit_tests/_unit_tests.dm
@@ -2,6 +2,7 @@
//Keep this sorted alphabetically
#ifdef UNIT_TESTS
+#include "anchored_mobs.dm"
#include "reagent_id_typos.dm"
#include "reagent_recipe_collisions.dm"
#include "spawn_humans.dm"
diff --git a/code/modules/unit_tests/anchored_mobs.dm b/code/modules/unit_tests/anchored_mobs.dm
new file mode 100644
index 0000000000..5324179bb7
--- /dev/null
+++ b/code/modules/unit_tests/anchored_mobs.dm
@@ -0,0 +1,9 @@
+/datum/unit_test/anchored_mobs/Run()
+ var/list/L = list()
+ for(var/i in typesof(/mob))
+ var/mob/M = i
+ if(initial(M.anchored))
+ L += "[i]"
+ if(!L.len)
+ return //passed!
+ Fail("The following mobs are defined as anchored. This is incompatible with the new move force/resist system and needs to be revised.: [L.Join(" ")]")
\ No newline at end of file
diff --git a/modular_citadel/code/modules/mob/living/silicon/robot/dogborg_equipment.dm b/modular_citadel/code/modules/mob/living/silicon/robot/dogborg_equipment.dm
index de3f43f8ee..adecad7885 100644
--- a/modular_citadel/code/modules/mob/living/silicon/robot/dogborg_equipment.dm
+++ b/modular_citadel/code/modules/mob/living/silicon/robot/dogborg_equipment.dm
@@ -351,17 +351,17 @@ SLEEPER CODE IS IN game/objects/items/devices/dogborg_sleeper.dm !
cell.use(750) //Less than a stunbaton since stunbatons hit everytime.
weather_immunities -= "lava"
-/mob/living/silicon/robot/throw_impact(atom/A)
+/mob/living/silicon/robot/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
if(!leaping)
return ..()
- if(A)
- if(isliving(A))
- var/mob/living/L = A
+ if(hit_atom)
+ if(isliving(hit_atom))
+ var/mob/living/L = hit_atom
var/blocked = 0
- if(ishuman(A))
- var/mob/living/carbon/human/H = A
+ if(ishuman(hit_atom))
+ var/mob/living/carbon/human/H = hit_atom
if(H.check_shields(0, "the [name]", src, attack_type = LEAP_ATTACK))
blocked = 1
if(!blocked)
@@ -377,8 +377,8 @@ SLEEPER CODE IS IN game/objects/items/devices/dogborg_sleeper.dm !
pounce_cooldown = !pounce_cooldown
spawn(pounce_cooldown_time) //3s by default
pounce_cooldown = !pounce_cooldown
- else if(A.density && !A.CanPass(src))
- visible_message("[src] smashes into [A]!", "You smash into [A]!")
+ else if(hit_atom.density && !hit_atom.CanPass(src))
+ visible_message("[src] smashes into [hit_atom]!", "You smash into [hit_atom]!")
playsound(src, 'sound/items/trayhit1.ogg', 50, 1)
Knockdown(15, 1, 1)
diff --git a/modular_citadel/code/modules/projectiles/guns/ballistic/spinfusor.dm b/modular_citadel/code/modules/projectiles/guns/ballistic/spinfusor.dm
index d16df6b285..5c4775a1e1 100644
--- a/modular_citadel/code/modules/projectiles/guns/ballistic/spinfusor.dm
+++ b/modular_citadel/code/modules/projectiles/guns/ballistic/spinfusor.dm
@@ -22,11 +22,11 @@
throwforce = 15 //still deadly when thrown
throw_speed = 3
-/obj/item/ammo_casing/caseless/spinfusor/throw_impact(atom/target) //disks detonate when thrown
+/obj/item/ammo_casing/caseless/spinfusor/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum) //disks detonate when thrown
if(!..()) // not caught in mid-air
visible_message("[src] detonates!")
- playsound(src.loc, "sparks", 50, 1)
- explosion(target, -1, -1, 1, 1, -1)
+ playsound(loc, "sparks", 50, 1)
+ explosion(hit_atom, -1, -1, 1, 1, -1)
qdel(src)
return 1
diff --git a/tgstation.dme b/tgstation.dme
index 702f34f80c..d1e526d6ea 100755
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -68,6 +68,7 @@
#include "code\__DEFINES\misc.dm"
#include "code\__DEFINES\mobs.dm"
#include "code\__DEFINES\monkeys.dm"
+#include "code\__DEFINES\move_force.dm"
#include "code\__DEFINES\movespeed_modification.dm"
#include "code\__DEFINES\nanites.dm"
#include "code\__DEFINES\networks.dm"
@@ -377,6 +378,7 @@
#include "code\datums\components\rotation.dm"
#include "code\datums\components\signal_redirect.dm"
#include "code\datums\components\slippery.dm"
+#include "code\datums\components\spawner.dm"
#include "code\datums\components\spooky.dm"
#include "code\datums\components\squeak.dm"
#include "code\datums\components\stationloving.dm"
@@ -1022,6 +1024,7 @@
#include "code\game\objects\structures\reflector.dm"
#include "code\game\objects\structures\safe.dm"
#include "code\game\objects\structures\showcase.dm"
+#include "code\game\objects\structures\spawner.dm"
#include "code\game\objects\structures\spirit_board.dm"
#include "code\game\objects\structures\statues.dm"
#include "code\game\objects\structures\table_frames.dm"
@@ -1063,6 +1066,7 @@
#include "code\game\objects\structures\crates_lockers\crates\large.dm"
#include "code\game\objects\structures\crates_lockers\crates\secure.dm"
#include "code\game\objects\structures\crates_lockers\crates\wooden.dm"
+#include "code\game\objects\structures\lavaland\necropolis_tendril.dm"
#include "code\game\objects\structures\signs\_signs.dm"
#include "code\game\objects\structures\signs\signs_departments.dm"
#include "code\game\objects\structures\signs\signs_maps.dm"
@@ -2171,7 +2175,6 @@
#include "code\modules\mob\living\simple_animal\simple_animal.dm"
#include "code\modules\mob\living\simple_animal\simple_animal_vr.dm"
#include "code\modules\mob\living\simple_animal\simplemob_vore_values.dm"
-#include "code\modules\mob\living\simple_animal\spawner.dm"
#include "code\modules\mob\living\simple_animal\status_procs.dm"
#include "code\modules\mob\living\simple_animal\bot\bot.dm"
#include "code\modules\mob\living\simple_animal\bot\cleanbot.dm"
@@ -2275,7 +2278,6 @@
#include "code\modules\mob\living\simple_animal\hostile\mining_mobs\gutlunch.dm"
#include "code\modules\mob\living\simple_animal\hostile\mining_mobs\hivelord.dm"
#include "code\modules\mob\living\simple_animal\hostile\mining_mobs\mining_mobs.dm"
-#include "code\modules\mob\living\simple_animal\hostile\mining_mobs\necropolis_tendril.dm"
#include "code\modules\mob\living\simple_animal\hostile\retaliate\bat.dm"
#include "code\modules\mob\living\simple_animal\hostile\retaliate\clown.dm"
#include "code\modules\mob\living\simple_animal\hostile\retaliate\frog.dm"