Files
Aurora.3/code/modules/research/weaponsanalyzer.dm
FenodyreeAv 87848fbdc3 Adds skill based upgrades to modular lasers. (#22425)
**Modular laser changes**

- tl:dr Shooting mod laser at a mob counts up an improvement potential
variable, which when the gun is disassembled, gets spread across the
components.
- When the components are repaired, by someone with high skill, there is
a chance to increase a variable, such as damage, shot count, accuracy.
- Players give the biggest increase, followed by non-player humans
(protohumans & mechs fall in this catagory), then simple mobs and
finally monkeys. Non combat modlasers improve slowly when used for their
intended task (betarays slowly tick up when shooting hydroponics bays)

The philosophy for these changes is that research should continue to be
involved with the equipment they give out.
It should also be difficult (but not impossible) for research to upgrade
their weapons without support from other departments.
Xenobio slimes, protohumans, medical help to keep the protohuman alive
while you shoot it, security help to actually use the guns on real
targets (this gives the biggest increases).

The more powerful a weapon is the harder it is to upgrade, as it'll kill
it's targets quicker. The more upgraded a weapon is the more difficult
it is to upgrade further, improvement potential will get wasted on
components that have already hit their cap.

On their own, by the time the 5 monkeys research start with are dead,
it's possible for a max skill scientist to improve one gun's worth of
components by ~25%.

The weapons research can now make at roundstart (with max tech) are
significantly worse than what spawns in the armoury.
The weapons research can make with gold, silver, uranium and phoron are
equal to what spawns in the armoury.
The weapons research can make with diamonds are better than what spawns
in the armoury.

Major nerfs to heat vents, auxiliary capacitor and capacitor overcharge,
these were the problem children.
It is now impossible to make a weapon that does not take damage through
use. Safe designs will only break with heavy use, by people who don't
bring them back to research for repairs.
Powerful designs break quickly and need repeated repairs.

Adds two side grade capacitors that can be made at roundstart (with max
tech), one higher damage and fewer shots. One lower damage and more
shots.

High skill characters get much more information when examining modular
lasers and a chance to throw the gun away before it explodes.

Adds some defines to help balance these changes:
Improvement cap (the total percent a component can increase) Currently
100%.
Increase & Decrease Cap, how much an individual variable can be
increased or decreased. Currently 2x and 0.2x.
Improvement multiplier, multiplies all improvement gains. Currently 1x.

**Firing Pin fixes & changes**
Fixed ID locked firing pins and adds them to R&D.

Adds the inner research department to the firing range locked pins. This
is so xenobotanists, xenobiologists & xenoarchs can use the modular
lasers. Freeze rays for slimes, betarays for xenobotanists, laser
activation for artifacts for xenoarch.

It is still impossible for a scientist to make a weapon they can use
anywhere, but they can give them to security who can use them freely.

**Misc bug fixes**
The weapon analyzer UI now updates and only shows relevant statistics,
instead of displaying every variable even when they do nothing.
Replaces the buggy tesla zap with just electrocuting the holder.
Fixes a few broken visual messages.
Fixes the vermin modulator not killing anything, now it also works on
grems.
Fixes click delay on malfunction.
Fixes radiation damage numbers.
Fixes the temperature modulator.
Fixes the report printout showing the wrong numbers.

---------

Co-authored-by: Copilot <copilot@github.com>
2026-05-27 15:51:37 +00:00

311 lines
11 KiB
Plaintext

/obj/structure/machinery/r_n_d/weapons_analyzer
name = "weapons analyzer"
desc = "A research device which can be used to put together modular energy weapons, or to gain knowledge about the effectiveness of various objects as weaponry."
icon_state = "weapon_analyzer"
idle_power_usage = 60
active_power_usage = 2000
var/obj/item/item = null
var/process = FALSE
component_types = list(
/obj/item/stock_parts/scanning_module = 2,
/obj/item/stock_parts/capacitor = 1,
/obj/item/stock_parts/console_screen = 1
)
/obj/structure/machinery/r_n_d/weapons_analyzer/feedback_hints(mob/user, distance, is_adjacent)
. += ..()
. += SPAN_NOTICE("It has [item ? "[item.name]" : "nothing"] attached.")
/obj/structure/machinery/r_n_d/weapons_analyzer/attackby(obj/item/attacking_item, mob/user)
if(!attacking_item || !user || !ishuman(user))
return
var/mob/living/carbon/human/H = user
if(istype(attacking_item, /obj/item/gun))
check_swap(user, attacking_item)
item = attacking_item
H.drop_from_inventory(attacking_item)
attacking_item.forceMove(src)
update_icon()
else if(istype(attacking_item, /obj/item/laser_assembly))
check_swap(user, attacking_item)
var/obj/item/laser_assembly/A = attacking_item
A.ready_to_craft = TRUE
item = A
H.drop_from_inventory(attacking_item)
attacking_item.forceMove(src)
A.analyzer = WEAKREF(src)
update_icon()
else if(istype(attacking_item, /obj/item/laser_components) && istype(item, /obj/item/laser_assembly))
if(process)
to_chat(user, SPAN_WARNING("\The [src] is busy installing a component already."))
return
var/obj/item/laser_assembly/A = item
var/success = A.attackby(attacking_item, user)
if(!success)
return
if(success == 2)
playsound(loc, 'sound/machines/weapons_analyzer_finish.ogg', 75, 1)
addtimer(CALLBACK(src, PROC_REF(reset)), 32)
else
playsound(loc, 'sound/machines/weapons_analyzer.ogg', 75, 1)
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)
ui_interact(user)
/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)
to_chat(user, SPAN_NOTICE("You swap \the [item] out for \the [I]."))
if(istype(item, /obj/item/laser_assembly))
var/obj/item/laser_assembly/A = item
A.ready_to_craft = FALSE
A.analyzer = null
item.forceMove(get_turf(src))
user.put_in_hands(item)
item = null
update_icon()
/obj/structure/machinery/r_n_d/weapons_analyzer/verb/eject()
set name = "Eject Inserted Item"
set category = "Object"
set src in view(1)
if(use_check_and_message(usr))
return
if(istype(item, /obj/item/laser_assembly))
var/obj/item/laser_assembly/A = item
A.ready_to_craft = FALSE
A.analyzer = null
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]."))
/obj/structure/machinery/r_n_d/weapons_analyzer/update_icon()
icon_state = initial(icon_state)
ClearOverlays()
var/icon/Icon_used
if(istype(item, /obj/item/laser_assembly))
var/obj/item/laser_assembly/A = item
A.update_icon()
icon_state = process ? "[icon_state]_working" : "[icon_state]_on"
Icon_used = new /icon(item.icon, item.icon_state)
else if(item)
icon_state = "[icon_state]_on"
Icon_used = new /icon(item.icon, item.icon_state)
if(Icon_used)
// Making gun sprite smaller and centering it where we want, cause dang they are thicc
Icon_used.Scale(round(Icon_used.Width() * 0.75), round(Icon_used.Height() * 0.75))
var/image/gun_overlay = image(Icon_used)
gun_overlay.pixel_x += 4
gun_overlay.pixel_y += 12
AddOverlays(gun_overlay)
/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
var/list/mods = list()
for(var/i in list(assembly.capacitor, assembly.focusing_lens, assembly.modulator) + assembly.gun_mods)
var/obj/item/laser_components/l_component = i
if(!l_component)
continue
var/l_repair_name = initial(l_component.repair_item.name) ? initial(l_component.repair_item.name) : "nothing"
var/list/mod = list(
"name" = initial(l_component.name),
"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)
else if(istype(item, /obj/item/gun))
var/obj/item/gun/gun = item
data["gun"] = list(
"name" = gun.name,
"max_shots" = 0,
"recharge" = "none",
"recharge_time" = "none",
"damage" = 0,
"shrapnel_type" = "none",
"armor_penetration" = "none",
"gun_mods" = "none",
"stun" = "does not stun"
)
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"] = 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)
data["gun"]["damage_type"] = initial(P.damage_type)
data["gun"]["check_armor"] = initial(P.check_armor)
data["gun"]["stun"] = initial(P.stun) ? "stuns" : "does not stun"
if(P.shrapnel_type)
var/obj/item/S = new P.shrapnel_type
data["gun"]["shrapnel_type"] = S.name
else
data["gun"]["shrapnel_type"] = "none"
data["gun"]["armor_penetration"] = initial(P.armor_penetration)
if(istype(gun, /obj/item/gun/energy/laser/prototype))
var/obj/item/gun/energy/laser/prototype/E_prototype = gun
var/list/mods = list()
var/l_modified_damage = 1 / (max(1, gun.burst - 1)) //E_prototype.capacitor.damage * E_prototype.modulator.damage
var/l_modified_max_shots = 1 //E_prototype.capacitor.shots
for(var/i in list(E_prototype.capacitor, E_prototype.focusing_lens, E_prototype.modulator) + E_prototype.gun_mods)
var/obj/item/laser_components/l_component = i
if (l_component.damage != 0)
l_modified_damage *= l_component.damage
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"
var/list/mod = list(
"name" = initial(l_component.name),
"repair_tool" = l_repair_name
)
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)
var/obj/projectile/P_second = E.secondary_projectile_type
data["gun"]["secondary_damage"] = initial(P_second.damage)
data["gun"]["secondary_damage_type"] = initial(P_second.damage_type)
data["gun"]["secondary_check_armor"] = initial(P_second.check_armor)
data["gun"]["secondary_stun"] = initial(P_second.stun) ? "stuns" : "does not stun"
data["gun"]["secondary_shrapnel_type"] = initial(P_second.shrapnel_type) ? initial(P_second.shrapnel_type) : "none"
data["gun"]["secondary_armor_penetration"] = initial(P_second.armor_penetration)
else
var/obj/item/gun/projectile/P_gun = gun
var/obj/item/ammo_casing/casing = new P_gun.ammo_type
var/obj/projectile/P = new casing.projectile_type
data["gun"]["max_shots"] = P_gun.max_shells
data["gun"]["damage"] = initial(P.damage)
data["gun"]["damage_type"] = initial(P.damage_type)
data["gun"]["check_armor"] = initial(P.check_armor)
data["gun"]["stun"] = initial(P.stun) ? "stuns" : "does not stun"
if(P.shrapnel_type)
var/obj/item/S = new P.shrapnel_type
data["shrapnel_type"] = S.name
else
data["shrapnel_type"] = "none"
data["gun"]["burst"] = gun.burst
data["gun"]["reliability"] = gun.reliability
else if(item)
data["item"] = list()
data["item"]["name"] = item.name
data["item"]["force"] = item.force
data["item"]["sharp"] = item.sharp ? "yes" : "no"
data["item"]["edge"] = item.edge ? "likely to dismember" : "unlikely to dismember"
data["item"]["penetration"] = item.armor_penetration
data["item"]["throw_force"] = item.throwforce
data["item"]["damage_type"] = item.damtype
if(istype(item, /obj/item/melee/energy))
data["item"]["energy"] = TRUE
var/obj/item/melee/energy/E_item = item
data["item"]["active_force"] = E_item.active_force
data["item"]["active_throw_force"] = E_item.active_throwforce
data["item"]["can_block"] = E_item.can_block_bullets ? "can block" : "cannot block"
data["item"]["base_reflect_chance"] = E_item.base_reflectchance
data["item"]["base_block_chance"] = E_item.base_block_chance
data["item"]["shield_power"] = E_item.shield_power
return data
/obj/structure/machinery/r_n_d/weapons_analyzer/ui_interact(mob/user, var/datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "WeaponsAnalyzer", "Weapons Analyzer", 600, 600)
ui.open()
/obj/structure/machinery/r_n_d/weapons_analyzer/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
if((action == "print") && item)
do_print()
. = TRUE
/obj/structure/machinery/r_n_d/weapons_analyzer/proc/do_print()
var/obj/item/paper/R = new /obj/item/paper(get_turf(src))
R.color = "#fef8ff"
R.set_content_unsafe("Weapon Analysis ([item.name])", get_print_info(item))
print(R, message = "\The [src] beeps, printing \the [R] after a moment.", user = usr)
/obj/structure/machinery/r_n_d/weapons_analyzer/proc/get_print_info(var/obj/item/device)
var/dat = "<span class='notice'><b>Analysis performed at [worldtime2text()]</b></span><br>"
dat += "<span class='notice'><b>Analyzer Item: [device.name]</b></span><br><br>"
dat += device.get_print_info()
return dat