Files
Batrachophreno cde601b021 TGUI Fixes, Did You Think I Was Done Edition (#22641)
Fixes https://github.com/Aurorastation/Aurora.3/issues/15357
Fixes https://github.com/Aurorastation/Aurora.3/issues/22459
Fixes https://github.com/Aurorastation/Aurora.3/issues/22593
Fixes https://github.com/Aurorastation/Aurora.3/issues/22634

changes:
- bugfix: "Restores chat highlight background color selection
functionality (PR #20701)"
  - bugfix: "Restores customizable chat message retention settings."
  - bugfix: "Updates recommended vscode extension config file."
- bugfix: "Corrects some mismapped and missing scss files as well as
removing temporary working files leftover in PR."
- bugfix: "Fixes silicons/AI being unable to interact remotely with
shield capacitor and shield generator machinery."
- bugfix: "Fixes gene data disks produced by xenobotany's
lysis-isolation centrifuge machine not displaying gene names."
  - bugfix: "Makes AI remote access to machinery TGUIs more consistent."
- bugfix: "Fixes the Sensors interface not displaying sensor machinery's
current health."
- bugfix: "Fixes the Sensors interface displaying strange values for
current range."
- bugfix: "Fixes modular computer software displaying device themes
instead of explicit software themes."
  - bugfix: "Fixes MC tab links."
  - spellcheck: "Fixes a typo in the TGUI fatal error message."
2026-06-07 00:16:53 +02:00

376 lines
13 KiB
Plaintext

//renwicks: fictional unit to describe shield strength
//a small meteor hit will deduct 1 renwick of strength from that shield tile
//light explosion range will do 1 renwick's damage
//medium explosion range will do 2 renwick's damage
//heavy explosion range will do 3 renwick's damage
//explosion damage is cumulative. if a tile is in range of light, medium and heavy damage, it will take a hit from all three
/obj/structure/machinery/shield_gen
name = "bubble shield generator"
desc = "Machine that generates an impenetrable field of energy when activated."
icon = 'icons/obj/machinery/shielding.dmi'
icon_state = "generator0"
density = TRUE
use_power = POWER_USE_OFF //doesn't use APC power
req_one_access = list(ACCESS_CAPTAIN, ACCESS_SECURITY, ACCESS_ENGINE)
/// Our owned energy field.
var/datum/energy_field/energy_field
/// If the machine is powered or not.
var/powered = FALSE
/// Whether the shield generator is active or not.
var/active = FALSE
/// ID lock.
var/locked = FALSE
/// Radius of the field.
var/field_radius = 3
/// Maximum field radius.
var/max_field_radius = 100
/// The shield capacitors attached to this shield generator.
var/list/owned_capacitors = list()
/// If this shield generator supports multi-z.
var/multiz = TRUE
///How much the manipulator rating increases the maximum field strengthen rate. This increases how fast the shield can strengthen, allowing it to recover faster.
var/manipulator_bonus = 0.1 //Maximum recovery rate with base components is 0.5 renwicks per second, with maxed out manipulators it's 0.9 renwicks per second.
///How much the capacitor rating increases the field conversion rate. This increases the efficiency of converting power into shield strength, decreasing all shield power costs.
var/capacitor_bonus = 0.000005
///How much the micro laser rating decreased the field dissipation rate. This reduces the amount of shield strength lost per tick, reducing the shield upkeep.
var/micro_laser_bonus = 0.002
component_types = list(
/obj/item/circuitboard/shield_gen,
/obj/item/stock_parts/manipulator = 2,
/obj/item/stock_parts/micro_laser = 2,
/obj/item/stock_parts/capacitor = 2,
/obj/item/stock_parts/subspace/transmitter = 1,
/obj/item/stock_parts/subspace/crystal = 1,
/obj/item/stock_parts/subspace/amplifier = 1,
/obj/item/stock_parts/console_screen = 1,
/obj/item/stack/cable_coil = 5
)
/obj/structure/machinery/shield_gen/Initialize()
owned_capacitors = list()
for(var/obj/structure/machinery/shield_capacitor/possible_cap in range(1, src)) //Attach nearby capacitors
attach_capacitor(possible_cap)
energy_field = new(src, get_shielded_turfs())
. = ..()
/obj/structure/machinery/shield_gen/Destroy()
for(var/obj/structure/machinery/shield_capacitor/cap in owned_capacitors) //Detach any owned capacitors
detach_capacitor(cap)
owned_capacitors = list()
return ..()
/obj/structure/machinery/shield_gen/upgrade_hints(mob/user, distance, is_adjacent)
. += ..()
. += "Upgraded <b>manipulators</b> will increase the maximum field strengthen rate."
. += SPAN_NOTICE("\t- The current maximum field strengthen rate is <b>[energy_field.max_strengthen_rate]</b> renwicks per second.")
. += "Upgraded <b>capacitors</b> will increase the field conversion rate."
. += SPAN_NOTICE("\t- The current field conversion efficiency increase is: <b>[(initial(energy_field.energy_conversion_rate) + (2 * capacitor_bonus)) / energy_field.energy_conversion_rate * 100]%</b>")
. += "Upgraded <b>micro lasers</b> will decrease the energy field dispersion rate."
. += SPAN_NOTICE("\t- The current field dissipation rate decrease is: <b>[(initial(energy_field.dissipation_rate) - (2 * micro_laser_bonus)) / energy_field.dissipation_rate * 100]%</b>")
/obj/structure/machinery/shield_gen/emag_act(var/remaining_charges, var/mob/user)
if(prob(75))
locked = !locked
to_chat(user, "Controls are now [locked ? "locked." : "unlocked."]")
. = TRUE
updateDialog()
spark(src, 5, GLOB.alldirs)
/obj/structure/machinery/shield_gen/attackby(obj/item/attacking_item, mob/user)
if(default_part_replacement(user, attacking_item))
return TRUE
else if(default_deconstruction_screwdriver(user, attacking_item))
return TRUE
else if(default_deconstruction_crowbar(user, attacking_item))
return TRUE
if(istype(attacking_item, /obj/item/card/id))
if(allowed(user))
locked = !locked
to_chat(user, "Controls are now [locked ? "locked." : "unlocked."]")
updateDialog()
else
to_chat(user, SPAN_ALERT("Access denied."))
else if(attacking_item.tool_behaviour == TOOL_WRENCH)
anchored = !anchored
visible_message(SPAN_NOTICE("\The [src] has been [anchored ? "bolted to the floor":"unbolted from the floor"] by \the [user]."))
if(active)
toggle()
if(anchored)
// Attach nearby capacitors wrenching down the shield
owned_capacitors = list()
for(var/obj/structure/machinery/shield_capacitor/cap in range(1, src))
attach_capacitor(cap)
else
// Detach any owned capacitors when unwrenching
for(var/obj/structure/machinery/shield_capacitor/cap in owned_capacitors)
detach_capacitor(cap)
owned_capacitors = list()
else
..()
/obj/structure/machinery/shield_gen/RefreshParts()
..()
var/max_strengthen_rate_increase = 0
var/energy_conversion_rate_increase = 0
var/dissipation_rate_decrease = 0
for(var/obj/item/stock_parts/P in component_parts)
if(ismanipulator(P))
max_strengthen_rate_increase += P.rating
else if(iscapacitor(P))
energy_conversion_rate_increase += P.rating
else if(ismicrolaser(P))
dissipation_rate_decrease += P.rating
if(energy_field)
energy_field.max_strengthen_rate = initial(energy_field.max_strengthen_rate) + (max_strengthen_rate_increase * manipulator_bonus)
energy_field.energy_conversion_rate = initial(energy_field.energy_conversion_rate) + (energy_conversion_rate_increase * capacitor_bonus)
energy_field.dissipation_rate = max(0, initial(energy_field.dissipation_rate) - (dissipation_rate_decrease * micro_laser_bonus))
energy_field.strengthen_rate = min(energy_field.strengthen_rate, energy_field.max_strengthen_rate) ///If the strengthen rate got decreased and the current strengthen rate is now above the max, reduce it to the max.
/obj/structure/machinery/shield_gen/attack_ai(mob/user)
if(!ai_can_interact(user))
return
return attack_hand(user)
/obj/structure/machinery/shield_gen/attack_hand(mob/user)
if(stat & BROKEN)
return
interact(user)
/obj/structure/machinery/shield_gen/interact(mob/user)
if(locked)
to_chat(user, SPAN_WARNING("The device is locked. Swipe your ID to unlock it."))
return
if(!anchored)
to_chat(user, SPAN_WARNING("The device needs to be bolted to the ground first."))
return
else
for(var/obj/structure/machinery/shield_capacitor/cap in owned_capacitors) //Make sure the capacitors weren't blown up
if(!(cap in range(1, src)) || get_dir(cap, src) != cap.dir || !cap.anchored)
if(cap && cap.owned_gen == src)
cap.owned_gen = null
owned_capacitors -= cap
if(!owned_capacitors || owned_capacitors.len == 0) // Try to attach any valid adjacent capacitors
for(var/obj/structure/machinery/shield_capacitor/cap in range(1, src))
attach_capacitor(cap)
return ui_interact(user)
/obj/structure/machinery/shield_gen/proc/attach_capacitor(obj/structure/machinery/shield_capacitor/cap)
if(!cap || cap.owned_gen || !cap.anchored)
return FALSE
if(!(cap in range(1, src)) || get_dir(cap, src) != cap.dir)
return FALSE
if(!owned_capacitors)
owned_capacitors = list()
owned_capacitors += cap
cap.owned_gen = src
updateDialog()
return TRUE
/obj/structure/machinery/shield_gen/proc/detach_capacitor(obj/structure/machinery/shield_capacitor/cap)
if(!cap)
return FALSE
if(owned_capacitors && (cap in owned_capacitors))
owned_capacitors -= cap
if(cap.owned_gen == src)
cap.owned_gen = null
updateDialog()
return TRUE
/obj/structure/machinery/shield_gen/process()
if(active)
if(!anchored)
toggle()
if(stat & BROKEN)
toggle()
return PROCESS_KILL
if(istype(energy_field))
var/required_energy = energy_field.get_required_energy()
var/assumed_charge = assume_charge(required_energy)
energy_field.handle_strength(assumed_charge)
/**
* Called whenever the field needs to take charge from attached capacitors.
*/
/obj/structure/machinery/shield_gen/proc/assume_charge(required_energy)
if(!owned_capacitors || owned_capacitors.len == 0)
return 0
var/list/active_capacitors = list()
var/available_charge = 0
for(var/obj/structure/machinery/shield_capacitor/cap in owned_capacitors) //Loop through capacitors that can provide charge
if(!cap || !cap.active || cap.stored_charge <= 0)
continue
active_capacitors += cap
available_charge += cap.stored_charge
if(!active_capacitors.len || available_charge <= 0) //If we have no capacitors, or they're not charged stop
return 0
var/assumed_charge = min(available_charge, required_energy)
if(assumed_charge >= available_charge)
for(var/obj/structure/machinery/shield_capacitor/cap in active_capacitors)
cap.stored_charge = 0
return assumed_charge
var/remaining = assumed_charge
for(var/i = 1, i <= active_capacitors.len, i++)
var/obj/structure/machinery/shield_capacitor/cap = active_capacitors[i]
var/took = (i == active_capacitors.len) ? remaining : round(assumed_charge * (cap.stored_charge / available_charge))
took = min(took, cap.stored_charge, remaining)
if(took <= 0)
continue
cap.stored_charge -= took
remaining -= took
if(remaining <= 0)
break
return assumed_charge - remaining
/obj/structure/machinery/shield_gen/ex_act(var/severity)
if(active)
toggle()
return ..()
/obj/structure/machinery/shield_gen/proc/toggle()
if(!active)
if(!owned_capacitors || owned_capacitors.len == 0)
balloon_alert_to_viewers("no capacitor")
return
else
var/has_active = FALSE
for(var/obj/structure/machinery/shield_capacitor/cap in owned_capacitors)
if(cap && cap.active)
has_active = TRUE
break
if(!has_active)
balloon_alert_to_viewers("capacitors offline")
return
active = !active
update_icon()
if(active)
for(var/mob/M as anything in hearers(5,src))
to_chat(M, SPAN_NOTICE("[icon2html(src, M)] You hear heavy droning start up."))
energy_field.set_shielded_turfs(get_shielded_turfs())
else
for(var/mob/M as anything in hearers(5,src))
to_chat(M, SPAN_NOTICE("[icon2html(src, M)] You hear heavy droning fade out."))
energy_field.clear_field()
/obj/structure/machinery/shield_gen/update_icon()
if(stat & BROKEN)
icon_state = "broke"
else
if (active)
icon_state = "generator1"
else
icon_state = "generator0"
//grab the border tiles in a square around this machine
/obj/structure/machinery/shield_gen/proc/get_shielded_turfs()
var/turf/T = get_turf(src)
. = list()
if(field_radius > 0 && T)
var/connected_levels = list(T.z)
if(multiz)
for(var/turf/connected_z_turf as anything in (getzabove(T) + getzbelow(T)))
connected_levels += connected_z_turf.z
. += block(\
locate(T.x - field_radius, T.y + field_radius, min(connected_levels)),\
locate(T.x + field_radius - 1, T.y + field_radius, max(connected_levels))\
)
. += block(\
locate(T.x + field_radius, T.y - field_radius, min(connected_levels)),\
locate(T.x - field_radius + 1, T.y - field_radius, max(connected_levels))\
)
. += block(\
locate(T.x - field_radius, T.y + field_radius - 1, min(connected_levels)),\
locate(T.x - field_radius, T.y - field_radius, max(connected_levels))\
)
. += block(\
locate(T.x + field_radius, T.y + field_radius, min(connected_levels)),\
locate(T.x + field_radius, T.y - field_radius + 1, max(connected_levels))\
)
return . - get_turf(src)
/obj/structure/machinery/shield_gen/ui_interact(mob/user, var/datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "ShieldGenerator", "Shield Generator", 480, 400)
ui.open()
/obj/structure/machinery/shield_gen/ui_data(mob/user)
var/list/data = list()
data["owned_capacitor"] = length(owned_capacitors)
data["owned_capacitor_count"] = length(owned_capacitors)
data["active"] = active
data["time_since_fail"] = energy_field ? energy_field.time_since_fail : 0
data["multiz"] = multiz
data["field_radius"] = field_radius
data["min_field_radius"] = 1
data["max_field_radius"] = max_field_radius
data = energy_field.add_field_ui_data(data)
return data
/obj/structure/machinery/shield_gen/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
switch(action)
if("toggle")
toggle()
if("multiz")
multiz = !multiz
if ("size_set")
field_radius = between(1, text2num(params["size_set"]), max_field_radius)
if ("charge_set")
energy_field.strengthen_rate = (between(1, text2num(params["charge_set"]), (energy_field.max_strengthen_rate * 10)) / 10)
if ("field_set")
energy_field.target_field_strength = between(1, text2num(params["field_set"]), 10)
add_fingerprint(usr)
/obj/structure/machinery/shield_gen/proc/getzabove(var/turf/location)
var/connected = list()
var/turf/above = GET_TURF_ABOVE(location)
if(above)
connected += above
var/connected_levels = getzabove(above)
for(var/turf/z as anything in connected_levels)
connected += z
return connected
/obj/structure/machinery/shield_gen/proc/getzbelow(var/turf/location)
var/connected = list()
var/turf/below = GET_TURF_BELOW(location)
if(below)
connected += below
var/connected_levels = getzbelow(below)
for(var/turf/z as anything in connected_levels)
connected += z
return connected