diff --git a/code/__DEFINES/combat.dm b/code/__DEFINES/combat.dm
index 05c732f45dd..6262592f7fd 100644
--- a/code/__DEFINES/combat.dm
+++ b/code/__DEFINES/combat.dm
@@ -281,6 +281,5 @@ GLOBAL_LIST_INIT(shove_disarming_types, typecacheof(list(
#define BULLET_ACT_HIT "HIT" //It's a successful hit, whatever that means in the context of the thing it's hitting.
#define BULLET_ACT_BLOCK "BLOCK" //It's a blocked hit, whatever that means in the context of the thing it's hitting.
#define BULLET_ACT_FORCE_PIERCE "PIERCE" //It pierces through the object regardless of the bullet being piercing by default.
-#define BULLET_ACT_TURF "TURF" //It hit us but it should hit something on the same turf too. Usually used for turfs.
#define NICE_SHOT_RICOCHET_BONUS 10 //if the shooter has the NICE_SHOT trait and they fire a ricocheting projectile, add this to the ricochet chance and auto aim angle
diff --git a/code/__DEFINES/flags.dm b/code/__DEFINES/flags.dm
index 04b67226664..2ee255e0167 100644
--- a/code/__DEFINES/flags.dm
+++ b/code/__DEFINES/flags.dm
@@ -107,6 +107,7 @@ GLOBAL_LIST_INIT(bitflags, list(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 204
#define PASSBLOB (1<<3)
#define PASSMOB (1<<4)
#define PASSCLOSEDTURF (1<<5)
+/// Let thrown things past us. **ONLY MEANINGFUL ON pass_flags_self!**
#define LETPASSTHROW (1<<6)
#define PASSMACHINE (1<<7)
#define PASSSTRUCTURE (1<<8)
@@ -117,8 +118,8 @@ GLOBAL_LIST_INIT(bitflags, list(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 204
#define FLYING (1<<1)
#define VENTCRAWLING (1<<2)
#define FLOATING (1<<3)
-/// When moving, will Bump()/Cross()/Uncross() everything, but won't be stopped.
-#define UNSTOPPABLE (1<<4)
+/// When moving, will Cross()/Uncross() everything, but won't stop or Bump() anything.
+#define PHASING (1<<4)
//Fire and Acid stuff, for resistance_flags
#define LAVA_PROOF (1<<0)
diff --git a/code/__DEFINES/projectiles.dm b/code/__DEFINES/projectiles.dm
new file mode 100644
index 00000000000..de6ee687cae
--- /dev/null
+++ b/code/__DEFINES/projectiles.dm
@@ -0,0 +1,9 @@
+// check_pierce() return values
+/// Default behavior: hit and delete self
+#define PROJECTILE_PIERCE_NONE 0
+/// Hit the thing but go through without deleting. Causes on_hit to be called with pierced = TRUE
+#define PROJECTILE_PIERCE_HIT 1
+/// Entirely phase through the thing without ever hitting.
+#define PROJECTILE_PIERCE_PHASE 2
+// Delete self without hitting
+#define PROJECTILE_DELETE_WITHOUT_HITTING 3
diff --git a/code/_globalvars/bitfields.dm b/code/_globalvars/bitfields.dm
index f291e07e75f..bae4ac0cc55 100644
--- a/code/_globalvars/bitfields.dm
+++ b/code/_globalvars/bitfields.dm
@@ -219,7 +219,7 @@ DEFINE_BITFIELD(movement_type, list(
"FLOATING" = FLOATING,
"FLYING" = FLYING,
"GROUND" = GROUND,
- "UNSTOPPABLE" = UNSTOPPABLE,
+ "PHASING" = PHASING,
"VENTCRAWLING" = VENTCRAWLING,
))
diff --git a/code/_onclick/adjacent.dm b/code/_onclick/adjacent.dm
index db2f62052f4..ddf6e6f41e7 100644
--- a/code/_onclick/adjacent.dm
+++ b/code/_onclick/adjacent.dm
@@ -94,7 +94,7 @@
for(var/obj/O in src)
if((mover && O.CanPass(mover,get_step(src,target_dir))) || (!mover && !O.density))
continue
- if(O == target_atom || O == mover || (O.pass_flags & LETPASSTHROW)) //check if there's a dense object present on the turf
+ if(O == target_atom || O == mover || (O.pass_flags_self & 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_1&ON_BORDER_1) // windows are on border, check them first
diff --git a/code/controllers/subsystem/throwing.dm b/code/controllers/subsystem/throwing.dm
index ee6cb105451..ddde1daba0e 100644
--- a/code/controllers/subsystem/throwing.dm
+++ b/code/controllers/subsystem/throwing.dm
@@ -199,6 +199,6 @@ SUBSYSTEM_DEF(throwing)
var/atom/movable/AM = thing
if (AM == thrownthing || (AM == thrower && !ismob(thrownthing)))
continue
- if (AM.density && !(AM.pass_flags & LETPASSTHROW) && !(AM.flags_1 & ON_BORDER_1))
+ if (AM.density && !(AM.pass_flags_self & LETPASSTHROW) && !(AM.flags_1 & ON_BORDER_1))
finalize(hit=TRUE, target=AM)
return TRUE
diff --git a/code/datums/components/mirv.dm b/code/datums/components/mirv.dm
index 140e1134764..198a9336f24 100644
--- a/code/datums/components/mirv.dm
+++ b/code/datums/components/mirv.dm
@@ -39,5 +39,5 @@
P.range = override_projectile_range
P.preparePixelProjectile(shootat_turf, target)
P.firer = firer // don't hit ourself that would be really annoying
- P.permutated += target // don't hit the target we hit already with the flak
+ P.impacted = list(target = TRUE) // don't hit the target we hit already with the flak
P.fire()
diff --git a/code/datums/components/pellet_cloud.dm b/code/datums/components/pellet_cloud.dm
index 18f821b5f10..4e1258cc758 100644
--- a/code/datums/components/pellet_cloud.dm
+++ b/code/datums/components/pellet_cloud.dm
@@ -266,7 +266,7 @@
P.original = target
P.fired_from = parent
P.firer = parent // don't hit ourself that would be really annoying
- P.permutated += parent // don't hit the target we hit already with the flak
+ P.impacted = list(parent = TRUE) // don't hit the target we hit already with the flak
P.suppressed = SUPPRESSED_VERY // set the projectiles to make no message so we can do our own aggregate message
P.preparePixelProjectile(target, parent)
RegisterSignal(P, COMSIG_PROJECTILE_SELF_ON_HIT, .proc/pellet_hit)
diff --git a/code/game/atoms.dm b/code/game/atoms.dm
index aa2cab5ce4c..8eb0b40e893 100644
--- a/code/game/atoms.dm
+++ b/code/game/atoms.dm
@@ -9,6 +9,9 @@
plane = GAME_PLANE
appearance_flags = TILE_BOUND
+ /// pass_flags that we are. If any of this matches a pass_flag on a moving thing, by default, we let them through.
+ var/pass_flags_self = NONE
+
///If non-null, overrides a/an/some in all cases
var/article
@@ -294,7 +297,7 @@
return FALSE
if((P.flag in list(BULLET, BOMB)) && P.ricochet_incidence_leeway)
if((a_incidence_s < 90 && a_incidence_s < 90 - P.ricochet_incidence_leeway) || (a_incidence_s > 270 && a_incidence_s -270 > P.ricochet_incidence_leeway))
- return
+ return FALSE
var/new_angle_s = SIMPLIFY_DEGREES(face_angle + incidence_s)
P.setAngle(new_angle_s)
return TRUE
@@ -303,7 +306,7 @@
/atom/proc/CanPass(atom/movable/mover, turf/target)
SHOULD_CALL_PARENT(TRUE)
SHOULD_BE_PURE(TRUE)
- if(mover.movement_type & UNSTOPPABLE)
+ if(mover.movement_type & PHASING)
return TRUE
. = CanAllowThrough(mover, target)
// This is cheaper than calling the proc every time since most things dont override CanPassThrough
@@ -314,6 +317,10 @@
/atom/proc/CanAllowThrough(atom/movable/mover, turf/target)
SHOULD_CALL_PARENT(TRUE)
//SHOULD_BE_PURE(TRUE)
+ if(mover.pass_flags & pass_flags_self)
+ return TRUE
+ if(mover.throwing && (pass_flags_self & LETPASSTHROW))
+ return TRUE
return !density
/**
@@ -504,7 +511,7 @@
return FALSE
/atom/proc/CheckExit()
- return 1
+ return TRUE
///Is this atom within 1 tile of another atom
/atom/proc/HasProximity(atom/movable/AM as mob|obj)
@@ -530,10 +537,15 @@
* React to a hit by a projectile object
*
* Default behaviour is to send the [COMSIG_ATOM_BULLET_ACT] and then call [on_hit][/obj/projectile/proc/on_hit] on the projectile
+ *
+ * @params
+ * P - projectile
+ * def_zone - zone hit
+ * piercing_hit - is this hit piercing or normal?
*/
-/atom/proc/bullet_act(obj/projectile/P, def_zone)
+/atom/proc/bullet_act(obj/projectile/P, def_zone, piercing_hit = FALSE)
SEND_SIGNAL(src, COMSIG_ATOM_BULLET_ACT, P, def_zone)
- . = P.on_hit(src, 0, def_zone)
+ . = P.on_hit(src, 0, def_zone, piercing_hit)
///Return true if we're inside the passed in atom
/atom/proc/in_contents_of(container)//can take class or object instance as argument
diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm
index 3d5a3f20f28..7653f59afa9 100644
--- a/code/game/atoms_movable.dm
+++ b/code/game/atoms_movable.dm
@@ -26,6 +26,7 @@
var/inertia_moving = 0
var/inertia_next_move = 0
var/inertia_move_delay = 5
+ /// Things we can pass through while moving. If any of this matches the thing we're trying to pass's [pass_flags_self], then we can pass through.
var/pass_flags = NONE
/// If false makes [CanPass][/atom/proc/CanPass] call [CanPassThrough][/atom/movable/proc/CanPassThrough] on this type instead of using default behaviour
var/generic_canpass = TRUE
diff --git a/code/game/machinery/_machinery.dm b/code/game/machinery/_machinery.dm
index 9225fd4ebba..a573ebaaa99 100644
--- a/code/game/machinery/_machinery.dm
+++ b/code/game/machinery/_machinery.dm
@@ -87,6 +87,7 @@
verb_say = "beeps"
verb_yell = "blares"
pressure_resistance = 15
+ pass_flags_self = PASSMACHINE
max_integrity = 200
layer = BELOW_OBJ_LAYER //keeps shit coming out of the machine from ending up underneath it.
flags_ricochet = RICOCHET_HARD
@@ -532,12 +533,6 @@
deconstruct(FALSE)
return ..()
-/obj/machinery/CanAllowThrough(atom/movable/mover, turf/target)
- . = ..()
-
- if(mover.pass_flags & PASSMACHINE)
- return TRUE
-
/obj/machinery/proc/default_deconstruction_screwdriver(mob/user, icon_state_open, icon_state_closed, obj/item/I)
if(!(flags_1 & NODECONSTRUCT_1) && I.tool_behaviour == TOOL_SCREWDRIVER)
I.play_tool_sound(src, 50)
diff --git a/code/game/machinery/deployable.dm b/code/game/machinery/deployable.dm
index bf833c68cf7..99d64b00294 100644
--- a/code/game/machinery/deployable.dm
+++ b/code/game/machinery/deployable.dm
@@ -96,7 +96,6 @@
/obj/structure/barricade/wooden/make_debris()
new /obj/item/stack/sheet/mineral/wood(get_turf(src), drop_amount)
-
/obj/structure/barricade/sandbags
name = "sandbags"
desc = "Bags of sand. Self explanatory."
@@ -105,14 +104,13 @@
base_icon_state = "sandbags"
max_integrity = 280
proj_pass_rate = 20
- pass_flags = LETPASSTHROW
+ pass_flags_self = LETPASSTHROW
bar_material = SAND
climbable = TRUE
smoothing_flags = SMOOTH_BITMASK
smoothing_groups = list(SMOOTH_GROUP_SANDBAGS)
canSmoothWith = list(SMOOTH_GROUP_SANDBAGS, SMOOTH_GROUP_WALLS, SMOOTH_GROUP_SECURITY_BARRICADE)
-
/obj/structure/barricade/security
name = "security barrier"
desc = "A deployable barrier. Provides good cover in fire fights."
diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm
index 6e6c2274b00..73e51a47854 100644
--- a/code/game/machinery/doors/door.dm
+++ b/code/game/machinery/doors/door.dm
@@ -149,7 +149,7 @@
. = ..()
if(.)
return
-
+ // Snowflake handling for PASSGLASS.
if(istype(mover) && (mover.pass_flags & PASSGLASS))
return !opacity
diff --git a/code/game/machinery/doors/firedoor.dm b/code/game/machinery/doors/firedoor.dm
index f590a7be092..e9592bf9d6b 100644
--- a/code/game/machinery/doors/firedoor.dm
+++ b/code/game/machinery/doors/firedoor.dm
@@ -228,18 +228,13 @@
/obj/machinery/door/firedoor/border_only/CanAllowThrough(atom/movable/mover, turf/target)
. = ..()
- if(istype(mover) && (mover.pass_flags & PASSGLASS))
- return TRUE
if(!(get_dir(loc, target) == dir)) //Make sure looking at appropriate border
return TRUE
/obj/machinery/door/firedoor/border_only/CheckExit(atom/movable/mover as mob|obj, turf/target)
- if(istype(mover) && (mover.pass_flags & PASSGLASS))
- return TRUE
if(get_dir(loc, target) == dir)
return !density
- else
- return TRUE
+ return TRUE
/obj/machinery/door/firedoor/border_only/CanAtmosPass(turf/T)
if(get_dir(loc, T) == dir)
diff --git a/code/game/machinery/doors/windowdoor.dm b/code/game/machinery/doors/windowdoor.dm
index 7ee8f70eca7..3d639d05c5e 100644
--- a/code/game/machinery/doors/windowdoor.dm
+++ b/code/game/machinery/doors/windowdoor.dm
@@ -13,6 +13,7 @@
visible = FALSE
flags_1 = ON_BORDER_1
opacity = FALSE
+ pass_flags_self = PASSGLASS
CanAtmosPass = ATMOS_PASS_PROC
interaction_flags_machine = INTERACT_MACHINE_WIRES_IF_OPEN | INTERACT_MACHINE_ALLOW_SILICON | INTERACT_MACHINE_OPEN_SILICON | INTERACT_MACHINE_REQUIRES_SILICON | INTERACT_MACHINE_OPEN
var/obj/item/electronics/airlock/electronics = null
@@ -101,8 +102,8 @@
/obj/machinery/door/window/CanAllowThrough(atom/movable/mover, turf/target)
. = ..()
- if(istype(mover) && (mover.pass_flags & PASSGLASS))
- return TRUE
+ if(.)
+ return
if(get_dir(loc, target) == dir) //Make sure looking at appropriate border
return
if(istype(mover, /obj/structure/window))
@@ -128,13 +129,12 @@
/obj/machinery/door/window/CanAStarPass(obj/item/card/id/ID, to_dir)
return !density || (dir != to_dir) || (check_access(ID) && hasPower())
-/obj/machinery/door/window/CheckExit(atom/movable/mover as mob|obj, turf/target)
- if(istype(mover) && (mover.pass_flags & PASSGLASS))
- return 1
+/obj/machinery/door/window/CheckExit(atom/movable/mover, turf/target)
+ if((pass_flags_self & mover.pass_flags) || ((pass_flags_self & LETPASSTHROW) && mover.throwing))
+ return TRUE
if(get_dir(loc, target) == dir)
return !density
- else
- return 1
+ return TRUE
/obj/machinery/door/window/open(forced=FALSE)
if (operating) //doors can still open when emag-disabled
diff --git a/code/game/objects/effects/effect_system/effects_foam.dm b/code/game/objects/effects/effect_system/effects_foam.dm
index 23e12d21786..2a83c0621f1 100644
--- a/code/game/objects/effects/effect_system/effects_foam.dm
+++ b/code/game/objects/effects/effect_system/effects_foam.dm
@@ -302,6 +302,7 @@
icon_state = "atmos_resin"
alpha = 120
max_integrity = 10
+ pass_flags_self = PASSGLASS
/obj/structure/foamedmetal/resin/Initialize()
. = ..()
@@ -330,11 +331,6 @@
for(var/obj/item/Item in O)
Item.extinguish()
-/obj/structure/foamedmetal/resin/CanAllowThrough(atom/movable/mover, turf/target)
- . = ..()
- if(istype(mover) && (mover.pass_flags & PASSGLASS))
- return TRUE
-
#undef ALUMINUM_FOAM
#undef IRON_FOAM
#undef RESIN_FOAM
diff --git a/code/game/objects/items/cardboard_cutouts.dm b/code/game/objects/items/cardboard_cutouts.dm
index 65e6f6be761..f6e6104c09e 100644
--- a/code/game/objects/items/cardboard_cutouts.dm
+++ b/code/game/objects/items/cardboard_cutouts.dm
@@ -84,9 +84,9 @@
if(prob(I.force))
push_over()
-/obj/item/cardboard_cutout/bullet_act(obj/projectile/P)
+/obj/item/cardboard_cutout/bullet_act(obj/projectile/P, def_zone, piercing_hit = FALSE)
if(istype(P, /obj/projectile/bullet/reusable))
- P.on_hit(src, 0)
+ P.on_hit(src, 0, piercing_hit)
visible_message("[src] is hit by [P]!")
playsound(src, 'sound/weapons/slice.ogg', 50, TRUE)
if(prob(P.damage))
diff --git a/code/game/objects/items/devices/forcefieldprojector.dm b/code/game/objects/items/devices/forcefieldprojector.dm
index 7a618fc1292..5fd886314ff 100644
--- a/code/game/objects/items/devices/forcefieldprojector.dm
+++ b/code/game/objects/items/devices/forcefieldprojector.dm
@@ -84,6 +84,7 @@
icon_state = "forcefield"
layer = ABOVE_ALL_MOB_LAYER
anchored = TRUE
+ pass_flags_self = PASSGLASS
density = TRUE
mouse_opacity = MOUSE_OPACITY_OPAQUE
resistance_flags = INDESTRUCTIBLE
@@ -102,11 +103,6 @@
generator = null
return ..()
-/obj/structure/projected_forcefield/CanAllowThrough(atom/movable/mover, turf/target)
- . = ..()
- if(istype(mover) && (mover.pass_flags & PASSGLASS))
- return TRUE
-
/obj/structure/projected_forcefield/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0)
playsound(loc, 'sound/weapons/egloves.ogg', 80, TRUE)
diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm
index e3fda7d65d9..c35e0ad7bc9 100644
--- a/code/game/objects/objs.dm
+++ b/code/game/objects/objs.dm
@@ -198,7 +198,11 @@
/obj/get_dumping_location(datum/component/storage/source,mob/user)
return get_turf(src)
-/obj/proc/CanAStarPass()
+/obj/proc/CanAStarPass(ID, dir, caller)
+ if(ismovable(caller))
+ var/atom/movable/AM = caller
+ if(AM.pass_flags & pass_flags_self)
+ return TRUE
. = !density
/obj/proc/check_uplink_validity()
diff --git a/code/game/objects/structures.dm b/code/game/objects/structures.dm
index 77e30bf8f7f..08d3fda3597 100644
--- a/code/game/objects/structures.dm
+++ b/code/game/objects/structures.dm
@@ -7,6 +7,7 @@
layer = BELOW_OBJ_LAYER
flags_ricochet = RICOCHET_HARD
receive_ricochet_chance_mod = 0.6
+ pass_flags_self = PASSSTRUCTURE
var/climb_time = 20
var/climb_stun = 20
@@ -115,12 +116,6 @@
if(examine_status)
. += examine_status
-/obj/structure/CanAllowThrough(atom/movable/mover, turf/target)
- . = ..()
-
- if(mover.pass_flags & PASSSTRUCTURE)
- return TRUE
-
/obj/structure/proc/examine_status(mob/user) //An overridable proc, mostly for falsewalls.
var/healthpercent = (obj_integrity/max_integrity) * 100
switch(healthpercent)
diff --git a/code/game/objects/structures/grille.dm b/code/game/objects/structures/grille.dm
index 71fa3c987ea..4732f0cb8fc 100644
--- a/code/game/objects/structures/grille.dm
+++ b/code/game/objects/structures/grille.dm
@@ -5,6 +5,7 @@
icon_state = "grille"
density = TRUE
anchored = TRUE
+ pass_flags_self = PASSGRILLE
flags_1 = CONDUCT_1 | RAD_PROTECT_CONTENTS_1 | RAD_NO_CONTAMINATE_1
pressure_resistance = 5*ONE_ATMOSPHERE
armor = list(MELEE = 50, BULLET = 70, LASER = 70, ENERGY = 100, BOMB = 10, BIO = 100, RAD = 100, FIRE = 0, ACID = 0)
@@ -118,9 +119,7 @@
/obj/structure/grille/CanAllowThrough(atom/movable/mover, turf/target)
. = ..()
- if(mover.pass_flags & PASSGRILLE)
- return TRUE
- else if(!. && istype(mover, /obj/projectile))
+ if(!. && istype(mover, /obj/projectile))
return prob(30)
/obj/structure/grille/CanAStarPass(ID, dir, caller)
diff --git a/code/game/objects/structures/holosign.dm b/code/game/objects/structures/holosign.dm
index 3db53c4b159..7e9c6610e6a 100644
--- a/code/game/objects/structures/holosign.dm
+++ b/code/game/objects/structures/holosign.dm
@@ -48,7 +48,7 @@
name = "holobarrier"
desc = "A short holographic barrier which can only be passed by walking."
icon_state = "holosign_sec"
- pass_flags = LETPASSTHROW
+ pass_flags_self = PASSTABLE | PASSGRILLE | PASSGLASS | LETPASSTHROW
density = TRUE
max_integrity = 20
var/allow_walk = TRUE //can we pass through it on walk intent
@@ -57,8 +57,6 @@
. = ..()
if(.)
return
- if(mover.pass_flags & (PASSGLASS|PASSTABLE|PASSGRILLE))
- return TRUE
if(iscarbon(mover))
var/mob/living/carbon/C = mover
if(C.stat) // Lets not prevent dragging unconscious/dead people.
diff --git a/code/game/objects/structures/morgue.dm b/code/game/objects/structures/morgue.dm
index e142c33f522..0197bface56 100644
--- a/code/game/objects/structures/morgue.dm
+++ b/code/game/objects/structures/morgue.dm
@@ -314,7 +314,7 @@ GLOBAL_LIST_EMPTY(crematoriums)
density = TRUE
var/obj/structure/bodycontainer/connected = null
anchored = TRUE
- pass_flags = LETPASSTHROW
+ pass_flags_self = LETPASSTHROW
max_integrity = 350
/obj/structure/tray/Destroy()
@@ -388,16 +388,11 @@ GLOBAL_LIST_EMPTY(crematoriums)
name = "morgue tray"
desc = "Apply corpse before closing."
icon_state = "morguet"
+ pass_flags_self = PASSTABLE
/obj/structure/tray/m_tray/CanAllowThrough(atom/movable/mover, turf/target)
. = ..()
- if(istype(mover) && (mover.pass_flags & PASSTABLE))
- return TRUE
+ if(.)
+ return
if(locate(/obj/structure/table) in get_turf(mover))
return TRUE
-
-/obj/structure/tray/m_tray/CanAStarPass(ID, dir, caller)
- . = !density
- if(ismovable(caller))
- var/atom/movable/mover = caller
- . = . || (mover.pass_flags & PASSTABLE)
diff --git a/code/game/objects/structures/railings.dm b/code/game/objects/structures/railings.dm
index 09e42276973..0a479b6556b 100644
--- a/code/game/objects/structures/railings.dm
+++ b/code/game/objects/structures/railings.dm
@@ -80,7 +80,7 @@
/obj/structure/railing/CheckExit(atom/movable/mover, turf/target)
..()
if(get_dir(loc, target) & dir)
- var/checking = UNSTOPPABLE | FLYING | FLOATING
+ var/checking = PHASING | FLYING | FLOATING
return !density || mover.throwing || mover.movement_type & checking || mover.move_force >= MOVE_FORCE_EXTREMELY_STRONG
return TRUE
diff --git a/code/game/objects/structures/tables_racks.dm b/code/game/objects/structures/tables_racks.dm
index 7a38d86a6e4..44e542f92eb 100644
--- a/code/game/objects/structures/tables_racks.dm
+++ b/code/game/objects/structures/tables_racks.dm
@@ -20,9 +20,9 @@
base_icon_state = "table"
density = TRUE
anchored = TRUE
+ pass_flags_self = PASSTABLE | LETPASSTHROW
layer = TABLE_LAYER
climbable = TRUE
- 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
@@ -100,9 +100,8 @@
/obj/structure/table/CanAllowThrough(atom/movable/mover, turf/target)
. = ..()
-
- if(istype(mover) && (mover.pass_flags & PASSTABLE))
- return TRUE
+ if(.)
+ return
if(mover.throwing)
return TRUE
if(locate(/obj/structure/table) in get_turf(mover))
@@ -610,7 +609,7 @@
layer = TABLE_LAYER
density = TRUE
anchored = TRUE
- pass_flags = LETPASSTHROW //You can throw objects over this, despite it's density.
+ pass_flags_self = LETPASSTHROW //You can throw objects over this, despite it's density.
max_integrity = 20
/obj/structure/rack/examine(mob/user)
@@ -624,12 +623,6 @@
if(istype(mover) && (mover.pass_flags & PASSTABLE))
return TRUE
-/obj/structure/rack/CanAStarPass(ID, dir, caller)
- . = !density
- if(ismovable(caller))
- var/atom/movable/mover = caller
- . = . || (mover.pass_flags & PASSTABLE)
-
/obj/structure/rack/MouseDrop_T(obj/O, mob/user)
. = ..()
if ((!( istype(O, /obj/item) ) || user.get_active_held_item() != O))
diff --git a/code/game/objects/structures/tank_holder.dm b/code/game/objects/structures/tank_holder.dm
index 3e9d0cbd4c5..9210cb17ae7 100644
--- a/code/game/objects/structures/tank_holder.dm
+++ b/code/game/objects/structures/tank_holder.dm
@@ -9,7 +9,7 @@
density = FALSE
anchored = FALSE
- pass_flags = LETPASSTHROW
+ pass_flags_self = LETPASSTHROW
max_integrity = 20
resistance_flags = FIRE_PROOF
diff --git a/code/game/objects/structures/transit_tubes/transit_tube.dm b/code/game/objects/structures/transit_tubes/transit_tube.dm
index 2669d12f1b5..223d690c2bc 100644
--- a/code/game/objects/structures/transit_tubes/transit_tube.dm
+++ b/code/game/objects/structures/transit_tubes/transit_tube.dm
@@ -8,16 +8,12 @@
layer = LOW_ITEM_LAYER
anchored = TRUE
climbable = TRUE
+ pass_flags_self = PASSGLASS
var/tube_construction = /obj/structure/c_transit_tube
var/list/tube_dirs //list of directions this tube section can connect to.
var/exit_delay = 1
var/enter_delay = 0
-/obj/structure/transit_tube/CanAllowThrough(atom/movable/mover, turf/target)
- . = ..()
- if(istype(mover) && (mover.pass_flags & PASSGLASS))
- return TRUE
-
/obj/structure/transit_tube/New(loc, newdirection)
..(loc)
if(newdirection)
diff --git a/code/game/objects/structures/windoor_assembly.dm b/code/game/objects/structures/windoor_assembly.dm
index a6312838f20..2f9debb0381 100644
--- a/code/game/objects/structures/windoor_assembly.dm
+++ b/code/game/objects/structures/windoor_assembly.dm
@@ -52,8 +52,6 @@
/obj/structure/windoor_assembly/CanAllowThrough(atom/movable/mover, turf/target)
. = ..()
- if(istype(mover) && (mover.pass_flags & PASSGLASS))
- return TRUE
if(get_dir(loc, target) == dir) //Make sure looking at appropriate border
return
if(istype(mover, /obj/structure/window))
@@ -73,14 +71,13 @@
else
return 1
-/obj/structure/windoor_assembly/CheckExit(atom/movable/mover as mob|obj, turf/target)
- if(istype(mover) && (mover.pass_flags & PASSGLASS))
- return 1
+/obj/structure/windoor_assembly/CheckExit(atom/movable/mover, turf/target)
+ if(mover.pass_flags & pass_flags_self)
+ return TRUE
if(get_dir(loc, target) == dir)
return !density
else
- return 1
-
+ return TRUE
/obj/structure/windoor_assembly/attackby(obj/item/W, mob/user, params)
//I really should have spread this out across more states but thin little windoors are hard to sprite.
diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm
index 2c7cb426e57..c9904591d15 100644
--- a/code/game/objects/structures/window.dm
+++ b/code/game/objects/structures/window.dm
@@ -13,6 +13,7 @@
armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, BIO = 0, RAD = 0, FIRE = 80, ACID = 100)
CanAtmosPass = ATMOS_PASS_PROC
rad_insulation = RAD_VERY_LIGHT_INSULATION
+ pass_flags_self = PASSGLASS
var/ini_dir = null
var/state = WINDOW_OUT_OF_FRAME
var/reinf = FALSE
@@ -105,8 +106,8 @@
/obj/structure/window/CanAllowThrough(atom/movable/mover, turf/target)
. = ..()
- if(istype(mover) && (mover.pass_flags & PASSGLASS))
- return TRUE
+ if(.)
+ return
if(dir == FULLTILE_WINDOW_DIR)
return FALSE //full tile window, you can't move into it!
var/attempted_dir = get_dir(loc, target)
diff --git a/code/game/turfs/closed/_closed.dm b/code/game/turfs/closed/_closed.dm
index f597dc276e6..e0e5c382200 100644
--- a/code/game/turfs/closed/_closed.dm
+++ b/code/game/turfs/closed/_closed.dm
@@ -5,6 +5,7 @@
blocks_air = TRUE
flags_1 = RAD_PROTECT_CONTENTS_1 | RAD_NO_CONTAMINATE_1
rad_insulation = RAD_MEDIUM_INSULATION
+ pass_flags_self = PASSCLOSEDTURF
/turf/closed/AfterChange()
. = ..()
@@ -13,11 +14,6 @@
/turf/closed/get_smooth_underlay_icon(mutable_appearance/underlay_appearance, turf/asking_turf, adjacency_dir)
return FALSE
-/turf/closed/CanAllowThrough(atom/movable/mover, turf/target)
- . = ..()
- if(istype(mover) && (mover.pass_flags & PASSCLOSEDTURF))
- return TRUE
-
/turf/closed/indestructible
name = "wall"
desc = "Effectively impervious to conventional methods of destruction."
diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm
index 6fa6678c03c..f87777248ec 100755
--- a/code/game/turfs/turf.dm
+++ b/code/game/turfs/turf.dm
@@ -269,7 +269,7 @@ GLOBAL_LIST_EMPTY(station_turfs)
// Here's hoping it doesn't stay like this for years before we finish conversion to step_
var/atom/firstbump
var/canPassSelf = CanPass(mover, src)
- if(canPassSelf || (mover.movement_type & UNSTOPPABLE))
+ if(canPassSelf || (mover.movement_type & PHASING))
for(var/i in contents)
if(QDELETED(mover))
return FALSE //We were deleted, do not attempt to proceed with movement.
@@ -279,7 +279,7 @@ GLOBAL_LIST_EMPTY(station_turfs)
if(!thing.Cross(mover))
if(QDELETED(mover)) //Mover deleted from Cross/CanPass, do not proceed.
return FALSE
- if((mover.movement_type & UNSTOPPABLE))
+ if((mover.movement_type & PHASING))
mover.Bump(thing)
continue
else
@@ -291,7 +291,7 @@ GLOBAL_LIST_EMPTY(station_turfs)
firstbump = src
if(firstbump)
mover.Bump(firstbump)
- return (mover.movement_type & UNSTOPPABLE)
+ return (mover.movement_type & PHASING)
return TRUE
/turf/Exit(atom/movable/mover, atom/newloc)
@@ -305,7 +305,7 @@ GLOBAL_LIST_EMPTY(station_turfs)
if(!thing.Uncross(mover, newloc))
if(thing.flags_1 & ON_BORDER_1)
mover.Bump(thing)
- if(!(mover.movement_type & UNSTOPPABLE))
+ if(!(mover.movement_type & PHASING))
return FALSE
if(QDELETED(mover))
return FALSE //We were deleted.
@@ -584,11 +584,6 @@ GLOBAL_LIST_EMPTY(station_turfs)
/turf/proc/Melt()
return ScrapeAway(flags = CHANGETURF_INHERIT_AIR)
-/turf/bullet_act(obj/projectile/P)
- . = ..()
- if(. != BULLET_ACT_FORCE_PIERCE)
- . = BULLET_ACT_TURF
-
/// Handles exposing a turf to reagents.
/turf/expose_reagents(list/reagents, datum/reagents/source, methods=TOUCH, volume_modifier=1, show_message=TRUE)
. = ..()
diff --git a/code/modules/antagonists/blob/structures/_blob.dm b/code/modules/antagonists/blob/structures/_blob.dm
index 6336fa4c7a1..bee8f01595a 100644
--- a/code/modules/antagonists/blob/structures/_blob.dm
+++ b/code/modules/antagonists/blob/structures/_blob.dm
@@ -8,6 +8,7 @@
opacity = FALSE
anchored = TRUE
layer = BELOW_MOB_LAYER
+ pass_flags_self = PASSBLOB
CanAtmosPass = ATMOS_PASS_PROC
var/point_return = 0 //How many points the blob gets back when it removes a blob of that type. If less than 0, blob cannot be removed.
max_integrity = 30
@@ -68,20 +69,9 @@
/obj/structure/blob/BlockSuperconductivity()
return atmosblock
-/obj/structure/blob/CanAllowThrough(atom/movable/mover, turf/target)
- . = ..()
- if(!(mover.pass_flags & PASSBLOB))
- return FALSE
-
/obj/structure/blob/CanAtmosPass(turf/T)
return !atmosblock
-/obj/structure/blob/CanAStarPass(ID, dir, caller)
- . = 0
- if(ismovable(caller))
- var/atom/movable/mover = caller
- . = . || (mover.pass_flags & PASSBLOB)
-
/obj/structure/blob/update_icon() //Updates color based on overmind color if we have an overmind.
if(overmind)
add_atom_colour(overmind.blobstrain.color, FIXED_COLOUR_PRIORITY)
diff --git a/code/modules/assembly/infrared.dm b/code/modules/assembly/infrared.dm
index 457b7fd98d7..8cad2ee7450 100644
--- a/code/modules/assembly/infrared.dm
+++ b/code/modules/assembly/infrared.dm
@@ -224,7 +224,8 @@
icon_state = "ibeam"
anchored = TRUE
density = FALSE
- pass_flags = PASSTABLE|PASSGLASS|PASSGRILLE|LETPASSTHROW
+ pass_flags = PASSTABLE|PASSGLASS|PASSGRILLE
+ pass_flags_self = LETPASSTHROW
var/obj/item/assembly/infra/master
/obj/effect/beam/i_beam/Crossed(atom/movable/AM as mob|obj)
diff --git a/code/modules/awaymissions/capture_the_flag.dm b/code/modules/awaymissions/capture_the_flag.dm
index bdf55ae4150..8911d83c447 100644
--- a/code/modules/awaymissions/capture_the_flag.dm
+++ b/code/modules/awaymissions/capture_the_flag.dm
@@ -402,9 +402,10 @@
/obj/projectile/bullet/ctf
damage = 0
-/obj/projectile/bullet/ctf/prehit(atom/target)
+/obj/projectile/bullet/ctf/prehit_pierce(atom/target)
if(is_ctf_target(target))
damage = 60
+ return PROJECTILE_PIERCE_NONE /// hey uhh don't hit anyone behind them
. = ..()
/obj/item/gun/ballistic/automatic/laser/ctf
@@ -438,9 +439,10 @@
damage = 0
icon_state = "omnilaser"
-/obj/projectile/beam/ctf/prehit(atom/target)
+/obj/projectile/beam/ctf/prehit_pierce(atom/target)
if(is_ctf_target(target))
damage = 150
+ return PROJECTILE_PIERCE_NONE /// hey uhhh don't hit anyone behind them
. = ..()
/proc/is_ctf_target(atom/target)
diff --git a/code/modules/hydroponics/grown/towercap.dm b/code/modules/hydroponics/grown/towercap.dm
index 4abdf60f56a..c8a6e31bbb5 100644
--- a/code/modules/hydroponics/grown/towercap.dm
+++ b/code/modules/hydroponics/grown/towercap.dm
@@ -156,6 +156,7 @@
density = FALSE
anchored = TRUE
buckle_lying = 0
+ pass_flags_self = PASSTABLE | LETPASSTHROW
var/burning = 0
var/burn_icon = "bonfire_on_fire" //for a softer more burning embers icon, use "bonfire_warm"
var/grill = FALSE
@@ -168,13 +169,6 @@
. = ..()
StartBurning()
-/obj/structure/bonfire/CanAllowThrough(atom/movable/mover, turf/target)
- . = ..()
- if(istype(mover) && (mover.pass_flags & PASSTABLE))
- return TRUE
- if(mover.throwing)
- return TRUE
-
/obj/structure/bonfire/attackby(obj/item/W, mob/user, params)
if(istype(W, /obj/item/stack/rods) && !can_buckle && !grill)
var/obj/item/stack/rods/R = W
diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm
index 6b9d1183093..146a5235e79 100644
--- a/code/modules/mob/living/carbon/human/human_defense.dm
+++ b/code/modules/mob/living/carbon/human/human_defense.dm
@@ -53,7 +53,7 @@
dna.species.on_hit(P, src)
-/mob/living/carbon/human/bullet_act(obj/projectile/P, def_zone)
+/mob/living/carbon/human/bullet_act(obj/projectile/P, def_zone, piercing_hit = FALSE)
if(dna?.species)
var/spec_return = dna.species.bullet_act(P, src)
if(spec_return)
@@ -74,7 +74,7 @@
// Find a turf near or on the original location to bounce to
if(!isturf(loc)) //Open canopy mech (ripley) check. if we're inside something and still got hit
P.force_hit = TRUE //The thing we're in passed the bullet to us. Pass it back, and tell it to take the damage.
- loc.bullet_act(P)
+ loc.bullet_act(P, def_zone, piercing_hit)
return BULLET_ACT_HIT
if(P.starting)
var/new_x = P.starting.x + pick(0, 0, 0, 0, 0, -1, 1, -2, 2)
@@ -95,10 +95,11 @@
return BULLET_ACT_FORCE_PIERCE // complete projectile permutation
if(check_shields(P, P.damage, "the [P.name]", PROJECTILE_ATTACK, P.armour_penetration))
- P.on_hit(src, 100, def_zone)
+ P.on_hit(src, 100, def_zone, piercing_hit)
return BULLET_ACT_HIT
- return ..(P, def_zone)
+ return ..()
+
///Reflection checks for anything in your l_hand, r_hand, or wear_suit based on the reflection chance of the object
/mob/living/carbon/human/proc/check_reflect(def_zone)
if(wear_suit)
diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm
index afa1058ee8c..dec3922f772 100644
--- a/code/modules/mob/living/living_defense.dm
+++ b/code/modules/mob/living/living_defense.dm
@@ -47,9 +47,9 @@
/mob/living/proc/on_hit(obj/projectile/P)
return BULLET_ACT_HIT
-/mob/living/bullet_act(obj/projectile/P, def_zone)
+/mob/living/bullet_act(obj/projectile/P, def_zone, piercing_hit = FALSE)
var/armor = run_armor_check(def_zone, P.flag, "","",P.armour_penetration)
- var/on_hit_state = P.on_hit(src, armor)
+ var/on_hit_state = P.on_hit(src, armor, piercing_hit)
if(!P.nodamage && on_hit_state != BULLET_ACT_BLOCK)
apply_damage(P.damage, P.damage_type, def_zone, armor, wound_bonus=P.wound_bonus, bare_wound_bonus=P.bare_wound_bonus, sharpness = P.sharpness)
apply_effects(P.stun, P.knockdown, P.unconscious, P.irradiate, P.slur, P.stutter, P.eyeblur, P.drowsy, armor, P.stamina, P.jitter, P.paralyze, P.immobilize)
diff --git a/code/modules/mob/living/living_movement.dm b/code/modules/mob/living/living_movement.dm
index fa809b563c6..709550cbc56 100644
--- a/code/modules/mob/living/living_movement.dm
+++ b/code/modules/mob/living/living_movement.dm
@@ -5,19 +5,15 @@
/mob/living/CanAllowThrough(atom/movable/mover, turf/target)
. = ..()
- if((mover.pass_flags & PASSMOB))
- return TRUE
- if(istype(mover, /obj/projectile))
- var/obj/projectile/P = mover
- return !P.can_hit_target(src, P.permutated, src == P.original, TRUE)
+ if(.)
+ return
if(mover.throwing)
return (!density || body_position == LYING_DOWN || (mover.throwing.thrower == src && !ismob(mover)))
if(buckled == mover)
return TRUE
if(ismob(mover) && (mover in buckled_mobs))
return TRUE
- return . || !mover.density || body_position == LYING_DOWN
-
+ return !mover.density || body_position == LYING_DOWN
/mob/living/toggle_move_intent()
. = ..()
diff --git a/code/modules/mob/living/silicon/silicon_defense.dm b/code/modules/mob/living/silicon/silicon_defense.dm
index 57e3ac5c8e7..9a8422210f5 100644
--- a/code/modules/mob/living/silicon/silicon_defense.dm
+++ b/code/modules/mob/living/silicon/silicon_defense.dm
@@ -109,7 +109,7 @@
M.visible_message("[M] is thrown off of [src]!")
flash_act(affect_silicon = 1)
-/mob/living/silicon/bullet_act(obj/projectile/Proj, def_zone)
+/mob/living/silicon/bullet_act(obj/projectile/Proj, def_zone, piercing_hit = FALSE)
SEND_SIGNAL(src, COMSIG_ATOM_BULLET_ACT, Proj, def_zone)
if((Proj.damage_type == BRUTE || Proj.damage_type == BURN))
adjustBruteLoss(Proj.damage)
@@ -122,7 +122,7 @@
for(var/mob/living/M in buckled_mobs)
unbuckle_mob(M)
M.visible_message("[M] is knocked off of [src] by the [Proj]!")
- Proj.on_hit(src)
+ Proj.on_hit(src, 0, piercing_hit)
return BULLET_ACT_HIT
/mob/living/silicon/flash_act(intensity = 1, override_blindness_check = 0, affect_silicon = 0, visual = 0, type = /atom/movable/screen/fullscreen/flash/static)
diff --git a/code/modules/mob/living/simple_animal/animal_defense.dm b/code/modules/mob/living/simple_animal/animal_defense.dm
index cad3d1e1277..e331283e48c 100644
--- a/code/modules/mob/living/simple_animal/animal_defense.dm
+++ b/code/modules/mob/living/simple_animal/animal_defense.dm
@@ -139,9 +139,9 @@
apply_damage(damage, damagetype, null, getarmor(null, armorcheck))
return TRUE
-/mob/living/simple_animal/bullet_act(obj/projectile/Proj)
+/mob/living/simple_animal/bullet_act(obj/projectile/Proj, def_zone, piercing_hit = FALSE)
apply_damage(Proj.damage, Proj.damage_type)
- Proj.on_hit(src)
+ Proj.on_hit(src, 0, piercing_hit)
return BULLET_ACT_HIT
/mob/living/simple_animal/ex_act(severity, target, origin)
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 ef31752fedd..88fb58836ca 100644
--- a/code/modules/mob/living/simple_animal/hostile/jungle/mook.dm
+++ b/code/modules/mob/living/simple_animal/hostile/jungle/mook.dm
@@ -24,7 +24,7 @@
melee_damage_upper = 30
ranged = TRUE
ranged_cooldown_time = 10
- pass_flags = LETPASSTHROW
+ pass_flags_self = LETPASSTHROW
robust_searching = TRUE
stat_attack = HARD_CRIT
attack_sound = 'sound/weapons/rapierhit.ogg'
diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/legion.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/legion.dm
index 45d4fb7f0e5..e20ed2a3a96 100644
--- a/code/modules/mob/living/simple_animal/hostile/megafauna/legion.dm
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/legion.dm
@@ -388,7 +388,7 @@
muzzle_type = /obj/effect/projectile/tracer/legion
impact_type = /obj/effect/projectile/tracer/legion
hitscan = TRUE
- movement_type = UNSTOPPABLE
+ projectile_piercing = ALL
///Used for the legion turret tracer.
/obj/effect/projectile/tracer/legion/tracer
diff --git a/code/modules/mob/living/simple_animal/slime/slime.dm b/code/modules/mob/living/simple_animal/slime/slime.dm
index b0d28bebadc..3354a0ff0a8 100644
--- a/code/modules/mob/living/simple_animal/slime/slime.dm
+++ b/code/modules/mob/living/simple_animal/slime/slime.dm
@@ -222,11 +222,11 @@
amount = -abs(amount)
return ..() //Heals them
-/mob/living/simple_animal/slime/bullet_act(obj/projectile/Proj)
+/mob/living/simple_animal/slime/bullet_act(obj/projectile/Proj, def_zone, piercing_hit = FALSE)
attacked += 10
if((Proj.damage_type == BURN))
adjustBruteLoss(-abs(Proj.damage)) //fire projectiles heals slimes.
- Proj.on_hit(src)
+ Proj.on_hit(src, 0, piercing_hit)
else
. = ..(Proj)
. = . || BULLET_ACT_BLOCK
diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm
index 40c10f1f5df..3cd864f05a7 100644
--- a/code/modules/mob/mob_defines.dm
+++ b/code/modules/mob/mob_defines.dm
@@ -17,6 +17,7 @@
mouse_drag_pointer = MOUSE_ACTIVE_POINTER
throwforce = 10
blocks_emissive = EMISSIVE_BLOCK_GENERIC
+ pass_flags_self = PASSMOB
///when this be added to vis_contents of something it inherit something.plane, important for visualisation of mob in openspace.
vis_flags = VIS_INHERIT_PLANE
diff --git a/code/modules/projectiles/guns/energy/kinetic_accelerator.dm b/code/modules/projectiles/guns/energy/kinetic_accelerator.dm
index cdc58f7062f..4ba555769bc 100644
--- a/code/modules/projectiles/guns/energy/kinetic_accelerator.dm
+++ b/code/modules/projectiles/guns/energy/kinetic_accelerator.dm
@@ -186,14 +186,14 @@
kinetic_gun = null
return ..()
-/obj/projectile/kinetic/prehit(atom/target)
+/obj/projectile/kinetic/prehit_pierce(atom/target)
. = ..()
if(.)
if(kinetic_gun)
var/list/mods = kinetic_gun.modkits
for(var/obj/item/borg/upgrade/modkit/M in mods)
M.projectile_prehit(src, target, kinetic_gun)
- if(!lavaland_equipment_pressure_check(get_turf(target)))
+ if(!pressure_decrease_active && !lavaland_equipment_pressure_check(get_turf(target)))
name = "weakened [name]"
damage = damage * pressure_decrease
pressure_decrease_active = TRUE
diff --git a/code/modules/projectiles/guns/misc/beam_rifle.dm b/code/modules/projectiles/guns/misc/beam_rifle.dm
index 23b94a5e43c..db1f3a1325c 100644
--- a/code/modules/projectiles/guns/misc/beam_rifle.dm
+++ b/code/modules/projectiles/guns/misc/beam_rifle.dm
@@ -431,11 +431,9 @@
var/aoe_mob_damage = 0
var/impact_structure_damage = 0
var/impact_direct_damage = 0
- var/turf/cached
var/list/pierced = list()
/obj/projectile/beam/beam_rifle/proc/AOE(turf/epicenter)
- set waitfor = FALSE
if(!epicenter)
return
new /obj/effect/temp_visual/explosion/fast(epicenter)
@@ -449,31 +447,22 @@
if(!isitem(O))
O.take_damage(aoe_structure_damage * get_damage_coeff(O), BURN, LASER, FALSE)
-/obj/projectile/beam/beam_rifle/proc/check_pierce(atom/target)
- if(!do_pierce)
- return FALSE
- if(pierced[target]) //we already pierced them go away
- return TRUE
- if(isclosedturf(target))
- if(wall_pierce++ < wall_pierce_amount)
- if(prob(wall_devastate))
- if(iswallturf(target))
- var/turf/closed/wall/W = target
- W.dismantle_wall(TRUE, TRUE)
- else
- SSexplosions.medturf += target
- return TRUE
- if(ismovable(target))
- var/atom/movable/AM = target
- if(AM.density && !AM.CanPass(src, get_turf(target)) && !ismob(AM))
- if(structure_pierce < structure_pierce_amount)
- if(isobj(AM))
- var/obj/O = AM
- O.take_damage((impact_structure_damage + aoe_structure_damage) * structure_bleed_coeff * get_damage_coeff(AM), BURN, ENERGY, FALSE)
- pierced[AM] = TRUE
- structure_pierce++
- return TRUE
- return FALSE
+/obj/projectile/beam/beam_rifle/prehit_pierce(atom/A)
+ if(isclosedturf(A) && (wall_pierce < wall_pierce_amount))
+ if(prob(wall_devastate))
+ if(iswallturf(A))
+ var/turf/closed/wall/W = A
+ W.dismantle_wall(TRUE, TRUE)
+ else
+ SSexplosions.medturf += A
+ ++wall_pierce
+ return PROJECTILE_PIERCE_PHASE // yeah this gun is a snowflakey piece of garbage
+ if(isobj(A) && (structure_pierce < structure_pierce_amount))
+ ++structure_pierce
+ var/obj/O = A
+ O.take_damage((impact_structure_damage + aoe_structure_damage) * structure_bleed_coeff * get_damage_coeff(A), BURN, ENERGY, FALSE)
+ return PROJECTILE_PIERCE_PHASE // ditto and this could be refactored to on_hit honestly
+ return ..()
/obj/projectile/beam/beam_rifle/proc/get_damage_coeff(atom/target)
if(istype(target, /obj/machinery/door))
@@ -491,32 +480,18 @@
L.adjustFireLoss(impact_direct_damage)
L.emote("scream")
-/obj/projectile/beam/beam_rifle/proc/handle_hit(atom/target)
+/obj/projectile/beam/beam_rifle/proc/handle_hit(atom/target, piercing_hit = FALSE)
set waitfor = FALSE
- if(!cached && !QDELETED(target))
- cached = get_turf(target)
if(nodamage)
return FALSE
- playsound(cached, 'sound/effects/explosion3.ogg', 100, TRUE)
- AOE(cached)
+ playsound(src, 'sound/effects/explosion3.ogg', 100, TRUE)
+ if(!piercing_hit)
+ AOE(get_turf(target) || get_turf(src))
if(!QDELETED(target))
handle_impact(target)
-/obj/projectile/beam/beam_rifle/Bump(atom/target)
- if(check_pierce(target))
- permutated += target
- trajectory_ignore_forcemove = TRUE
- forceMove(target.loc)
- trajectory_ignore_forcemove = FALSE
- return FALSE
- if(!QDELETED(target))
- cached = get_turf(target)
- return ..()
-
-/obj/projectile/beam/beam_rifle/on_hit(atom/target, blocked = FALSE)
- if(!QDELETED(target))
- cached = get_turf(target)
- handle_hit(target)
+/obj/projectile/beam/beam_rifle/on_hit(atom/target, blocked = FALSE, piercing_hit = FALSE)
+ handle_hit(target, piercing_hit)
return ..()
/obj/projectile/beam/beam_rifle/hitscan
@@ -554,10 +529,9 @@
hitscan_light_color_override = "#99ff99"
reflectable = REFLECT_FAKEPROJECTILE
-/obj/projectile/beam/beam_rifle/hitscan/aiming_beam/prehit(atom/target)
- qdel(src)
- return FALSE
+/obj/projectile/beam/beam_rifle/hitscan/aiming_beam/prehit_pierce(atom/target)
+ return PROJECTILE_DELETE_WITHOUT_HITTING
/obj/projectile/beam/beam_rifle/hitscan/aiming_beam/on_hit()
qdel(src)
- return BULLET_ACT_HIT
+ return BULLET_ACT_BLOCK
diff --git a/code/modules/projectiles/guns/misc/blastcannon.dm b/code/modules/projectiles/guns/misc/blastcannon.dm
index b831d311654..b6e4b88f6f8 100644
--- a/code/modules/projectiles/guns/misc/blastcannon.dm
+++ b/code/modules/projectiles/guns/misc/blastcannon.dm
@@ -116,7 +116,8 @@
icon_state = "blastwave"
damage = 0
nodamage = FALSE
- movement_type = FLYING | UNSTOPPABLE
+ movement_type = FLYING
+ projectile_phasing = ALL // just blows up the turfs lmao
var/heavyr = 0
var/mediumr = 0
var/lightr = 0
diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm
index 70d950b670e..2dbf436bdd3 100644
--- a/code/modules/projectiles/projectile.dm
+++ b/code/modules/projectiles/projectile.dm
@@ -8,10 +8,10 @@
icon_state = "bullet"
density = FALSE
anchored = TRUE
- pass_flags = PASSTABLE
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
movement_type = FLYING
wound_bonus = CANT_WOUND // can't wound by default
+ generic_canpass = FALSE
//The sound this plays on impact.
var/hitsound = 'sound/weapons/pierce.ogg'
var/hitsound_wall = ""
@@ -25,7 +25,6 @@
var/xo = null
var/atom/original = null // the original target clicked
var/turf/starting = null // the projectile's starting turf
- var/list/permutated = list() // we've passed through these atoms, don't try to hit them again
var/p_x = 16
var/p_y = 16 // the pixel location of the tile that the player clicked. Default is the center
@@ -37,6 +36,35 @@
var/time_offset = 0
var/datum/point/vector/trajectory
var/trajectory_ignore_forcemove = FALSE //instructs forceMove to NOT reset our trajectory to the new location!
+ /// We already impacted these things, do not impact them again. Used to make sure we can pierce things we want to pierce. Lazylist, typecache style (object = TRUE) for performance.
+ var/list/impacted
+ /// If TRUE, we can hit our firer.
+ var/ignore_source_check = FALSE
+ /// We are flagged PHASING temporarily to not stop moving when we Bump something but want to keep going anyways.
+ var/temporary_unstoppable_movement = FALSE
+
+ /** PROJECTILE PIERCING
+ * WARNING:
+ * Projectile piercing MUST be done using these variables.
+ * Ordinary passflags will be **IGNORED**.
+ * The two flag variables below both use pass flags.
+ * In the context of LETPASStHROW, it means the projectile will ignore things that are currently "in the air" from a throw.
+ *
+ * Also, projectiles sense hits using Bump(), and then pierce them if necessary.
+ * They simply do not follow conventional movement rules.
+ * NEVER flag a projectile as PHASING movement type.
+ * If you so badly need to make one go through *everything*, override check_pierce() for your projectile to always return PROJECTILE_PIERCE_PHASE/HIT.
+ */
+ /// The "usual" flags of pass_flags is used in that can_hit_target ignores these unless they're specifically targeted/clicked on. This behavior entirely bypasses process_hit if triggered, rather than phasing which uses prehit_pierce() to check.
+ pass_flags = PASSTABLE
+ /// If FALSE, allow us to hit something directly targeted/clicked/whatnot even if we're able to phase through it
+ var/phasing_ignore_direct_target = FALSE
+ /// Bitflag for things the projectile should just phase through entirely - No hitting unless direct target and [phasing_ignore_direct_target] is FALSE. Uses pass_flags flags.
+ var/projectile_phasing = NONE
+ /// Bitflag for things the projectile should hit, but pierce through without deleting itself. Defers to projectile_phasing. Uses pass_flags flags.
+ var/projectile_piercing = NONE
+ /// number of times we've pierced something. Incremented BEFORE bullet_act and on_hit proc!
+ var/pierces = 0
var/speed = 0.8 //Amount of deciseconds it takes for projectile to travel
var/Angle = 0
@@ -93,8 +121,6 @@
var/homing_offset_x = 0
var/homing_offset_y = 0
- var/ignore_source_check = FALSE
-
var/damage = 10
var/damage_type = BRUTE //BRUTE, BURN, TOX, OXY, CLONE are the only things that should be in here
var/nodamage = FALSE //Determines if the projectile will skip any damage inflictions
@@ -123,8 +149,6 @@
var/impact_effect_type //what type of impact effect to show when hitting something
var/log_override = FALSE //is this type spammed enough to not log? (KAs)
- var/temporary_unstoppable_movement = FALSE
-
///If defined, on hit we create an item of this type then call hitby() on the hit target with this, mainly used for embedding items (bullets) in targets
var/shrapnel_type
///If we have a shrapnel_type defined, these embedding stats will be passed to the spawned shrapnel type, which will roll for embedding on the target
@@ -140,10 +164,8 @@
///How much we want to drop the embed_chance value, if we can embed, per tile, for falloff purposes
var/embed_falloff_tile
-
/obj/projectile/Initialize()
. = ..()
- permutated = list()
decayedRange = range
if(embedding)
updateEmbedding()
@@ -173,11 +195,15 @@
else //when a limb is missing the damage is actually passed to the chest
return BODY_ZONE_CHEST
-/obj/projectile/proc/prehit(atom/target)
- return TRUE
-
-/// Called when the projectile hits something
-/obj/projectile/proc/on_hit(atom/target, blocked = FALSE)
+/**
+ * Called when the projectile hits something
+ *
+ * @params
+ * target - thing hit
+ * blocked - percentage of hit blocked
+ * pierce_hit - are we piercing through or regular hitting
+ */
+/obj/projectile/proc/on_hit(atom/target, blocked = FALSE, pierce_hit)
if(fired_from)
SEND_SIGNAL(fired_from, COMSIG_PROJECTILE_ON_HIT, firer, target, Angle)
// i know that this is probably more with wands and gun mods in mind, but it's a bit silly that the projectile on_hit signal doesn't ping the projectile itself.
@@ -213,6 +239,11 @@
if(!isliving(target))
if(impact_effect_type && !hitscan)
new impact_effect_type(target_loca, hitx, hity)
+ if(isturf(target) && hitsound_wall)
+ var/volume = clamp(vol_by_damage() + 20, 0, 100)
+ if(suppressed)
+ volume = 5
+ playsound(loc, hitsound_wall, volume, TRUE, -1)
return BULLET_ACT_HIT
var/mob/living/L = target
@@ -243,7 +274,7 @@
else
if(hitsound)
var/volume = vol_by_damage()
- playsound(loc, hitsound, volume, TRUE, -1)
+ playsound(src, hitsound, volume, TRUE, -1)
L.visible_message("[L] is hit by \a [src][organ_hit_text]!", \
"You're hit by \a [src][organ_hit_text]!", null, COMBAT_MESSAGE_RANGE)
L.on_hit(src)
@@ -292,16 +323,36 @@
beam_segments[beam_index] = null
/obj/projectile/Bump(atom/A)
+ SEND_SIGNAL(src, COMSIG_MOVABLE_BUMP, A)
+ if(!can_hit_target(A, A == original, TRUE))
+ return
+ Impact(A)
+
+/**
+ * Called when the projectile hits something
+ * This can either be from it bumping something,
+ * or it passing over a turf/being crossed and scanning that there is infact
+ * a valid target it needs to hit.
+ * This target isn't however necessarily WHAT it hits
+ * that is determined by process_hit and select_target.
+ *
+ * Furthermore, this proc shouldn't check can_hit_target - this should only be called if can hit target is already checked.
+ * Also, we select_target to find what to process_hit first.
+ */
+/obj/projectile/proc/Impact(atom/A)
if(!trajectory)
qdel(src)
- return
+ return FALSE
+ if(impacted[A]) // NEVER doublehit
+ return FALSE
var/datum/point/pcache = trajectory.copy_to()
var/turf/T = get_turf(A)
if(ricochets < ricochets_max && check_ricochet_flag(A) && check_ricochet(A))
ricochets++
if(A.handle_ricochet(src))
on_ricochet(A)
- ignore_source_check = TRUE
+ impacted = list() // Shoot a x-ray laser at a pair of mirrors I dare you
+ ignore_source_check = TRUE // Firer is no longer immune
decayedRange = max(0, decayedRange - reflect_range_decrease)
ricochet_chance *= ricochet_decay_chance
damage *= ricochet_decay_damage
@@ -313,73 +364,221 @@
var/distance = get_dist(T, starting) // Get the distance between the turf shot from and the mob we hit and use that for the calculations.
def_zone = ran_zone(def_zone, max(100-(7*distance), 5)) //Lower accurancy/longer range tradeoff. 7 is a balanced number to use.
- if(isturf(A) && hitsound_wall)
- var/volume = clamp(vol_by_damage() + 20, 0, 100)
- if(suppressed)
- volume = 5
- playsound(loc, hitsound_wall, volume, TRUE, -1)
+ return process_hit(T, select_target(T, A)) // SELECT TARGET FIRST!
- return process_hit(T, select_target(T, A))
-
-#define QDEL_SELF 1 //Delete if we're not UNSTOPPABLE flagged non-temporarily
-#define DO_NOT_QDEL 2 //Pass through.
-#define FORCE_QDEL 3 //Force deletion.
-
-/obj/projectile/proc/process_hit(turf/T, atom/target, qdel_self, hit_something = FALSE) //probably needs to be reworked entirely when pixel movement is done.
- if(QDELETED(src) || !T || !target) //We're done, nothing's left.
- if((qdel_self == FORCE_QDEL) || ((qdel_self == QDEL_SELF) && !temporary_unstoppable_movement && !(movement_type & UNSTOPPABLE)))
- qdel(src)
- return hit_something
- permutated |= target //Make sure we're never hitting it again. If we ever run into weirdness with piercing projectiles needing to hit something multiple times.. well.. that's a to-do.
- if(!prehit(target))
- return process_hit(T, select_target(T), qdel_self, hit_something) //Hit whatever else we can since that didn't work.
- SEND_SIGNAL(target, COMSIG_PROJECTILE_PREHIT, args)
- var/result = target.bullet_act(src, def_zone)
- if(result == BULLET_ACT_FORCE_PIERCE)
- if(!(movement_type & UNSTOPPABLE))
- temporary_unstoppable_movement = TRUE
- movement_type |= UNSTOPPABLE
- return process_hit(T, select_target(T), qdel_self, TRUE) //Hit whatever else we can since we're piercing through but we're still on the same tile.
- else if(result == BULLET_ACT_TURF) //We hit the turf but instead we're going to also hit something else on it.
- return process_hit(T, select_target(T), QDEL_SELF, TRUE)
- else //Whether it hit or blocked, we're done!
- qdel_self = QDEL_SELF
- hit_something = TRUE
- if((qdel_self == FORCE_QDEL) || ((qdel_self == QDEL_SELF) && !temporary_unstoppable_movement && !(movement_type & UNSTOPPABLE)))
+/**
+ * The primary workhorse proc of projectile impacts.
+ * This is a RECURSIVE call - process_hit is called on the first selected target, and then repeatedly called if the projectile still hasn't been deleted.
+ *
+ * Order of operations:
+ * 1. Checks if we are deleted, or if we're somehow trying to hit a null, in which case, bail out
+ * 2. Adds the thing we're hitting to impacted so we can make sure we don't doublehit
+ * 3. Checks piercing - stores this.
+ * Afterwards:
+ * Hit and delete, hit without deleting and pass through, pass through without hitting, or delete without hitting depending on result
+ * If we're going through without hitting, find something else to hit if possible and recurse, set unstoppable movement to true
+ * If we're deleting without hitting, delete and return
+ * Otherwise, send signal of COMSIG_PROJECTILE_PREHIT to target
+ * Then, hit, deleting ourselves if necessary.
+ * @params
+ * T - Turf we're on/supposedly hitting
+ * target - target we're hitting
+ * hit_something - only should be set by recursive calling by this proc - tracks if we hit something already
+ *
+ * Returns if we hit something.
+ */
+/obj/projectile/proc/process_hit(turf/T, atom/target, hit_something = FALSE)
+ // 1.
+ if(QDELETED(src) || !T || !target)
+ return
+ // 2.
+ impacted[target] = TRUE //hash lookup > in for performance in hit-checking
+ // 3.
+ var/mode = prehit_pierce(target)
+ if(mode == PROJECTILE_DELETE_WITHOUT_HITTING)
qdel(src)
+ return hit_something
+ else if(mode == PROJECTILE_PIERCE_PHASE)
+ if(!(movement_type & PHASING))
+ temporary_unstoppable_movement = TRUE
+ movement_type |= PHASING
+ return process_hit(T, select_target(T, target), hit_something) // try to hit something else
+ // at this point we are going to hit the thing
+ // in which case send signal to it
+ SEND_SIGNAL(target, COMSIG_PROJECTILE_PREHIT, args)
+ if(mode == PROJECTILE_PIERCE_HIT)
+ ++pierces
+ hit_something = TRUE
+ var/result = target.bullet_act(src, def_zone, mode == PROJECTILE_PIERCE_HIT)
+ if((result == BULLET_ACT_FORCE_PIERCE) || (mode == PROJECTILE_PIERCE_HIT))
+ if(!(movement_type & PHASING))
+ temporary_unstoppable_movement = TRUE
+ movement_type |= PHASING
+ return process_hit(T, select_target(T, target), TRUE)
+ qdel(src)
return hit_something
-#undef QDEL_SELF
-#undef DO_NOT_QDEL
-#undef FORCE_QDEL
-
-/obj/projectile/proc/select_target(turf/T, atom/target) //Select a target from a turf.
- if((original in T) && can_hit_target(original, permutated, TRUE, TRUE))
+/**
+ * Selects a target to hit from a turf
+ *
+ * @params
+ * T - The turf
+ * target - The "preferred" atom to hit, usually what we Bumped() first.
+ *
+ * Priority:
+ * 0. Anything that is already in impacted is ignored no matter what. Furthermore, in any bracket, if the target atom parameter is in it, that's hit first.
+ * Furthermore, can_hit_target is always checked. This (entire proc) is PERFORMANCE OVERHEAD!! But, it shouldn't be ""too"" bad and I frankly don't have a better *generic non snowflakey* way that I can think of right now at 3 AM.
+ * FURTHERMORE, mobs/objs have a density check from can_hit_target - to hit non dense objects over a turf, you must click on them, same for mobs that usually wouldn't get hit.
+ * 1. The thing originally aimed at/clicked on
+ * 2. Mobs - picks lowest buckled mob to prevent scarp piggybacking memes
+ * 3. Objs
+ * 4. Turf
+ * 5. Nothing
+ */
+/obj/projectile/proc/select_target(turf/T, atom/target)
+ // 1. original
+ if(can_hit_target(original, TRUE, FALSE))
return original
- if(target && can_hit_target(target, permutated, target == original, TRUE))
- return target
- var/list/mob/living/possible_mobs = typecache_filter_list(T, GLOB.typecache_mob)
- var/list/mob/mobs = list()
- for(var/mob/living/M in possible_mobs)
- if(!can_hit_target(M, permutated, M == original, TRUE))
+ var/list/atom/possible = list() // let's define these ONCE
+ var/list/atom/considering = list()
+ // 2. mobs
+ possible = typecache_filter_list(T, GLOB.typecache_living) // living only
+ for(var/i in possible)
+ if(!can_hit_target(i, i == original, TRUE))
continue
- mobs += M
- if (length(mobs))
- var/mob/M = pick(mobs)
+ considering += i
+ if(considering.len)
+ var/mob/living/M = pick(considering)
return M.lowest_buckled_mob()
- var/list/obj/possible_objs = typecache_filter_list(T, GLOB.typecache_machine_or_structure)
- var/list/obj/objs = list()
- for(var/obj/O in possible_objs)
- if(!can_hit_target(O, permutated, O == original, TRUE))
+ considering.len = 0
+ // 3. objs
+ possible = typecache_filter_list(T, GLOB.typecache_machine_or_structure) // because why are items ever dense?
+ for(var/i in possible)
+ if(!can_hit_target(i, i == original, TRUE))
continue
- objs += O
- if (length(objs))
- var/obj/O = pick(objs)
- return O
- //Nothing else is here that we can hit, hit the turf if we haven't.
- if(!(T in permutated) && can_hit_target(T, permutated, T == original, TRUE))
+ considering += i
+ if(considering.len)
+ return pick(considering)
+ // 4. turf
+ if(can_hit_target(T, T == original, TRUE))
return T
- //Returns null if nothing at all was found.
+ // 5. nothing
+ // (returns null)
+
+//Returns true if the target atom is on our current turf and above the right layer
+//If direct target is true it's the originally clicked target.
+/obj/projectile/proc/can_hit_target(atom/target, direct_target = FALSE, ignore_loc = FALSE)
+ if(QDELETED(target) || impacted[target])
+ return FALSE
+ if(!ignore_loc && (loc != target.loc))
+ return FALSE
+ // if pass_flags match, pass through entirely
+ if(target.pass_flags_self & pass_flags) // phasing
+ return FALSE
+ if(!ignore_source_check && firer)
+ var/mob/M = firer
+ if((target == firer) || ((target == firer.loc) && ismecha(firer.loc)) || (target in firer.buckled_mobs) || (istype(M) && (M.buckled == target)))
+ return FALSE
+ if(target.density) //This thing blocks projectiles, hit it regardless of layer/mob stuns/etc.
+ return TRUE
+ if(!isliving(target))
+ if(isturf(target)) // non dense turfs
+ return FALSE
+ if(target.layer < PROJECTILE_HIT_THRESHHOLD_LAYER)
+ return FALSE
+ else if(!direct_target) // non dense objects do not get hit unless specifically clicked
+ return FALSE
+ else
+ var/mob/living/L = target
+ if(direct_target)
+ return TRUE
+ // If target not able to use items, move and stand - or if they're just dead, pass over.
+ if(L.stat == DEAD || (!hit_stunned_targets && HAS_TRAIT(L, TRAIT_IMMOBILIZED) && HAS_TRAIT(L, TRAIT_FLOORED) && HAS_TRAIT(L, TRAIT_HANDS_BLOCKED)))
+ return FALSE
+ return TRUE
+
+/**
+ * Scan if we should hit something and hit it if we need to
+ * The difference between this and handling in Impact is
+ * In this we strictly check if we need to Impact() something in specific
+ * If we do, we do
+ * We don't even check if it got hit already - Impact() does that
+ * In impact there's more code for selecting WHAT to hit
+ * So this proc is more of checking if we should hit something at all BY having an atom cross us.
+ */
+/obj/projectile/proc/scan_crossed_hit(atom/movable/A)
+ if(can_hit_target(A, direct_target = (A == original)))
+ Impact(A)
+
+/**
+ * Scans if we should hit something on the turf we just moved to if we haven't already
+ *
+ * This proc is a little high in overhead but allows us to not snowflake CanPass in living and other things.
+ */
+/obj/projectile/proc/scan_moved_turf()
+ // Optimally, we scan: mobs --> objs --> turf for impact
+ // but, overhead is a thing and 2 for loops every time it moves is a no-go.
+ // realistically, since we already do select_target in impact, we can not do that
+ // and hope projectiles get refactored again in the future to have a less stupid impact detection system
+ // that hopefully won't also involve a ton of overhead
+ if(can_hit_target(original, TRUE, FALSE))
+ Impact(original) // try to hit thing clicked on
+ // else, try to hit mobs
+ else // because if we impacted original and pierced we'll already have select target'd and hit everything else we should be hitting
+ for(var/mob/M in loc) // so I guess we're STILL doing a for loop of mobs because living movement would otherwise have snowflake code for projectile CanPass
+ // so the snowflake vs performance is pretty arguable here
+ if(can_hit_target(M, M == original, TRUE))
+ Impact(M)
+ break
+
+/**
+ * Projectile crossed: When something enters a projectile's tile, make sure the projectile hits it if it should be hitting it.
+ */
+/obj/projectile/Crossed(atom/movable/AM)
+ . = ..()
+ scan_crossed_hit(AM)
+
+/**
+ * Projectile can pass through
+ * Used to not even attempt to Bump() or fail to Cross() anything we already hit.
+ */
+/obj/projectile/CanPassThrough(atom/blocker, turf/target, blocker_opinion)
+ return impacted[blocker]? TRUE : ..()
+
+/**
+ * Projectile moved:
+ *
+ * If not fired yet, do not do anything. Else,
+ *
+ * If temporary unstoppable movement used for piercing through things we already hit (impacted list) is set, unset it.
+ * Scan turf we're now in for anything we can/should hit. This is useful for hitting non dense objects the user
+ * directly clicks on, as well as for PHASING projectiles to be able to hit things at all as they don't ever Bump().
+ */
+/obj/projectile/Moved(atom/OldLoc, Dir)
+ . = ..()
+ if(!fired)
+ return
+ if(temporary_unstoppable_movement)
+ temporary_unstoppable_movement = FALSE
+ movement_type &= ~PHASING
+ scan_moved_turf() //mostly used for making sure we can hit a non-dense object the user directly clicked on, and for penetrating projectiles that don't bump
+
+/**
+ * Checks if we should pierce something.
+ *
+ * NOT meant to be a pure proc, since this replaces prehit() which was used to do things.
+ * Return PROJECTILE_DELETE_WITHOUT_HITTING to delete projectile without hitting at all!
+ */
+/obj/projectile/proc/prehit_pierce(atom/A)
+ if(projectile_phasing & A.pass_flags_self)
+ return PROJECTILE_PIERCE_PHASE
+ if(projectile_piercing & A.pass_flags_self)
+ return PROJECTILE_PIERCE_HIT
+ if(ismovable(A))
+ var/atom/movable/AM = A
+ if(AM.throwing)
+ return (projectile_phasing & LETPASSTHROW)? PROJECTILE_PIERCE_PHASE : ((projectile_piercing & LETPASSTHROW)? PROJECTILE_PIERCE_HIT : PROJECTILE_PIERCE_NONE)
+ return PROJECTILE_PIERCE_NONE
/obj/projectile/proc/check_ricochet(atom/A)
var/chance = ricochet_chance * A.receive_ricochet_chance_mod
@@ -447,10 +646,9 @@
AddElement(/datum/element/embed, projectile_payload = shrapnel_type)
if(!log_override && firer && original)
log_combat(firer, original, "fired at", src, "from [get_area_name(src, TRUE)]")
- if(direct_target)
- if(prehit(direct_target))
- direct_target.bullet_act(src, def_zone)
- qdel(src)
+ if(direct_target && (get_dist(direct_target, get_turf(src)) <= 1)) // point blank shots
+ process_hit(get_turf(direct_target), direct_target)
+ if(QDELETED(src))
return
if(isnum(angle))
setAngle(angle)
@@ -469,6 +667,7 @@
var/matrix/M = new
M.Turn(Angle)
transform = M
+ LAZYINITLIST(impacted)
trajectory_ignore_forcemove = TRUE
forceMove(starting)
trajectory_ignore_forcemove = FALSE
@@ -500,6 +699,8 @@
if(zc)
before_z_change(old, target)
. = ..()
+ if(QDELETED(src)) // we coulda bumped something
+ return
if(trajectory && !trajectory_ignore_forcemove && isturf(target))
if(hitscan)
finalize_hitscan_and_generate_tracers(FALSE)
@@ -609,33 +810,6 @@
if(prob(50))
homing_offset_y = -homing_offset_y
-//Returns true if the target atom is on our current turf and above the right layer
-//If direct target is true it's the originally clicked target.
-/obj/projectile/proc/can_hit_target(atom/target, list/passthrough, direct_target = FALSE, ignore_loc = FALSE)
- if(QDELETED(target))
- return FALSE
- if(!ignore_source_check && firer)
- var/mob/M = firer
- if((target == firer) || ((target == firer.loc) && ismecha(firer.loc)) || (target in firer.buckled_mobs) || (istype(M) && (M.buckled == target)))
- return FALSE
- if(!ignore_loc && (loc != target.loc))
- return FALSE
- if(target in passthrough)
- return FALSE
- if(target.density) //This thing blocks projectiles, hit it regardless of layer/mob stuns/etc.
- return TRUE
- if(!isliving(target))
- if(target.layer < PROJECTILE_HIT_THRESHHOLD_LAYER)
- return FALSE
- else
- var/mob/living/L = target
- if(direct_target)
- return TRUE
- // If target not able to use items, move and stand - or if they're just dead, pass over.
- if(L.stat == DEAD || (!hit_stunned_targets && HAS_TRAIT(L, TRAIT_IMMOBILIZED) && HAS_TRAIT(L, TRAIT_FLOORED) && HAS_TRAIT(L, TRAIT_HANDS_BLOCKED)))
- return FALSE
- return TRUE
-
//Spread is FORCED!
/obj/projectile/proc/preparePixelProjectile(atom/target, atom/source, params, spread = 0)
var/turf/curloc = get_turf(source)
@@ -695,22 +869,6 @@
angle = ATAN2(y - oy, x - ox)
return list(angle, p_x, p_y)
-/obj/projectile/Crossed(atom/movable/AM) //A mob moving on a tile with a projectile is hit by it.
- . = ..()
- if(isliving(AM) && !(pass_flags & PASSMOB))
- var/mob/living/L = AM
- if(can_hit_target(L, permutated, (AM == original)))
- Bump(AM)
-
-/obj/projectile/Move(atom/newloc, dir = NONE)
- . = ..()
- if(.)
- if(temporary_unstoppable_movement)
- temporary_unstoppable_movement = FALSE
- movement_type &= ~(UNSTOPPABLE)
- if(fired && can_hit_target(original, permutated, TRUE))
- Bump(original)
-
/obj/projectile/Destroy()
if(hitscan)
finalize_hitscan_and_generate_tracers()
diff --git a/code/modules/projectiles/projectile/bullets/sniper.dm b/code/modules/projectiles/projectile/bullets/sniper.dm
index a065bc23ee6..7c33e0c4761 100644
--- a/code/modules/projectiles/projectile/bullets/sniper.dm
+++ b/code/modules/projectiles/projectile/bullets/sniper.dm
@@ -33,7 +33,8 @@
name = "penetrator round"
icon_state = "gauss"
damage = 60
- movement_type = FLYING | UNSTOPPABLE
+ projectile_piercing = PASSMOB
+ projectile_phasing = (ALL & (~PASSMOB))
dismemberment = 0 //It goes through you cleanly.
paralyze = 0
breakthings = FALSE
diff --git a/code/modules/projectiles/projectile/bullets/special.dm b/code/modules/projectiles/projectile/bullets/special.dm
index 755eee796aa..8927594054d 100644
--- a/code/modules/projectiles/projectile/bullets/special.dm
+++ b/code/modules/projectiles/projectile/bullets/special.dm
@@ -3,7 +3,8 @@
/obj/projectile/bullet/honker
name = "banana"
damage = 0
- movement_type = FLYING | UNSTOPPABLE
+ movement_type = FLYING
+ projectile_piercing = ALL
nodamage = TRUE
hitsound = 'sound/items/bikehorn.ogg'
icon = 'icons/obj/hydroponics/harvest.dmi'
diff --git a/code/modules/projectiles/projectile/magic.dm b/code/modules/projectiles/projectile/magic.dm
index 2b1ab775aa4..618f9e0f59b 100644
--- a/code/modules/projectiles/projectile/magic.dm
+++ b/code/modules/projectiles/projectile/magic.dm
@@ -402,18 +402,17 @@
. = ..()
locker_temp_instance = new(src)
-/obj/projectile/magic/locker/prehit(atom/A)
+/obj/projectile/magic/locker/prehit_pierce(atom/A)
+ . = ..()
if(isliving(A) && locker_suck)
var/mob/living/M = A
- if(M.anti_magic_check())
+ if(M.anti_magic_check()) // no this doesn't check if ..() returned to phase through do I care no it's magic ain't gotta explain shit
M.visible_message("[src] vanishes on contact with [A]!")
- qdel(src)
- return
+ return PROJECTILE_DELETE_WITHOUT_HITTING
if(!locker_temp_instance.insertion_allowed(M))
- return ..()
+ return
M.forceMove(src)
- return FALSE
- return ..()
+ return PROJECTILE_PIERCE_PHASE
/obj/projectile/magic/locker/on_hit(target)
if(created)
diff --git a/code/modules/projectiles/projectile/special/curse.dm b/code/modules/projectiles/projectile/special/curse.dm
index c4b90ac9ba9..a60bf444c6d 100644
--- a/code/modules/projectiles/projectile/special/curse.dm
+++ b/code/modules/projectiles/projectile/special/curse.dm
@@ -17,7 +17,6 @@
/obj/projectile/curse_hand/Initialize(mapload)
. = ..()
- movement_type |= UNSTOPPABLE
handedness = prob(50)
icon_state = "cursehand[handedness]"
@@ -29,18 +28,14 @@
arm = starting.Beam(src, icon_state = "curse[handedness]", time = INFINITY, maxdistance = INFINITY, beam_type=/obj/effect/ebeam/curse_arm)
..()
-/obj/projectile/curse_hand/prehit(atom/target)
- if(target == original)
- movement_type &= ~(UNSTOPPABLE)
- else if(!isturf(target))
- return FALSE
- return ..()
+/obj/projectile/curse_hand/prehit_pierce(atom/target)
+ return (target == original)? PROJECTILE_PIERCE_NONE : PROJECTILE_PIERCE_PHASE
/obj/projectile/curse_hand/Destroy()
if(arm)
arm.End()
arm = null
- if((movement_type & UNSTOPPABLE))
+ if((movement_type & PHASING))
playsound(src, 'sound/effects/curse3.ogg', 25, TRUE, -1)
var/turf/T = get_step(src, dir)
var/obj/effect/temp_visual/dir_setting/curse/hand/leftover = new(T, dir)
diff --git a/code/modules/religion/religion_structures.dm b/code/modules/religion/religion_structures.dm
index ffad8ef18d1..061d4285ec4 100644
--- a/code/modules/religion/religion_structures.dm
+++ b/code/modules/religion/religion_structures.dm
@@ -7,7 +7,7 @@
anchored = TRUE
layer = TABLE_LAYER
climbable = TRUE
- pass_flags = LETPASSTHROW
+ pass_flags_self = LETPASSTHROW
can_buckle = TRUE
buckle_lying = 90 //we turn to you!
///Avoids having to check global everytime by referencing it locally.
diff --git a/code/modules/spells/spell_types/projectile.dm b/code/modules/spells/spell_types/projectile.dm
index c70e9988333..8441365eeac 100644
--- a/code/modules/spells/spell_types/projectile.dm
+++ b/code/modules/spells/spell_types/projectile.dm
@@ -91,7 +91,7 @@
projectile.icon_state = proj_icon_state
projectile.name = proj_name
if(proj_insubstantial)
- projectile.movement_type |= UNSTOPPABLE
+ projectile.movement_type |= PHASING
if(proj_homing)
projectile.homing = TRUE
projectile.homing_turn_speed = 360 //Perfect tracking
diff --git a/code/modules/unit_tests/_unit_tests.dm b/code/modules/unit_tests/_unit_tests.dm
index 167f9df247b..27cf984fbc8 100644
--- a/code/modules/unit_tests/_unit_tests.dm
+++ b/code/modules/unit_tests/_unit_tests.dm
@@ -52,6 +52,7 @@
#include "outfit_sanity.dm"
#include "pills.dm"
#include "plantgrowth_tests.dm"
+#include "projectiles.dm"
#include "reagent_id_typos.dm"
#include "reagent_mod_expose.dm"
#include "reagent_mod_procs.dm"
diff --git a/code/modules/unit_tests/projectiles.dm b/code/modules/unit_tests/projectiles.dm
new file mode 100644
index 00000000000..06a8fb0780a
--- /dev/null
+++ b/code/modules/unit_tests/projectiles.dm
@@ -0,0 +1,5 @@
+/datum/unit_test/projectile_movetypes/Run()
+ for(var/path in typesof(/obj/projectile))
+ var/obj/projectile/projectile = path
+ if(initial(projectile.movement_type) & PHASING)
+ Fail("[path] has default movement type PHASING. Piercing projectiles should be done using the projectile piercing system, not movement_types!")
diff --git a/code/modules/vehicles/ridden.dm b/code/modules/vehicles/ridden.dm
index c6fbcd8a6ac..fdd43c8719b 100644
--- a/code/modules/vehicles/ridden.dm
+++ b/code/modules/vehicles/ridden.dm
@@ -4,6 +4,7 @@
max_buckled_mobs = 1
buckle_lying = 0
default_driver_move = FALSE
+ pass_flags_self = PASSTABLE
var/rider_check_flags = REQUIRES_LEGS | REQUIRES_ARMS
COOLDOWN_DECLARE(message_cooldown)
@@ -69,7 +70,7 @@
return FALSE
if(rider_check_flags & REQUIRES_LEGS && HAS_TRAIT(user, TRAIT_FLOORED))
- if(rider_check_flags & UNBUCKLE_DISABLED_RIDER)
+ if(rider_check_flags & UNBUCKLE_DISABLED_RIDER)
unbuckle_mob(user, TRUE)
user.visible_message("[user] falls off \the [src].",\
"You fall off \the [src] while trying to operate it while unable to stand!")
@@ -96,7 +97,7 @@
to_chat(user, "You can't seem to manage that unable to hold onto \the [src] to move it...")
COOLDOWN_START(src, message_cooldown, 5 SECONDS)
return FALSE
-
+
var/datum/component/riding/R = GetComponent(/datum/component/riding)
R.handle_ride(user, direction)
return ..()
@@ -114,9 +115,3 @@
/obj/vehicle/ridden/zap_act(power, zap_flags)
zap_buckle_check(power)
return ..()
-
-/obj/vehicle/ridden/CanAllowThrough(atom/movable/mover, turf/target)
- . = ..()
-
- if(mover.pass_flags & PASSTABLE)
- return TRUE
diff --git a/tgstation.dme b/tgstation.dme
index d2186bfcbc0..29fcd1decd7 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -94,6 +94,7 @@
#include "code\__DEFINES\processing.dm"
#include "code\__DEFINES\procpath.dm"
#include "code\__DEFINES\profile.dm"
+#include "code\__DEFINES\projectiles.dm"
#include "code\__DEFINES\qdel.dm"
#include "code\__DEFINES\radiation.dm"
#include "code\__DEFINES\radio.dm"