diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm
index a3b70bf5613..fb343084460 100644
--- a/code/__DEFINES/misc.dm
+++ b/code/__DEFINES/misc.dm
@@ -517,3 +517,8 @@
return B.current.client
#define SERVER_MESSAGES_REDIS_CHANNEL "byond.servermessages"
+
+/// Projectile reflectability defines
+#define REFLECTABILITY_NEVER 0
+#define REFLECTABILITY_PHYSICAL 1
+#define REFLECTABILITY_ENERGY 2
diff --git a/code/game/gamemodes/cult/cult_items.dm b/code/game/gamemodes/cult/cult_items.dm
index 68bb13ad945..4fce21806ea 100644
--- a/code/game/gamemodes/cult/cult_items.dm
+++ b/code/game/gamemodes/cult/cult_items.dm
@@ -480,7 +480,7 @@
qdel(src)
return FALSE
- if(P.is_reflectable)
+ if(P.is_reflectable(REFLECTABILITY_ENERGY))
return FALSE //To avoid reflection chance double-dipping with block chance
// Hit by a melee weapon or blocked a projectile
diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm
index c2a1cfbcecd..8712052229b 100644
--- a/code/modules/mob/living/carbon/human/human_defense.dm
+++ b/code/modules/mob/living/carbon/human/human_defense.dm
@@ -13,7 +13,7 @@ emp_act
if(!dna.species.bullet_act(P, src))
add_attack_logs(P.firer, src, "hit by [P.type] but got deflected by species '[dna.species]'")
return FALSE
- if(P.is_reflectable)
+ if(P.is_reflectable(REFLECTABILITY_ENERGY))
var/can_reflect = check_reflect(def_zone)
var/reflected = FALSE
@@ -40,7 +40,7 @@ emp_act
if(!lying && !HAS_TRAIT(src, TRAIT_HULK) && mind.martial_art.try_deflect(src)) //But only if they're not lying down, and hulks can't do it
add_attack_logs(P.firer, src, "hit by [P.type] but got deflected by martial arts '[mind.martial_art]'")
playsound(src, pick('sound/weapons/bulletflyby.ogg', 'sound/weapons/bulletflyby2.ogg', 'sound/weapons/bulletflyby3.ogg'), 75, TRUE)
- if(HAS_TRAIT(src, TRAIT_PACIFISM))
+ if(HAS_TRAIT(src, TRAIT_PACIFISM) || !P.is_reflectable(REFLECTABILITY_PHYSICAL)) //if it cannot be reflected, it hits the floor. This is the exception to the rule
// Pacifists can deflect projectiles, but not reflect them.
// Instead, they deflect them into the ground below them.
var/turf/T = get_turf(src)
diff --git a/code/modules/mob/living/carbon/human/species/golem.dm b/code/modules/mob/living/carbon/human/species/golem.dm
index a15c9095ea2..6c9a0bd1599 100644
--- a/code/modules/mob/living/carbon/human/species/golem.dm
+++ b/code/modules/mob/living/carbon/human/species/golem.dm
@@ -412,7 +412,7 @@
/datum/species/golem/glass/bullet_act(obj/item/projectile/P, mob/living/carbon/human/H)
if(!(P.original == H && P.firer == H)) //self-shots don't reflect
- if(P.is_reflectable)
+ if(P.is_reflectable(REFLECTABILITY_ENERGY))
H.visible_message("[P] gets reflected by [H]'s glass skin!", \
"[P] gets reflected by [H]'s glass skin!")
diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm
index 99e847c6983..8c5011113a8 100644
--- a/code/modules/mob/living/silicon/robot/robot.dm
+++ b/code/modules/mob/living/silicon/robot/robot.dm
@@ -1330,7 +1330,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
playsound(get_turf(src), 'sound/mecha/nominalnano.ogg', 75, FALSE)
/mob/living/silicon/robot/deathsquad/bullet_act(obj/item/projectile/P)
- if(istype(P) && P.is_reflectable && P.starting)
+ if(istype(P) && P.is_reflectable(REFLECTABILITY_ENERGY) && P.starting)
visible_message("[P] gets reflected by [src]!", "[P] gets reflected by [src]!")
P.reflect_back(src)
return -1
diff --git a/code/modules/mob/living/simple_animal/constructs.dm b/code/modules/mob/living/simple_animal/constructs.dm
index 400be2933da..982659e3627 100644
--- a/code/modules/mob/living/simple_animal/constructs.dm
+++ b/code/modules/mob/living/simple_animal/constructs.dm
@@ -140,7 +140,7 @@
environment_smash = 1 //only token destruction, don't smash the cult wall NO STOP
/mob/living/simple_animal/hostile/construct/armoured/bullet_act(obj/item/projectile/P)
- if(P.is_reflectable)
+ if(P.is_reflectable(REFLECTABILITY_ENERGY))
var/reflectchance = 80 - round(P.damage/3)
if(prob(reflectchance))
if((P.damage_type == BRUTE || P.damage_type == BURN))
diff --git a/code/modules/projectiles/guns/energy.dm b/code/modules/projectiles/guns/energy.dm
index 2cc86678317..28a1f199950 100644
--- a/code/modules/projectiles/guns/energy.dm
+++ b/code/modules/projectiles/guns/energy.dm
@@ -58,6 +58,7 @@
/obj/item/gun/energy/proc/update_ammo_types()
var/obj/item/ammo_casing/energy/shot
+ select = clamp(select, 1, length(ammo_type)) // If we decrease ammo types while selecting a removed one, we want to make sure it doesnt try to select an out of bounds index
for(var/i = 1, i <= ammo_type.len, i++)
var/shottype = ammo_type[i]
shot = new shottype(src)
diff --git a/code/modules/projectiles/guns/energy/special.dm b/code/modules/projectiles/guns/energy/special.dm
index 42c5d6c908c..619e9904e97 100644
--- a/code/modules/projectiles/guns/energy/special.dm
+++ b/code/modules/projectiles/guns/energy/special.dm
@@ -772,6 +772,7 @@
var/linked_pinpointer_UID
shaded_charge = TRUE
can_holster = TRUE
+ can_fit_in_turrets = FALSE
can_charge = FALSE
unique_reskin = TRUE
charge_sections = 5
diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm
index 9faf6c6486e..2840b3c9412 100644
--- a/code/modules/projectiles/projectile.dm
+++ b/code/modules/projectiles/projectile.dm
@@ -39,7 +39,8 @@
var/flag = 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/projectile_type = "/obj/item/projectile"
var/range = 50 //This will de-increment every step. When 0, it will delete the projectile.
- var/is_reflectable = FALSE // Can it be reflected or not?
+ /// Determines the reflectability level of a projectile, either REFLECTABILITY_NEVER, REFLECTABILITY_PHYSICAL, REFLECTABILITY_ENERGY in order of ease to reflect.
+ var/reflectability = REFLECTABILITY_PHYSICAL
var/alwayslog = FALSE // ALWAYS log this projectile on hit even if it doesn't hit a living target. Useful for AOE explosion / EMP.
//Effects
var/stun = 0
@@ -196,7 +197,7 @@
if(!yes) //prevents double bumps.
return
- if(check_ricochet(A) && check_ricochet_flag(A) && ricochets < ricochets_max)
+ if(check_ricochet(A) && check_ricochet_flag(A) && ricochets < ricochets_max && is_reflectable(REFLECTABILITY_PHYSICAL))
ricochets++
if(A.handle_ricochet(src))
on_ricochet(A)
@@ -330,8 +331,6 @@
if(ismob(source))
firer = source // The reflecting mob will be the new firer
- else
- firer = null // Reflected by something other than a mob so firer will be null
// redirect the projectile
original = locate(new_x, new_y, z)
@@ -390,3 +389,10 @@
return
if(trajectory && !trajectory_ignore_forcemove && isturf(target))
trajectory.initialize_location(target.x, target.y, target.z, 0, 0)
+
+/obj/item/projectile/proc/is_reflectable(desired_reflectability_level)
+ if(reflectability == REFLECTABILITY_NEVER) //You'd trust coders not to try and override never reflectable things, but heaven help us I do not
+ return FALSE
+ if(reflectability < desired_reflectability_level)
+ return FALSE
+ return TRUE
diff --git a/code/modules/projectiles/projectile/beams.dm b/code/modules/projectiles/projectile/beams.dm
index e9c206cae0b..c4743e80fcb 100644
--- a/code/modules/projectiles/projectile/beams.dm
+++ b/code/modules/projectiles/projectile/beams.dm
@@ -9,7 +9,7 @@
flag = "laser"
eyeblur = 4 SECONDS
impact_effect_type = /obj/effect/temp_visual/impact_effect/red_laser
- is_reflectable = TRUE
+ reflectability = REFLECTABILITY_ENERGY
light_range = 2
light_color = LIGHT_COLOR_DARKRED
ricochets_max = 50 //Honk!
diff --git a/code/modules/projectiles/projectile/energy.dm b/code/modules/projectiles/projectile/energy.dm
index fd6935e368d..2600942c342 100644
--- a/code/modules/projectiles/projectile/energy.dm
+++ b/code/modules/projectiles/projectile/energy.dm
@@ -4,7 +4,7 @@
damage = 0
damage_type = BURN
flag = "energy"
- is_reflectable = TRUE
+ reflectability = REFLECTABILITY_ENERGY
/obj/item/projectile/energy/electrode
name = "electrode"
@@ -146,7 +146,7 @@
damage_type = BURN
armour_penetration = 10 // It can have a little armor pen, as a treat. Bigger than it looks, energy armor is often low.
shield_buster = TRUE
- is_reflectable = FALSE //I will let eswords block it like a normal projectile, but it's not getting reflected, and eshields will take the hit hard.
+ reflectability = REFLECTABILITY_PHYSICAL //I will let eswords block it like a normal projectile, but it's not getting reflected, and eshields will take the hit hard. Carp still can reflect though, screw you.
/obj/item/projectile/energy/detective
name = "energy revolver shot"
@@ -170,6 +170,7 @@
icon_state = "yellow_laser"
light_color = LIGHT_COLOR_YELLOW
stamina = 0
+ reflectability = REFLECTABILITY_PHYSICAL //No mr cult juggernaught, please don't set me to search!
/obj/item/projectile/energy/detective/tracker_warrant_shot/on_hit(atom/target)
. = ..()
@@ -182,26 +183,24 @@
/obj/item/projectile/energy/detective/tracker_warrant_shot/proc/start_tracking(atom/target)
var/obj/item/gun/energy/detective/D = firer_source_atom
if(!D)
- no_worky()
+ no_worky(target)
return
if(D.tracking_target_UID)
no_worky(tracking_already = TRUE)
return
D.start_pointing(target.UID())
- qdel(src)
/obj/item/projectile/energy/detective/tracker_warrant_shot/proc/set_warrant(atom/target)
var/mob/living/carbon/human/target_to_mark = target
var/perpname = target_to_mark.get_visible_name(TRUE)
if(!perpname || perpname == "Unknown")
- no_worky(warrant_fail = TRUE)
+ no_worky(target, warrant_fail = TRUE)
return
var/datum/data/record/R = find_record("name", perpname, GLOB.data_core.security)
if(!R || (R.fields["criminal"] in list(SEC_RECORD_STATUS_EXECUTE, SEC_RECORD_STATUS_ARREST)))
- to_chat(firer, "Weapon Alert: Target already set to higher warrant status or has no record!")
+ no_worky(target, warrant_fail = TRUE)
return
set_criminal_status(firer, R, SEC_RECORD_STATUS_SEARCH, "Target tagged by Detective Revolver", "Detective Revolver")
- qdel(src)
/obj/item/projectile/energy/detective/tracker_warrant_shot/proc/no_worky(atom/target, tracking_already, warrant_fail)
if(tracking_already)