diff --git a/code/__DEFINES/modular_guns.dm b/code/__DEFINES/modular_guns.dm
index 2ab4bff43cc..8a268ea1c14 100644
--- a/code/__DEFINES/modular_guns.dm
+++ b/code/__DEFINES/modular_guns.dm
@@ -5,6 +5,16 @@
#define MOD_SILENCE 1
#define MOD_NUCLEAR_CHARGE 2
+///The maximum improvement that can be applied to a weapon component.
+#define IMPROVEMENT_CAP 100
+///The maximum increase an individual variable can recieve over it's initial value.
+#define INCREASE_CAP 2
+///The maximum decrease an individual variable can recieve under it's initial value.
+#define DECREASE_CAP 0.2
+///All improvements are multiplied by this value, tweak down if they are too strong, up if they are too weak.
+#define IMPROVEMENT_MULTIPLIER 1
+
+
#define islasercapacitor(A) istype(A, /obj/item/laser_components/capacitor)
#define ismodifier(A) istype(A, /obj/item/laser_components/modifier)
#define ismodulator(A) istype(A, /obj/item/laser_components/modulator)
diff --git a/code/datums/components/skills/science/research_skill_component.dm b/code/datums/components/skills/science/research_skill_component.dm
index 409f2d9fa02..f27ad081f39 100644
--- a/code/datums/components/skills/science/research_skill_component.dm
+++ b/code/datums/components/skills/science/research_skill_component.dm
@@ -1 +1,4 @@
+/**
+ * Component used for the Research skill. Mobs with this component are better at working with modular lasers, no other functionality is currently implemented.
+ */
/datum/component/skill/research
diff --git a/code/datums/skills/occupational/science.dm b/code/datums/skills/occupational/science.dm
index b6606ea5716..29bd3e23fdc 100644
--- a/code/datums/skills/occupational/science.dm
+++ b/code/datums/skills/occupational/science.dm
@@ -1,10 +1,11 @@
/singleton/skill/research
name = "Research"
- description = "Not currently implemented"
+ description = "The Research skill governs your ability to conduct scientific research, iterate on designs in R&D and unlock the secrets of the universe. Currently only implemented for modular lasers. "
maximum_level = SKILL_LEVEL_PROFESSIONAL
uneducated_skill_cap = SKILL_LEVEL_TRAINED
category = /singleton/skill_category/occupational
subcategory = SKILL_SUBCATEGORY_SCIENCE
+ required = TRUE
component_type = RESEARCH_SKILL_COMPONENT
/singleton/skill/xenobotany
diff --git a/code/game/objects/items/weapons/storage/boxes.dm b/code/game/objects/items/weapons/storage/boxes.dm
index 35e6b343217..ef30178ab94 100644
--- a/code/game/objects/items/weapons/storage/boxes.dm
+++ b/code/game/objects/items/weapons/storage/boxes.dm
@@ -533,16 +533,13 @@
desc = "A box full of laser modulators, used to build laser weapons."
illustration = "firecracker"
starts_with = list(
+ /obj/item/laser_components/modulator = 1,
/obj/item/laser_components/modulator/taser = 1,
/obj/item/laser_components/modulator/tesla = 1,
/obj/item/laser_components/modulator/ion = 1,
/obj/item/laser_components/modulator/floramut = 1,
/obj/item/laser_components/modulator/floramut2 = 1,
- /obj/item/laser_components/modulator/arodentia = 1,
- /obj/item/laser_components/modulator/red = 1,
- /obj/item/laser_components/modulator/blue = 1,
- /obj/item/laser_components/modulator/omni = 1,
- /obj/item/laser_components/modulator/practice = 1,
+ /obj/item/laser_components/modulator/xenovermin = 1,
/obj/item/laser_components/modulator/mindflayer = 1,
/obj/item/laser_components/modulator/decloner = 1,
/obj/item/laser_components/modulator/ebow = 1,
diff --git a/code/game/objects/structures/crates_lockers/closets/secure/scientist.dm b/code/game/objects/structures/crates_lockers/closets/secure/scientist.dm
index 2a0d1775215..13882881d66 100644
--- a/code/game/objects/structures/crates_lockers/closets/secure/scientist.dm
+++ b/code/game/objects/structures/crates_lockers/closets/secure/scientist.dm
@@ -21,6 +21,8 @@
new /obj/item/laser_assembly(src)
new /obj/item/laser_assembly/medium(src)
new /obj/item/laser_assembly/large(src)
+ new /obj/item/firing_pin(src)
+ new /obj/item/firing_pin(src)
new /obj/item/storage/box/modlaser(src)
new /obj/item/storage/box/modlaser/modulators(src)
new /obj/item/storage/box/modlaser/lens(src)
diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm
index daa3d99b23c..eea1678057d 100644
--- a/code/modules/projectiles/gun.dm
+++ b/code/modules/projectiles/gun.dm
@@ -420,11 +420,6 @@ ABSTRACT_TYPE(/obj/item/gun)
if(!special_check(user))
return FALSE
- var/failure_chance = 100 - reliability
- if(prob(failure_chance))
- handle_reliability_fail(user)
- return FALSE
-
if(world.time < next_fire_time)
if(world.time % 3 && !can_autofire) //to prevent spam
to_chat(user, SPAN_WARNING("\The [src] is not ready to fire again!"))
@@ -434,6 +429,11 @@ ABSTRACT_TYPE(/obj/item/gun)
user.setClickCooldown(shoot_time)
next_fire_time = world.time + shoot_time
+ var/failure_chance = 100 - reliability //Here so there is click delay even if the gun malfunctions.
+ if(prob(failure_chance))
+ handle_reliability_fail(user)
+ return FALSE
+
user.face_atom(target, TRUE)
return TRUE
diff --git a/code/modules/projectiles/guns/energy/modular.dm b/code/modules/projectiles/guns/energy/modular.dm
index 22bdf9de1b6..fb2ed61252a 100644
--- a/code/modules/projectiles/guns/energy/modular.dm
+++ b/code/modules/projectiles/guns/energy/modular.dm
@@ -26,30 +26,176 @@
var/obj/item/laser_components/modulator/modulator
var/chargetime
var/is_charging
+ ///All malfunction effects are multiplied by this value. It can be increased by some powerful mods and decreased by safety mods.
var/criticality = 1 //multiplier for the negative effects of capacitor failures. Not just limited to critical failures.
+ ///This multiplies the malus of all components, above 1 the gun degrades faster, below 1 it degrades slower.
+ var/fragility = 1
+ ///True if the gun has a custom name.
var/named = 0
+ ///True if the gun has a custom description.
var/described = 0
+ ///When the weapon is disasembled, this is distributed randomly among its components. When a component with improvement potential is repaired, one appropriate variable gets better by up to improvement potential percent. The actual chance is based on skill level.
+ var/improvement_potential = 0 //This is a percentage increase of a single variable on one component, a 100% increase is not a doubling of all stats, it is instead spread out across all components.
+
+/obj/item/gun/energy/laser/prototype/mechanics_hints(mob/user, distance, is_adjacent)
+ . = list()
+ var/skill_level = (GET_SKILL_LEVEL(user, FIREARMS_SKILL_COMPONENT) + GET_SKILL_LEVEL(user, RESEARCH_SKILL_COMPONENT))
+ switch(skill_level ? skill_level : 6)
+ if(2)
+ . += "Your complete lack of skill in firearms and research will hide all information about this weapon from you, you will also be unable to repair it decreasing its reliability."
+ if(3)
+ . += "Your low familiarity with firearms and research will hide most information about this weapon from you, you will also struggle to repair it without decreasing its reliability."
+ if(4)
+ . += "Your combined familiarity with firearms and research will show you most information about this weapon, you will also be able to repair it without decreasing its reliability, but are not likely to be able to improve it much."
+ if(5)
+ . += "Your combined training in firearms and research will show you all information about this weapon, you will also be able to repair it without decreasing its reliability and have a chance to improve it when repairing."
+ . += "It can be improved up to its improvement potential, which is increased by firing it. Firing it at players increases it most rapidly, following by firing it at simple mobs, then firing it at objects."
+ if(6 to INFINITY)
+ . += "Your combined professional expertise in firearms and research will show you all information about this weapon, you will also be able to repair it without decreasing its reliability, and have an excellent chance to improve it when repairing."
+ . += "It can be improved up to its improvement potential, which is increased by firing it. Firing it at players increases it most rapidly, following by firing it at simple mobs, then firing it at objects."
/obj/item/gun/energy/laser/prototype/get_examine_text(mob/user, distance, is_adjacent, infix, suffix)
. = ..()
if(distance > 1)
return
- if(gun_mods.len)
- for(var/obj/item/laser_components/modifier/modifier in gun_mods)
- . += "You can see \a [modifier] attached."
- if(capacitor)
- . += "You can see \a [capacitor] attached."
- if(focusing_lens)
- . += "You can see \a [focusing_lens] attached."
- if(modulator)
- . += "You can see \a [modulator] attached."
+ var/skill_level = (GET_SKILL_LEVEL(user, FIREARMS_SKILL_COMPONENT) + GET_SKILL_LEVEL(user, RESEARCH_SKILL_COMPONENT))
+
+ if (capacitor.condition > 0 || focusing_lens.condition > 0 || modulator.condition > 0)
+ switch(skill_level ? skill_level : 6)
+ if (2 to 3) //At this level you can only tell that its damaged, nothing else.
+ . += SPAN_WARNING("It appears to be damaged.")
+ if (4 to 5) //At this level you can tell when the weapon is damaged and if it could malfunction.
+ if (reliability > 100)
+ . += SPAN_NOTICE("It appears to be damaged.")
+ else
+ . += SPAN_WARNING("It appears to be damaged and could malfunction!")
+ if (6 to INFINITY) //At this level you can estimate the weapon's reliability, but only if its damaged. This won't help if you build a weapon that's inherently unreliable.
+ if(improvement_potential > 2.5)
+ . += SPAN_GOOD("You see a few places where damage has revealed design flaws. Correcting them could improve one of the weapon's components by up to [round(improvement_potential, 5)]%.")
+ switch(reliability)
+ if (0 to 65)
+ . += SPAN_HIGHDANGER("It appears to be damaged and could go critical! You estimate it to be around [round(reliability, 5)]% reliable!")
+ if (66 to 80)
+ . += SPAN_DANGER("It appears to be damaged and could overload! You estimate it to be around [round(reliability, 5)]% reliable!")
+ if (81 to 100)
+ . += SPAN_WARNING("It appears to be damaged and could malfunction! You estimate it to be around [round(reliability, 5)]% reliable!")
+ if (101 to INFINITY)
+ . += SPAN_NOTICE("It appears to be damaged. You estimate it to be around [round(reliability, 5)]% reliable.")
+
+ switch(skill_level ? skill_level : 6)
+ if(2) //At this level you get no information about the weapon at all.
+ . += "This weapon is completely incomprehensible to you. It seems to be some sort of energy weapon, but you can't make out any details about how it functions."
+ if(3) //At this level you can only tell if the weapon has modifications or not, you can't make out what those modifications are.
+ if(gun_mods.len)
+ . += "This weapon is mostly incomprehensible to you. You can make out that it has some modifications, but the details of how they function are a mystery."
+ if(capacitor)
+ . += "You can see \a [capacitor] attached."
+ if(focusing_lens)
+ . += "You can see \a [focusing_lens] attached."
+ if(modulator)
+ . += "You can see \a [modulator] attached."
+ if(4) //At this level you can identify all components and if they're damaged or not.
+ if(gun_mods.len)
+ for(var/obj/item/laser_components/modifier/modifier in gun_mods)
+ . += "You can see \a [icon2html(modifier, user)][modifier] attached. [modifier.malus > modifier.base_malus ? SPAN_WARNING("It appears to be damaged.") : "It appears to be in good condition."]"
+ if(capacitor)
+ . += "You can see \a [icon2html(capacitor, user)][capacitor] attached. [capacitor.condition > 0 ? SPAN_WARNING("It appears to be damaged.") : "It appears to be in good condition."]"
+ if(focusing_lens)
+ . += "You can see \a [icon2html(focusing_lens, user)][focusing_lens] attached. [focusing_lens.condition > 0 ? SPAN_WARNING("It appears to be damaged.") : "It appears to be in good condition."]"
+ if(modulator)
+ . += "You can see \a [icon2html(modulator, user)][modulator] attached. [modulator.condition > 0 ? SPAN_WARNING("It appears to be damaged.") : "It appears to be in good condition."]"
+ if(5) //At this level you can identify all parts and can estimate the health of the three components, but not the modifiers, or the improvement potential.
+ if(gun_mods.len)
+ for(var/obj/item/laser_components/modifier/modifier in gun_mods)
+ . += "You can see \a [icon2html(modifier, user)][modifier] attached. [modifier.malus > modifier.base_malus ? SPAN_WARNING("It appears to be damaged.") : "It appears to be in good condition."]"
+ if(capacitor)
+ . += "You can see \a [icon2html(capacitor, user)][capacitor] attached. [capacitor.condition > 0 ? SPAN_WARNING("It appears to be [round(max(0, min(100, 100 - ((capacitor.condition / capacitor.reliability) * 100))))]% reliable.") : "It is good condition."]"
+ if(focusing_lens)
+ . += "You can see \a [icon2html(focusing_lens, user)][focusing_lens] attached. [focusing_lens.condition > 0 ? SPAN_WARNING("It appears to be [round(max(0, min(100, 100 - ((focusing_lens.condition / focusing_lens.reliability) * 100))))]% reliable.") : "It is good condition."]"
+ if(modulator)
+ . += "You can see \a [icon2html(modulator, user)][modulator] attached. [modulator.condition > 0 ? SPAN_WARNING("It appears to be [round(max(0, min(100, 100 - ((modulator.condition / modulator.reliability) * 100))))]% reliable.") : "It is good condition."]"
+ if(6 to INFINITY) //At this level you get the health of the gun, all components and the improvement potential.
+ if(gun_mods.len)
+ for(var/obj/item/laser_components/modifier/modifier in gun_mods)
+ . += "You can see \a [icon2html(modifier, user)][modifier] attached. [modifier.malus > modifier.base_malus ? SPAN_WARNING("It appears to be [round(max(0, min(100, 100 - (((modifier.malus - modifier.base_malus) / modifier.base_malus) * 100))))]% reliable.") : "It is good condition."]" //Base malus can be zero, modifiers with no base malus can't get damaged.
+ if(capacitor)
+ . += "You can see \a [icon2html(capacitor, user)][capacitor] attached. [capacitor.condition > 0 ? SPAN_WARNING("It appears to be [round(max(0, min(100, 100 - ((capacitor.condition / capacitor.reliability) * 100))))]% reliable.") : "It is good condition."]"
+ if(focusing_lens)
+ . += "You can see \a [icon2html(focusing_lens, user)][focusing_lens] attached. [focusing_lens.condition > 0 ? SPAN_WARNING("It appears to be [round(max(0, min(100, 100 - ((focusing_lens.condition / focusing_lens.reliability) * 100))))]% reliable.") : "It is good condition."]"
+ if(modulator)
+ . += "You can see \a [icon2html(modulator, user)][modulator] attached. [modulator.condition > 0 ? SPAN_WARNING("It appears to be [round(max(0, min(100, 100 - ((modulator.condition / modulator.reliability) * 100))))]% reliable.") : "It is good condition."]"
/obj/item/gun/energy/laser/prototype/attackby(obj/item/attacking_item, mob/user)
+ var/skill_level = (GET_SKILL_LEVEL(user, FIREARMS_SKILL_COMPONENT) + GET_SKILL_LEVEL(user, RESEARCH_SKILL_COMPONENT))
+
+ if(istype(attacking_item, /obj/item/stack/nanopaste)) //Nanopaste can be used for emergency repairs, but its not ideal and will reduce the reliability of the gun.
+ var/obj/item/stack/nanopaste/N = attacking_item
+
+ if (skill_level ? skill_level : 6)
+ to_chat(user, "You begin applying \the [N] to \the [src], repairing it in such a slapdash manner will damage it.")
+ if(do_after(user, 12/skill_level SECONDS, src, DO_REPAIR_CONSTRUCT))
+ repair_components(N, skill_level, user)
+
+ //TODO: High skilled technicans can add mods to guns after final assembly, requires a rework of the weapon analyzer.
+ //if(istype(attacking_item, /obj/item/laser_components/modifier))
+
if(attacking_item.tool_behaviour != TOOL_SCREWDRIVER)
return ..()
to_chat(user, "You disassemble \the [src].")
disassemble(user)
+/obj/item/gun/energy/laser/prototype/proc/repair_components(var/obj/item/stack/nanopaste/N, var/skill_level, var/mob/user)
+ if(capacitor && capacitor.condition > 0)
+ if(N.use(1))
+ capacitor.condition = max(0, capacitor.condition - 5 * skill_level)
+ capacitor.reliability = max(0, capacitor.reliability - 12/skill_level) //Using nanopaste to repair the capacitor reduces its reliability, this is to represent the fact that its a suboptimal repair job, and to prevent players from using nanopaste as a free repair method.
+ to_chat(user, "You repair \the [capacitor]. The leftover nanopaste is gumming up the delicate component, reducing its reliability.")
+ else if(focusing_lens && focusing_lens.condition > 0)
+ if(N.use(1))
+ focusing_lens.condition = max(0, focusing_lens.condition - 5 * skill_level)
+ focusing_lens.reliability = max(0, focusing_lens.reliability - 12/skill_level)
+ to_chat(user, "You repair \the [focusing_lens]. The leftover nanopaste is obscuring the lens, reducing its reliability.")
+ else if(modulator && modulator.condition > 0)
+ if(N.use(1))
+ modulator.condition = max(0, modulator.condition - 5 * skill_level)
+ modulator.reliability = max(0, modulator.reliability - 12/skill_level)
+ to_chat(user, "You repair \the [modulator]. The leftover nanopaste is blocking \the [modulator]'s emitters, reducing its reliability.")
+ else
+ to_chat(user, "There is nothing to repair on the gun!")
+
+/obj/item/gun/energy/laser/prototype/handle_post_fire(mob/user, atom/target) //This handles the improvement potential gain from firing the weapon. The actual improvement is handled in repair of the individual compoents, this just determines how much improvement potential is gained.
+ if(target)
+ improvement_potential += (0.1 * IMPROVEMENT_MULTIPLIER) / (max(1, burst)) //100 shots to improve by 1%, you can only improve if your gun takes damage, so it will need many repair cycles for this to be significant.
+
+ if (istype(target, /obj/structure/machinery/portable_atmospherics/hydroponics))
+ if (istype(modulator, /obj/item/laser_components/modulator/floramut) || istype(modulator, /obj/item/laser_components/modulator/floramut2))
+ improvement_potential += (1 * IMPROVEMENT_MULTIPLIER) / (max(1, burst))
+ return ..()
+
+ if(target != user) //No improvement from shooting yourself.
+ if(isliving(target)) //No improvement unless your target is a mob.
+ var/mob/living/target_mob = target
+ if(target_mob.stat != DEAD) //No improvement from shooting at dead things. Bring a doctor in to keep your target dummy alive. This also makes it harder to improve more powerful weapons, as you kill your target faster.
+ if(ishuman(target_mob))
+ var/mob/living/carbon/human/human_target = target
+ if (istype(modulator, /obj/item/laser_components/modulator/taser))
+ if (human_target.incapacitated()) //No benefit from tasing someone who's already incapacitated. Get the phramacist to make you oxycomorphine.
+ return ..()
+ if(human_target.get_species() == SPECIES_MONKEY)
+ improvement_potential += (2 * IMPROVEMENT_MULTIPLIER) / (max(1, burst)) //Monkeys die easily, research only gets two boxes of monkey cubes without xenobiology.
+ return ..()
+ if(human_target.client)
+ improvement_potential += (10 * IMPROVEMENT_MULTIPLIER) / (max(1, burst)) //10 seems like a lot, but this is a 20% improvement to 1 variable on 1 component. A 5 mod gun (8 total components), with an average of 3 improvable variables would need 120 shots on a player to max out.
+ return ..()
+
+ if(isslime(target_mob))
+ if (istype(modulator, /obj/item/laser_components/modulator/freeze))
+ improvement_potential += (2 * IMPROVEMENT_MULTIPLIER) / (max(1, burst))
+ return ..()
+
+ improvement_potential += (5 * IMPROVEMENT_MULTIPLIER) / (max(1, burst)) //Mechs, protohumans, and any other human mobs without a player.
+ ..()
+
/obj/item/gun/energy/laser/prototype/update_icon()
..()
underlays.Cut()
@@ -74,34 +220,34 @@
fire_delay_wielded = initial(fire_delay_wielded)
accuracy = initial(accuracy)
criticality = initial(criticality)
+ fragility = initial(fragility)
fire_sound = initial(fire_sound)
force = initial(force)
is_wieldable = initial(is_wieldable)
action_button_name = initial(action_button_name)
+
+/obj/item/gun/energy/laser/prototype/proc/delayed_overload(var/mob/user)
+ if(capacitor.reliability - capacitor.condition > 0)
+ to_chat(user, SPAN_DANGER("\The [src.capacitor] stops overloading, you fixed it just in time."))
+ else
+ if(prob(50 * criticality))
+ critical_fail(user)
+ else if(prob(75 * criticality))
+ medium_fail(user)
+ else
+ small_fail(user)
+ qdel(capacitor)
+ capacitor = null
+ disassemble(user)
+
/obj/item/gun/energy/laser/prototype/proc/updatetype(var/mob/user)
- reset_vars()
+ reset_vars() //Reset the gun to its initial values, then recalculate all stats based on the current state of components and mods.
+
if(!focusing_lens || !capacitor || !modulator)
- disassemble(user)
return
update_chassis()
- //TODO: When the skill system test is merged, rework this to give high skills a chance to avoid the failure.
- // if(capacitor.reliability - capacitor.condition <= 0)
- // if(prob(66))
- // capacitor.small_fail(user)
- // else
- // capacitor.medium_fail(user)
- // qdel(capacitor)
- // capacitor = null
-
- // if(focusing_lens.reliability - focusing_lens.condition <= 0)
- // qdel(focusing_lens)
- // focusing_lens = null
-
- if(!focusing_lens || !capacitor || !modulator)
- disassemble(user)
- return
reliability = (capacitor.reliability - capacitor.condition) + (focusing_lens.reliability - focusing_lens.condition)
@@ -109,21 +255,43 @@
projectile_type = modulator.projectile
fire_delay = capacitor.fire_delay
- max_shots = capacitor.shots
-
+ max_shots = round(capacitor.shots)
dispersion = focusing_lens.dispersion
accuracy = focusing_lens.accuracy
- burst += focusing_lens.burst
+ burst += round(focusing_lens.burst)
fire_sound = modulator.firing_sound
+ fragility = capacitor.malus_multiplier
if(gun_mods.len)
handle_mod()
+ if(capacitor.reliability - capacitor.condition <= 0) //The gun explodes if its capacitor reaches 0 reliability.
+ var/overload_delay = 3/criticality
+ var/skill_level = (GET_SKILL_LEVEL(user, FIREARMS_SKILL_COMPONENT) + GET_SKILL_LEVEL(user, RESEARCH_SKILL_COMPONENT))
+ switch(skill_level ? skill_level : 6)
+ if(2 to 3)
+ to_chat(user, SPAN_HIGHDANGER("\The [src] hisses ominously!"))
+ if(4 to 5)
+ overload_delay *= 1.5
+ to_chat(user, SPAN_HIGHDANGER("\The [src] hisses ominously as \the [capacitor]'s housing begins to fail, you release the trigger but it's too late! Get rid of it!"))
+ if(6 to INFINITY)
+ overload_delay *= 2
+ to_chat(user, SPAN_HIGHDANGER("You instantly release the trigger as \the [capacitor]'s housing begins to fail! You have less than [round(overload_delay)] seconds to repair \the [src] or throw it away!"))
+
+ addtimer(CALLBACK(src, PROC_REF(delayed_overload)), overload_delay SECONDS, TIMER_STOPPABLE|TIMER_DELETE_ME)
+ animate(src, overload_delay SECONDS + rand(-5, 5), -1, LINEAR_EASING, color = COLOR_RED)
+ return
+
+ if(focusing_lens.reliability - focusing_lens.condition <= 0)
+ to_chat(user, SPAN_DANGER("\The [src]'s focusing lens shatters with a loud crack!"))
+ qdel(focusing_lens)
+ focusing_lens = null
+
power_supply.maxcharge = max_shots*charge_cost
charge_cost /= max(1, (burst - 1))
fire_delay_wielded = fire_delay * 0.75
- accuracy_wielded = accuracy + accuracy/4
- scoped_accuracy = accuracy_wielded + accuracy/4
+ accuracy_wielded = accuracy + abs(accuracy)/2
+ scoped_accuracy = accuracy_wielded + abs(accuracy)
w_class = gun_type
reliability = max(reliability, 1)
@@ -143,6 +311,7 @@
slot_flags = SLOT_BACK
item_state = "large_3"
is_wieldable = TRUE
+ one_hand_fa_penalty = 12
/obj/item/gun/energy/laser/prototype/proc/handle_mod()
for(var/obj/item/laser_components/modifier/modifier in gun_mods)
@@ -151,16 +320,16 @@
suppressed = TRUE
if(MOD_NUCLEAR_CHARGE)
self_recharge = TRUE
- criticality *= 2
fire_delay *= modifier.fire_delay
reliability += modifier.reliability
burst += modifier.burst
burst_delay += modifier.burst_delay
- max_shots *= modifier.shots
+ max_shots *= modifier.shots //We want to add all the shot multipliers together, then apply them all at once.
force = min(force + modifier.gun_force, 40)
- chargetime += modifier.chargetime*10
+ chargetime += modifier.chargetime SECONDS
accuracy += modifier.accuracy
criticality *= modifier.criticality
+ fragility *= modifier.malus_multiplier
if(modifier.scope_name)
zoomdevicename = modifier.scope_name
@@ -171,7 +340,7 @@
return null
if(!power_supply.checked_use(charge_cost))
return null
- if(!capacitor)
+ if(!capacitor || capacitor.condition >= capacitor.reliability)
return null
if (self_recharge)
addtimer(CALLBACK(src, PROC_REF(try_recharge)), recharge_time * 2 SECONDS, TIMER_UNIQUE)
@@ -180,19 +349,21 @@
var/damage_coeff = 1
for(var/obj/item/laser_components/modifier/modifier in gun_mods)
damage_coeff *= modifier.damage
+
+ if(!bypass_degrade)
+ for(var/obj/item/laser_components/modifier/modifier in gun_mods) //This repeats for EVERY MOD, fail chance goes up quadratically with the number of mods
+ if(prob(max(1,(gun_mods.len * 2 * damage_coeff)/(max(1,(burst))))))
+ capacitor.degrade(modifier.malus * fragility)
+ if(prob(max(1,(gun_mods.len * damage_coeff)/(max(1,(burst))))))
+ focusing_lens.degrade(modifier.malus * fragility)
+ if(prob(max(1,(5 + capacitor.condition)/(max(1,(burst)))))) //Firing a gun with a damaged capacitor risks arcing to other components, damaging them
+ modifier.degrade(0.2 * fragility)
+
if(burst > 1)
A.damage = A.damage/(max(1, burst - 1)) //Damage is divided by the number of shots
damage_coeff *= modulator.damage
A.damage *= damage_coeff
A.damage = min(A.damage, 60) //Caps the maximum damage one shot can do, this matches the laser cannon
- if(!bypass_degrade)
- for(var/obj/item/laser_components/modifier/modifier in gun_mods) //This repeats for EVERY MOD, fail chance goes up quadratically with the number of mods
- if(prob((gun_mods.len * damage_coeff)/(max(1,(burst)))))
- capacitor.degrade(modifier.malus)
- if(prob((gun_mods.len * damage_coeff)/(max(1,(burst)))))
- focusing_lens.degrade(modifier.malus)
- if(prob((5 + capacitor.damage)/(max(1,(burst))))) //Firing a gun with a damaged capacitor risks arcing to other components, damaging them
- modifier.degrade(0.2)
updatetype(ismob(loc) ? loc : null)
return A
@@ -202,10 +373,40 @@
if(!A)
return
- if(gun_mods.len)
- for(var/obj/item/laser_components/modifier/modifier in gun_mods)
- modifier.forceMove(A)
- gun_mods.Remove(modifier)
+ var/list/damaged_components = list()
+
+ if(capacitor && capacitor.condition > 0)
+ damaged_components += capacitor
+ if(focusing_lens && focusing_lens.condition > 0)
+ damaged_components += focusing_lens
+ if(modulator && modulator.condition > 0)
+ damaged_components += modulator
+ for(var/obj/item/laser_components/modifier/modifier in gun_mods)
+ if(modifier.malus > modifier.base_malus)
+ damaged_components += modifier
+
+ while(improvement_potential > 0 && damaged_components.len)
+ var/list/upgradable_components = list()
+ for(var/obj/item/laser_components/component in damaged_components)
+ if(component.total_improved < IMPROVEMENT_CAP)
+ upgradable_components += component
+
+ if(!upgradable_components.len)
+ to_chat(user, SPAN_WARNING("There's nothing to improve on the components of this gun."))
+ break
+
+ var/obj/item/laser_components/selected_component = pick(upgradable_components)
+ var/potential_to_components = min(rand(0, improvement_potential), 30) //Sends up a random amount up to 30 improvement points to the component.
+
+ if(potential_to_components)
+ selected_component.improvement_potential += potential_to_components
+ improvement_potential -= potential_to_components
+
+ // Drop all components to the ground
+ for(var/obj/item/laser_components/modifier/modifier in gun_mods)
+ modifier.forceMove(A)
+ gun_mods.Remove(modifier)
+
if(capacitor)
capacitor.forceMove(A)
capacitor = null
@@ -230,20 +431,16 @@
/obj/item/gun/energy/laser/prototype/small_fail(var/mob/user)
if(capacitor)
- to_chat(user, SPAN_DANGER("\The [src]'s [capacitor] short-circuits!"))
- visible_message(SPAN_DANGER("Sparks fly from \the [src] as it short-circuits!"), range = 6)
capacitor.small_fail(user, src)
return
/obj/item/gun/energy/laser/prototype/medium_fail(var/mob/user)
if(capacitor)
- to_chat(user, SPAN_DANGER("\The [src]'s [capacitor] overloads!"))
capacitor.medium_fail(user, src)
return
/obj/item/gun/energy/laser/prototype/critical_fail(var/mob/user)
if(capacitor)
- to_chat(user, SPAN_DANGER("\The [src]'s [capacitor] goes critical!"))
capacitor.critical_fail(user, src)
return
@@ -274,9 +471,6 @@
if(is_charging && chargetime)
to_chat(user, SPAN_DANGER("\The [src] is already charging!"))
return 0
- if(!wielded && (origin_chassis == CHASSIS_LARGE))
- to_chat(user, SPAN_DANGER("You require both hands to fire this weapon!"))
- return 0
if(chargetime)
user.visible_message(
SPAN_DANGER("\The [user] begins charging the [src]!"),
@@ -340,10 +534,16 @@
. += "
Component Name: [initial(l_component.name)]
"
var/l_repair_name = initial(l_component.repair_item.name) ? initial(l_component.repair_item.name) : "nothing"
- . += "Reliability: [initial(l_component.reliability)]
"
- . += "Damage Modifier: [initial(l_component.damage)]
"
- . += "Fire Delay Modifier: [initial(l_component.fire_delay)]
"
- . += "Shots Modifier: [initial(l_component.shots)]
"
- . += "Burst Modifier: [initial(l_component.burst)]
"
- . += "Accuracy Modifier: [initial(l_component.accuracy)]
"
+ if(l_component.reliability != 0)
+ . += "Reliability: [round(l_component.reliability, 1)]
"
+ if(l_component.damage != 1)
+ . += "Damage Modifier: [round(l_component.damage, 0.1)]
"
+ if(l_component.fire_delay != 1)
+ . += "Fire Delay Modifier: [round(l_component.fire_delay, 0.1)]
"
+ if(l_component.shots != 1)
+ . += "Shots Modifier: [round(l_component.shots, 0.1)]
"
+ if(l_component.burst != 0)
+ . += "Burst Modifier: [round(l_component.burst, 1)]
"
+ if(l_component.accuracy != 0)
+ . += "Accuracy Modifier: [round(l_component.accuracy, 0.1)]
"
. += "Repair Tool: [l_repair_name]
"
diff --git a/code/modules/projectiles/modular/laser_base.dm b/code/modules/projectiles/modular/laser_base.dm
index 6305eb41629..971e88cc41e 100644
--- a/code/modules/projectiles/modular/laser_base.dm
+++ b/code/modules/projectiles/modular/laser_base.dm
@@ -3,16 +3,36 @@
icon_state = "bfg"
contained_sprite = TRUE
w_class = WEIGHT_CLASS_SMALL //A dissasembled gun is easier to carry, this lets people bring bits of their broken gun back to R&D.
+ ///The Max HP of the component. This is added to the overall reliability of the weapon.
var/reliability = 0
+ //This multiplies the damage of a shot, the base damage is determined by the capacitor.
var/damage = 1
+ ///This multiplies the fire delay of the weapon, the base fire delay is determined by the capacitor.
var/fire_delay = 1
- var/condition = 0 //inverse health of the component. subtracted from reliability.
- var/base_malus = 0 //when modifiers get damaged they do not break, but make other components break faster
- var/malus = 0 //subtracted from weapon's overall reliability everytime it's fired
+ ///The amount of damage a component has taken. Subtracted from reliability.
+ var/condition = 0
+ ///The base amount of damage this modifier does to other components when the gun is fired.
+ var/base_malus = 0.1 //when modifiers get damaged they do not break, but make other components break faster
+ ///The amount of damage this modifier does to other components. This increases as the modifier itself gets damaged.
+ var/malus = 0
+ ///The malus of all components is multiplied by this value.
+ var/malus_multiplier = 1
+ ///Multiplies the total number of shots the gun can fire before recharge.
var/shots = 1
+ ///This is added to the number of shots the gun fires in one click.
var/burst = 0
+ ///The amount it's possible to improve a component by repairing it. This increases when the weapon is used, and decreases when it's repaired. When it hits 0, the component can no longer be improved by repairing it, but can still lose reliability.
+ var/improvement_potential = 0
+ ///The total amount the component has been improved by repairs. This is used to put an upper limit on how much a component can be improved.
+ var/total_improved = 0
+ ///Added to the accuracy of the weapon.
var/accuracy = 0
+ ///The item required to repair the component.
var/obj/item/repair_item
+ ///Lists of the variables on this component that can be improved by repairing it.
+ var/list/increasable_stats = list("reliability")
+ ///List of variables that are better when lower, such as fire delay or malus.
+ var/list/decreaseable_stats = list()
var/gun_overlay
/obj/item/laser_components/proc/degrade(var/increment = 1)
@@ -21,6 +41,109 @@
if(condition > reliability)
condition = reliability
+/obj/item/laser_components/proc/handle_improvement(var/skill_level, var/mob/user)
+ while (improvement_potential > 0)
+
+ var/improvement = min(abs(improvement_potential / 100), 0.2) //Caps improvement from a single repair to 20%. This spreads the effect out across multiple stats
+ var/stat_direction = 1
+ var/stat_name = null
+
+ //TODO: Make it possible to target a specific value by repairing with a randomly selected part that research doesn't typically have access to.
+
+ if (increasable_stats.len && decreaseable_stats.len) //Picks a random available stat to upgrade.
+ if (prob(50))
+ stat_name = pick(increasable_stats)
+ else
+ stat_name = pick(decreaseable_stats)
+ stat_direction = -1
+ else if (increasable_stats.len)
+ stat_name = pick(increasable_stats)
+ stat_direction = 1
+ else if (decreaseable_stats.len)
+ stat_name = pick(decreaseable_stats)
+ stat_direction = -1
+ else
+ break //No stats to improve, it shouldn't be possible to have improvement potential and no stats to improve, but just in case.
+
+ if (stat_name in src.vars)
+ improvement_potential -= improvement * 100 //Decreases improvement potential before any skill modifiers.
+
+ switch(skill_level ? skill_level : 6)
+ if(-INFINITY to 2)
+ improvement *= (rand(-5, -1) / 10) //Always damage it.
+ if(3)
+ improvement *= (rand(-5, 3) / 10)
+ if(4)
+ improvement *= (rand(3, 5) / 10)
+ if(5)
+ improvement *= (rand(7, 10) / 10)
+ if(6 to INFINITY)
+ improvement *= (rand(8, 12) / 10)
+
+ if (src.vars[stat_name] > (initial(src.vars[stat_name]) * INCREASE_CAP) && stat_direction > 0) //It is possible to waste improvement potential by hitting the cap on a stat, this is fine, it should be harder to improve a component that's already of high quality.
+ continue
+ if (src.vars[stat_name] < (initial(src.vars[stat_name]) * DECREASE_CAP) && stat_direction < 0)
+ continue
+
+ src.vars[stat_name] += stat_direction * abs(initial(src.vars[stat_name]) * improvement) //Adds improvement % of the initial value to the stat.
+
+ if (improvement > 0)
+ to_chat(user, SPAN_NOTICE("Your careful repairs to \the [src] [stat_direction > 0 ? "increase" : "decrease"] its [replacetext(stat_name, "_", " ")] by [improvement * 100] percent!"))
+ total_improved += improvement
+ else if (improvement == 0)
+ to_chat(user, SPAN_NOTICE("Your repairs to \the [src], don't seem to improve it, but at least you didn't make it worse."))
+ else
+ to_chat(user, SPAN_WARNING("Your repairs to \the [src] end up damaging it!"))
+
+/obj/item/laser_components/get_examine_text(mob/user, distance, is_adjacent, infix, suffix)
+ . = ..()
+ if(distance > 1)
+ return
+
+ var/skill_level = (GET_SKILL_LEVEL(user, FIREARMS_SKILL_COMPONENT) + GET_SKILL_LEVEL(user, RESEARCH_SKILL_COMPONENT))
+ switch(skill_level ? skill_level : 6)
+ if (-INFINITY to 2)
+ if(improvement_potential > 0)
+ . += SPAN_WARNING("You don't think you could repair \the [src] without making it worse.")
+ if (3)
+ if(improvement_potential > 0)
+ . += SPAN_WARNING("You might be able to repair \the [src], but you think you're much more likely to make it worse.")
+ if (4)
+ for (var/stat in increasable_stats)
+ if (src.vars[stat] != initial(src.vars[stat]))
+ . += SPAN_NOTICE("You can see \the [src]'s [replacetext(stat, "_", " ")] has had some custom work done to it.")
+ for (var/stat in decreaseable_stats)
+ if (src.vars[stat] != initial(src.vars[stat]))
+ . += SPAN_NOTICE("You can see \the [src]'s [replacetext(stat, "_", " ")] has had some custom work done to it.")
+ if(improvement_potential > 0)
+ . += SPAN_GOOD("You think you could repair \the [src] and improve it, but you might also make it worse if you aren't careful.")
+ if (5)
+ for (var/stat in increasable_stats)
+ if (src.vars[stat] > initial(src.vars[stat]))
+ . += SPAN_NOTICE("You can see \the [src]'s [replacetext(stat, "_", " ")] has been improved and increased.")
+ else if (src.vars[stat] < initial(src.vars[stat]))
+ . += SPAN_WARNING("You can see \the [src]'s [replacetext(stat, "_", " ")] has been degraded and decreased.")
+ for (var/stat in decreaseable_stats)
+ if (src.vars[stat] < initial(src.vars[stat]))
+ . += SPAN_NOTICE("You can see \the [src]'s [replacetext(stat, "_", " ")] has been improved and decreased.")
+ else if (src.vars[stat] > initial(src.vars[stat]))
+ . += SPAN_WARNING("You can see \the [src]'s [replacetext(stat, "_", " ")] has been degraded and increased.")
+ if(improvement_potential > 0)
+ . += SPAN_GOOD("You see a few places where damage has revealed design flaws. You could correct them to improve \the [src].")
+ if (6 to INFINITY)
+ for (var/stat in increasable_stats)
+ if (src.vars[stat] > initial(src.vars[stat]))
+ . += SPAN_NOTICE("You can see \the [src]'s [replacetext(stat, "_", " ")] has been improved, increasing it by approximately [round((src.vars[stat] - initial(src.vars[stat])) / initial(src.vars[stat]) * 100)] percent.")
+ else if (src.vars[stat] < initial(src.vars[stat]))
+ . += SPAN_WARNING("You can see \the [src]'s [replacetext(stat, "_", " ")] has been degraded, decreasing it by approximately [round((initial(src.vars[stat]) - src.vars[stat]) / initial(src.vars[stat]) * 100)] percent.")
+ for (var/stat in decreaseable_stats)
+ if (src.vars[stat] < initial(src.vars[stat]))
+ . += SPAN_NOTICE("You can see \the [src]'s [replacetext(stat, "_", " ")] has been improved, decreasing it by approximately [round((src.vars[stat] - initial(src.vars[stat])) / initial(src.vars[stat]) * 100)] percent.")
+ else if (src.vars[stat] > initial(src.vars[stat]))
+ . += SPAN_WARNING("You can see \the [src]'s [replacetext(stat, "_", " ")] has been degraded, increasing it by approximately [round((initial(src.vars[stat]) - src.vars[stat]) / initial(src.vars[stat]) * 100)] percent.")
+ if(improvement_potential > 0)
+ . += SPAN_GOOD("You see a few places where damage has revealed design flaws. Correcting them could improve \the [src] by up to [improvement_potential] percent.")
+
/obj/item/laser_components/attackby(obj/item/attacking_item, mob/user)
if(!istype(attacking_item, repair_item))
return ..()
@@ -28,13 +151,14 @@
to_chat(user, SPAN_WARNING("\The [src] is not damaged."))
return ..()
to_chat(user, SPAN_WARNING("You begin repairing \the [src]."))
- if(do_after(user, rand(2 SECONDS, 6 SECONDS), src, DO_UNIQUE) && repair_module(attacking_item))
+ var/skill_level = (GET_SKILL_LEVEL(user, FIREARMS_SKILL_COMPONENT) + GET_SKILL_LEVEL(user, RESEARCH_SKILL_COMPONENT))
+ if(do_after(user, rand(2 SECONDS, 6 SECONDS), src, DO_UNIQUE) && repair_module(attacking_item, skill_level, user))
to_chat(user, SPAN_NOTICE("You repair \the [src]."))
else
to_chat(user, SPAN_WARNING("You fail to repair \the [src]."))
-/obj/item/laser_components/proc/repair_module(var/obj/item/D)
- return 1
+/obj/item/laser_components/proc/repair_module(var/obj/item/D, var/skill_level, var/mob/user)
+ return 1
/obj/item/laser_components/modifier
name = "modifier"
@@ -45,7 +169,7 @@
var/chargetime = 0
var/burst_delay = 0
var/scope_name
- var/criticality
+ var/criticality = 1
repair_item = /obj/item/weldingtool
/obj/item/laser_components/modifier/condition_hints(mob/user, distance, is_adjacent)
@@ -60,12 +184,13 @@
if(malus > abs(base_malus*2))
malus = abs(base_malus*2)
-/obj/item/laser_components/modifier/repair_module(var/obj/item/weldingtool/W)
+/obj/item/laser_components/modifier/repair_module(var/obj/item/weldingtool/W, var/skill_level, var/mob/user)
if(!istype(W))
return
if(malus == base_malus)
return 0
if(W.use(2))
+ handle_improvement(skill_level, user)
malus = max(malus - 5, base_malus)
return 1
return 0
@@ -79,56 +204,93 @@
reliability = 50
fire_delay = 5
repair_item = /obj/item/stack/cable_coil
+ increasable_stats = list("reliability", "damage", "shots")
+ decreaseable_stats = list()
/obj/item/laser_components/capacitor/condition_hints(mob/user, distance, is_adjacent)
. += ..()
if(distance <= 1 && condition > 0)
. += SPAN_WARNING("\The [src] appears damaged.")
-/obj/item/laser_components/capacitor/repair_module(var/obj/item/stack/cable_coil/C)
+/obj/item/laser_components/capacitor/repair_module(var/obj/item/stack/cable_coil/C, var/skill_level, var/mob/user)
if(!istype(C))
return
if(!condition > 0)
return 0
if(C.use(2))
- condition = max(condition - 5, 0)
+ handle_improvement(skill_level, user)
+ condition = max(condition - 3 * skill_level, 0)
return 1
return 0
/obj/item/laser_components/capacitor/proc/small_fail(var/mob/living/user, var/obj/item/gun/energy/laser/prototype/prototype)
- tesla_zap(prototype, 0, 1000*max(prototype.criticality, 1))
+ if(ishuman(user))
+ var/mob/living/carbon/human/H = user
+ var/active_hand = H.hand
+ var/shock_damage = round(damage * 0.25 *prototype.criticality) + rand(-5, 5)
+ if(active_hand)
+ H.electrocute_act(shock_damage, prototype, def_zone = BP_L_HAND, tesla_shock = 0)
+ H.electrocute_act(shock_damage * 0.4, prototype, def_zone = BP_L_ARM, tesla_shock = 0) //Can arc past insulated gloves and into the arm.
+ else
+ H.electrocute_act(shock_damage, prototype, def_zone = BP_R_HAND, tesla_shock = 0)
+ H.electrocute_act(shock_damage * 0.4, prototype, def_zone = BP_R_ARM, tesla_shock = 0)
+ else
+ tesla_zap(prototype, 0, 1000*prototype.criticality)
return
/obj/item/laser_components/capacitor/proc/medium_fail(var/mob/living/user, var/obj/item/gun/energy/laser/prototype/prototype)
- tesla_zap(prototype, 0, 1500*max(prototype.criticality, 1))
- visible_message(SPAN_DANGER("\The [src] in \the [prototype] sparks a little angrily as it overloads!"), range = 3)
+ if(ishuman(user))
+ var/mob/living/carbon/human/H = user
+ var/active_hand = H.hand
+ var/shock_damage = round(damage * 0.5 *prototype.criticality) + rand(-5, 5)
+ if(active_hand)
+ H.electrocute_act(shock_damage, prototype, def_zone = BP_L_HAND, tesla_shock = 0)
+ H.electrocute_act(shock_damage * 0.4, prototype, def_zone = BP_L_ARM, tesla_shock = 0) //Can arc past insulated gloves and into the arm.
+ else
+ H.electrocute_act(shock_damage, prototype, def_zone = BP_R_HAND, tesla_shock = 0)
+ H.electrocute_act(shock_damage * 0.4, prototype, def_zone = BP_R_ARM, tesla_shock = 0)
+ else
+ tesla_zap(prototype, 0, 2000*prototype.criticality)
return
/obj/item/laser_components/capacitor/proc/critical_fail(var/mob/living/user, var/obj/item/gun/energy/laser/prototype/prototype)
- tesla_zap(prototype, 1, 2000*max(prototype.criticality, 1))
- visible_message(SPAN_DANGER("\The [src] in \the [prototype] goes critical in a shower of sparks!"), range = 5)
+ if(ishuman(user))
+ var/mob/living/carbon/human/H = user
+ var/active_hand = H.hand
+ var/shock_damage = round(damage * prototype.criticality) + rand(-5, 5)
+ if(active_hand)
+ H.electrocute_act(shock_damage, prototype, def_zone = BP_L_HAND, tesla_shock = 0)
+ H.electrocute_act(shock_damage * 0.4, prototype, def_zone = BP_L_ARM, tesla_shock = 0) //Can arc past insulated gloves and into the arm.
+ else
+ H.electrocute_act(shock_damage, prototype, def_zone = BP_R_HAND, tesla_shock = 0)
+ H.electrocute_act(shock_damage * 0.4, prototype, def_zone = BP_R_ARM, tesla_shock = 0)
+ else
+ tesla_zap(prototype, 0, 4000*prototype.criticality)
return
/obj/item/laser_components/focusing_lens
name = "focusing lens"
desc = "A basic laser weapon focusing lens."
icon_state = "lens"
- var/list/dispersion = list(0, 5, 10, 15, 20, 25, 30, 35, 40, 45)
- reliability = 25
+ var/list/dispersion = list(2, 4, 6, 8, 10)
+ accuracy = 1
repair_item = /obj/item/stack/material/glass
+ increasable_stats = list("reliability", "accuracy")
+ decreaseable_stats = list()
/obj/item/laser_components/focusing_lens/condition_hints(mob/user, distance, is_adjacent)
. += ..()
if(distance <= 1 && condition > 0)
. += SPAN_WARNING("\The [src] appears damaged.")
-/obj/item/laser_components/focusing_lens/repair_module(var/obj/item/stack/material/G)
+/obj/item/laser_components/focusing_lens/repair_module(var/obj/item/stack/material/G, var/skill_level, var/mob/user)
if(!istype(G))
return
if(!condition > 0)
return 0
if(G.use(1))
- condition = max(condition - 5, 0)
+ handle_improvement(skill_level, user)
+ condition = max(condition - 3 * skill_level, 0)
return 1
return 0
@@ -267,10 +429,16 @@
. += "
Component Name: [initial(l_component.name)]
"
var/l_repair_name = initial(l_component.repair_item.name) ? initial(l_component.repair_item.name) : "nothing"
- . += "Reliability: [initial(l_component.reliability)]
"
- . += "Damage Modifier: [initial(l_component.damage)]
"
- . += "Fire Delay Modifier: [initial(l_component.fire_delay)]
"
- . += "Shots Modifier: [initial(l_component.fire_delay)]
"
- . += "Burst Modifier: [initial(l_component.burst)]
"
- . += "Accuracy Modifier: [initial(l_component.accuracy)]
"
+ if(l_component.reliability != 0)
+ . += "Reliability: [l_component.reliability]
"
+ if(l_component.damage != 1)
+ . += "Damage Modifier: [l_component.damage]
"
+ if(l_component.fire_delay != 1)
+ . += "Fire Delay Modifier: [l_component.fire_delay]
"
+ if(l_component.shots != 1)
+ . += "Shots Modifier: [l_component.shots]
"
+ if(l_component.burst != 0)
+ . += "Burst Modifier: [l_component.burst]
"
+ if(l_component.accuracy != 0)
+ . += "Accuracy Modifier: [l_component.accuracy]
"
. += "Repair Tool: [l_repair_name]
"
diff --git a/code/modules/projectiles/modular/laser_components.dm b/code/modules/projectiles/modular/laser_components.dm
index 0a01f5f1d82..f0483690043 100644
--- a/code/modules/projectiles/modular/laser_components.dm
+++ b/code/modules/projectiles/modular/laser_components.dm
@@ -35,6 +35,23 @@
desc = "A reinforced laser weapon capacitor."
icon_state = "reinforced_capacitor"
reliability = 100
+ malus_multiplier = 0.9
+
+/obj/item/laser_components/capacitor/highcap
+ name = "high-capacity capacitor"
+ desc = "A capacitor with increased charge capacity, at the cost of peak output"
+ icon_state = "reinforced_capacitor"
+ reliability = 45
+ shots = 15
+ damage = 8
+
+/obj/item/laser_components/capacitor/highpower
+ name = "overclocked capacitor"
+ desc = "A capacitor withhigher output, at the cost of total charge."
+ icon_state = "reinforced_capacitor"
+ damage = 20
+ shots = 4
+ reliability = 45
/obj/item/laser_components/capacitor/nuclear
name = "uranium-enriched capacitor"
@@ -46,34 +63,31 @@
/obj/item/laser_components/capacitor/reinforced/small_fail(var/mob/living/user, var/obj/item/gun/energy/laser/prototype/prototype)
- tesla_zap(prototype, 0, 1000*max((prototype.criticality / 2), 1)) //This capacitor is the safest you can make.
- return
+ ..()
/obj/item/laser_components/capacitor/reinforced/medium_fail(var/mob/living/user, var/obj/item/gun/energy/laser/prototype/prototype)
- tesla_zap(prototype, 0, 1200*max((prototype.criticality / 2), 1))
visible_message(SPAN_DANGER("\The [src] powering \the [prototype] mostly contains the sparks as it overloads!"), range = 3)
- return
+ ..()
/obj/item/laser_components/capacitor/reinforced/critical_fail(var/mob/living/user, var/obj/item/gun/energy/laser/prototype/prototype)
- tesla_zap(prototype, 1, 1600*max((prototype.criticality / 2), 1))
visible_message(SPAN_DANGER("\The [src] powering \the [prototype] goes critical but contains the worst of the sparks!"), range = 4)
- return
+ ..()
/obj/item/laser_components/capacitor/nuclear/small_fail(var/mob/living/user, var/obj/item/gun/energy/laser/prototype/prototype)
- tesla_zap(prototype, 1, 1000*max(prototype.criticality, 1))
+ visible_message(SPAN_WARNING("\The [src] powering \the [prototype] sparks and briefly glows a sickly yellow."), range = 4)
var/turf/T = get_turf(src)
- for (var/mob/living/M in range(0, T)) //Only a minor failure, enjoy your radiation if you're in the same tile or carrying it
+ for (var/mob/living/M in range(0, T))
to_chat(M, SPAN_WARNING("You feel a warm sensation."))
- M.apply_damage(rand(1,10)*max(prototype.criticality, 1), DAMAGE_RADIATION)
- return
+ M.apply_damage(rand(50,100)*max(prototype.criticality, 1), DAMAGE_RADIATION)
+ ..()
/obj/item/laser_components/capacitor/nuclear/medium_fail(var/mob/living/user, var/obj/item/gun/energy/laser/prototype/prototype)
visible_message(SPAN_DANGER("\The [src] in \the [prototype] glows ominously as it overloads!"), range = 6)
var/turf/T = get_turf(src)
- for (var/mob/living/M in range(round(max(prototype.criticality, 1)),T)) //Only a minor failure, enjoy your radiation if you're in the same tile or carrying it
+ for (var/mob/living/M in range(round(max(prototype.criticality, 1)),T))
to_chat(M, SPAN_WARNING("You feel a warm sensation."))
- M.apply_damage(rand(1,40)*max(prototype.criticality, 1), DAMAGE_RADIATION)
- return
+ M.apply_damage(rand(100,200)*max(prototype.criticality, 1), DAMAGE_RADIATION)
+ ..()
/obj/item/laser_components/capacitor/nuclear/critical_fail(var/mob/living/user, var/obj/item/gun/energy/laser/prototype/prototype)
visible_message(SPAN_DANGER("\The [src] in \the [prototype] goes critical and explodes in a burst of radiation!"), range = 6)
@@ -81,8 +95,11 @@
for (var/mob/living/M in range(rand(2,6)*max(prototype.criticality, 1),T))
to_chat(M, SPAN_WARNING("You feel a wave of heat wash over you."))
M.apply_damage(300*max(prototype.criticality, 1), DAMAGE_RADIATION)
+ SSradiation.radiate(src, 50*max(prototype.criticality, 1))
+ new /obj/effect/decal/cleanable/greenglow/radioactive/medium(T)
..()
+
/obj/item/laser_components/capacitor/teranium
name = "teranium-enriched capacitor"
desc = "A capacitor built from teranium enriched materials."
@@ -92,17 +109,18 @@
reliability = 55
/obj/item/laser_components/capacitor/teranium/small_fail(var/mob/user, var/obj/item/gun/energy/laser/prototype/prototype)
+ visible_message(SPAN_WARNING("\The [src] in \the [prototype] spits angry sparks!"))
tesla_zap(prototype, 2, 1000*max(prototype.criticality, 1))
return
/obj/item/laser_components/capacitor/teranium/medium_fail(var/mob/user, var/obj/item/gun/energy/laser/prototype/prototype)
- visible_message(SPAN_DANGER("\The [src] in \the [prototype] shoots random arcs of electricity as it overloads!"), range = 6)
- tesla_zap(prototype, round(max(prototype.criticality, 1)*2,1), 2000*max(prototype.criticality, 1))
+ visible_message(SPAN_WARNING("\The [src] in \the [prototype] shoots random arcs of electricity as it overloads!"))
+ tesla_zap(prototype, round(max(prototype.criticality, 1)*2,1), 2000*prototype.criticality)
return
/obj/item/laser_components/capacitor/teranium/critical_fail(var/mob/user, var/obj/item/gun/energy/laser/prototype/prototype)
- visible_message(SPAN_DANGER("\The [src] in \the [prototype] blasts huge lightning bolts in all directions as it goes critical!"), range = 6)
- tesla_zap(prototype, round(max(prototype.criticality, 1)*2,1), 4000*max(prototype.criticality, 1), TRUE)
+ visible_message(SPAN_DANGER("\The [src] in \the [prototype] blasts huge lightning bolts in all directions as it goes critical!"))
+ tesla_zap(prototype, round(max(prototype.criticality, 1)*2,1), 4000*prototype.criticality, TRUE)
..()
/obj/item/laser_components/capacitor/phoron
@@ -110,38 +128,39 @@
desc = "A capacitor built from phoron enriched materials."
icon_state = "phoron_capacitor"
damage = 30
- shots = 25
+ shots = 20
reliability = 50
/obj/item/laser_components/capacitor/phoron/small_fail(var/mob/living/user, var/obj/item/gun/energy/laser/prototype/prototype)
- tesla_zap(prototype, 1, 1000*max(prototype.criticality, 1))
- var/turf/T = get_turf(src)
- for (var/mob/living/M in range(0, T)) //Only a minor failure, enjoy your radiation if you're in the same tile or carrying it
- to_chat(M, SPAN_WARNING("You feel a warm sensation."))
- M.apply_damage(rand(1,10)*max(prototype.criticality, 1), DAMAGE_RADIATION)
- return
+ if (user)
+ visible_message(SPAN_WARNING("\The [src] powering \the [prototype] sparks in \the [user]'s hand and briefly glows a sickly yellow."), range = 4)
+ user.apply_damage(rand(50,100)*max(prototype.criticality, 1), DAMAGE_RADIATION)
+ else
+ visible_message(SPAN_WARNING("\The [src] powering \the [prototype] sparks and briefly glows a sickly yellow."), range = 4)
+ SSradiation.radiate(src, 10*max(prototype.criticality, 1))
+ ..()
/obj/item/laser_components/capacitor/phoron/medium_fail(var/mob/living/user, var/obj/item/gun/energy/laser/prototype/prototype)
if (user)
- visible_message(SPAN_DANGER("\The [src] powering \the [prototype] hisses in \the [user]'s hand and explosively vents hot phoron!"), range = 6)
+ visible_message(SPAN_WARNING("\The [src] powering \the [prototype] hisses in \the [user]'s hand and explosively vents hot phoron!"))
if (prototype in list(user.l_hand))
user.apply_damage(25*max(prototype.criticality, 1), DAMAGE_BRUTE, BP_L_HAND, prototype, DAMAGE_FLAG_EXPLODE)
else if (prototype in list(user.r_hand))
user.apply_damage(25*max(prototype.criticality, 1), DAMAGE_BRUTE, BP_R_HAND, prototype, DAMAGE_FLAG_EXPLODE)
else
- visible_message(SPAN_DANGER("\The [src] powering \the [prototype] hisses and explosively vents hot phoron!"), range = 6)
+ visible_message(SPAN_DANGER("\The [src] powering \the [prototype] hisses and explosively vents hot phoron!"))
explosion(get_turf(prototype), 0, 0, round(max(prototype.criticality, 1)*2,1))
- return
+ ..()
/obj/item/laser_components/capacitor/phoron/critical_fail(var/mob/living/user, var/obj/item/gun/energy/laser/prototype/prototype)
if (user)
- visible_message(SPAN_DANGER("\The [src] powering \the [prototype] goes critical in \the [user]'s hand causing a massive explosion!"), range = 6)
+ visible_message(SPAN_DANGER("\The [src] powering \the [prototype] goes critical in \the [user]'s hand causing a massive explosion!"))
if (prototype in list(user.l_hand))
user.apply_damage(40*max(prototype.criticality, 1), DAMAGE_BRUTE, BP_L_HAND, prototype, DAMAGE_FLAG_EXPLODE)
else if (prototype in list(user.r_hand))
user.apply_damage(40*max(prototype.criticality, 1), DAMAGE_BRUTE, BP_R_HAND, prototype, DAMAGE_FLAG_EXPLODE)
else
- visible_message(SPAN_DANGER("\The [src] powering \the [prototype] goes critical causing a massive explosion!"), range = 6)
+ visible_message(SPAN_DANGER("\The [src] powering \the [prototype] goes critical causing a massive explosion!"))
empulse(get_turf(src), round(max(prototype.criticality, 1),1), round(max(prototype.criticality, 1)*4,1))
explosion(get_turf(prototype), 0, round(max(prototype.criticality, 1),1), round(max(prototype.criticality, 1)*3,1))
..()
@@ -151,26 +170,25 @@
desc = "A capacitor built from bluespace enriched materials."
icon_state = "bluespace_capacitor"
damage = 35
- shots = 30
+ shots = 25
reliability = 45
/obj/item/laser_components/capacitor/bluespace/small_fail(var/mob/living/user, var/obj/item/gun/energy/laser/prototype/prototype)
- tesla_zap(prototype, 1, 1000*max(prototype.criticality, 1))
+ visible_message(SPAN_WARNING("\The [src] in \the [prototype] sparks and everything touching it teleports a short distance!"))
var/turf/T = get_turf(src)
for (var/mob/living/M in range(round(max(prototype.criticality, 1),1),T))
do_teleport(M, get_turf(M), rand(1,3)*round(max(prototype.criticality, 1),1), asoundin = 'sound/effects/phasein.ogg')
- return
+ ..()
/obj/item/laser_components/capacitor/bluespace/medium_fail(var/mob/living/user, var/obj/item/gun/energy/laser/prototype/prototype)
- visible_message(SPAN_DANGER("\The [src] in \the [prototype] flickers then vanishes along with everything around it!"), range = 6)
+ visible_message(SPAN_WARNING("\The [src] in \the [prototype] flickers then vanishes along with everything around it!"))
var/turf/T = get_turf(src)
for (var/mob/living/M in range(round(3*max(prototype.criticality, 1),1),T))
empulse(get_turf(M), 0, round(max(prototype.criticality, 1)*2,1))
do_teleport(M, get_turf(M), rand(2,6)*round(max(prototype.criticality, 1),1), asoundin = 'sound/effects/phasein.ogg')
- return
-
+ ..()
/obj/item/laser_components/capacitor/bluespace/critical_fail(var/mob/living/user, var/obj/item/gun/energy/laser/prototype/prototype)
- visible_message(SPAN_DANGER("\The [src] in \the [prototype] implodes in a catastrophic spatial anomaly, teleporting everything around it!"), range = 6)
+ visible_message(SPAN_DANGER("\The [src] in \the [prototype] implodes in a catastrophic spatial anomaly, teleporting everything around it!"))
var/turf/T = get_turf(src)
for (var/mob/living/M in range(round(6*max(prototype.criticality, 1),1),T))
empulse(get_turf(M), 0, round(max(prototype.criticality, 1)*4,1))
@@ -182,7 +200,7 @@
name = "splitter lens"
desc = "A focusing lens that splits the beam into several sub-beams."
icon_state = "splitter_lens"
- dispersion = list(5, 15, 25, 35, 45, 55, 65, 75)
+ dispersion = list(5, 10, 15, 20, 25, 30, 35, 40)
burst = 4
accuracy = -1
reliability = 35
@@ -193,7 +211,7 @@
icon_state = "precise_lens"
accuracy = 2
reliability = 30
- dispersion = list(0)
+ dispersion = list(0, 0, 0, 0, 0, 1, 2, 3, 4, 5)
/obj/item/laser_components/focusing_lens/strong
name = "reinforced lens"
@@ -218,6 +236,8 @@
malus = 2
reliability = -10
criticality = 1.5
+ increasable_stats = list()
+ decreaseable_stats = list("base_malus", "criticality")
/obj/item/laser_components/modifier/surge
name = "surge protector"
@@ -227,6 +247,8 @@
malus = 0 //subtracted from weapon's overall reliability everytime it's fired
criticality = 0.5
icon_state = "surge_protector"
+ increasable_stats = list()
+ decreaseable_stats = list("criticality")
/obj/item/laser_components/modifier/repeater
name = "pulser"
@@ -235,20 +257,25 @@
malus = 0.5 //subtracted from weapon's overall reliability everytime it's fired
burst_delay = 1
burst = 3
+ damage = 1.1
fire_delay = 3
accuracy = -1
icon_state = "pulser"
+ increasable_stats = list("shots", "accuracy")
+ decreaseable_stats = list("base_malus", "fire_delay", "burst_delay", "fire_delay")
/obj/item/laser_components/modifier/auxiliarycap
name = "auxiliary capacitor"
- desc = "A string of sub-capacitors along the central cell provide additional bang for your buck."
+ desc = "A string of sub-capacitors along the central capacitor moderates its discharge, increasing capacitor efficiency by reducing peak output."
base_malus = 3
malus = 3
reliability = -10
shots = 2
- damage = 1.5
+ damage = 0.8
criticality = 1.25
icon_state = "aux_capacitor"
+ increasable_stats = list("reliability", "shots", "damage")
+ decreaseable_stats = list("base_malus", "criticality")
/obj/item/laser_components/modifier/overcharge
name = "capacitor overcharge"
@@ -257,9 +284,11 @@
malus = 5
reliability = -25
shots = 0.5
- damage = 2.5
+ damage = 2
criticality = 2
icon_state = "capacitor_overcharge"
+ increasable_stats = list("reliability")
+ decreaseable_stats = list("base_malus", "criticality")
/obj/item/laser_components/modifier/gatling
name = "gatling rotator"
@@ -268,10 +297,13 @@
malus = 0.5
burst_delay = 1
burst = 10
+ damage = 2 //With the way armour works, splitting the damage across multiple shots actually reduces the overall damage, so we compensate by increasing it.
fire_delay = 10
chargetime = 3
- accuracy = -3
+ accuracy = -1
icon_state = "rotating_lens"
+ increasable_stats = list()
+ decreaseable_stats = list("fire_delay", "chargetime")
/obj/item/laser_components/modifier/scope
name = "telescopic sight"
@@ -296,16 +328,21 @@
name = "exhaust venting"
desc = "More efficient exhaust venting reduces the impact of firing the prototype."
reliability = 0
- base_malus = -5
- malus = -5
+ base_malus = -0.5
+ malus = -0.5
+ malus_multiplier = 0.5
icon_state = "vents"
+ increasable_stats = list()
+ decreaseable_stats = list("base_malus")
/obj/item/laser_components/modifier/grip
name = "enhanced grip"
desc = "A modification that improves the fire delay of the prototype."
- fire_delay = 0.5
+ fire_delay = 0.8
gun_overlay = "grip"
icon_state = "grip"
+ increasable_stats = list()
+ decreaseable_stats = list("fire_delay")
/obj/item/laser_components/modifier/grip/improved
name = "enhanced grip MK2"
@@ -317,15 +354,17 @@
/obj/item/laser_components/modifier/stock
name = "improved stock"
desc = "A modification that improves the accuracy."
- fire_delay = 0.5
+ fire_delay = 0.9
accuracy = 1
gun_overlay = "stock"
icon_state = "improved_stock"
+ increasable_stats = list("accuracy")
+ decreaseable_stats = list("fire_delay")
/obj/item/laser_components/modifier/stock/gyro
name = "stability stock"
desc = "A better version of na improved stock. This stock is more ergonomic, with in-built gyroscope increasing handly and accuracy."
- fire_delay = 0.7
+ fire_delay = 0.8
accuracy = 1.5
icon_state = "stable_stock"
@@ -335,6 +374,8 @@
gun_force = 10
gun_overlay = "bayonet"
icon_state = "bayonet_item"
+ increasable_stats = list("gun_force")
+ decreaseable_stats = list()
/obj/item/laser_components/modifier/ebayonet
name = "energy bayonet"
@@ -342,6 +383,8 @@
gun_force = 25
gun_overlay = "ebayonet"
icon_state = "ebayonet_item"
+ increasable_stats = list("gun_force")
+ decreaseable_stats = list()
//Projectile modulators
@@ -396,42 +439,14 @@
icon_state = "betaray"
firing_sound = 'sound/effects/stealthoff.ogg'
-/obj/item/laser_components/modulator/arodentia
- name = "arodentia modulator"
- desc = "Modulates the beam into firing precise electrical arcs designed for pest control."
- projectile = /obj/projectile/beam/mousegun
- damage = 0
+/obj/item/laser_components/modulator/xenovermin
+ name = "xenovermin modulator"
+ desc = "Modulates the beam into firing precise electrical arcs designed for pest and xenofauna control."
+ projectile = /obj/projectile/beam/mousegun/xenofauna
+ damage = 0.1
icon_state = "pesker"
firing_sound = 'sound/weapons/taser2.ogg'
-/obj/item/laser_components/modulator/red
- name = "red team modulator"
- desc = "Modulates the beam into firing red team tagger beams."
- projectile = /obj/projectile/beam/laser_tag
- damage = 0
- icon_state = "red"
-
-/obj/item/laser_components/modulator/blue
- name = "blue team modulator"
- desc = "Modulates the beam into firing blue team tagger beams."
- projectile = /obj/projectile/beam/laser_tag/blue
- damage = 0
- icon_state = "blue"
-
-/obj/item/laser_components/modulator/omni
- name = "omni team modulator"
- desc = "Modulates the beam into firing omni team tagger beams."
- projectile = /obj/projectile/beam/laser_tag/omni
- damage = 0
- icon_state = "omni"
-
-/obj/item/laser_components/modulator/practice
- name = "practice beam modulator"
- desc = "Modulates the beam into firing nonlethal practice beams."
- projectile = /obj/projectile/beam/practice
- damage = 0
- icon_state = "practice"
-
/obj/item/laser_components/modulator/mindflayer
name = "mind flayer modulator"
desc = "Modulates the beam into firing \"mind flayer\" beams."
diff --git a/code/modules/projectiles/pins.dm b/code/modules/projectiles/pins.dm
index 8796961d0bc..0116b5732be 100644
--- a/code/modules/projectiles/pins.dm
+++ b/code/modules/projectiles/pins.dm
@@ -111,9 +111,9 @@ Pins Below.
// Test pin, works only near firing ranges.
/obj/item/firing_pin/test_range
- name = "test-range firing pin"
- desc = "This safety firing pin allows weapons to be fired within proximity to a firing range."
- fail_message = SPAN_WARNING("TEST RANGE CHECK FAILED.")
+ name = "research firing pin"
+ desc = "This safety firing pin allows weapons to be fired within the research department or a firing range."
+ fail_message = SPAN_WARNING("AREA CHECK FAILED.")
pin_replaceable = 1
durable = TRUE
origin_tech = list(TECH_MATERIAL = 2, TECH_COMBAT = 2)
@@ -246,7 +246,7 @@ Pins Below.
req_access = list(ACCESS_WEAPONS)
/obj/item/firing_pin/access/pin_auth(mob/living/user)
- return !allowed(user)
+ return allowed(user)
/obj/item/firing_pin/away_site
name = "away site firing pin"
diff --git a/code/modules/projectiles/projectile/beams.dm b/code/modules/projectiles/projectile/beams.dm
index 46a575ed569..7ce3d1727d5 100644
--- a/code/modules/projectiles/projectile/beams.dm
+++ b/code/modules/projectiles/projectile/beams.dm
@@ -506,6 +506,7 @@
damage = 15
damage_type = DAMAGE_BURN
check_armor = ENERGY
+ var/temperature_damage = 40
muzzle_type = /obj/effect/projectile/muzzle/laser/blue
tracer_type = /obj/effect/projectile/tracer/laser/blue
@@ -515,7 +516,7 @@
. = ..()
if(isliving(target))
var/mob/living/L = target
- L.bodytemperature -= 40
+ L.bodytemperature -= temperature_damage
if(ishuman(L))
var/mob/living/carbon/human/H = L
diff --git a/code/modules/research/designs/protolathe/modular_gun_designs.dm b/code/modules/research/designs/protolathe/modular_gun_designs.dm
index 1bc78b2c077..ca2a27f607b 100644
--- a/code/modules/research/designs/protolathe/modular_gun_designs.dm
+++ b/code/modules/research/designs/protolathe/modular_gun_designs.dm
@@ -9,6 +9,10 @@
/datum/design/item/modular_weapon/firing_pin/away
build_path = /obj/item/firing_pin/away_site
+/datum/design/item/modular_weapon/firing_pin/access
+ req_tech = list(TECH_MATERIAL = 3, TECH_DATA = 4)
+ build_path = /obj/item/firing_pin/access
+
/datum/design/item/modular_weapon/modular_small
req_tech = list(TECH_MATERIAL = 1)
materials = list(DEFAULT_WALL_MATERIAL = 2000)
@@ -39,6 +43,16 @@
materials = list(DEFAULT_WALL_MATERIAL = 4000)
build_path = /obj/item/laser_components/capacitor/reinforced
+/datum/design/item/modular_weapon/modular_capacitor_highcap
+ req_tech = list(TECH_POWER = 3, TECH_ENGINEERING = 3)
+ materials = list(DEFAULT_WALL_MATERIAL = 4000, MATERIAL_GLASS = 1000, MATERIAL_PHORON = 500)
+ build_path = /obj/item/laser_components/capacitor/highcap
+
+/datum/design/item/modular_weapon/modular_capacitor_highpower
+ req_tech = list(TECH_POWER = 3, TECH_ENGINEERING = 3)
+ materials = list(DEFAULT_WALL_MATERIAL = 4000, MATERIAL_GLASS = 1000, MATERIAL_PHORON = 500)
+ build_path = /obj/item/laser_components/capacitor/highpower
+
/datum/design/item/modular_weapon/modular_nuke
req_tech = list(TECH_POWER = 5, TECH_ENGINEERING = 5)
materials = list(DEFAULT_WALL_MATERIAL = 4000, MATERIAL_URANIUM = 1000)
@@ -192,27 +206,7 @@
/datum/design/item/modular_weapon/modular_pest
req_tech = list(TECH_MATERIAL = 1, TECH_BIO = 4, TECH_POWER = 3)
materials = list(DEFAULT_WALL_MATERIAL = 2000, MATERIAL_GLASS = 1000, MATERIAL_URANIUM = 500)
- build_path = /obj/item/laser_components/modulator/arodentia
-
-/datum/design/item/modular_weapon/modular_tag1
- req_tech = list(TECH_COMBAT = 1, TECH_MATERIAL = 1)
- materials = list(DEFAULT_WALL_MATERIAL = 2000)
- build_path = /obj/item/laser_components/modulator/red
-
-/datum/design/item/modular_weapon/modular_tag2
- req_tech = list(TECH_COMBAT = 1, TECH_MATERIAL = 1)
- materials = list(DEFAULT_WALL_MATERIAL = 2000)
- build_path = /obj/item/laser_components/modulator/blue
-
-/datum/design/item/modular_weapon/modular_tag3
- req_tech = list(TECH_COMBAT = 2, TECH_MATERIAL = 1)
- materials = list(DEFAULT_WALL_MATERIAL = 2000)
- build_path = /obj/item/laser_components/modulator/omni
-
-/datum/design/item/modular_weapon/modular_practice
- req_tech = list(TECH_MATERIAL = 1)
- materials = list(DEFAULT_WALL_MATERIAL = 2000)
- build_path = /obj/item/laser_components/modulator/practice
+ build_path = /obj/item/laser_components/modulator/xenovermin
/datum/design/item/modular_weapon/modular_decloner
req_tech = list(TECH_COMBAT = 5, TECH_PHORON = 4)
diff --git a/code/modules/research/weaponsanalyzer.dm b/code/modules/research/weaponsanalyzer.dm
index c570fb751eb..1f0421a5e15 100644
--- a/code/modules/research/weaponsanalyzer.dm
+++ b/code/modules/research/weaponsanalyzer.dm
@@ -55,12 +55,14 @@
addtimer(CALLBACK(src, PROC_REF(reset)), 15)
process = TRUE
update_icon()
+
else if(attacking_item)
check_swap(user, attacking_item)
item = attacking_item
H.drop_from_inventory(attacking_item)
attacking_item.forceMove(src)
update_icon()
+ ui_interact(user)
/obj/structure/machinery/r_n_d/weapons_analyzer/attack_hand(mob/user)
user.set_machine(src)
@@ -69,6 +71,7 @@
/obj/structure/machinery/r_n_d/weapons_analyzer/proc/reset()
process = FALSE
update_icon()
+ SStgui.update_uis(src)
/obj/structure/machinery/r_n_d/weapons_analyzer/proc/check_swap(var/mob/user, var/obj/I)
if(item)
@@ -97,11 +100,13 @@
A.forceMove(get_turf(src))
item = null
update_icon()
+ SStgui.update_uis(src)
else if(item)
item.forceMove(get_turf(src))
item = null
update_icon()
+ SStgui.update_uis(src)
else
to_chat(usr, SPAN_WARNING("There is nothing in \the [src]."))
@@ -131,6 +136,10 @@
/obj/structure/machinery/r_n_d/weapons_analyzer/ui_data(mob/user)
var/list/data = list()
+ data["laser_assembly"] = null
+ data["gun"] = null
+ data["item"] = null
+ data["gun_mods"] = null
if(istype(item, /obj/item/laser_assembly))
var/obj/item/laser_assembly/assembly = item
@@ -141,16 +150,24 @@
continue
var/l_repair_name = initial(l_component.repair_item.name) ? initial(l_component.repair_item.name) : "nothing"
- mods += list(list(
+ var/list/mod = list(
"name" = initial(l_component.name),
- "reliability" = initial(l_component.reliability),
- "damage_modifier" = initial(l_component.damage),
- "fire_delay_modifier" = initial(l_component.fire_delay),
- "shots_modifier" = initial(l_component.shots),
- "burst_modifier" = initial(l_component.burst),
- "accuracy_modifier" = initial(l_component.accuracy),
"repair_tool" = l_repair_name
- ))
+ )
+ if(l_component.reliability != 0)
+ mod["reliability"] = l_component.reliability
+ if(l_component.damage != 1)
+ mod["damage_modifier"] = l_component.damage
+ if(l_component.fire_delay != 1)
+ mod["fire_delay_modifier"] = l_component.fire_delay
+ if(l_component.shots != 1)
+ mod["shots_modifier"] = l_component.shots
+ if(l_component.burst != 0)
+ mod["burst_modifier"] = l_component.burst
+ if(l_component.accuracy != 0)
+ mod["accuracy_modifier"] = l_component.accuracy
+ mods += list(mod)
+
data["gun_mods"] = mods
data["laser_assembly"] = list("name" = assembly.name)
@@ -171,7 +188,7 @@
if(istype(gun, /obj/item/gun/energy))
var/obj/item/gun/energy/E = gun
var/obj/projectile/P = new E.projectile_type
- data["gun"]["max_shots"] = initial(E.max_shots)
+ data["gun"]["max_shots"] = E.max_shots
data["gun"]["recharge"] = E.self_recharge ? "self recharging" : "not self recharging" //Not initial because modular guns are not self charging at initialization
data["gun"]["recharge_time"] = initial(E.recharge_time)
data["gun"]["damage"] = initial(P.damage)
@@ -197,18 +214,26 @@
if (l_component.shots != 0)
l_modified_max_shots *= l_component.shots
var/l_repair_name = initial(l_component.repair_item.name) ? initial(l_component.repair_item.name) : "nothing"
- mods += list(list(
+ var/list/mod = list(
"name" = initial(l_component.name),
- "reliability" = initial(l_component.reliability),
- "damage_modifier" = initial(l_component.damage),
- "fire_delay_modifier" = initial(l_component.fire_delay),
- "shots_modifier" = initial(l_component.shots),
- "burst_modifier" = initial(l_component.burst),
- "accuracy_modifier" = initial(l_component.accuracy),
"repair_tool" = l_repair_name
- ))
- data["gun"]["damage"] = min(60, l_modified_damage)
- data["gun"]["max_shots"] = l_modified_max_shots
+ )
+ if(l_component.reliability != 0)
+ mod["reliability"] = round(l_component.reliability, 1) //only show these if they do something
+ if(l_component.damage != 1)
+ mod["damage_modifier"] = round(l_component.damage, 0.1)
+ if(l_component.fire_delay != 1)
+ mod["fire_delay_modifier"] = round(l_component.fire_delay, 0.1)
+ if(l_component.shots != 1)
+ mod["shots_modifier"] = round(l_component.shots, 0.1)
+ if(l_component.burst != 0)
+ mod["burst_modifier"] = round(l_component.burst, 1)
+ if(l_component.accuracy != 0)
+ mod["accuracy_modifier"] = round(l_component.accuracy, 0.1)
+ mods += list(mod)
+
+ data["gun"]["damage"] = round(min(60, l_modified_damage), 1)
+ data["gun"]["max_shots"] = round(l_modified_max_shots)
data["gun_mods"] = mods
if(E.secondary_projectile_type)
@@ -258,18 +283,11 @@
return data
/obj/structure/machinery/r_n_d/weapons_analyzer/ui_interact(mob/user, var/datum/tgui/ui)
- var/height = item ? 600: 300
- var/width = item ? 500 : 300
- if(istype(item, /obj/item/gun/energy/laser/prototype) || istype(item, /obj/item/laser_assembly))
- width = 600
-
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, "WeaponsAnalyzer", "Weapons Analyzer", width, height)
+ ui = new(user, src, "WeaponsAnalyzer", "Weapons Analyzer", 600, 600)
ui.open()
- ui.open()
-
/obj/structure/machinery/r_n_d/weapons_analyzer/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
diff --git a/html/changelogs/Fenodyree-ModlaserSkillsRework.yml b/html/changelogs/Fenodyree-ModlaserSkillsRework.yml
new file mode 100644
index 00000000000..05dc4c42fd7
--- /dev/null
+++ b/html/changelogs/Fenodyree-ModlaserSkillsRework.yml
@@ -0,0 +1,71 @@
+################################
+# 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: Fenodyree
+
+# 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:
+ - rscadd: "Adds skill checks to all modular laser examine texts, obscuring information from low skill levels and giving more detail to high skill levels."
+ - rscadd: "Adds a skill based upgrade system to modular lasers. This allows high research & firearm skill characters to upgrade weapons. This requires they take damage through use, with more improvement coming from real use against living targets."
+ - rscadd: "Adds two sidegrade capacitor designs, buildable with roundstart materials and max tech."
+ - rscadd: "Adds the interior of research to the allowed areas of the firing range pin and renames it."
+ - rscdel: "Removes the practice laser and laser tag emitters. They were broken and cluttered the research menu."
+ - balance: "Nerfs the starting stats of the most powerful modular laser components and modifiers. They can be upgraded to slightly higher than their previous starting stats."
+ - balance: "Nerfs the three strongest modifiers, aux capacitor, capacitor overcharge and heat vents. It is now impossible to build unbreakable guns."
+ - balance: "Buffs the gattling modifier, low damage shots struggle against armour, so it was a downgrade in every way previously."
+ - bugfix: "Fixes the weapon analyzer UI not updating."
+ - bugfix: "Fixes the weapon printout."
+ - bugfix: "Fixes click delay not happening on malfunction."
+ - bugfix: "Fixes the radiation and electrocution damage on malfunction."
+ - bugfix: "Fixes the xenozapper modulator to use the correct projectile."
+
diff --git a/maps/sccv_horizon/areas/horizon_areas_science.dm b/maps/sccv_horizon/areas/horizon_areas_science.dm
index fe1991bceb1..b90f4514392 100644
--- a/maps/sccv_horizon/areas/horizon_areas_science.dm
+++ b/maps/sccv_horizon/areas/horizon_areas_science.dm
@@ -5,6 +5,7 @@
department = LOC_SCIENCE
horizon_deck = 2
icon_state = "research"
+ area_flags = AREA_FLAG_FIRING_RANGE //Lets science shoot guns inside their own department.
area_blurb = "The science sectors of the ship lend themselves to a clean, functional sterility; at least when everything is going well."
/area/horizon/rnd/conference
@@ -14,6 +15,7 @@
name = "Hallway"
holomap_color = HOLOMAP_AREACOLOR_CIVILIAN
lightswitch = TRUE
+ area_flags = null //Shouldn't be shooting in the public hallway.
location_ew = LOC_PORT
/area/horizon/rnd/hallway/secondary
diff --git a/tgui/packages/tgui/interfaces/WeaponsAnalyzer.tsx b/tgui/packages/tgui/interfaces/WeaponsAnalyzer.tsx
index 5ca342e1647..4870d724232 100644
--- a/tgui/packages/tgui/interfaces/WeaponsAnalyzer.tsx
+++ b/tgui/packages/tgui/interfaces/WeaponsAnalyzer.tsx
@@ -227,24 +227,48 @@ export const GunMods = (props, context) => {
{data.gun_mods.map((mod) => (
-
- {mod.reliability}
-
-
- {mod.damage_modifier}
-
-
- {mod.fire_delay_modifier}
-
-
- {mod.shots_modifier}
-
-
- {mod.burst_modifier}
-
-
- {mod.accuracy_modifier}
-
+ {mod.reliability ? (
+
+ {mod.reliability}
+
+ ) : (
+ ''
+ )}
+ {mod.damage_modifier ? (
+
+ {mod.damage_modifier}
+
+ ) : (
+ ''
+ )}
+ {mod.fire_delay_modifier ? (
+
+ {mod.fire_delay_modifier}
+
+ ) : (
+ ''
+ )}
+ {mod.shots_modifier ? (
+
+ {mod.shots_modifier}
+
+ ) : (
+ ''
+ )}
+ {mod.burst_modifier ? (
+
+ {mod.burst_modifier}
+
+ ) : (
+ ''
+ )}
+ {mod.accuracy_modifier ? (
+
+ {mod.accuracy_modifier}
+
+ ) : (
+ ''
+ )}
{capitalizeAll(mod.repair_tool)}