Shell May Cry: A comprehensive rework of synthetics. (#20721)

https://www.youtube.com/watch?v=9mPvZ96pHJI

A pull request commissioned by the Synthetic Lore Team to
comprehensively rework synthetics (read: IPCs) and how they work in
Aurora. The objective is to make IPCs as unique as possible from humans,
upgrading the robotic feel and atmosphere, while also preserving a good
sense of balance in-game.

Key features:

- A comprehensive expansion of synthetic organs, all of which now
fulfill a purpose: hydraulics, cooling units, power systems, actuators,
diagnostics units.
- Customizable organs with benefits and drawbacks, such as with cooling
units and power systems.
- Unique ways to repair the organs and more involved steps.
- Unique damage mechanics - every organ has wiring and electronics which
affect its functioning, and they are defended by plating which provides
natural armour.
- Improved and immersive diagnostics.
- Unique features, benefits, and drawbacks for every IPC frame.
- A rework of the positronic brain, which can be either destroyed or
shut down, alongside effects caused by low integrity.
- A rework of how EMPs affect IPC organs.
- Non-binary damage states for each organ.

To-do:

- [x] Finish the unique features for each frame.
- [x] Look into if mechanical synthskin is possible.
- [x] Power system.
- [x] Posibrain mechanics.
- [ ] Passive cooling expansion.
- [x] EMP mechanics.
- [x] Repair mechanics.
- [x] Mob weight mechanics.
- [ ] Gurney for heavy mobs.
- [x] New augments.
- [ ] IPC tag scanning and flashing.

---------

Signed-off-by: Werner <1331699+Arrow768@users.noreply.github.com>
Co-authored-by: Matt Atlas <liermattia@gmail.com>
Co-authored-by: Geeves <22774890+Geevies@users.noreply.github.com>
Co-authored-by: Werner <1331699+Arrow768@users.noreply.github.com>
This commit is contained in:
Matt Atlas
2025-12-21 16:26:23 +00:00
committed by GitHub
co-authored by Matt Atlas Geeves Werner
parent 1898ad3417
commit e0aa218843
201 changed files with 5962 additions and 896 deletions
@@ -142,7 +142,7 @@
if(user.isSynthetic() && ishuman(user)) // Let's do something with it, if we're a robot.
var/mob/living/carbon/human/H = user
charge_to_give = charge_to_give - (core.max_energy - core.energy)
var/obj/item/organ/internal/cell/C = H.internal_organs_by_name[BP_CELL]
var/obj/item/organ/internal/machine/power_core/C = H.internal_organs_by_name[BP_CELL]
var/obj/item/cell/potato = C.get_cell()
potato.give(charge_to_give)
to_chat(user, SPAN_NOTICE("Redirected energy to internal microcell."))
+4 -4
View File
@@ -99,7 +99,7 @@
minimal_player_age = 7
outfit = /obj/outfit/job/warden
blacklisted_species = list(SPECIES_IPC_G1, SPECIES_IPC_G2, SPECIES_IPC_XION, SPECIES_IPC_XION_REMOTE, SPECIES_VAURCA_BULWARK, SPECIES_DIONA_COEUS, SPECIES_VAURCA_BREEDER)
blacklisted_species = list(SPECIES_IPC_XION_REMOTE, SPECIES_VAURCA_BULWARK, SPECIES_DIONA_COEUS, SPECIES_VAURCA_BREEDER)
/obj/outfit/job/warden
name = "Warden"
@@ -163,7 +163,7 @@
)
minimal_player_age = 3
outfit = /obj/outfit/job/forensics
blacklisted_species = list(SPECIES_IPC_G1, SPECIES_IPC_G2, SPECIES_IPC_XION, SPECIES_IPC_XION_REMOTE, SPECIES_VAURCA_BULWARK, SPECIES_VAURCA_BREEDER)
blacklisted_species = list(SPECIES_IPC_XION_REMOTE, SPECIES_VAURCA_BULWARK, SPECIES_VAURCA_BREEDER)
/obj/outfit/job/forensics
name = "Investigator"
@@ -222,7 +222,7 @@
minimal_player_age = 7
outfit = /obj/outfit/job/officer
blacklisted_species = list(SPECIES_IPC_G1, SPECIES_IPC_G2, SPECIES_IPC_XION, SPECIES_IPC_XION_REMOTE, SPECIES_VAURCA_BULWARK, SPECIES_DIONA_COEUS, SPECIES_VAURCA_BREEDER)
blacklisted_species = list(SPECIES_IPC_XION_REMOTE, SPECIES_VAURCA_BULWARK, SPECIES_DIONA_COEUS, SPECIES_VAURCA_BREEDER)
/obj/outfit/job/officer
name = "Security Officer"
@@ -289,7 +289,7 @@
SPECIES_SKRELL_AXIORI = 50
)
blacklisted_species = list(SPECIES_IPC_G1, SPECIES_IPC_G2, SPECIES_IPC_XION, SPECIES_IPC_XION_REMOTE, SPECIES_VAURCA_BULWARK, SPECIES_DIONA_COEUS, SPECIES_VAURCA_BREEDER)
blacklisted_species = list(SPECIES_IPC_XION_REMOTE, SPECIES_VAURCA_BULWARK, SPECIES_DIONA_COEUS, SPECIES_VAURCA_BREEDER)
/obj/outfit/job/intern_sec
name = "Security Cadet"
+42 -6
View File
@@ -26,6 +26,9 @@
///The connected surgery computer
var/obj/machinery/computer/operating/computer = null
/// If the patient must be lying on this surgery table for it to set the patient as occupant.
var/occupant_must_be_lying = TRUE
/obj/machinery/optable/mechanics_hints(mob/user, distance, is_adjacent)
. += ..()
. += "Click your target with Grab intent, then click on the table with an empty hand, to place them on it."
@@ -154,6 +157,7 @@
if((user.a_intent != I_HELP) && buckled)
user_unbuckle(user)
return
if(user != occupant_resolved && !use_check_and_message(user)) // Skip checks if you're doing it to yourself or turning it off, this is an anti-griefing mechanic more than anything.
user.visible_message(SPAN_WARNING("\The [user] begins switching [suppressing ? "off" : "on"] \the [src]'s neural suppressor."))
if(!do_after(user, 3 SECONDS, src, DO_UNIQUE))
@@ -201,10 +205,11 @@
//does not mean it's suppressed, just that it's occupying the table
var/mob/living/carbon/human/new_occupant = locate() in loc
if(istype(new_occupant))
if(new_occupant.lying)
//Set the occupant weakref and get his view
occupant = WEAKREF(new_occupant)
acquire_view(new_occupant)
if(occupant_must_be_lying && !new_occupant.lying)
return FALSE
//Set the occupant weakref and get his view
occupant = WEAKREF(new_occupant)
acquire_view(new_occupant)
//We have a patient, and it's not synthetic
else if(!occupant_resolved.isSynthetic())
@@ -257,11 +262,17 @@
add_fingerprint(patient)
add_fibers(patient)
patient.resting = TRUE
patient.forceMove(loc)
move_patient_to_table(patient, giver)
return TRUE
/**
* Actually moves a patient to the table.
*/
/obj/machinery/optable/proc/move_patient_to_table(mob/living/carbon/patient, mob/living/carbon/giver)
patient.resting = TRUE
patient.forceMove(loc)
/obj/machinery/optable/mouse_drop_receive(atom/dropped, mob/user, params)
//If the user is a ghost, stop.
if(isghost(user))
@@ -333,3 +344,28 @@
return TRUE
if(default_part_replacement(user, attacking_item))
return TRUE
/obj/machinery/optable/robotics
name = "machinery chair"
desc = "Some sort of hybrid between an operating table and a chair, typically used by machinists and roboticists to strap synthetics to while they work on them. \
It comes with an access cable for easy access to a synthetic's diagnostics unit."
icon_state = "machinist_or_table"
density = FALSE
occupant_must_be_lying = FALSE
can_buckle = list(/mob/living/carbon/human)
/obj/machinery/optable/robotics/mechanics_hints(mob/user, distance, is_adjacent)
. = ..()
. += list("Use a <b>non-help</b> intent to unbuckle.")
/obj/machinery/optable/robotics/refresh_icon_state()
return
/obj/machinery/optable/robotics/move_patient_to_table(mob/living/carbon/patient, mob/living/carbon/giver)
buckle(patient, giver)
visible_message(SPAN_NOTICE("[giver] buckles [patient] to \the [src]."))
/obj/machinery/optable/robotics/buckle(atom/movable/buckling_atom, mob/user)
. = ..()
if(.)
playsound(src, 'sound/effects/metal_close.ogg', 20)
+31 -1
View File
@@ -29,6 +29,9 @@
/// The zlevel that this computer is spawned on.
var/starting_z_level = null
/// The access cable inserted into this computer, if any.
var/obj/item/access_cable/inserted_cable
/obj/machinery/computer/Initialize()
. = ..()
overlay_layer = layer
@@ -36,6 +39,12 @@
power_change()
update_icon()
/obj/machinery/computer/Destroy()
if(inserted_cable)
inserted_cable.retract()
inserted_cable = null
return ..()
/obj/machinery/computer/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = TRUE)
if(!operable() || !is_station_level(z) || user.stat)
user.unset_machine()
@@ -47,7 +56,6 @@
if(prob(20/severity))
set_broken()
/obj/machinery/computer/ex_act(severity)
switch(severity)
if(1.0)
@@ -194,6 +202,15 @@
update_icon()
return TRUE
else
if(istype(attacking_item, /obj/item/access_cable))
var/obj/item/access_cable/access_cable = attacking_item
if(inserted_cable)
to_chat(user, SPAN_WARNING("There's already a cable in the universal port!"))
return
if(do_after(user, 1 SECONDS))
visible_message(SPAN_NOTICE("[user] slots \the [access_cable] into \the [src]."))
insert_cable(access_cable, user)
return ..()
/obj/machinery/computer/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
@@ -218,6 +235,19 @@
to_chat(user, SPAN_WARNING("\The [src]'s non-stick surface prevents you from attaching a sticker to it!"))
return FALSE
/obj/machinery/computer/insert_cable(obj/item/access_cable/cable, mob/user)
. = ..()
inserted_cable = cable
cable.create_cable(src)
/obj/machinery/computer/cable_interact(obj/item/access_cable/cable, mob/user)
. = ..()
attack_hand(user)
/obj/machinery/computer/remove_cable(obj/item/access_cable/cable)
..()
inserted_cable = null
/obj/machinery/computer/terminal
name = "terminal"
icon = 'icons/obj/machinery/modular_terminal.dmi'
+1 -1
View File
@@ -554,7 +554,7 @@
var/choice = alert(M, "Would you like to save your tag data?", "Tag Persistence", "Yes", "No")
if(choice == "Yes")
var/mob/living/carbon/human/H = M
var/obj/item/organ/internal/ipc_tag/tag = H.internal_organs_by_name[BP_IPCTAG]
var/obj/item/organ/internal/machine/ipc_tag/tag = H.internal_organs_by_name[BP_IPCTAG]
if(tag)
M.client.prefs.machine_ownership_status = tag.ownership_info
M.client.prefs.machine_serial_number = tag.serial_number
+17 -23
View File
@@ -297,7 +297,7 @@ Class Procs:
update_icon()
/obj/machinery/CanUseTopic(var/mob/user)
/obj/machinery/CanUseTopic(mob/user)
if(stat & BROKEN)
return STATUS_CLOSE
@@ -306,18 +306,18 @@ Class Procs:
return ..()
/obj/machinery/CouldUseTopic(var/mob/user)
/obj/machinery/CouldUseTopic(mob/user)
..()
if(clicksound && iscarbon(user))
playsound(src, clicksound, clickvol)
user.set_machine(src)
/obj/machinery/CouldNotUseTopic(var/mob/user)
/obj/machinery/CouldNotUseTopic(mob/user)
user.unset_machine()
////////////////////////////////////////////////////////////////////////////////////////////
/obj/machinery/attack_ai(mob/user as mob)
/obj/machinery/attack_ai(mob/user)
if(!ai_can_interact(user))
return
if(isrobot(user))
@@ -328,33 +328,27 @@ Class Procs:
else
return src.attack_hand(user)
/obj/machinery/attack_hand(mob/user as mob)
/obj/machinery/attack_hand(mob/user)
if(!operable(MAINT))
return 1
return TRUE
if(user.lying || user.stat)
return 1
if ( ! (istype(usr, /mob/living/carbon/human) || \
istype(usr, /mob/living/silicon)))
return TRUE
if (!istype(usr, /mob/living/carbon/human) && !istype(usr, /mob/living/silicon))
to_chat(usr, SPAN_WARNING("You don't have the dexterity to do this!"))
return 1
/*
//distance checks are made by atom/proc/DblClick
if ((get_dist(src, user) > 1 || !istype(src.loc, /turf)) && !istype(user, /mob/living/silicon))
return 1
*/
if (ishuman(user))
var/mob/living/carbon/human/H = user
if(H.getBrainLoss() >= 60)
visible_message(SPAN_WARNING("[H] stares cluelessly at [src] and drools."))
return 1
else if(prob(H.getBrainLoss()))
to_chat(user, SPAN_WARNING("You momentarily forget how to use [src]."))
return 1
return TRUE
src.add_fingerprint(user)
return ..()
/obj/machinery/attack_ranged(mob/user, params)
. = ..()
if(isipc(user))
var/mob/living/carbon/human/robot = user
var/obj/item/organ/internal/machine/wireless_access/wireless_access_point = robot.internal_organs_by_name[BP_WIRELESS_ACCESS]
if(wireless_access_point?.access_terminal(src))
attack_hand(user)
/obj/machinery/attackby(obj/item/attacking_item, mob/user)
if(obj_flags & OBJ_FLAG_SIGNALER)
if(issignaler(attacking_item))
+79 -55
View File
@@ -1,6 +1,6 @@
/obj/machinery/mecha_part_fabricator
name = "mechatronic fabricator"
desc = "A general purpose fabricator that can be used to fabricate robotic equipment."
name = "synthetic fabricator"
desc = "A general purpose fabricator that can be used to fabricate equipment for synthetics or exosuits."
icon = 'icons/obj/machinery/robotics_fabricator.dmi'
icon_state = "fab"
density = TRUE
@@ -15,6 +15,7 @@
/obj/item/stock_parts/micro_laser,
/obj/item/stock_parts/console_screen
)
manufacturer = "hephaestus"
/**
* A `/list` of enqueued `/datum/design` to be printed, processed in the queue
@@ -36,6 +37,9 @@
var/datum/research/files
/// The looping sound for production.
var/datum/looping_sound/synth_fab/fab_loop
var/list/categories = list()
var/category = null
var/sync_message = ""
@@ -57,11 +61,13 @@
. = ..()
files = new /datum/research(src) //Setup the research data holder.
fab_loop = new(src)
limb_manufacturer = GLOB.basic_robolimb.company
update_categories()
/obj/machinery/mecha_part_fabricator/update_icon()
ClearOverlays()
update_fab_audio()
if(panel_open)
AddOverlays("[icon_state]_panel")
if(!(stat & (NOPOWER|BROKEN)))
@@ -72,9 +78,19 @@
AddOverlays(emissive_appearance(icon, "[icon_state]_lights_working"))
AddOverlays("[icon_state]_lights_working")
/// Starts and stops the necessary audio.
/obj/machinery/mecha_part_fabricator/proc/update_fab_audio()
if(!fab_loop)
return
if(build_callback_timer)
fab_loop.start()
else
fab_loop.stop()
/obj/machinery/mecha_part_fabricator/dismantle()
for(var/f in materials)
eject_materials(f, -1)
eject_materials(f, materials[f])
//Stop the queue building if you're dismantling
deltimer(build_callback_timer)
@@ -106,27 +122,36 @@
do_hair_pull(user)
ui_interact(user)
/obj/machinery/mecha_part_fabricator/ui_interact(var/mob/user, var/ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
var/data[0]
/obj/machinery/mecha_part_fabricator/ui_interact(mob/user, datum/tgui/ui)
. = ..()
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "SyntheticFabricator", "Synthetic Fabricator", 900, 700)
ui.open()
/obj/machinery/mecha_part_fabricator/ui_data(mob/user)
var/list/data = list()
data["manufacturer"] = manufacturer
var/datum/design/current = queue.len ? queue[1] : null
if(current)
data["current"] = current.name
data["queue"] = get_queue_names()
data["buildable"] = get_build_options()
data["category"] = category
data["categories"] = categories
data["available_categories"] = list()
for(var/possible_category in categories)
data["available_categories"] += list(list("name" = possible_category))
if(GLOB.fabricator_robolimbs)
var/list/T = list()
for(var/A in GLOB.fabricator_robolimbs)
var/datum/robolimb/R = GLOB.fabricator_robolimbs[A]
T += list(list("id" = A, "company" = R.company))
T += list(list("name" = R.company, "type" = A))
data["manufacturers"] = T
data["manufacturer"] = limb_manufacturer
data["selected_manufacturer"] = limb_manufacturer
data["materials"] = get_materials()
data["maxres"] = res_max_amount
data["maximum_resource_amount"] = res_max_amount
data["sync"] = sync_message
//If we have something in the queue that we're trying to build, show the time left if the build is undergoing, otherwise null
@@ -136,42 +161,46 @@
data["timeleft"] = round(timeleft(build_callback_timer))
else
data["timeleft"] = null
return data
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
if(!ui)
ui = new(user, src, ui_key, "mechfab.tmpl", "Exosuit Fabricator UI", 800, 600)
ui.set_initial_data(data)
ui.open()
ui.set_auto_update(1)
/obj/machinery/mecha_part_fabricator/Topic(href, href_list)
if(..())
/obj/machinery/mecha_part_fabricator/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
if(href_list["build"])
var/path = text2path(href_list["build"])
add_to_queue(path)
switch(action)
if("build")
var/path = text2path(params["build"])
add_to_queue(path)
. = TRUE
if(href_list["remove"])
remove_from_queue(text2num(href_list["remove"]))
if("remove")
remove_from_queue(text2num(params["remove"]))
. = TRUE
if(href_list["category"])
if(href_list["category"] in categories)
category = href_list["category"]
if("category")
if(params["category"] in categories)
category = params["category"]
. = TRUE
if(href_list["manufacturer"])
if(href_list["manufacturer"] in GLOB.fabricator_robolimbs)
limb_manufacturer = href_list["manufacturer"]
if("manufacturer")
if(params["manufacturer"] in GLOB.fabricator_robolimbs)
limb_manufacturer = params["manufacturer"]
. = TRUE
if(href_list["eject"])
eject_materials(href_list["eject"], text2num(href_list["amount"]))
if("eject")
eject_materials(params["eject"], text2num(params["amount"]))
. = TRUE
if(href_list["sync"])
sync()
else
sync_message = ""
if("sync")
sync()
. = TRUE
else
sync_message = ""
return 1
if(.)
playsound(src, 'sound/machines/synthfab/synthfab_button.ogg', 50)
/obj/machinery/mecha_part_fabricator/attackby(obj/item/attacking_item, mob/user)
if(build_callback_timer)
@@ -195,7 +224,7 @@
return TRUE
var/sname = "[M.name]"
if(materials[M.material.name] + M.perunit <= res_max_amount)
if(materials[M.material.name] + SHEET_MATERIAL_AMOUNT <= res_max_amount)
if(M.amount >= 1)
var/count = 0
var/mutable_appearance/MA = mutable_appearance(icon, "material_insertion")
@@ -206,7 +235,7 @@
//now play the progress bar animation
flick_overlay_view(mutable_appearance(icon, "fab_progress"), 1 SECONDS)
while(materials[M.material.name] + M.perunit <= res_max_amount && M.amount >= 1)
while(materials[M.material.name] + SHEET_MATERIAL_AMOUNT <= res_max_amount && M.amount >= 1)
materials[M.material.name] += M.perunit
M.use(1)
count++
@@ -344,6 +373,7 @@
else
visible_message(SPAN_NOTICE("[icon2html(src, viewers(get_turf(src)))] \The [src] flashes: Insufficient materials to complete build: [D.name]."))
remove_from_queue(1)
update_icon()
@@ -383,17 +413,17 @@
/obj/machinery/mecha_part_fabricator/proc/get_queue_names()
. = list()
for(var/i = 2 to queue.len)
for(var/i = 1 to queue.len)
var/datum/design/D = queue[i]
. += D.name
. += list(list("name" = D.name, "time" = get_design_time(D), "index" = i))
/obj/machinery/mecha_part_fabricator/proc/get_build_options()
. = list()
for(var/path in files.known_designs)
var/datum/design/D = files.known_designs[path]
if(!D.build_path || !(D.build_type & MECHFAB) || !D.category)
if(!D.build_path || !(D.build_type & MECHFAB) || !D.category || (D.category != category))
continue
. += list(list("name" = D.name, "id" = D.type, "category" = D.category, "resourses" = get_design_resourses(D), "time" = get_design_time(D)))
. += list(list("name" = D.name, "desc" = D.desc, "type" = D.type, "category" = D.category, "resources" = get_design_resourses(D), "time" = get_design_time(D)))
/obj/machinery/mecha_part_fabricator/proc/get_design_resourses(var/datum/design/D)
var/list/F = list()
@@ -417,25 +447,19 @@
/obj/machinery/mecha_part_fabricator/proc/get_materials()
. = list()
for(var/T in materials)
. += list(list("mat" = capitalize(T), "amt" = materials[T]))
. += list(list("name" = capitalize(T), "amount" = materials[T]))
/obj/machinery/mecha_part_fabricator/proc/eject_materials(var/material, var/amount) // 0 amount = 0 means ejecting a full stack; -1 means eject everything
var/recursive = amount == -1 ? 1 : 0
/obj/machinery/mecha_part_fabricator/proc/eject_materials(var/material, var/amount)
if(!amount)
return
material = lowertext(material)
var/material/mattype = SSmaterials.get_material_by_name(material)
var/stack_type = mattype.stack_type
var/obj/item/stack/material/S = new stack_type(loc)
if(amount <= 0)
amount = S.max_amount
var/ejected = min(round(materials[material] / S.perunit), amount)
S.amount = min(ejected, amount)
if(S.amount <= 0)
qdel(S)
return
materials[material] -= ejected * S.perunit
if(recursive && materials[material] >= S.perunit)
eject_materials(material, -1)
var/real_amount = round(amount / SHEET_MATERIAL_AMOUNT)
var/obj/item/stack/material/S = new stack_type(loc, real_amount)
S.update_icon()
materials[material] -= amount
/obj/machinery/mecha_part_fabricator/proc/sync()
sync_message = "Error: no console found."
+23 -3
View File
@@ -117,6 +117,9 @@
return
var/obj/item/cell/target
// We use the reactor variable to see if this is an IPC that's charging.
var/obj/item/organ/internal/machine/reactor/reactor
if(isrobot(occupant))
var/mob/living/silicon/robot/R = occupant
@@ -132,9 +135,15 @@
if(ishuman(occupant))
var/mob/living/carbon/human/H = occupant
var/obj/item/organ/internal/cell/IC = H.internal_organs_by_name[BP_CELL]
if(IC)
var/obj/item/organ/internal/machine/power_core/IC = H.internal_organs_by_name[BP_CELL]
if(istype(IC))
target = IC.cell
// Different reactor types have different external recharge speeds.
reactor = H.internal_organs_by_name[BP_REACTOR]
if(!istype(reactor))
return
if((!target || target.percent() > 95) && istype(H.back, /obj/item/rig))
var/obj/item/rig/R = H.back
if(R.cell && !R.cell.fully_charged())
@@ -143,7 +152,11 @@
if(target && !target.fully_charged())
var/diff = min(target.maxcharge - target.charge, charging_power * CELLRATE * seconds_per_tick) // Capped by charging_power / tick
var/charge_used = cell.use(diff)
target.give(charge_used*charging_efficiency)
if(!reactor) // not an IPC
target.give(charge_used * charging_efficiency)
else
reactor.generate_power(charge_used * charging_efficiency * reactor.external_charge_multiplier)
if(isDrone(occupant))
var/mob/living/silicon/robot/drone/D = occupant
@@ -172,6 +185,13 @@
if(cell)
cell.emp_act(severity)
/obj/machinery/recharge_station/attack_hand(mob/user)
. = ..()
if(occupant && (user != occupant))
if(do_after(user, 1 SECOND, src))
user.visible_message(SPAN_NOTICE("[user] lowers the manual release lever on \the [src]."), SPAN_NOTICE("You lower the manual release lever on \the [src]."))
go_out()
/obj/machinery/recharge_station/attackby(obj/item/attacking_item, mob/user)
if(!occupant)
if(default_deconstruction_screwdriver(user, attacking_item))
@@ -180,7 +180,7 @@
if(!emagged)
playsound(src.loc, 'sound/effects/sparks4.ogg', 75, 1)
emagged = 1
to_chat(user, SPAN_NOTICE("You you disable the security protocols"))
to_chat(user, SPAN_NOTICE("You disable the security protocols."))
src.updateUsrDialog()
return 1
@@ -157,6 +157,6 @@
if(!emagged)
playsound(src.loc, 'sound/effects/sparks4.ogg', 75, 1)
emagged = 1
to_chat(user, SPAN_NOTICE("You you disable the security protocols"))
to_chat(user, SPAN_NOTICE("You disable the security protocols."))
src.updateUsrDialog()
return 1
@@ -234,6 +234,6 @@
if(!emagged)
playsound(src.loc, 'sound/effects/sparks4.ogg', 75, 1)
emagged = 1
to_chat(user, SPAN_NOTICE("You you disable the security protocols"))
to_chat(user, SPAN_NOTICE("You disable the security protocols."))
src.updateUsrDialog()
return 1
+1 -1
View File
@@ -420,7 +420,7 @@
make_announcement("pings, \"Resuscitation successful.\"", "notice")
playsound(get_turf(src), 'sound/machines/defib_success.ogg', 50, 0)
H.resuscitate()
var/obj/item/organ/internal/cell/potato = H.internal_organs_by_name[BP_CELL]
var/obj/item/organ/internal/machine/power_core/potato = H.internal_organs_by_name[BP_CELL]
if(istype(potato) && potato.cell)
var/obj/item/cell/C = potato.cell
C.give(chargecost)
@@ -23,7 +23,7 @@
var/on = 0 //is it turned on?
var/cover_open = 0 //is the cover open?
var/obj/item/cell/cell
var/max_cooling = 12 //in degrees per second - probably don't need to mess with heat capacity here
var/max_cooling = 24 //in degrees per second - probably don't need to mess with heat capacity here
var/charge_consumption = 8.3 //charge per second at max_cooling
var/thermostat = T20C
@@ -95,7 +95,7 @@
var/charge_usage = (temp_adj/max_cooling)*charge_consumption
H.bodytemperature -= temp_adj*efficiency
H.bodytemperature = max(T0C, H.bodytemperature - temp_adj*efficiency)
cell.use(charge_usage)
+22 -3
View File
@@ -83,6 +83,25 @@
var/obj/item/organ/external/S = H.get_organ(target_zone)
if (S && (S.status & ORGAN_ASSISTED))
var/datum/component/synthetic_endoskeleton/endoskeleton = H.GetComponent(/datum/component/synthetic_endoskeleton)
if(istype(endoskeleton) && endoskeleton.damage)
if(target_mob == user)
if(endoskeleton.damage >= endoskeleton.max_damage * 0.5)
to_chat(user, SPAN_WARNING("Your control over your limbs is too damaged to apply the nanopaste precisely!"))
return
application_time *= application_multiplier // It takes longer to apply nanopaste to yourself than to someone else.
if (application_in_progress == FALSE)
application_in_progress = TRUE
user.visible_message(SPAN_NOTICE("\The [user] begins to apply nanite past to the broken support systems of [user != target_mob ? " \the [target_mob]'s" : "\the [user]'s"] endoskeleton..."), \
SPAN_NOTICE("You begin to apply nanite paste to the broken support systems of [user != target_mob ? " \the [target_mob]'s" : "your"] [endoskeleton]..."))
if(do_mob(user, target_mob, application_time))
SEND_SIGNAL(target_mob, COMSIG_SYNTH_ENDOSKELETON_REPAIR, rand(15, 30))
user.visible_message(SPAN_NOTICE("\The [user] mends the broken links in [user != target_mob ? " \the [target_mob]'s" : "\the [user]'s"] endoskeleton with \the [src]."),\
SPAN_NOTICE("You successfully mend the broken links in[user == target_mob ? "your" : "[target_mob]'s"] endoskeleton with \the [src]."))
use(1)
application_in_progress = FALSE
if(S.get_damage())
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
@@ -144,7 +163,7 @@
return FALSE
if (isipc(M))
var/obj/item/organ/internal/surge/s = M.internal_organs_by_name["surge"]
var/obj/item/organ/internal/machine/surge/s = M.internal_organs_by_name[BP_SURGE_PROTECTOR]
if(isnull(s))
user.visible_message(
SPAN_NOTICE("[user] is trying to apply [src] to [(M == user) ? ("itself") : (M)]!"),
@@ -154,9 +173,9 @@
if (!do_mob(user, M, 2))
return FALSE
s = new /obj/item/organ/internal/surge()
s = new /obj/item/organ/internal/machine/surge()
M.internal_organs += s
M.internal_organs_by_name["surge"] = s
M.internal_organs_by_name[BP_SURGE_PROTECTOR] = s
user.visible_message(
SPAN_NOTICE("[user] applies some nanite paste to [(M == user) ? ("itself") : (M)]!"),
SPAN_NOTICE("You apply [src] to [(M == user) ? ("youself") : (M)].")
@@ -68,11 +68,11 @@
/obj/item/implanter/ipc_tag
name = "IPC tag implanter"
desc = "A special implanter used for implanting synthetics with a special tag."
var/obj/item/organ/internal/ipc_tag/ipc_tag
var/obj/item/organ/internal/machine/ipc_tag/ipc_tag
/obj/item/implanter/ipc_tag/Initialize()
. = ..()
ipc_tag = new /obj/item/organ/internal/ipc_tag(src)
ipc_tag = new /obj/item/organ/internal/machine/ipc_tag(src)
update_icon()
/obj/item/implanter/ipc_tag/update_icon()
@@ -121,7 +121,7 @@
ipc_tag = null
update_icon()
return
if(istype(attacking_item, /obj/item/organ/internal/ipc_tag))
if(istype(attacking_item, /obj/item/organ/internal/machine/ipc_tag))
if(ipc_tag)
to_chat(user, SPAN_WARNING("\The [src] already has an IPC tag loaded."))
return
@@ -40,7 +40,7 @@
to_chat(user, SPAN_WARNING("You analyze \the [target_mob], but find that they're not an IPC at all!"))
return
var/mob/living/carbon/human/IPC = target_mob
var/obj/item/organ/internal/ipc_tag/tag = IPC.internal_organs_by_name[BP_IPCTAG]
var/obj/item/organ/internal/machine/ipc_tag/tag = IPC.internal_organs_by_name[BP_IPCTAG]
if(isnull(tag) || !tag)
to_chat(user, SPAN_WARNING("Error: Serial Identification Missing."))
return
@@ -11,16 +11,17 @@
throw_speed = 3
throw_range = 5
w_class = WEIGHT_CLASS_NORMAL
matter = list(DEFAULT_WALL_MATERIAL = 700, MATERIAL_GLASS = 50)
recyclable = TRUE
/// Note %age converted to actual charge in New()
var/charge = 0
var/maxcharge = 2500
/// Maximum cell charge.
var/maxcharge = 1000
/// True if rigged to explode
var/rigged = FALSE
/// If not 100% reliable, it will build up faults.
var/minor_fault = 0
matter = list(DEFAULT_WALL_MATERIAL = 700, MATERIAL_GLASS = 50)
recyclable = TRUE
/// Percentage of maxcharge to recharge per minute, though it will trickle charge every process tick (every ~2 seconds), leave null for no self-recharge
var/self_charge_percentage
@@ -126,10 +127,10 @@
matter = list(DEFAULT_WALL_MATERIAL = 700, MATERIAL_GLASS = 80)
/obj/item/cell/infinite/check_charge()
return 1
return 1
/obj/item/cell/infinite/use()
return 1
return 1
/obj/item/cell/potato
name = "potato battery"
@@ -139,8 +140,6 @@
icon_state = "potato_cell" //"potato_battery"
charge = 100
maxcharge = 300
minor_fault = 1
/obj/item/cell/slime
name = "charged slime core"
+24
View File
@@ -355,6 +355,30 @@
/obj/proc/do_signaler()
return
/**
* Called when an access cable - from an IPC or from a roboticist tool - is inserted into an object.
*/
/obj/proc/insert_cable(obj/item/access_cable/cable, mob/user)
user.drop_from_inventory(cable)
cable.forceMove(src)
cable.target = src
/**
* Called when an access cable is removed from something, forcibly or not.
* Override as needed to clear variables up.
*/
/obj/proc/remove_cable(obj/item/access_cable/cable)
SHOULD_CALL_PARENT(TRUE)
cable.target = null
/**
* Called when an access cable - from an IPC or from a roboticist tool - is used on an object to interact with it.
* For example, brings up diagnostics for an access port if it's hooked into one.
*/
/obj/proc/cable_interact(obj/item/access_cable/cable, mob/user)
return
/*#############################################
PERSISTENT
#############################################*/
@@ -73,6 +73,7 @@
new /obj/item/storage/belt/utility(src)
new /obj/item/storage/belt/utility/alt(src)
new /obj/item/storage/toolbox/mechanical(src)
new /obj/item/modular_computer/laptop/preset/supply/robotics(src)
// Miner
/obj/structure/closet/secure_closet/miner
@@ -49,6 +49,7 @@
new /obj/item/device/taperecorder(src)
new /obj/item/sampler(src)
new /obj/item/storage/box/shells/tranquilizer(src)
new /obj/item/modular_computer/laptop/preset/supply/robotics(src)
/// Used for when we want to populate the contents of this locker ourself. Good for away sites
/obj/structure/closet/secure_closet/RD/empty