Files
FenodyreeAv f66007e324 Modlaser fixes (#22550)
Fixes the easy stuff from: #22545

The only real balance change, dropped improvement per variable by 50%.
Should have been like this from the start, but I mistakenly assumed no
one would even reach close to the cap.

Makes repairs with welders go through .use_tool, fixes not needing
welding goggles and adds nice sparky visuals.
Much more robust handling of improvable variables, negatives don't break
improvement assignment now.
Fixes improvement cap, missed a *100 to convert from decimal to
percentage.
Fixes improvement potential being assigned to things that couldn't be
upgraded.
Fixes the Gatling mod upgrading fire delay (pretty useless on something
with a 3 second warmup)
Fixes the weapon analyzer print out showing completely incorrect
numbers.
Fixes the weapon analyzer eating your combitool and being
indestructible.
Gives some more feedback on why things can and can't be upgraded.
2026-06-03 15:24:32 +00:00

319 lines
12 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(default_deconstruction_screwdriver(user, attacking_item))
return TRUE
if(default_deconstruction_crowbar(user, attacking_item))
return TRUE
if(default_part_replacement(user, attacking_item))
return TRUE
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(istype(attacking_item, /obj/item/combitool))
return
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