diff --git a/code/__DEFINES/flags.dm b/code/__DEFINES/flags.dm
index 9ddae527eec..0da4f75e1bf 100644
--- a/code/__DEFINES/flags.dm
+++ b/code/__DEFINES/flags.dm
@@ -42,11 +42,12 @@
the atom/checkpass() proc uses them (tables will call movable atom checkpass(PASSTABLE) for example)
*/
//flags for pass_flags
-#define PASSTABLE 1
-#define PASSGLASS 2
-#define PASSGRILLE 4
-#define PASSBLOB 8
-#define PASSMOB 16
+#define PASSTABLE 1
+#define PASSGLASS 2
+#define PASSGRILLE 4
+#define PASSBLOB 8
+#define PASSMOB 16
+#define LETPASSTHROW 32
//flags for species
diff --git a/code/_onclick/adjacent.dm b/code/_onclick/adjacent.dm
index f1db0379efc..2c2b87f9c5f 100644
--- a/code/_onclick/adjacent.dm
+++ b/code/_onclick/adjacent.dm
@@ -23,7 +23,7 @@
* If you are in the same turf, always true
* If you are vertically/horizontally adjacent, ensure there are no border objects
* If you are diagonally adjacent, ensure you can pass through at least one of the mutually adjacent square.
- * Passing through in this case ignores anything with the throwpass flag, such as tables, racks, and morgue trays.
+ * Passing through in this case ignores anything with the LETPASSTHROW pass flag, such as tables, racks, and morgue trays.
*/
/turf/Adjacent(var/atom/neighbor, var/atom/target = null)
var/turf/T0 = get_turf(neighbor)
@@ -80,28 +80,18 @@
/*
This checks if you there is uninterrupted airspace between that turf and this one.
- This is defined as any dense ON_BORDER object, or any dense object without throwpass.
+ This is defined as any dense ON_BORDER object, or any dense object without LETPASSTHROW.
The border_only flag allows you to not objects (for source and destination squares)
*/
/turf/proc/ClickCross(target_dir, border_only, target_atom = null)
for(var/obj/O in src)
- if( !O.density || O == target_atom || O.throwpass) //check if there's a dense object present on the turf
- continue // throwpass is used for anything you can click through (or the firedoor special case, see above)
+ if( !O.density || O == target_atom || (O.pass_flags & LETPASSTHROW)) //check if there's a dense object present on the turf
+ continue // LETPASSTHROW is used for anything you can click through (or the firedoor special case, see above)
- if( O.flags&ON_BORDER) // windows have throwpass but are on border, check them first
+ if( O.flags&ON_BORDER) // windows are on border, check them first
if( O.dir & target_dir || O.dir & (O.dir-1) ) // full tile windows are just diagonals mechanically
return 0 //O.dir&(O.dir-1) is false for any cardinal direction, but true for diagonal ones
else if( !border_only ) // dense, not on border, cannot pass over
return 0
return 1
-
-/*
- Aside: throwpass does not do what I thought it did originally, and is only used for checking whether or not
- a thrown object should stop after already successfully entering a square. Currently the throw code involved
- only seems to affect hitting mobs, because the checks performed against objects are already performed when
- entering or leaving the square. Since throwpass isn't used on mobs, but only on objects, it is effectively
- useless. Throwpass may later need to be removed and replaced with a passcheck (bitfield on movable atom passflags).
-
- Since I don't want to complicate the click code rework by messing with unrelated systems it won't be changed here.
-*/
\ No newline at end of file
diff --git a/code/game/atoms.dm b/code/game/atoms.dm
index 5d29949d6d7..ba8f7708085 100644
--- a/code/game/atoms.dm
+++ b/code/game/atoms.dm
@@ -6,8 +6,6 @@
var/list/fingerprintshidden
var/fingerprintslast = null
var/list/blood_DNA
- var/last_bumped = 0
- var/throwpass = 0
///Chemistry.
var/datum/reagents/reagents = null
@@ -50,26 +48,6 @@
return 0
-/atom/proc/throw_impact(atom/hit_atom,mob/thrower)
- if(istype(hit_atom,/mob/living))
- var/mob/living/M = hit_atom
- M.hitby(src,thrower)
-
- else if(isobj(hit_atom))
- var/obj/O = hit_atom
- if(!O.anchored)
- step(O, src.dir)
- O.hitby(src)
-
- else if(isturf(hit_atom))
- var/turf/T = hit_atom
- if(T.density)
- spawn(2)
- step(src, turn(src.dir, 180))
- if(istype(src,/mob/living))
- var/mob/living/M = src
- M.take_organ_damage(20)
-
/atom/proc/attack_hulk(mob/living/carbon/human/hulk, do_attack_animation = 0)
if(do_attack_animation)
hulk.changeNext_move(CLICK_CD_MELEE)
@@ -280,9 +258,10 @@ its easier to just keep the beam vertical.
/atom/proc/fire_act()
return
-/atom/proc/hitby(atom/movable/AM as mob|obj)
- return
-
+/atom/proc/hitby(atom/movable/AM, skip, var/hitpush)
+ if(density && !has_gravity(AM)) //thrown stuff bounces off dense stuff in no grav.
+ spawn(2)
+ step(AM, turn(AM.dir, 180))
var/list/blood_splatter_icons = list()
diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm
index d1a70ec7e96..8a24343e0bb 100644
--- a/code/game/atoms_movable.dm
+++ b/code/game/atoms_movable.dm
@@ -93,12 +93,12 @@
/atom/movable/Crossed(atom/movable/AM)
return
-/atom/movable/Bump(var/atom/A as mob|obj|turf|area, yes)
- if(throwing)
- throw_impact(A)
- throwing = 0
- if ((A && yes))
- A.last_bumped = world.time
+/atom/movable/Bump(atom/A, yes) //the "yes" arg is to differentiate our Bump proc from byond's, without it every Bump() call would become a double Bump().
+ if((A && yes))
+ if(throwing)
+ throwing = 0
+ throw_impact(A)
+ . = 1
A.Bumped(src)
@@ -143,7 +143,6 @@
if(!direction)
return 1
-
var/old_dir = dir
. = step(src, direction)
dir = old_dir
@@ -151,70 +150,92 @@
/atom/movable/proc/checkpass(passflag)
return pass_flags&passflag
-/atom/movable/proc/hit_check(mob/thrower) // todo: this is partly obsolete due to passflags already, add throwing stuff to mob CanPass and finish it
- if(src.throwing)
- for(var/atom/A in get_turf(src))
- if(A == src) continue
- if(istype(A,/mob/living))
- if(A:lying) continue
- src.throw_impact(A,thrower)
- if(src.throwing == 1)
- src.throwing = 0
- if(isobj(A))
- if(A.density && !A.throwpass) // **TODO: Better behaviour for windows which are dense, but shouldn't always stop movement
- src.throw_impact(A,thrower)
- src.throwing = 0
+/atom/movable/proc/throw_impact(atom/hit_atom)
+ return hit_atom.hitby(src)
-/atom/movable/proc/throw_at(atom/target, range, speed, mob/thrower, var/spin = 1)
+/atom/movable/hitby(atom/movable/AM, skip, var/hitpush = 1)
+ if(!anchored && hitpush)
+ step(src, AM.dir)
+ return ..()
+
+/atom/movable/proc/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0)
if(!target || !src || (flags & NODROP)) return 0
//use a modified version of Bresenham's algorithm to get from the atom's current position to that of the target
- src.throwing = 1
- if(spin) // turns out 1000+ spinning objects being thrown at the singularity creates lag - Iamgoofball
+ throwing = 1
+ if(spin) //if we don't want the /atom/movable to spin.
SpinAnimation(5, 1)
+
+ var/dist_travelled = 0
+ var/dist_since_sleep = 0
+
var/dist_x = abs(target.x - src.x)
var/dist_y = abs(target.y - src.y)
var/dx = (target.x > src.x) ? EAST : WEST
var/dy = (target.y > src.y) ? NORTH : SOUTH
- var/dist_travelled = 0
- var/dist_since_sleep = 0
- var/tdist_x = dist_x;
- var/tdist_y = dist_y;
- var/tdx = dx;
- var/tdy = dy;
+ var/pure_diagonal = 0
+ if(dist_x == dist_y)
+ pure_diagonal = 1
if(dist_x <= dist_y)
- tdist_x = dist_y;
- tdist_y = dist_x;
- tdx = dy;
- tdy = dx;
+ var/olddist_x = dist_x
+ var/olddx = dx
+ dist_x = dist_y
+ dist_y = olddist_x
+ dx = dy
+ dy = olddx
- var/error = tdist_x/2 - tdist_y
- while(target && (((((dist_x > dist_y) && ((src.x < target.x && dx == EAST) || (src.x > target.x && dx == WEST))) || ((dist_x <= dist_y) && ((src.y < target.y && dy == NORTH) || (src.y > target.y && dy == SOUTH))) || (src.x > target.x && dx == WEST)) && dist_travelled < range) || !has_gravity(src)))
- // only stop when we've gone the whole distance (or max throw range) and are on a non-space tile, or hit something, or hit the end of the map, or someone picks it up
- if(!src.throwing) break
- if(!istype(src.loc, /turf)) break
+ var/error = dist_x/2 - dist_y
+ var/atom/finalturf = get_turf(target)
+ var/hit = 0
- var/atom/step = get_step(src, (error < 0) ? tdy : tdx)
+ while(target && ((dist_travelled < range && loc != finalturf) || !has_gravity(src))) //stop if we reached our destination (or max range) and aren't floating
+
+ if(!istype(loc, /turf))
+ hit = 1
+ break
+
+ var/atom/step = get_step(src, get_dir(src, target))
+ if(!pure_diagonal && !diagonals_first) // not a purely diagonal trajectory and we don't want all diagonal moves to be done first
+ if(error >= 0 && get_dist(src, finalturf) > 1)
+ step = get_step(src, dx)
+ error += (error < 0) ? dist_x/2 : -dist_y
if(!step) // going off the edge of the map makes get_step return null, don't let things go off the edge
break
- src.Move(step, get_dir(loc, step))
- hit_check(thrower)
- error += (error < 0) ? tdist_x : -tdist_y;
+ Move(step, get_dir(loc, step))
+ if(!throwing) // we hit something during our move
+ hit = 1
+ break
dist_travelled++
dist_since_sleep++
if(dist_since_sleep >= speed)
dist_since_sleep = 0
sleep(1)
- //done throwing, either because it hit something or it finished moving
- src.throwing = 0
- if(isobj(src))
- src.throw_impact(get_turf(src),thrower)
+ if(!dist_since_sleep && hitcheck()) //to catch sneaky things moving on our tile during our sleep(1)
+ hit = 1
+ break
+ //done throwing, either because it hit something or it finished moving
+ throwing = 0
+ if(!hit)
+ for(var/atom/A in get_turf(src)) //looking for our target on the turf we land on.
+ if(A == target)
+ hit = 1
+ throw_impact(A)
+ return 1
+
+ throw_impact(get_turf(src)) // we haven't hit something yet and we still must, let's hit the ground.
return 1
+/atom/movable/proc/hitcheck()
+ for(var/atom/movable/AM in get_turf(src))
+ if(AM == src)
+ continue
+ if(AM.density && !(AM.pass_flags & LETPASSTHROW) && !(AM.flags & ON_BORDER))
+ throw_impact(AM)
+ return 1
//Overlays
/atom/movable/overlay
diff --git a/code/game/gamemodes/cult/cult_structures.dm b/code/game/gamemodes/cult/cult_structures.dm
index 01bab649c0c..aaac82f87df 100644
--- a/code/game/gamemodes/cult/cult_structures.dm
+++ b/code/game/gamemodes/cult/cult_structures.dm
@@ -24,17 +24,6 @@
name = "desk"
desc = "A desk covered in arcane manuscripts and tomes in unknown languages. Looking at the text makes your skin crawl"
icon_state = "tomealtar"
-// luminosity = 5
-
-//sprites for this no longer exist -Pete
-//(they were stolen from another game anyway)
-/*
-/obj/structure/cult/pillar
- name = "Pillar"
- desc = "This should not exist"
- icon_state = "pillar"
- icon = 'magic_pillar.dmi'
-*/
/obj/effect/gateway
name = "gateway"
@@ -44,13 +33,3 @@
density = 1
unacidable = 1
anchored = 1.0
-
-/obj/effect/gateway/Bumped(mob/M as mob|obj)
- spawn(0)
- return
- return
-
-/obj/effect/gateway/Crossed(AM as mob|obj)
- spawn(0)
- return
- return
\ No newline at end of file
diff --git a/code/game/machinery/bots/ed209bot.dm b/code/game/machinery/bots/ed209bot.dm
index 8aaf4173d49..140fc457c41 100644
--- a/code/game/machinery/bots/ed209bot.dm
+++ b/code/game/machinery/bots/ed209bot.dm
@@ -397,14 +397,6 @@ Auto Patrol[]"},
return 1
return 0
-/* terrible
-/obj/machinery/bot/ed209/Bumped(atom/movable/M as mob|obj)
- spawn(0)
- if (M)
- var/turf/T = get_turf(src)
- M:loc = T
-*/
-
/obj/machinery/bot/ed209/explode()
walk_to(src,0)
visible_message("[src] blows apart!")
diff --git a/code/game/machinery/computer/HolodeckControl.dm b/code/game/machinery/computer/HolodeckControl.dm
index ffaecb88a14..6a96b8ac0fb 100644
--- a/code/game/machinery/computer/HolodeckControl.dm
+++ b/code/game/machinery/computer/HolodeckControl.dm
@@ -410,6 +410,7 @@
var/mob/living/carbon/M = hit_atom
playsound(src, 'sound/items/dodgeball.ogg', 50, 1)
M.apply_damage(10, STAMINA)
+ loc = get_turf(hit_atom) //drop at the target's feet
if(prob(5))
M.Weaken(3)
visible_message("[M] is knocked right off \his feet!", 3)
@@ -421,7 +422,6 @@
icon_state = "hoop"
anchored = 1
density = 1
- throwpass = 1
/obj/structure/holohoop/attackby(obj/item/weapon/W, mob/user, params)
if (istype(W, /obj/item/weapon/grab) && get_dist(src,user)<2)
diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm
index 87ad6c9728c..129d03039b9 100644
--- a/code/game/machinery/doors/door.dm
+++ b/code/game/machinery/doors/door.dm
@@ -45,8 +45,8 @@
/obj/machinery/door/Bumped(atom/AM)
if(operating || emagged) return
- if(ismob(AM))
- var/mob/M = AM
+ if(isliving(AM))
+ var/mob/living/M = AM
if(world.time - M.last_bumped <= 10) return //Can bump-open one airlock per second. This is to prevent shock spam.
M.last_bumped = world.time
if(!M.restrained())
diff --git a/code/game/machinery/doors/firedoor.dm b/code/game/machinery/doors/firedoor.dm
index d60c8bf9e9b..27f01b8c864 100644
--- a/code/game/machinery/doors/firedoor.dm
+++ b/code/game/machinery/doors/firedoor.dm
@@ -16,8 +16,10 @@
closingLayer = 3.11
/obj/machinery/door/firedoor/Bumped(atom/AM)
- if(p_open || operating) return
- if(!density) return ..()
+ if(p_open || operating)
+ return
+ if(!density)
+ return ..()
return 0
diff --git a/code/game/machinery/recycler.dm b/code/game/machinery/recycler.dm
index 38fe78bd912..9e4b3da49d0 100644
--- a/code/game/machinery/recycler.dm
+++ b/code/game/machinery/recycler.dm
@@ -101,6 +101,8 @@ var/const/SAFETY_COOLDOWN = 100
if(stat & (BROKEN|NOPOWER))
return
+ if(!anchored)
+ return
if(safety_mode)
return
diff --git a/code/game/objects/effects/mines.dm b/code/game/objects/effects/mines.dm
index e1c0f16074a..4c159f7a431 100644
--- a/code/game/objects/effects/mines.dm
+++ b/code/game/objects/effects/mines.dm
@@ -15,17 +15,14 @@
if(isanimal(AM))
var/mob/living/simple_animal/SA = AM
if(!SA.flying)
- Bumped(SA)
+ triggermine(SA)
else
- Bumped(AM)
-
-/obj/effect/mine/Bumped(AM as mob|obj)
-
- if(triggered) return
- visible_message("[AM] sets off \icon[src] [src]!")
- triggermine(AM)
+ triggermine(AM)
/obj/effect/mine/proc/triggermine(mob/victim)
+ if(triggered)
+ return
+ visible_message("[victim] sets off \icon[src] [src]!")
var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread
s.set_up(3, 1, src)
s.start()
diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm
index 2b1e95fc358..ce2a1fc1875 100644
--- a/code/game/objects/items.dm
+++ b/code/game/objects/items.dm
@@ -55,6 +55,8 @@ var/global/image/fire_overlay = image("icon" = 'icons/effects/fire.dmi', "icon_s
var/suittoggled = 0
var/hooded = 0
+ var/mob/thrownby = null
+
/obj/item/mouse_drag_pointer = MOUSE_ACTIVE_POINTER //the icon to indicate this object is being dragged
//So items can have custom embedd values
@@ -469,26 +471,17 @@ var/global/image/fire_overlay = image("icon" = 'icons/effects/fire.dmi', "icon_s
if(!findtext(desc, "it looks slightly melted...")) //it looks slightly melted... it looks slightly melted... it looks slightly melted... etc.
desc += " it looks slightly melted..." //needs a space at the start, formatting
+/obj/item/throw_impact(atom/A)
+ var/itempush = 1
+ if(w_class < 4)
+ itempush = 0 //too light to push anything
+ return A.hitby(src,thrownby, 0, itempush)
-
-/obj/item/throw_impact(A)
- if(throw_speed >= EMBED_THROWSPEED_THRESHOLD)
- if(istype(A, /mob/living/carbon/human))
- var/mob/living/carbon/human/H = A
- if(can_embed(src))
- if(prob(embed_chance) && !(PIERCEIMMUNE in H.dna.species.specflags))
- H.throw_alert("embeddedobject")
- var/obj/item/organ/limb/L = pick(H.organs)
- L.embedded_objects |= src
- add_blood(H)//it embedded itself in you, of course it's bloody!
- loc = H
- L.take_damage(w_class*embedded_impact_pain_multiplier)
- H.visible_message("\the [name] embeds itself in [H]'s [L.getDisplayName()]!","\the [name] embeds itself in your [L.getDisplayName()]!")
- return
-
- //Reset regardless of if we hit a human.
+/obj/item/throw_at(atom/target, range, speed, mob/thrower, spin=1)
+ thrownby = thrower
+ . = ..()
throw_speed = initial(throw_speed) //explosions change this.
- ..()
+
/obj/item/proc/remove_item_from_storage(atom/newLoc) //please use this if you're going to snowflake an item out of a obj/item/weapon/storage
if(!newLoc)
diff --git a/code/game/objects/items/toys.dm b/code/game/objects/items/toys.dm
index 0b1b452afc5..a340ca49274 100644
--- a/code/game/objects/items/toys.dm
+++ b/code/game/objects/items/toys.dm
@@ -44,41 +44,40 @@
/obj/item/toy/balloon/afterattack(atom/A as mob|obj, mob/user, proximity)
if(!proximity) return
- if (istype(A, /obj/structure/reagent_dispensers/watertank) && get_dist(src,A) <= 1)
- A.reagents.trans_to(src, 10)
- user << "You fill the balloon with the contents of [A]."
- src.desc = "A translucent balloon with some form of liquid sloshing around in it."
- src.update_icon()
- return
+ if (istype(A, /obj/structure/reagent_dispensers))
+ var/obj/structure/reagent_dispensers/RD = A
+ if(RD.reagents.total_volume <= 0)
+ user << "[RD] is empty."
+ else if(reagents.total_volume >= 10)
+ user << "[src] is full."
+ else
+ A.reagents.trans_to(src, 10)
+ user << "You fill the balloon with the contents of [A]."
+ desc = "A translucent balloon with some form of liquid sloshing around in it."
+ update_icon()
/obj/item/toy/balloon/attackby(obj/O, mob/user, params)
if(istype(O, /obj/item/weapon/reagent_containers/glass))
if(O.reagents)
- if(O.reagents.total_volume < 1)
- user << "The [O] is empty."
- else if(O.reagents.total_volume >= 1)
- if(O.reagents.has_reagent("facid", 1))
- user << "The acid chews through the balloon!"
- O.reagents.reaction(user)
- qdel(src)
- else
- src.desc = "A translucent balloon with some form of liquid sloshing around in it."
- user << "You fill the balloon with the contents of [O]."
- O.reagents.trans_to(src, 10)
- src.update_icon()
- return
+ if(O.reagents.total_volume <= 0)
+ user << "[O] is empty."
+ else if(reagents.total_volume >= 10)
+ user << "[src] is full."
+ else
+ desc = "A translucent balloon with some form of liquid sloshing around in it."
+ user << "You fill the balloon with the contents of [O]."
+ O.reagents.trans_to(src, 10)
+ update_icon()
/obj/item/toy/balloon/throw_impact(atom/hit_atom)
- if(src.reagents.total_volume >= 1)
- src.visible_message("\The [src] bursts!","You hear a pop and a splash.")
- src.reagents.reaction(get_turf(hit_atom))
- for(var/atom/A in get_turf(hit_atom))
- src.reagents.reaction(A)
- src.icon_state = "burst"
- spawn(5)
- if(src)
- qdel(src)
- return
+ if(!..()) //was it caught by a mob?
+ if(reagents.total_volume >= 1)
+ visible_message("[src] bursts!","You hear a pop and a splash.")
+ reagents.reaction(get_turf(hit_atom))
+ for(var/atom/A in get_turf(hit_atom))
+ reagents.reaction(A)
+ icon_state = "burst"
+ qdel(src)
/obj/item/toy/balloon/update_icon()
if(src.reagents.total_volume >= 1)
@@ -514,20 +513,24 @@
icon_state = "snappop"
w_class = 1
-/obj/item/toy/snappop/fire_act()
- throw_impact()
- return
-
-/obj/item/toy/snappop/throw_impact(atom/hit_atom)
- ..()
+/obj/item/toy/snappop/proc/pop_burst()
var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread
s.set_up(3, 1, src)
s.start()
- new /obj/effect/decal/cleanable/ash(src.loc)
- src.visible_message("The [src.name] explodes!","You hear a snap!")
+ new /obj/effect/decal/cleanable/ash(loc)
+ visible_message("The [src.name] explodes!","You hear a snap!")
playsound(src, 'sound/effects/snap.ogg', 50, 1)
qdel(src)
+/obj/item/toy/snappop/fire_act()
+ pop_burst()
+ return
+
+/obj/item/toy/snappop/throw_impact(atom/hit_atom)
+ if(!..())
+ pop_burst()
+
+
/obj/item/toy/snappop/Crossed(H as mob|obj)
if((ishuman(H))) //i guess carp and shit shouldn't set them off
var/mob/living/carbon/M = H
@@ -1100,12 +1103,12 @@
w_class = 2.0
/obj/item/toy/minimeteor/throw_impact(atom/hit_atom)
- ..()
- playsound(src, 'sound/effects/meteorimpact.ogg', 40, 1)
- for(var/mob/M in range(10, src))
- if(!M.stat && !istype(M, /mob/living/silicon/ai))\
- shake_camera(M, 3, 1)
- qdel(src)
+ if(!..())
+ playsound(src, 'sound/effects/meteorimpact.ogg', 40, 1)
+ for(var/mob/M in range(10, src))
+ if(!M.stat && !istype(M, /mob/living/silicon/ai))\
+ shake_camera(M, 3, 1)
+ qdel(src)
/*
* Carp plushie
diff --git a/code/game/objects/items/weapons/dice.dm b/code/game/objects/items/weapons/dice.dm
index 11490f263d4..3d5800ae934 100644
--- a/code/game/objects/items/weapons/dice.dm
+++ b/code/game/objects/items/weapons/dice.dm
@@ -56,7 +56,7 @@
/obj/item/weapon/dice/attack_self(mob/user)
diceroll(user)
-/obj/item/weapon/dice/throw_at(atom/target, range, speed, mob/user)
+/obj/item/weapon/dice/throw_at(atom/target, range, speed, mob/user, spin=1)
if(!..())
return
diceroll(user)
diff --git a/code/game/objects/structures/crates_lockers/closets.dm b/code/game/objects/structures/crates_lockers/closets.dm
index a72592a458e..e41fac7870f 100644
--- a/code/game/objects/structures/crates_lockers/closets.dm
+++ b/code/game/objects/structures/crates_lockers/closets.dm
@@ -79,15 +79,20 @@
return 1
/obj/structure/closet/proc/dump_contents()
-
for(var/obj/O in src)
O.loc = loc
+ if(throwing) //you keep some momentum when getting out of a thrown closet
+ step(O, dir)
for(var/mob/M in src)
M.loc = loc
if(M.client)
M.client.eye = M.client.mob
M.client.perspective = MOB_PERSPECTIVE
+ if(throwing)
+ step(M, dir)
+ if(throwing)
+ throwing = 0
/obj/structure/closet/proc/take_contents()
@@ -100,14 +105,13 @@
return 0
if(!can_open())
return 0
- dump_contents()
-
opened = 1
if(istype(src, /obj/structure/closet/body_bag))
playsound(loc, 'sound/items/zip.ogg', 15, 1, -3)
else
playsound(loc, 'sound/machines/click.ogg', 15, 1, -3)
density = 0
+ dump_contents()
update_icon()
return 1
diff --git a/code/game/objects/structures/mineral_doors.dm b/code/game/objects/structures/mineral_doors.dm
index 23be1ce7c31..fe14690944a 100644
--- a/code/game/objects/structures/mineral_doors.dm
+++ b/code/game/objects/structures/mineral_doors.dm
@@ -65,9 +65,9 @@
/obj/structure/mineral_door/proc/TryToSwitchState(atom/user)
if(isSwitchingStates) return
- if(ismob(user))
- var/mob/M = user
- if(world.time - user.last_bumped <= 60) return //NOTE do we really need that?
+ if(isliving(user))
+ var/mob/living/M = user
+ if(world.time - M.last_bumped <= 60) return //NOTE do we really need that?
if(M.client)
if(iscarbon(M))
var/mob/living/carbon/C = M
diff --git a/code/game/objects/structures/morgue.dm b/code/game/objects/structures/morgue.dm
index d3565376d41..3bd5ef645d2 100644
--- a/code/game/objects/structures/morgue.dm
+++ b/code/game/objects/structures/morgue.dm
@@ -226,7 +226,7 @@ Crematorium Switch
layer = 2.9
var/obj/structure/bodycontainer/connected = null
anchored = 1.0
- throwpass = 1
+ pass_flags = LETPASSTHROW
/obj/structure/tray/Destroy()
if(connected)
@@ -278,3 +278,12 @@ Crematorium Switch
desc = "Apply corpse before closing."
icon_state = "morguet"
+/obj/structure/tray/m_tray/CanPass(atom/movable/mover, turf/target, height=0)
+ if(height==0) return 1
+
+ if(istype(mover) && mover.checkpass(PASSTABLE))
+ return 1
+ if(locate(/obj/structure/table) in get_turf(mover))
+ return 1
+ else
+ return 0
diff --git a/code/game/objects/structures/tables_racks.dm b/code/game/objects/structures/tables_racks.dm
index 1dbf75a2ee8..47eef2e9eb7 100644
--- a/code/game/objects/structures/tables_racks.dm
+++ b/code/game/objects/structures/tables_racks.dm
@@ -20,7 +20,7 @@
density = 1
anchored = 1.0
layer = 2.8
- throwpass = 1 //You can throw objects over this, despite it's density.")
+ pass_flags = LETPASSTHROW //You can throw objects over this, despite it's density.")
var/frame = /obj/structure/table_frame
var/framestack = /obj/item/stack/rods
var/buildstack = /obj/item/stack/sheet/metal
@@ -364,7 +364,7 @@
icon_state = "rack"
density = 1
anchored = 1.0
- throwpass = 1 //You can throw objects over this, despite it's density.
+ pass_flags = LETPASSTHROW //You can throw objects over this, despite it's density.
var/health = 5
/obj/structure/rack/ex_act(severity, target)
diff --git a/code/game/objects/weapons.dm b/code/game/objects/weapons.dm
index 803f29cb1aa..e85375ba5ed 100644
--- a/code/game/objects/weapons.dm
+++ b/code/game/objects/weapons.dm
@@ -9,8 +9,3 @@
hitsound = 'sound/items/welder.ogg'
if(damtype == "brute")
hitsound = "swing_hit"
-
-/obj/item/weapon/Bump(mob/M)
- spawn(0)
- ..()
- return
\ No newline at end of file
diff --git a/code/modules/flufftext/Hallucination.dm b/code/modules/flufftext/Hallucination.dm
index c819175a48e..8ad8c75980a 100644
--- a/code/modules/flufftext/Hallucination.dm
+++ b/code/modules/flufftext/Hallucination.dm
@@ -171,47 +171,6 @@ Gunshots/explosions/opening doors/less rare audio (done)
name = "alien hunter ([rand(1, 1000)])"
return
-/obj/effect/hallucination/simple/xeno/throw_at(atom/target, range, speed) // TODO : Make diagonal trhow into proc/property
- if(!target || !src || (flags & NODROP)) return 0
-
- src.throwing = 1
-
- var/dist_x = abs(target.x - src.x)
- var/dist_y = abs(target.y - src.y)
- var/dist_travelled = 0
- var/dist_since_sleep = 0
-
- var/tdist_x = dist_x;
- var/tdist_y = dist_y;
-
- if(dist_x <= dist_y)
- tdist_x = dist_y;
- tdist_y = dist_x;
-
- var/error = tdist_x/2 - tdist_y
- while(target && (((((dist_x > dist_y) && ((src.x < target.x) || (src.x > target.x))) || ((dist_x <= dist_y) && ((src.y < target.y) || (src.y > target.y))) || (src.x > target.x)) && dist_travelled < range) || !has_gravity(src)))
-
- if(!src.throwing) break
- if(!istype(src.loc, /turf)) break
-
- var/atom/step = get_step(src, get_dir(src,target))
- if(!step)
- break
- src.Move(step, get_dir(src, step))
- hit_check()
- error += (error < 0) ? tdist_x : -tdist_y;
- dist_travelled++
- dist_since_sleep++
- if(dist_since_sleep >= speed)
- dist_since_sleep = 0
- sleep(1)
-
-
- src.throwing = 0
- src.throw_impact(get_turf(src))
-
- return 1
-
/obj/effect/hallucination/simple/xeno/throw_impact(A)
update_icon("alienh_pounce")
if(A == target)
@@ -232,10 +191,10 @@ Gunshots/explosions/opening doors/less rare audio (done)
xeno = new(pump.loc,target)
sleep(10)
xeno.update_icon("alienh_leap",'icons/mob/alienleap.dmi',-32,-32)
- xeno.throw_at(target,7,1)
+ xeno.throw_at(target,7,1, spin = 0, diagonals_first = 1)
sleep(10)
xeno.update_icon("alienh_leap",'icons/mob/alienleap.dmi',-32,-32)
- xeno.throw_at(pump,7,1)
+ xeno.throw_at(pump,7,1, spin = 0, diagonals_first = 1)
sleep(10)
var/xeno_name = xeno.name
target << "[xeno_name] begins climbing into the ventilation system..."
diff --git a/code/modules/food&drinks/food/snacks_egg.dm b/code/modules/food&drinks/food/snacks_egg.dm
index 6a17202d6f0..473ad4ebde5 100644
--- a/code/modules/food&drinks/food/snacks_egg.dm
+++ b/code/modules/food&drinks/food/snacks_egg.dm
@@ -18,10 +18,11 @@
filling_color = "#F0E68C"
/obj/item/weapon/reagent_containers/food/snacks/egg/throw_impact(atom/hit_atom)
- ..()
- new/obj/effect/decal/cleanable/egg_smudge(src.loc)
- reagents.reaction(hit_atom, TOUCH)
- del(src) // Not qdel, because it'll hit other mobs then the floor for runtimes.
+ if(!..()) //was it caught by a mob?
+ var/turf/T = get_turf(hit_atom)
+ new/obj/effect/decal/cleanable/egg_smudge(T)
+ reagents.reaction(hit_atom, TOUCH)
+ qdel(src)
/obj/item/weapon/reagent_containers/food/snacks/egg/attackby(obj/item/weapon/W, mob/user, params)
if(istype( W, /obj/item/toy/crayon ))
diff --git a/code/modules/food&drinks/food/snacks_pie.dm b/code/modules/food&drinks/food/snacks_pie.dm
index e4fe6700f67..6a9be926630 100644
--- a/code/modules/food&drinks/food/snacks_pie.dm
+++ b/code/modules/food&drinks/food/snacks_pie.dm
@@ -23,10 +23,11 @@
list_reagents = list("nutriment" = 6, "banana" = 5, "vitamin" = 2)
/obj/item/weapon/reagent_containers/food/snacks/pie/cream/throw_impact(atom/hit_atom)
- ..()
- new/obj/effect/decal/cleanable/pie_smudge(src.loc)
- reagents.reaction(hit_atom, TOUCH)
- del(src) // Not qdel, because it'll hit other mobs then the floor for runtimes.
+ if(!..()) //was it caught by a mob?
+ var/turf/T = get_turf(hit_atom)
+ new/obj/effect/decal/cleanable/pie_smudge(T)
+ reagents.reaction(hit_atom, TOUCH)
+ qdel(src)
/obj/item/weapon/reagent_containers/food/snacks/pie/berryclafoutis
diff --git a/code/modules/hydroponics/grown.dm b/code/modules/hydroponics/grown.dm
index 6c11a41934e..f27866be2d2 100644
--- a/code/modules/hydroponics/grown.dm
+++ b/code/modules/hydroponics/grown.dm
@@ -806,17 +806,16 @@ obj/item/weapon/reagent_containers/food/snacks/grown/shell/eggy/add_juice()
bitesize = 1 + round(reagents.total_volume / 2, 1)
/obj/item/weapon/reagent_containers/food/snacks/grown/tomato/proc/squish(atom/target)
- new splat(src.loc)
- src.visible_message("The [src.name] has been squashed.","You hear a smack.")
+ var/turf/T = get_turf(target)
+ new splat(T)
+ visible_message("The [src.name] has been squashed.","You hear a smack.")
for(var/atom/A in get_turf(target))
- src.reagents.reaction(A)
+ reagents.reaction(A)
/obj/item/weapon/reagent_containers/food/snacks/grown/tomato/throw_impact(atom/hit_atom)
- ..()
- squish(hit_atom)
- del(src) // Not qdel, because it'll hit other mobs then the floor for runtimes.
- return
-
+ if(!..()) //was it caught by a mob?
+ squish(hit_atom)
+ qdel(src)
/obj/item/weapon/reagent_containers/food/snacks/grown/tomato/killer
seed = /obj/item/seeds/killertomatoseed
@@ -906,7 +905,8 @@ obj/item/weapon/reagent_containers/food/snacks/grown/shell/eggy/add_juice()
..()
var/teleport_radius = potency / 10
if(isliving(squishee))
- new /obj/effect/decal/cleanable/molten_item(squishee.loc) //Leave a pile of goo behind for dramatic effect...
+ var/turf/T = get_turf(squishee)
+ new /obj/effect/decal/cleanable/molten_item(T) //Leave a pile of goo behind for dramatic effect...
do_teleport(squishee, get_turf(squishee), teleport_radius)
diff --git a/code/modules/mob/living/carbon/alien/alien_defense.dm b/code/modules/mob/living/carbon/alien/alien_defense.dm
index fa0f58d7f07..7f1e5337575 100644
--- a/code/modules/mob/living/carbon/alien/alien_defense.dm
+++ b/code/modules/mob/living/carbon/alien/alien_defense.dm
@@ -1,5 +1,5 @@
/mob/living/carbon/alien/hitby(atom/movable/AM)
- ..(AM, 1)
+ ..(AM, skip = 1)
/*Code for aliens attacking aliens. Because aliens act on a hivemind, I don't see them as very aggressive with each other.
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 68fda08c934..dd900163da0 100644
--- a/code/modules/mob/living/carbon/alien/humanoid/caste/hunter.dm
+++ b/code/modules/mob/living/carbon/alien/humanoid/caste/hunter.dm
@@ -82,11 +82,11 @@
else //Maybe uses plasma in the future, although that wouldn't make any sense...
leaping = 1
update_icons()
- throw_at(A,MAX_ALIEN_LEAP_DIST,1)
+ throw_at(A,MAX_ALIEN_LEAP_DIST,1, spin=0, diagonals_first = 1)
leaping = 0
update_icons()
-/mob/living/carbon/alien/humanoid/hunter/throw_impact(A)
+/mob/living/carbon/alien/humanoid/hunter/throw_impact(atom/A)
if(!leaping)
return ..()
@@ -103,60 +103,19 @@
pounce_cooldown = !pounce_cooldown
spawn(pounce_cooldown_time) //3s by default
pounce_cooldown = !pounce_cooldown
- else
+ else if(A.density && !A.CanPass(src))
visible_message("[src] smashes into [A]!", "[src] smashes into [A]!")
weakened = 2
if(leaping)
leaping = 0
+ update_icons()
update_canmove()
-
/mob/living/carbon/alien/humanoid/float(on)
if(leaping)
return
..()
-//Modified throw_at() that will use diagonal dirs where appropriate
-//instead of locking it to cardinal dirs
-/mob/living/carbon/alien/humanoid/throw_at(atom/target, range, speed)
- if(!target || !src || (flags & NODROP)) return 0
-
- src.throwing = 1
-
- var/dist_x = abs(target.x - src.x)
- var/dist_y = abs(target.y - src.y)
- var/dist_travelled = 0
- var/dist_since_sleep = 0
-
- var/tdist_x = dist_x;
- var/tdist_y = dist_y;
-
- if(dist_x <= dist_y)
- tdist_x = dist_y;
- tdist_y = dist_x;
-
- var/error = tdist_x/2 - tdist_y
- while(target && (((((dist_x > dist_y) && ((src.x < target.x) || (src.x > target.x))) || ((dist_x <= dist_y) && ((src.y < target.y) || (src.y > target.y))) || (src.x > target.x)) && dist_travelled < range) || !has_gravity(src)))
-
- if(!src.throwing) break
- if(!istype(src.loc, /turf)) break
-
- var/atom/step = get_step(src, get_dir(src,target))
- if(!step)
- break
- src.Move(step, get_dir(src, step))
- hit_check()
- error += (error < 0) ? tdist_x : -tdist_y;
- dist_travelled++
- dist_since_sleep++
- if(dist_since_sleep >= speed)
- dist_since_sleep = 0
- sleep(1)
-
-
- src.throwing = 0
-
- return 1
diff --git a/code/modules/mob/living/carbon/carbon_defense.dm b/code/modules/mob/living/carbon/carbon_defense.dm
index 7ef10003c95..9b705841a82 100644
--- a/code/modules/mob/living/carbon/carbon_defense.dm
+++ b/code/modules/mob/living/carbon/carbon_defense.dm
@@ -8,8 +8,8 @@
put_in_active_hand(I)
visible_message("[src] catches [I]!")
throw_mode_off()
- return
- ..()
+ return 1
+ return ..()
/mob/living/carbon/attackby(obj/item/I, mob/user, params)
diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm
index 72a5d682d0c..01787095f8b 100644
--- a/code/modules/mob/living/carbon/human/human_defense.dm
+++ b/code/modules/mob/living/carbon/human/human_defense.dm
@@ -455,4 +455,18 @@ emp_act
else
..()
- return
+/mob/living/carbon/human/hitby(atom/movable/AM)
+ if(throw_speed >= EMBED_THROWSPEED_THRESHOLD)
+ if(istype(AM, /obj/item))
+ var/obj/item/I = AM
+ if(can_embed(I))
+ if(prob(I.embed_chance) && !(dna && (PIERCEIMMUNE in dna.species.specflags)))
+ throw_alert("embeddedobject")
+ var/obj/item/organ/limb/L = pick(organs)
+ L.embedded_objects |= I
+ I.add_blood(src)//it embedded itself in you, of course it's bloody!
+ I.loc = src
+ L.take_damage(I.w_class*I.embedded_impact_pain_multiplier)
+ visible_message("\the [I.name] embeds itself in [src]'s [L.getDisplayName()]!","\the [I.name] embeds itself in your [L.getDisplayName()]!")
+ return
+ return ..()
diff --git a/code/modules/mob/living/carbon/human/human_movement.dm b/code/modules/mob/living/carbon/human/human_movement.dm
index b0078a13719..664933c175b 100644
--- a/code/modules/mob/living/carbon/human/human_movement.dm
+++ b/code/modules/mob/living/carbon/human/human_movement.dm
@@ -45,7 +45,7 @@
return shoes && shoes.negates_gravity()
/mob/living/carbon/human/Move(NewLoc, direct)
- ..()
+ . = ..()
if(dna)
for(var/datum/mutation/human/HM in dna.mutations)
HM.on_move(src, NewLoc)
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index ea570bc40b7..bbcdc72c6aa 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -52,13 +52,14 @@ Sorry Giacom. Please don't be mad :(
//Generic Bump(). Override MobBump() and ObjBump() instead of this.
/mob/living/Bump(atom/A, yes)
+ if(..()) //we are thrown onto something
+ return
if (buckled || !yes || now_pushing)
return
if(ismob(A))
var/mob/M = A
if(MobBump(M))
return
- ..()
if(isobj(A))
var/obj/O = A
if(ObjBump(O))
@@ -68,6 +69,10 @@ Sorry Giacom. Please don't be mad :(
if(PushAM(AM))
return
+/mob/living/Bumped(atom/movable/AM)
+ ..()
+ last_bumped = world.time
+
//Called when we bump onto a mob
/mob/living/proc/MobBump(mob/M)
//Even if we don't push/swap places, we "touched" them, so spread fire
diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm
index c620bda6767..43e2c2735bf 100644
--- a/code/modules/mob/living/living_defense.dm
+++ b/code/modules/mob/living/living_defense.dm
@@ -44,7 +44,13 @@
else
return 0
-/mob/living/hitby(atom/movable/AM,mob/thrower)//Standardization and logging -Sieve
+/mob/living/throw_impact(atom/hit_atom)
+ . = ..()
+ if(hit_atom.density)
+ Weaken(1)
+ take_organ_damage(10)
+
+/mob/living/hitby(atom/movable/AM)
if(istype(AM, /obj/item))
var/obj/item/I = AM
var/zone = ran_zone("chest", 65)//Hits a random part of the body, geared towards the chest
@@ -71,9 +77,11 @@
"[src] has been hit by [I].")
var/armor = run_armor_check(zone, "melee", "Your armor has protected your [parse_zone(zone)].", "Your armor has softened hit to your [parse_zone(zone)].",I.armour_penetration)
apply_damage(I.throwforce, dtype, zone, armor, I)
-
- if(thrower)
- add_logs(thrower, src, "hit", I)
+ if(I.thrownby)
+ add_logs(I.thrownby, src, "hit", I)
+ else
+ playsound(loc, 'sound/weapons/genhit.ogg', 50, 1, -1)
+ return ..()
/mob/living/mech_melee_attack(obj/mecha/M)
if(M.occupant.a_intent == "harm")
diff --git a/code/modules/mob/living/living_defines.dm b/code/modules/mob/living/living_defines.dm
index 096da4e0b0d..56c0426c5bc 100644
--- a/code/modules/mob/living/living_defines.dm
+++ b/code/modules/mob/living/living_defines.dm
@@ -47,4 +47,6 @@
var/smoke_delay = 0 //used to prevent spam with smoke reagent reaction on mob.
- var/list/say_log = list() //a log of what we've said, plain text, no spans or junk, essentially just each individual "message"
\ No newline at end of file
+ var/list/say_log = list() //a log of what we've said, plain text, no spans or junk, essentially just each individual "message"
+
+ var/last_bumped = 0
diff --git a/code/modules/mob/mob_movement.dm b/code/modules/mob/mob_movement.dm
index c5f8a272d8b..d65438f65e7 100644
--- a/code/modules/mob/mob_movement.dm
+++ b/code/modules/mob/mob_movement.dm
@@ -1,18 +1,16 @@
/mob/CanPass(atom/movable/mover, turf/target, height=0)
- if(height==0) return 1
-
- if(istype(mover) && mover.checkpass(PASSMOB))
+ if(height==0)
return 1
- if(istype(mover, /obj/item/projectile))
+ if(istype(mover, /obj/item/projectile) || mover.throwing)
return (!density || lying)
+ if(mover.checkpass(PASSMOB))
+ return 1
if(ismob(mover))
var/mob/moving_mob = mover
if ((other_mobs && moving_mob.other_mobs))
return 1
- return (!mover.density || !density || lying)
- else
- return (!mover.density || !density || lying)
- return
+ return (!mover.density || !density || lying)
+
/client/Northeast()
diff --git a/code/modules/power/singularity/containment_field.dm b/code/modules/power/singularity/containment_field.dm
index fb0a69a9be2..08278a00ea7 100644
--- a/code/modules/power/singularity/containment_field.dm
+++ b/code/modules/power/singularity/containment_field.dm
@@ -43,7 +43,7 @@
/obj/machinery/field/containment/Crossed(obj/mover)
if(istype(mover, /obj/machinery) || istype(mover, /obj/structure) || istype(mover, /obj/mecha))
- bump(mover)
+ bump_field(mover)
/obj/machinery/field/containment/proc/set_master(master1,master2)
if(!master1 || !master2)
@@ -77,7 +77,7 @@
if((istype(mover, /obj/machinery) && !istype(mover, /obj/singularity)) || \
istype(mover, /obj/structure) || \
istype(mover, /obj/mecha))
- bump(mover)
+ bump_field(mover)
return 0
return ..()
@@ -106,13 +106,13 @@
"You hear an electrical crack.")
user.updatehealth()
- bump(user)
+ bump_field(user)
spawn(5)
hasShocked = 0
return
-/obj/machinery/field/proc/bump(atom/movable/AM as mob|obj)
+/obj/machinery/field/proc/bump_field(atom/movable/AM as mob|obj)
var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread
s.set_up(5, 1, AM.loc)
s.start()
diff --git a/code/modules/power/singularity/field_generator.dm b/code/modules/power/singularity/field_generator.dm
index 9c9f3c0f948..550567bca53 100644
--- a/code/modules/power/singularity/field_generator.dm
+++ b/code/modules/power/singularity/field_generator.dm
@@ -356,6 +356,6 @@ field_generator power level display
if(fields.len)
..()
-/obj/machinery/field/generator/bump(atom/movable/AM as mob|obj)
+/obj/machinery/field/generator/bump_field(atom/movable/AM as mob|obj)
if(fields.len)
..()
diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm
index 29931e4e7bc..0a1eddc7eb5 100644
--- a/code/modules/projectiles/projectile.dm
+++ b/code/modules/projectiles/projectile.dm
@@ -92,7 +92,7 @@
return 50 //if the projectile doesn't do damage, play its hitsound at 50% volume
/obj/item/projectile/Bump(atom/A, yes)
- if(!yes) //prevents multi bumps.
+ if(!yes) //prevents double bumps.
return
if(A == firer || A == src)
loc = A.loc
diff --git a/code/modules/reagents/reagent_containers.dm b/code/modules/reagents/reagent_containers.dm
index c75e6c3e82d..b64d21f6c14 100644
--- a/code/modules/reagents/reagent_containers.dm
+++ b/code/modules/reagents/reagent_containers.dm
@@ -79,8 +79,8 @@
reagents.handle_reactions()
..()
-/obj/item/weapon/reagent_containers/throw_impact(atom/target,mob/thrower)
- ..()
+/obj/item/weapon/reagent_containers/throw_impact(atom/target)
+ . = ..()
if(!reagents.total_volume || !spillable)
return
@@ -96,12 +96,11 @@
R += A.id + " ("
R += num2text(A.volume) + "),"
-
+ if(thrownby)
+ add_logs(thrownby, M, "splashed", R)
reagents.reaction(target, TOUCH)
- if(thrower)
- add_logs(thrower, M, "splashed", R)
- else if((!target.density || target.throwpass) && thrower && thrower.mind && thrower.mind.assigned_role == "Bartender")
+ else if((target.CanPass(src, get_turf(src))) && thrownby && thrownby.mind && thrownby.mind.assigned_role == "Bartender")
visible_message("[src] lands onto the [target.name] without spilling a single drop.")
return
diff --git a/code/modules/telesci/bscrystal.dm b/code/modules/telesci/bscrystal.dm
index 589731a3635..811ef4d991f 100644
--- a/code/modules/telesci/bscrystal.dm
+++ b/code/modules/telesci/bscrystal.dm
@@ -28,13 +28,14 @@
do_teleport(L, get_turf(L), blink_range, asoundin = 'sound/effects/phasein.ogg')
/obj/item/bluespace_crystal/throw_impact(atom/hit_atom)
- ..()
- visible_message("[src] fizzles and disappears upon impact!")
- PoolOrNew(/obj/effect/effect/sparks, loc)
- playsound(src.loc, "sparks", 50, 1)
- if(isliving(hit_atom))
- blink_mob(hit_atom)
- qdel(src)
+ if(!..()) // not caught in mid-air
+ visible_message("[src] fizzles and disappears upon impact!")
+ var/turf/T = get_turf(hit_atom)
+ PoolOrNew(/obj/effect/effect/sparks, T)
+ playsound(src.loc, "sparks", 50, 1)
+ if(isliving(hit_atom))
+ blink_mob(hit_atom)
+ qdel(src)
// Artifical bluespace crystal, doesn't give you much research.
diff --git a/html/changelogs/phil235-CatchThrownPieFix.yml b/html/changelogs/phil235-CatchThrownPieFix.yml
new file mode 100644
index 00000000000..4f467b01345
--- /dev/null
+++ b/html/changelogs/phil235-CatchThrownPieFix.yml
@@ -0,0 +1,8 @@
+
+author: phil235
+
+# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again.
+delete-after: True
+
+changes:
+ - tweak: "Thrown things no longer bounces off walls, unless they're in no gravity. Heavy items and mobs thrown can push the mobs and non anchored objects they land on. Throwing a mob onto a mob in no gravity will push each one in opposite direction. Thrown mobs landing on wall, or dense object or mob gets stunned and a bit injured. The damage when mob hit a wall is nerfed."