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"