diff --git a/aurorastation.dme b/aurorastation.dme
index 191dcdea47c..55e1e7d7a92 100644
--- a/aurorastation.dme
+++ b/aurorastation.dme
@@ -45,6 +45,7 @@
#include "code\__DEFINES\chemistry.dm"
#include "code\__DEFINES\circuitboard.dm"
#include "code\__DEFINES\color.dm"
+#include "code\__DEFINES\combat.dm"
#include "code\__DEFINES\components.dm"
#include "code\__DEFINES\construction.dm"
#include "code\__DEFINES\cooldowns.dm"
@@ -655,6 +656,7 @@
#include "code\game\area\areas.dm"
#include "code\game\atom\_atom.dm"
#include "code\game\atom\atom_act.dm"
+#include "code\game\atom\atom_defense.dm"
#include "code\game\atom\atom_examine.dm"
#include "code\game\atom\atoms_initializing_EXPENSIVE.dm"
#include "code\game\dna\dna2.dm"
diff --git a/code/ZAS/Atom.dm b/code/ZAS/Atom.dm
index ef3768ad985..d1138035768 100644
--- a/code/ZAS/Atom.dm
+++ b/code/ZAS/Atom.dm
@@ -1,5 +1,9 @@
/turf/CanPass(atom/movable/mover, turf/target, height=1.5,air_group=0)
- if(!target) return 0
+ if(!target)
+ return FALSE
+
+ if(mover?.movement_type & PHASING)
+ return TRUE
if(istype(mover)) // turf/Enter(...) will perform more advanced checks
return !density
diff --git a/code/__DEFINES/_flags.dm b/code/__DEFINES/_flags.dm
index c9e1c03fd85..0b7ecadf5b1 100644
--- a/code/__DEFINES/_flags.dm
+++ b/code/__DEFINES/_flags.dm
@@ -10,6 +10,11 @@ GLOBAL_LIST_INIT(bitflags, list(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 204
///Whether /atom/Initialize() has already run for the object
#define INITIALIZED_1 (1<<5)
+/// If the thing can reflect light (lasers/energy)
+#define RICOCHET_SHINY (1<<0)
+/// If the thing can reflect matter (bullets/bomb shrapnel)
+#define RICOCHET_HARD (1<<1)
+
/*
These defines are used specifically with the atom/pass_flags bitmask
the atom/checkpass() proc uses them (tables will call movable atom checkpass(PASSTABLE) for example)
diff --git a/code/__DEFINES/combat.dm b/code/__DEFINES/combat.dm
new file mode 100644
index 00000000000..0bbb7748e59
--- /dev/null
+++ b/code/__DEFINES/combat.dm
@@ -0,0 +1,6 @@
+//bullet_act() return values
+#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 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/dcs/signals/signals_atom/signals_atom_x_act.dm b/code/__DEFINES/dcs/signals/signals_atom/signals_atom_x_act.dm
index b1d0d978cae..0a44eae825c 100644
--- a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_x_act.dm
+++ b/code/__DEFINES/dcs/signals/signals_atom/signals_atom_x_act.dm
@@ -8,3 +8,13 @@
#define COMSIG_ATOM_EMP_ACT "atom_emp_act"
///from base of atom/fire_act(): (exposed_temperature, exposed_volume)
#define COMSIG_ATOM_FIRE_ACT "atom_fire_act"
+///from base of atom/bullet_act(): (/obj/projectile, def_zone)
+#define COMSIG_ATOM_PRE_BULLET_ACT "pre_atom_bullet_act"
+ /// All this does is prevent default bullet on_hit from being called, [BULLET_ACT_HIT] being return is implied
+ #define COMPONENT_BULLET_ACTED (1<<0)
+ /// Forces bullet act to return [BULLET_ACT_BLOCK], takes priority over above
+ #define COMPONENT_BULLET_BLOCKED (1<<1)
+ /// Forces bullet act to return [BULLET_ACT_FORCE_PIERCE], takes priority over above
+ #define COMPONENT_BULLET_PIERCED (1<<2)
+///from base of atom/bullet_act(): (/obj/projectile, def_zone)
+#define COMSIG_ATOM_BULLET_ACT "atom_bullet_act"
diff --git a/code/__DEFINES/dcs/signals/signals_object/signals_object.dm b/code/__DEFINES/dcs/signals/signals_object/signals_object.dm
index e0b2007c016..bf28b192571 100644
--- a/code/__DEFINES/dcs/signals/signals_object/signals_object.dm
+++ b/code/__DEFINES/dcs/signals/signals_object/signals_object.dm
@@ -17,3 +17,49 @@
#define COMSIG_ITEM_DROPPED "item_drop"
///from base of obj/item/pickup(): (mob/user)
#define COMSIG_ITEM_PICKUP "item_pickup"
+
+// /obj/projectile signals (sent to the firer)
+
+///from base of /obj/projectile/proc/on_hit(), like COMSIG_PROJECTILE_ON_HIT but on the projectile itself and with the hit limb (if any): (atom/movable/firer, atom/target, angle, hit_limb, blocked)
+#define COMSIG_PROJECTILE_SELF_ON_HIT "projectile_self_on_hit"
+///from base of /obj/projectile/proc/on_hit(): (atom/movable/firer, atom/target, angle, hit_limb, blocked)
+#define COMSIG_PROJECTILE_ON_HIT "projectile_on_hit"
+///from base of /obj/projectile/proc/fire(): (obj/projectile, atom/original_target)
+#define COMSIG_PROJECTILE_BEFORE_FIRE "projectile_before_fire"
+///from base of /obj/projectile/proc/fire(): (obj/projectile, atom/firer, atom/original_target)
+#define COMSIG_PROJECTILE_FIRER_BEFORE_FIRE "projectile_firer_before_fire"
+///from the base of /obj/projectile/proc/fire(): ()
+#define COMSIG_PROJECTILE_FIRE "projectile_fire"
+///sent to targets during the process_hit proc of projectiles
+#define COMSIG_PROJECTILE_PREHIT "com_proj_prehit"
+ #define PROJECTILE_INTERRUPT_HIT (1<<0)
+///from /obj/projectile/pixel_move(): ()
+#define COMSIG_PROJECTILE_PIXEL_STEP "projectile_pixel_step"
+///sent to self during the process_hit proc of projectiles
+#define COMSIG_PROJECTILE_SELF_PREHIT "com_proj_prehit"
+///from the base of /obj/projectile/Range(): ()
+#define COMSIG_PROJECTILE_RANGE "projectile_range"
+///from the base of /obj/projectile/on_range(): ()
+#define COMSIG_PROJECTILE_RANGE_OUT "projectile_range_out"
+///from the base of /obj/projectile/process(): ()
+#define COMSIG_PROJECTILE_BEFORE_MOVE "projectile_before_move"
+///from [/obj/item/proc/tryEmbed] sent when trying to force an embed (mainly for projectiles and eating glass)
+#define COMSIG_EMBED_TRY_FORCE "item_try_embed"
+ #define COMPONENT_EMBED_SUCCESS (1<<1)
+// FROM [/obj/item/proc/updateEmbedding] sent when an item's embedding properties are changed : ()
+#define COMSIG_ITEM_EMBEDDING_UPDATE "item_embedding_update"
+
+///sent to targets during the process_hit proc of projectiles
+// #define COMSIG_FIRE_CASING "fire_casing"
+
+///from the base of /obj/item/ammo_casing/ready_proj() : (atom/target, mob/living/user, quiet, zone_override, atom/fired_from)
+#define COMSIG_CASING_READY_PROJECTILE "casing_ready_projectile"
+
+///sent to the projectile after an item is spawned by the projectile_drop element: (new_item)
+#define COMSIG_PROJECTILE_ON_SPAWN_DROP "projectile_on_spawn_drop"
+///sent to the projectile when spawning the item (shrapnel) that may be embedded: (new_item)
+#define COMSIG_PROJECTILE_ON_SPAWN_EMBEDDED "projectile_on_spawn_embedded"
+
+/// from /obj/projectile/energy/fisher/on_hit() or /obj/item/gun/energy/recharge/fisher when striking a target
+#define COMSIG_HIT_BY_SABOTEUR "hit_by_saboteur"
+ #define COMSIG_SABOTEUR_SUCCESS (1<<0)
diff --git a/code/__DEFINES/layers.dm b/code/__DEFINES/layers.dm
index 23c28359191..49aec27f2fa 100644
--- a/code/__DEFINES/layers.dm
+++ b/code/__DEFINES/layers.dm
@@ -71,6 +71,7 @@
//HIDING MOB
#define HIDING_MOB_LAYER 2.16
#define SHALLOW_FLUID_LAYER 2.17
+ #define PROJECTILE_HIT_THRESHHOLD_LAYER 2.3
// OBJ
#define BELOW_DOOR_LAYER 2.69
#define OPEN_DOOR_LAYER 2.70
diff --git a/code/__DEFINES/maths.dm b/code/__DEFINES/maths.dm
index 861c4f4d297..b20013b9005 100644
--- a/code/__DEFINES/maths.dm
+++ b/code/__DEFINES/maths.dm
@@ -13,8 +13,34 @@
#define ROUND_UP(x) ( -round(-(x)))
+// round() acts like floor(x, 1) by default but can't handle other values
+#define FLOOR(x, y) ( round((x) / (y)) * (y) )
+
+// Real modulus that handles decimals
+#define MODULUS(x, y) ( (x) - FLOOR(x, y))
+
#define ATAN2(x, y) ( !(x) && !(y) ? 0 : (y) >= 0 ? arccos((x) / sqrt((x)*(x) + (y)*(y))) : -arccos((x) / sqrt((x)*(x) + (y)*(y))) )
+// Will filter out extra rotations and negative rotations
+// E.g: 540 becomes 180. -180 becomes 180.
+#define SIMPLIFY_DEGREES(degrees) (MODULUS((degrees), 360))
+
+#define GET_ANGLE_OF_INCIDENCE(face, input) (MODULUS((face) - (input), 360))
+
+//Finds the shortest angle that angle A has to change to get to angle B. Aka, whether to move clock or counterclockwise.
+/proc/closer_angle_difference(a, b)
+ if(!isnum(a) || !isnum(b))
+ return
+ a = SIMPLIFY_DEGREES(a)
+ b = SIMPLIFY_DEGREES(b)
+ var/inc = b - a
+ if(inc < 0)
+ inc += 360
+ var/dec = a - b
+ if(dec < 0)
+ dec += 360
+ . = inc > dec? -dec : inc
+
/// Converts a probability/second chance to probability/seconds_per_tick chance
/// For example, if you want an event to happen with a 10% per second chance, but your proc only runs every 5 seconds, do `if(prob(100*SPT_PROB_RATE(0.1, 5)))`
#define SPT_PROB_RATE(prob_per_second, seconds_per_tick) (1 - (1 - (prob_per_second)) ** (seconds_per_tick))
diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm
index ef377742c13..295809d1fcd 100644
--- a/code/__DEFINES/misc.dm
+++ b/code/__DEFINES/misc.dm
@@ -229,12 +229,6 @@
#define SCANNER_REAGENT BITFLAG(1)
#define SCANNER_GAS BITFLAG(2)
-// Special return values from bullet_act(). Positive return values are already used to indicate the blocked level of the projectile.
-#define PROJECTILE_CONTINUE -1 //if the projectile should continue flying after calling bullet_act()
-#define PROJECTILE_FORCE_MISS -2 //if the projectile should treat the attack as a miss (suppresses attack and admin logs) - only applies to mobs.
-#define PROJECTILE_DODGED -3 //this is similar to the above, but the check and message is run on the mob, instead of on the projectile code. basically just has a unique message
-#define PROJECTILE_STOPPED -4 //stops the projectile completely, as if a shield absorbed it
-
//Camera capture modes
#define CAPTURE_MODE_REGULAR 0 //Regular polaroid camera mode
#define CAPTURE_MODE_ALL 1 //Admin camera mode
diff --git a/code/__DEFINES/projectiles.dm b/code/__DEFINES/projectiles.dm
index c7f6b7548f6..95bb1d6ee5f 100644
--- a/code/__DEFINES/projectiles.dm
+++ b/code/__DEFINES/projectiles.dm
@@ -1,3 +1,13 @@
+// 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
+
// IFF values
#define IFF_DEFAULT "station"
#define IFF_TCFL "tcfl"
diff --git a/code/__DEFINES/subsystem-priority.dm b/code/__DEFINES/subsystem-priority.dm
index 9a8d183c34f..572c39c4edb 100644
--- a/code/__DEFINES/subsystem-priority.dm
+++ b/code/__DEFINES/subsystem-priority.dm
@@ -28,7 +28,6 @@
#define SS_PRIORITY_SMOOTHING 10 // Smooth turf generation.
#define SS_PRIORITY_ORBIT 5 // Orbit datum updates.
#define SS_PRIORITY_ICON_UPDATE 5 // Queued icon updates. Mostly used by APCs and tables.
-#define SS_PRIORITY_PROJECTILES 5 // Projectile processing!
// Normal
#define SS_PRIORITY_TICKER 100 // Gameticker.
diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm
index 547a7fa7504..e682f517258 100644
--- a/code/__DEFINES/traits.dm
+++ b/code/__DEFINES/traits.dm
@@ -189,3 +189,10 @@
/// Causes the mob to never clot their wounds
#define TRAIT_DISABILITY_HEMOPHILIA_MAJOR "disability_hemophilia_major"
+
+/// hnnnnnnnggggg..... you're pretty good....
+#define TRAIT_NICE_SHOT "nice_shot"
+/// Mobs with this trait cannot be hit by projectiles, meaning the projectiles will just go through.
+#define TRAIT_UNHITTABLE_BY_PROJECTILES "unhittable_by_projectiles"
+///This mob is currently blocking a projectile.
+#define TRAIT_BLOCKING_PROJECTILES "blocking_projectiles"
diff --git a/code/__HELPERS/atoms.dm b/code/__HELPERS/atoms.dm
index a9f1a550e54..9b3e54af836 100644
--- a/code/__HELPERS/atoms.dm
+++ b/code/__HELPERS/atoms.dm
@@ -66,6 +66,15 @@
return atom_list
+///Returns the last atom type in the specified loc
+/proc/get_highest_loc(atom/loc, type)
+ var/atom/last_found = null
+ while(loc)
+ if(istype(loc, type))
+ last_found = loc
+ loc = loc.loc
+ return last_found
+
/// Returns an x and y value require to reverse the transformations made to center an oversized icon
/atom/proc/get_oversized_icon_offsets()
if (pixel_x == 0 && pixel_y == 0)
diff --git a/code/__HELPERS/maths.dm b/code/__HELPERS/maths.dm
index 3ac127c5343..cffb2951054 100644
--- a/code/__HELPERS/maths.dm
+++ b/code/__HELPERS/maths.dm
@@ -1,5 +1,20 @@
-// round() acts like floor(x, 1) by default but can't handle other values
-#define FLOOR(x, y) ( round((x) / (y)) * (y) )
+///Calculate the angle between two movables and the west|east coordinate
+/proc/get_angle(atom/movable/start, atom/movable/end)//For beams.
+ if(!start || !end)
+ return 0
+ var/dy =(32 * end.y + end.pixel_y) - (32 * start.y + start.pixel_y)
+ var/dx =(32 * end.x + end.pixel_x) - (32 * start.x + start.pixel_x)
+ return delta_to_angle(dx, dy)
+
+/// Calculate the angle produced by a pair of x and y deltas
+/proc/delta_to_angle(x, y)
+ if(!y)
+ return (x >= 0) ? 90 : 270
+ . = arctan(x/y)
+ if(y < 0)
+ . += 180
+ else if(x < 0)
+ . += 360
// round() acts like floor(x, 1) by default but can't handle other values
#define FLOOR_FLOAT(x, y) ( round((x) / (y)) * (y) )
@@ -37,9 +52,6 @@
/proc/Ceiling(x, y=1)
return -round(-x / y) * y
-// Real modulus that handles decimals
-#define MODULUS(x, y) ( (x) - FLOOR_FLOAT(x, y))
-
/proc/Percent(current_value, max_value, rounding = 1)
return round((current_value / max_value) * 100, rounding)
@@ -166,10 +178,6 @@
if(isnum(num)&&isnum(min)&&isnum(max))
return ((min <= num) && (num <= max))
-// Will filter out extra rotations and negative rotations
-// E.g: 540 becomes 180. -180 becomes 180.
-#define SIMPLIFY_DEGREES(degrees) (MODULUS((degrees), 360))
-
/// Value or the next multiple of divisor in a positive direction. Ceilm(-1.5, 0.3) = -1.5 , Ceilm(-1.5, 0.4) = -1.2
#define Ceilm(value, divisor) ( -round(-(value) / (divisor)) * (divisor) )
diff --git a/code/__HELPERS/matrices.dm b/code/__HELPERS/matrices.dm
index 661a7aa06a6..1faa78aa816 100644
--- a/code/__HELPERS/matrices.dm
+++ b/code/__HELPERS/matrices.dm
@@ -40,8 +40,7 @@
decompose_matrix.rotation = arctan(cossine, sine) * flip_sign
/matrix/proc/TurnTo(old_angle, new_angle)
- . = new_angle - old_angle
- Turn(.) //BYOND handles cases such as -270, 360, 540 etc. DOES NOT HANDLE 180 TURNS WELL, THEY TWEEN AND LOOK LIKE SHIT
+ return Turn(new_angle - old_angle) //BYOND handles cases such as -270, 360, 540 etc. DOES NOT HANDLE 180 TURNS WELL, THEY TWEEN AND LOOK LIKE SHIT
/**
* Shear the transform on either or both axes.
diff --git a/code/__HELPERS/view.dm b/code/__HELPERS/view.dm
index 8d17b316772..7479d49d4b1 100644
--- a/code/__HELPERS/view.dm
+++ b/code/__HELPERS/view.dm
@@ -1,15 +1,24 @@
/proc/getviewsize(view)
- var/viewX
- var/viewY
+ if(!view) // Just to avoid any runtimes that could otherwise cause constant disconnect loops.
+ stack_trace("Missing value for 'view' in getviewsize(), defaulting to world.view!")
+ view = world.view
+
if(isnum(view))
- var/totalviewrange = 1 + 2 * view
- viewX = totalviewrange
- viewY = totalviewrange
+ var/totalviewrange = (view < 0 ? -1 : 1) + 2 * view
+ return list(totalviewrange, totalviewrange)
else
- var/list/viewrangelist = splittext(view,"x")
- viewX = text2num(viewrangelist[1])
- viewY = text2num(viewrangelist[2])
- return list(viewX, viewY)
+ var/list/viewrangelist = splittext(view, "x")
+ return list(text2num(viewrangelist[1]), text2num(viewrangelist[2]))
+
+
+/// Takes a string or num view, and converts it to pixel width/height in a list(pixel_width, pixel_height)
+/proc/view_to_pixels(view)
+ if(!view)
+ return list(0, 0)
+ var/list/view_info = getviewsize(view)
+ view_info[1] *= world.icon_size
+ view_info[2] *= world.icon_size
+ return view_info
/proc/in_view_range(mob/user, atom/A)
var/list/view_range = getviewsize(user.client.view)
diff --git a/code/_onclick/click.dm b/code/_onclick/click.dm
index 82abbd77408..7518ba94274 100644
--- a/code/_onclick/click.dm
+++ b/code/_onclick/click.dm
@@ -341,12 +341,7 @@
setClickCooldown(4)
var/turf/T = get_turf(src)
src.visible_message(SPAN_DANGER("\The [src]'s eyes flare with ruby light!"))
- var/obj/projectile/beam/LE = new (T)
- LE.muzzle_type = /obj/effect/projectile/muzzle/eyelaser
- LE.tracer_type = /obj/effect/projectile/tracer/eyelaser
- LE.impact_type = /obj/effect/projectile/impact/eyelaser
- playsound(usr.loc, 'sound/weapons/wave.ogg', 75, 1)
- LE.launch_projectile(A, zone_sel? zone_sel.selecting : null, src, params)
+ fire_projectile(/obj/projectile/beam, T, 'sound/weapons/wave.ogg', firer = src)
/mob/living/carbon/human/LaserEyes(atom/A, params)
if(nutrition <= 0)
diff --git a/code/controllers/configuration.dm b/code/controllers/configuration.dm
index 2cd675b4cb5..fad3f9ea33c 100644
--- a/code/controllers/configuration.dm
+++ b/code/controllers/configuration.dm
@@ -278,9 +278,6 @@ GLOBAL_LIST_EMPTY(gamemode_cache)
//so that it's similar to DAMAGE_PAIN. Lowered it a bit since hitting paincrit takes much longer to wear off than a halloss stun.
var/organ_damage_spillover_multiplier = 0.5
- var/bones_can_break = 0
- var/limbs_can_break = 0
-
var/revival_pod_plants = 1
var/revival_cloning = 1
var/revival_brain_life = -1
@@ -1117,10 +1114,6 @@ GENERAL_PROTECT_DATUM(/datum/configuration)
GLOB.config.default_brain_health = text2num(value)
if(!GLOB.config.default_brain_health || GLOB.config.default_brain_health < 1)
GLOB.config.default_brain_health = initial(GLOB.config.default_brain_health)
- if("bones_can_break")
- GLOB.config.bones_can_break = value
- if("limbs_can_break")
- GLOB.config.limbs_can_break = value
if("walk_speed")
GLOB.config.walk_speed = value
diff --git a/code/controllers/subsystems/processing/projectiles.dm b/code/controllers/subsystems/processing/projectiles.dm
index 2b7200cfb99..8f68b59f2c8 100644
--- a/code/controllers/subsystems/processing/projectiles.dm
+++ b/code/controllers/subsystems/processing/projectiles.dm
@@ -1,7 +1,15 @@
PROCESSING_SUBSYSTEM_DEF(projectiles)
name = "Projectiles"
- stat_tag = "PROJ"
- priority = SS_PRIORITY_PROJECTILES
- flags = SS_TICKER|SS_NO_INIT
wait = 1
+ stat_tag = "PP"
+ flags = SS_NO_INIT|SS_TICKER
var/global_max_tick_moves = 10
+ var/global_pixel_speed = 2
+ var/global_iterations_per_move = 16
+
+/datum/controller/subsystem/processing/projectiles/proc/set_pixel_speed(new_speed)
+ global_pixel_speed = new_speed
+ for(var/i in processing)
+ var/obj/projectile/P = i
+ if(istype(P)) //there's non projectiles on this too.
+ P.set_pixel_speed(new_speed)
diff --git a/code/defines/obj/weapon.dm b/code/defines/obj/weapon.dm
index 07bd9c5a273..27d2a2ad668 100644
--- a/code/defines/obj/weapon.dm
+++ b/code/defines/obj/weapon.dm
@@ -86,7 +86,7 @@
if (targetIsHuman)
var/mob/living/carbon/human/targethuman = target
armorpercent = targethuman.get_blocked_ratio(target_zone, DAMAGE_BRUTE, damage = force)*100
- wasblocked = targethuman.check_shields(force, src, user, target_zone, null)
+ wasblocked = (targethuman.check_shields(force, src, user, target_zone, null) in list(BULLET_ACT_BLOCK, BULLET_ACT_FORCE_PIERCE))
var/damageamount = force
diff --git a/code/game/atom/_atom.dm b/code/game/atom/_atom.dm
index f7c23e7ef14..ec86e6e7256 100644
--- a/code/game/atom/_atom.dm
+++ b/code/game/atom/_atom.dm
@@ -13,6 +13,13 @@
///First atom flags var
var/flags_1 = NONE
+ var/flags_ricochet = NONE
+
+ ///When a projectile tries to ricochet off this atom, the projectile ricochet chance is multiplied by this
+ var/receive_ricochet_chance_mod = 1
+ ///When a projectile ricochets off this atom, it deals the normal damage * this modifier to this atom
+ var/receive_ricochet_damage_coeff = 0.33
+
var/update_icon_on_init = FALSE // Default to 'no'.
layer = TURF_LAYER
@@ -107,6 +114,20 @@
. = ..()
+/atom/proc/handle_ricochet(obj/projectile/ricocheting_projectile)
+ var/turf/p_turf = get_turf(ricocheting_projectile)
+ var/face_direction = get_dir(src, p_turf) || get_dir(src, ricocheting_projectile)
+ var/face_angle = dir2angle(face_direction)
+ var/incidence_s = GET_ANGLE_OF_INCIDENCE(face_angle, (ricocheting_projectile.Angle + 180))
+ var/a_incidence_s = abs(incidence_s)
+ if(a_incidence_s > 90 && a_incidence_s < 270)
+ return FALSE
+ // if((ricocheting_projectile.armor_flag in list(BULLET, BOMB)) && ricocheting_projectile.ricochet_incidence_leeway)
+ // if((a_incidence_s < 90 && a_incidence_s < 90 - ricocheting_projectile.ricochet_incidence_leeway) || (a_incidence_s > 270 && a_incidence_s -270 > ricocheting_projectile.ricochet_incidence_leeway))
+ // return FALSE
+ var/new_angle_s = SIMPLIFY_DEGREES(face_angle + incidence_s)
+ ricocheting_projectile.set_angle(new_angle_s)
+ return TRUE
///Purpose: Determines if the object (or airflow) can pass this atom.
///Called by: Movement, airflow.
@@ -116,6 +137,8 @@
/atom/proc/CanPass(atom/movable/mover, turf/target, height=1.5, air_group = 0)
//I have condensed TG's `CanAllowThrough()` into this proc
if(mover) //Because some procs send null as a mover
+ if(mover.movement_type & PHASING)
+ return TRUE
if(mover.pass_flags & pass_flags_self)
return TRUE
if(mover.throwing && (pass_flags_self & LETPASSTHROW))
diff --git a/code/game/atom/atom_act.dm b/code/game/atom/atom_act.dm
index b94bb71c7ec..95ab64b9249 100644
--- a/code/game/atom/atom_act.dm
+++ b/code/game/atom/atom_act.dm
@@ -35,6 +35,36 @@
if(density && !has_gravity(hitting_atom)) //thrown stuff bounces off dense stuff in no grav, unless the thrown stuff ends up inside what it hit(embedding, bola, etc...).
addtimer(CALLBACK(src, PROC_REF(hitby_react), hitting_atom), 0.2 SECONDS)
+/**
+ * React to a hit by a projectile object
+ *
+ * @params
+ * * hitting_projectile - projectile
+ * * def_zone - zone hit
+ * * piercing_hit - is this hit piercing or normal?
+ */
+/atom/proc/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit = FALSE)
+ SHOULD_CALL_PARENT(TRUE)
+
+ var/sigreturn = SEND_SIGNAL(src, COMSIG_ATOM_PRE_BULLET_ACT, hitting_projectile, def_zone)
+ if(sigreturn & COMPONENT_BULLET_PIERCED)
+ return BULLET_ACT_FORCE_PIERCE
+ if(sigreturn & COMPONENT_BULLET_BLOCKED)
+ return BULLET_ACT_BLOCK
+ if(sigreturn & COMPONENT_BULLET_ACTED)
+ return BULLET_ACT_HIT
+
+ SEND_SIGNAL(src, COMSIG_ATOM_BULLET_ACT, hitting_projectile, def_zone)
+ if(QDELETED(hitting_projectile)) // Signal deleted it?
+ return BULLET_ACT_BLOCK
+
+ return hitting_projectile.on_hit(
+ target = src,
+ // This armor check only matters for the visuals and messages in on_hit(), it's not actually used to reduce damage since
+ // only living mobs use armor to reduce damage, but on_hit() is going to need the value no matter what is shot.
+ blocked = check_projectile_armor(def_zone, hitting_projectile),
+ def_zone = def_zone) //Last param is the zone, unlike TG's one which is piercing hit
+
/**
* We have have actually hit the passed in atom
*
diff --git a/code/game/atom/atom_defense.dm b/code/game/atom/atom_defense.dm
new file mode 100644
index 00000000000..1bf1fa61d2e
--- /dev/null
+++ b/code/game/atom/atom_defense.dm
@@ -0,0 +1,5 @@
+/// A cut-out proc for [/atom/proc/bullet_act] so living mobs can have their own armor behavior checks without causing issues with needing their own on_hit call
+/atom/proc/check_projectile_armor(def_zone, obj/projectile/impacting_projectile, is_silent)
+ // if(uses_integrity)
+ // return clamp(PENETRATE_ARMOUR(get_armor_rating(impacting_projectile.armor_flag), impacting_projectile.armour_penetration), 0, 100)
+ return 0
diff --git a/code/game/atoms.dm b/code/game/atoms.dm
index f21659df7d6..fdea78fcd83 100644
--- a/code/game/atoms.dm
+++ b/code/game/atoms.dm
@@ -118,10 +118,6 @@
/atom/proc/flash_act(intensity = FLASH_PROTECTION_MODERATE, override_blindness_check = FALSE, affect_silicon = FALSE, ignore_inherent = FALSE, type = /atom/movable/screen/fullscreen/flash, length = 2.5 SECONDS)
return
-/atom/proc/bullet_act(obj/projectile/P, def_zone)
- P.on_hit(src, 0, def_zone)
- . = 0
-
/atom/proc/in_contents_of(container) // Can take class or object instance as argument.
if(ispath(container))
if(istype(src.loc, container))
diff --git a/code/game/gamemodes/cult/structures/pylon.dm b/code/game/gamemodes/cult/structures/pylon.dm
index 55cbefc7e42..8d30aac1ebc 100644
--- a/code/game/gamemodes/cult/structures/pylon.dm
+++ b/code/game/gamemodes/cult/structures/pylon.dm
@@ -328,20 +328,19 @@
last_target_loc = get_turf(target.loc)
process_interval = 1 //Instantly wake up if we found a target
- var/obj/projectile/beam/cult/A
+
+ var/projectile_type
if(empowered > 0)
- A = new /obj/projectile/beam/cult/heavy(loc)
+ projectile_type = /obj/projectile/beam/cult/heavy
empowered = max(0, empowered-1)
- playsound(loc, 'sound/weapons/laserdeep.ogg', 100, 1)
if(empowered <= 0)
update_icon()
else
- A = new /obj/projectile/beam/cult(loc)
- playsound(loc, 'sound/weapons/laserdeep.ogg', 65, 1)
- A.ignore = sacrificer
- A.launch_projectile(target)
+ projectile_type = new /obj/projectile/beam/cult(loc)
+
+ src.fire_projectile(projectile_type, target, 'sound/weapons/laserdeep.ogg', src, list(sacrificer))
+
next_shot = world.time + shot_delay
- A = null //So projectiles can GC
addtimer(CALLBACK(src, PROC_REF(handle_firing)), shot_delay + 1)
/obj/structure/cult/pylon/attack_hand(mob/living/user)
@@ -398,8 +397,12 @@
return
return ..()
-/obj/structure/cult/pylon/bullet_act(var/obj/projectile/Proj)
- attackpylon(Proj.firer, Proj.damage, Proj)
+/obj/structure/cult/pylon/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
+ . = ..()
+ if(. != BULLET_ACT_HIT)
+ return .
+
+ attackpylon(hitting_projectile.firer, hitting_projectile.damage, hitting_projectile)
//Explosions will usually cause instant shattering, or heavy damage
//Class 3 or lower blast is sometimes survivable. 2 or higher will always shatter
diff --git a/code/game/gamemodes/technomancer/devices/shield_armor.dm b/code/game/gamemodes/technomancer/devices/shield_armor.dm
index b4fa472e621..6c4fd11f0a2 100644
--- a/code/game/gamemodes/technomancer/devices/shield_armor.dm
+++ b/code/game/gamemodes/technomancer/devices/shield_armor.dm
@@ -30,7 +30,7 @@
/obj/item/clothing/suit/armor/shield/handle_shield(mob/user, var/on_back, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack")
//Since this is a pierce of armor that is passive, we do not need to check if the user is incapacitated.
if(!active)
- return FALSE
+ return BULLET_ACT_HIT
var/modified_block_percentage = block_percentage
@@ -46,7 +46,7 @@
to_chat(user, SPAN_DANGER("Your shield fades due to lack of energy!"))
active = 0
update_icon()
- return 0
+ return BULLET_ACT_HIT
damage = damage - damage_blocked
@@ -65,7 +65,7 @@
spark(src, 5, GLOB.cardinal)
playsound(src, 'sound/weapons/blade.ogg', 50, 1)
- return FALSE // This shield does not block all damage, so returning 0 is needed to tell the game to apply the new damage.
+ return BULLET_ACT_HIT // This shield does not block all damage, so returning 0 is needed to tell the game to apply the new damage.
/obj/item/clothing/suit/armor/shield/attack_self(mob/user)
active = !active
diff --git a/code/game/gamemodes/technomancer/devices/tesla_armor.dm b/code/game/gamemodes/technomancer/devices/tesla_armor.dm
index 59157671d16..5da0c3e29d5 100644
--- a/code/game/gamemodes/technomancer/devices/tesla_armor.dm
+++ b/code/game/gamemodes/technomancer/devices/tesla_armor.dm
@@ -28,17 +28,17 @@
var/obj/projectile/P = damage_source
if(P.firer && get_dist(user, P.firer) <= 3)
if(ready)
- shoot_lightning(P.firer, 2000)
+ INVOKE_ASYNC(src, PROC_REF(shoot_lightning), P.firer, 2000)
else
- shoot_lightning(P.firer, 1000, /obj/projectile/beam/lightning/small)
+ INVOKE_ASYNC(src, PROC_REF(shoot_lightning), P.firer, 1000, /obj/projectile/beam/lightning/small)
else
if(attacker && attacker != user)
if(get_dist(user, attacker) <= 3) //Anyone farther away than three tiles is too far to shoot lightning at.
if(ready)
- shoot_lightning(attacker, 2000)
+ INVOKE_ASYNC(src, PROC_REF(shoot_lightning), attacker, 2000)
else
- shoot_lightning(attacker, 1000, /obj/projectile/beam/lightning/small)
+ INVOKE_ASYNC(src, PROC_REF(shoot_lightning), attacker, 1000, /obj/projectile/beam/lightning/small)
//Deal with protecting our wearer now.
if(ready)
@@ -46,8 +46,8 @@
addtimer(CALLBACK(src, PROC_REF(recharge), user), cooldown_to_charge)
visible_message(SPAN_DANGER("\The [user]'s [src.name] blocks [attack_text]!"))
update_icon()
- return PROJECTILE_STOPPED
- return FALSE
+ return BULLET_ACT_BLOCK
+ return BULLET_ACT_HIT
/obj/item/clothing/suit/armor/tesla/proc/recharge(var/mob/user)
ready = TRUE
diff --git a/code/game/gamemodes/technomancer/spells/energy_siphon.dm b/code/game/gamemodes/technomancer/spells/energy_siphon.dm
index 1b498824e81..0368abddef2 100644
--- a/code/game/gamemodes/technomancer/spells/energy_siphon.dm
+++ b/code/game/gamemodes/technomancer/spells/energy_siphon.dm
@@ -181,7 +181,7 @@
/obj/projectile/beam/lightning/energy_siphon/Bump(atom/A as mob|obj|turf|area, forced=0)
if(A == firer) // For this, you CAN shoot yourself.
- on_impact(A)
+ on_hit(A)
density = 0
set_invisibility(101)
@@ -190,7 +190,12 @@
return 1
..()
-/obj/projectile/beam/lightning/energy_siphon/attack_mob(var/mob/living/target_mob, var/distance, var/miss_modifier=0)
+/obj/projectile/beam/lightning/energy_siphon/on_hit(atom/target, blocked, def_zone)
+ if(!isliving(target))
+ return ..()
+
+ var/mob/living/target_mob = target
+
if(target_mob == firer) // This shouldn't actually occur due to Bump(), but just in-case.
return 1
if(ishuman(target_mob)) // Otherwise someone else stood in the beam and is going to pay for it.
diff --git a/code/game/gamemodes/technomancer/spells/projectile/lightning.dm b/code/game/gamemodes/technomancer/spells/projectile/lightning.dm
index 88370ad5b61..1f99217a435 100644
--- a/code/game/gamemodes/technomancer/spells/projectile/lightning.dm
+++ b/code/game/gamemodes/technomancer/spells/projectile/lightning.dm
@@ -42,7 +42,6 @@
power = 1000
-/obj/projectile/beam/lightning/attack_mob(var/mob/living/target_mob, var/distance, var/miss_modifier=0)
- ..()
- tesla_zap(target_mob, 3, power)
- return 1
+/obj/projectile/beam/lightning/on_hit(atom/target, blocked, def_zone)
+ . = ..()
+ tesla_zap(target, 3, power)
diff --git a/code/game/gamemodes/technomancer/spells/reflect.dm b/code/game/gamemodes/technomancer/spells/reflect.dm
index 030e715fb4f..d451c653e5d 100644
--- a/code/game/gamemodes/technomancer/spells/reflect.dm
+++ b/code/game/gamemodes/technomancer/spells/reflect.dm
@@ -28,14 +28,14 @@
/obj/item/spell/reflect/handle_shield(mob/user, var/on_back, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack")
if(user.incapacitated())
- return FALSE
+ return BULLET_ACT_HIT
var/damage_to_energy_cost = (damage_to_energy_multiplier * damage)
if(!pay_energy(damage_to_energy_cost))
to_chat(owner, SPAN_DANGER("Your shield fades due to lack of energy!"))
qdel(src)
- return FALSE
+ return BULLET_ACT_HIT
//block as long as they are not directly behind us
var/bad_arc = reverse_direction(user.dir) //arc of directions from which we cannot block
@@ -66,7 +66,7 @@
to_chat(owner, SPAN_DANGER("Your shield fades due being used up!"))
qdel(src)
- return PROJECTILE_CONTINUE // complete projectile permutation
+ return BULLET_ACT_FORCE_PIERCE // complete projectile permutation
else if(istype(damage_source, /obj/item))
var/obj/item/W = damage_source
@@ -85,5 +85,5 @@
spawn(2 SECONDS) //To ensure that most or all of a burst fire cycle is reflected.
to_chat(owner, SPAN_DANGER("Your shield fades due being used up!"))
qdel(src)
- return PROJECTILE_STOPPED
- return FALSE
+ return BULLET_ACT_BLOCK
+ return BULLET_ACT_HIT
diff --git a/code/game/gamemodes/technomancer/spells/shield.dm b/code/game/gamemodes/technomancer/spells/shield.dm
index c53e2790ada..d6c580ec175 100644
--- a/code/game/gamemodes/technomancer/spells/shield.dm
+++ b/code/game/gamemodes/technomancer/spells/shield.dm
@@ -29,7 +29,7 @@
/obj/item/spell/shield/handle_shield(mob/user, var/on_back, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack")
if(user.incapacitated())
- return FALSE
+ return BULLET_ACT_HIT
var/damage_to_energy_cost = damage_to_energy_multiplier * damage
@@ -47,7 +47,7 @@
if(!pay_energy(damage_to_energy_cost))
to_chat(owner, SPAN_DANGER("Your shield fades due to lack of energy!"))
qdel(src)
- return FALSE
+ return BULLET_ACT_HIT
//block as long as they are not directly behind us
var/bad_arc = reverse_direction(user.dir) //arc of directions from which we cannot block
@@ -56,5 +56,5 @@
spark(src, 3, GLOB.cardinal)
playsound(src, 'sound/weapons/blade.ogg', 50, 1)
adjust_instability(2)
- return PROJECTILE_STOPPED
- return FALSE
+ return BULLET_ACT_BLOCK
+ return BULLET_ACT_HIT
diff --git a/code/game/gamemodes/vampire/vampire_powers.dm b/code/game/gamemodes/vampire/vampire_powers.dm
index 052ca5e100b..59ab9e9d631 100644
--- a/code/game/gamemodes/vampire/vampire_powers.dm
+++ b/code/game/gamemodes/vampire/vampire_powers.dm
@@ -451,6 +451,10 @@
var/datum/vampire/owner_vampire = null
var/warning_level = 0
+/obj/effect/dummy/veil_walk/Initialize(mapload, ...)
+ . = ..()
+ RegisterSignal(src, COMSIG_ATOM_PRE_BULLET_ACT, PROC_REF(handle_bullet_act))
+
/obj/effect/dummy/veil_walk/Destroy()
eject_all()
@@ -458,6 +462,11 @@
return ..()
+/obj/effect/dummy/veil_walk/proc/handle_bullet_act(datum/source, obj/projectile/projectile)
+ SIGNAL_HANDLER
+
+ return COMPONENT_BULLET_BLOCKED
+
/obj/effect/dummy/veil_walk/proc/eject_all()
for(var/atom/movable/A in src)
A.forceMove(last_valid_turf)
@@ -573,9 +582,6 @@
/obj/effect/dummy/veil_walk/ex_act(vars)
return
-/obj/effect/dummy/veil_walk/bullet_act(vars)
- return
-
// Heals the vampire at the cost of blood.
/mob/living/carbon/human/proc/vampire_bloodheal()
set category = "Vampire"
diff --git a/code/game/machinery/anti_bluespace.dm b/code/game/machinery/anti_bluespace.dm
index 9f736bef925..bf95026e000 100644
--- a/code/game/machinery/anti_bluespace.dm
+++ b/code/game/machinery/anti_bluespace.dm
@@ -74,11 +74,15 @@ var/global/list/bluespace_inhibitors
do_break()
-/obj/machinery/anti_bluespace/bullet_act(var/obj/projectile/Proj)
- if(!(Proj.damage_type == DAMAGE_BRUTE || Proj.damage_type == DAMAGE_BURN))
- return
- if(!Proj.damage)
- return
+/obj/machinery/anti_bluespace/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
+ . = ..()
+ if(. != BULLET_ACT_HIT)
+ return .
+
+ if(!(hitting_projectile.damage_type == DAMAGE_BRUTE || hitting_projectile.damage_type == DAMAGE_BURN))
+ return BULLET_ACT_BLOCK
+ if(!hitting_projectile.damage)
+ return BULLET_ACT_BLOCK
do_break()
diff --git a/code/game/machinery/atmoalter/canister.dm b/code/game/machinery/atmoalter/canister.dm
index d5755f82dd8..081648ba9f6 100644
--- a/code/game/machinery/atmoalter/canister.dm
+++ b/code/game/machinery/atmoalter/canister.dm
@@ -393,14 +393,17 @@ update_flag
return GM.return_pressure()
return 0
-/obj/machinery/portable_atmospherics/canister/bullet_act(var/obj/projectile/Proj)
- if(!(Proj.damage_type == DAMAGE_BRUTE || Proj.damage_type == DAMAGE_BURN))
- return
+/obj/machinery/portable_atmospherics/canister/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
+ . = ..()
+ if(. != BULLET_ACT_HIT)
+ return .
- if(Proj.damage)
- src.health -= round(Proj.damage / 2)
+ if(!(hitting_projectile.damage_type == DAMAGE_BRUTE || hitting_projectile.damage_type == DAMAGE_BURN))
+ return BULLET_ACT_BLOCK
+
+ if(hitting_projectile.damage)
+ src.health -= round(hitting_projectile.damage / 2)
healthcheck()
- ..()
/obj/machinery/portable_atmospherics/canister/AltClick(var/mob/abstract/observer/admin)
if (istype(admin))
diff --git a/code/game/machinery/bots/bots.dm b/code/game/machinery/bots/bots.dm
index e07a948b1df..1345205f195 100644
--- a/code/game/machinery/bots/bots.dm
+++ b/code/game/machinery/bots/bots.dm
@@ -94,9 +94,13 @@
/obj/machinery/bot/bullet_act(var/obj/projectile/Proj)
if(!(Proj.damage_type == DAMAGE_BRUTE || Proj.damage_type == DAMAGE_BURN))
- return
+ return BULLET_ACT_BLOCK
+
+ . = ..()
+ if(. != BULLET_ACT_HIT)
+ return .
+
health -= Proj.damage
- ..()
healthcheck()
/obj/machinery/bot/ex_act(severity)
diff --git a/code/game/machinery/camera/camera.dm b/code/game/machinery/camera/camera.dm
index b8dc9b5dfe6..b4e7967850d 100644
--- a/code/game/machinery/camera/camera.dm
+++ b/code/game/machinery/camera/camera.dm
@@ -138,8 +138,12 @@
update_icon()
update_coverage()
-/obj/machinery/camera/bullet_act(var/obj/projectile/P)
- take_damage(P.get_structure_damage())
+/obj/machinery/camera/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
+ . = ..()
+ if(. != BULLET_ACT_HIT)
+ return .
+
+ take_damage(hitting_projectile.get_structure_damage())
/obj/machinery/camera/ex_act(severity)
if(src.invuln)
diff --git a/code/game/machinery/computer/computer.dm b/code/game/machinery/computer/computer.dm
index 4d41eb98fc8..693583474b0 100644
--- a/code/game/machinery/computer/computer.dm
+++ b/code/game/machinery/computer/computer.dm
@@ -67,10 +67,13 @@
set_broken()
return
-/obj/machinery/computer/bullet_act(var/obj/projectile/Proj)
- if(prob(Proj.get_structure_damage()))
+/obj/machinery/computer/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
+ . = ..()
+ if(. != BULLET_ACT_HIT)
+ return .
+
+ if(prob(hitting_projectile.get_structure_damage()))
set_broken()
- ..()
/obj/machinery/computer/update_icon()
switch(dir)
@@ -201,6 +204,8 @@
/obj/machinery/computer/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
if (!mover)
return 1
+ if(mover?.movement_type & PHASING)
+ return TRUE
if(istype(mover,/obj/projectile) && density && is_holographic)
if (prob(80))
//Holoscreens are non solid, and the frames of the computers are thin. So projectiles will usually
diff --git a/code/game/machinery/constructable_frame.dm b/code/game/machinery/constructable_frame.dm
index 984e7ef5042..58087644d45 100644
--- a/code/game/machinery/constructable_frame.dm
+++ b/code/game/machinery/constructable_frame.dm
@@ -232,6 +232,8 @@
/obj/machinery/constructable_frame/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
if(!mover)
return TRUE
+ if(mover.movement_type & PHASING)
+ return TRUE
if(istype(mover,/obj/projectile) && density)
if(prob(50))
return TRUE
diff --git a/code/game/machinery/deployable.dm b/code/game/machinery/deployable.dm
index 4dbf75e24ce..bfe5deeb169 100644
--- a/code/game/machinery/deployable.dm
+++ b/code/game/machinery/deployable.dm
@@ -41,16 +41,20 @@ Deployable Kits
maxhealth = material.integrity
health = maxhealth
-/obj/structure/blocker/bullet_act(obj/projectile/P, def_zone)
+/obj/structure/blocker/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
+ . = ..()
+ if(. != BULLET_ACT_HIT)
+ return .
+
var/damage_modifier = 0.4
- switch(P.damage_type)
+ switch(hitting_projectile.damage_type)
if(DAMAGE_BURN)
damage_modifier = 1
if(DAMAGE_BRUTE)
damage_modifier = 0.75
- health -= P.damage * damage_modifier
+ health -= hitting_projectile.damage * damage_modifier
if(!check_dismantle())
- visible_message(SPAN_WARNING("\The [src] is hit by \the [P]!"))
+ visible_message(SPAN_WARNING("\The [src] is hit by \the [hitting_projectile]!"))
/obj/structure/blocker/attackby(obj/item/attacking_item, mob/user)
if(attacking_item.ishammer() && user.a_intent != I_HURT)
@@ -105,6 +109,8 @@ Deployable Kits
/obj/structure/blocker/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)//So bullets will fly over and stuff.
if(air_group || (height==0))
return TRUE
+ if(mover.movement_type & PHASING)
+ return TRUE
if(istype(mover, /obj/projectile))
var/obj/projectile/P = mover
if(P.original == src)
@@ -217,6 +223,8 @@ Deployable Kits
icon_state = "[initial(icon_state)][src.locked]"
/obj/machinery/deployable/barrier/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)//So bullets will fly over and stuff.
+ if(mover?.movement_type & PHASING)
+ return TRUE
if(air_group || (height==0))
return 1
if(istype(mover) && mover.pass_flags & PASSTABLE)
diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm
index a9b21796057..41723b60354 100644
--- a/code/game/machinery/doors/door.dm
+++ b/code/game/machinery/doors/door.dm
@@ -207,8 +207,11 @@
/obj/machinery/door/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
- if(air_group) return !block_air_zones
+ if(air_group)
+ return !block_air_zones
if (istype(mover))
+ if(mover.movement_type & PHASING)
+ return TRUE
if(mover.pass_flags & PASSGLASS)
return !opacity
if(density && hashatch && mover.pass_flags & PASSDOORHATCH)
@@ -236,17 +239,19 @@
else do_animate("deny")
return
-/obj/machinery/door/bullet_act(var/obj/projectile/Proj)
- ..()
+/obj/machinery/door/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
+ . = ..()
+ if(. != BULLET_ACT_HIT)
+ return .
- var/damage = Proj.get_structure_damage()
+ var/damage = hitting_projectile.get_structure_damage()
// Emitter Blasts - these will eventually completely destroy the door, given enough time.
if (damage > 90)
destroy_hits--
if (destroy_hits <= 0)
visible_message(SPAN_DANGER("\The [src.name] disintegrates!"))
- switch (Proj.damage_type)
+ switch (hitting_projectile.damage_type)
if(DAMAGE_BRUTE)
new /obj/item/stack/material/steel(src.loc, 2)
new /obj/item/stack/rods(src.loc, 3)
diff --git a/code/game/machinery/doors/windowdoor.dm b/code/game/machinery/doors/windowdoor.dm
index 5a76977ed68..eb0cf01d385 100644
--- a/code/game/machinery/doors/windowdoor.dm
+++ b/code/game/machinery/doors/windowdoor.dm
@@ -83,11 +83,17 @@
flick("[base_state]deny", src)
/obj/machinery/door/window/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
+ if(mover?.movement_type & PHASING)
+ return TRUE
if(get_dir(loc, target) == dir) //Make sure looking at appropriate border
- if(air_group) return 0
+ if(air_group)
+ return FALSE
+
return !density
+
else
- return 1
+
+ return TRUE
/obj/machinery/door/window/CheckExit(atom/movable/mover as mob|obj, turf/target as turf)
if(istype(mover) && mover.pass_flags & PASSGLASS)
diff --git a/code/game/machinery/firealarm.dm b/code/game/machinery/firealarm.dm
index cccd2a3ae0a..f21a925c33c 100644
--- a/code/game/machinery/firealarm.dm
+++ b/code/game/machinery/firealarm.dm
@@ -82,8 +82,12 @@
if(exposed_temperature > T0C+200)
src.alarm() // added check of detector status here
-/obj/machinery/firealarm/bullet_act()
- return src.alarm()
+/obj/machinery/firealarm/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
+ . = ..()
+ if(. != BULLET_ACT_HIT)
+ return .
+
+ src.alarm()
/obj/machinery/firealarm/emp_act(severity)
. = ..()
diff --git a/code/game/machinery/howitzer.dm b/code/game/machinery/howitzer.dm
index 5f41106066f..f22dc116a94 100644
--- a/code/game/machinery/howitzer.dm
+++ b/code/game/machinery/howitzer.dm
@@ -321,7 +321,10 @@ ABSTRACT_TYPE(/obj/machinery/howitzer)
stack_trace("Unable to locate the target, somehow.")
return
- shot_projectile.launch_projectile(target)
+ shot_projectile.preparePixelProjectile(target, get_turf(src))
+ shot_projectile.firer = src
+ shot_projectile.fired_from = src
+ shot_projectile.fire()
flick((icon_state + "_fire"), src)
@@ -383,9 +386,8 @@ ABSTRACT_TYPE(/obj/projectile/howitzer)
icon_state = "howitzer_ammo"
damage = 0
range = 999 //Follow what the path says, not range
- forcedodge = TRUE //Don't directly hit people
-/obj/projectile/howitzer/can_hit_target(atom/target, list/passthrough)
+/obj/projectile/howitzer/can_hit_target(atom/target, direct_target = FALSE, ignore_loc = FALSE, cross_failed = FALSE)
if(target == original)
return TRUE
else
@@ -394,14 +396,14 @@ ABSTRACT_TYPE(/obj/projectile/howitzer)
//We have to handle collisions like the snowflake projectile we are. Or rewrite the projectile logic, you can do that if you want, I do not
/obj/projectile/howitzer/Collide(atom/A)
if(A == original)
- on_impact(A)
+ on_hit(A)
qdel(src)
-/obj/projectile/howitzer/on_impact(atom/A, affected_limb)
+/obj/projectile/howitzer/on_hit(atom/target, blocked, def_zone)
. = ..()
- if(A == original)
- terminal_effect(get_turf(A))
+ if(target == original)
+ terminal_effect(get_turf(target))
/**
* Takes care of performing the terminal effect of the projectile
diff --git a/code/game/machinery/machinery.dm b/code/game/machinery/machinery.dm
index 9cd51336f28..26dc04c4e72 100644
--- a/code/game/machinery/machinery.dm
+++ b/code/game/machinery/machinery.dm
@@ -504,10 +504,13 @@ Class Procs:
paper.forceMove(loc)
printing = FALSE
-/obj/machinery/bullet_act(obj/projectile/P, def_zone)
+/obj/machinery/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
. = ..()
- if(P.get_structure_damage() > 5)
- bullet_ping(P)
+ if(. != BULLET_ACT_HIT)
+ return .
+
+ if(hitting_projectile.get_structure_damage() > 5)
+ bullet_ping(hitting_projectile)
/obj/machinery/proc/do_hair_pull(mob/living/carbon/human/H)
if(stat & (NOPOWER|BROKEN))
diff --git a/code/game/machinery/portable_turret.dm b/code/game/machinery/portable_turret.dm
index 1cf8558dc0a..c844cf9aa84 100644
--- a/code/game/machinery/portable_turret.dm
+++ b/code/game/machinery/portable_turret.dm
@@ -444,17 +444,20 @@
if(health <= 0)
die() //the death process :(
-/obj/machinery/porta_turret/bullet_act(obj/projectile/Proj)
- var/damage = Proj.get_structure_damage()
+/obj/machinery/porta_turret/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
+ var/damage = hitting_projectile.get_structure_damage()
if(!damage)
- return
+ return BULLET_ACT_BLOCK
if(enabled)
if(!attacked && !emagged)
attacked = 1
addtimer(CALLBACK(src, PROC_REF(reset_attacked)), 60, TIMER_UNIQUE | TIMER_OVERRIDE)
- ..()
+
+ . = ..()
+ if(. != BULLET_ACT_HIT)
+ return .
take_damage(damage)
@@ -609,8 +612,8 @@
if(ispath(projectile, /obj/projectile/beam) || ispath(eprojectile, /obj/projectile/beam))
flags |= PASSTABLE|PASSGLASS|PASSGRILLE|PASSRAILING
- if(!(L in check_trajectory(L, src, pass_flags=flags))) //check if we have true line of sight
- return TURRET_NOT_TARGET
+ // if(!(L in check_trajectory(L, src, pass_flags=flags))) //check if we have true line of sight
+ // return TURRET_NOT_TARGET
if(emagged) // If emagged not even the dead get a rest
return L.stat ? TURRET_SECONDARY_TARGET : TURRET_PRIORITY_TARGET
@@ -769,9 +772,12 @@
//Turrets aim for the center of mass by default.
//If the target is grabbing someone then the turret smartly aims for extremities
- var/def_zone = get_exposed_defense_zone(target)
+ A.def_zone = get_exposed_defense_zone(target)
//Shooting Code:
- A.launch_projectile(target, def_zone)
+ A.preparePixelProjectile(target, T)
+ A.firer = src
+ A.fired_from = src
+ A.fire()
last_fired = TRUE
addtimer(CALLBACK(src, PROC_REF(reset_last_fired)), shot_delay, TIMER_UNIQUE | TIMER_OVERRIDE)
diff --git a/code/game/objects/auras/auras.dm b/code/game/objects/auras/auras.dm
index 158e5aab426..2085b5e12af 100644
--- a/code/game/objects/auras/auras.dm
+++ b/code/game/objects/auras/auras.dm
@@ -6,6 +6,15 @@ They should also be used for when you want to effect the ENTIRE mob, like having
/obj/aura
var/mob/living/user
+/obj/aura/Initialize(mapload, ...)
+ . = ..()
+ RegisterSignal(src, COMSIG_ATOM_PRE_BULLET_ACT, PROC_REF(handle_bullet_act))
+
+/obj/aura/proc/handle_bullet_act(datum/source, obj/projectile/projectile)
+ SIGNAL_HANDLER
+
+ return COMPONENT_BULLET_BLOCKED
+
/obj/aura/Destroy()
if(user)
user.remove_aura(src)
@@ -24,8 +33,5 @@ They should also be used for when you want to effect the ENTIRE mob, like having
/obj/aura/attackby(obj/item/attacking_item, mob/user)
return FALSE
-/obj/aura/bullet_act(obj/projectile/P, def_zone)
- return FALSE
-
/obj/aura/hitby(atom/movable/hitting_atom, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum)
return FALSE
diff --git a/code/game/objects/auras/personal_shield.dm b/code/game/objects/auras/personal_shield.dm
index 917a156f1ff..9da6d165a4d 100644
--- a/code/game/objects/auras/personal_shield.dm
+++ b/code/game/objects/auras/personal_shield.dm
@@ -3,13 +3,23 @@
icon = 'icons/effects/effects.dmi'
icon_state = "shield"
+/obj/aura/personal_shield/handle_bullet_act(datum/source, obj/projectile/projectile)
+ user.visible_message(SPAN_WARNING("\The [user]'s [src.name] flashes before \the [projectile] can hit them!"))
+
+ flick("shield_impact", src)
+ playsound(user, 'sound/effects/basscannon.ogg', 35, TRUE)
+
+ return COMPONENT_BULLET_BLOCKED
+
/obj/aura/personal_shield/added_to(mob/living/L)
..()
playsound(user, 'sound/weapons/flash.ogg', 35, 1)
to_chat(user, SPAN_NOTICE("You feel your body prickle as \the [src] comes online."))
-/obj/aura/personal_shield/bullet_act(obj/projectile/P, var/def_zone)
- user.visible_message(SPAN_WARNING("\The [user]'s [src.name] flashes before \the [P] can hit them!"))
+/obj/aura/personal_shield/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
+ SHOULD_CALL_PARENT(FALSE) //Ancient coders deserve the rope
+
+ user.visible_message(SPAN_WARNING("\The [user]'s [src.name] flashes before \the [hitting_projectile] can hit them!"))
flick("shield_impact", src)
playsound(user, 'sound/effects/basscannon.ogg', 35, TRUE)
diff --git a/code/game/objects/auras/radiant_aura.dm b/code/game/objects/auras/radiant_aura.dm
index 62d3f0257cb..39818bfe880 100644
--- a/code/game/objects/auras/radiant_aura.dm
+++ b/code/game/objects/auras/radiant_aura.dm
@@ -5,6 +5,11 @@
alpha = 75
layer = ABOVE_WINDOW_LAYER
+/obj/aura/radiant_aura/handle_bullet_act(datum/source, obj/projectile/projectile)
+ if(projectile.check_armor == LASER)
+ user.visible_message(SPAN_WARNING("\The [projectile] refracts, bending into \the [user]'s aura."))
+ return COMPONENT_BULLET_BLOCKED
+
/obj/aura/radiant_aura/added_to(mob/living/user)
..()
to_chat(user, SPAN_NOTICE("A bubble of light appears around you, exuding protection and warmth."))
@@ -14,8 +19,10 @@
to_chat(user, SPAN_WARNING("Your protective aura dissipates, leaving you feeling cold and unsafe."))
return ..()
-/obj/aura/radiant_aura/bullet_act(obj/projectile/P, var/def_zone)
- if(P.check_armor == LASER)
- user.visible_message(SPAN_WARNING("\The [P] refracts, bending into \the [user]'s aura."))
+/obj/aura/radiant_aura/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
+ SHOULD_CALL_PARENT(FALSE) //Ancient coders deserve the rope
+
+ if(hitting_projectile.check_armor == LASER)
+ user.visible_message(SPAN_WARNING("\The [hitting_projectile] refracts, bending into \the [user]'s aura."))
return AURA_FALSE
return FALSE
diff --git a/code/game/objects/effects/chem/foam.dm b/code/game/objects/effects/chem/foam.dm
index e521f13146c..ea36be39f94 100644
--- a/code/game/objects/effects/chem/foam.dm
+++ b/code/game/objects/effects/chem/foam.dm
@@ -170,7 +170,11 @@
/obj/structure/foamedmetal/ex_act(severity)
qdel(src)
-/obj/structure/foamedmetal/bullet_act()
+/obj/structure/foamedmetal/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
+ . = ..()
+ if(. != BULLET_ACT_HIT)
+ return .
+
if(metal == 1 || prob(50))
qdel(src)
diff --git a/code/game/objects/effects/effect_system.dm b/code/game/objects/effects/effect_system.dm
index 691cd3366fa..6289a23fe14 100644
--- a/code/game/objects/effects/effect_system.dm
+++ b/code/game/objects/effects/effect_system.dm
@@ -184,11 +184,17 @@ steam.start() -- spawns the effect
M.coughedtime = 0
/obj/effect/effect/smoke/bad/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
- if(air_group || (height==0)) return 1
+ if(air_group || (height==0))
+ return TRUE
+
+ if(mover?.movement_type & PHASING)
+ return TRUE
+
if(istype(mover, /obj/projectile/beam))
var/obj/projectile/beam/B = mover
B.damage = (B.damage/2)
- return 1
+
+ return TRUE
/////////////////////////////////////////////
// Sleep smoke
/////////////////////////////////////////////
diff --git a/code/game/objects/effects/projectile/projectile_effects.dm b/code/game/objects/effects/projectile/projectile_effects.dm
index 4b2fb055155..8583d6c4bd6 100644
--- a/code/game/objects/effects/projectile/projectile_effects.dm
+++ b/code/game/objects/effects/projectile/projectile_effects.dm
@@ -9,7 +9,7 @@
light_range = 2
light_color = "#00ffff"
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
- appearance_flags = 0
+ appearance_flags = LONG_GLIDE
/obj/effect/projectile/singularity_pull()
return
@@ -40,7 +40,7 @@
apply_vars(angle_override, p_x, p_y, color_override, scaling)
return ..()
-/obj/effect/projectile/proc/apply_vars(angle_override, p_x = 0, p_y = 0, color_override, scaling = 1, new_loc, increment = 0)
+/obj/effect/projectile/proc/apply_vars(angle_override, p_x = 0, p_y = 0, color_override, scaling = 1, atom/new_loc, increment = 0)
var/mutable_appearance/look = new(src)
look.pixel_x = p_x
look.pixel_y = p_y
@@ -49,8 +49,16 @@
appearance = look
scale_to(1,scaling, FALSE)
turn_to(angle_override, FALSE)
- if(!isnull(new_loc)) //If you want to null it just delete it...
+ if(!isnull(new_loc)) //If you want to null it just delete it...
forceMove(new_loc)
for(var/i in 1 to increment)
pixel_x += round((sin(angle_override)+16*sin(angle_override)*2), 1)
pixel_y += round((cos(angle_override)+16*cos(angle_override)*2), 1)
+
+/obj/effect/projectile_lighting
+ var/owner
+
+/obj/effect/projectile_lighting/Initialize(mapload, color, range, intensity, owner_key)
+ . = ..()
+ set_light(range, intensity, color)
+ owner = owner_key
diff --git a/code/game/objects/effects/projectile/projectile_tracer.dm b/code/game/objects/effects/projectile/projectile_tracer.dm
index 54f18d62fd2..e722f186b15 100644
--- a/code/game/objects/effects/projectile/projectile_tracer.dm
+++ b/code/game/objects/effects/projectile/projectile_tracer.dm
@@ -1,12 +1,22 @@
-/proc/generate_tracer_between_points(datum/point/starting, datum/point/ending, beam_type, color, qdel_in = 5) //Do not pass z-crossing points as that will not be properly (and likely will never be properly until it's absolutely needed) supported!
+/proc/generate_tracer_between_points(datum/point/starting, datum/point/ending, beam_type, color, qdel_in = 5, light_range = 2, light_color_override, light_intensity = 1, instance_key) //Do not pass z-crossing points as that will not be properly (and likely will never be properly until it's absolutely needed) supported!
if(!istype(starting) || !istype(ending) || !ispath(beam_type))
return
- if(starting.z != ending.z)
- crash_with("Projectile tracer generation of cross-Z beam detected. This feature is not supported!") //Do it anyways though.
var/datum/point/midpoint = point_midpoint_points(starting, ending)
var/obj/effect/projectile/tracer/PB = new beam_type
+ if(isnull(light_color_override))
+ light_color_override = color
PB.apply_vars(angle_between_points(starting, ending), midpoint.return_px(), midpoint.return_py(), color, pixel_length_between_points(starting, ending) / world.icon_size, midpoint.return_turf(), 0)
. = PB
+ if(light_range > 0 && light_intensity > 0)
+ var/list/turf/line = get_line(starting.return_turf(), ending.return_turf())
+ tracing_line:
+ for(var/i in line)
+ var/turf/T = i
+ for(var/obj/effect/projectile_lighting/PL in T)
+ if(PL.owner == instance_key)
+ continue tracing_line
+ QDEL_IN(new /obj/effect/projectile_lighting(T, light_color_override, light_range, light_intensity, instance_key), qdel_in > 0? qdel_in : 5)
+ line = null
if(qdel_in)
QDEL_IN(PB, qdel_in)
diff --git a/code/game/objects/effects/spiders.dm b/code/game/objects/effects/spiders.dm
index ffb00f983c6..46349b798b4 100644
--- a/code/game/objects/effects/spiders.dm
+++ b/code/game/objects/effects/spiders.dm
@@ -37,9 +37,12 @@
health -= damage
healthcheck()
-/obj/effect/spider/bullet_act(var/obj/projectile/Proj)
- ..()
- health -= Proj.get_structure_damage()
+/obj/effect/spider/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
+ . = ..()
+ if(. != BULLET_ACT_HIT)
+ return .
+
+ health -= hitting_projectile.get_structure_damage()
healthcheck()
/obj/effect/spider/proc/healthcheck()
@@ -69,6 +72,8 @@
/obj/effect/spider/stickyweb/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
if(air_group || height == 0)
return TRUE
+ if(mover?.movement_type & PHASING)
+ return TRUE
if(istype(mover, /mob/living/simple_animal/hostile/giant_spider))
return TRUE
else if(istype(mover, /mob/living))
diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm
index 9234077fb31..eaa03e2f698 100644
--- a/code/game/objects/items.dm
+++ b/code/game/objects/items.dm
@@ -776,12 +776,17 @@ var/list/global/slot_flags_enumeration = list(
/obj/item/proc/ui_action_click()
attack_self(usr)
-//RETURN VALUES
-//handle_shield
-//Return a negative value corresponding to the degree an attack is blocked. PROJECTILE_STOPPED stops the attack entirely, and is the default for projectile and non-projectile attacks
-//Otherwise should return 0 to indicate that the attack is not affected in any way.
+/**
+ * Called when a mob is hit by a projectile, item or by an attack
+ *
+ * Return one of the `BULLET_ACT_*` defines
+ *
+ * BULLET_ACT_HIT will let the attack continue, BULLET_ACT_BLOCK will block the attack
+ */
/obj/item/proc/handle_shield(mob/user, var/on_back, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack")
- return FALSE
+ SHOULD_NOT_SLEEP(TRUE)
+
+ return BULLET_ACT_HIT
/obj/item/proc/can_shield_back()
return
diff --git a/code/game/objects/items/airbubble.dm b/code/game/objects/items/airbubble.dm
index d69daa9e205..48daa63ab2d 100644
--- a/code/game/objects/items/airbubble.dm
+++ b/code/game/objects/items/airbubble.dm
@@ -291,8 +291,11 @@
t_air.merge(inside_air)
// When we shoot bubble, make it rip.
-/obj/structure/closet/airbubble/bullet_act(var/obj/projectile/Proj)
- ..()
+/obj/structure/closet/airbubble/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
+ . = ..()
+ if(. != BULLET_ACT_HIT)
+ return .
+
ripped = TRUE
update_icon()
diff --git a/code/game/objects/items/devices/chameleonproj.dm b/code/game/objects/items/devices/chameleonproj.dm
index a5975f72fec..d495075798e 100644
--- a/code/game/objects/items/devices/chameleonproj.dm
+++ b/code/game/objects/items/devices/chameleonproj.dm
@@ -120,10 +120,13 @@
to_chat(M, SPAN_WARNING("Your chameleon-projector deactivates."))
master.disrupt()
-/obj/effect/dummy/chameleon/bullet_act()
+/obj/effect/dummy/chameleon/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
+ . = ..()
+ if(. != BULLET_ACT_HIT)
+ return .
+
for(var/mob/M in src)
to_chat(M, SPAN_WARNING("Your chameleon-projector deactivates."))
- ..()
master.disrupt()
/obj/effect/dummy/chameleon/relaymove(mob/living/user, direction)
diff --git a/code/game/objects/items/devices/magnetic_lock.dm b/code/game/objects/items/devices/magnetic_lock.dm
index 79e3ff6d44c..123023de5a3 100644
--- a/code/game/objects/items/devices/magnetic_lock.dm
+++ b/code/game/objects/items/devices/magnetic_lock.dm
@@ -107,9 +107,12 @@
else
..()
-/obj/item/device/magnetic_lock/bullet_act(var/obj/projectile/Proj)
- takedamage(Proj.damage)
- ..()
+/obj/item/device/magnetic_lock/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
+ . = ..()
+ if(. != BULLET_ACT_HIT)
+ return .
+
+ takedamage(hitting_projectile.damage)
/obj/item/device/magnetic_lock/attackby(obj/item/attacking_item, mob/user)
if (status == STATUS_BROKEN)
diff --git a/code/game/objects/items/shooting_range.dm b/code/game/objects/items/shooting_range.dm
index 3fd1b43f340..d8fdc5b45c4 100644
--- a/code/game/objects/items/shooting_range.dm
+++ b/code/game/objects/items/shooting_range.dm
@@ -48,15 +48,17 @@
desc = "A shooting target with a threatening silhouette."
hp = 2350 // alium onest too kinda
-/obj/item/target/bullet_act(var/obj/projectile/Proj)
- var/p_x = Proj.p_x + pick(0,0,0,0,0,-1,1) // really ugly way of coding "sometimes offset Proj.p_x!"
- var/p_y = Proj.p_y + pick(0,0,0,0,0,-1,1)
- var/decaltype = (Proj.damage_flags & DAMAGE_FLAG_BULLET) ? DECAL_BULLET : DECAL_SCORCH
+/obj/item/target/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
+ SHOULD_CALL_PARENT(FALSE) //Ancient coders deserve the rope
+
+ var/p_x = hitting_projectile.p_x + pick(0,0,0,0,0,-1,1) // really ugly way of coding "sometimes offset Proj.p_x!"
+ var/p_y = hitting_projectile.p_y + pick(0,0,0,0,0,-1,1)
+ var/decaltype = (hitting_projectile.damage_flags & DAMAGE_FLAG_BULLET) ? DECAL_BULLET : DECAL_SCORCH
virtual_icon = new(icon, icon_state)
if(virtual_icon.GetPixel(p_x, p_y)) // if the located pixel isn't blank (null)
- hp -= Proj.damage
+ hp -= hitting_projectile.damage
if(hp <= 0)
visible_message(SPAN_WARNING("\The [src] breaks into tiny pieces and collapses!"))
qdel(src)
@@ -75,7 +77,7 @@
bmark.pixel_x--
bmark.pixel_y--
- if(Proj.damage >= 20 || istype(Proj, /obj/projectile/beam/practice))
+ if(hitting_projectile.damage >= 20 || istype(hitting_projectile, /obj/projectile/beam/practice))
bmark.icon_state = "scorch"
bmark.set_dir(pick(NORTH,SOUTH,EAST,WEST)) // random scorch design
else
@@ -83,12 +85,12 @@
else // Bullets are hard. They make dents!
bmark.icon_state = "dent"
- if(Proj.damage >= 10 && length(bullet_holes) <= 35) // maximum of 35 bullet holes
+ if(hitting_projectile.damage >= 10 && length(bullet_holes) <= 35) // maximum of 35 bullet holes
if(decaltype == DECAL_BULLET)
- if(prob(Proj.damage+30)) // bullets make holes more commonly!
+ if(prob(hitting_projectile.damage+30)) // bullets make holes more commonly!
new /datum/bullethole(src, bmark.pixel_x, bmark.pixel_y) // create new bullet hole
else // Lasers!
- if(prob(Proj.damage-10)) // lasers make holes less commonly
+ if(prob(hitting_projectile.damage-10)) // lasers make holes less commonly
new /datum/bullethole(src, bmark.pixel_x, bmark.pixel_y) // create new bullet hole
// draw bullet holes
@@ -101,7 +103,7 @@
icon = virtual_icon // apply bullet_holes over decals
return
- return PROJECTILE_CONTINUE // the bullet/projectile goes through the target!
+ return BULLET_ACT_HIT // the bullet/projectile goes through the target!
// Small memory holder entity for transparent bullet holes
diff --git a/code/game/objects/items/toys.dm b/code/game/objects/items/toys.dm
index e489c186470..f5b4a762a0e 100644
--- a/code/game/objects/items/toys.dm
+++ b/code/game/objects/items/toys.dm
@@ -168,7 +168,11 @@
if(prob(50))
qdel(src)
-/obj/item/toy/balloon/bullet_act()
+/obj/item/toy/balloon/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
+ . = ..()
+ if(. != BULLET_ACT_HIT)
+ return .
+
burst()
/obj/item/toy/balloon/fire_act(temperature, volume)
diff --git a/code/game/objects/items/weapons/grenades/fragmentation.dm b/code/game/objects/items/weapons/grenades/fragmentation.dm
index db41c80356b..f8b723e8575 100644
--- a/code/game/objects/items/weapons/grenades/fragmentation.dm
+++ b/code/game/objects/items/weapons/grenades/fragmentation.dm
@@ -12,20 +12,23 @@
P.damage = p_dam
P.pellets = fragments_per_projectile
P.range_step = p_range
- P.shot_from = source
P.range = shard_range
P.name = "shrapnel"
- P.launch_projectile(T)
+ P.preparePixelProjectile(T, get_turf(source))
+ P.firer = source
+ P.fired_from = source
+ P.fire()
if(can_cover)
for(var/mob/living/M in O)
//lying on a frag grenade while the grenade is on the ground causes you to absorb most of the shrapnel.
//you will most likely be dead, but others nearby will be spared the fragments that hit you instead.
if(M.lying && isturf(get_turf(source)))
- P.attack_mob(M, 0, 0)
+ P.process_hit(get_turf(M), M)
else
- P.attack_mob(M, 0, 100) //otherwise, allow a decent amount of fragments to pass
+ if(prob(20))
+ P.process_hit(get_turf(M), M)
//Fragmentation grenade projectile
/obj/projectile/bullet/pellet/fragment
diff --git a/code/game/objects/items/weapons/grenades/stinger.dm b/code/game/objects/items/weapons/grenades/stinger.dm
index ec0210f9958..32dc937c2cd 100644
--- a/code/game/objects/items/weapons/grenades/stinger.dm
+++ b/code/game/objects/items/weapons/grenades/stinger.dm
@@ -38,18 +38,21 @@
P.damage = p_dam
P.balls = fragments_per_projectile
P.range_step = p_range
- P.shot_from = source
P.range = shard_range
P.name = "rubber ball"
- P.launch_projectile(T)
+ P.preparePixelProjectile(T, get_turf(source))
+ P.firer = source
+ P.fired_from = source
+ P.fire()
if(can_cover)
for(var/mob/living/M in O)
if(M.lying && isturf(get_turf(source)))
- P.attack_mob(M, 0, 0)
+ P.process_hit(get_turf(M), M)
else
- P.attack_mob(M, 0, 100)
+ if(prob(20))
+ P.process_hit(get_turf(M), M)
/obj/item/grenade/stinger/prime()
..()
diff --git a/code/game/objects/items/weapons/landmines.dm b/code/game/objects/items/weapons/landmines.dm
index b912fd9f83d..9b9876ddd88 100644
--- a/code/game/objects/items/weapons/landmines.dm
+++ b/code/game/objects/items/weapons/landmines.dm
@@ -176,7 +176,11 @@
else if(attacking_item.force > 10 && deployed)
trigger(user)
-/obj/item/landmine/bullet_act()
+/obj/item/landmine/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
+ . = ..()
+ if(. != BULLET_ACT_HIT)
+ return .
+
if(deployed)
trigger()
diff --git a/code/game/objects/items/weapons/material/swords.dm b/code/game/objects/items/weapons/material/swords.dm
index 387a05b2bc8..fbd09e87a70 100644
--- a/code/game/objects/items/weapons/material/swords.dm
+++ b/code/game/objects/items/weapons/material/swords.dm
@@ -33,8 +33,8 @@
if(default_parry_check(user, attacker, damage_source) && prob(parry_chance * parry_bonus))
user.visible_message(SPAN_DANGER("\The [user] parries [attack_text] with \the [src]!"))
playsound(user.loc, 'sound/weapons/bladeparry.ogg', 50, 1)
- return PROJECTILE_STOPPED
- return FALSE
+ return BULLET_ACT_BLOCK
+ return BULLET_ACT_HIT
/obj/item/material/sword/perform_technique(var/mob/living/carbon/human/target, var/mob/living/carbon/human/user, var/target_zone)
var/armor_reduction = target.get_blocked_ratio(target_zone, DAMAGE_BRUTE, DAMAGE_FLAG_EDGE|DAMAGE_FLAG_SHARP, damage = force)*100
diff --git a/code/game/objects/items/weapons/material/twohanded.dm b/code/game/objects/items/weapons/material/twohanded.dm
index a80a0d3edbf..daa46381aaf 100644
--- a/code/game/objects/items/weapons/material/twohanded.dm
+++ b/code/game/objects/items/weapons/material/twohanded.dm
@@ -97,8 +97,8 @@
if(wielded && default_parry_check(user, attacker, damage_source) && prob(parry_chance))
user.visible_message(SPAN_DANGER("\The [user] parries [attack_text] with \the [src]!"))
playsound(user.loc, /singleton/sound_category/punchmiss_sound, 50, 1)
- return PROJECTILE_STOPPED
- return FALSE
+ return BULLET_ACT_BLOCK
+ return BULLET_ACT_HIT
/obj/item/material/twohanded/update_icon()
icon_state = "[base_icon][wielded]"
diff --git a/code/game/objects/items/weapons/melee/energy.dm b/code/game/objects/items/weapons/melee/energy.dm
index 62cf443993e..98c40d8e224 100644
--- a/code/game/objects/items/weapons/melee/energy.dm
+++ b/code/game/objects/items/weapons/melee/energy.dm
@@ -67,13 +67,13 @@
user.visible_message(SPAN_DANGER("\The [user] parries [attack_text] with \the [src]!"))
spark(src, 5)
playsound(user.loc, 'sound/weapons/blade.ogg', 50, 1)
- return PROJECTILE_STOPPED
+ return BULLET_ACT_BLOCK
else
if(!active)
- return FALSE //turn it on first!
+ return BULLET_ACT_HIT //turn it on first!
if(user.incapacitated())
- return FALSE
+ return BULLET_ACT_HIT
//block as long as they are not directly behind us
var/bad_arc = reverse_direction(user.dir) //arc of directions from which we cannot block
@@ -87,7 +87,7 @@
if(shield_power <= 0)
to_chat(user, SPAN_DANGER("\The [src]'s integrated shield goes out! It will no longer assist in parrying."))
shield_power = 0
- return FALSE
+ return BULLET_ACT_HIT
if(istype(damage_source, /obj/projectile/energy) || istype(damage_source, /obj/projectile/beam))
var/obj/projectile/P = damage_source
@@ -106,10 +106,10 @@
P.firer = user
P.old_style_target(locate(new_x, new_y, P.z))
- return PROJECTILE_CONTINUE // complete projectile permutation
+ return BULLET_ACT_FORCE_PIERCE // complete projectile permutation
else
user.visible_message(SPAN_DANGER("\The [user] blocks [attack_text] with \the [src]!"))
- return PROJECTILE_STOPPED
+ return BULLET_ACT_BLOCK
else if(istype(damage_source, /obj/projectile/bullet) && can_block_bullets)
var/reflectchance = (base_reflectchance) - round(damage/3)
@@ -117,7 +117,7 @@
reflectchance /= 2
if(prob(reflectchance))
user.visible_message(SPAN_DANGER("\The [user] blocks [attack_text] with \the [src]!"))
- return PROJECTILE_STOPPED
+ return BULLET_ACT_BLOCK
/obj/item/melee/energy/get_print_info()
. = ..()
diff --git a/code/game/objects/items/weapons/shields.dm b/code/game/objects/items/weapons/shields.dm
index d88f488661d..467297d33f4 100644
--- a/code/game/objects/items/weapons/shields.dm
+++ b/code/game/objects/items/weapons/shields.dm
@@ -43,12 +43,12 @@
var/shield_dir = on_back ? user.dir : GLOB.reverse_dir[user.dir]
if(user.incapacitated() || !(check_shield_arc(user, shield_dir, damage_source, attacker)))
- return FALSE
+ return BULLET_ACT_HIT
if(prob(get_block_chance(user, damage, damage_source, attacker)))
user.visible_message(SPAN_DANGER("\The [user] blocks [attack_text] with \the [src]!"))
- return PROJECTILE_STOPPED
- return FALSE
+ return BULLET_ACT_BLOCK
+ return BULLET_ACT_HIT
/obj/item/shield/can_shield_back()
return TRUE
@@ -74,7 +74,8 @@
/obj/item/shield/riot/handle_shield(mob/user)
. = ..()
- if(.) playsound(user.loc, 'sound/weapons/Genhit.ogg', 50, 1)
+ if(. != BULLET_ACT_HIT)
+ playsound(user.loc, 'sound/weapons/Genhit.ogg', 50, 1)
/obj/item/shield/riot/get_block_chance(mob/user, var/damage, atom/damage_source = null, mob/attacker = null)
if(istype(damage_source, /obj/projectile))
@@ -113,7 +114,8 @@
/obj/item/shield/buckler/handle_shield(mob/user)
. = ..()
- if(.) playsound(user.loc, 'sound/weapons/Genhit.ogg', 50, 1)
+ if(. != BULLET_ACT_HIT)
+ playsound(user.loc, 'sound/weapons/Genhit.ogg', 50, 1)
/obj/item/shield/buckler/get_block_chance(mob/user, var/damage, atom/damage_source = null, mob/attacker = null)
if(istype(damage_source, /obj/projectile))
@@ -178,9 +180,7 @@
var/shield_dir = on_back ? user.dir : GLOB.reverse_dir[user.dir]
if(!active || user.incapacitated() || !(check_shield_arc(user, shield_dir, damage_source, attacker)))
- return FALSE
- if(.)
- spark(user.loc, 5)
+ return BULLET_ACT_HIT
if(prob(get_block_chance(user, damage, damage_source, attacker)))
spark(user.loc, 5)
@@ -214,13 +214,13 @@
new_angle_s -= 360
P.set_angle(new_angle_s)
- return PROJECTILE_CONTINUE // complete projectile permutation
+ return BULLET_ACT_FORCE_PIERCE // complete projectile permutation
else
user.visible_message(SPAN_DANGER("\The [user] blocks [attack_text] with \the [src]!"))
- return PROJECTILE_STOPPED
+ return BULLET_ACT_BLOCK
else
user.visible_message(SPAN_DANGER("\The [user] blocks [attack_text] with \the [src]!"))
- return PROJECTILE_STOPPED
+ return BULLET_ACT_BLOCK
/obj/item/shield/energy/get_block_chance(mob/user, damage, atom/damage_source = null, mob/attacker = null)
@@ -323,11 +323,11 @@
/obj/item/shield/riot/tact/handle_shield(mob/user)
if(!active)
- return FALSE //turn it on first!
+ return BULLET_ACT_HIT //turn it on first!
. = ..()
- if(.)
- if(.) playsound(user.loc, 'sound/weapons/Genhit.ogg', 50, 1)
+ if(. != BULLET_ACT_HIT)
+ playsound(user.loc, 'sound/weapons/Genhit.ogg', 50, 1)
/obj/item/shield/riot/tact/attack_self(mob/living/user)
active = !active
diff --git a/code/game/objects/items/weapons/traps.dm b/code/game/objects/items/weapons/traps.dm
index ad90cfc2dbb..ce79bd92699 100644
--- a/code/game/objects/items/weapons/traps.dm
+++ b/code/game/objects/items/weapons/traps.dm
@@ -582,10 +582,14 @@
new /obj/item/stack/material/steel(get_turf(src))
qdel(src)
-/obj/item/trap/animal/bullet_act(var/obj/projectile/Proj)
+/obj/item/trap/animal/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
+ . = ..()
+ if(. != BULLET_ACT_HIT)
+ return .
+
var/mob/living/captured_mob = captured ? captured.resolve() : null
if(captured_mob)
- captured_mob.bullet_act(Proj)
+ captured_mob.bullet_act(arglist(args))
/obj/item/trap/animal/proc/release(var/mob/user, var/turf/target)
if(!target)
@@ -846,6 +850,10 @@
/obj/item/trap/animal/large/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
if(deployed)
return TRUE
+
+ if(mover?.movement_type & PHASING)
+ return TRUE
+
return FALSE
/obj/item/large_trap_foundation
diff --git a/code/game/objects/items/weapons/weaponry.dm b/code/game/objects/items/weapons/weaponry.dm
index 2e7559a923f..3abaa590fee 100644
--- a/code/game/objects/items/weapons/weaponry.dm
+++ b/code/game/objects/items/weapons/weaponry.dm
@@ -12,7 +12,7 @@
if(!QDELETED(src))
QDEL_IN(src, 1)
-/obj/item/energy_net/throw_impact(atom/hit_atom)
+/obj/item/energy_net/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
..()
var/mob/living/M = hit_atom
@@ -74,10 +74,13 @@
qdel(src)
return
-/obj/effect/energy_net/bullet_act(var/obj/projectile/Proj)
- health -= Proj.get_structure_damage()
+/obj/effect/energy_net/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
+ . = ..()
+ if(. != BULLET_ACT_HIT)
+ return .
+
+ health -= hitting_projectile.get_structure_damage()
health_check()
- return FALSE
/obj/effect/energy_net/ex_act(var/severity = 2.0)
health = 0
diff --git a/code/game/objects/structures.dm b/code/game/objects/structures.dm
index 8c1635d547f..c2e9750f5ac 100644
--- a/code/game/objects/structures.dm
+++ b/code/game/objects/structures.dm
@@ -78,9 +78,12 @@
dismantle_material.place_sheet(loc)
qdel(src)
-/obj/structure/bullet_act(obj/projectile/P, def_zone)
+/obj/structure/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
. = ..()
- bullet_ping(P)
+ if(. != BULLET_ACT_HIT)
+ return .
+
+ bullet_ping(hitting_projectile)
/obj/structure/proc/climb_on()
diff --git a/code/game/objects/structures/barricades/_barricade.dm b/code/game/objects/structures/barricades/_barricade.dm
index 26d8467acb8..23bd4ae8f60 100644
--- a/code/game/objects/structures/barricades/_barricade.dm
+++ b/code/game/objects/structures/barricades/_barricade.dm
@@ -83,6 +83,8 @@
return prob(max(30,(100.0*health)/maxhealth))
/obj/structure/barricade/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
+ if(mover?.movement_type & PHASING)
+ return TRUE
if(air_group || (height==0))
return TRUE
if(istype(mover, /obj/projectile))
@@ -197,11 +199,14 @@
playsound(src, barricade_hitsound, 25, 1)
hit_barricade(attacking_item, user)
-/obj/structure/barricade/bullet_act(obj/projectile/P)
- bullet_ping(P)
- var/damage_to_take = P.damage * P.anti_materiel_potential
+/obj/structure/barricade/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
+ . = ..()
+ if(. != BULLET_ACT_HIT)
+ return .
+
+ bullet_ping(hitting_projectile)
+ var/damage_to_take = hitting_projectile.damage * hitting_projectile.anti_materiel_potential
take_damage(damage_to_take)
- return TRUE
/obj/structure/barricade/proc/barricade_deconstruct(deconstruct)
if(deconstruct && is_wired)
diff --git a/code/game/objects/structures/bonfire.dm b/code/game/objects/structures/bonfire.dm
index 9f6ff4cb6b2..bcee006fac6 100644
--- a/code/game/objects/structures/bonfire.dm
+++ b/code/game/objects/structures/bonfire.dm
@@ -382,6 +382,8 @@ GLOBAL_LIST_EMPTY(total_active_bonfires)
AddOverlays("fireplace_glow")
/obj/structure/bonfire/fireplace/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
+ if(mover?.movement_type & PHASING)
+ return TRUE
if(get_dir(loc, target) == NORTH)
return !density
return TRUE
diff --git a/code/game/objects/structures/crates_lockers/closets.dm b/code/game/objects/structures/crates_lockers/closets.dm
index 7d41673e1ec..e99606ad583 100644
--- a/code/game/objects/structures/crates_lockers/closets.dm
+++ b/code/game/objects/structures/crates_lockers/closets.dm
@@ -150,7 +150,8 @@
return content_size
/obj/structure/closet/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
- if(air_group || (height==0 || wall_mounted)) return 1
+ if(air_group || (height==0 || wall_mounted))
+ return TRUE
return ..()
/obj/structure/closet/proc/can_open()
@@ -316,17 +317,19 @@
new /obj/item/stack/material/steel(get_turf(src))
qdel(src)
-/obj/structure/closet/bullet_act(var/obj/projectile/Proj)
- var/proj_damage = Proj.get_structure_damage()
+/obj/structure/closet/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
+ . = ..()
+ if(. != BULLET_ACT_HIT)
+ return .
+
+ var/proj_damage = hitting_projectile.get_structure_damage()
if(!proj_damage)
- return
+ return BULLET_ACT_BLOCK
- if(Proj.penetrating || istype(Proj, /obj/projectile/bullet))
- var/distance = get_dist(Proj.starting, get_turf(loc))
+ if(hitting_projectile.penetrating || istype(hitting_projectile, /obj/projectile/bullet))
for(var/mob/living/L in contents)
- Proj.attack_mob(L, distance)
+ hitting_projectile.Impact(L)
- ..()
damage(proj_damage)
/obj/structure/closet/attackby(obj/item/attacking_item, mob/user)
diff --git a/code/game/objects/structures/crates_lockers/closets/statue.dm b/code/game/objects/structures/crates_lockers/closets/statue.dm
index 44b001e2e06..522bb528e65 100644
--- a/code/game/objects/structures/crates_lockers/closets/statue.dm
+++ b/code/game/objects/structures/crates_lockers/closets/statue.dm
@@ -124,11 +124,13 @@
for(var/mob/M in src)
shatter(M)
-/obj/structure/closet/statue/bullet_act(var/obj/projectile/Proj)
- health -= Proj.get_structure_damage()
- check_health()
+/obj/structure/closet/statue/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
+ . = ..()
+ if(. != BULLET_ACT_HIT)
+ return .
- return
+ health -= hitting_projectile.get_structure_damage()
+ check_health()
/obj/structure/closet/statue/attack_generic(var/mob/user, damage, attacktext, environment_smash)
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
diff --git a/code/game/objects/structures/crates_lockers/crates.dm b/code/game/objects/structures/crates_lockers/crates.dm
index 2196540394b..0e96ebd9249 100644
--- a/code/game/objects/structures/crates_lockers/crates.dm
+++ b/code/game/objects/structures/crates_lockers/crates.dm
@@ -97,6 +97,8 @@
==========================
*/
/obj/structure/closet/crate/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
+ if(mover?.movement_type & PHASING)
+ return TRUE
if (istype(mover, /obj/structure/closet/crate))//Handle interaction with other crates
var/obj/structure/closet/crate/C = mover
if (tablestatus && tablestatus != C.tablestatus) // Crates can go under tables with crates on top of them, and vice versa
diff --git a/code/game/objects/structures/curtains.dm b/code/game/objects/structures/curtains.dm
index 52a6ae0ff56..900d53fd2b1 100644
--- a/code/game/objects/structures/curtains.dm
+++ b/code/game/objects/structures/curtains.dm
@@ -20,12 +20,14 @@
layer = ABOVE_HUMAN_LAYER
opacity = 0
-/obj/structure/curtain/bullet_act(obj/projectile/P, def_zone)
- if(!P.nodamage)
- visible_message(SPAN_WARNING("[P] tears [src] down!"))
+/obj/structure/curtain/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
+ . = ..()
+ if(. != BULLET_ACT_HIT)
+ return .
+
+ if(hitting_projectile.damage > 0)
+ visible_message(SPAN_WARNING("[hitting_projectile] tears [src] down!"))
qdel(src)
- else
- ..(P, def_zone)
/obj/structure/curtain/attack_hand(mob/user)
playsound(get_turf(loc), 'sound/effects/curtain.ogg', 15, 1, -5)
diff --git a/code/game/objects/structures/displaycase.dm b/code/game/objects/structures/displaycase.dm
index 7724d5ae6a7..8bbcce167ee 100644
--- a/code/game/objects/structures/displaycase.dm
+++ b/code/game/objects/structures/displaycase.dm
@@ -34,9 +34,12 @@
health_check()
-/obj/structure/displaycase/bullet_act(var/obj/projectile/Proj)
- health -= Proj.get_structure_damage()
- ..()
+/obj/structure/displaycase/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
+ . = ..()
+ if(. != BULLET_ACT_HIT)
+ return .
+
+ health -= hitting_projectile.get_structure_damage()
health_check()
/obj/structure/displaycase/proc/health_check()
diff --git a/code/game/objects/structures/full_window_frame.dm b/code/game/objects/structures/full_window_frame.dm
index d6286c00b23..21193017351 100644
--- a/code/game/objects/structures/full_window_frame.dm
+++ b/code/game/objects/structures/full_window_frame.dm
@@ -48,6 +48,8 @@
/obj/structure/window_frame/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
if(air_group || (height == 0))
return TRUE
+ if(mover?.movement_type & PHASING)
+ return TRUE
if(istype(mover, /obj/structure/closet/crate))
return TRUE
if(istype(mover) && mover.pass_flags & PASSTABLE)
diff --git a/code/game/objects/structures/girders.dm b/code/game/objects/structures/girders.dm
index c2aff0b6c71..a5ba268838b 100644
--- a/code/game/objects/structures/girders.dm
+++ b/code/game/objects/structures/girders.dm
@@ -41,16 +41,16 @@
health = 50
cover = 25
-/obj/structure/girder/bullet_act(var/obj/projectile/Proj)
+/obj/structure/girder/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
//Girders only provide partial cover. There's a chance that the projectiles will just pass through. (unless you are trying to shoot the girder)
- if(Proj.original != src && !prob(cover))
- return PROJECTILE_CONTINUE //pass through
+ if(hitting_projectile.original != src && !prob(cover))
+ return BULLET_ACT_HIT //pass through
- var/damage = Proj.get_structure_damage()
+ var/damage = hitting_projectile.get_structure_damage()
if(!damage)
- return
+ return BULLET_ACT_BLOCK
- if(!istype(Proj, /obj/projectile/beam))
+ if(!istype(hitting_projectile, /obj/projectile/beam))
damage *= 0.4 //non beams do reduced damage
take_damage(damage)
@@ -376,6 +376,8 @@
/obj/structure/girder/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
if (!mover)
return 1
+ if(mover?.movement_type & PHASING)
+ return TRUE
if(istype(mover,/obj/projectile) && density)
if (prob(50))
return 1
diff --git a/code/game/objects/structures/gore/core.dm b/code/game/objects/structures/gore/core.dm
index 022bf68d04f..fb3d3c8d994 100644
--- a/code/game/objects/structures/gore/core.dm
+++ b/code/game/objects/structures/gore/core.dm
@@ -31,10 +31,13 @@
visible_message(SPAN_WARNING(final_message))
qdel(src)
-/obj/structure/gore/bullet_act(var/obj/projectile/Proj)
- health -= Proj.damage
+/obj/structure/gore/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
+ . = ..()
+ if(. != BULLET_ACT_HIT)
+ return .
+
+ health -= hitting_projectile.damage
healthcheck()
- return ..()
/obj/structure/gore/ex_act(severity)
switch(severity)
@@ -78,6 +81,8 @@
/obj/structure/gore/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
if(air_group)
return FALSE
+ if(mover?.movement_type & PHASING)
+ return TRUE
if(istype(mover) && mover.pass_flags & PASSGLASS)
return !opacity
return !density
diff --git a/code/game/objects/structures/grille.dm b/code/game/objects/structures/grille.dm
index f100888f669..2edea7ce4d7 100644
--- a/code/game/objects/structures/grille.dm
+++ b/code/game/objects/structures/grille.dm
@@ -103,45 +103,54 @@
attack_generic(user,damage_dealt,attack_message)
/obj/structure/grille/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
- if(air_group || (height==0)) return 1
+ if(air_group || (height==0))
+ return TRUE
+ if(mover?.movement_type & PHASING)
+ return TRUE
if(istype(mover) && mover.pass_flags & PASSGRILLE)
- return 1
+ return TRUE
else
if(istype(mover, /obj/projectile))
return prob(30)
else
return !density
-/obj/structure/grille/bullet_act(var/obj/projectile/Proj)
- if(!Proj) return
+/obj/structure/grille/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
+ . = ..()
+ if(. != BULLET_ACT_HIT)
+ return .
+
+ if(!hitting_projectile)
+ return BULLET_ACT_BLOCK
//Flimsy grilles aren't so great at stopping projectiles. However they can absorb some of the impact
- var/damage = Proj.get_structure_damage()
+ var/damage = hitting_projectile.get_structure_damage()
var/passthrough = 0
- if(!damage) return
+ if(!damage)
+ return BULLET_ACT_BLOCK
//20% chance that the grille provides a bit more cover than usual. Support structure for example might take up 20% of the grille's area.
//If they click on the grille itself then we assume they are aiming at the grille itself and the extra cover behaviour is always used.
- switch(Proj.damage_type)
+ switch(hitting_projectile.damage_type)
if(DAMAGE_BRUTE)
//bullets
- if(Proj.original == src || prob(20))
- Proj.damage *= between(0, Proj.damage/60, 0.5)
+ if(hitting_projectile.original == src || prob(20))
+ hitting_projectile.damage *= between(0, hitting_projectile.damage/60, 0.5)
if(prob(max((damage-10)/25, 0))*100)
passthrough = 1
else
- Proj.damage *= between(0, Proj.damage/60, 1)
+ hitting_projectile.damage *= between(0, hitting_projectile.damage/60, 1)
passthrough = 1
if(DAMAGE_BURN)
//beams and other projectiles are either blocked completely by grilles or stop half the damage.
- if(!(Proj.original == src || prob(20)))
- Proj.damage *= 0.5
+ if(!(hitting_projectile.original == src || prob(20)))
+ hitting_projectile.damage *= 0.5
passthrough = 1
if(passthrough)
- . = PROJECTILE_CONTINUE
- damage = between(0, (damage - Proj.damage)*(Proj.damage_type == DAMAGE_BRUTE? 0.4 : 1), 10) //if the bullet passes through then the grille avoids most of the damage
+ . = BULLET_ACT_HIT
+ damage = between(0, (damage - hitting_projectile.damage)*(hitting_projectile.damage_type == DAMAGE_BRUTE? 0.4 : 1), 10) //if the bullet passes through then the grille avoids most of the damage
src.health -= damage*0.2
spawn(0) healthcheck() //spawn to make sure we return properly if the grille is deleted
@@ -313,7 +322,7 @@
/obj/structure/grille/cult/CanPass(atom/movable/mover, turf/target, height = 1.5, air_group = 0)
if(air_group)
return 0 //Make sure air doesn't drain
- ..()
+ . = ..()
/obj/structure/grille/crescent/attack_hand()
return
@@ -329,6 +338,3 @@
/obj/structure/grille/crescent/hitby(atom/movable/hitting_atom, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum)
return
-
-/obj/structure/grille/crescent/bullet_act()
- return
diff --git a/code/game/objects/structures/hadii_statue.dm b/code/game/objects/structures/hadii_statue.dm
index 00968d1b2ac..188da667c7f 100644
--- a/code/game/objects/structures/hadii_statue.dm
+++ b/code/game/objects/structures/hadii_statue.dm
@@ -63,13 +63,18 @@
if(health <= 0)
topple()
-/obj/structure/hadii_statue/bullet_act(var/obj/projectile/Proj)
+/obj/structure/hadii_statue/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
if(toppled)
- return
- if(!Proj)
- return
- if(!Proj.damage)
- visible_message(SPAN_WARNING("\The [Proj] bounces off \the [src]!"))
- return
- visible_message(SPAN_WARNING("\The [Proj] hits \the [src]!"))
- do_integrity_check(Proj.damage)
+ return BULLET_ACT_BLOCK
+ if(!hitting_projectile)
+ return BULLET_ACT_BLOCK
+ if(!hitting_projectile.damage)
+ visible_message(SPAN_WARNING("\The [hitting_projectile] bounces off \the [src]!"))
+ return BULLET_ACT_BLOCK
+
+ . = ..()
+ if(. != BULLET_ACT_HIT)
+ return .
+
+ visible_message(SPAN_WARNING("\The [hitting_projectile] hits \the [src]!"))
+ do_integrity_check(hitting_projectile.damage)
diff --git a/code/game/objects/structures/inflatable.dm b/code/game/objects/structures/inflatable.dm
index bd5a2877998..f2321413ae3 100644
--- a/code/game/objects/structures/inflatable.dm
+++ b/code/game/objects/structures/inflatable.dm
@@ -60,21 +60,28 @@
return ..()
/obj/structure/inflatable/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
+ if(mover?.movement_type & PHASING)
+ return TRUE
+
if(isprojectile(mover))
visible_message(SPAN_DANGER("\The [src] rapidly deflates!"))
deflate(TRUE)
return TRUE
return FALSE
-/obj/structure/inflatable/bullet_act(var/obj/projectile/Proj)
- var/proj_damage = Proj.get_structure_damage()
+/obj/structure/inflatable/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
+ var/proj_damage = hitting_projectile.get_structure_damage()
if(!proj_damage)
return
- bullet_ping(Proj)
+ . = ..()
+ if(. != BULLET_ACT_HIT)
+ return .
+
+ bullet_ping(hitting_projectile)
health -= proj_damage
- ..()
+
if(health <= 0)
deflate(TRUE)
return
@@ -203,6 +210,8 @@
/obj/structure/inflatable/door/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
if(air_group)
return state
+ if(mover?.movement_type & PHASING)
+ return TRUE
if(istype(mover, /obj/effect/beam))
return !opacity
if(isprojectile(mover))
diff --git a/code/game/objects/structures/janicart.dm b/code/game/objects/structures/janicart.dm
index 9800d4b09a4..c759a41d19c 100644
--- a/code/game/objects/structures/janicart.dm
+++ b/code/game/objects/structures/janicart.dm
@@ -423,11 +423,14 @@
return
/obj/structure/janitorialcart/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
- if(air_group || (height==0)) return 1
+ if(air_group || (height==0))
+ return TRUE
+ if(mover?.movement_type & PHASING)
+ return TRUE
if(istype(mover) && mover.pass_flags & PASSTABLE)
- return 1
+ return TRUE
if(istype(mover, /mob/living) && mover == pulling)
- return 1
+ return TRUE
else
if(istype(mover, /obj/projectile))
return prob(30)
diff --git a/code/game/objects/structures/kitchen_spike.dm b/code/game/objects/structures/kitchen_spike.dm
index f00afcb565e..4e74cb264a7 100644
--- a/code/game/objects/structures/kitchen_spike.dm
+++ b/code/game/objects/structures/kitchen_spike.dm
@@ -67,6 +67,9 @@
if (!mover)
return 1
+ if(mover.movement_type & PHASING)
+ return TRUE
+
if(istype(mover,/obj/projectile) && density)
if (!occupied && prob(80))
//Wiry frame, usually wont be cover
diff --git a/code/game/objects/structures/mirror.dm b/code/game/objects/structures/mirror.dm
index 4bc3b70957c..ef8331ef4ed 100644
--- a/code/game/objects/structures/mirror.dm
+++ b/code/game/objects/structures/mirror.dm
@@ -57,13 +57,16 @@
reflection.update_mirror_filters()
-/obj/structure/mirror/bullet_act(var/obj/projectile/Proj)
- if(prob(Proj.get_structure_damage() * 2))
+/obj/structure/mirror/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
+ . = ..()
+ if(. != BULLET_ACT_HIT)
+ return .
+
+ if(prob(hitting_projectile.get_structure_damage() * 2))
if(!shattered)
shatter()
else
playsound(src, 'sound/effects/hit_on_shattered_glass.ogg', 70, 1)
- ..()
/obj/structure/mirror/attackby(obj/item/attacking_item, mob/user)
if(shattered)
diff --git a/code/game/objects/structures/plasticflaps.dm b/code/game/objects/structures/plasticflaps.dm
index 9b2e0f25369..3e74ae96d10 100644
--- a/code/game/objects/structures/plasticflaps.dm
+++ b/code/game/objects/structures/plasticflaps.dm
@@ -56,27 +56,30 @@
return AIR_BLOCKED
return FALSE
-/obj/structure/plasticflaps/CanPass(atom/A, turf/T)
- if(istype(A) && A.pass_flags & PASSGLASS)
+/obj/structure/plasticflaps/CanPass(atom/movable/mover, turf/target)
+ if(istype(mover) && mover.pass_flags & PASSGLASS)
return prob(60)
- var/obj/structure/bed/B = A
- if (istype(A, /obj/structure/bed) && B.buckled)//if it's a bed/chair and someone is buckled, it will not pass
+ if(mover?.movement_type & PHASING)
+ return TRUE
+
+ var/obj/structure/bed/B = mover
+ if (istype(mover, /obj/structure/bed) && B.buckled)//if it's a bed/chair and someone is buckled, it will not pass
return 0
- if(istype(A, /obj/vehicle)) //no vehicles
+ if(istype(mover, /obj/vehicle)) //no vehicles
return 0
//Bots can always pass
- if(isbot(A))
+ if(isbot(mover))
return TRUE
- var/mob/living/M = A
+ var/mob/living/M = mover
if(istype(M))
if(M.lying)
return ..()
for(var/mob_type in mobs_can_pass)
- if(istype(A, mob_type))
+ if(istype(mover, mob_type))
return ..()
return issmall(M)
diff --git a/code/game/objects/structures/railing.dm b/code/game/objects/structures/railing.dm
index 821cf90b71a..cc1bc0c3b2f 100644
--- a/code/game/objects/structures/railing.dm
+++ b/code/game/objects/structures/railing.dm
@@ -8,7 +8,7 @@
climbable = TRUE
layer = OBJ_LAYER
anchored = FALSE
- pass_flags_self = LETPASSTHROW|PASSSTRUCTURE
+ pass_flags_self = LETPASSTHROW|PASSSTRUCTURE|PASSRAILING
atom_flags = ATOM_FLAG_CHECKS_BORDER
obj_flags = OBJ_FLAG_ROTATABLE|OBJ_FLAG_MOVES_UNSUPPORTED
@@ -88,6 +88,8 @@
. += FONT_SMALL(SPAN_NOTICE("\The [src] is [anchored ? "" : "not"] screwed to the floor."))
/obj/structure/railing/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
+ if(mover?.movement_type & PHASING)
+ return TRUE
if(istype(mover,/obj/projectile))
return TRUE
if(!istype(mover) || mover.pass_flags & PASSRAILING)
diff --git a/code/game/objects/structures/simple_doors.dm b/code/game/objects/structures/simple_doors.dm
index b3cc7be472f..d6632fe1cc3 100644
--- a/code/game/objects/structures/simple_doors.dm
+++ b/code/game/objects/structures/simple_doors.dm
@@ -86,7 +86,10 @@
return
/obj/structure/simple_door/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
- if(air_group) return 0
+ if(air_group)
+ return FALSE
+ if(mover?.movement_type & PHASING)
+ return TRUE
if(istype(mover, /obj/effect/beam))
return !opacity
return !density
@@ -193,9 +196,13 @@
attack_hand(user)
return
-/obj/structure/simple_door/bullet_act(var/obj/projectile/Proj)
- health -= Proj.damage
- bullet_ping(Proj)
+/obj/structure/simple_door/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
+ . = ..()
+ if(. != BULLET_ACT_HIT)
+ return .
+
+ health -= hitting_projectile.damage
+ bullet_ping(hitting_projectile)
CheckHealth()
/obj/structure/simple_door/proc/CheckHealth()
diff --git a/code/game/objects/structures/stool_bed_chair_nest/bed.dm b/code/game/objects/structures/stool_bed_chair_nest/bed.dm
index 15023cb9923..635bc2a4bc6 100644
--- a/code/game/objects/structures/stool_bed_chair_nest/bed.dm
+++ b/code/game/objects/structures/stool_bed_chair_nest/bed.dm
@@ -127,9 +127,6 @@
if(buckled)
buckled.forceMove(dest)
-/obj/structure/bed/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
- return ..()
-
/obj/structure/bed/ex_act(severity)
switch(severity)
if(1.0)
diff --git a/code/game/objects/structures/urban.dm b/code/game/objects/structures/urban.dm
index c6d5bd7df18..4bad6b287bd 100644
--- a/code/game/objects/structures/urban.dm
+++ b/code/game/objects/structures/urban.dm
@@ -261,6 +261,8 @@ ABSTRACT_TYPE(/obj/structure/stairs/urban/road_ramp)
anchored = TRUE
/obj/structure/rod_railing/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
+ if(mover?.movement_type & PHASING)
+ return TRUE
if(istype(mover,/obj/projectile))
return TRUE
if(!istype(mover) || mover.pass_flags & PASSRAILING)
@@ -313,6 +315,8 @@ ABSTRACT_TYPE(/obj/structure/stairs/urban/road_ramp)
icon_state = "guard_top_end"
/obj/structure/road_barrier/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
+ if(mover?.movement_type & PHASING)
+ return TRUE
if(istype(mover,/obj/projectile))
return TRUE
if(!istype(mover) || mover.pass_flags & PASSRAILING)
@@ -343,6 +347,8 @@ ABSTRACT_TYPE(/obj/structure/stairs/urban/road_ramp)
can_be_unanchored = FALSE
/obj/structure/chainlink_fence/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)//So bullets will fly over and stuff.
+ if(mover?.movement_type & PHASING)
+ return TRUE
if(air_group || (height==0))
return TRUE
if(istype(mover, /obj/projectile))
@@ -378,6 +384,8 @@ ABSTRACT_TYPE(/obj/structure/stairs/urban/road_ramp)
can_be_unanchored = FALSE
/obj/structure/rope_railing/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
+ if(mover?.movement_type & PHASING)
+ return TRUE
if(istype(mover,/obj/projectile))
return TRUE
if(!istype(mover) || mover.pass_flags & PASSRAILING)
diff --git a/code/game/objects/structures/windoor_assembly.dm b/code/game/objects/structures/windoor_assembly.dm
index 53e0a1e077a..7b27c92fe79 100644
--- a/code/game/objects/structures/windoor_assembly.dm
+++ b/code/game/objects/structures/windoor_assembly.dm
@@ -49,6 +49,8 @@
icon_state = "[facing]_[secure]windoor_assembly[state]"
/obj/structure/windoor_assembly/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
+ if(mover?.movement_type & PHASING)
+ return TRUE
if(istype(mover) && mover.pass_flags & PASSGLASS)
return 1
if(get_dir(loc, target) == dir) //Make sure looking at appropriate border
diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm
index 4f2b2751550..8e22458567a 100644
--- a/code/game/objects/structures/window.dm
+++ b/code/game/objects/structures/window.dm
@@ -138,12 +138,15 @@
qdel(src)
return
-/obj/structure/window/bullet_act(var/obj/projectile/Proj)
- var/proj_damage = Proj.get_structure_damage()
+/obj/structure/window/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
+ var/proj_damage = hitting_projectile.get_structure_damage()
if(!proj_damage)
- return
+ return BULLET_ACT_BLOCK
+
+ . = ..()
+ if(. != BULLET_ACT_HIT)
+ return .
- ..()
take_damage(proj_damage)
return
@@ -167,6 +170,8 @@
return (dir == SOUTHWEST || dir == SOUTHEAST || dir == NORTHWEST || dir == NORTHEAST)
/obj/structure/window/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
+ if(mover?.movement_type & PHASING)
+ return TRUE
if(istype(mover) && mover.pass_flags & PASSGLASS)
return 1
if(is_full_window())
diff --git a/code/game/turfs/flooring/urban.dm b/code/game/turfs/flooring/urban.dm
index 2fa5d06ade5..a49593468a4 100644
--- a/code/game/turfs/flooring/urban.dm
+++ b/code/game/turfs/flooring/urban.dm
@@ -94,6 +94,8 @@
layer = ABOVE_HUMAN_LAYER
/obj/structure/ledge/roof/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
+ if(mover?.movement_type & PHASING)
+ return TRUE
if(istype(mover,/obj/projectile))
return TRUE
if(!istype(mover) || mover.pass_flags & PASSRAILING)
diff --git a/code/game/turfs/simulated/walls.dm b/code/game/turfs/simulated/walls.dm
index cfb0c2f2f31..00e5f604a27 100644
--- a/code/game/turfs/simulated/walls.dm
+++ b/code/game/turfs/simulated/walls.dm
@@ -84,24 +84,26 @@
return TRUE
return ..()
-/turf/simulated/wall/bullet_act(var/obj/projectile/Proj)
- if(istype(Proj,/obj/projectile/beam))
+/turf/simulated/wall/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
+ . = ..()
+ if(. != BULLET_ACT_HIT)
+ return .
+
+ if(istype(hitting_projectile,/obj/projectile/beam))
burn(2500)
- else if(istype(Proj,/obj/projectile/ion))
+ else if(istype(hitting_projectile,/obj/projectile/ion))
burn(500)
- bullet_ping(Proj)
- create_bullethole(Proj)
+ bullet_ping(hitting_projectile)
+ create_bullethole(hitting_projectile)
- var/proj_damage = Proj.get_structure_damage()
+ var/proj_damage = hitting_projectile.get_structure_damage()
var/damage = proj_damage
//cap the amount of damage, so that things like emitters can't destroy walls in one hit.
- if(Proj.anti_materiel_potential > 1)
+ if(hitting_projectile.anti_materiel_potential > 1)
damage = min(proj_damage, 100)
- Proj.on_hit(src)
-
take_damage(damage)
/turf/simulated/wall/hitby(atom/movable/hitting_atom, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum)
diff --git a/code/modules/blob/blob.dm b/code/modules/blob/blob.dm
index 28608af768b..a032a0f011e 100644
--- a/code/modules/blob/blob.dm
+++ b/code/modules/blob/blob.dm
@@ -39,7 +39,9 @@
update_icon()
START_PROCESSING(SSprocessing, src)
-/obj/effect/blob/CanPass(var/atom/movable/mover, var/turf/target, var/height = 0, var/air_group = 0)
+/obj/effect/blob/CanPass(atom/movable/mover, turf/target, height, air_group)
+ if(mover?.movement_type & PHASING)
+ return TRUE
if(air_group || height == 0)
return TRUE
return FALSE
@@ -190,16 +192,19 @@
attack_living(victim)
attack_time = world.time
-/obj/effect/blob/bullet_act(var/obj/projectile/Proj)
- if(!Proj)
+/obj/effect/blob/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
+ . = ..()
+ if(. != BULLET_ACT_HIT)
+ return .
+
+ if(!hitting_projectile)
return
- switch(Proj.damage_type)
+ switch(hitting_projectile.damage_type)
if(DAMAGE_BRUTE)
- take_damage(Proj.damage / brute_resist)
+ take_damage(hitting_projectile.damage / brute_resist)
if(DAMAGE_BURN)
- take_damage((Proj.damage / laser_resist) / fire_resist)
- return FALSE
+ take_damage((hitting_projectile.damage / laser_resist) / fire_resist)
/obj/effect/blob/attackby(obj/item/attacking_item, mob/user)
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
@@ -355,6 +360,8 @@
icon_state = "blob_damaged"
/obj/effect/blob/shield/CanPass(var/atom/movable/mover, var/turf/target, var/height = 0, var/air_group = 0)
+ if(mover?.movement_type & PHASING)
+ return TRUE
return !density
/obj/effect/blob/ravaging
diff --git a/code/modules/clothing/clothing.dm b/code/modules/clothing/clothing.dm
index 3207a079826..60d8dd3ad72 100644
--- a/code/modules/clothing/clothing.dm
+++ b/code/modules/clothing/clothing.dm
@@ -336,7 +336,9 @@
P.firer = user
P.old_style_target(locate(new_x, new_y, P.z))
- return PROJECTILE_CONTINUE // complete projectile permutation
+ return BULLET_ACT_FORCE_PIERCE // complete projectile permutation
+
+ return BULLET_ACT_HIT
/proc/calculate_material_armor(amount)
var/result = 1 - MATERIAL_ARMOR_COEFFICENT * amount / (1 + MATERIAL_ARMOR_COEFFICENT * abs(amount))
diff --git a/code/modules/clothing/gloves/miscellaneous.dm b/code/modules/clothing/gloves/miscellaneous.dm
index 03e5c2312bf..81d78aca66c 100644
--- a/code/modules/clothing/gloves/miscellaneous.dm
+++ b/code/modules/clothing/gloves/miscellaneous.dm
@@ -405,7 +405,13 @@
var/turf/T = get_turf(user)
user.visible_message(SPAN_DANGER("\The [user] crackles with energy!"))
var/obj/projectile/beam/tesla/LE = new (T)
- LE.launch_projectile(A, user.zone_sel? user.zone_sel.selecting : null, user)
+
+ LE.preparePixelProjectile(A, user)
+ LE.firer = user
+ LE.fired_from = src
+ LE.def_zone = user.zone_sel? user.zone_sel.selecting : null
+ LE.fire()
+
spark(src, 3, GLOB.alldirs)
playsound(user.loc, 'sound/magic/LightningShock.ogg', 75, 1)
charged = FALSE
diff --git a/code/modules/clothing/suits/armor.dm b/code/modules/clothing/suits/armor.dm
index 7978048b50e..1b7416a29ff 100644
--- a/code/modules/clothing/suits/armor.dm
+++ b/code/modules/clothing/suits/armor.dm
@@ -168,8 +168,8 @@
playsound(user.loc, /singleton/sound_category/spark_sound, 50, 1)
user.forceMove(picked)
- return PROJECTILE_FORCE_MISS
- return FALSE
+ return BULLET_ACT_BLOCK
+ return BULLET_ACT_HIT
/obj/item/clothing/suit/armor/reactive/attack_self(mob/user as mob)
src.active = !( src.active )
diff --git a/code/modules/custom_ka/projectiles.dm b/code/modules/custom_ka/projectiles.dm
index e354d5f844e..cd1e3bb7786 100644
--- a/code/modules/custom_ka/projectiles.dm
+++ b/code/modules/custom_ka/projectiles.dm
@@ -18,8 +18,10 @@
/obj/projectile/kinetic/mech/burst
damage = 25
-/obj/projectile/kinetic/on_impact(var/atom/A)
- var/turf/target_turf = get_turf(A)
+/obj/projectile/kinetic/on_hit(atom/target, blocked, def_zone)
+ . = ..()
+
+ var/turf/target_turf = get_turf(target)
if(!target_turf)
target_turf = get_turf(src)
if(istype(target_turf))
diff --git a/code/modules/heavy_vehicle/equipment/combat.dm b/code/modules/heavy_vehicle/equipment/combat.dm
index 4cd94c0907c..c35e3e9b954 100644
--- a/code/modules/heavy_vehicle/equipment/combat.dm
+++ b/code/modules/heavy_vehicle/equipment/combat.dm
@@ -409,17 +409,20 @@
else
icon_state = "shield_null"
-/obj/aura/mechshield/bullet_act(obj/projectile/P, var/def_zone)
+/obj/aura/mechshield/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
if(!active)
return
+
+ . = ..()
+
if(shields?.charge)
- P.damage = shields.stop_damage(P.damage)
+ hitting_projectile.damage = shields.stop_damage(hitting_projectile.damage)
user.visible_message(SPAN_WARNING("\The [shields.owner]'s shields flash and crackle."))
flick("shield_impact", src)
playsound(user, 'sound/effects/basscannon.ogg', 35, TRUE)
//light up the night.
new /obj/effect/effect/smoke/illumination(get_turf(src), 5, 4, 1, "#ffffff")
- if(P.damage <= 0)
+ if(hitting_projectile.damage <= 0)
return AURA_FALSE|AURA_CANCEL
spark(get_turf(src), 5, GLOB.alldirs)
diff --git a/code/modules/holodeck/HolodeckObjects.dm b/code/modules/holodeck/HolodeckObjects.dm
index 1bf25d9cd3f..7daa8417e8a 100644
--- a/code/modules/holodeck/HolodeckObjects.dm
+++ b/code/modules/holodeck/HolodeckObjects.dm
@@ -284,8 +284,8 @@
spark(user.loc, 5)
playsound(user.loc, 'sound/weapons/blade.ogg', 50, 1)
- return PROJECTILE_STOPPED
- return FALSE
+ return BULLET_ACT_BLOCK
+ return BULLET_ACT_HIT
/obj/item/holo/esword/New()
if(!item_color)
@@ -363,7 +363,7 @@
visible_message(SPAN_WARNING("\The [I] bounces off of \the [src]'s rim!"), range = 3)
return 0
else
- return ..(mover, target, height, air_group)
+ return ..()
/obj/machinery/readybutton
diff --git a/code/modules/hydroponics/trays/tray.dm b/code/modules/hydroponics/trays/tray.dm
index 73041a72691..618494228be 100644
--- a/code/modules/hydroponics/trays/tray.dm
+++ b/code/modules/hydroponics/trays/tray.dm
@@ -208,29 +208,33 @@
connect()
update_icon()
-/obj/machinery/portable_atmospherics/hydroponics/bullet_act(var/obj/projectile/Proj)
+/obj/machinery/portable_atmospherics/hydroponics/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
//Don't act on seeds like dionaea that shouldn't change.
if(seed && seed.get_trait(TRAIT_IMMUTABLE) > 0)
- return
+ return BULLET_ACT_HIT
//Override for somatoray projectiles.
- if(istype(Proj ,/obj/projectile/energy/floramut)&& prob(20))
- if(istype(Proj, /obj/projectile/energy/floramut/gene))
- var/obj/projectile/energy/floramut/gene/G = Proj
+ if(istype(hitting_projectile ,/obj/projectile/energy/floramut)&& prob(20))
+ if(istype(hitting_projectile, /obj/projectile/energy/floramut/gene))
+ var/obj/projectile/energy/floramut/gene/G = hitting_projectile
if(seed)
seed = seed.diverge_mutate_gene(G.gene, get_turf(loc)) //get_turf just in case it's not in a turf.
else
mutate(1)
- return
- else if(istype(Proj ,/obj/projectile/energy/florayield) && prob(20))
+ return BULLET_ACT_HIT
+ else if(istype(hitting_projectile ,/obj/projectile/energy/florayield) && prob(20))
yield_mod = min(10,yield_mod+rand(1,2))
- return
+ return BULLET_ACT_HIT
- ..()
+ . = ..()
/obj/machinery/portable_atmospherics/hydroponics/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
- if(air_group || (height==0)) return TRUE
+ if(air_group || (height==0))
+ return TRUE
+
+ if(mover?.movement_type & PHASING)
+ return TRUE
if(istype(mover) && mover.pass_flags & PASSTABLE)
return TRUE
diff --git a/code/modules/mining/mine_turfs.dm b/code/modules/mining/mine_turfs.dm
index e5c25656cd1..f95bd3e9667 100644
--- a/code/modules/mining/mine_turfs.dm
+++ b/code/modules/mining/mine_turfs.dm
@@ -129,21 +129,37 @@ var/list/mineral_can_smooth_with = list(
GetDrilled()
SSicon_smooth.add_to_queue_neighbors(src)
-/turf/simulated/mineral/bullet_act(var/obj/projectile/Proj)
- if(istype(Proj, /obj/projectile/beam/plasmacutter))
- var/obj/projectile/beam/plasmacutter/PC_beam = Proj
+/turf/simulated/mineral/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
+ SHOULD_CALL_PARENT(FALSE) //Fucking snowflake stack of procs
+
+ var/sigreturn = SEND_SIGNAL(src, COMSIG_ATOM_PRE_BULLET_ACT, hitting_projectile, def_zone)
+ if(sigreturn & COMPONENT_BULLET_PIERCED)
+ return BULLET_ACT_FORCE_PIERCE
+ if(sigreturn & COMPONENT_BULLET_BLOCKED)
+ return BULLET_ACT_BLOCK
+ if(sigreturn & COMPONENT_BULLET_ACTED)
+ return BULLET_ACT_HIT
+
+ SEND_SIGNAL(src, COMSIG_ATOM_BULLET_ACT, hitting_projectile, def_zone)
+ if(QDELETED(hitting_projectile)) // Signal deleted it?
+ return BULLET_ACT_BLOCK
+
+ if(istype(hitting_projectile, /obj/projectile/beam/plasmacutter))
+ var/obj/projectile/beam/plasmacutter/PC_beam = hitting_projectile
var/list/cutter_results = PC_beam.pass_check(src)
. = cutter_results[1]
if(cutter_results[2]) // the cutter mined the turf, just pass on
- return
+ return BULLET_ACT_HIT
// Emitter blasts
- if(istype(Proj, /obj/projectile/beam/emitter))
+ if(istype(hitting_projectile, /obj/projectile/beam/emitter))
emitter_blasts_taken++
if(emitter_blasts_taken >= 3)
GetDrilled()
+ hitting_projectile.on_hit(src, 0, def_zone)
+
/turf/simulated/mineral/CollidedWith(atom/bumped_atom)
. = ..()
if(istype(bumped_atom, /mob/living/carbon/human))
diff --git a/code/modules/mob/animations.dm b/code/modules/mob/animations.dm
index ac7d13aac2b..2a66bc58a38 100644
--- a/code/modules/mob/animations.dm
+++ b/code/modules/mob/animations.dm
@@ -184,6 +184,8 @@ note dizziness decrements automatically in the mob's Life() proc.
// either attack_item OR attack_image should be used. if both are used, attack_image will be the one chosen
/mob/do_attack_animation(atom/A, var/atom/attack_item, var/image/attack_image)
+ set waitfor = FALSE
+
var/initial_pixel_x = get_standard_pixel_x()
var/initial_pixel_y = get_standard_pixel_y()
..(A, attack_item, attack_image, initial_pixel_x, initial_pixel_y)
diff --git a/code/modules/mob/living/carbon/carbon_defense.dm b/code/modules/mob/living/carbon/carbon_defense.dm
index dea0e561392..51b7bc52a83 100644
--- a/code/modules/mob/living/carbon/carbon_defense.dm
+++ b/code/modules/mob/living/carbon/carbon_defense.dm
@@ -28,28 +28,48 @@
sleeping = 0
willfully_sleeping = FALSE
-/mob/living/carbon/bullet_act(var/obj/projectile/P, var/def_zone)
- ..(P, def_zone)
- var/show_ssd
+/mob/living/carbon/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
+ . = ..()
+ if(. != BULLET_ACT_HIT)
+ return .
+
+ //Carbon have blood, so when hit with sufficient force, they bleed; this shows the effect of bleeding when hit by a projectile
+ if(hitting_projectile.damage_type == DAMAGE_BRUTE && hitting_projectile.damage > 5) //weak hits shouldn't make you gush blood
+ var/splatter_color = COLOR_HUMAN_BLOOD
+ if (src.species && src.get_blood_color())
+ splatter_color = src.get_blood_color()
+
+ var/splatter_dir = hitting_projectile.starting ? get_dir(hitting_projectile.starting, get_turf(src)) : dir
+ new /obj/effect/temp_visual/dir_setting/bloodsplatter(get_turf(src), splatter_dir, splatter_color)
+
var/mob/living/carbon/human/H
if(ishuman(src))
H = src
- show_ssd = H.species.show_ssd
- if(H && show_ssd && !client && !teleop)
- if(H.bg)
- visible_message(SPAN_DANGER("[P] hit [src] waking [get_pronoun("him")] up!"))
- if(H.health / H.maxHealth < 0.5)
- H.bg.awaken_impl(TRUE)
- sleeping = 0
- willfully_sleeping = FALSE
- else
- to_chat(H, SPAN_DANGER("You sense great disturbance to your physical body!"))
- else if(!vr_mob)
- visible_message(SPAN_DANGER("[P] hit [src], but they do not respond... Maybe they have S.S.D?"))
- else if(client && willfully_sleeping)
- visible_message(SPAN_DANGER("[P] hit [src] waking [get_pronoun("him")] up!"))
- sleeping = 0
- willfully_sleeping = FALSE
+
+ if(hitting_projectile.damage > 0)
+ if(H && H.species.show_ssd && !client && !teleop)
+ if(H.bg)
+ if(!hitting_projectile.do_not_log)
+ visible_message(SPAN_DANGER("[hitting_projectile] hit [src] waking [get_pronoun("him")] up!"))
+
+ if(H.health / H.maxHealth < 0.5)
+ H.bg.awaken_impl(TRUE)
+ sleeping = 0
+ willfully_sleeping = FALSE
+ else
+ if(!hitting_projectile.do_not_log)
+ to_chat(H, SPAN_DANGER("You sense great disturbance to your physical body!"))
+
+ else if(!vr_mob)
+ if(!hitting_projectile.do_not_log)
+ visible_message(SPAN_DANGER("[hitting_projectile] hit [src], but they do not respond... Maybe they have S.S.D?"))
+
+ else if(client && willfully_sleeping)
+ if(!hitting_projectile.do_not_log)
+ visible_message(SPAN_DANGER("[hitting_projectile] hit [src] waking [get_pronoun("him")] up!"))
+
+ sleeping = 0
+ willfully_sleeping = FALSE
/mob/living/carbon/standard_weapon_hit_effects(obj/item/I, mob/living/user, var/effective_force, var/hit_zone)
var/show_ssd
diff --git a/code/modules/mob/living/carbon/human/exercise.dm b/code/modules/mob/living/carbon/human/exercise.dm
index 9484524f70e..b47fcfe77f4 100644
--- a/code/modules/mob/living/carbon/human/exercise.dm
+++ b/code/modules/mob/living/carbon/human/exercise.dm
@@ -72,7 +72,7 @@ Exercise Verbs
var/list/extremities = list(BP_L_HAND, BP_R_HAND, BP_L_FOOT, BP_R_FOOT, BP_L_ARM, BP_R_ARM, BP_L_LEG, BP_R_LEG)
for(var/zone in extremities)
- if(!get_organ(zone, TRUE))
+ if(!get_organ(zone))
to_chat(src, SPAN_WARNING("You can't do pushups with missing limbs."))
return FALSE
diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm
index ea8f1c3c598..2667692bc12 100644
--- a/code/modules/mob/living/carbon/human/human.dm
+++ b/code/modules/mob/living/carbon/human/human.dm
@@ -2105,14 +2105,14 @@
return BULLET_IMPACT_METAL
return BULLET_IMPACT_MEAT
-/mob/living/carbon/human/bullet_impact_visuals(var/obj/projectile/P, var/def_zone, var/damage, var/blocked_ratio)
- ..()
- if(blocked_ratio > 0.7)
+/mob/living/carbon/human/bullet_impact_visuals(obj/projectile/impacting_projectile, def_zone, damage, blocked)
+ . = ..()
+ if(blocked > 70)
return
switch(get_bullet_impact_effect_type(def_zone))
if(BULLET_IMPACT_MEAT)
- if(P.damage_type == DAMAGE_BRUTE)
- var/hit_dir = get_dir(P.starting, src)
+ if(impacting_projectile.damage_type == DAMAGE_BRUTE)
+ var/hit_dir = get_dir(impacting_projectile.starting, src)
var/obj/effect/decal/cleanable/blood/B = blood_splatter(get_step(src, hit_dir), src, 1, hit_dir)
B.icon_state = pick("dir_splatter_1","dir_splatter_2")
var/scale = min(1, round(damage / 50, 0.2))
diff --git a/code/modules/mob/living/carbon/human/human_attackhand.dm b/code/modules/mob/living/carbon/human/human_attackhand.dm
index 3108c6f56b4..82b674b6ef6 100644
--- a/code/modules/mob/living/carbon/human/human_attackhand.dm
+++ b/code/modules/mob/living/carbon/human/human_attackhand.dm
@@ -36,7 +36,7 @@
// Should this all be in Touch()?
if(istype(H))
- if(H != src && check_shields(0, null, H, H.zone_sel.selecting, H.name))
+ if(H != src && (check_shields(0, null, H, H.zone_sel.selecting, H.name) != BULLET_ACT_HIT))
H.do_attack_animation(src)
return 0
@@ -549,7 +549,7 @@
user.attack_log += "\[[time_stamp()]\] attacked [src.name] ([src.ckey])"
src.attack_log += "\[[time_stamp()]\] was attacked by [user.name] ([user.ckey])"
user.do_attack_animation(src)
- if(damage < 15 && check_shields(damage, null, user, null, "\the [user]"))
+ if(damage < 15 && (check_shields(damage, null, user, null, "\the [user]") == BULLET_ACT_HIT))
return
visible_message(SPAN_DANGER("[user] has [attack_message] [src]!"))
diff --git a/code/modules/mob/living/carbon/human/human_damage.dm b/code/modules/mob/living/carbon/human/human_damage.dm
index 787697ed1b1..998613a65ef 100644
--- a/code/modules/mob/living/carbon/human/human_damage.dm
+++ b/code/modules/mob/living/carbon/human/human_damage.dm
@@ -408,18 +408,33 @@ This function restores all organs.
return 0
return
-
-/mob/living/carbon/human/proc/get_organ(var/zone, var/allow_no_result = FALSE)
+/**
+ * Get an organ depending on the zone
+ *
+ * * zone - One of the `BP_*` defines
+ *
+ * Returns an `/obj/item/organ` corresponding to the `zone`, or null if the organ doesn't exist
+ */
+/mob/living/carbon/human/proc/get_organ(zone)
SHOULD_NOT_SLEEP(TRUE)
+ SHOULD_BE_PURE(TRUE)
RETURN_TYPE(/obj/item/organ)
+
+ //This allows us to maintain the proc purity, though it kinda looks stupid
+ var/zone_to_get
+
if(!zone)
- if(allow_no_result)
- return
- else
- zone = BP_CHEST
- if (zone in list(BP_EYES, BP_MOUTH))
- zone = BP_HEAD
- return organs_by_name[zone]
+ stack_trace("WARNING: get_organ() called with no zone! Defaulting to BP_CHEST, but it won't be supported anymore in the future!")
+ zone_to_get = BP_CHEST
+
+ if(zone in list(BP_EYES, BP_MOUTH))
+ zone_to_get = BP_HEAD
+
+ if(!zone_to_get)
+ zone_to_get = zone
+
+ if(zone_to_get in organs_by_name)
+ return organs_by_name[zone_to_get]
/mob/living/carbon/human/apply_damage(damage = 0, damagetype = DAMAGE_BRUTE, def_zone, blocked, used_weapon, damage_flags = 0, armor_pen, silent = FALSE)
if (invisibility == INVISIBILITY_LEVEL_TWO && back && (istype(back, /obj/item/rig)))
@@ -427,7 +442,7 @@ This function restores all organs.
to_chat(src, SPAN_DANGER("You are now visible."))
set_invisibility(0)
- var/obj/item/organ/external/organ = isorgan(def_zone) ? def_zone : get_organ(def_zone, TRUE)
+ var/obj/item/organ/external/organ = isorgan(def_zone) ? def_zone : (isnull(def_zone) ? get_organ(BP_CHEST) : get_organ(def_zone))
if(!organ)
if(!def_zone)
if(damage_flags & DAMAGE_FLAG_DISPERSED)
diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm
index 32d3301c656..6e530294768 100644
--- a/code/modules/mob/living/carbon/human/human_defense.dm
+++ b/code/modules/mob/living/carbon/human/human_defense.dm
@@ -8,48 +8,38 @@ emp_act
*/
-/mob/living/carbon/human/bullet_act(var/obj/projectile/P, var/def_zone)
- var/species_check = src.species.bullet_act(P, def_zone, src)
-
+/mob/living/carbon/human/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
+ var/species_check = src.species.bullet_act(hitting_projectile, def_zone, src)
if(species_check)
return species_check
if(!is_physically_disabled())
var/deflection_chance = check_martial_deflection_chance()
if(prob(deflection_chance))
- visible_message(SPAN_WARNING("\The [src] deftly dodges \the [P]!"), SPAN_NOTICE("You deftly dodge \the [P]!"))
+ visible_message(SPAN_WARNING("\The [src] deftly dodges \the [hitting_projectile]!"), SPAN_NOTICE("You deftly dodge \the [hitting_projectile]!"))
playsound(src, /singleton/sound_category/bulletflyby_sound, 75, TRUE)
- return PROJECTILE_DODGED
+ return BULLET_ACT_FORCE_PIERCE
def_zone = check_zone(def_zone)
if(!has_organ(def_zone))
- return PROJECTILE_FORCE_MISS //if they don't have the organ in question then the projectile just passes by.
+ return BULLET_ACT_FORCE_PIERCE //if they don't have the organ in question then the projectile just passes by.
- //Shields
- var/shield_check = check_shields(P.damage, P, null, def_zone, "the [P.name]")
- if(shield_check)
- if(shield_check < 0)
- return shield_check
- else
- P.on_hit(src, 100, def_zone)
- return 100
+ . = ..()
+ if(. != BULLET_ACT_HIT)
+ return .
var/obj/item/organ/external/organ = get_organ(def_zone)
// Tell clothing we're wearing that it got hit by a bullet/laser/etc
var/list/clothing = get_clothing_list_organ(organ)
for(var/obj/item/clothing/C in clothing)
- C.clothing_impact(P, P.damage)
+ C.clothing_impact(hitting_projectile, hitting_projectile.damage)
//Shrapnel
- if(!(species.flags & NO_EMBED) && P.can_embed())
- var/armor = get_blocked_ratio(def_zone, DAMAGE_BRUTE, P.damage_flags(), armor_pen = P.armor_penetration, damage = P.damage)*100
- if(prob(20 + max(P.damage + P.embed_chance - armor, -10)))
- P.do_embed(organ)
-
- var/blocked = ..(P, def_zone)
-
- return blocked
+ if(!(species.flags & NO_EMBED) && hitting_projectile.can_embed())
+ var/armor = get_blocked_ratio(def_zone, DAMAGE_BRUTE, hitting_projectile.damage_flags(), armor_pen = hitting_projectile.armor_penetration, damage = hitting_projectile.damage)*100
+ if(prob(20 + max(hitting_projectile.damage + hitting_projectile.embed_chance - armor, -10)))
+ hitting_projectile.do_embed(organ)
/mob/living/carbon/human/stun_effect_act(var/stun_amount, var/agony_amount, var/def_zone, var/used_weapon, var/damage_flags)
var/obj/item/organ/external/affected = get_organ(check_zone(def_zone))
@@ -161,7 +151,7 @@ emp_act
return gear
return null
-/mob/living/carbon/human/proc/check_shields(var/damage = 0, var/atom/damage_source = null, var/mob/attacker = null, var/def_zone = null, var/attack_text = "the attack")
+/mob/living/carbon/human/check_shields(damage, atom/damage_source, mob/attacker, def_zone, attack_text = "the attack")
for(var/obj/item/shield in list(l_hand, r_hand, wear_suit, back))
if(!shield)
continue
@@ -170,10 +160,9 @@ emp_act
if(!shield.can_shield_back())
continue
is_on_back = TRUE
- . = shield.handle_shield(src, is_on_back, damage, damage_source, attacker, def_zone, attack_text)
- if(.)
- return
- return FALSE
+ return shield.handle_shield(src, is_on_back, damage, damage_source, attacker, def_zone, attack_text)
+
+ return BULLET_ACT_HIT
/mob/living/carbon/human/emp_act(severity)
. = ..()
@@ -209,7 +198,7 @@ emp_act
visible_message(SPAN_DANGER("[user] misses [src] with \the [I]!"))
return
- if(check_shields(I.force, I, user, target_zone, "the [I.name]"))
+ if(check_shields(I.force, I, user, target_zone, "the [I.name]") != BULLET_ACT_HIT)
return
var/obj/item/organ/external/affecting = get_organ(hit_zone)
@@ -334,7 +323,7 @@ emp_act
if(isobj(hitting_atom))
var/obj/O = hitting_atom
- if(in_throw_mode && !get_active_hand() && throwingdatum.speed <= THROWFORCE_SPEED_DIVISOR) //empty active hand and we're in throw mode
+ if(in_throw_mode && !get_active_hand() && throwingdatum?.speed <= THROWFORCE_SPEED_DIVISOR) //empty active hand and we're in throw mode
if(canmove && !restrained())
if(isturf(O.loc))
put_in_active_hand(O)
@@ -343,7 +332,9 @@ emp_act
return
var/dtype = O.damtype
- var/throw_damage = O.throwforce*(throwingdatum.speed/THROWFORCE_SPEED_DIVISOR)
+ var/throw_damage = O.throwforce
+ if(throwingdatum)
+ throw_damage *= (throwingdatum.speed/THROWFORCE_SPEED_DIVISOR)
var/zone
if (istype(O.throwing?.thrower?.resolve(), /mob/living))
@@ -361,10 +352,8 @@ emp_act
if(zone && O.throwing?.thrower?.resolve() != src)
var/shield_check = check_shields(throw_damage, O, throwing?.thrower?.resolve(), zone, "[O]")
- if(shield_check == PROJECTILE_FORCE_MISS)
+ if(shield_check != BULLET_ACT_HIT)
zone = null
- else if(shield_check)
- return
if(!zone)
visible_message(SPAN_NOTICE("\The [O] misses [src] narrowly!"))
@@ -409,7 +398,10 @@ emp_act
if(isitem(O))
var/obj/item/I = O
mass = I.w_class/THROWNOBJ_KNOCKBACK_DIVISOR
- var/momentum = throwingdatum.speed*mass
+
+ var/momentum = 0
+ if(throwingdatum)
+ momentum = throwingdatum.speed*mass
if(O.throwing?.thrower?.resolve() && momentum >= THROWNOBJ_KNOCKBACK_SPEED)
var/dir = get_dir(O.throwing?.thrower?.resolve(), src)
diff --git a/code/modules/mob/living/carbon/human/species/station/golem.dm b/code/modules/mob/living/carbon/human/species/station/golem.dm
index 9b6c2e032a9..bc7795b8025 100644
--- a/code/modules/mob/living/carbon/human/species/station/golem.dm
+++ b/code/modules/mob/living/carbon/human/species/station/golem.dm
@@ -408,7 +408,7 @@ var/global/list/golem_types = list(
P.firer = H
P.old_style_target(locate(new_x, new_y, P.z))
- return -1 // complete projectile permutation
+ return BULLET_ACT_FORCE_PIERCE // complete projectile permutation
/datum/species/golem/glass/handle_death(var/mob/living/carbon/human/H)
for(var/i in 1 to 5)
@@ -614,7 +614,7 @@ var/global/list/golem_types = list(
P.firer = H
P.old_style_target(locate(new_x, new_y, P.z))
- return -1 // complete projectile permutation
+ return BULLET_ACT_FORCE_PIERCE // complete projectile permutation
/datum/species/golem/marble
name = SPECIES_GOLEM_MARBLE
diff --git a/code/modules/mob/living/carbon/human/unarmed_attack.dm b/code/modules/mob/living/carbon/human/unarmed_attack.dm
index 04288aec5d8..b0a45293353 100644
--- a/code/modules/mob/living/carbon/human/unarmed_attack.dm
+++ b/code/modules/mob/living/carbon/human/unarmed_attack.dm
@@ -104,7 +104,7 @@ GLOBAL_LIST_EMPTY(sparring_attack_cache)
target.visible_message(SPAN_WARNING("[target] looks like [target.get_pronoun("he")] [target.get_pronoun("is")] in pain!"),
SPAN_WARNING("[(target.gender=="female") ? "Oh god that hurt!" : "Oh no, that REALLY hurt!"]"))
- target.apply_effects(stutter = attack_damage * 2, agony = attack_damage* 3, blocked = armor)
+ target.apply_effects(stutter = attack_damage * 2, agony = attack_damage* 3, blocked = (armor * 100))
if(BP_L_LEG, BP_L_FOOT, BP_R_LEG, BP_R_FOOT)
if(!target.lying)
if(pain_message)
diff --git a/code/modules/mob/living/carbon/slime/slime.dm b/code/modules/mob/living/carbon/slime/slime.dm
index d0a11e93e04..49f2b8bad6f 100644
--- a/code/modules/mob/living/carbon/slime/slime.dm
+++ b/code/modules/mob/living/carbon/slime/slime.dm
@@ -246,10 +246,9 @@
..(-abs(amount)) // Heals them
return
-/mob/living/carbon/slime/bullet_act(var/obj/projectile/Proj)
+/mob/living/carbon/slime/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
attacked += 10
- ..(Proj)
- return FALSE
+ . = ..()
/mob/living/carbon/slime/emp_act(severity)
. = ..()
diff --git a/code/modules/mob/living/damage_procs.dm b/code/modules/mob/living/damage_procs.dm
index cd84fde438c..4d17e7aa807 100644
--- a/code/modules/mob/living/damage_procs.dm
+++ b/code/modules/mob/living/damage_procs.dm
@@ -9,6 +9,8 @@
*/
/mob/living/proc/apply_damage(damage = 0, damagetype = DAMAGE_BRUTE, def_zone, blocked, used_weapon, damage_flags = 0, armor_pen, silent = FALSE)
+ SHOULD_NOT_SLEEP(TRUE)
+
if(!damage)
return FALSE
@@ -54,7 +56,7 @@
*
* * effect - The amount of effect to apply, a number
* * effect_type - An effect eg. `STUN`, `WEAKEN`; see `code\__DEFINES\damage_organs.dm` for relative defines
- * * blocked - The amount of effect that was blocked, eg. by armor, a number, percentage
+ * * blocked - The **percentage** of effect that was blocked, eg. by armor, a number
*
* Returns `TRUE` if the effect was applied, `FALSE` otherwise
*/
@@ -111,18 +113,44 @@
return TRUE
+/**
+ * Convenience proc to apply effects to a mob
+ *
+ * * blocked - The **percentage** of effects that was blocked, eg. by armor, a number
+ *
+ * Returns `TRUE` if the effects were not completely blocked, `FALSE` otherwise
+ */
+/mob/living/proc/apply_effects(stun = 0, weaken = 0, paralyze = 0, irradiate = 0, stutter = 0, eyeblur = 0, drowsy = 0, agony = 0, incinerate = 0, blocked = 0)
+ SHOULD_NOT_SLEEP(TRUE)
-/mob/living/proc/apply_effects(var/stun = 0, var/weaken = 0, var/paralyze = 0, var/irradiate = 0, var/stutter = 0, var/eyeblur = 0, var/drowsy = 0, var/agony = 0, var/incinerate = 0, var/blocked = 0)
- if(blocked >= 2) return 0
- if(stun) apply_effect(stun, STUN, blocked)
- if(weaken) apply_effect(weaken, WEAKEN, blocked)
- if(paralyze) apply_effect(paralyze, PARALYZE, blocked)
- if(stutter) apply_effect(stutter, STUTTER, blocked)
- if(eyeblur) apply_effect(eyeblur, EYE_BLUR, blocked)
- if(drowsy) apply_effect(drowsy, DROWSY, blocked)
- if(agony) apply_effect(agony, DAMAGE_PAIN, blocked)
- if(incinerate) apply_effect(incinerate, INCINERATE, blocked)
- return 1
+ if(blocked >= 100)
+ return FALSE
+
+ if(stun)
+ apply_effect(stun, STUN, blocked)
+
+ if(weaken)
+ apply_effect(weaken, WEAKEN, blocked)
+
+ if(paralyze)
+ apply_effect(paralyze, PARALYZE, blocked)
+
+ if(stutter)
+ apply_effect(stutter, STUTTER, blocked)
+
+ if(eyeblur)
+ apply_effect(eyeblur, EYE_BLUR, blocked)
+
+ if(drowsy)
+ apply_effect(drowsy, DROWSY, blocked)
+
+ if(agony)
+ apply_effect(agony, DAMAGE_PAIN, blocked)
+
+ if(incinerate)
+ apply_effect(incinerate, INCINERATE, blocked)
+
+ return TRUE
// overridden by human
/mob/living/proc/apply_radiation(var/rads)
diff --git a/code/modules/mob/living/life.dm b/code/modules/mob/living/life.dm
index acf31f96b72..67b61d7902e 100644
--- a/code/modules/mob/living/life.dm
+++ b/code/modules/mob/living/life.dm
@@ -21,7 +21,8 @@
handle_status_effects()
if(stat != DEAD)
- aura_check(AURA_TYPE_LIFE)
+ if(LAZYLEN(auras))
+ aura_check(AURA_TYPE_LIFE)
if(!InStasis())
//Mutations and radiation
handle_mutations_and_radiation()
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index 2333f3e6094..359540c2123 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -1067,3 +1067,7 @@ default behaviour is:
/mob/living/get_speech_bubble_state_modifier()
return isSynthetic() ? "robot" : ..()
+
+///Performs the aftereffects of blocking a projectile.
+/mob/living/proc/block_projectile_effects()
+ return
diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm
index e9e637a9204..eed2a0b90fe 100644
--- a/code/modules/mob/living/living_defense.dm
+++ b/code/modules/mob/living/living_defense.dm
@@ -5,6 +5,12 @@
var/datum/component/armor/armor_datum = armor
. = armor_datum.apply_damage_modifications(arglist(.))
+/mob/living/check_projectile_armor(def_zone, obj/projectile/impacting_projectile, is_silent)
+ if(impacting_projectile.damage > 0) // Run the block computation if we did damage or if we only use armor for effects (nodamage)
+ //Since we get a ratio from this and we need a percentage, we multiply by 100 to get the percentage
+ return (get_blocked_ratio(def_zone, impacting_projectile.damage_type, impacting_projectile.damage_flags(), impacting_projectile.armor_penetration, impacting_projectile.damage) * 100)
+ return 0
+
/mob/living/proc/get_blocked_ratio(def_zone, damage_type, damage_flags, armor_pen, damage)
var/list/armors = get_armors_by_zone(def_zone, damage_type, damage_flags)
. = 0
@@ -23,36 +29,113 @@
if(armor)
. += armor
-/mob/living/bullet_act(var/obj/projectile/P, var/def_zone, var/used_weapon = null)
+/**
+ * Determines if the mob has a shield, and what to do with it
+ *
+ * Returns one of the `BULLET_ACT_*` defines
+ *
+ * `BULLET_ACT_HIT` if the shield didn't block the hit,
+ * `BULLET_ACT_BLOCK` if the shield blocked the hit,
+ * `BULLET_ACT_FORCE_PIERCE` if the shield avoided the mob from being hit, but whatever hit it wasn't stopped (eg. a bullet that doesn't hit the mob, but gets deflected)
+ */
+/mob/living/proc/check_shields(damage, atom/damage_source, mob/attacker, def_zone, attack_text)
+ return BULLET_ACT_HIT
- //Being hit while using a cloaking device
- var/obj/item/cloaking_device/C = locate(/obj/item/cloaking_device) in src
- if(C && C.active)
- C.attack_self(src)//Should shut it off
- update_icon()
- to_chat(src, SPAN_NOTICE("Your [C.name] was disrupted!"))
- Stun(2)
+/mob/living/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
+ . = ..()
+ if(. != BULLET_ACT_HIT)
+ return .
- //Being hit while using a deadman switch
- if(istype(get_active_hand(),/obj/item/device/assembly/signaler))
- var/obj/item/device/assembly/signaler/signaler = get_active_hand()
- if(signaler.deadman && prob(80))
- log_and_message_admins("has triggered a signaler deadman's switch")
- src.visible_message(SPAN_WARNING("[src] triggers their deadman's switch!"))
- signaler.signal()
+ /*#############################################
+ THINGS THAT CAN AVOID THE MOB BEING HIT
+ OR ALTER THE DAMAGE WE TAKE
+ #############################################*/
- //Armor
- var/damage = P.damage
- var/flags = P.damage_flags()
+ var/blocked
+
+ //Auras, essentially magic and encompassing around us, gets checked first
+ if(!src.aura_check(AURA_TYPE_BULLET, hitting_projectile, def_zone, piercing_hit))
+ blocked = 100
+ return BULLET_ACT_FORCE_PIERCE
+
+
+ //Shields, in front of us (hopefully), check the bullet before it reaches us
+ var/shield_check = check_shields(hitting_projectile.damage, hitting_projectile, hitting_projectile.firer, def_zone, "the [hitting_projectile.name]")
+ if(shield_check != BULLET_ACT_HIT)
+ hitting_projectile.on_hit(src, 100, def_zone)
+ return shield_check
+
+
+ //Armor, right above our skin, can soften the hit
+
+ // we need a second, silent armor check to actually know how much to reduce damage taken, as opposed to
+ // on [/atom/proc/bullet_act] where it's just to pass it to the projectile's on_hit().
+ blocked = check_projectile_armor(def_zone, hitting_projectile, is_silent = TRUE)
+
+ var/damage = hitting_projectile.damage //We use a supporting variable to store the damage, since we can alter it below
+ //If it's anti material vulnerable, apply the anti-material potential factor
if(is_anti_materiel_vulnerable())
- damage = P.damage * P.anti_materiel_potential
- var/damaged
- if(!P.nodamage)
- damaged = apply_damage(damage, P.damage_type, def_zone, damage_flags = P.damage_flags(), used_weapon = P, armor_pen = P.armor_penetration)
- if(damaged || P.nodamage) // Run the block computation if we did damage or if we only use armor for effects (nodamage)
- . = get_blocked_ratio(def_zone, P.damage_type, flags, P.armor_penetration, P.damage)
- bullet_impact_visuals(P, def_zone, damage, .)
- P.on_hit(src, ., def_zone)
+ damage = hitting_projectile.damage * hitting_projectile.anti_materiel_potential
+
+ /*##################################
+ OK WE ARE BEING HIT NOW IF
+ WE HAVE REACHED THIS POINT
+ ##################################*/
+
+ //If the projectile has damage and it's not completely blocked, apply it
+ if(damage > 0 && blocked < 100)
+ //Apply the damage to the living mob
+ apply_damage(damage, hitting_projectile.damage_type, def_zone, blocked, used_weapon = hitting_projectile, damage_flags = hitting_projectile.damage_flags(), used_weapon = hitting_projectile, armor_pen = hitting_projectile.armor_penetration)
+
+ /*### START Other effects of being hit and damaged ###*/
+
+ //Being hit while using a cloaking device
+ var/obj/item/cloaking_device/C = locate(/obj/item/cloaking_device) in src
+ if(C && C.active)
+ C.attack_self(src)//Should shut it off
+ update_icon()
+ to_chat(src, SPAN_NOTICE("Your [C.name] was disrupted!"))
+ Stun(2)
+
+ //Being hit while using a deadman switch
+ var/obj/item/device/assembly/signaler/signaler = get_active_hand()
+ if(istype(signaler))
+ if(signaler.deadman && prob(80))
+ log_and_message_admins("has triggered a signaler deadman's switch")
+ src.visible_message(SPAN_WARNING("[src] triggers their deadman's switch!"))
+ signaler.signal()
+
+ /*### END Other effects of being hit and damaged ###*/
+
+ bullet_impact_visuals(hitting_projectile, def_zone, damage, blocked)
+
+ //Messages related to being hit (not necessarily damaged)
+ if(!hitting_projectile.do_not_log)
+ var/impacted_organ = get_organ_name_from_zone(def_zone)
+
+ //If the projectile was blocked and it's not at point blank range, then it missed
+ if(blocked >= 100 && !hitting_projectile.point_blank)
+ src.visible_message(SPAN_NOTICE("\The [hitting_projectile] misses [src] narrowly!"))
+ playsound(src, /singleton/sound_category/bulletflyby_sound, 50, 1)
+
+ //Otherwise it hit
+ else
+
+ //If the projectile is suppressed, give a message only to the mob, and a smaller one
+ if(hitting_projectile.suppressed)
+ to_chat(src, SPAN_DANGER("You've been hit in the [impacted_organ] by \a [hitting_projectile]!"))
+
+ //Otherwise announce it as a visible message, and make it larger
+ else
+ //X has fired Y is now given by the guns so you cant tell who shot you if you could not see the shooter
+ src.visible_message(SPAN_DANGER("\The [src] is hit by \a [hitting_projectile] in the [impacted_organ]!"),
+ SPAN_DANGER(FONT_LARGE("You are hit by \a [hitting_projectile] in the [impacted_organ]!")))
+
+
+ if(blocked < 100)
+ return BULLET_ACT_HIT
+ else
+ return BULLET_ACT_BLOCK
/mob/living/proc/get_flash_protection(ignore_inherent = FALSE)
return FLASH_PROTECTION_NONE
@@ -68,8 +151,7 @@
return TRUE
. = TRUE
var/list/newargs = args - args[1]
- for(var/a in auras)
- var/obj/aura/aura = a
+ for(var/obj/aura/aura as anything in auras)
var/result = 0
switch(type)
if(AURA_TYPE_WEAPON)
@@ -85,9 +167,18 @@
if(result & AURA_CANCEL)
break
-//For visuals, blood splatters and so on.
-/mob/living/proc/bullet_impact_visuals(var/obj/projectile/P, var/def_zone, var/damage, var/blocked_ratio)
- var/list/impact_sounds = LAZYACCESS(P.impact_sounds, get_bullet_impact_effect_type(def_zone))
+/**
+ * For visuals, blood splatters and so on
+ *
+ * * impacting_projectile - The `/obj/projectile` that hit the mob
+ * * def_zone - The zone that the projectile hit the mob in, use the defines
+ * * damage - The amount of damage the projectile did
+ * * blocked - The **percentage** of damage that was blocked, eg. by armor, a number
+ */
+/mob/living/proc/bullet_impact_visuals(obj/projectile/impacting_projectile, def_zone, damage, blocked)
+ SHOULD_NOT_SLEEP(TRUE)
+
+ var/list/impact_sounds = LAZYACCESS(impacting_projectile.impact_sounds, get_bullet_impact_effect_type(def_zone))
if(length(impact_sounds))
playsound(src, pick(impact_sounds), 75)
diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm
index 31f7fcede6a..437906427c5 100644
--- a/code/modules/mob/living/silicon/robot/robot.dm
+++ b/code/modules/mob/living/silicon/robot/robot.dm
@@ -567,11 +567,13 @@
/mob/living/silicon/robot/restrained()
return FALSE
-/mob/living/silicon/robot/bullet_act(var/obj/projectile/Proj)
- ..(Proj)
- if(prob(75) && Proj.damage > 0)
+/mob/living/silicon/robot/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
+ . = ..()
+ if(. != BULLET_ACT_HIT)
+ return .
+
+ if(prob(75) && hitting_projectile.damage > 0)
spark_system.queue()
- return 2
/mob/living/silicon/robot/attackby(obj/item/attacking_item, mob/user)
if(istype(attacking_item, /obj/item/handcuffs)) // fuck i don't even know why isrobot() in handcuff code isn't working so this will have to do
diff --git a/code/modules/mob/living/silicon/silicon.dm b/code/modules/mob/living/silicon/silicon.dm
index 49639762e4b..23d34ce9d24 100644
--- a/code/modules/mob/living/silicon/silicon.dm
+++ b/code/modules/mob/living/silicon/silicon.dm
@@ -138,17 +138,19 @@
/mob/living/silicon/IsAdvancedToolUser()
return TRUE
-/mob/living/silicon/bullet_act(obj/projectile/Proj)
- if(!Proj.nodamage)
- switch(Proj.damage_type)
- if(DAMAGE_BRUTE)
- adjustBruteLoss(Proj.damage)
- if(DAMAGE_BURN)
- adjustFireLoss(Proj.damage)
+/mob/living/silicon/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
+ SHOULD_CALL_PARENT(FALSE) //More shitcode
- Proj.on_hit(src, 100)
+ if(hitting_projectile.damage > 0)
+ switch(hitting_projectile.damage_type)
+ if(DAMAGE_BRUTE)
+ adjustBruteLoss(hitting_projectile.damage)
+ if(DAMAGE_BURN)
+ adjustFireLoss(hitting_projectile.damage)
+
+ hitting_projectile.on_hit(src, 100)
updatehealth()
- return 100
+ return BULLET_ACT_HIT
/mob/living/silicon/apply_effect(var/effect = 0,var/effecttype = STUN, var/blocked = 0)
return FALSE
diff --git a/code/modules/mob/living/simple_animal/constructs/constructs/juggernaut.dm b/code/modules/mob/living/simple_animal/constructs/constructs/juggernaut.dm
index 7e0041a144d..99b536f668e 100644
--- a/code/modules/mob/living/simple_animal/constructs/constructs/juggernaut.dm
+++ b/code/modules/mob/living/simple_animal/constructs/constructs/juggernaut.dm
@@ -22,23 +22,25 @@
resistance = 10
construct_spells = list(/spell/aoe_turf/conjure/forcewall/lesser)
-/mob/living/simple_animal/construct/armored/bullet_act(var/obj/projectile/P)
- if(istype(P, /obj/projectile/energy) || istype(P, /obj/projectile/beam))
- var/reflectchance = 80 - round(P.damage / 3)
+/mob/living/simple_animal/construct/armored/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
+ . = ..()
+ if(. != BULLET_ACT_HIT)
+ return .
+
+ if(istype(hitting_projectile, /obj/projectile/energy) || istype(hitting_projectile, /obj/projectile/beam))
+ var/reflectchance = 80 - round(hitting_projectile.damage / 3)
if(prob(reflectchance))
- adjustBruteLoss(P.damage * 0.3)
- visible_message(SPAN_DANGER("\The [P.name] gets reflected by \the [src]'s shell!"), \
- SPAN_DANGER("\The [P.name] gets reflected by \the [src]'s shell!"))
+ adjustBruteLoss(hitting_projectile.damage * 0.3)
+ visible_message(SPAN_DANGER("\The [hitting_projectile.name] gets reflected by \the [src]'s shell!"), \
+ SPAN_DANGER("\The [hitting_projectile.name] gets reflected by \the [src]'s shell!"))
// Find a turf near or on the original location to bounce to
- if(P.starting)
- var/new_x = P.starting.x + text2num(pickweight(list("0" = 1, "1" = 1, "2" = 3, "3" = 2))) * pick(1, -1)
- var/new_y = P.starting.y + text2num(pickweight(list("0" = 1, "1" = 1, "2" = 3, "3" = 2))) * pick(1, -1)
+ if(hitting_projectile.starting)
+ var/new_x = hitting_projectile.starting.x + text2num(pickweight(list("0" = 1, "1" = 1, "2" = 3, "3" = 2))) * pick(1, -1)
+ var/new_y = hitting_projectile.starting.y + text2num(pickweight(list("0" = 1, "1" = 1, "2" = 3, "3" = 2))) * pick(1, -1)
// redirect the projectile
- P.firer = src
- P.old_style_target(locate(new_x, new_y, P.z))
+ hitting_projectile.firer = src
+ hitting_projectile.old_style_target(locate(new_x, new_y, hitting_projectile.z))
- return -1 // complete projectile permutation
-
- return (..(P))
+ return BULLET_ACT_FORCE_PIERCE // complete projectile permutation
diff --git a/code/modules/mob/living/simple_animal/friendly/adhomai.dm b/code/modules/mob/living/simple_animal/friendly/adhomai.dm
index 117b315a8f0..431287d8e66 100644
--- a/code/modules/mob/living/simple_animal/friendly/adhomai.dm
+++ b/code/modules/mob/living/simple_animal/friendly/adhomai.dm
@@ -239,10 +239,13 @@
unburrow()
..()
-/mob/living/simple_animal/ice_catcher/bullet_act(obj/projectile/P, def_zone)
+/mob/living/simple_animal/ice_catcher/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
+ . = ..()
+ if(. != BULLET_ACT_HIT)
+ return .
+
if(burrowed && (stat != DEAD))
unburrow()
- ..()
/mob/living/simple_animal/ice_catcher/death(gibbed)
..()
diff --git a/code/modules/mob/living/simple_animal/friendly/cat.dm b/code/modules/mob/living/simple_animal/friendly/cat.dm
index 1bba511b5b8..c3b3351985d 100644
--- a/code/modules/mob/living/simple_animal/friendly/cat.dm
+++ b/code/modules/mob/living/simple_animal/friendly/cat.dm
@@ -185,9 +185,12 @@
. = ..()
set_flee_target(src.loc)
-/mob/living/simple_animal/cat/bullet_act(var/obj/projectile/proj)
+/mob/living/simple_animal/cat/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
. = ..()
- set_flee_target(proj.firer? proj.firer : src.loc)
+ if(. != BULLET_ACT_HIT)
+ return .
+
+ set_flee_target(hitting_projectile.firer? hitting_projectile.firer : src.loc)
/mob/living/simple_animal/cat/hitby(atom/movable/hitting_atom, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum)
. = ..()
diff --git a/code/modules/mob/living/simple_animal/hostile/adhomai.dm b/code/modules/mob/living/simple_animal/hostile/adhomai.dm
index 23ea80afdc7..0557cd92897 100644
--- a/code/modules/mob/living/simple_animal/hostile/adhomai.dm
+++ b/code/modules/mob/living/simple_animal/hostile/adhomai.dm
@@ -168,7 +168,7 @@
/mob/living/simple_animal/hostile/plasmageist/ex_act(severity)
return
-/obj/projectile/beam/tesla/plasmageist/on_impact(atom/target)
+/obj/projectile/beam/tesla/plasmageist/on_hit(atom/target, blocked, def_zone)
. = ..()
if(isliving(target))
explosion(target, -1, 0, 2)
diff --git a/code/modules/mob/living/simple_animal/hostile/bear.dm b/code/modules/mob/living/simple_animal/hostile/bear.dm
index 5a9b8d68201..9e40c76a234 100644
--- a/code/modules/mob/living/simple_animal/hostile/bear.dm
+++ b/code/modules/mob/living/simple_animal/hostile/bear.dm
@@ -273,13 +273,16 @@
anger++
instant_aggro(1)
-
-/mob/living/simple_animal/hostile/bear/bullet_act(obj/projectile/P, def_zone)//Teleport around when shot, so its harder to burst it down with a carbine
+//Teleport around when shot, so its harder to burst it down with a carbine
+/mob/living/simple_animal/hostile/bear/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
var/healthbefore = health
- ..()
- spawn(1)
- if (health < healthbefore)
- instant_aggro()
+
+ . = ..()
+ if(. != BULLET_ACT_HIT)
+ return .
+
+ if (health < healthbefore)
+ instant_aggro()
/mob/living/simple_animal/hostile/bear/ex_act(var/severity = 2.0)
var/healthbefore = health
@@ -508,9 +511,13 @@
forceMove(target)
spark_system.queue()
-/mob/living/simple_animal/hostile/bear/spatial/bullet_act(obj/projectile/P, def_zone)//Teleport around when shot, so its harder to burst it down with a carbine
- ..(P, def_zone)
- if (prob(P.damage*1.5))//Bear has a good chance of teleporting when shot, making it harder to burst down
+//Teleport around when shot, so its harder to burst it down with a carbine
+/mob/living/simple_animal/hostile/bear/spatial/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
+ . = ..()
+ if(. != BULLET_ACT_HIT)
+ return .
+
+ if (prob(hitting_projectile.damage*1.5))//Bear has a good chance of teleporting when shot, making it harder to burst down
teleport_tactical()
/mob/living/simple_animal/hostile/bear/spatial/FoundTarget()
diff --git a/code/modules/mob/living/simple_animal/hostile/commanded/commanded.dm b/code/modules/mob/living/simple_animal/hostile/commanded/commanded.dm
index 78f50750f40..179f3136354 100644
--- a/code/modules/mob/living/simple_animal/hostile/commanded/commanded.dm
+++ b/code/modules/mob/living/simple_animal/hostile/commanded/commanded.dm
@@ -308,11 +308,13 @@ ABSTRACT_TYPE(/mob/living/simple_animal/hostile/commanded)
change_stance(HOSTILE_STANCE_IDLE)
audible_emote("[pick(sad_emote)].",0)
-/mob/living/simple_animal/hostile/commanded/bullet_act(var/obj/projectile/P, var/def_zone)
- ..()
+/mob/living/simple_animal/hostile/commanded/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
+ . = ..()
+ if(. != BULLET_ACT_HIT)
+ return .
// We forgive our master
- if (ismob(P.firer) && P.firer == master)
+ if (ismob(hitting_projectile.firer) && hitting_projectile.firer == master)
target_mob = null
change_stance(HOSTILE_STANCE_IDLE)
audible_emote("[pick(sad_emote)].",0)
diff --git a/code/modules/mob/living/simple_animal/hostile/hivebots/hivebot.dm b/code/modules/mob/living/simple_animal/hostile/hivebots/hivebot.dm
index 3e7bf222d2b..737046955a0 100644
--- a/code/modules/mob/living/simple_animal/hostile/hivebots/hivebot.dm
+++ b/code/modules/mob/living/simple_animal/hostile/hivebots/hivebot.dm
@@ -81,11 +81,11 @@
else
return "blood_overlay_armed"
-/mob/living/simple_animal/hostile/hivebot/bullet_act(var/obj/projectile/Proj)
- if(istype(Proj, /obj/projectile/bullet/pistol/hivebotspike) || istype(Proj, /obj/projectile/beam/hivebot))
- return PROJECTILE_CONTINUE
+/mob/living/simple_animal/hostile/hivebot/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
+ if(istype(hitting_projectile, /obj/projectile/bullet/pistol/hivebotspike) || istype(hitting_projectile, /obj/projectile/beam/hivebot))
+ return BULLET_ACT_BLOCK
else
- return ..(Proj)
+ return ..()
/mob/living/simple_animal/hostile/hivebot/death()
..(null,"blows apart!")
@@ -194,10 +194,14 @@
has_exploded = TRUE
addtimer(CALLBACK(src, PROC_REF(burst)), 20)
-/mob/living/simple_animal/hostile/hivebot/bomber/bullet_act(var/obj/projectile/Proj)
- if(istype(Proj, /obj/projectile/bullet/pistol/hivebotspike) || istype(Proj, /obj/projectile/beam/hivebot))
- return PROJECTILE_CONTINUE
+/mob/living/simple_animal/hostile/hivebot/bomber/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
+ if(istype(hitting_projectile, /obj/projectile/bullet/pistol/hivebotspike) || istype(hitting_projectile, /obj/projectile/beam/hivebot))
+ return BULLET_ACT_BLOCK
else if(!has_exploded)
+ . = ..()
+ if(. != BULLET_ACT_HIT)
+ return .
+
has_exploded = TRUE
burst()
diff --git a/code/modules/mob/living/simple_animal/hostile/hivebots/hivebot_beacon.dm b/code/modules/mob/living/simple_animal/hostile/hivebots/hivebot_beacon.dm
index d4789bac7e2..249651bfdf2 100644
--- a/code/modules/mob/living/simple_animal/hostile/hivebots/hivebot_beacon.dm
+++ b/code/modules/mob/living/simple_animal/hostile/hivebots/hivebot_beacon.dm
@@ -208,11 +208,11 @@
/mob/living/simple_animal/hostile/hivebotbeacon/AirflowCanMove(n)
return 0
-/mob/living/simple_animal/hostile/hivebotbeacon/bullet_act(var/obj/projectile/Proj)
- if(istype(Proj, /obj/projectile/bullet/pistol/hivebotspike) || istype(Proj, /obj/projectile/beam/hivebot))
- return PROJECTILE_CONTINUE
+/mob/living/simple_animal/hostile/hivebotbeacon/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
+ if(istype(hitting_projectile, /obj/projectile/bullet/pistol/hivebotspike) || istype(hitting_projectile, /obj/projectile/beam/hivebot))
+ return BULLET_ACT_BLOCK
else
- ..(Proj)
+ . = ..()
/mob/living/simple_animal/hostile/hivebotbeacon/emp_act(severity)
. = ..()
diff --git a/code/modules/mob/living/simple_animal/hostile/hivebots/hivebot_harvester.dm b/code/modules/mob/living/simple_animal/hostile/hivebots/hivebot_harvester.dm
index ac54554539d..90efd9535f6 100644
--- a/code/modules/mob/living/simple_animal/hostile/hivebots/hivebot_harvester.dm
+++ b/code/modules/mob/living/simple_animal/hostile/hivebots/hivebot_harvester.dm
@@ -72,11 +72,11 @@
/mob/living/simple_animal/hostile/retaliate/hivebotharvester/AirflowCanMove(n)
return 0
-/mob/living/simple_animal/hostile/retaliate/hivebotharvester/bullet_act(var/obj/projectile/Proj)
- if(istype(Proj, /obj/projectile/bullet/pistol/hivebotspike) || istype(Proj, /obj/projectile/beam/hivebot))
- return PROJECTILE_CONTINUE
+/mob/living/simple_animal/hostile/retaliate/hivebotharvester/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
+ if(istype(hitting_projectile, /obj/projectile/bullet/pistol/hivebotspike) || istype(hitting_projectile, /obj/projectile/beam/hivebot))
+ return BULLET_ACT_BLOCK
else
- return ..(Proj)
+ return ..()
/mob/living/simple_animal/hostile/retaliate/hivebotharvester/emp_act(severity)
. = ..()
diff --git a/code/modules/mob/living/simple_animal/hostile/hostile.dm b/code/modules/mob/living/simple_animal/hostile/hostile.dm
index 0a3f1858fe3..2213eda7cf4 100644
--- a/code/modules/mob/living/simple_animal/hostile/hostile.dm
+++ b/code/modules/mob/living/simple_animal/hostile/hostile.dm
@@ -118,10 +118,13 @@ ABSTRACT_TYPE(/mob/living/simple_animal/hostile)
change_stance(HOSTILE_STANCE_ATTACKING)
visible_message(SPAN_WARNING("\The [src] gets taunted by \the [H] and begins to retaliate!"))
-/mob/living/simple_animal/hostile/bullet_act(var/obj/projectile/P, var/def_zone)
- ..()
- if (ismob(P.firer) && target_mob != P.firer)
- target_mob = P.firer
+/mob/living/simple_animal/hostile/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
+ . = ..()
+ if(. != BULLET_ACT_HIT)
+ return .
+
+ if (ismob(hitting_projectile.firer) && target_mob != hitting_projectile.firer)
+ target_mob = hitting_projectile.firer
change_stance(HOSTILE_STANCE_ATTACK)
/mob/living/simple_animal/hostile/handle_attack_by(var/mob/user)
@@ -373,11 +376,9 @@ ABSTRACT_TYPE(/mob/living/simple_animal/hostile)
if(target == start)
return
- var/obj/projectile/A = new projectiletype(user.loc)
- playsound(user, projectilesound, 100, 1)
- if(!A) return
- var/def_zone = get_exposed_defense_zone(target)
- A.launch_projectile(target, def_zone)
+ // var/def_zone = get_exposed_defense_zone(target)
+
+ fire_projectile(/obj/projectile, target, projectilesound, firer = user)
/mob/living/simple_animal/hostile/proc/DestroySurroundings(var/bypass_prob = FALSE)
if(ON_ATTACK_COOLDOWN(src))
diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/cavern.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/cavern.dm
index 9655e35b11a..28357adeac2 100644
--- a/code/modules/mob/living/simple_animal/hostile/retaliate/cavern.dm
+++ b/code/modules/mob/living/simple_animal/hostile/retaliate/cavern.dm
@@ -67,7 +67,9 @@
else
..()
-/obj/projectile/beam/cavern/on_hit(var/atom/target, var/blocked = 0)
+/obj/projectile/beam/cavern/on_hit(atom/target, blocked, def_zone)
+ . = ..()
+
if(ishuman(target))
var/mob/living/carbon/human/M = target
var/shock_damage = rand(10,20)
diff --git a/code/modules/mob/living/simple_animal/hostile/sarlacc.dm b/code/modules/mob/living/simple_animal/hostile/sarlacc.dm
index fe6da2e445c..ae8bbd36e19 100644
--- a/code/modules/mob/living/simple_animal/hostile/sarlacc.dm
+++ b/code/modules/mob/living/simple_animal/hostile/sarlacc.dm
@@ -361,7 +361,7 @@
/obj/projectile/energy/thoughtbubble
name = "psionic blast"
icon_state = "ion"
- nodamage = TRUE
+ damage = 0
agony = 20
check_armor = "energy"
pass_flags = PASSTABLE | PASSGLASS | PASSGRILLE
@@ -391,10 +391,10 @@
"You've got a bad feeling about this."
)
-/obj/projectile/energy/thoughtbubble/on_impact(var/atom/A)
- ..()
- if(istype(A, /mob/living))
- var/mob/living/L = A
+/obj/projectile/energy/thoughtbubble/on_hit(atom/target, blocked, def_zone)
+ . = ..()
+ if(istype(target, /mob/living))
+ var/mob/living/L = target
if(L.reagents)
var/madhouse = pick(/singleton/reagent/drugs/psilocybin,/singleton/reagent/drugs/mindbreaker,/singleton/reagent/drugs/impedrezene,/singleton/reagent/drugs/cryptobiolin,/singleton/reagent/soporific,/singleton/reagent/mutagen)
var/madhouse_verbal_component = pick(thoughts)
diff --git a/code/modules/mob/living/simple_animal/hostile/space_fauna.dm b/code/modules/mob/living/simple_animal/hostile/space_fauna.dm
index 81a7420af89..03a7ea35246 100644
--- a/code/modules/mob/living/simple_animal/hostile/space_fauna.dm
+++ b/code/modules/mob/living/simple_animal/hostile/space_fauna.dm
@@ -227,7 +227,11 @@
has_exploded = TRUE
addtimer(CALLBACK(src, PROC_REF(explode)), 5)
-/mob/living/simple_animal/hostile/carp/bloater/bullet_act(var/obj/projectile/Proj)
+/mob/living/simple_animal/hostile/carp/bloater/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
+ . = ..()
+ if(. != BULLET_ACT_HIT)
+ return .
+
if(!has_exploded)
has_exploded = TRUE
explode()
diff --git a/code/modules/mob/living/simple_animal/hostile/syndicate.dm b/code/modules/mob/living/simple_animal/hostile/syndicate.dm
index 05bdcf0262c..34168ee73a0 100644
--- a/code/modules/mob/living/simple_animal/hostile/syndicate.dm
+++ b/code/modules/mob/living/simple_animal/hostile/syndicate.dm
@@ -78,13 +78,16 @@
visible_message(SPAN_WARNING("[user] gently taps [src] with the [attacking_item]."))
-/mob/living/simple_animal/hostile/syndicate/melee/bullet_act(var/obj/projectile/Proj)
- if(!Proj) return
- if(prob(65))
- src.health -= Proj.damage
- else
- visible_message(SPAN_DANGER("[src] blocks [Proj] with its shield!"))
- return 0
+/mob/living/simple_animal/hostile/syndicate/melee/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
+ if(prob(35))
+ visible_message(SPAN_DANGER("[src] blocks [hitting_projectile] with its shield!"))
+ return BULLET_ACT_BLOCK
+
+ . = ..()
+ if(. != BULLET_ACT_HIT)
+ return .
+
+ src.health -= hitting_projectile.damage
/mob/living/simple_animal/hostile/syndicate/melee/space
diff --git a/code/modules/mob/living/simple_animal/hostile/vannatusk.dm b/code/modules/mob/living/simple_animal/hostile/vannatusk.dm
index 72095c89e47..50bf124c876 100644
--- a/code/modules/mob/living/simple_animal/hostile/vannatusk.dm
+++ b/code/modules/mob/living/simple_animal/hostile/vannatusk.dm
@@ -61,10 +61,7 @@
/mob/living/simple_animal/hostile/vannatusk/proc/fire_spike(var/mob/living/target_mob)
visible_message(SPAN_DANGER("\The [src] fires a spike at [target_mob]!"))
- playsound(get_turf(src), 'sound/weapons/bloodyslice.ogg', 50, 1)
- var/obj/projectile/bonedart/A = new /obj/projectile/bonedart(get_turf(src))
- var/def_zone = get_exposed_defense_zone(target_mob)
- A.launch_projectile(target_mob, def_zone)
+ fire_projectile(/obj/projectile/bonedart, target_mob, 'sound/weapons/bloodyslice.ogg', firer = src)
/obj/item/bone_dart/vannatusk
name = "bone dart"
diff --git a/code/modules/mob/living/simple_animal/illusion.dm b/code/modules/mob/living/simple_animal/illusion.dm
index 3938914993d..54a8c037e28 100644
--- a/code/modules/mob/living/simple_animal/illusion.dm
+++ b/code/modules/mob/living/simple_animal/illusion.dm
@@ -38,14 +38,14 @@
else
return list("???")
-/mob/living/simple_animal/illusion/bullet_act(obj/projectile/P)
- if(!P)
- return
+/mob/living/simple_animal/illusion/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
+ if(!hitting_projectile)
+ return BULLET_ACT_BLOCK
if(realistic)
return ..()
- return PROJECTILE_FORCE_MISS
+ return BULLET_ACT_BLOCK
/mob/living/simple_animal/illusion/attack_hand(mob/living/carbon/human/M)
if(!realistic)
diff --git a/code/modules/mob/living/simple_animal/parrot.dm b/code/modules/mob/living/simple_animal/parrot.dm
index c388587d01a..cb5bf7425ef 100644
--- a/code/modules/mob/living/simple_animal/parrot.dm
+++ b/code/modules/mob/living/simple_animal/parrot.dm
@@ -254,8 +254,11 @@
return
//Bullets
-/mob/living/simple_animal/parrot/bullet_act(var/obj/projectile/Proj)
- ..()
+/mob/living/simple_animal/parrot/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
+ . = ..()
+ if(. != BULLET_ACT_HIT)
+ return .
+
if(!stat && !client)
if(parrot_state == PARROT_PERCH)
parrot_sleep_dur = parrot_sleep_max //Reset it's sleep timer if it was perched
@@ -265,7 +268,6 @@
parrot_been_shot += 5
icon_state = "parrot_fly"
drop_held_item(0)
- return
/*
diff --git a/code/modules/mob/living/simple_animal/simple_animal.dm b/code/modules/mob/living/simple_animal/simple_animal.dm
index 4819452a744..3822d7a4a5e 100644
--- a/code/modules/mob/living/simple_animal/simple_animal.dm
+++ b/code/modules/mob/living/simple_animal/simple_animal.dm
@@ -654,10 +654,13 @@
if(ismob(throwingdatum.thrower?.resolve()))
handle_attack_by(throwingdatum.thrower.resolve())
-/mob/living/simple_animal/bullet_act(obj/projectile/P, def_zone)
+/mob/living/simple_animal/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
. = ..()
- if(ismob(P.firer))
- handle_attack_by(P.firer)
+ if(. != BULLET_ACT_HIT)
+ return .
+
+ if(ismob(hitting_projectile.firer))
+ handle_attack_by(hitting_projectile.firer)
/mob/living/simple_animal/apply_damage(damage = 0, damagetype = DAMAGE_BRUTE, def_zone, blocked, used_weapon, damage_flags = 0, armor_pen, silent = FALSE)
. = ..()
@@ -965,12 +968,12 @@
/mob/living/simple_animal/get_digestion_product()
return /singleton/reagent/nutriment
-/mob/living/simple_animal/bullet_impact_visuals(var/obj/projectile/P, var/def_zone, var/damage)
- ..()
+/mob/living/simple_animal/bullet_impact_visuals(obj/projectile/impacting_projectile, def_zone, damage, blocked)
+ . = ..()
switch(get_bullet_impact_effect_type(def_zone))
if(BULLET_IMPACT_MEAT)
- if(P.damage_type == DAMAGE_BRUTE)
- var/hit_dir = get_dir(P.starting, src)
+ if(impacting_projectile.damage_type == DAMAGE_BRUTE)
+ var/hit_dir = get_dir(impacting_projectile.starting, src)
var/obj/effect/decal/cleanable/blood/B = blood_splatter(get_step(src, hit_dir), src, 1, hit_dir)
B.icon_state = pick("dir_splatter_1","dir_splatter_2")
B.basecolor = blood_type
diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm
index 29da0e09ce8..10396350d46 100644
--- a/code/modules/mob/mob_helpers.dm
+++ b/code/modules/mob/mob_helpers.dm
@@ -276,6 +276,14 @@ var/list/global/organ_rel_size = list(
BP_R_FOOT = 10
)
+///Find the mob at the bottom of a buckle chain
+/mob/proc/lowest_buckled_mob()
+ . = src
+ //buckled -> buckled_to from TG
+ if(buckled_to && ismob(buckled_to))
+ var/mob/Buckled = buckled_to
+ . = Buckled.lowest_buckled_mob()
+
/proc/check_zone(zone)
if(!zone)
return BP_CHEST
@@ -286,36 +294,24 @@ var/list/global/organ_rel_size = list(
zone = BP_HEAD
return zone
-// Returns zone with a certain probability. If the probability fails, or no zone is specified, then a random body part is chosen.
-// Do not use this if someone is intentionally trying to hit a specific body part.
-// Use get_zone_with_miss_chance() for that.
-/proc/ran_zone(zone, probability)
- if (zone)
+/**
+ * Return the zone or randomly, another valid zone
+ *
+ * _Do not use this if someone is intentionally trying to hit a specific body part - use get_zone_with_miss_chance() for that_
+ *
+ * probability controls the chance it chooses the passed in zone, or another random zone
+ * defaults to 80
+ */
+/proc/ran_zone(zone, probability = 80, list/weighted_list)
+ if(prob(probability))
zone = check_zone(zone)
- if (prob(probability))
- return zone
+ else
+ zone = pick_weight(weighted_list ? weighted_list : organ_rel_size) //Slightly different from TG, we have a list with organ sizes
+ return zone
- var/ran_zone = zone
- while (ran_zone == zone)
- ran_zone = pick (
- organ_rel_size[BP_HEAD]; BP_HEAD,
- organ_rel_size[BP_CHEST]; BP_CHEST,
- organ_rel_size[BP_GROIN]; BP_GROIN,
- organ_rel_size[BP_L_ARM]; BP_L_ARM,
- organ_rel_size[BP_R_ARM]; BP_R_ARM,
- organ_rel_size[BP_L_LEG]; BP_L_LEG,
- organ_rel_size[BP_R_LEG]; BP_R_LEG,
- organ_rel_size[BP_L_HAND]; BP_L_HAND,
- organ_rel_size[BP_R_HAND]; BP_R_HAND,
- organ_rel_size[BP_L_FOOT]; BP_L_FOOT,
- organ_rel_size[BP_R_FOOT]; BP_R_FOOT
- )
-
- return ran_zone
-
-// Emulates targetting a specific body part, and miss chances
-// May return null if missed
-// miss_chance_mod may be negative.
+/// Emulates targetting a specific body part, and miss chances
+/// May return null if missed
+/// miss_chance_mod may be negative.
/proc/get_zone_with_miss_chance(zone, var/mob/target, var/miss_chance_mod = 0, var/ranged_attack=0, var/point_blank = FALSE)
zone = check_zone(zone)
diff --git a/code/modules/mob/mob_movement.dm b/code/modules/mob/mob_movement.dm
index 75ceb33e58b..ed639f27c96 100644
--- a/code/modules/mob/mob_movement.dm
+++ b/code/modules/mob/mob_movement.dm
@@ -1,5 +1,9 @@
/mob/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
- if(air_group || (height==0)) return 1
+ if(air_group || (height==0))
+ return TRUE
+
+ if(mover?.movement_type & PHASING)
+ return TRUE
if(ismob(mover))
var/mob/moving_mob = mover
diff --git a/code/modules/modular_computers/computers/modular_computer/damage.dm b/code/modules/modular_computers/computers/modular_computer/damage.dm
index e5a83c4c529..5d1d3a0c8cd 100644
--- a/code/modules/modular_computers/computers/modular_computer/damage.dm
+++ b/code/modules/modular_computers/computers/modular_computer/damage.dm
@@ -52,11 +52,15 @@
// "Stun" weapons can cause minor damage to components (short-circuits?)
// "Burn" damage is equally strong against internal components and exterior casing
// "Brute" damage mostly damages the casing.
-/obj/item/modular_computer/bullet_act(var/obj/projectile/Proj)
- switch(Proj.damage_type)
+/obj/item/modular_computer/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
+ . = ..()
+ if(. != BULLET_ACT_HIT)
+ return .
+
+ switch(hitting_projectile.damage_type)
if(DAMAGE_BRUTE)
- take_damage(Proj.damage, Proj.damage / 2)
+ take_damage(hitting_projectile.damage, hitting_projectile.damage / 2)
if(DAMAGE_PAIN)
- take_damage(Proj.damage, Proj.damage / 3, 0)
+ take_damage(hitting_projectile.damage, hitting_projectile.damage / 3, 0)
if(DAMAGE_BURN)
- take_damage(Proj.damage, Proj.damage / 1.5)
+ take_damage(hitting_projectile.damage, hitting_projectile.damage / 1.5)
diff --git a/code/modules/modular_computers/computers/subtypes/preset_console.dm b/code/modules/modular_computers/computers/subtypes/preset_console.dm
index fbe4f879522..216390995b0 100644
--- a/code/modules/modular_computers/computers/subtypes/preset_console.dm
+++ b/code/modules/modular_computers/computers/subtypes/preset_console.dm
@@ -1,4 +1,6 @@
/obj/item/modular_computer/console/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
+ if(mover?.movement_type & PHASING)
+ return TRUE
if(istype(mover,/obj/projectile))
if(prob(80))
//Holoscreens are non solid, and the frames of the computers are thin. So projectiles will usually
diff --git a/code/modules/multiz/structures.dm b/code/modules/multiz/structures.dm
index 2e1de070a44..2b9ed7e40a6 100644
--- a/code/modules/multiz/structures.dm
+++ b/code/modules/multiz/structures.dm
@@ -179,6 +179,8 @@
return M.forceMove(T)
/obj/structure/ladder/CanPass(obj/mover, turf/source, height, airflow)
+ if(mover?.movement_type & PHASING)
+ return TRUE
return airflow || !density
/obj/structure/ladder/update_icon()
@@ -296,6 +298,9 @@
if(airflow)
return TRUE
+ if(mover?.movement_type & PHASING)
+ return TRUE
+
// Disallow stepping onto the elevated part of the stairs.
if(isliving(mover) && z == mover.z && mover.loc != loc && get_step(mover, get_dir(mover, src)) == loc)
return FALSE
@@ -347,6 +352,9 @@
density = TRUE
/obj/structure/stairs_railing/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
+ if(mover?.movement_type & PHASING)
+ return TRUE
+
if(istype(mover,/obj/projectile))
return TRUE
if(!istype(mover) || mover.pass_flags & PASSRAILING)
@@ -420,6 +428,9 @@
color = COLOR_DARK_GUNMETAL
/obj/structure/platform/CanPass(atom/movable/mover, turf/target, height, air_group)
+ if(mover?.movement_type & PHASING)
+ return TRUE
+
if(istype(mover, /obj/projectile))
return TRUE
if(!istype(mover) || mover.pass_flags & PASSRAILING)
diff --git a/code/modules/organs/organ_external.dm b/code/modules/organs/organ_external.dm
index b47dbae1480..761ef90e9fd 100644
--- a/code/modules/organs/organ_external.dm
+++ b/code/modules/organs/organ_external.dm
@@ -405,7 +405,7 @@
if(spillover > 0)
burn = max(burn - spillover, 0)
- handle_limb_gibbing(used_weapon, brute, burn)
+ handle_limb_gibbing(used_weapon, brute_dam, burn_dam)
if(brute_dam + brute > min_broken_damage && prob(brute_dam + brute * (1 + blunt)))
if(blunt || brute > FRACTURE_AND_TENDON_DAM_THRESHOLD)
@@ -499,7 +499,7 @@
/obj/item/organ/external/proc/handle_limb_gibbing(var/used_weapon, var/brute, var/burn)
//If limb took enough damage, try to cut or tear it off
if(owner && loc == owner && !is_stump())
- if((limb_flags & ORGAN_CAN_AMPUTATE) && GLOB.config.limbs_can_break)
+ if((limb_flags & ORGAN_CAN_AMPUTATE))
if((brute_dam + burn_dam) >= (max_damage * GLOB.config.organ_health_multiplier))
diff --git a/code/modules/overmap/exoplanets/decor/flora/potted_big.dm b/code/modules/overmap/exoplanets/decor/flora/potted_big.dm
index 0dc5ba46e23..3dda945912d 100644
--- a/code/modules/overmap/exoplanets/decor/flora/potted_big.dm
+++ b/code/modules/overmap/exoplanets/decor/flora/potted_big.dm
@@ -65,11 +65,13 @@
to_chat(user,SPAN_NOTICE("You take \the [stored_item] from [src]."))
stored_item = null
-/obj/structure/flora/pottedplant/bullet_act(var/obj/projectile/Proj)
- if (prob(Proj.damage*2))
+/obj/structure/flora/pottedplant/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
+ . = ..()
+ if(. != BULLET_ACT_HIT)
+ return .
+
+ if (prob(hitting_projectile.damage*2))
death()
- return 1
- return ..()
// ------------------------------------ dead/empty
diff --git a/code/modules/overmap/exoplanets/decor/flora/potted_small.dm b/code/modules/overmap/exoplanets/decor/flora/potted_small.dm
index ff89a1c371e..b1afd18896c 100644
--- a/code/modules/overmap/exoplanets/decor/flora/potted_small.dm
+++ b/code/modules/overmap/exoplanets/decor/flora/potted_small.dm
@@ -23,11 +23,13 @@
death()
return ..()
-/obj/item/flora/pottedplant_small/bullet_act(var/obj/projectile/Proj)
- if (prob(Proj.damage*2))
+/obj/item/flora/pottedplant_small/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
+ . = ..()
+ if(. != BULLET_ACT_HIT)
+ return .
+
+ if (prob(hitting_projectile.damage*2))
death()
- return 1
- return ..()
// ------------------------------------ dead/empty
diff --git a/code/modules/overmap/ship_weaponry/_ship_ammunition.dm b/code/modules/overmap/ship_weaponry/_ship_ammunition.dm
index 4ce7fa13f75..d81fa1511c5 100644
--- a/code/modules/overmap/ship_weaponry/_ship_ammunition.dm
+++ b/code/modules/overmap/ship_weaponry/_ship_ammunition.dm
@@ -255,4 +255,5 @@
pellet.ammo.impact_type = ammo.impact_type
pellet.dir = dir
var/turf/front_turf = get_step(pellet, pellet.dir)
- pellet.launch_projectile(front_turf)
+ pellet.preparePixelProjectile(target_turf, front_turf)
+ pellet.fire()
diff --git a/code/modules/overmap/ship_weaponry/_ship_gun.dm b/code/modules/overmap/ship_weaponry/_ship_gun.dm
index debb68067ae..65ec49783bd 100644
--- a/code/modules/overmap/ship_weaponry/_ship_gun.dm
+++ b/code/modules/overmap/ship_weaponry/_ship_gun.dm
@@ -230,7 +230,6 @@
projectile.desc = SA.desc
projectile.ammo = SA
projectile.dir = barrel.dir
- projectile.shot_from = name
SA.overmap_target = overmap_target
SA.entry_point = landmark
SA.origin = linked
@@ -243,7 +242,9 @@
SA.heading = barrel.dir
SA.forceMove(projectile)
var/turf/target = get_step(projectile, barrel.dir)
- projectile.launch_projectile(target)
+ projectile.preparePixelProjectile(target, firing_turf)
+ projectile.fired_from = barrel
+ projectile.fire()
return TRUE
/obj/machinery/ship_weapon/proc/consume_ammo()
@@ -300,8 +301,12 @@
M.turf_collision(src, throwingdatum.speed)
return
-/obj/structure/ship_weapon_dummy/bullet_act(obj/projectile/P, def_zone)
- connected.bullet_act(P)
+/obj/structure/ship_weapon_dummy/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
+ . = ..()
+ if(. != BULLET_ACT_HIT)
+ return .
+
+ connected.bullet_act(hitting_projectile)
/obj/structure/ship_weapon_dummy/ex_act(severity)
connected.ex_act(severity)
diff --git a/code/modules/overmap/ship_weaponry/projectiles/_overmap_projectiles.dm b/code/modules/overmap/ship_weaponry/projectiles/_overmap_projectiles.dm
index 6e3560782cd..b207c6da02c 100644
--- a/code/modules/overmap/ship_weaponry/projectiles/_overmap_projectiles.dm
+++ b/code/modules/overmap/ship_weaponry/projectiles/_overmap_projectiles.dm
@@ -128,7 +128,9 @@
widowmaker.on_translate(entry_turf, target_turf)
log_and_message_admins("A projectile ([widowmaker.name]) has entered a z-level at [entry_target.name], with direction [dir2text(widowmaker.dir)]! (JMP)")
say_dead_direct("A projectile ([widowmaker.name]) has entered a z-level at [entry_target.name], with direction [dir2text(widowmaker.dir)]!")
- widowmaker.launch_projectile(target_turf)
+ widowmaker.preparePixelProjectile(target_turf, T)
+ widowmaker.fired_from = src
+ widowmaker.fire()
qdel(src)
if(istype(A, /obj/effect/overmap/event))
var/obj/effect/overmap/event/EV = A
diff --git a/code/modules/overmap/ship_weaponry/weaponry/bruiser.dm b/code/modules/overmap/ship_weaponry/weaponry/bruiser.dm
index e10c57bad98..f6a1df6f658 100644
--- a/code/modules/overmap/ship_weaponry/weaponry/bruiser.dm
+++ b/code/modules/overmap/ship_weaponry/weaponry/bruiser.dm
@@ -152,9 +152,7 @@
penetrating = 6
stun = 4
weaken = 4
- maiming = TRUE
maim_rate = 3
- maim_type = DROPLIMB_BLUNT
anti_materiel_potential = 2
/obj/projectile/ship_ammo/bruiser/real/on_hit(atom/target, blocked, def_zone, is_landmark_hit)
@@ -164,8 +162,10 @@
if(ammo.impact_type == SHIP_AMMO_IMPACT_AP)
explosion(target, 0, 2, 4)
-/obj/projectile/ship_ammo/bruiser/real/beehive/on_hit(atom/movable/target, blocked, def_zone, is_landmark_hit)
- if(istype(target))
- var/throwdir = dir
- target.throw_at(get_edge_target_turf(target, throwdir),9,8)
+/obj/projectile/ship_ammo/bruiser/real/beehive/on_hit(atom/target, blocked, def_zone, is_landmark_hit)
+ . = ..()
+
+ if(ismovable(target))
+ var/atom/movable/M = target
+ M.throw_at(get_edge_target_turf(target, dir),9,8)
return 1
diff --git a/code/modules/overmap/ship_weaponry/weaponry/francisca.dm b/code/modules/overmap/ship_weaponry/weaponry/francisca.dm
index 44d58f78ccc..6ac56ba8ef8 100644
--- a/code/modules/overmap/ship_weaponry/weaponry/francisca.dm
+++ b/code/modules/overmap/ship_weaponry/weaponry/francisca.dm
@@ -64,6 +64,6 @@
armor_penetration = 50
penetrating = 1
-/obj/projectile/ship_ammo/francisca/frag/on_impact(var/atom/A)
+/obj/projectile/ship_ammo/francisca/frag/on_hit(atom/target, blocked, def_zone, is_landmark_hit)
fragem(src, 70, 70, 1, 2, 10, 4, TRUE)
- ..()
+ . = ..()
diff --git a/code/modules/overmap/ship_weaponry/weaponry/leviathan.dm b/code/modules/overmap/ship_weaponry/weaponry/leviathan.dm
index 9bb6e628534..a3a00b9dee3 100644
--- a/code/modules/overmap/ship_weaponry/weaponry/leviathan.dm
+++ b/code/modules/overmap/ship_weaponry/weaponry/leviathan.dm
@@ -151,6 +151,8 @@
impact_type = /obj/effect/projectile/impact/pulse
/obj/projectile/ship_ammo/leviathan/on_hit(atom/target, blocked, def_zone, is_landmark_hit)
+ . = ..()
+
if(!is_landmark_hit)
if(ismob(target))
var/mob/M = target
diff --git a/code/modules/overmap/ship_weaponry/weaponry/longbow.dm b/code/modules/overmap/ship_weaponry/weaponry/longbow.dm
index b51b5cebddf..7a67cea9d8a 100644
--- a/code/modules/overmap/ship_weaponry/weaponry/longbow.dm
+++ b/code/modules/overmap/ship_weaponry/weaponry/longbow.dm
@@ -17,7 +17,7 @@
armor_penetration = 1000
var/penetrated = FALSE
-/obj/projectile/ship_ammo/longbow/launch_projectile(atom/target, target_zone, mob/user, params, angle_override, forced_spread)
+/obj/projectile/ship_ammo/longbow/fire_projectile(projectile_type, atom/target, sound, firer, list/ignore_targets)
if(ammo.impact_type == SHIP_AMMO_IMPACT_AP)
penetrating = 1
if(ammo.impact_type == SHIP_AMMO_IMPACT_BUNKERBUSTER)
diff --git a/code/modules/overmap/ship_weaponry/weaponry/longbow_ammo.dm b/code/modules/overmap/ship_weaponry/weaponry/longbow_ammo.dm
index e55d28c9898..4bfa18d2755 100644
--- a/code/modules/overmap/ship_weaponry/weaponry/longbow_ammo.dm
+++ b/code/modules/overmap/ship_weaponry/weaponry/longbow_ammo.dm
@@ -140,9 +140,12 @@
if(prob(10))
cookoff(FALSE)
-/obj/item/warhead/longbow/bullet_act(obj/projectile/P, def_zone)
+/obj/item/warhead/longbow/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
. = ..()
- if(P.damage > 5)
+ if(. != BULLET_ACT_HIT)
+ return .
+
+ if(hitting_projectile.damage > 5)
cookoff(TRUE)
/obj/item/warhead/longbow/attackby(obj/item/attacking_item, mob/user)
diff --git a/code/modules/overmap/ships/computers/sensors.dm b/code/modules/overmap/ships/computers/sensors.dm
index 9c2bfe0459b..821d18d99ed 100644
--- a/code/modules/overmap/ships/computers/sensors.dm
+++ b/code/modules/overmap/ships/computers/sensors.dm
@@ -503,9 +503,12 @@
else if(health < max_health * 0.75)
. += "\The [src] shows signs of damage!"
-/obj/machinery/shipsensors/bullet_act(var/obj/projectile/Proj)
- take_damage(Proj.get_structure_damage())
- ..()
+/obj/machinery/shipsensors/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
+ . = ..()
+ if(. != BULLET_ACT_HIT)
+ return .
+
+ take_damage(hitting_projectile.get_structure_damage())
/obj/machinery/shipsensors/proc/toggle()
if(use_power) // reset desired range when turning off
diff --git a/code/modules/overmap/ships/engines/gas_thruster.dm b/code/modules/overmap/ships/engines/gas_thruster.dm
index ed9aefa4b17..da18d6621e2 100644
--- a/code/modules/overmap/ships/engines/gas_thruster.dm
+++ b/code/modules/overmap/ships/engines/gas_thruster.dm
@@ -113,7 +113,9 @@
return 0
/obj/machinery/atmospherics/unary/engine/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
- return 0
+ if(mover?.movement_type & PHASING)
+ return TRUE
+ return FALSE
/obj/machinery/atmospherics/unary/engine/atmos_init()
..()
diff --git a/code/modules/power/fusion/core/_core.dm b/code/modules/power/fusion/core/_core.dm
index 13eef03f1c0..7b066db563d 100644
--- a/code/modules/power/fusion/core/_core.dm
+++ b/code/modules/power/fusion/core/_core.dm
@@ -55,9 +55,13 @@
owned_field.AddParticles(name, quantity)
. = 1
-/obj/machinery/power/fusion_core/bullet_act(obj/projectile/Proj)
+/obj/machinery/power/fusion_core/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
+ . = ..()
+ if(. != BULLET_ACT_HIT)
+ return .
+
if(owned_field)
- . = owned_field.bullet_act(Proj)
+ . = owned_field.bullet_act(hitting_projectile)
/obj/machinery/power/fusion_core/proc/set_strength(value)
value = clamp(value, MIN_FIELD_STR, MAX_FIELD_STR)
diff --git a/code/modules/power/fusion/core/core_field.dm b/code/modules/power/fusion/core/core_field.dm
index 13f9e7089fa..2764884d132 100644
--- a/code/modules/power/fusion/core/core_field.dm
+++ b/code/modules/power/fusion/core/core_field.dm
@@ -370,9 +370,7 @@
energy += a_energy
plasma_temperature += a_plasma_temperature
if(a_energy && percent_unstable > 0)
- percent_unstable -= a_energy/10000
- if(percent_unstable < 0)
- percent_unstable = 0
+ percent_unstable = max(percent_unstable - (a_energy/10000), 0)
while(energy >= 100)
energy -= 100
plasma_temperature += 1
@@ -582,10 +580,13 @@
STOP_PROCESSING(SSprocessing, src)
. = ..()
-/obj/effect/fusion_em_field/bullet_act(obj/projectile/Proj)
- AddEnergy(Proj.damage)
+/obj/effect/fusion_em_field/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
+ . = ..()
+ if(. != BULLET_ACT_HIT)
+ return .
+
+ AddEnergy(hitting_projectile.damage)
update_icon()
- return 0
/particles/fusion
width = 500
diff --git a/code/modules/power/fusion/fusion_particle_catcher.dm b/code/modules/power/fusion/fusion_particle_catcher.dm
index c7fecc3728f..a642fb4f88e 100644
--- a/code/modules/power/fusion/fusion_particle_catcher.dm
+++ b/code/modules/power/fusion/fusion_particle_catcher.dm
@@ -8,10 +8,11 @@
var/mysize = 0
/obj/effect/fusion_particle_catcher/Destroy()
- . =..()
parent.particle_catchers -= src
parent = null
+ . = ..()
+
/obj/effect/fusion_particle_catcher/proc/SetSize(newsize)
name = "collector [newsize]"
mysize = newsize
@@ -31,10 +32,15 @@
density = FALSE
name = "collector [mysize] OFF"
-/obj/effect/fusion_particle_catcher/bullet_act(obj/projectile/Proj)
- parent.AddEnergy(Proj.damage)
+/obj/effect/fusion_particle_catcher/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
+ . = ..()
+ if(. != BULLET_ACT_HIT)
+ return .
+
+ parent.AddEnergy(hitting_projectile.damage)
update_icon()
- return FALSE
/obj/effect/fusion_particle_catcher/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
+ if(mover?.movement_type & PHASING)
+ return TRUE
return ismob(mover)
diff --git a/code/modules/power/lights/fixtures.dm b/code/modules/power/lights/fixtures.dm
index 85bdc7510ac..050773e5672 100644
--- a/code/modules/power/lights/fixtures.dm
+++ b/code/modules/power/lights/fixtures.dm
@@ -507,8 +507,12 @@
else
user.visible_message(SPAN_WARNING("\The [user] hits \the [src], but it doesn't break."), SPAN_WARNING("You hit \the [src], but it doesn't break."), SPAN_WARNING("You hear something hitting against glass."))
-/obj/machinery/light/bullet_act(obj/projectile/P, def_zone)
- bullet_ping(P)
+/obj/machinery/light/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
+ . = ..()
+ if(. != BULLET_ACT_HIT)
+ return .
+
+ bullet_ping(hitting_projectile)
shatter()
// returns whether this light has power
diff --git a/code/modules/power/singularity/emitter.dm b/code/modules/power/singularity/emitter.dm
index 599df7a1bb7..4cdc8cce05a 100644
--- a/code/modules/power/singularity/emitter.dm
+++ b/code/modules/power/singularity/emitter.dm
@@ -158,7 +158,10 @@
var/obj/projectile/beam/emitter/A = get_emitter_beam()
A.damage = round(power_per_shot / EMITTER_DAMAGE_POWER_TRANSFER)
- A.launch_projectile(get_step(src, dir))
+
+ A.preparePixelProjectile(get_step(src, dir), get_turf(src))
+ A.fired_from = src
+ A.fire()
shot_counter++
/obj/machinery/power/emitter/attackby(obj/item/attacking_item, mob/user)
diff --git a/code/modules/power/singularity/field_generator.dm b/code/modules/power/singularity/field_generator.dm
index 21390d85b50..a60f1480fb9 100644
--- a/code/modules/power/singularity/field_generator.dm
+++ b/code/modules/power/singularity/field_generator.dm
@@ -160,11 +160,14 @@ field_generator power level display
return 0
-/obj/machinery/field_generator/bullet_act(var/obj/projectile/Proj)
- if(istype(Proj, /obj/projectile/beam))
- power += Proj.damage * 1.25 * EMITTER_DAMAGE_POWER_TRANSFER
+/obj/machinery/field_generator/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
+ . = ..()
+ if(. != BULLET_ACT_HIT)
+ return .
+
+ if(istype(hitting_projectile, /obj/projectile/beam))
+ power += hitting_projectile.damage * 1.25 * EMITTER_DAMAGE_POWER_TRANSFER
update_icon()
- return 0
/obj/machinery/field_generator/Destroy()
diff --git a/code/modules/power/singularity/singularity.dm b/code/modules/power/singularity/singularity.dm
index b7c4aa32396..88e775fa66c 100644
--- a/code/modules/power/singularity/singularity.dm
+++ b/code/modules/power/singularity/singularity.dm
@@ -71,8 +71,12 @@
energy += round((rand(20,60)/2),1)
return
-/obj/singularity/bullet_act(obj/projectile/P)
- return 0 //Will there be an impact? Who knows. Will we see it? No.
+/obj/singularity/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
+ . = ..()
+ if(. != BULLET_ACT_HIT)
+ return .
+
+ return BULLET_ACT_BLOCK //Will there be an impact? Who knows. Will we see it? No.
/obj/singularity/Collide(atom/A)
. = ..()
diff --git a/code/modules/power/smes_construction.dm b/code/modules/power/smes_construction.dm
index b3e94706cf0..d569fe750bc 100644
--- a/code/modules/power/smes_construction.dm
+++ b/code/modules/power/smes_construction.dm
@@ -135,10 +135,13 @@
wires = null
return ..()
-/obj/machinery/power/smes/buildable/bullet_act(obj/projectile/P, def_zone)
+/obj/machinery/power/smes/buildable/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
. = ..()
- visible_message(SPAN_WARNING("\The [src] is hit by \the [P]!"))
- health_check(P.damage)
+ if(. != BULLET_ACT_HIT)
+ return .
+
+ visible_message(SPAN_WARNING("\The [src] is hit by \the [hitting_projectile]!"))
+ health_check(hitting_projectile.damage)
/obj/machinery/power/smes/buildable/proc/health_check(var/health_reduction = 0)
health -= health_reduction
diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm
index 9559f2598a0..e0a57eff649 100644
--- a/code/modules/projectiles/gun.dm
+++ b/code/modules/projectiles/gun.dm
@@ -428,12 +428,13 @@
var/disp = dispersion[min(i, dispersion.len)]
P.accuracy = accuracy + acc
- P.dispersion = disp
+ P.spread += disp
- P.shot_from = src.name
P.suppressed = suppressed
- P.launch_projectile(target)
+ P.preparePixelProjectile(target, get_turf(src))
+ P.fired_from = src
+ P.fire()
handle_post_fire() // should be safe to not include arguments here, as there are failsafes in effect (?)
@@ -539,7 +540,7 @@
//Accuracy modifiers
P.accuracy = accuracy + acc_mod
- P.dispersion = dispersion
+ P.spread += dispersion
//Increasing accuracy across the board, ever so slightly
P.accuracy += 1
@@ -556,7 +557,7 @@
F = firemodes[sel_mode]
if(one_hand_fa_penalty > 2 && !wielded && F?.name == "full auto") // todo: make firemode names defines
P.accuracy -= one_hand_fa_penalty * 0.5
- P.dispersion -= one_hand_fa_penalty * 0.5
+ P.spread -= one_hand_fa_penalty * 0.5
//does the actual launching of the projectile
/obj/item/gun/proc/process_projectile(obj/projectile, mob/user, atom/target, target_zone, params)
@@ -573,7 +574,12 @@
else if(mob.shock_stage > 70)
added_spread = 15
- return !P.launch_from_gun(target, target_zone, user, params, null, added_spread, src)
+ P.preparePixelProjectile(target, src, deviation = added_spread)
+ P.firer = user
+ P.fired_from = src
+ P.def_zone = target_zone
+
+ return !P.fire()
//Suicide handling.
/obj/item/gun/var/mouthshoot = FALSE //To stop people from suiciding twice... >.>
diff --git a/code/modules/projectiles/guns/energy/mining.dm b/code/modules/projectiles/guns/energy/mining.dm
index 01212187909..ecbaf932f7e 100644
--- a/code/modules/projectiles/guns/energy/mining.dm
+++ b/code/modules/projectiles/guns/energy/mining.dm
@@ -80,19 +80,18 @@
muzzle_type = /obj/effect/projectile/muzzle/plasma_cutter
tracer_type = /obj/effect/projectile/tracer/plasma_cutter
impact_type = /obj/effect/projectile/impact/plasma_cutter
- maiming = TRUE
maim_rate = 1
/obj/projectile/beam/plasmacutter/proc/pass_check(var/turf/simulated/mineral/mine_turf)
if(mineral_passes <= 0)
return list(null, FALSE) // the projectile stops
mineral_passes--
- var/mineral_destroyed = on_impact(mine_turf)
- return list(PROJECTILE_CONTINUE, mineral_destroyed) // the projectile tunnels deeper
+ var/mineral_destroyed = on_hit(mine_turf)
+ return list(BULLET_ACT_HIT, mineral_destroyed) // the projectile tunnels deeper
-/obj/projectile/beam/plasmacutter/on_impact(var/atom/A)
- if(istype(A, /turf/simulated/mineral))
- var/turf/simulated/mineral/M = A
+/obj/projectile/beam/plasmacutter/on_hit(atom/target, blocked, def_zone)
+ if(istype(target, /turf/simulated/mineral))
+ var/turf/simulated/mineral/M = target
if(prob(33))
M.GetDrilled(1)
return TRUE
diff --git a/code/modules/projectiles/guns/projectile/dartgun.dm b/code/modules/projectiles/guns/projectile/dartgun.dm
index 5fe14641488..71fe48983a2 100644
--- a/code/modules/projectiles/guns/projectile/dartgun.dm
+++ b/code/modules/projectiles/guns/projectile/dartgun.dm
@@ -13,7 +13,8 @@
reagents = new/datum/reagents(reagent_amount)
reagents.my_atom = src
-/obj/projectile/bullet/chemdart/on_hit(var/atom/target, var/blocked = 0, var/def_zone = null)
+/obj/projectile/bullet/chemdart/on_hit(atom/target, blocked, def_zone)
+ . = ..()
if(blocked < 100 && ishuman(target))
var/mob/living/carbon/human/H = target
if(H.can_inject(target_zone=def_zone))
diff --git a/code/modules/projectiles/guns/projectile/revolver.dm b/code/modules/projectiles/guns/projectile/revolver.dm
index 5446ede5746..53dedf75828 100644
--- a/code/modules/projectiles/guns/projectile/revolver.dm
+++ b/code/modules/projectiles/guns/projectile/revolver.dm
@@ -300,8 +300,8 @@
if(default_parry_check(user, attacker, damage_source) && prob(20))
user.visible_message(SPAN_DANGER("\The [user] parries [attack_text] with \the [src]!"))
playsound(user.loc, "punchmiss", 50, 1)
- return PROJECTILE_STOPPED
- return FALSE
+ return BULLET_ACT_BLOCK
+ return BULLET_ACT_HIT
/obj/item/gun/projectile/revolver/konyang/pirate
name = "reclaimed revolver"
diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm
index fcc21c57c31..eeb2c78b3c0 100644
--- a/code/modules/projectiles/projectile.dm
+++ b/code/modules/projectiles/projectile.dm
@@ -1,36 +1,186 @@
-#define MUZZLE_EFFECT_PIXEL_INCREMENT 16 //How many pixels to move the muzzle flash up so your character doesn't look like they're shitting out lasers.
+#define MOVES_HITSCAN -1 //Not actually hitscan but close as we get without actual hitscan.
+#define MUZZLE_EFFECT_PIXEL_INCREMENT 17 //How many pixels to move the muzzle flash up so your character doesn't look like they're shitting out lasers.
+#define MAX_RANGE_HIT_PRONE_TARGETS 10 //How far do the projectile hits the prone mob
/obj/projectile
name = "projectile"
icon = 'icons/obj/projectiles.dmi'
icon_state = "bullet"
- density = TRUE
- unacidable = TRUE
+ density = FALSE
anchored = TRUE //There's a reason this is here, Mport. God fucking damn it -Agouri. Find&Fix by Pete. The reason this is here is to stop the curving of emitter shots.
- pass_flags = PASSTABLE|PASSRAILING
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
- animate_movement = 0 //Use SLIDE_STEPS in conjunction with legacy
- var/projectile_type = /obj/projectile
+ movement_type = FLYING
+ blocks_emissive = EMISSIVE_BLOCK_GENERIC
+ layer = MOB_LAYER
+ var/hitsound_wall = ""
+
+ unacidable = TRUE //should be `resistance_flags` but we don't have it yet
+ var/def_zone = "" //Aiming at
+ var/atom/movable/firer = null//Who shot it
+ var/datum/fired_from = null // the thing that the projectile was fired from (gun, turret, spell)
+ var/suppressed = FALSE //Attack message
+ var/yo = null
+ var/xo = null
+ var/atom/original // the original target clicked
+ var/turf/starting // the projectile's starting turf
+ var/p_x = 16
+ var/p_y = 16 // the pixel location of the tile that the player clicked. Default is the center
+
+ //Fired processing vars
+ var/fired = FALSE //Have we been fired yet
+ var/paused = FALSE //for suspending the projectile midair
+ var/last_projectile_move = 0
+ var/last_process = 0
+ 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 = list()
+ /// 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 result in can_hit_target being false unless directly clicked on - similar to projectile_phasing but without even going to process_hit.
+ * 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|PASSRAILING
+ /// 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
+
+ /// If objects are below this layer, we pass through them
+ var/hit_threshhold = PROJECTILE_HIT_THRESHHOLD_LAYER
+
+ /// During each fire of SSprojectiles, the number of deciseconds since the last fire of SSprojectiles
+ /// is divided by this var, and the result truncated to the next lowest integer is
+ /// the number of times the projectile's `pixel_move` proc will be called.
+ var/speed = 0.2
+
+ /// This var is multiplied by SSprojectiles.global_pixel_speed to get how many pixels
+ /// the projectile moves during each iteration of the movement loop
+ ///
+ /// If you want to make a fast-moving projectile, you should keep this equal to 1 and
+ /// reduce the value of `speed`. If you want to make a slow-moving projectile, make
+ /// `speed` a modest value like 1 and set this to a low value like 0.2.
+ var/pixel_speed_multiplier = 1
+
+ /// The current angle of the projectile. Initially null, so if the arg is missing from [/fire()], we can calculate it from firer and target as fallback.
+ var/Angle
+ var/original_angle = 0 //Angle at firing
+ var/nondirectional_sprite = FALSE //Set TRUE to prevent projectiles from having their sprites rotated based on firing angle
+ var/spread = 0 //amount (in degrees) of projectile spread
+ animate_movement = NO_STEPS //Use SLIDE_STEPS in conjunction with legacy
+ /// how many times we've ricochet'd so far (instance variable, not a stat)
+ var/ricochets = 0
+ /// how many times we can ricochet max
+ var/ricochets_max = 0
+ /// how many times we have to ricochet min (unless we hit an atom we can ricochet off)
+ var/min_ricochets = 0
+ /// 0-100 (or more, I guess), the base chance of ricocheting, before being modified by the atom we shoot and our chance decay
+ var/ricochet_chance = 0
+ /// 0-1 (or more, I guess) multiplier, the ricochet_chance is modified by multiplying this after each ricochet
+ var/ricochet_decay_chance = 0.7
+ /// 0-1 (or more, I guess) multiplier, the projectile's damage is modified by multiplying this after each ricochet
+ var/ricochet_decay_damage = 0.7
+ /// On ricochet, if nonzero, we consider all mobs within this range of our projectile at the time of ricochet to home in on like Revolver Ocelot, as governed by ricochet_auto_aim_angle
+ var/ricochet_auto_aim_range = 0
+ /// On ricochet, if ricochet_auto_aim_range is nonzero, we'll consider any mobs within this range of the normal angle of incidence to home in on, higher = more auto aim
+ var/ricochet_auto_aim_angle = 30
+ /// the angle of impact must be within this many degrees of the struck surface, set to 0 to allow any angle
+ var/ricochet_incidence_leeway = 40
+ /// Can our ricochet autoaim hit our firer?
+ var/ricochet_shoots_firer = TRUE
+
+ //Hitscan
+ var/hitscan = FALSE //Whether this is hitscan. If it is, speed is basically ignored.
+ var/list/beam_segments //assoc list of datum/point or datum/point/vector, start = end. Used for hitscan effect generation.
+ /// Last turf an angle was changed in for hitscan projectiles.
+ var/turf/last_angle_set_hitscan_store
+ var/datum/point/beam_index
+ var/turf/hitscan_last //last turf touched during hitscanning.
+ var/tracer_type
+ var/muzzle_type
+ var/impact_type
+
+ //Fancy hitscan lighting effects!
+ var/hitscan_light_intensity = 1.5
+ var/hitscan_light_range = 0.75
+ var/hitscan_light_color_override
+ var/muzzle_flash_intensity = 3
+ var/muzzle_flash_range = 1.5
+ var/muzzle_flash_color_override
+ var/impact_light_intensity = 3
+ var/impact_light_range = 2
+ var/impact_light_color_override
+
+ //Homing
+ var/homing = FALSE
+ var/atom/homing_target
+ var/homing_turn_speed = 10 //Angle per tick.
+ var/homing_inaccuracy_min = 0 //in pixels for these. offsets are set once when setting target.
+ var/homing_inaccuracy_max = 0
+ var/homing_offset_x = 0
+ var/homing_offset_y = 0
+
+ var/damage = 10
+ var/damage_type = DAMAGE_BRUTE //DAMAGE_BRUTE, DAMAGE_BURN, DAMAGE_TOXIN, DAMAGE_OXY, DAMAGE_CLONE, DAMAGE_PAIN are the only things that should be in here
+
+ var/range = 50 //This will de-increment every step. When 0, it will deletze the projectile.
+ var/decayedRange //stores original range
+ var/reflect_range_decrease = 5 //amount of original range that falls off when reflecting, so it doesn't go forever
+
+ 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)
+ /// If true, the projectile won't cause any logging. Used for hallucinations and shit.
+ var/do_not_log = FALSE
+
+ var/shrapnel_type //type of shrapnel the projectile leaves in its target.
+
+ ///If TRUE, hit mobs, even if they are lying on the floor and are not our target within MAX_RANGE_HIT_PRONE_TARGETS tiles
+ var/hit_prone_targets = FALSE
+ ///if TRUE, ignores the range of MAX_RANGE_HIT_PRONE_TARGETS tiles of hit_prone_targets
+ var/ignore_range_hit_prone_targets = FALSE
+ ///How much we want to drop damage per tile as it travels through the air
+ var/damage_falloff_tile
+ ///How much accuracy is lost for each tile travelled
+ var/accuracy_falloff = 7
+ ///How much accuracy before falloff starts to matter. Formula is range - falloff * tiles travelled
+ var/accurate_range = 100
+ var/static/list/projectile_connections = list(COMSIG_ATOM_ENTERED = PROC_REF(on_entered))
+ /// If true directly targeted turfs can be hit
+ var/can_hit_turfs = FALSE
+
+
+ /*#########################################
+ START AURORA SNOWFLAKE VARS SECTION
+ #########################################*/
+
var/ping_effect = "ping_b" //Effect displayed when a bullet hits a barricade. See atom/proc/bullet_ping.
- var/def_zone = "" //Aiming at
- var/hit_zone // The place that actually got hit
- var/mob/firer = null//Who shot it
- var/suppressed = FALSE //Attack message
-
- var/shot_from = "" // name of the object which shot us
-
+ ///How accurate a bullet is *if it's hitting a mob* at getting the zone aimed at
var/accuracy = 0
- var/dispersion = 0.0
//used for shooting at blank range, you shouldn't be able to miss
var/point_blank = FALSE
//Effects
- var/damage = 10
- var/damage_type = DAMAGE_BRUTE //DAMAGE_BRUTE, DAMAGE_BURN, DAMAGE_TOXIN, DAMAGE_OXY, DAMAGE_CLONE, DAMAGE_PAIN are the only things that should be in here
var/damage_flags = DAMAGE_FLAG_BULLET
- var/nodamage = FALSE //Determines if the projectile will skip any damage inflictions
var/check_armor = "bullet" //Defines what armor to use when it hits things. Must be set to bullet, laser, energy,or bomb //Cael - bio and rad are also valid
var/list/impact_sounds //for different categories, IMPACT_MEAT etc
@@ -46,95 +196,894 @@
var/incinerate = 0
var/embed = 0 // whether or not the projectile can embed itself in the mob
var/embed_chance = 0 // a flat bonus to the % chance to embed
- var/shrapnel_type //type of shrapnel the projectile leaves in its target.
-
- var/p_x = 16
- var/p_y = 16 // the pixel location of the tile that the player clicked. Default is the center
//For Maim / Maiming.
- var/maiming = 0 //Enables special limb dismemberment calculation; used primarily for ranged weapons that can maim, but do not do brute damage.
var/maim_rate = 0 //Factor that the recipiant will be maimed by the projectile (NOT OUT OF 100%.)
- var/clean_cut = 0 //Is the delimbning painful and unclean? Probably. Can be a function or proc, if you're doing something odd.
- var/maim_type = DROPLIMB_EDGE
- /*Does the projectile simply lop/tear the limb off, or does it vaporize it?
- Set maim_type to DROPLIMB_EDGE to chop off the limb
- set maim_type to DROPLIMB_BURN to vaporize it.
- set maim_type to DROPLIMB_BLUNT to gib (Explode/Hamburger) the limb.
- */
- //Movement parameters
- var/speed = 0.2 //Amount of deciseconds it takes for projectile to travel
- var/pixel_speed = 33 //pixels per move - DO NOT FUCK WITH THIS UNLESS YOU ABSOLUTELY KNOW WHAT YOU ARE DOING OR UNEXPECTED THINGS /WILL/ HAPPEN!
- var/Angle = 0
- var/original_angle = 0 //Angle at firing
- var/nondirectional_sprite = FALSE //Set TRUE to prevent projectiles from having their sprites rotated based on firing angle
- var/yo = null
- var/xo = null
- var/atom/original // the target clicked (not necessarily where the projectile is headed). Should probably be renamed to 'target' or something.
- var/turf/starting // the projectile's starting turf
- var/list/permutated // we've passed through these atoms, don't try to hit them again
- var/penetrating = 0 //If greater than zero, the projectile will pass through dense objects as specified by on_penetrate()
- var/forcedodge = FALSE //to pass through everything
- var/ignore_source_check = FALSE
-
- //Fired processing vars
- var/fired = FALSE //Have we been fired yet
- var/paused = FALSE //for suspending the projectile midair
var/reflected = FALSE
- var/last_projectile_move = 0
- var/last_process = 0
- 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!
- var/range = 50 //This will de-increment every step. When 0, it will deletze the projectile.
+
+ var/penetrating = 0 //If greater than zero, the projectile will pass through dense objects as specified by on_penetrate()
+
+
var/aoe = 0 //For KAs, really
- //Hitscan
- var/hitscan = FALSE //Whether this is hitscan. If it is, speed is basically ignored.
- var/list/beam_segments //assoc list of datum/point or datum/point/vector, start = end. Used for hitscan effect generation.
- var/datum/point/beam_index
- var/turf/hitscan_last //last turf touched during hitscanning.
- var/tracer_type
- var/muzzle_type
- var/impact_type
- var/hit_effect
var/anti_materiel_potential = 1 //how much the damage of this bullet is increased against mechs
- var/iff // identify friend or foe. will check mob's IDs to see if they match, if they do, won't hit
-
///If the projectile launches a secondary projectile in addition to itself.
var/secondary_projectile
-/obj/projectile/CanPass()
+ /*########################################
+ END AURORA SNOWFLAKE VARS SECTION
+ ########################################*/
+
+/obj/projectile/Initialize()
+ . = ..()
+ decayedRange = range
+ AddElement(/datum/element/connect_loc, projectile_connections)
+
+/obj/projectile/proc/Range()
+ range--
+ if(damage_falloff_tile && damage >= 0)
+ damage += damage_falloff_tile
+ // if(stamina_falloff_tile && stamina >= 0)
+ // stamina += stamina_falloff_tile
+
+ SEND_SIGNAL(src, COMSIG_PROJECTILE_RANGE)
+ if(range <= 0 && loc)
+ on_range()
+
+ // if(damage_falloff_tile && damage <= 0 || stamina_falloff_tile && stamina <= 0)
+ if(damage_falloff_tile && damage <= 0)
+ on_range()
+
+/obj/projectile/proc/on_range() //if we want there to be effects when they reach the end of their range
+ SEND_SIGNAL(src, COMSIG_PROJECTILE_RANGE_OUT)
+ qdel(src)
+
+/**
+ * Called when the projectile hits something
+ *
+ * _NOT THE SAME OF TG_
+ *
+ * By default parent call will always return [BULLET_ACT_HIT] (unless qdeleted)
+ * so it is save to assume a successful hit in children (though not necessarily successfully damaged - it could've been blocked)
+ *
+ * Arguments
+ * * target - thing hit
+ * * blocked - percentage of hit blocked (0 to 100)
+ * * pierce_hit - boolean, are we piercing through or regular hitting - NOT THERE YET
+ *
+ * Returns
+ * * Returns [BULLET_ACT_HIT] if we hit something. Default return value.
+ * * Returns [BULLET_ACT_BLOCK] if we were hit but sustained no effects (blocked it). Note, Being "blocked" =/= "blocked is 100".
+ * * Returns [BULLET_ACT_FORCE_PIERCE] to have the projectile keep going instead of "hitting", as if we were not hit at all.
+ */
+/obj/projectile/proc/on_hit(atom/target, blocked = 0, var/def_zone = null)
+ SHOULD_CALL_PARENT(TRUE)
+
+ if(fired_from)
+ SEND_SIGNAL(fired_from, COMSIG_PROJECTILE_ON_HIT, firer, target, Angle, def_zone, blocked)
+ SEND_SIGNAL(src, COMSIG_PROJECTILE_SELF_ON_HIT, firer, target, Angle, def_zone, blocked)
+
+ if(QDELETED(src)) // in case one of the above signals deleted the projectile for whatever reason
+ return BULLET_ACT_BLOCK
+ var/turf/target_turf = get_turf(target)
+
+ var/hitx
+ var/hity
+ if(target == original)
+ hitx = target.pixel_x + p_x - 16
+ hity = target.pixel_y + p_y - 16
+ else
+ hitx = target.pixel_x + rand(-8, 8)
+ hity = target.pixel_y + rand(-8, 8)
+
+ if(isturf(target_turf) && hitsound_wall)
+ var/volume = clamp(vol_by_damage() + 20, 0, 100)
+ if(suppressed)
+ volume = 5
+ playsound(loc, hitsound_wall, volume, TRUE, -1)
+
+ if(blocked >= 100) //Full block
+ return BULLET_ACT_BLOCK
+
+ if(!isliving(target))
+ if(impact_effect_type && !hitscan)
+ new impact_effect_type(target_turf, hitx, hity)
+ return BULLET_ACT_HIT
+
+ var/mob/living/living_target = target
+
+ living_target.apply_effects(0, weaken, paralyze, 0, stutter, eyeblur, drowsy, 0, incinerate, blocked)
+ living_target.stun_effect_act(stun, agony, def_zone, src, damage_flags)
+ living_target.apply_damage(irradiate, DAMAGE_RADIATION, damage_flags = DAMAGE_FLAG_DISPERSED) //radiation protection is handled separately from other armor types.
+
+ if(!do_not_log)
+ log_combat(firer, living_target, "shot", src)
+ //Because `admin_attack_log` expects both to be mobs
+ if(ismob(target) && ismob(firer))
+ admin_attack_log(firer, living_target, "shot with \a [src.type]", "shot with \a [src.type]", "shot (\a [src.type])")
+
+ return BULLET_ACT_HIT
+
+/obj/projectile/proc/vol_by_damage()
+ if(src.damage)
+ return clamp((src.damage) * 0.67, 30, 100)// Multiply projectile damage by 0.67, then CLAMP the value between 30 and 100
+ else
+ return 50 //if the projectile doesn't do damage, play its hitsound at 50% volume
+
+/obj/projectile/proc/on_ricochet(atom/A)
+ if(!ricochet_auto_aim_angle || !ricochet_auto_aim_range)
+ return
+
+ var/mob/living/unlucky_sob
+ var/best_angle = ricochet_auto_aim_angle
+ if(firer && HAS_TRAIT(firer, TRAIT_NICE_SHOT))
+ best_angle += NICE_SHOT_RICOCHET_BONUS
+ for(var/mob/living/L in range(ricochet_auto_aim_range, src.loc))
+ if(L.stat == DEAD || !is_in_sight(src, L) || (!ricochet_shoots_firer && L == firer))
+ continue
+ var/our_angle = abs(closer_angle_difference(Angle, get_angle(src.loc, L.loc)))
+ if(our_angle < best_angle)
+ best_angle = our_angle
+ unlucky_sob = L
+
+ if(unlucky_sob)
+ set_angle(get_angle(src, unlucky_sob.loc))
+
+/obj/projectile/proc/store_hitscan_collision(datum/point/point_cache)
+ beam_segments[beam_index] = point_cache
+ beam_index = point_cache
+ beam_segments[beam_index] = null
+
+/obj/projectile/Collide(atom/A)
+ SEND_SIGNAL(src, COMSIG_MOVABLE_BUMP, A)
+ if(!can_hit_target(A, A == original, TRUE, 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 FALSE
+ if(impacted[A.weak_reference]) // NEVER doublehit
+ return FALSE
+ var/datum/point/point_cache = 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)
+ 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
+ // stamina *= ricochet_decay_damage
+ range = decayedRange
+ if(hitscan)
+ store_hitscan_collision(point_cache)
+ return TRUE
+
+ 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.
+ // Originally was only `def_zone = ran_zone(def_zone, max(100-(7*distance), 5)) //Lower accurancy/longer range tradeoff. 7 is a balanced number to use.`
+ //Because snowflake aurora BS, this is how we calculate what to hit if anything with a mob
+ if(ismob(A))
+ var/miss_modifier = max(15*(distance-1) - round(25*accuracy), 0)
+ def_zone = get_zone_with_miss_chance(def_zone, A, miss_modifier, (distance > 1 || original != A), point_blank)
+ else
+ def_zone = ran_zone(def_zone, clamp(accurate_range - (accuracy_falloff * distance), 5, 100)) //Lower accurancy/longer range tradeoff. 7 is a balanced number to use.
+
+ return process_hit(T, select_target(T, A, A), A) // SELECT TARGET FIRST!
+
+/**
+ * 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
+ * bumped - target we originally bumped. it's here to ensure that if something blocks our projectile by means of Cross() failure, we hit it
+ * even if it is not dense.
+ * 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, atom/bumped, hit_something = FALSE)
+ // 1.
+ if(QDELETED(src) || !T || !target)
+ return
+ // 2.
+ impacted[WEAKREF(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, bumped), bumped, hit_something) // try to hit something else
+ // at this point we are going to hit the thing
+ // in which case send signal to it
+ if ((SEND_SIGNAL(target, COMSIG_PROJECTILE_PREHIT, args, src) & PROJECTILE_INTERRUPT_HIT) || (SEND_SIGNAL(src, COMSIG_PROJECTILE_SELF_PREHIT, args) & PROJECTILE_INTERRUPT_HIT))
+ qdel(src)
+ return BULLET_ACT_BLOCK
+ 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, bumped), bumped, TRUE)
+ qdel(src)
+ return hit_something
+
+/**
+ * Selects a target to hit from a turf
+ *
+ * @params
+ * T - The turf
+ * target - The "preferred" atom to hit, usually what we Bumped() first.
+ * bumped - used to track if something is the reason we impacted in the first place.
+ * If set, this atom is always treated as dense by can_hit_target.
+ *
+ * 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. Special check on what we bumped to see if it's a border object that intercepts hitting anything behind it
+ * 2. The thing originally aimed at/clicked on
+ * 3. Mobs - picks lowest buckled mob to prevent scarp piggybacking memes
+ * 4. Objs
+ * 5. Turf
+ * 6. Nothing
+ */
+/obj/projectile/proc/select_target(turf/our_turf, atom/target, atom/bumped)
+ // 1. special bumped border object check
+ // if((bumped?.flags_1 & ON_BORDER_1) && can_hit_target(bumped, original == bumped, TRUE, TRUE))
+ if((bumped?.atom_flags & ATOM_FLAG_CHECKS_BORDER) && can_hit_target(bumped, original == bumped, TRUE, TRUE))
+ return bumped
+ // 2. original
+ if(can_hit_target(original, TRUE, FALSE, original == bumped))
+ return original
+ var/list/atom/considering = list() // let's define this ONCE
+ // 3. mobs
+ for(var/mob/living/iter_possible_target in our_turf)
+ if(can_hit_target(iter_possible_target, iter_possible_target == original, TRUE, iter_possible_target == bumped))
+ considering |= iter_possible_target
+ if(length(considering))
+ return pick(considering)
+ // 4. objs and other dense things
+ for(var/i in our_turf)
+ if(can_hit_target(i, i == original, TRUE, i == bumped))
+ considering += i
+ if(length(considering))
+ return pick(considering)
+ // 5. turf
+ if(can_hit_target(our_turf, our_turf == original, TRUE, our_turf == bumped))
+ return our_turf
+ // 6. 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, cross_failed = FALSE)
+ if(QDELETED(target) || impacted[target.weak_reference])
+ return FALSE
+ if(!ignore_loc && (loc != target.loc) && !(can_hit_turfs && direct_target && loc == target))
+ return FALSE
+ // if pass_flags match, pass through entirely - unless direct target is set.
+ if((target.pass_flags_self & pass_flags) && !direct_target)
+ return FALSE
+ if(HAS_TRAIT(target, TRAIT_UNHITTABLE_BY_PROJECTILES))
+ if(!HAS_TRAIT(target, TRAIT_BLOCKING_PROJECTILES) && isliving(target))
+ var/mob/living/living_target = target
+ living_target.block_projectile_effects()
+ return FALSE
+ if(target.density || cross_failed) //This thing blocks projectiles, hit it regardless of layer/mob stuns/etc.
+ return TRUE
+ if(!isliving(target))
+ if(isturf(target)) // non dense turfs
+ return can_hit_turfs && direct_target
+ if(target.layer < hit_threshhold)
+ return FALSE
+ else if(!direct_target) // non dense objects do not get hit unless specifically clicked
+ return FALSE
+ else
+ var/mob/living/living_target = target
+ if(direct_target)
+ return TRUE
+ if(living_target.stat == DEAD)
+ return FALSE
+ // if(HAS_TRAIT(living_target, TRAIT_IMMOBILIZED) && HAS_TRAIT(living_target, TRAIT_FLOORED) && HAS_TRAIT(living_target, TRAIT_HANDS_BLOCKED))
+ // return FALSE
+ if(hit_prone_targets)
+ var/mob/living/buckled_to = living_target.lowest_buckled_mob()
+ if((decayedRange - range) <= MAX_RANGE_HIT_PRONE_TARGETS) // after MAX_RANGE_HIT_PRONE_TARGETS tiles, auto-aim hit for mobs on the floor turns off
+ return TRUE
+ if(ignore_range_hit_prone_targets) // doesn't apply to projectiles that must hit the target in combat mode or something else, no matter what
+ return TRUE
+ if(buckled_to.density) // Will just be us if we're not buckled to another mob
+ return TRUE
+ return FALSE
+ // else if(living_target.body_position == LYING_DOWN)
+ else if(living_target.lying)
+ return FALSE
+ return (target && (loc == get_turf(target)))
+
+/**
+ * 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/proc/on_entered(datum/source, atom/movable/AM)
+ SIGNAL_HANDLER
+ scan_crossed_hit(AM)
+
+/**
+ * Projectile can pass through
+ * Used to not even attempt to Bump() or fail to Cross() anything we already hit.
+ *
+ * This was called CanPassThrough() on TG but we don't have it yet
+ */
+/obj/projectile/CanPass(atom/blocker, movement_dir, blocker_opinion)
+ return ..() || impacted[blocker.weak_reference]
+
+/**
+ * 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/old_loc, movement_dir, forced, list/old_locs, momentum_change = TRUE)
+ . = ..()
+ 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) && (phasing_ignore_direct_target || original != A))
+ 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
+ if(firer && HAS_TRAIT(firer, TRAIT_NICE_SHOT))
+ chance += NICE_SHOT_RICOCHET_BONUS
+ if(ricochets < min_ricochets || prob(chance))
+ return TRUE
+ return FALSE
+
+/obj/projectile/proc/check_ricochet_flag(atom/A)
+ // if((armor_flag in list(ENERGY, LASER)) && (A.flags_ricochet & RICOCHET_SHINY))
+ // return TRUE
+
+ // if((armor_flag in list(BOMB, BULLET)) && (A.flags_ricochet & RICOCHET_HARD))
+ // return TRUE
+
+ //Keep it easy for now
+ if(A.flags_ricochet & RICOCHET_SHINY|RICOCHET_HARD)
+ return TRUE
+
+ return FALSE
+
+/obj/projectile/proc/return_predicted_turf_after_moves(moves, forced_angle) //I say predicted because there's no telling that the projectile won't change direction/location in flight.
+ if(!trajectory && isnull(forced_angle) && isnull(Angle))
+ return FALSE
+ var/datum/point/vector/current = trajectory
+ if(!current)
+ var/turf/T = get_turf(src)
+ current = new(T.x, T.y, T.z, pixel_x, pixel_y, isnull(forced_angle)? Angle : forced_angle, SSprojectiles.global_pixel_speed)
+ var/datum/point/vector/v = current.return_vector_after_increments(moves * SSprojectiles.global_iterations_per_move)
+ return v.return_turf()
+
+/obj/projectile/proc/return_pathing_turfs_in_moves(moves, forced_angle)
+ var/turf/current = get_turf(src)
+ var/turf/ending = return_predicted_turf_after_moves(moves, forced_angle)
+ return get_line(current, ending)
+
+/obj/projectile/Process_Spacemove(movement_dir = 0, continuous_move = FALSE)
+ return TRUE //Bullets don't drift in space
+
+/obj/projectile/process()
+ last_process = world.time
+ if(!loc || !fired || !trajectory)
+ fired = FALSE
+ return PROCESS_KILL
+ if(paused || !isturf(loc))
+ last_projectile_move += world.time - last_process //Compensates for pausing, so it doesn't become a hitscan projectile when unpaused from charged up ticks.
+ return
+ var/elapsed_time_deciseconds = (world.time - last_projectile_move) + time_offset
+ time_offset = 0
+ var/required_moves = speed > 0? FLOOR(elapsed_time_deciseconds / speed, 1) : MOVES_HITSCAN //Would be better if a 0 speed made hitscan but everyone hates those so I can't make it a universal system :<
+ if(required_moves == MOVES_HITSCAN)
+ required_moves = SSprojectiles.global_max_tick_moves
+ else
+ if(required_moves > SSprojectiles.global_max_tick_moves)
+ var/overrun = required_moves - SSprojectiles.global_max_tick_moves
+ required_moves = SSprojectiles.global_max_tick_moves
+ time_offset += overrun * speed
+ time_offset += MODULUS(elapsed_time_deciseconds, speed)
+ SEND_SIGNAL(src, COMSIG_PROJECTILE_BEFORE_MOVE)
+ for(var/i in 1 to required_moves)
+ pixel_move(pixel_speed_multiplier, FALSE)
+
+/obj/projectile/proc/fire(angle, atom/direct_target)
+ LAZYINITLIST(impacted)
+ if(fired_from)
+ SEND_SIGNAL(fired_from, COMSIG_PROJECTILE_BEFORE_FIRE, src, original)
+ if(firer)
+ SEND_SIGNAL(firer, COMSIG_PROJECTILE_FIRER_BEFORE_FIRE, src, fired_from, original)
+ if(!log_override && firer && original && !do_not_log)
+ log_combat(firer, original, "fired at", src, "from [get_area_name(src, TRUE)]")
+ //note: mecha projectile logging is handled in /obj/item/mecha_parts/mecha_equipment/weapon/action(). try to keep these messages roughly the sameish just for consistency's sake.
+ 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
+ var/turf/starting = get_turf(src)
+ if(isnum(angle))
+ set_angle(angle)
+ else if(isnull(Angle)) //Try to resolve through offsets if there's no angle set.
+ if(isnull(xo) || isnull(yo))
+ stack_trace("WARNING: Projectile [type] deleted due to being unable to resolve a target after angle was null!")
+ qdel(src)
+ return
+ var/turf/target = locate(clamp(starting + xo, 1, world.maxx), clamp(starting + yo, 1, world.maxy), starting.z)
+ set_angle(get_angle(src, target))
+ if(spread)
+ set_angle(Angle + (rand() - 0.5) * spread)
+ original_angle = Angle
+ trajectory_ignore_forcemove = TRUE
+ forceMove(starting)
+ trajectory_ignore_forcemove = FALSE
+ trajectory = new(starting.x, starting.y, starting.z, pixel_x, pixel_y, Angle, SSprojectiles.global_pixel_speed)
+ last_projectile_move = world.time
+ fired = TRUE
+ // play_fov_effect(starting, 6, "gunfire", dir = NORTH, angle = Angle)
+ SEND_SIGNAL(src, COMSIG_PROJECTILE_FIRE)
+ if(hitscan)
+ process_hitscan()
+ if(QDELETED(src))
+ return
+ if(!(datum_flags & DF_ISPROCESSING))
+ START_PROCESSING(SSprojectiles, src)
+ pixel_move(pixel_speed_multiplier, FALSE) //move it now!
+
+/obj/projectile/set_angle(new_angle) //wrapper for overrides.
+ // . = ..() DO NOT CALL PARENT
+
+ if(!nondirectional_sprite)
+ transform = transform.TurnTo(Angle, new_angle)
+ Angle = new_angle
+ if(trajectory)
+ trajectory.set_angle(new_angle)
+ if(fired && hitscan && isloc(loc) && (loc != last_angle_set_hitscan_store))
+ last_angle_set_hitscan_store = loc
+ var/datum/point/point_cache = new (src)
+ point_cache = trajectory.copy_to()
+ store_hitscan_collision(point_cache)
return TRUE
-//TODO: make it so this is called more reliably, instead of sometimes by bullet_act() and sometimes not
-/obj/projectile/proc/on_hit(var/atom/target, var/blocked = 0, var/def_zone = null)
- if(blocked >= 100) //Full block
- return FALSE
- if(!isliving(target))
- return FALSE
- if(isanimal(target))
- return FALSE
- var/mob/living/L = target
- if(damage_type == DAMAGE_BRUTE && damage > 5) //weak hits shouldn't make you gush blood
- var/splatter_color = COLOR_HUMAN_BLOOD
- var/mob/living/carbon/human/H = target
- if (istype(H) && H.species && H.get_blood_color())
- splatter_color = H.get_blood_color()
- var/splatter_dir = starting ? get_dir(starting, target.loc) : dir
- new /obj/effect/temp_visual/dir_setting/bloodsplatter(target.loc, splatter_dir, splatter_color)
- if(hit_effect)
- new hit_effect(target.loc)
+/// Same as set_angle, but the reflection continues from the center of the object that reflects it instead of the side
+/obj/projectile/proc/set_angle_centered(new_angle)
+ if(!nondirectional_sprite)
+ transform = transform.TurnTo(Angle, new_angle)
+ Angle = new_angle
+ if(trajectory)
+ trajectory.set_angle(new_angle)
- L.apply_effects(0, weaken, paralyze, 0, stutter, eyeblur, drowsy, 0, incinerate, blocked)
- L.stun_effect_act(stun, agony, def_zone, src, damage_flags)
- L.apply_damage(irradiate, DAMAGE_RADIATION, damage_flags = DAMAGE_FLAG_DISPERSED) //radiation protection is handled separately from other armor types.
- return 1
+ var/list/coordinates = trajectory.return_coordinates()
+ trajectory.set_location(coordinates[1], coordinates[2], coordinates[3]) // Sets the trajectory to the center of the tile it bounced at
-//called when the projectile stops flying because it collided with something
-/obj/projectile/proc/on_impact(var/atom/A, var/affected_limb)
- return
+ if(fired && hitscan && isloc(loc) && (loc != last_angle_set_hitscan_store)) // Handles hitscan projectiles
+ last_angle_set_hitscan_store = loc
+ var/datum/point/point_cache = new (src)
+ point_cache.initialize_location(coordinates[1], coordinates[2], coordinates[3]) // Take the center of the hitscan collision tile
+ store_hitscan_collision(point_cache)
+ return TRUE
+
+
+
+/obj/projectile/forceMove(atom/target)
+ if(!isloc(target) || !isloc(loc) || !z)
+ return ..()
+ var/zc = target.z != z
+ var/old = loc
+ 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)
+ trajectory.initialize_location(target.x, target.y, target.z, 0, 0)
+ if(hitscan)
+ record_hitscan_start(RETURN_PRECISE_POINT(src))
+ if(zc)
+ after_z_change(old, target)
+
+/obj/projectile/proc/after_z_change(atom/olcloc, atom/newloc)
+
+/obj/projectile/proc/before_z_change(turf/oldloc, turf/newloc)
+
+/obj/projectile/vv_edit_var(var_name, var_value)
+ switch(var_name)
+ if(NAMEOF(src, Angle))
+ set_angle(var_value)
+ return TRUE
+ else
+ return ..()
+
+/obj/projectile/proc/set_pixel_speed(new_speed)
+ if(trajectory)
+ trajectory.set_speed(new_speed)
+ return TRUE
+ return FALSE
+
+/obj/projectile/proc/record_hitscan_start(datum/point/point_cache)
+ if(point_cache)
+ beam_segments = list()
+ beam_index = point_cache
+ beam_segments[beam_index] = null //record start.
+
+/obj/projectile/proc/process_hitscan()
+ var/safety = range * 10
+ record_hitscan_start(RETURN_POINT_VECTOR_INCREMENT(src, Angle, MUZZLE_EFFECT_PIXEL_INCREMENT, 1))
+ while(loc && !QDELETED(src))
+ if(paused)
+ stoplag(1)
+ continue
+ if(safety-- <= 0)
+ if(loc)
+ Collide(loc) // Bump(loc)
+ if(!QDELETED(src))
+ qdel(src)
+ return //Kill!
+ pixel_move(1, TRUE)
+ // No kevinz I do not care that this is a hitscan weapon, it is not allowed to travel 100 turfs in a tick
+ if(CHECK_TICK && QDELETED(src))
+ return
+
+/obj/projectile/proc/pixel_move(trajectory_multiplier, hitscanning = FALSE)
+ if(!loc || !trajectory)
+ return
+ last_projectile_move = world.time
+ if(homing)
+ process_homing()
+ var/forcemoved = FALSE
+ for(var/i in 1 to SSprojectiles.global_iterations_per_move)
+ if(QDELETED(src))
+ return
+ trajectory.increment(trajectory_multiplier)
+ var/turf/T = trajectory.return_turf()
+ if(!istype(T))
+ // step back to the last valid turf before we Destroy
+ trajectory.increment(-trajectory_multiplier)
+ qdel(src)
+ return
+ if (T == loc)
+ continue
+ if (T.z == loc.z)
+ step_towards(src, T)
+ hitscan_last = loc
+ SEND_SIGNAL(src, COMSIG_PROJECTILE_PIXEL_STEP)
+ continue
+ var/old = loc
+ before_z_change(loc, T)
+ trajectory_ignore_forcemove = TRUE
+ forceMove(T)
+ trajectory_ignore_forcemove = FALSE
+ after_z_change(old, loc)
+ if(!hitscanning)
+ pixel_x = trajectory.return_px()
+ pixel_y = trajectory.return_py()
+ forcemoved = TRUE
+ hitscan_last = loc
+ SEND_SIGNAL(src, COMSIG_PROJECTILE_PIXEL_STEP)
+ if(QDELETED(src)) //deleted on last move
+ return
+ if(!hitscanning && !forcemoved)
+ pixel_x = trajectory.return_px() - trajectory.mpx * trajectory_multiplier * SSprojectiles.global_iterations_per_move
+ pixel_y = trajectory.return_py() - trajectory.mpy * trajectory_multiplier * SSprojectiles.global_iterations_per_move
+ animate(src, pixel_x = trajectory.return_px(), pixel_y = trajectory.return_py(), time = 1, flags = ANIMATION_END_NOW)
+ Range()
+
+/obj/projectile/proc/process_homing() //may need speeding up in the future performance wise.
+ if(!homing_target)
+ return FALSE
+ var/datum/point/PT = RETURN_PRECISE_POINT(homing_target)
+ PT.x += clamp(homing_offset_x, 1, world.maxx)
+ PT.y += clamp(homing_offset_y, 1, world.maxy)
+ var/angle = closer_angle_difference(Angle, angle_between_points(RETURN_PRECISE_POINT(src), PT))
+ set_angle(Angle + clamp(angle, -homing_turn_speed, homing_turn_speed))
+
+/obj/projectile/proc/set_homing_target(atom/A)
+ if(!A || (!isturf(A) && !isturf(A.loc)))
+ return FALSE
+ homing = TRUE
+ homing_target = A
+ homing_offset_x = rand(homing_inaccuracy_min, homing_inaccuracy_max)
+ homing_offset_y = rand(homing_inaccuracy_min, homing_inaccuracy_max)
+ if(prob(50))
+ homing_offset_x = -homing_offset_x
+ if(prob(50))
+ homing_offset_y = -homing_offset_y
+
+/**
+ * Aims the projectile at a target.
+ *
+ * Must be passed at least one of a target or a list of click parameters.
+ * If only passed the click modifiers the source atom must be a mob with a client.
+ *
+ * Arguments:
+ * - [target][/atom]: (Optional) The thing that the projectile will be aimed at.
+ * - [source][/atom]: The initial location of the projectile or the thing firing it.
+ * - [modifiers][/list]: (Optional) A list of click parameters to apply to this operation.
+ * - deviation: (Optional) How the trajectory should deviate from the target in degrees.
+ * - //Spread is FORCED!
+ */
+/obj/projectile/proc/preparePixelProjectile(atom/target, atom/source, list/modifiers = null, deviation = 0)
+ if(!(isnull(modifiers) || islist(modifiers)))
+ stack_trace("WARNING: Projectile [type] fired with non-list modifiers, likely was passed click params.")
+ modifiers = null
+
+ var/turf/source_loc = get_turf(source)
+ var/turf/target_loc = get_turf(target)
+ if(isnull(source_loc))
+ stack_trace("WARNING: Projectile [type] fired from nullspace.")
+ qdel(src)
+ return FALSE
+
+ trajectory_ignore_forcemove = TRUE
+ forceMove(source_loc)
+ trajectory_ignore_forcemove = FALSE
+
+ starting = source_loc
+ // Find the last atom movable in our loc chain, or if we're a turf use us
+ var/atom/source_position = get_highest_loc(source, /atom/movable) || source
+ pixel_x = source_position.pixel_x
+ pixel_y = source_position.pixel_y
+ pixel_w = source_position.pixel_w
+ pixel_z = source_position.pixel_z
+ original = target
+ if(length(modifiers))
+ var/list/calculated = calculate_projectile_angle_and_pixel_offsets(source_position, target_loc && target, modifiers)
+
+ p_x = calculated[2]
+ p_y = calculated[3]
+ set_angle(calculated[1] + deviation)
+ return TRUE
+
+ if(target_loc)
+ yo = target_loc.y - source_loc.y
+ xo = target_loc.x - source_loc.x
+ set_angle(get_angle(src, target_loc) + deviation)
+ return TRUE
+
+ stack_trace("WARNING: Projectile [type] fired without a target or mouse parameters to aim with.")
+ qdel(src)
+ return FALSE
+
+/**
+ * Calculates the pixel offsets and angle that a projectile should be launched at.
+ *
+ * Arguments:
+ * - [source][/atom]: The thing that the projectile is being shot from.
+ * - [target][/atom]: (Optional) The thing that the projectile is being shot at.
+ * - If this is not provided the source atom must be a mob with a client.
+ * - [modifiers][/list]: A list of click parameters used to modify the shot angle.
+ */
+/proc/calculate_projectile_angle_and_pixel_offsets(atom/source, atom/target, modifiers)
+ var/angle = 0
+ var/p_x = LAZYACCESS(modifiers, ICON_X) ? text2num(LAZYACCESS(modifiers, ICON_X)) : world.icon_size / 2 // ICON_(X|Y) are measured from the bottom left corner of the icon.
+ var/p_y = LAZYACCESS(modifiers, ICON_Y) ? text2num(LAZYACCESS(modifiers, ICON_Y)) : world.icon_size / 2 // This centers the target if modifiers aren't passed.
+
+ if(target)
+ var/turf/source_loc = get_turf(source)
+ var/turf/target_loc = get_turf(target)
+ var/dx = ((target_loc.x - source_loc.x) * world.icon_size) + (target.pixel_x - source.pixel_x) + (p_x - (world.icon_size / 2))
+ var/dy = ((target_loc.y - source_loc.y) * world.icon_size) + (target.pixel_y - source.pixel_y) + (target.pixel_z - source.pixel_z) + (p_y - (world.icon_size / 2))
+ angle = ATAN2(dy, dx)
+ return list(angle, p_x, p_y)
+
+ if(!ismob(source) || !LAZYACCESS(modifiers, SCREEN_LOC))
+ CRASH("Can't make trajectory calculations without a target or click modifiers and a client.")
+
+ var/mob/user = source
+ if(!user.client)
+ CRASH("Can't make trajectory calculations without a target or click modifiers and a client.")
+
+ //Split screen-loc up into X+Pixel_X and Y+Pixel_Y
+ var/list/screen_loc_params = splittext(LAZYACCESS(modifiers, SCREEN_LOC), ",")
+ //Split X+Pixel_X up into list(X, Pixel_X)
+ var/list/screen_loc_X = splittext(screen_loc_params[1],":")
+ //Split Y+Pixel_Y up into list(Y, Pixel_Y)
+ var/list/screen_loc_Y = splittext(screen_loc_params[2],":")
+
+ var/tx = (text2num(screen_loc_X[1]) - 1) * world.icon_size + text2num(screen_loc_X[2])
+ // We are here trying to lower our target location by the firing source's visual offset
+ // So visually things make a nice straight line while properly accounting for actual physical position
+ var/ty = (text2num(screen_loc_Y[1]) - 1) * world.icon_size + text2num(screen_loc_Y[2]) - source.pixel_z
+
+ //Calculate the "resolution" of screen based on client's view and world's icon size. This will work if the user can view more tiles than average.
+ var/list/screenview = view_to_pixels(user.client.view)
+
+ var/ox = round(screenview[1] / 2) - user.client.pixel_x //"origin" x
+ var/oy = round(screenview[2] / 2) - user.client.pixel_y - source.pixel_z //"origin" y
+ angle = ATAN2(tx - oy, ty - ox)
+ return list(angle, p_x, p_y)
+
+/obj/projectile/Destroy()
+ if(hitscan)
+ finalize_hitscan_and_generate_tracers()
+ STOP_PROCESSING(SSprojectiles, src)
+ cleanup_beam_segments()
+ if(trajectory)
+ QDEL_NULL(trajectory)
+ return ..()
+
+/obj/projectile/proc/cleanup_beam_segments()
+ QDEL_LIST_ASSOC(beam_segments)
+ beam_segments = list()
+ QDEL_NULL(beam_index)
+
+/obj/projectile/proc/finalize_hitscan_and_generate_tracers(impacting = TRUE)
+ if(trajectory && beam_index)
+ var/datum/point/point_cache = trajectory.copy_to()
+ beam_segments[beam_index] = point_cache
+ generate_hitscan_tracers(null, null, impacting)
+
+/obj/projectile/proc/generate_hitscan_tracers(cleanup = TRUE, duration = 3, impacting = TRUE)
+ if(!length(beam_segments))
+ return
+ if(tracer_type)
+ var/tempref = REF(src)
+ for(var/datum/point/p in beam_segments)
+ generate_tracer_between_points(p, beam_segments[p], tracer_type, color, duration, hitscan_light_range, hitscan_light_color_override, hitscan_light_intensity, tempref)
+ if(muzzle_type && duration > 0)
+ var/datum/point/p = beam_segments[1]
+ var/atom/movable/thing = new muzzle_type
+ p.move_atom_to_src(thing)
+ var/matrix/matrix = new
+ matrix.Turn(original_angle)
+ thing.transform = matrix
+ thing.color = color
+ thing.set_light(muzzle_flash_range, muzzle_flash_intensity, muzzle_flash_color_override? muzzle_flash_color_override : color)
+ QDEL_IN(thing, duration)
+ if(impacting && impact_type && duration > 0)
+ var/datum/point/p = beam_segments[beam_segments[beam_segments.len]]
+ var/atom/movable/thing = new impact_type
+ p.move_atom_to_src(thing)
+ var/matrix/matrix = new
+ matrix.Turn(Angle)
+ thing.transform = matrix
+ thing.color = color
+ thing.set_light(impact_light_range, impact_light_intensity, impact_light_color_override? impact_light_color_override : color)
+ QDEL_IN(thing, duration)
+ if(cleanup)
+ cleanup_beam_segments()
+
+/// Reflects the projectile off of something
+/obj/projectile/proc/reflect(atom/hit_atom)
+ if(!starting)
+ return
+ var/new_x = starting.x + pick(0, 0, 0, 0, 0, -1, 1, -2, 2)
+ var/new_y = starting.y + pick(0, 0, 0, 0, 0, -1, 1, -2, 2)
+ var/turf/current_tile = get_turf(hit_atom)
+
+ // redirect the projectile
+ original = locate(new_x, new_y, z)
+ starting = current_tile
+ firer = hit_atom
+ yo = new_y - current_tile.y
+ xo = new_x - current_tile.x
+ var/new_angle_s = Angle + rand(120,240)
+ while(new_angle_s > 180) // Translate to regular projectile degrees
+ new_angle_s -= 360
+ set_angle(new_angle_s)
+
+/// Fire a projectile from this atom at another atom
+/atom/proc/fire_projectile(projectile_type, atom/target, sound, firer, list/ignore_targets = list())
+ if (!isnull(sound))
+ playsound(src, sound, vol = 100, vary = TRUE)
+
+ var/turf/startloc = get_turf(src)
+ var/obj/projectile/bullet = new projectile_type(startloc)
+ bullet.starting = startloc
+ for (var/atom/thing as anything in ignore_targets)
+ bullet.impacted[WEAKREF(thing)] = TRUE
+ bullet.firer = firer || src
+ bullet.fired_from = src
+ bullet.yo = target.y - startloc.y
+ bullet.xo = target.x - startloc.x
+ bullet.original = target
+ bullet.preparePixelProjectile(target, src)
+ bullet.fire()
+ return bullet
+
+
+/*##############################
+ AURORA SNOWFLAKE SECTION
+##############################*/
//Checks if the projectile is eligible for embedding. Not that it necessarily will.
/obj/projectile/proc/can_embed()
@@ -143,200 +1092,34 @@
return FALSE
return TRUE
-/obj/projectile/proc/do_embed(var/obj/item/organ/external/organ)
- var/obj/item/SP = new shrapnel_type(organ)
- SP.edge = TRUE
- SP.sharp = TRUE
- SP.name = (name != "shrapnel") ? "[initial(name)] shrapnel" : "shrapnel"
- SP.desc += " It looks like it was fired from [shot_from]."
- SP.forceMove(organ)
- organ.embed(SP)
- return SP
+//return TRUE if the projectile should be allowed to pass through after all, FALSE if not.
+/obj/projectile/proc/check_penetrate(atom/A)
+ return TRUE
+
+
+/obj/projectile/ex_act(var/severity = 2.0)
+ return //explosions probably shouldn't delete projectiles
+
+/obj/projectile/damage_flags()
+ return damage_flags
/obj/projectile/proc/get_structure_damage()
if(damage_type == DAMAGE_BRUTE || damage_type == DAMAGE_BURN)
return damage * anti_materiel_potential
return FALSE
-//return TRUE if the projectile should be allowed to pass through after all, FALSE if not.
-/obj/projectile/proc/check_penetrate(atom/A)
- return TRUE
+//Because I don't want to rewrite half the world to use embed_data just yet,
+//this is left as is, praise be the omnissiah
+/obj/projectile/proc/do_embed(var/obj/item/organ/external/organ)
+ var/obj/item/SP = new shrapnel_type(organ)
+ SP.edge = TRUE
+ SP.sharp = TRUE
+ SP.name = (name != "shrapnel") ? "[initial(name)] shrapnel" : "shrapnel"
+ SP.desc += " It looks like it was fired from [fired_from]."
+ SP.forceMove(organ)
+ organ.embed(SP)
+ return SP
-/obj/projectile/proc/launch_projectile(atom/target, target_zone, mob/user, params, angle_override, forced_spread = 0)
- original = target
- def_zone = check_zone(target_zone)
- firer = user
- var/direct_target
- if(get_turf(target) == get_turf(src))
- direct_target = target
-
- if(ispath(secondary_projectile))
- var/obj/projectile/BB = new secondary_projectile(src)
- BB.launch_projectile(target, target_zone, user, params, angle_override, forced_spread)
-
- preparePixelProjectile(target, user? user : get_turf(src), params, forced_spread)
- return fire(angle_override, direct_target)
-
-//called to launch a projectile from a gun
-/obj/projectile/proc/launch_from_gun(atom/target, target_zone, mob/user, params, angle_override, forced_spread, obj/item/gun/launcher)
-
- shot_from = launcher.name
- suppressed = launcher.suppressed
-
- if(launcher.iff_capable && user)
- iff = get_iff_from_user(user)
-
- return launch_projectile(target, target_zone, user, params, angle_override, forced_spread)
-
-/obj/projectile/proc/get_iff_from_user(var/mob/user)
- var/obj/item/card/id/ID = user.GetIdCard()
- if(ID)
- return ID.iff_faction
- return null
-
-//Called when the projectile intercepts a mob. Returns 1 if the projectile hit the mob, 0 if it missed and should keep flying.
-/obj/projectile/proc/attack_mob(var/mob/living/target_mob, var/distance, var/miss_modifier=0)
- if(!istype(target_mob))
- return
-
- //roll to-hit
- miss_modifier = max(15*(distance-1) - round(25*accuracy) + miss_modifier, 0)
- hit_zone = get_zone_with_miss_chance(def_zone, target_mob, miss_modifier, (distance > 1 || original != target_mob), point_blank) //if the projectile hits a target we weren't originally aiming at then retain the chance to miss
-
- var/result = PROJECTILE_FORCE_MISS
- if(hit_zone)
- def_zone = hit_zone //set def_zone, so if the projectile ends up hitting someone else later (to be implemented), it is more likely to hit the same part
- if(!target_mob.aura_check(AURA_TYPE_BULLET, src, def_zone))
- return TRUE
- result = target_mob.bullet_act(src, def_zone)
-
- switch(result)
- if(PROJECTILE_FORCE_MISS)
- if(!point_blank)
- if(!suppressed)
- target_mob.visible_message(SPAN_NOTICE("\The [src] misses [target_mob] narrowly!"))
- playsound(target_mob, /singleton/sound_category/bulletflyby_sound, 50, 1)
- return FALSE
- if(PROJECTILE_DODGED)
- return FALSE
- if(PROJECTILE_STOPPED)
- return TRUE
-
- var/impacted_organ = target_mob.get_organ_name_from_zone(def_zone)
- //hit messages
- if(suppressed)
- to_chat(target_mob, SPAN_DANGER("You've been hit in the [impacted_organ] by \a [src]!"))
- else
- target_mob.visible_message(SPAN_DANGER("\The [target_mob] is hit by \a [src] in the [impacted_organ]!"),
- SPAN_DANGER("You are hit by \a [src] in the [impacted_organ]!"))//X has fired Y is now given by the guns so you cant tell who shot you if you could not see the shooter
-
- var/no_clients = FALSE
- //admin logs
- if((!ismob(firer) || !firer.client) && !target_mob.client)
- no_clients = TRUE
- if(istype(target_mob, /mob/living/heavy_vehicle))
- var/mob/living/heavy_vehicle/HV = target_mob
- for(var/pilot in HV.pilots)
- var/mob/M = pilot
- if(M.client)
- no_clients = FALSE
- break
- if(!no_clients)
- if(ismob(firer))
-
- var/attacker_message = "shot with \a [src.type]"
- var/victim_message = "shot with \a [src.type]"
- var/admin_message = "shot (\a [src.type])"
-
- admin_attack_log(firer, target_mob, attacker_message, victim_message, admin_message)
- else
- target_mob.attack_log += "\[[time_stamp()]\] UNKNOWN SUBJECT (No longer exists) shot [target_mob]/[target_mob.ckey] with \a [src]"
- msg_admin_attack("UNKNOWN shot [target_mob] ([target_mob.ckey]) with \a [src] (JMP)",ckey=key_name(target_mob))
-
- //sometimes bullet_act() will want the projectile to continue flying
- if (result == PROJECTILE_CONTINUE)
- return FALSE
-
- return TRUE
-
-/obj/projectile/Collide(atom/A)
- . = ..()
- if(A == src)
- return FALSE //no.
-
- if(A in permutated)
- return FALSE
-
- if(firer && !ignore_source_check)
- if(A == firer || (A == firer.loc)) //cannot shoot yourself or your mech
- trajectory_ignore_forcemove = TRUE
- forceMove(get_turf(A))
- trajectory_ignore_forcemove = FALSE
- return FALSE
-
- var/distance = get_dist(get_turf(A), starting) // Get the distance between the turf shot from and the mob we hit and use that for the calculations.
- var/passthrough = FALSE //if the projectile should continue flying
- if(ismob(A))
- var/mob/M = A
- if(isliving(A)) //so ghosts don't stop bullets
- if(check_iff(M))
- passthrough = TRUE
- else
- if(M.dir & get_dir(M, starting)) // only check neckgrab if they're facing in the direction the bullets came from
- //if they have a neck grab on someone, that person gets hit instead
- for(var/obj/item/grab/G in list(M.l_hand, M.r_hand))
- if(!G.affecting.lying && G.state >= GRAB_NECK)
- visible_message(SPAN_DANGER("\The [M] uses [G.affecting] as a shield!"))
- if(Collide(G.affecting))
- return //If Collide() returns 0 (keep going) then we continue on to attack M.
-
- passthrough = !attack_mob(M, distance)
- else
- passthrough = TRUE
- else
- passthrough = (A.bullet_act(src, def_zone) == PROJECTILE_CONTINUE) //backwards compatibility
- if(isturf(A))
- for(var/obj/O in A)
- O.bullet_act(src)
- for(var/mob/living/M in A)
- attack_mob(M, distance)
-
- //penetrating projectiles can pass through things that otherwise would not let them
- if(!passthrough && penetrating > 0)
- if(check_penetrate(A))
- passthrough = TRUE
- penetrating--
-
- //the bullet passes through a dense object!
- if(passthrough || forcedodge)
- //move ourselves onto A so we can continue on our way.
- if(A)
- trajectory_ignore_forcemove = TRUE
- if(istype(A, /turf))
- forceMove(A)
- else
- forceMove(get_turf(A))
- trajectory_ignore_forcemove = FALSE
- permutated.Add(A)
- return FALSE
-
- //stop flying
- on_impact(A, hit_zone)
- qdel(src)
- return TRUE
-
-/obj/projectile/proc/check_iff(var/mob/M)
- if(isnull(iff))
- return FALSE
- var/obj/item/card/id/ID = M.GetIdCard()
- if(ID && (ID.iff_faction == iff))
- return TRUE
- return FALSE
-
-/obj/projectile/ex_act(var/severity = 2.0)
- return //explosions probably shouldn't delete projectiles
-
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/obj/projectile/proc/old_style_target(atom/target, atom/source)
if(!source)
source = get_turf(src)
@@ -344,267 +1127,6 @@
original = target
setAngle(get_projectile_angle(source, target))
-/obj/projectile/proc/fire(angle, atom/direct_target)
- //If no angle needs to resolve it from xo/yo!
- if(direct_target)
- direct_target.bullet_act(src, def_zone)
- on_impact(direct_target, def_zone)
- qdel(src)
- return
- if(isnum(angle))
- setAngle(angle)
- // trajectory dispersion
- var/turf/starting = get_turf(src)
- if(!starting)
- return
- if(isnull(Angle)) //Try to resolve through offsets if there's no angle set.
- if(isnull(xo) || isnull(yo))
- crash_with("WARNING: Projectile [type] deleted due to being unable to resolve a target after angle was null!")
- qdel(src)
- return
- var/turf/target = locate(Clamp(starting + xo, 1, world.maxx), Clamp(starting + yo, 1, world.maxy), starting.z)
- setAngle(get_projectile_angle(src, target))
- if(dispersion)
- setAngle(Angle + rand(-dispersion, dispersion))
- original_angle = Angle
- if(!nondirectional_sprite)
- var/matrix/M = new
- M.Turn(Angle)
- transform = M
- forceMove(starting)
- trajectory = new(starting.x, starting.y, starting.z, 0, 0, Angle, pixel_speed)
- last_projectile_move = world.time
- fired = TRUE
- if(hitscan)
- return process_hitscan()
- else
- generate_muzzle_flash()
- if(!(datum_flags & DF_ISPROCESSING))
- START_PROCESSING(SSprojectiles, src)
- pixel_move(1) //move it now!
-
-/obj/projectile/proc/preparePixelProjectile(atom/target, atom/source, params, angle_offset = 0)
- var/turf/curloc = get_turf(source)
- var/turf/targloc = get_turf(target)
- forceMove(get_turf(source))
- starting = get_turf(source)
- original = target
-
- var/list/calculated = list(null,null,null)
- if(isliving(source) && params)
- calculated = calculate_projectile_angle_and_pixel_offsets(source, params)
- p_x = calculated[2]
- p_y = calculated[3]
- setAngle(calculated[1])
-
- else if(targloc && curloc)
- yo = targloc.y - curloc.y
- xo = targloc.x - curloc.x
- setAngle(get_projectile_angle(src, targloc))
- else
- crash_with("WARNING: Projectile [type] fired without either mouse parameters, or a target atom to aim at!")
- qdel(src)
- if(angle_offset)
- setAngle(Angle + angle_offset)
-
-/obj/projectile/proc/before_move()
- return
-
-/obj/projectile/proc/after_move()
- return
-
-//A mob moving on a tile with a projectile is hit by it.
-/obj/projectile/proc/on_entered(datum/source, atom/movable/arrived, atom/old_loc, list/atom/old_locs)
- SIGNAL_HANDLER
-
- if(isliving(arrived) && (arrived.density || arrived == original) && !(pass_flags & PASSMOB))
- Collide(arrived)
-
-/obj/projectile/Initialize()
- . = ..()
- permutated = list()
-
- var/static/list/loc_connections = list(
- COMSIG_ATOM_ENTERED = PROC_REF(on_entered),
- )
-
- AddElement(/datum/element/connect_loc, loc_connections)
-
-/obj/projectile/damage_flags()
- return damage_flags
-
-/obj/projectile/proc/pixel_move(moves, trajectory_multiplier = 1, hitscanning = FALSE)
- if(!loc || !trajectory)
- if(!QDELETED(src))
- if(loc)
- on_impact(loc)
- qdel(src)
- return
-
- if (QDELETED(src))
- return
-
- last_projectile_move = world.time
- if(!nondirectional_sprite && !hitscanning)
- var/matrix/M = new
- M.Turn(Angle)
- transform = M
- trajectory.increment(trajectory_multiplier)
- var/turf/T = trajectory.return_turf()
-
- if (!T) // Nowhere to go. Just die.
- qdel(src)
- return
-
- if(T.z != loc.z)
- before_move()
- before_z_change(loc, T)
- trajectory_ignore_forcemove = TRUE
- forceMove(T)
- trajectory_ignore_forcemove = FALSE
- after_move()
- if(!hitscanning)
- pixel_x = trajectory.return_px()
- pixel_y = trajectory.return_py()
- else
- if(T != loc)
- before_move()
- Move(T)
- after_move()
- if(!hitscanning)
- pixel_x = trajectory.return_px() - trajectory.mpx * trajectory_multiplier
- pixel_y = trajectory.return_py() - trajectory.mpy * trajectory_multiplier
- if(!hitscanning)
- animate(src, pixel_x = trajectory.return_px(), pixel_y = trajectory.return_py(), time = 1, flags = ANIMATION_END_NOW)
- if(isturf(loc))
- hitscan_last = loc
- if(can_hit_target(original, permutated))
- Collide(original, TRUE)
- Range()
-
-//Returns true if the target atom is on our current turf and above the right layer
-/obj/projectile/proc/can_hit_target(atom/target, var/list/passthrough)
- return (target && ((target.layer >= TURF_LAYER + 0.3) || ismob(target)) && (loc == get_turf(target)) && (!(target in passthrough)))
-
-/proc/calculate_projectile_angle_and_pixel_offsets(mob/user, params)
- var/list/mouse_control = params2list(params)
- var/p_x = 0
- var/p_y = 0
- var/angle = 0
- if(mouse_control["icon-x"])
- p_x = text2num(mouse_control["icon-x"])
- if(mouse_control["icon-y"])
- p_y = text2num(mouse_control["icon-y"])
- if(mouse_control["screen-loc"])
- //Split screen-loc up into X+Pixel_X and Y+Pixel_Y
- var/list/screen_loc_params = splittext(mouse_control["screen-loc"], ",")
-
- //Split X+Pixel_X up into list(X, Pixel_X)
- var/list/screen_loc_X = splittext(screen_loc_params[1],":")
-
- //Split Y+Pixel_Y up into list(Y, Pixel_Y)
- var/list/screen_loc_Y = splittext(screen_loc_params[2],":")
- var/x = text2num(screen_loc_X[1]) * 32 + text2num(screen_loc_X[2]) - 32
- var/y = text2num(screen_loc_Y[1]) * 32 + text2num(screen_loc_Y[2]) - 32
-
- //Calculate the "resolution" of screen based on client's view and world's icon size. This will work if the user can view more tiles than average.
- if(istype(user, /mob/living/heavy_vehicle))
- var/mob/living/heavy_vehicle/H = user
- user = pick(H.pilots) //since i assume this is a list, we want only 1 person
- var/list/screenview = getviewsize(user.client.view)
- var/screenviewX = screenview[1] * world.icon_size
- var/screenviewY = screenview[2] * world.icon_size
-
- var/ox = round(screenviewX/2) - user.client.pixel_x //"origin" x
- var/oy = round(screenviewY/2) - user.client.pixel_y //"origin" y
- angle = Atan2(y - oy, x - ox)
- return list(angle, p_x, p_y)
-
-/obj/projectile/proc/Range()
- range--
- if(range <= 0 && loc)
- on_range()
-
-/obj/projectile/proc/on_range() //if we want there to be effects when they reach the end of their range
- on_impact(loc)
- qdel(src)
-
-/obj/projectile/proc/store_hitscan_collision(datum/point/pcache)
- beam_segments[beam_index] = pcache
- beam_index = pcache
- beam_segments[beam_index] = null
-
-/obj/projectile/proc/return_predicted_turf_after_moves(moves, forced_angle) //I say predicted because there's no telling that the projectile won't change direction/location in flight.
- if(!trajectory && isnull(forced_angle) && isnull(Angle))
- return FALSE
- var/datum/point/vector/current = trajectory
- if(!current)
- var/turf/T = get_turf(src)
- current = new(T.x, T.y, T.z, pixel_x, pixel_y, isnull(forced_angle)? Angle : forced_angle, pixel_speed)
- var/datum/point/vector/v = current.return_vector_after_increments(moves)
- return v.return_turf()
-
-/obj/projectile/proc/return_pathing_turfs_in_moves(moves, forced_angle)
- var/turf/current = get_turf(src)
- var/turf/ending = return_predicted_turf_after_moves(moves, forced_angle)
- return getline(current, ending)
-
-/obj/projectile/proc/process_hitscan()
- var/safety = range * 3
- var/return_vector = RETURN_POINT_VECTOR_INCREMENT(src, Angle, MUZZLE_EFFECT_PIXEL_INCREMENT, 1)
- record_hitscan_start(return_vector)
- while(loc && !QDELETED(src))
- if(paused)
- stoplag(1)
- continue
- if(safety-- <= 0)
- qdel(src)
- crash_with("WARNING: [type] projectile encountered infinite recursion during hitscanning in [__FILE__]/[__LINE__]!")
- return //Kill!
- pixel_move(1, 1, TRUE)
-
-/obj/projectile/proc/record_hitscan_start(datum/point/pcache)
- beam_segments = list() //initialize segment list with the list for the first segment
- beam_index = pcache
- beam_segments[beam_index] = null //record start.
-
-/obj/projectile/proc/vol_by_damage()
- if(src.damage)
- return Clamp((src.damage) * 0.67, 30, 100)// Multiply projectile damage by 0.67, then CLAMP the value between 30 and 100
- else
- return 50 //if the projectile doesn't do damage, play its hitsound at 50% volume.
-
-/obj/projectile/proc/before_z_change(turf/oldloc, turf/newloc)
- var/datum/point/pcache = trajectory.copy_to()
- if(hitscan)
- store_hitscan_collision(pcache)
-
-/obj/projectile/process()
- last_process = world.time
- if(!loc || !fired || !trajectory)
- fired = FALSE
- return PROCESS_KILL
- if(paused || !isturf(loc))
- last_projectile_move += world.time - last_process //Compensates for pausing, so it doesn't become a hitscan projectile when unpaused from charged up ticks.
- return
- var/elapsed_time_deciseconds = (world.time - last_projectile_move) + time_offset
- time_offset = 0
- var/required_moves = 0
- if(speed > 0)
- required_moves = FLOOR_FLOAT(elapsed_time_deciseconds / speed, 1)
- if(required_moves > SSprojectiles.global_max_tick_moves)
- var/overrun = required_moves - SSprojectiles.global_max_tick_moves
- required_moves = SSprojectiles.global_max_tick_moves
- time_offset += overrun * speed
- time_offset += MODULUS(elapsed_time_deciseconds, speed)
- else
- required_moves = SSprojectiles.global_max_tick_moves
- if(!required_moves)
- return
-
- for(var/i = 1; i <= required_moves && !QDELETED(src); i++)
- pixel_move(required_moves)
-
/obj/projectile/proc/setAngle(new_angle) //wrapper for overrides.
Angle = new_angle
if(!nondirectional_sprite)
@@ -618,63 +1140,6 @@
/obj/projectile/proc/redirect(x, y, starting, source)
old_style_target(locate(x, y, z), starting? get_turf(starting) : get_turf(source))
-/obj/projectile/forceMove(atom/target)
- . = ..()
- if(trajectory && !trajectory_ignore_forcemove && isturf(target))
- trajectory.initialize_location(target.x, target.y, target.z, 0, 0)
-
-/obj/projectile/Destroy()
- if(hitscan)
- if(loc && trajectory)
- var/datum/point/pcache = trajectory.copy_to()
- beam_segments[beam_index] = pcache
- generate_hitscan_tracers()
- STOP_PROCESSING(SSprojectiles, src)
- return ..()
-
-/obj/projectile/proc/generate_muzzle_flash(duration = 3)
- if(duration <= 0)
- return
- if(!muzzle_type || suppressed)
- return
- var/datum/point/p = trajectory
- var/atom/movable/thing = new muzzle_type
- p.move_atom_to_src(thing)
- var/matrix/M = new
- M.Turn(original_angle)
- thing.transform = M
- QDEL_IN(thing, duration)
-
-/obj/projectile/proc/generate_hitscan_tracers(cleanup = TRUE, duration = 3)
- if(!length(beam_segments))
- return
- if(duration <= 0)
- return
- if(tracer_type)
- for(var/datum/point/p in beam_segments)
- generate_tracer_between_points(p, beam_segments[p], tracer_type, color, duration)
- if(muzzle_type && !suppressed)
- var/datum/point/p = beam_segments[1]
- var/atom/movable/thing = new muzzle_type
- p.move_atom_to_src(thing)
- var/matrix/M = new
- M.Turn(original_angle)
- thing.transform = M
- QDEL_IN(thing, duration)
- if(impact_type)
- var/datum/point/p = beam_segments[beam_segments[beam_segments.len]]
- var/atom/movable/thing = new impact_type
- p.move_atom_to_src(thing)
- var/matrix/M = new
- M.Turn(Angle)
- thing.transform = M
- QDEL_IN(thing, duration)
- if(cleanup)
- for(var/i in beam_segments)
- qdel(i)
- beam_segments = null
- QDEL_NULL(beam_index)
-
/obj/projectile/proc/get_print_info()
. = "
"
. += "Damage: [initial(damage)]
"
@@ -702,7 +1167,6 @@
// Need to do this in order to prevent the ping from being deleted
addtimer(CALLBACK(I, TYPE_PROC_REF(/image, flick_overlay), src, 3), 1)
-
/image/proc/flick_overlay(var/atom/A, var/duration)
A.overlays.Add(src)
addtimer(CALLBACK(src, PROC_REF(flick_remove_overlay), A), duration)
@@ -711,4 +1175,6 @@
if(A)
A.overlays.Remove(src)
+#undef MOVES_HITSCAN
#undef MUZZLE_EFFECT_PIXEL_INCREMENT
+#undef MAX_RANGE_HIT_PRONE_TARGETS
diff --git a/code/modules/projectiles/projectile/animate.dm b/code/modules/projectiles/projectile/animate.dm
index a2f9f5a4d7a..1d93c23172d 100644
--- a/code/modules/projectiles/projectile/animate.dm
+++ b/code/modules/projectiles/projectile/animate.dm
@@ -3,7 +3,6 @@
icon_state = "ice_1"
damage = 0
damage_type = DAMAGE_BURN
- nodamage = 1
check_armor = "energy"
/obj/projectile/animate/Collide(atom/change)
diff --git a/code/modules/projectiles/projectile/beams.dm b/code/modules/projectiles/projectile/beams.dm
index a85c1cad4d1..d460636946c 100644
--- a/code/modules/projectiles/projectile/beams.dm
+++ b/code/modules/projectiles/projectile/beams.dm
@@ -130,10 +130,10 @@
damage = 45
armor_penetration = 40
-/obj/projectile/beam/pulse/on_hit(var/atom/target, var/blocked = 0)
+/obj/projectile/beam/pulse/on_hit(atom/target, blocked, def_zone)
if(isturf(target))
target.ex_act(2)
- ..()
+ . = ..()
/obj/projectile/beam/pulse/heavy
name = "heavy pulse laser"
@@ -168,7 +168,8 @@
tracer_type = /obj/effect/projectile/tracer/laser
impact_type = /obj/effect/projectile/impact/laser
-/obj/projectile/beam/laser_tag/on_hit(var/atom/target, var/blocked = 0)
+/obj/projectile/beam/laser_tag/on_hit(atom/target, blocked, def_zone)
+ . = ..()
if(ishuman(target))
var/mob/living/carbon/human/H = target
var/obj/item/clothing/suit/armor/riot/laser_tag/LT = H.wear_suit
@@ -234,7 +235,7 @@
tracer_type = /obj/effect/projectile/tracer/disabler
impact_type = /obj/effect/projectile/impact/disabler
-/obj/projectile/beam/disorient/on_hit(var/atom/target, var/blocked = 0)
+/obj/projectile/beam/disorient/on_hit(atom/target, blocked, def_zone)
if(ishuman(target) && blocked < 100 && !issilicon(target) && !isipc(target)) //Make them trip
var/mob/living/carbon/human/H = target
H.druggy = min(H.druggy + 15, 75)
@@ -268,9 +269,9 @@
tracer_type = /obj/effect/projectile/tracer/stun
impact_type = /obj/effect/projectile/impact/stun
-/obj/projectile/beam/mousegun/on_impact(var/atom/A)
- mousepulse(A, 1)
- ..()
+/obj/projectile/beam/mousegun/on_hit(atom/target, blocked, def_zone)
+ mousepulse(target, 1)
+ . = ..()
/obj/projectile/beam/mousegun/proc/mousepulse(turf/epicenter, range, log=0)
if(!epicenter)
@@ -309,8 +310,6 @@
/obj/projectile/beam/mousegun/emag
name = "diffuse electrical arc"
-
- nodamage = FALSE
damage_type = DAMAGE_BURN
damage = 15
agony = 30
@@ -349,7 +348,6 @@
return TRUE
/obj/projectile/beam/mousegun/xenofauna
- nodamage = FALSE
damage = 10
/obj/projectile/beam/mousegun/xenofauna/mousepulse(atom/target, range, log)
@@ -376,22 +374,22 @@
tracer_type = /obj/effect/projectile/tracer/solar
impact_type = /obj/effect/projectile/impact/solar
-/obj/projectile/beam/megaglaive/on_impact(var/atom/A)
- if(isturf(A))
- if(istype(A, /turf/simulated/mineral))
+/obj/projectile/beam/megaglaive/on_hit(atom/target, blocked, def_zone)
+ if(isturf(target))
+ if(istype(target, /turf/simulated/mineral))
if(prob(75)) //likely because its a mining tool
- var/turf/simulated/mineral/M = A
+ var/turf/simulated/mineral/M = target
if(prob(10))
M.GetDrilled(1)
else if(!M.emitter_blasts_taken)
M.emitter_blasts_taken += 1
else if(prob(33))
M.emitter_blasts_taken += 1
- if(ismob(A))
- var/mob/living/M = A
+ if(ismob(target))
+ var/mob/living/M = target
M.apply_effect(1, INCINERATE, 0)
- explosion(A, -1, 0, 2)
- ..()
+ explosion(target, -1, 0, 2)
+ . = ..()
/obj/projectile/beam/thermaldrill
name = "thermal drill"
@@ -402,9 +400,9 @@
tracer_type = /obj/effect/projectile/tracer/solar
impact_type = /obj/effect/projectile/impact/solar
-/obj/projectile/beam/thermaldrill/on_impact(var/atom/hit_atom)
- if(istype(hit_atom, /turf/simulated/mineral))
- var/turf/simulated/mineral/mineral = hit_atom
+/obj/projectile/beam/thermaldrill/on_hit(atom/target, blocked, def_zone)
+ if(istype(target, /turf/simulated/mineral))
+ var/turf/simulated/mineral/mineral = target
mineral.GetDrilled(TRUE)
return ..()
@@ -426,11 +424,11 @@
tracer_type = /obj/effect/projectile/tracer/cult
impact_type = /obj/effect/projectile/impact/cult
-/obj/projectile/beam/cult/attack_mob(var/mob/living/target_mob, var/distance, var/miss_modifier=0)
+/obj/projectile/beam/cult/on_hit(atom/target, blocked, def_zone)
//Harmlessly passes through cultists and constructs
- if (target_mob == ignore)
+ if (target == ignore)
return 0
- if (iscultist(target_mob))
+ if (iscultist(target))
return 0
return ..()
@@ -447,16 +445,16 @@
/obj/projectile/beam/energy_net
name = "energy net projection"
icon_state = "xray"
- nodamage = 1
+ damage = 0
damage_type = DAMAGE_PAIN
muzzle_type = /obj/effect/projectile/muzzle/xray
tracer_type = /obj/effect/projectile/tracer/xray
impact_type = /obj/effect/projectile/impact/xray
-/obj/projectile/beam/energy_net/on_hit(var/atom/netted)
- do_net(netted)
- ..()
+/obj/projectile/beam/energy_net/on_hit(atom/target, blocked, def_zone)
+ do_net(target)
+ . = ..()
/obj/projectile/beam/energy_net/proc/do_net(var/mob/M)
var/obj/item/energy_net/net = new (get_turf(M))
@@ -468,10 +466,7 @@
damage = 25
armor_penetration = 65
penetrating = 1
- maiming = 1
maim_rate = 5
- clean_cut = 1
- maim_type = DROPLIMB_BURN
muzzle_type = /obj/effect/projectile/muzzle/tachyon
tracer_type = /obj/effect/projectile/tracer/tachyon
@@ -490,7 +485,7 @@
tracer_type = /obj/effect/projectile/tracer/tesla
impact_type = /obj/effect/projectile/impact/tesla
-/obj/projectile/beam/tesla/on_impact(atom/target)
+/obj/projectile/beam/tesla/on_hit(atom/target, blocked, def_zone)
. = ..()
if(isliving(target))
tesla_zap(target, 5, 5000)
@@ -507,7 +502,7 @@
tracer_type = /obj/effect/projectile/tracer/laser/blue
impact_type = /obj/effect/projectile/impact/laser/blue
-/obj/projectile/beam/freezer/on_impact(atom/target)
+/obj/projectile/beam/freezer/on_hit(atom/target, blocked, def_zone)
. = ..()
if(isliving(target))
var/mob/living/L = target
diff --git a/code/modules/projectiles/projectile/bullets.dm b/code/modules/projectiles/projectile/bullets.dm
index 9aaa5b7f093..3ac4e808f3d 100644
--- a/code/modules/projectiles/projectile/bullets.dm
+++ b/code/modules/projectiles/projectile/bullets.dm
@@ -4,7 +4,6 @@
damage = 60
damage_type = DAMAGE_BRUTE
impact_sounds = list(BULLET_IMPACT_MEAT = SOUNDS_BULLET_MEAT, BULLET_IMPACT_METAL = SOUNDS_BULLET_METAL)
- nodamage = FALSE
check_armor = "bullet"
embed = TRUE
sharp = TRUE
@@ -13,12 +12,11 @@
muzzle_type = /obj/effect/projectile/muzzle/bullet
-/obj/projectile/bullet/on_hit(var/atom/target, var/blocked = 0, var/def_zone = null)
- if (..(target, blocked, def_zone))
+/obj/projectile/bullet/on_hit(atom/target, blocked, def_zone)
+ if(isliving(target) && (..(target, blocked, def_zone) == BULLET_ACT_HIT))
var/mob/living/L = target
shake_camera(L, 3, 2)
-/obj/projectile/bullet/attack_mob(var/mob/living/target_mob, var/distance, var/miss_modifier)
if(penetrating > 0 && damage > 20 && prob(damage))
mob_passthrough_check = 1
else
@@ -88,9 +86,16 @@
var/pellet_loss = round((distance - 1)/range_step) //pellets lost due to distance
return max(pellets - pellet_loss, 1)
-/obj/projectile/bullet/pellet/attack_mob(var/mob/living/target_mob, var/distance, var/miss_modifier)
+/obj/projectile/bullet/pellet/on_hit(atom/target, blocked, def_zone)
+ if(!isliving(target))
+ return ..()
+
+ var/mob/living/living_target = target
+
if (pellets < 0)
- return TRUE
+ return BULLET_ACT_BLOCK
+
+ var/distance = get_dist(src.starting, get_turf(living_target))
var/total_pellets = get_pellets(distance)
var/spread = max(base_spread - (spread_step*distance), 0)
@@ -102,7 +107,7 @@
var/hits = 0
for (var/i in 1 to total_pellets)
- if(target_mob.lying && target_mob != original && prob(prone_chance))
+ if(living_target.lying && living_target != original && prob(prone_chance))
continue
// pellet hits spread out across different zones, but 'aim at' the targeted zone with higher probability
@@ -112,20 +117,22 @@
// relatively hacky way of basing a shotgun pellet's likelihood of hitting on the first pellet of the burst while not affecting shrapnel explosions.
if (base_spread > 0)
if (i == 1)
- if (..())
+ if (..() == BULLET_ACT_HIT)
hits++
else
return 0
- else if (..(target_mob, distance, -100))
+ else if (..() == BULLET_ACT_HIT)
hits++
- else if (..())
+ else if (..() == BULLET_ACT_HIT)
hits++
def_zone = old_zone //restore the original zone the projectile was aimed at
pellets -= hits //each hit reduces the number of pellets left
- if (hits >= total_pellets || pellets <= 0)
- return TRUE
- return FALSE
+ // if (hits >= total_pellets || pellets <= 0)
+ // return TRUE
+ if(hits)
+ return BULLET_ACT_HIT //Technically not everything, but good enough
+ return BULLET_ACT_BLOCK //Nothing hit
/obj/projectile/bullet/pellet/get_structure_damage()
var/distance = get_dist(loc, starting)
@@ -157,7 +164,13 @@
var/ball_loss = round((distance - 1)/range_step)
return max(balls - ball_loss, 1)
-/obj/projectile/bullet/rubberball/attack_mob(var/mob/living/target_mob, var/distance, var/miss_modifier)
+/obj/projectile/bullet/rubberball/on_hit(atom/target, blocked, def_zone)
+ if(!isliving(target))
+ return ..()
+
+ var/mob/living/target_mob = target
+ var/distance = get_dist(src.starting, get_turf(target_mob))
+
if (balls < 0)
return TRUE
@@ -323,9 +336,7 @@
penetrating = 5
armor_penetration = 70
hitscan = 1 //so the PTR isn't useless as a sniper weapon
- maiming = 1
maim_rate = 3
- maim_type = DROPLIMB_BLUNT
anti_materiel_potential = 2
/obj/projectile/rifle/kumar_super
@@ -349,18 +360,15 @@
weaken = 3
penetrating = 5
armor_penetration = 10
- maiming = TRUE
maim_rate = 3
- maim_type = DROPLIMB_BLUNT
anti_materiel_potential = 2
-/obj/projectile/bullet/rifle/slugger/on_hit(var/atom/movable/target, var/blocked = 0)
- if(!istype(target))
- return FALSE
- var/throwdir = get_dir(firer, target)
- target.throw_at(get_edge_target_turf(target, throwdir), 3, 3)
- ..()
- return TRUE
+/obj/projectile/bullet/rifle/slugger/on_hit(atom/target, blocked, def_zone)
+ var/atom/movable/movable_target = target
+ if(istype(movable_target))
+ var/throwdir = get_dir(firer, movable_target)
+ movable_target.throw_at(get_edge_target_turf(movable_target, throwdir), 3, 3)
+ . = ..()
/obj/projectile/bullet/rifle/tranq
name = "dart"
@@ -448,7 +456,6 @@
name = "cap"
damage_type = DAMAGE_PAIN
damage = 0
- nodamage = 1
embed = 0
sharp = 0
@@ -493,17 +500,13 @@
damage = 10
armor_penetration = 30
-/obj/projectile/bullet/gauss/highex/on_impact(var/atom/A)
- explosion(A, -1, 0, 2)
- ..()
-
-/obj/projectile/bullet/gauss/highex/on_hit(var/atom/target, var/blocked = 0)
+/obj/projectile/bullet/gauss/highex/on_hit(atom/target, blocked, def_zone)
+ . = ..()
explosion(target, -1, 0, 2)
if(ismovable(target))
var/atom/movable/T = target
var/throwdir = get_dir(firer,target)
INVOKE_ASYNC(T, TYPE_PROC_REF(/atom/movable, throw_at), get_edge_target_turf(target, throwdir), 3, 3)
- return TRUE
/obj/projectile/bullet/cannonball
name = "cannonball"
@@ -519,9 +522,9 @@
penetrating = 0
armor_penetration = 5
-/obj/projectile/bullet/cannonball/explosive/on_impact(var/atom/A)
- explosion(A, -1, 1, 2)
- ..()
+/obj/projectile/bullet/cannonball/explosive/on_hit(atom/target, blocked, def_zone)
+ explosion(target, -1, 1, 2)
+ . = ..()
/obj/projectile/bullet/nuke
name = "miniaturized nuclear warhead"
@@ -529,15 +532,15 @@
damage = 25
anti_materiel_potential = 2
-/obj/projectile/bullet/nuke/on_impact(var/atom/A)
+/obj/projectile/bullet/nuke/on_hit(atom/target, blocked, def_zone)
for(var/mob/living/carbon/human/mob in GLOB.human_mob_list)
var/turf/T = get_turf(mob)
if(T && (loc.z == T.z))
if(ishuman(mob))
mob.apply_damage(250, DAMAGE_RADIATION, damage_flags = DAMAGE_FLAG_DISPERSED)
- new /obj/effect/temp_visual/nuke(A.loc)
- explosion(A,2,5,9)
- ..()
+ new /obj/effect/temp_visual/nuke(target.loc)
+ explosion(target,2,5,9)
+ . = ..()
/obj/projectile/bullet/shard
name = "shard"
@@ -559,9 +562,9 @@
penetrating = FALSE
var/heavy_impact_range = 1
-/obj/projectile/bullet/recoilless_rifle/on_impact(var/atom/A)
- explosion(A, -1, heavy_impact_range, 2)
- ..()
+/obj/projectile/bullet/recoilless_rifle/on_hit(atom/target, blocked, def_zone)
+ explosion(target, -1, heavy_impact_range, 2)
+ . = ..()
/obj/projectile/bullet/peac
name = "anti-tank missile"
@@ -581,8 +584,10 @@
return FALSE
return ..()
-/obj/projectile/bullet/peac/on_impact(var/atom/hit_atom)
- explosion(hit_atom, devastation_range, heavy_impact_range, light_impact_range)
+/obj/projectile/bullet/peac/on_hit(atom/target, blocked, def_zone)
+ . = ..()
+
+ explosion(target, devastation_range, heavy_impact_range, light_impact_range)
/obj/projectile/bullet/peac/he
name = "high-explosive missile"
@@ -600,12 +605,13 @@
light_impact_range = 1
-/obj/projectile/bullet/peac/shrapnel/preparePixelProjectile()
+/obj/projectile/bullet/peac/shrapnel/preparePixelProjectile(atom/target, atom/source, list/modifiers, deviation)
. = ..()
range = get_dist(firer, original)
-/obj/projectile/bullet/peac/shrapnel/on_impact(var/atom/hit_atom)
- ..()
+/obj/projectile/bullet/peac/shrapnel/on_hit(atom/target, blocked, def_zone)
+ . = ..()
+
spawn_shrapnel(starting ? get_dir(starting, original) : dir)
/obj/projectile/bullet/peac/shrapnel/proc/spawn_shrapnel(var/shrapnel_dir)
@@ -622,7 +628,9 @@
P.damage = 30
P.pellets = 4
P.range_step = 3
- P.shot_from = src
P.range = 15
P.name = "shrapnel"
- P.launch_projectile(T)
+ P.preparePixelProjectile(T, src)
+ P.firer = src
+ P.fired_from = src
+ P.fire()
diff --git a/code/modules/projectiles/projectile/change.dm b/code/modules/projectiles/projectile/change.dm
index c95f3da25b6..6d370295be9 100644
--- a/code/modules/projectiles/projectile/change.dm
+++ b/code/modules/projectiles/projectile/change.dm
@@ -3,11 +3,11 @@
icon_state = "ice_1"
damage = 0
damage_type = DAMAGE_BURN
- nodamage = 1
check_armor = "energy"
-/obj/projectile/change/on_hit(var/atom/change)
- wabbajack(change)
+/obj/projectile/change/on_hit(atom/target, blocked, def_zone)
+ . = ..()
+ wabbajack(target)
/obj/projectile/change/proc/wabbajack(var/mob/M)
if(istype(M, /mob/living) && M.stat != DEAD)
diff --git a/code/modules/projectiles/projectile/energy.dm b/code/modules/projectiles/projectile/energy.dm
index 84c45d39af4..c39a3763336 100644
--- a/code/modules/projectiles/projectile/energy.dm
+++ b/code/modules/projectiles/projectile/energy.dm
@@ -16,8 +16,10 @@
var/brightness = 7
var/light_duration = 5
-/obj/projectile/energy/flash/on_impact(var/atom/A, affected_limb)
- var/turf/T = flash_range ? src.loc : get_turf(A)
+/obj/projectile/energy/flash/on_hit(atom/target, blocked, def_zone)
+ . = ..()
+
+ var/turf/T = flash_range ? src.loc : get_turf(target)
if(!istype(T))
return
@@ -25,8 +27,6 @@
for(var/mob/living/M in viewers(T, flash_range))
if(M.flash_act(ignore_inherent = TRUE))
M.confused = rand(5, 15)
- else if(affected_limb && M == A)
- M.confused = rand(2, 7)
//snap pop
playsound(src, 'sound/effects/snap.ogg', 50, 1)
@@ -111,12 +111,12 @@
light_range = 4
light_color = "#b5ff5b"
-/obj/projectile/energy/bfg/on_impact(var/atom/A)
- if(ismob(A))
- var/mob/M = A
+/obj/projectile/energy/bfg/on_hit(atom/target, blocked, def_zone)
+ if(ismob(target))
+ var/mob/M = target
M.gib()
- explosion(A, -1, 0, 5)
- ..()
+ explosion(target, -1, 0, 5)
+ . = ..()
/obj/projectile/energy/bfg/New()
var/matrix/M = matrix()
@@ -124,21 +124,21 @@
src.transform = M
..()
-/obj/projectile/energy/bfg/after_move()
- for(var/a in range(1, src))
- if(isliving(a) && a != firer)
- var/mob/living/M = a
- if(M.stat == DEAD)
- M.gib()
- else
- M.apply_damage(60, DAMAGE_BRUTE, BP_HEAD)
- playsound(src, 'sound/magic/LightningShock.ogg', 75, 1)
- else if(isturf(a) || isobj(a))
- var/atom/A = a
- if(!A.density)
- continue
- A.ex_act(2)
- playsound(src, 'sound/magic/LightningShock.ogg', 75, 1)
+// /obj/projectile/energy/bfg/after_move()
+// for(var/a in range(1, src))
+// if(isliving(a) && a != firer)
+// var/mob/living/M = a
+// if(M.stat == DEAD)
+// M.gib()
+// else
+// M.apply_damage(60, DAMAGE_BRUTE, BP_HEAD)
+// playsound(src, 'sound/magic/LightningShock.ogg', 75, 1)
+// else if(isturf(a) || isobj(a))
+// var/atom/A = a
+// if(!A.density)
+// continue
+// A.ex_act(2)
+// playsound(src, 'sound/magic/LightningShock.ogg', 75, 1)
/obj/projectile/energy/gravitydisabler
name = "gravity disabler"
@@ -153,7 +153,7 @@
light_range = 4
light_color = "#b5ff5b"
-/obj/projectile/energy/gravitydisabler/on_impact(atom/target)
+/obj/projectile/energy/gravitydisabler/on_hit(atom/target, blocked, def_zone)
. = ..()
var/area/A = get_area(target)
if(A && A.has_gravity())
@@ -176,7 +176,7 @@
damage_flags = DAMAGE_FLAG_LASER
pass_flags = PASSTABLE | PASSGLASS | PASSGRILLE | PASSRAILING
muzzle_type = /obj/effect/projectile/muzzle/bolt
- hit_effect = /obj/effect/temp_visual/blaster_effect
+ impact_effect_type = /obj/effect/temp_visual/blaster_effect
/obj/projectile/energy/blaster/disruptor
damage = 20
diff --git a/code/modules/projectiles/projectile/force.dm b/code/modules/projectiles/projectile/force.dm
index 21ef0171747..529890e506a 100644
--- a/code/modules/projectiles/projectile/force.dm
+++ b/code/modules/projectiles/projectile/force.dm
@@ -10,13 +10,15 @@
name = "force bolt"
icon_state = "bluespace"
-/obj/projectile/forcebolt/on_hit(var/atom/movable/target, var/blocked = 0)
- if(istype(target))
+/obj/projectile/forcebolt/on_hit(atom/target, blocked, def_zone)
+ . = ..()
+ if(ismovable(target))
+ var/atom/movable/M = target
var/throwdir = get_dir(firer,target)
- target.throw_at(get_edge_target_turf(target, throwdir),10,10)
- return 1
+ M.throw_at(get_edge_target_turf(target, throwdir),10,10)
+ return BULLET_ACT_HIT
-/obj/projectile/forcebolt/strong/on_hit(var/atom/target, var/blocked = 0)
+/obj/projectile/forcebolt/strong/on_hit(atom/target, blocked, def_zone)
for(var/mob/M in hearers(2, src))
if(M.loc != src.loc)
var/throwdir = get_dir(firer,target)
diff --git a/code/modules/projectiles/projectile/special.dm b/code/modules/projectiles/projectile/special.dm
index e0d08e12656..65efd76f823 100644
--- a/code/modules/projectiles/projectile/special.dm
+++ b/code/modules/projectiles/projectile/special.dm
@@ -7,12 +7,16 @@
check_armor = "energy"
var/pulse_range = 1
-/obj/projectile/ion/on_impact(var/atom/A)
- empulse(A, pulse_range, pulse_range)
+/obj/projectile/ion/on_hit(atom/target, blocked, def_zone)
+ . = ..()
-/obj/projectile/ion/stun/on_impact(var/atom/A)
- if(isipc(A))
- var/mob/living/carbon/human/H = A
+ empulse(target, pulse_range, pulse_range)
+
+/obj/projectile/ion/stun/on_hit(atom/target, blocked, def_zone)
+ . = ..()
+
+ if(isipc(target))
+ var/mob/living/carbon/human/H = target
var/obj/item/organ/internal/surge/s = H.internal_organs_by_name["surge"]
if(!isnull(s))
if(s.surge_left >= 0.5)
@@ -28,8 +32,8 @@
return
else
to_chat(src, SPAN_DANGER("Warning: EMP detected, integrated surge prevention module is fried and unable to protect from EMP. Replacement recommended."))
- if (isrobot(A))
- var/mob/living/silicon/robot/R = A
+ if (isrobot(target))
+ var/mob/living/silicon/robot/R = target
var/datum/robot_component/surge/C = R.components["surge"]
if(C && C.installed)
if(C.surge_left >= 0.5)
@@ -48,7 +52,7 @@
R.emp_act(EMP_LIGHT) // Borgs emp_act is 1-2
else
- A.emp_act(EMP_LIGHT)
+ target.emp_act(EMP_LIGHT)
return
/obj/projectile/ion/small
@@ -71,16 +75,18 @@
sharp = 1
edge = TRUE
-/obj/projectile/bullet/gyro/on_impact(var/atom/A)
- explosion(A, -1, 0, 2)
- ..()
+/obj/projectile/bullet/gyro/on_hit(atom/target, blocked, def_zone)
+ explosion(target, -1, 0, 2)
+ . = ..()
/obj/projectile/bullet/gyro/law
name ="high-ex round"
icon_state= "bolter"
damage = 15
-/obj/projectile/bullet/gyro/law/on_hit(var/atom/target, var/blocked = 0)
+/obj/projectile/bullet/gyro/law/on_hit(atom/target, blocked, def_zone)
+ . = ..()
+
explosion(target, -1, 0, 2)
var/obj/T = target
var/throwdir = get_dir(firer,target)
@@ -92,12 +98,12 @@
icon_state = "ice_2"
damage = 0
damage_type = DAMAGE_BURN
- nodamage = 1
check_armor = "energy"
//var/temperature = 300
-/obj/projectile/temp/on_hit(var/atom/target, var/blocked = 0)//These two could likely check temp protection on the mob
+/obj/projectile/temp/on_hit(atom/target, blocked, def_zone)//These two could likely check temp protection on the mob
+ . = ..()
if(istype(target, /mob/living))
var/mob/M = target
M.bodytemperature = -273
@@ -109,7 +115,6 @@
icon_state = "small1"
damage = 0
damage_type = DAMAGE_BRUTE
- nodamage = 1
check_armor = "bullet"
/obj/projectile/meteor/Collide(atom/A)
@@ -136,7 +141,6 @@
icon_state = "energy"
damage = 0
damage_type = DAMAGE_TOXIN
- nodamage = 1
check_armor = "energy"
/obj/projectile/energy/floramut/gene
@@ -144,10 +148,10 @@
icon_state = "energy2"
damage = 0
damage_type = DAMAGE_TOXIN
- nodamage = TRUE
var/singleton/plantgene/gene = null
-/obj/projectile/energy/floramut/on_hit(var/atom/target, var/blocked = 0)
+/obj/projectile/energy/floramut/on_hit(atom/target, blocked, def_zone)
+ . = ..()
var/mob/living/M = target
if(ishuman(target))
var/mob/living/carbon/human/H = M
@@ -177,10 +181,10 @@
icon_state = "energy2"
damage = 0
damage_type = DAMAGE_TOXIN
- nodamage = 1
check_armor = "energy"
-/obj/projectile/energy/florayield/on_hit(var/atom/target, var/blocked = 0)
+/obj/projectile/energy/florayield/on_hit(atom/target, blocked, def_zone)
+ . = ..()
var/mob/M = target
if(ishuman(target)) //These rays make plantmen fat.
var/mob/living/carbon/human/H = M
@@ -195,7 +199,8 @@
/obj/projectile/beam/mindflayer
name = "flayer ray"
-/obj/projectile/beam/mindflayer/on_hit(var/atom/target, var/blocked = 0)
+/obj/projectile/beam/mindflayer/on_hit(atom/target, blocked, def_zone)
+ . = ..()
if(ishuman(target))
var/mob/living/carbon/human/M = target
M.adjustBrainLoss(5)
@@ -209,16 +214,15 @@
sharp = 1
edge = TRUE
-/obj/projectile/bullet/trod/on_impact(var/atom/A)
- explosion(A, 0, 0, 4)
- ..()
+/obj/projectile/bullet/trod/on_hit(atom/target, blocked, def_zone)
+ explosion(target, 0, 0, 4)
+ . = ..()
/obj/projectile/chameleon
name = "bullet"
icon_state = "bullet"
- damage = 1 // stop trying to murderbone with a fake gun dumbass!!!
+ damage = 0 // stop trying to murderbone with a fake gun dumbass!!!
embed = 0 // nope
- nodamage = 1
damage_type = DAMAGE_PAIN
muzzle_type = /obj/effect/projectile/muzzle/bullet
@@ -230,9 +234,9 @@
armor_penetration = 80
penetrating = 1
-/obj/projectile/bullet/cannon/on_impact(var/atom/A)
- explosion(A, 1, 2, 3, 3)
- ..()
+/obj/projectile/bullet/cannon/on_hit(atom/target, blocked, def_zone)
+ explosion(target, 1, 2, 3, 3)
+ . = ..()
//magic
@@ -251,9 +255,9 @@
damage = 20
damage_type = DAMAGE_BURN
-/obj/projectile/magic/fireball/on_impact(var/atom/A)
- explosion(A, 0, 0, 4)
- ..()
+/obj/projectile/magic/fireball/on_hit(atom/target, blocked, def_zone)
+ explosion(target, 0, 0, 4)
+ . = ..()
/obj/projectile/magic/teleport //literaly bluespace crystal code, because i am lazy and it seems to work
name = "bolt of teleportation"
@@ -261,12 +265,12 @@
icon_state = "energy2"
var/blink_range = 8
-/obj/projectile/magic/teleport/on_hit(var/atom/hit_atom)
- var/turf/T = get_turf(hit_atom)
+/obj/projectile/magic/teleport/on_hit(atom/target, blocked, def_zone)
+ var/turf/T = get_turf(target)
single_spark(T)
playsound(src.loc, /singleton/sound_category/spark_sound, 50, 1)
- if(isliving(hit_atom))
- blink_mob(hit_atom)
+ if(isliving(target))
+ blink_mob(target)
return ..()
/obj/projectile/magic/teleport/proc/blink_mob(mob/living/L)
@@ -316,7 +320,6 @@
damage = 35
damage_type = DAMAGE_BRUTE
impact_sounds = list(BULLET_IMPACT_MEAT = SOUNDS_BULLET_MEAT, BULLET_IMPACT_METAL = SOUNDS_BULLET_METAL)
- nodamage = FALSE
check_armor = "melee"
embed = TRUE
sharp = TRUE
diff --git a/code/modules/projectiles/projectile/trace.dm b/code/modules/projectiles/projectile/trace.dm
index b5620b05959..7a83a07ee47 100644
--- a/code/modules/projectiles/projectile/trace.dm
+++ b/code/modules/projectiles/projectile/trace.dm
@@ -10,16 +10,16 @@
trace.obj_flags = obj_flags
trace.pass_flags = pass_flags
- return trace.launch_projectile(target) //Test it!
+ trace.preparePixelProjectile(target, firer)
+ trace.firer = firer
-/obj/projectile/proc/_check_fire(atom/target as mob, mob/living/user as mob) //Checks if you can hit them or not.
- check_trajectory(target, user, pass_flags, obj_flags)
+ return trace.fire()
//"Tracing" projectile
/obj/projectile/test //Used to see if you can hit them.
- invisibility = 101 //Nope! Can't see me!
+ invisibility = INVISIBILITY_ABSTRACT //Nope! Can't see me!
hitscan = TRUE
- nodamage = TRUE
+ do_not_log = TRUE
damage = 0
var/list/hit = list()
@@ -34,5 +34,6 @@
hit |= A
return ..()
-/obj/projectile/test/attack_mob()
+/obj/projectile/test/on_hit(atom/target, blocked, def_zone)
+ SHOULD_CALL_PARENT(FALSE)
return
diff --git a/code/modules/psionics/abilities/hollow_purple.dm b/code/modules/psionics/abilities/hollow_purple.dm
index b021572b2f2..93f487ad581 100644
--- a/code/modules/psionics/abilities/hollow_purple.dm
+++ b/code/modules/psionics/abilities/hollow_purple.dm
@@ -72,35 +72,27 @@
/obj/projectile/hollow_purple/Destroy()
return ..()
-/obj/projectile/hollow_purple/on_impact(var/atom/A)
- if(ismob(A))
- if(A != firer)
- var/mob/M = A
- M.gib()
- explosion(A, 5, 5, 5)
- ..()
-
/obj/projectile/hollow_purple/on_hit(atom/target, blocked, def_zone)
if(ismob(target))
if(target != firer)
var/mob/M = target
M.gib()
explosion(target, 5, 5, 5)
- ..()
+ . = ..()
/obj/projectile/hollow_purple/check_penetrate(atom/A)
on_hit(A)
return TRUE
-/obj/projectile/hollow_purple/after_move()
- for(var/a in range(3, src))
- if(isliving(a) && a != firer)
- var/mob/living/M = a
- M.gib()
- playsound(src, 'sound/magic/LightningShock.ogg', 75, 1)
- else if(isturf(a) || isobj(a))
- var/atom/A = a
- if(!A.density)
- continue
- A.ex_act(3)
- playsound(src, 'sound/magic/LightningShock.ogg', 75, 1)
+// /obj/projectile/hollow_purple/after_move()
+// for(var/a in range(3, src))
+// if(isliving(a) && a != firer)
+// var/mob/living/M = a
+// M.gib()
+// playsound(src, 'sound/magic/LightningShock.ogg', 75, 1)
+// else if(isturf(a) || isobj(a))
+// var/atom/A = a
+// if(!A.density)
+// continue
+// A.ex_act(3)
+// playsound(src, 'sound/magic/LightningShock.ogg', 75, 1)
diff --git a/code/modules/psionics/abilities/lightning.dm b/code/modules/psionics/abilities/lightning.dm
index 4c478c47b86..04924bc03c7 100644
--- a/code/modules/psionics/abilities/lightning.dm
+++ b/code/modules/psionics/abilities/lightning.dm
@@ -54,7 +54,4 @@
/obj/projectile/beam/psi_lightning/wide/Initialize()
. = ..()
for(var/i = 1 to 4)
- var/turf/new_turf = get_random_turf_in_range(get_turf(firer), i + rand(0, i), 0, TRUE, FALSE)
- var/obj/projectile/beam/psi_lightning/pellet/pellet = new type(new_turf)
- var/turf/front_turf = get_step(pellet, pellet.dir)
- INVOKE_ASYNC(pellet, TYPE_PROC_REF(/obj/projectile/beam/psi_lightning/pellet, launch_projectile), front_turf)
+ INVOKE_ASYNC(src, TYPE_PROC_REF(/atom, fire_projectile), /obj/projectile/beam/psi_lightning/pellet, get_random_turf_in_range(get_turf(firer), i + rand(0, i), 0, TRUE, FALSE), firer = firer)
diff --git a/code/modules/reagents/reagent_containers/food/cans.dm b/code/modules/reagents/reagent_containers/food/cans.dm
index 90e192fe10b..27220a2b6c4 100644
--- a/code/modules/reagents/reagent_containers/food/cans.dm
+++ b/code/modules/reagents/reagent_containers/food/cans.dm
@@ -208,13 +208,16 @@
else
desc = initial(desc)
-/obj/item/reagent_containers/food/drinks/cans/bullet_act(obj/projectile/P)
- if(P.firer && REAGENT_VOLUME(reagents, /singleton/reagent/fuel) >= LETHAL_FUEL_CAPACITY)
- visible_message(SPAN_DANGER("\The [name] is hit by the [P]!"))
- log_and_message_admins("shot an improvised [name] explosive", P.firer)
- log_game("[key_name(P.firer)] shot improvised grenade at [loc.loc.name] ([loc.x],[loc.y],[loc.z]).")
- detonate(TRUE)
+/obj/item/reagent_containers/food/drinks/cans/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
. = ..()
+ if(. != BULLET_ACT_HIT)
+ return .
+
+ if(hitting_projectile.firer && REAGENT_VOLUME(reagents, /singleton/reagent/fuel) >= LETHAL_FUEL_CAPACITY)
+ visible_message(SPAN_DANGER("\The [name] is hit by the [hitting_projectile]!"))
+ log_and_message_admins("shot an improvised [name] explosive", hitting_projectile.firer)
+ log_game("[key_name(hitting_projectile.firer)] shot improvised grenade at [loc.loc.name] ([loc.x],[loc.y],[loc.z]).")
+ detonate(TRUE)
/obj/item/reagent_containers/food/drinks/cans/ex_act(severity)
detonate(TRUE)
diff --git a/code/modules/reagents/reagent_containers/food/drinks/bottle.dm b/code/modules/reagents/reagent_containers/food/drinks/bottle.dm
index 672c6d91b89..02ac8dd89a8 100644
--- a/code/modules/reagents/reagent_containers/food/drinks/bottle.dm
+++ b/code/modules/reagents/reagent_containers/food/drinks/bottle.dm
@@ -187,7 +187,11 @@
return blocked
-/obj/item/reagent_containers/food/drinks/bottle/bullet_act()
+/obj/item/reagent_containers/food/drinks/bottle/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
+ . = ..()
+ if(. != BULLET_ACT_HIT)
+ return .
+
smash(loc)
/*
@@ -450,8 +454,8 @@
agony = 10 // ow!
var/drop_type = /obj/item/trash/champagne_cork
-/obj/projectile/bullet/champagne_cork/on_impact(var/atom/A)
- ..()
+/obj/projectile/bullet/champagne_cork/on_hit(atom/target, blocked, def_zone)
+ . = ..()
new drop_type(src.loc) //always use src.loc so that ash doesn't end up inside windows
/obj/item/trash/champagne_cork
diff --git a/code/modules/reagents/reagent_containers/syringes.dm b/code/modules/reagents/reagent_containers/syringes.dm
index 31c71d218bd..a808f6538ad 100644
--- a/code/modules/reagents/reagent_containers/syringes.dm
+++ b/code/modules/reagents/reagent_containers/syringes.dm
@@ -314,7 +314,7 @@
var/hit_area = affecting.name
- if((user != target) && H.check_shields(7, src, user, "\the [src]"))
+ if((user != target) && (H.check_shields(7, src, user, "\the [src]") != BULLET_ACT_HIT))
return
var/armor = H.get_blocked_ratio(target_zone, DAMAGE_BRUTE, damage_flags = DAMAGE_FLAG_SHARP, damage = 5)*100
diff --git a/code/modules/reagents/reagent_dispenser.dm b/code/modules/reagents/reagent_dispenser.dm
index 971f65662cb..81d64d4137d 100644
--- a/code/modules/reagents/reagent_dispenser.dm
+++ b/code/modules/reagents/reagent_dispenser.dm
@@ -194,13 +194,17 @@
src.defuse = 0
message_admins("[key_name_admin(user)] reset fuse on fueltank at ([loc.x],[loc.y],[loc.z]).")
-/obj/structure/reagent_dispensers/fueltank/bullet_act(var/obj/projectile/Proj)
- if(Proj.get_structure_damage())
- if(istype(Proj.firer))
- log_and_message_admins("shot a welding tank", Proj.firer)
- log_game("[key_name(Proj.firer)] shot fueltank at [loc.loc.name] ([loc.x],[loc.y],[loc.z]).")
+/obj/structure/reagent_dispensers/fueltank/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
+ . = ..()
+ if(. != BULLET_ACT_HIT)
+ return .
- if(!istype(Proj ,/obj/projectile/beam/laser_tag) && !istype(Proj ,/obj/projectile/beam/practice) && !istype(Proj ,/obj/projectile/kinetic))
+ if(hitting_projectile.get_structure_damage())
+ if(istype(hitting_projectile.firer))
+ log_and_message_admins("shot a welding tank", hitting_projectile.firer)
+ log_game("[key_name(hitting_projectile.firer)] shot fueltank at [loc.loc.name] ([loc.x],[loc.y],[loc.z]).")
+
+ if(!istype(hitting_projectile ,/obj/projectile/beam/laser_tag) && !istype(hitting_projectile ,/obj/projectile/beam/practice) && !istype(hitting_projectile ,/obj/projectile/kinetic))
ex_act(2.0)
/obj/structure/reagent_dispensers/fueltank/ex_act(var/severity = 3.0)
@@ -396,8 +400,12 @@
capacity = 5000
reagents_to_add = list(/singleton/reagent/nutriment/triglyceride/oil/corn = 5000)
-/obj/structure/reagent_dispensers/cookingoil/bullet_act(var/obj/projectile/Proj)
- if(Proj.get_structure_damage())
+/obj/structure/reagent_dispensers/cookingoil/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
+ . = ..()
+ if(. != BULLET_ACT_HIT)
+ return .
+
+ if(hitting_projectile.get_structure_damage())
ex_act(2.0)
//Coolant tank
@@ -409,9 +417,13 @@
amount_per_transfer_from_this = 10
reagents_to_add = list(/singleton/reagent/coolant = 1000)
-/obj/structure/reagent_dispensers/coolanttank/bullet_act(var/obj/projectile/Proj)
- if(Proj.get_structure_damage())
- if (Proj.damage_type != DAMAGE_PAIN)
+/obj/structure/reagent_dispensers/coolanttank/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
+ . = ..()
+ if(. != BULLET_ACT_HIT)
+ return .
+
+ if(hitting_projectile.get_structure_damage())
+ if (hitting_projectile.damage_type != DAMAGE_PAIN)
explode()
/obj/structure/reagent_dispensers/coolanttank/ex_act(var/severity = 2.0)
diff --git a/code/modules/recycling/disposal.dm b/code/modules/recycling/disposal.dm
index da124ec062a..db15854eaf1 100644
--- a/code/modules/recycling/disposal.dm
+++ b/code/modules/recycling/disposal.dm
@@ -596,6 +596,8 @@
qdel(H)
/obj/machinery/disposal/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
+ if(mover?.movement_type & PHASING)
+ return TRUE
if(istype(mover, /obj/projectile))
return 1
if(istype(mover,/obj/item) && mover.throwing)
diff --git a/code/modules/research/xenoarchaeology/artifact/artifact_crystal_madness.dm b/code/modules/research/xenoarchaeology/artifact/artifact_crystal_madness.dm
index 3388ab5e3ee..3514e59909e 100644
--- a/code/modules/research/xenoarchaeology/artifact/artifact_crystal_madness.dm
+++ b/code/modules/research/xenoarchaeology/artifact/artifact_crystal_madness.dm
@@ -117,39 +117,43 @@
A large crystal, seemingly floating in the air, and giving off a light blue glow.\
"
-/obj/structure/crystal_madness/bullet_act(var/obj/projectile/projectile)
- if(istype(projectile, /obj/projectile/bullet))
+/obj/structure/crystal_madness/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
+ . = ..()
+ if(. != BULLET_ACT_HIT)
+ return .
+
+ if(istype(hitting_projectile, /obj/projectile/bullet))
src.visible_message(
pick(
- SPAN_WARNING("\The [src] appears to deflect \the [projectile], shattering it into dust."),
- SPAN_WARNING("\The [src] appears to deflect \the [projectile], sending it up in the air."),
- SPAN_WARNING("\The [src] appears to bounce off \the [projectile], sending it into the floor."),
- SPAN_WARNING("\The [src] appears to deflect \the [projectile]."),
- SPAN_WARNING("\The [src] appears to deflect \the [projectile]. It has little effect."),
- SPAN_WARNING("\The [src] appears to bounce off \the [projectile]."),
- SPAN_WARNING("\The [src] appears to bounce off \the [projectile]. It has little effect."),
+ SPAN_WARNING("\The [src] appears to deflect \the [hitting_projectile], shattering it into dust."),
+ SPAN_WARNING("\The [src] appears to deflect \the [hitting_projectile], sending it up in the air."),
+ SPAN_WARNING("\The [src] appears to bounce off \the [hitting_projectile], sending it into the floor."),
+ SPAN_WARNING("\The [src] appears to deflect \the [hitting_projectile]."),
+ SPAN_WARNING("\The [src] appears to deflect \the [hitting_projectile]. It has little effect."),
+ SPAN_WARNING("\The [src] appears to bounce off \the [hitting_projectile]."),
+ SPAN_WARNING("\The [src] appears to bounce off \the [hitting_projectile]. It has little effect."),
),
)
- else if(istype(projectile, /obj/projectile/energy))
+ else if(istype(hitting_projectile, /obj/projectile/energy))
src.visible_message(
pick(
- SPAN_WARNING("\The [src] appears to absorb \the [projectile]."),
- SPAN_WARNING("\The [src] appears to consume \the [projectile]."),
- SPAN_WARNING("\The [src] appears to absorb \the [projectile] entirely."),
- SPAN_WARNING("\The [src] appears to absorb \the [projectile]. It has little effect."),
- SPAN_WARNING("\The [src] appears to consume \the [projectile]. It has little effect."),
+ SPAN_WARNING("\The [src] appears to absorb \the [hitting_projectile]."),
+ SPAN_WARNING("\The [src] appears to consume \the [hitting_projectile]."),
+ SPAN_WARNING("\The [src] appears to absorb \the [hitting_projectile] entirely."),
+ SPAN_WARNING("\The [src] appears to absorb \the [hitting_projectile]. It has little effect."),
+ SPAN_WARNING("\The [src] appears to consume \the [hitting_projectile]. It has little effect."),
),
)
- else if(istype(projectile, /obj/projectile/beam))
- var/damage = projectile.get_structure_damage()
+ else if(istype(hitting_projectile, /obj/projectile/beam))
+ var/damage = hitting_projectile.get_structure_damage()
if(damage < 20)
src.visible_message(
pick(
- SPAN_WARNING("\The [src] appears to absorb \the [projectile]."),
- SPAN_WARNING("\The [src] appears to consume \the [projectile]."),
- SPAN_WARNING("\The [src] appears to absorb \the [projectile] entirely."),
- SPAN_WARNING("\The [src] appears to absorb \the [projectile]. It has little effect."),
- SPAN_WARNING("\The [src] appears to consume \the [projectile]. It has little effect."),
+ SPAN_WARNING("\The [src] appears to absorb \the [hitting_projectile]."),
+ SPAN_WARNING("\The [src] appears to consume \the [hitting_projectile]."),
+ SPAN_WARNING("\The [src] appears to absorb \the [hitting_projectile] entirely."),
+ SPAN_WARNING("\The [src] appears to absorb \the [hitting_projectile]. It has little effect."),
+ SPAN_WARNING("\The [src] appears to consume \the [hitting_projectile]. It has little effect."),
),
)
else
@@ -158,15 +162,15 @@
advance_stage()
src.visible_message(
pick(
- SPAN_WARNING("\The [src] appears to absorb \the [projectile] entirely."),
- SPAN_WARNING("\The [src] appears to absorb \the [projectile] completely."),
- SPAN_WARNING("\The [src] appears to absorb \the [projectile], and vibrates slightly."),
- SPAN_WARNING("\The [src] appears to absorb \the [projectile], and hums."),
- SPAN_WARNING("\The [src] appears to absorb \the [projectile]. It glows stronger momentarily."),
- SPAN_WARNING("\The [src] appears to absorb \the [projectile]. It glows brighter for a few seconds."),
- SPAN_WARNING("\The [src] appears to absorb \the [projectile]. You can hear it resonate."),
- SPAN_WARNING("\The [src] appears to absorb \the [projectile]. You can hear it vibrate."),
- SPAN_WARNING("\The [src] appears to absorb \the [projectile]. You can see it glow stronger for a few seconds."),
+ SPAN_WARNING("\The [src] appears to absorb \the [hitting_projectile] entirely."),
+ SPAN_WARNING("\The [src] appears to absorb \the [hitting_projectile] completely."),
+ SPAN_WARNING("\The [src] appears to absorb \the [hitting_projectile], and vibrates slightly."),
+ SPAN_WARNING("\The [src] appears to absorb \the [hitting_projectile], and hums."),
+ SPAN_WARNING("\The [src] appears to absorb \the [hitting_projectile]. It glows stronger momentarily."),
+ SPAN_WARNING("\The [src] appears to absorb \the [hitting_projectile]. It glows brighter for a few seconds."),
+ SPAN_WARNING("\The [src] appears to absorb \the [hitting_projectile]. You can hear it resonate."),
+ SPAN_WARNING("\The [src] appears to absorb \the [hitting_projectile]. You can hear it vibrate."),
+ SPAN_WARNING("\The [src] appears to absorb \the [hitting_projectile]. You can see it glow stronger for a few seconds."),
),
)
diff --git a/code/modules/research/xenoarchaeology/artifact/artifact_unknown.dm b/code/modules/research/xenoarchaeology/artifact/artifact_unknown.dm
index ca141b46047..ae6e2ec37d3 100644
--- a/code/modules/research/xenoarchaeology/artifact/artifact_unknown.dm
+++ b/code/modules/research/xenoarchaeology/artifact/artifact_unknown.dm
@@ -279,17 +279,18 @@
to_chat(H, "You accidentally touch [src].")
..()
-/obj/machinery/artifact/bullet_act(var/obj/projectile/P)
- if(istype(P,/obj/projectile/bullet) ||\
- istype(P,/obj/projectile/bullet/pistol/hivebotspike))
+/obj/machinery/artifact/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
+ . = ..()
+ if(. != BULLET_ACT_HIT)
+ return .
+
+ if(istype(hitting_projectile, /obj/projectile/bullet) || istype(hitting_projectile, /obj/projectile/bullet/pistol/hivebotspike))
if(my_effect.trigger == TRIGGER_FORCE)
my_effect.ToggleActivate()
if(secondary_effect?.trigger == TRIGGER_FORCE)
secondary_effect.ToggleActivate()
- else if(istype(P,/obj/projectile/beam) ||\
- istype(P,/obj/projectile/ion) ||\
- istype(P,/obj/projectile/energy))
+ else if(istype(hitting_projectile, /obj/projectile/beam) || istype(hitting_projectile, /obj/projectile/ion) || istype(hitting_projectile, /obj/projectile/energy))
if(my_effect.trigger == TRIGGER_ENERGY)
my_effect.ToggleActivate()
if(secondary_effect?.trigger == TRIGGER_ENERGY)
diff --git a/code/modules/shieldgen/emergency_shield.dm b/code/modules/shieldgen/emergency_shield.dm
index 6f1dc63dd7c..f6d7fef32b3 100644
--- a/code/modules/shieldgen/emergency_shield.dm
+++ b/code/modules/shieldgen/emergency_shield.dm
@@ -56,8 +56,13 @@
return ..()
/obj/machinery/shield/CanPass(atom/movable/mover, turf/target, height, air_group)
- if(!height || air_group) return FALSE
- else return ..()
+ if(mover?.movement_type & PHASING)
+ return TRUE
+
+ if(!height || air_group)
+ return FALSE
+ else
+ return ..()
/obj/machinery/shield/attackby(obj/item/attacking_item, mob/user)
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
@@ -74,9 +79,12 @@
..()
-/obj/machinery/shield/bullet_act(var/obj/projectile/Proj)
- health -= Proj.get_structure_damage()
- ..()
+/obj/machinery/shield/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
+ . = ..()
+ if(. != BULLET_ACT_HIT)
+ return .
+
+ health -= hitting_projectile.get_structure_damage()
check_failure()
opacity = 1
spawn(20) if(src) opacity = FALSE
diff --git a/code/modules/shieldgen/energy_field.dm b/code/modules/shieldgen/energy_field.dm
index 6ad41fc515f..7a2bc95baa8 100644
--- a/code/modules/shieldgen/energy_field.dm
+++ b/code/modules/shieldgen/energy_field.dm
@@ -54,8 +54,12 @@
/obj/effect/energy_field/ex_act(var/severity)
Stress(0.5 + severity)
-/obj/effect/energy_field/bullet_act(var/obj/projectile/Proj)
- Stress(Proj.get_structure_damage() / 10)
+/obj/effect/energy_field/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
+ . = ..()
+ if(. != BULLET_ACT_HIT)
+ return .
+
+ Stress(hitting_projectile.get_structure_damage() / 10)
/obj/effect/energy_field/proc/Stress(var/severity)
strength -= severity
@@ -115,10 +119,7 @@
diffuse_check()
/obj/effect/energy_field/CanPass(atom/movable/mover, turf/target, height=1.5, air_group = 0)
- //Purpose: Determines if the object (or airflow) can pass this atom.
- //Called by: Movement, airflow.
- //Inputs: The moving atom (optional), target turf, "height" and air group
- //Outputs: Boolean if can pass.
+ if(mover?.movement_type & PHASING)
+ return TRUE
- //return (!density || !height || air_group)
return (!density || air_group)
diff --git a/code/modules/shieldgen/shieldwallgen.dm b/code/modules/shieldgen/shieldwallgen.dm
index eacc42b45d9..c456b3a8b33 100644
--- a/code/modules/shieldgen/shieldwallgen.dm
+++ b/code/modules/shieldgen/shieldwallgen.dm
@@ -195,11 +195,14 @@
alldir_cleanup()
return ..()
-/obj/machinery/shieldwallgen/bullet_act(var/obj/projectile/Proj)
- storedpower -= 400 * Proj.get_structure_damage()
+/obj/machinery/shieldwallgen/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
+ . = ..()
+ if(. != BULLET_ACT_HIT)
+ return .
+
+ storedpower -= 400 * hitting_projectile.get_structure_damage()
if(power_state >= POWER_STARTING)
- visible_message(SPAN_WARNING("\The [src]'s shielding sparks as \the [Proj] hits it!"))
- return ..()
+ visible_message(SPAN_WARNING("\The [src]'s shielding sparks as \the [hitting_projectile] hits it!"))
/obj/shieldwall
name = "energy shield"
@@ -255,16 +258,19 @@
gen_primary.storedpower -= power_usage / 2
gen_secondary.storedpower -= power_usage / 2
-/obj/shieldwall/bullet_act(var/obj/projectile/Proj)
+/obj/shieldwall/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
+ . = ..()
+ if(. != BULLET_ACT_HIT)
+ return .
+
if(needs_power)
var/obj/machinery/shieldwallgen/G
if(prob(50))
G = gen_primary
else
G = gen_secondary
- visible_message(SPAN_WARNING("\The [src] wobbles precariously as \the [Proj] impacts it!"))
- G.storedpower -= 400 * Proj.get_structure_damage()
- return ..()
+ visible_message(SPAN_WARNING("\The [src] wobbles precariously as \the [hitting_projectile] impacts it!"))
+ G.storedpower -= 400 * hitting_projectile.get_structure_damage()
/obj/shieldwall/ex_act(severity)
if(needs_power)
@@ -294,6 +300,8 @@
/obj/shieldwall/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
if(air_group || (height==0))
return TRUE
+ if(mover?.movement_type & PHASING)
+ return TRUE
if(istype(mover) && mover.pass_flags & PASSGLASS)
return prob(20)
else
diff --git a/code/modules/shuttles/shuttle_console.dm b/code/modules/shuttles/shuttle_console.dm
index 9450252dc27..85ef3cfc4c9 100644
--- a/code/modules/shuttles/shuttle_console.dm
+++ b/code/modules/shuttles/shuttle_console.dm
@@ -20,6 +20,8 @@
else
SSshuttle.lonely_shuttle_computers += src
+ RegisterSignal(src, COMSIG_ATOM_PRE_BULLET_ACT, PROC_REF(handle_bullet_act))
+
/obj/machinery/computer/shuttle_control/Destroy()
SSshuttle.lonely_shuttle_computers -= src
var/datum/shuttle/shuttle = SSshuttle.shuttles[shuttle_tag]
@@ -160,9 +162,6 @@
to_chat(user, "You short out the console's ID checking system. It's now available to everyone!")
return TRUE
-/obj/machinery/computer/shuttle_control/bullet_act(var/obj/projectile/Proj)
- visible_message("\The [Proj] ricochets off \the [src]!")
-
/obj/machinery/computer/shuttle_control/ex_act()
return
@@ -170,3 +169,9 @@
. = ..()
return
+
+/obj/machinery/computer/shuttle_control/proc/handle_bullet_act(datum/source, obj/projectile/projectile)
+ SIGNAL_HANDLER
+
+ visible_message("\The [projectile] ricochets off \the [src]!")
+ return COMPONENT_BULLET_BLOCKED
diff --git a/code/modules/spell_system/artifacts/items/poppet.dm b/code/modules/spell_system/artifacts/items/poppet.dm
index 106a952eb89..d30b764e8f1 100644
--- a/code/modules/spell_system/artifacts/items/poppet.dm
+++ b/code/modules/spell_system/artifacts/items/poppet.dm
@@ -140,10 +140,14 @@
if(H)
H.electrocute_act(power, src)
-/obj/item/poppet/bullet_act(var/obj/projectile/Proj)
+/obj/item/poppet/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
+ . = ..()
+ if(. != BULLET_ACT_HIT)
+ return .
+
var/mob/living/carbon/human/H = target.resolve()
if(H)
- H.apply_damage(Proj.damage, DAMAGE_PAIN)
+ H.apply_damage(hitting_projectile.damage, DAMAGE_PAIN)
/obj/item/poppet/fire_act(exposed_temperature, exposed_volume)
. = ..()
diff --git a/code/modules/spell_system/spells/spell_list/projectile.dm b/code/modules/spell_system/spells/spell_list/projectile.dm
index 64a258374bb..7ef01a1fcfb 100644
--- a/code/modules/spell_system/spells/spell_list/projectile.dm
+++ b/code/modules/spell_system/spells/spell_list/projectile.dm
@@ -26,15 +26,16 @@ If the spell_projectile is seeking, it will update its target every process and
if(!projectile)
return
-
- projectile.shot_from = user //fired from the user
projectile.hitscan = !proj_step_delay
projectile.speed = proj_step_delay
if(istype(projectile, /obj/projectile/spell_projectile))
var/obj/projectile/spell_projectile/SP = projectile
SP.carried = src //casting is magical
- projectile.launch_projectile(target, target_zone=BP_CHEST)
- return
+
+ projectile.preparePixelProjectile(target, user)
+ projectile.firer = user
+ projectile.fired_from = user
+ projectile.fire()
/spell/targeted/projectile/proc/choose_prox_targets(mob/user = usr, var/atom/movable/spell_holder)
var/list/targets = list()
diff --git a/code/modules/spell_system/spells/spell_list/self/conjure/forcewall.dm b/code/modules/spell_system/spells/spell_list/self/conjure/forcewall.dm
index db04501496b..942a63a6339 100644
--- a/code/modules/spell_system/spells/spell_list/self/conjure/forcewall.dm
+++ b/code/modules/spell_system/spells/spell_list/self/conjure/forcewall.dm
@@ -21,12 +21,15 @@
density = 1
unacidable = 1
-/obj/effect/forcefield/bullet_act(var/obj/projectile/Proj, var/def_zone)
+/obj/effect/forcefield/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
+ . = ..()
+ if(. != BULLET_ACT_HIT)
+ return .
+
var/turf/T = get_turf(src.loc)
if(T)
for(var/mob/M in T)
- Proj.on_hit(M,M.bullet_act(Proj, def_zone))
- return
+ hitting_projectile.on_hit(M,M.bullet_act(hitting_projectile, def_zone))
/obj/effect/forcefield/attackby(obj/item/attacking_item, mob/user)
..()
diff --git a/code/modules/spell_system/spells/spell_list/self/generic/ethereal_jaunt.dm b/code/modules/spell_system/spells/spell_list/self/generic/ethereal_jaunt.dm
index 80cdee763c4..9d69f383400 100644
--- a/code/modules/spell_system/spells/spell_list/self/generic/ethereal_jaunt.dm
+++ b/code/modules/spell_system/spells/spell_list/self/generic/ethereal_jaunt.dm
@@ -113,5 +113,3 @@
/obj/effect/dummy/spell_jaunt/ex_act(blah)
return
-/obj/effect/dummy/spell_jaunt/bullet_act(blah)
- return
diff --git a/code/modules/spell_system/spells/spell_projectile.dm b/code/modules/spell_system/spells/spell_projectile.dm
index 2accd3a8782..53b4669d530 100644
--- a/code/modules/spell_system/spells/spell_projectile.dm
+++ b/code/modules/spell_system/spells/spell_projectile.dm
@@ -2,7 +2,7 @@
name = "spell"
icon = 'icons/obj/projectiles.dmi'
- nodamage = 1 //Most of the time, anyways
+ damage = 0
var/spell/targeted/projectile/carried
@@ -24,14 +24,14 @@
/obj/projectile/spell_projectile/ex_act(var/severity = 2.0)
return
-/obj/projectile/spell_projectile/before_move()
- if(proj_trail && src && src.loc) //pretty trails
- var/obj/effect/overlay/trail = new /obj/effect/overlay(src.loc)
- trails += trail
- trail.icon = proj_trail_icon
- trail.icon_state = proj_trail_icon_state
- trail.density = 0
- addtimer(CALLBACK(src, PROC_REF(post_trail), trail), proj_trail_lifespan)
+// /obj/projectile/spell_projectile/before_move()
+// if(proj_trail && src && src.loc) //pretty trails
+// var/obj/effect/overlay/trail = new /obj/effect/overlay(src.loc)
+// trails += trail
+// trail.icon = proj_trail_icon
+// trail.icon_state = proj_trail_icon_state
+// trail.density = 0
+// addtimer(CALLBACK(src, PROC_REF(post_trail), trail), proj_trail_lifespan)
/obj/projectile/spell_projectile/proc/post_trail(obj/effect/overlay/trail)
trails -= trail
@@ -48,7 +48,8 @@
prox_cast(carried.choose_prox_targets(user = carried.holder, spell_holder = src))
return 1
-/obj/projectile/spell_projectile/on_impact()
+/obj/projectile/spell_projectile/on_hit(atom/target, blocked, def_zone)
+ . = ..()
if(loc && carried)
prox_cast(carried.choose_prox_targets(user = carried.holder, spell_holder = src))
return 1
diff --git a/code/modules/supermatter/supermatter.dm b/code/modules/supermatter/supermatter.dm
index d656595ab20..d48a676cc7b 100644
--- a/code/modules/supermatter/supermatter.dm
+++ b/code/modules/supermatter/supermatter.dm
@@ -340,15 +340,19 @@
return 1
-/obj/machinery/power/supermatter/bullet_act(var/obj/projectile/Proj)
+/obj/machinery/power/supermatter/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
+ . = ..()
+ if(. != BULLET_ACT_HIT)
+ return .
+
var/turf/L = loc
if(!istype(L)) // We don't run process() when we are in space
return 0 // This stops people from being able to really power up the supermatter
// Then bring it inside to explode instantly upon landing on a valid turf.
- var/proj_damage = Proj.get_structure_damage()
- if(istype(Proj, /obj/projectile/beam))
+ var/proj_damage = hitting_projectile.get_structure_damage()
+ if(istype(hitting_projectile, /obj/projectile/beam))
power += proj_damage * config_bullet_energy * CHARGING_FACTOR / POWER_FACTOR
else
damage += proj_damage * config_bullet_energy
diff --git a/code/modules/tables/interactions.dm b/code/modules/tables/interactions.dm
index 53f65f6d288..bfaecdee5cf 100644
--- a/code/modules/tables/interactions.dm
+++ b/code/modules/tables/interactions.dm
@@ -1,5 +1,10 @@
/obj/structure/table/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
- if(air_group || (height==0)) return 1
+ if(air_group || (height==0))
+ return TRUE
+
+ if(mover?.movement_type & PHASING)
+ return TRUE
+
if(istype(mover,/obj/projectile))
return (check_cover(mover,target))
if (flipped == 1)
diff --git a/code/modules/vehicles/animal.dm b/code/modules/vehicles/animal.dm
index 36688404d65..49ce0947b9a 100644
--- a/code/modules/vehicles/animal.dm
+++ b/code/modules/vehicles/animal.dm
@@ -83,11 +83,10 @@
to_chat(user, "You unbuckle [load] from \the [src]")
to_chat(load, "You were unbuckled from \the [src] by [user]")
-/obj/vehicle/animal/bullet_act(var/obj/projectile/Proj)
+/obj/vehicle/animal/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
var/datum/component/armor/armor_component = GetComponent(/datum/component/armor)
- if(buckled && prob((1 - armor_component.get_blocked(Proj.damage_type, Proj.damage_flags, Proj.armor_penetration))*100))
- buckled.bullet_act(Proj)
- return
+ if(buckled && prob((1 - armor_component.get_blocked(hitting_projectile.damage_type, hitting_projectile.damage_flags, hitting_projectile.armor_penetration))*100))
+ return buckled.bullet_act(arglist(args))
..()
/obj/vehicle/animal/relaymove(mob/living/user, direction)
diff --git a/code/modules/vehicles/bike.dm b/code/modules/vehicles/bike.dm
index 75e1b727158..92b2d912e78 100644
--- a/code/modules/vehicles/bike.dm
+++ b/code/modules/vehicles/bike.dm
@@ -251,10 +251,10 @@
..()
-/obj/vehicle/bike/bullet_act(var/obj/projectile/Proj)
+/obj/vehicle/bike/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
if(buckled && prob(protection_percent))
- buckled.bullet_act(Proj)
- return
+ return buckled.bullet_act(arglist(args))
+
..()
/obj/vehicle/bike/update_icon()
diff --git a/code/modules/vehicles/cargo_train.dm b/code/modules/vehicles/cargo_train.dm
index cd4b6c6c550..929b8e3645c 100644
--- a/code/modules/vehicles/cargo_train.dm
+++ b/code/modules/vehicles/cargo_train.dm
@@ -149,11 +149,11 @@
// Cargo trains are open topped, so you can shoot at the driver.
// Or you can shoot at the tug itself, if you're good.
-/obj/vehicle/train/cargo/bullet_act(var/obj/projectile/Proj)
- if (buckled && Proj.original == buckled)
- buckled.bullet_act(Proj)
+/obj/vehicle/train/cargo/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
+ if (buckled && hitting_projectile.original == buckled)
+ buckled.bullet_act(arglist(args))
else
- ..()
+ . = ..()
/obj/vehicle/train/cargo/update_icon()
if(open)
diff --git a/code/modules/vehicles/vehicle.dm b/code/modules/vehicles/vehicle.dm
index 259cf60c691..4d6a385be69 100644
--- a/code/modules/vehicles/vehicle.dm
+++ b/code/modules/vehicles/vehicle.dm
@@ -124,9 +124,12 @@
else
..()
-/obj/vehicle/bullet_act(var/obj/projectile/Proj)
- health -= Proj.get_structure_damage()
- ..()
+/obj/vehicle/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
+ . = ..()
+ if(. != BULLET_ACT_HIT)
+ return .
+
+ health -= hitting_projectile.get_structure_damage()
if (prob(20) && !organic)
spark(src, 5, GLOB.alldirs)
diff --git a/code/modules/vehicles/wasp_torpedo.dm b/code/modules/vehicles/wasp_torpedo.dm
index c2f00beca76..c3f7139efc3 100644
--- a/code/modules/vehicles/wasp_torpedo.dm
+++ b/code/modules/vehicles/wasp_torpedo.dm
@@ -88,11 +88,14 @@
return
..()
-/obj/vehicle/bike/wasp_torpedo/bullet_act(var/obj/projectile/Proj)
- if(Proj.get_structure_damage())
+/obj/vehicle/bike/wasp_torpedo/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
+ . = ..()
+ if(. != BULLET_ACT_HIT)
+ return .
+
+ if(hitting_projectile.get_structure_damage())
if(prob(10))
torpedo_explosion()
- ..()
/obj/vehicle/bike/wasp_torpedo/ex_act(severity)
switch(severity)
diff --git a/config/example/game_options.txt b/config/example/game_options.txt
index 44a682e08e5..2624af33c51 100644
--- a/config/example/game_options.txt
+++ b/config/example/game_options.txt
@@ -6,13 +6,6 @@ HEALTH_THRESHOLD_SOFTCRIT 50
## level of health at which a mob becomes dead
HEALTH_THRESHOLD_DEAD 0
-## Determines whether bones can be broken through excessive damage to the organ
-## 0 means bones can't break, 1 means they can
-BONES_CAN_BREAK 1
-## Determines whether limbs can be amputated through excessive damage to the organ
-## 0 means limbs can't be amputated, 1 means they can
-LIMBS_CAN_BREAK 1
-
## multiplier which enables organs to take more damage before bones breaking or limbs being destroyed
## 100 means normal, 50 means half
ORGAN_HEALTH_MULTIPLIER 100
diff --git a/html/changelogs/fluffyghost-updateprojectilecode.yml b/html/changelogs/fluffyghost-updateprojectilecode.yml
new file mode 100644
index 00000000000..adc4b3a06f4
--- /dev/null
+++ b/html/changelogs/fluffyghost-updateprojectilecode.yml
@@ -0,0 +1,69 @@
+################################
+# Example Changelog File
+#
+# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb.
+#
+# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.)
+# When it is, any changes listed below will disappear.
+#
+# Valid Prefixes:
+# bugfix
+# - (fixes bugs)
+# wip
+# - (work in progress)
+# qol
+# - (quality of life)
+# soundadd
+# - (adds a sound)
+# sounddel
+# - (removes a sound)
+# rscadd
+# - (adds a feature)
+# rscdel
+# - (removes a feature)
+# imageadd
+# - (adds an image or sprite)
+# imagedel
+# - (removes an image or sprite)
+# spellcheck
+# - (fixes spelling or grammar)
+# experiment
+# - (experimental change)
+# balance
+# - (balance changes)
+# code_imp
+# - (misc internal code change)
+# refactor
+# - (refactors code)
+# config
+# - (makes a change to the config files)
+# admin
+# - (makes changes to administrator tools)
+# server
+# - (miscellaneous changes to server)
+#################################
+
+# Your name.
+author: FluffyGhost
+
+# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again.
+delete-after: True
+
+# Any changes you've made. See valid prefix list above.
+# INDENT WITH TWO SPACES. NOT TABS. SPACES.
+# SCREW THIS UP AND IT WON'T WORK.
+# Also, this gets changed to [] after reading. Just remove the brackets when you add new shit.
+# Please surround your changes in double quotes ("). It works without them, but if you use certain characters it screws up compiling. The quotes will not show up in the changelog.
+changes:
+ - refactor: "Refactored the projectile code, mostly in line with TG's now."
+ - refactor: "Refactored various procs that are used or depends on it."
+ - rscadd: "Projectiles can now ricochet if enabled to."
+ - rscadd: "Damage falloffs with distance."
+ - rscadd: "Homing projectiles can now have accuracy falloff with distance."
+ - rscadd: "Projectiles have a maximum range."
+ - rscadd: "Muzzle flash is configurable per projectile."
+ - rscadd: "Impact effect of the projectile is configurable per projectile."
+ - rscadd: "Accuracy decreases with distance."
+ - rscadd: "Projectiles work with signals and emits them, for easy hooking up from other parts of the code."
+ - balance: "Meatshielding is now less effective ."
+ - rscadd: "Impact sound is now configurable per projectile."