From 983159c8c59855d3d93d0c3c3c76e7130d7641f3 Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Mon, 8 Mar 2021 22:15:13 +1100 Subject: [PATCH 1/3] Wide-scale MMI and brainmob refactor. Working commit to get brainmob PR to compile. Reworking vital organ tracking. Amending some strings and comments for brainmob PR. Post-rebase update. Attempting to make brain key transfers consistent. --- code/__defines/mobs.dm | 1 - code/_helpers/mobs.dm | 12 + code/_macros.dm | 2 +- code/_onclick/rig.dm | 3 - .../subsystems/initialization/robots.dm | 19 +- code/datums/repositories/follow.dm | 2 +- code/datums/trading/traders/goods.dm | 74 ++-- code/game/gamemodes/cult/cult_structures.dm | 3 +- code/game/machinery/computer/ai_core.dm | 32 +- code/game/machinery/cryopod.dm | 16 +- code/game/objects/items/robot/robot_frame.dm | 31 +- .../brain_interface/_brain_interface.dm | 150 ++++++++ .../brain_interface/interface_radio.dm | 61 ++++ .../spacesuits/rig/modules/computer.dm | 4 +- code/modules/emotes/definitions/_mob.dm | 16 +- code/modules/emotes/emote_mob.dm | 4 +- .../designs_machine_intelligence.dm | 10 +- code/modules/ghosttrap/trap.dm | 50 +-- .../subtypes/manipulation.dm | 2 +- code/modules/mob/death.dm | 2 +- code/modules/mob/living/brain/brain.dm | 119 +++++++ code/modules/mob/living/brain/death.dm | 17 + code/modules/mob/living/brain/say.dm | 16 + code/modules/mob/living/carbon/brain/MMI.dm | 189 ---------- code/modules/mob/living/carbon/brain/brain.dm | 38 -- code/modules/mob/living/carbon/brain/death.dm | 14 - code/modules/mob/living/carbon/brain/life.dm | 170 --------- code/modules/mob/living/carbon/brain/login.dm | 3 - code/modules/mob/living/carbon/brain/robot.dm | 15 - code/modules/mob/living/carbon/brain/say.dm | 38 -- code/modules/mob/living/carbon/human/death.dm | 3 + code/modules/mob/living/carbon/human/human.dm | 2 +- code/modules/mob/living/silicon/ai/ai.dm | 9 +- .../mob/living/silicon/robot/component.dm | 2 +- .../modules/mob/living/silicon/robot/death.dm | 4 +- .../mob/living/silicon/robot/drone/drone.dm | 2 +- .../living/silicon/robot/drone/drone_items.dm | 3 +- code/modules/mob/living/silicon/robot/life.dm | 2 +- .../modules/mob/living/silicon/robot/robot.dm | 56 +-- code/modules/mob/mob_transformation_simple.dm | 2 +- code/modules/mob/skills/skill.dm | 2 +- code/modules/mob/transform_procs.dm | 7 +- code/modules/organs/external/_external.dm | 13 +- code/modules/organs/internal/_internal.dm | 69 ++++ code/modules/organs/internal/brain.dm | 79 +++-- .../modules/organs/internal/brain_computer.dm | 92 +++++ code/modules/organs/internal/cell.dm | 96 +++++ code/modules/organs/internal/heart.dm | 6 +- code/modules/organs/internal/posibrain.dm | 333 ------------------ code/modules/organs/organ.dm | 10 +- code/modules/projectiles/projectile/change.dm | 10 +- code/modules/surgery/limb_reattach.dm | 10 +- code/modules/surgery/organs_internal.dm | 11 +- code/modules/surgery/robotics.dm | 120 +------ icons/obj/assemblies.dmi | Bin 24039 -> 16447 bytes icons/obj/items/brain_interface_organic.dmi | Bin 0 -> 3568 bytes icons/obj/items/brain_interface_robotic.dmi | Bin 0 -> 2535 bytes maps/exodus/exodus-2.dmm | 8 +- maps/exodus/jobs/synthetics.dm | 2 +- mods/content/shackles/_shackles.dme | 1 - mods/content/shackles/laws_pref.dm | 11 +- mods/content/shackles/posibrain.dm | 16 - mods/content/xenobiology/overrides.dm | 6 + .../bayliens/unathi/organs/organs_internal.dm | 2 +- mods/species/utility_frames/species.dm | 6 +- .../utility_frames/species_bodytypes.dm | 8 +- mods/species/vox/organs_vox.dm | 8 +- nebula.dme | 15 +- 68 files changed, 915 insertions(+), 1224 deletions(-) create mode 100644 code/modules/brain_interface/_brain_interface.dm create mode 100644 code/modules/brain_interface/interface_radio.dm create mode 100644 code/modules/mob/living/brain/brain.dm create mode 100644 code/modules/mob/living/brain/death.dm create mode 100644 code/modules/mob/living/brain/say.dm delete mode 100644 code/modules/mob/living/carbon/brain/MMI.dm delete mode 100644 code/modules/mob/living/carbon/brain/brain.dm delete mode 100644 code/modules/mob/living/carbon/brain/death.dm delete mode 100644 code/modules/mob/living/carbon/brain/life.dm delete mode 100644 code/modules/mob/living/carbon/brain/login.dm delete mode 100644 code/modules/mob/living/carbon/brain/robot.dm delete mode 100644 code/modules/mob/living/carbon/brain/say.dm create mode 100644 code/modules/organs/internal/brain_computer.dm create mode 100644 code/modules/organs/internal/cell.dm delete mode 100644 code/modules/organs/internal/posibrain.dm create mode 100644 icons/obj/items/brain_interface_organic.dmi create mode 100644 icons/obj/items/brain_interface_robotic.dmi delete mode 100644 mods/content/shackles/posibrain.dm diff --git a/code/__defines/mobs.dm b/code/__defines/mobs.dm index 739b95e1ccc..3c168317126 100644 --- a/code/__defines/mobs.dm +++ b/code/__defines/mobs.dm @@ -182,7 +182,6 @@ #define BP_ACETONE "acetone reactor" // Robo Organs. -#define BP_POSIBRAIN "posibrain" #define BP_VOICE "vocal synthesiser" #define BP_STACK "stack" #define BP_OPTICS "optics" diff --git a/code/_helpers/mobs.dm b/code/_helpers/mobs.dm index 9d714318131..fe8247f9e9b 100644 --- a/code/_helpers/mobs.dm +++ b/code/_helpers/mobs.dm @@ -311,3 +311,15 @@ var/global/list/bodypart_coverage_cache = list() /proc/get_sorted_mob_list() . = sortTim(SSmobs.mob_list.Copy(), /proc/cmp_name_asc) . = sortTim(., /proc/cmp_mob_sortvalue_asc) + +/proc/transfer_key_from_mob_to_mob(var/mob/from_mob, var/mob/to_mob) + if(!from_mob || !from_mob.key || !to_mob) + return FALSE + var/initial_key = from_mob.key + if(to_mob.key) + to_mob.ghostize() + if(from_mob.mind) + from_mob.mind.transfer_to(to_mob) + if(initial_key && to_mob.key != initial_key) + to_mob.key = initial_key + return to_mob.key == initial_key diff --git a/code/_macros.dm b/code/_macros.dm index bcbd7efd3f8..0ac36021a18 100644 --- a/code/_macros.dm +++ b/code/_macros.dm @@ -24,7 +24,7 @@ #define isatom(A) isloc(A) -#define isbrain(A) istype(A, /mob/living/carbon/brain) +#define isbrain(A) istype(A, /mob/living/brain) #define iscarbon(A) istype(A, /mob/living/carbon) diff --git a/code/_onclick/rig.dm b/code/_onclick/rig.dm index 7813f9d5d3c..8dd8799e561 100644 --- a/code/_onclick/rig.dm +++ b/code/_onclick/rig.dm @@ -28,9 +28,6 @@ /mob/living/carbon/human/can_use_rig() return 1 -/mob/living/carbon/brain/can_use_rig() - return istype(loc, /obj/item/mmi) - /mob/living/silicon/ai/can_use_rig() return carded diff --git a/code/controllers/subsystems/initialization/robots.dm b/code/controllers/subsystems/initialization/robots.dm index b71138afa75..75c70a7fe4c 100644 --- a/code/controllers/subsystems/initialization/robots.dm +++ b/code/controllers/subsystems/initialization/robots.dm @@ -10,18 +10,15 @@ SUBSYSTEM_DEF(robots) var/list/robot_alt_titles = list() var/list/mob_types_by_title = list( - "robot, flying" = /mob/living/silicon/robot/flying, - "drone, flying" = /mob/living/silicon/robot/flying, - "cyborg, flying" = /mob/living/silicon/robot/flying + "cyborg, flying" = /mob/living/silicon/robot/flying, + "robot, flying" = /mob/living/silicon/robot/flying ) var/list/mmi_types_by_title = list( - "cyborg" = /obj/item/mmi, - "robot" = /obj/item/organ/internal/posibrain, - "drone" = /obj/item/mmi/digital/robot, - "cyborg, flying" = /obj/item/mmi, - "robot, flying" = /obj/item/organ/internal/posibrain, - "drone, flying" = /obj/item/mmi/digital/robot + "cyborg" = /obj/item/organ/internal/brain_interface, + "robot" = /obj/item/organ/internal/brain/robotic, + "cyborg, flying" = /obj/item/organ/internal/brain_interface, + "robot, flying" = /obj/item/organ/internal/brain/robotic ) /datum/controller/subsystem/robots/Initialize() @@ -60,8 +57,8 @@ SUBSYSTEM_DEF(robots) if(modules[include_override]) .[include_override] = modules[include_override] -/datum/controller/subsystem/robots/proc/get_mmi_type_by_title(var/check_title) - . = mmi_types_by_title[lowertext(trim(check_title))] || /obj/item/mmi +/datum/controller/subsystem/robots/proc/get_brain_type_by_title(var/check_title) + . = mmi_types_by_title[lowertext(trim(check_title))] || /obj/item/organ/internal/brain/robotic /datum/controller/subsystem/robots/proc/get_mob_type_by_title(var/check_title) . = mob_types_by_title[lowertext(trim(check_title))] || /mob/living/silicon/robot \ No newline at end of file diff --git a/code/datums/repositories/follow.dm b/code/datums/repositories/follow.dm index 4e36a96450d..11393225a41 100644 --- a/code/datums/repositories/follow.dm +++ b/code/datums/repositories/follow.dm @@ -175,7 +175,7 @@ var/global/repository/follow/follow_repository = new() /datum/follow_holder/brain sort_order = 3 - followed_type = /mob/living/carbon/brain + followed_type = /mob/living/brain suffix = "Brain" /datum/follow_holder/alien diff --git a/code/datums/trading/traders/goods.dm b/code/datums/trading/traders/goods.dm index 76c338c2815..e4fd109c083 100644 --- a/code/datums/trading/traders/goods.dm +++ b/code/datums/trading/traders/goods.dm @@ -218,43 +218,43 @@ Sells devices, odds and ends, and medical stuff "McGillicuddy's" ) possible_trading_items = list( - /obj/item/flashlight = TRADER_ALL, - /obj/item/kit/paint = TRADER_SUBTYPES_ONLY, - /obj/item/aicard = TRADER_THIS_TYPE, - /obj/item/binoculars = TRADER_THIS_TYPE, - /obj/item/cable_painter = TRADER_THIS_TYPE, - /obj/item/flash = TRADER_THIS_TYPE, - /obj/item/paint_sprayer = TRADER_THIS_TYPE, - /obj/item/multitool = TRADER_THIS_TYPE, - /obj/item/lightreplacer = TRADER_THIS_TYPE, - /obj/item/megaphone = TRADER_THIS_TYPE, - /obj/item/paicard = TRADER_THIS_TYPE, - /obj/item/scanner/health = TRADER_THIS_TYPE, - /obj/item/scanner/breath = TRADER_THIS_TYPE, - /obj/item/scanner/gas = TRADER_ALL, - /obj/item/scanner/spectrometer = TRADER_ALL, - /obj/item/scanner/reagent = TRADER_ALL, - /obj/item/scanner/xenobio = TRADER_THIS_TYPE, - /obj/item/suit_cooling_unit = TRADER_THIS_TYPE, - /obj/item/t_scanner = TRADER_THIS_TYPE, - /obj/item/taperecorder = TRADER_THIS_TYPE, - /obj/item/batterer = TRADER_THIS_TYPE, - /obj/item/synthesized_instrument/violin = TRADER_THIS_TYPE, - /obj/item/hailer = TRADER_THIS_TYPE, - /obj/item/uv_light = TRADER_THIS_TYPE, - /obj/item/mmi = TRADER_ALL, - /obj/item/robotanalyzer = TRADER_THIS_TYPE, - /obj/item/chems/toner_cartridge = TRADER_THIS_TYPE, - /obj/item/camera_film = TRADER_THIS_TYPE, - /obj/item/camera = TRADER_THIS_TYPE, - /obj/item/destTagger = TRADER_THIS_TYPE, - /obj/item/gps = TRADER_THIS_TYPE, - /obj/item/measuring_tape = TRADER_THIS_TYPE, - /obj/item/ano_scanner = TRADER_THIS_TYPE, - /obj/item/core_sampler = TRADER_THIS_TYPE, - /obj/item/depth_scanner = TRADER_THIS_TYPE, - /obj/item/pinpointer/radio = TRADER_THIS_TYPE, - /obj/item/stack/medical/advanced = TRADER_BLACKLIST + /obj/item/flashlight = TRADER_ALL, + /obj/item/kit/paint = TRADER_SUBTYPES_ONLY, + /obj/item/aicard = TRADER_THIS_TYPE, + /obj/item/binoculars = TRADER_THIS_TYPE, + /obj/item/cable_painter = TRADER_THIS_TYPE, + /obj/item/flash = TRADER_THIS_TYPE, + /obj/item/paint_sprayer = TRADER_THIS_TYPE, + /obj/item/multitool = TRADER_THIS_TYPE, + /obj/item/lightreplacer = TRADER_THIS_TYPE, + /obj/item/megaphone = TRADER_THIS_TYPE, + /obj/item/paicard = TRADER_THIS_TYPE, + /obj/item/scanner/health = TRADER_THIS_TYPE, + /obj/item/scanner/breath = TRADER_THIS_TYPE, + /obj/item/scanner/gas = TRADER_ALL, + /obj/item/scanner/spectrometer = TRADER_ALL, + /obj/item/scanner/reagent = TRADER_ALL, + /obj/item/scanner/xenobio = TRADER_THIS_TYPE, + /obj/item/suit_cooling_unit = TRADER_THIS_TYPE, + /obj/item/t_scanner = TRADER_THIS_TYPE, + /obj/item/taperecorder = TRADER_THIS_TYPE, + /obj/item/batterer = TRADER_THIS_TYPE, + /obj/item/synthesized_instrument/violin = TRADER_THIS_TYPE, + /obj/item/hailer = TRADER_THIS_TYPE, + /obj/item/uv_light = TRADER_THIS_TYPE, + /obj/item/organ/internal/brain_interface = TRADER_SUBTYPES_ONLY, + /obj/item/robotanalyzer = TRADER_THIS_TYPE, + /obj/item/chems/toner_cartridge = TRADER_THIS_TYPE, + /obj/item/camera_film = TRADER_THIS_TYPE, + /obj/item/camera = TRADER_THIS_TYPE, + /obj/item/destTagger = TRADER_THIS_TYPE, + /obj/item/gps = TRADER_THIS_TYPE, + /obj/item/measuring_tape = TRADER_THIS_TYPE, + /obj/item/ano_scanner = TRADER_THIS_TYPE, + /obj/item/core_sampler = TRADER_THIS_TYPE, + /obj/item/depth_scanner = TRADER_THIS_TYPE, + /obj/item/pinpointer/radio = TRADER_THIS_TYPE, + /obj/item/stack/medical/advanced = TRADER_BLACKLIST ) speech = list( TRADER_HAIL_GENERIC = "Hello, hello! Bits and bobs and everything in between, I hope you find what you're looking for!", diff --git a/code/game/gamemodes/cult/cult_structures.dm b/code/game/gamemodes/cult/cult_structures.dm index 1b8a9ee79aa..769fbc05658 100644 --- a/code/game/gamemodes/cult/cult_structures.dm +++ b/code/game/gamemodes/cult/cult_structures.dm @@ -150,8 +150,7 @@ if(isrobot(M)) var/mob/living/silicon/robot/Robot = M - if(Robot.mmi) - qdel(Robot.mmi) + QDEL_NULL(Robot.central_processor) else for(var/obj/item/W in M) M.drop_from_inventory(W) diff --git a/code/game/machinery/computer/ai_core.dm b/code/game/machinery/computer/ai_core.dm index 3fa89eef553..efc4136a173 100644 --- a/code/game/machinery/computer/ai_core.dm +++ b/code/game/machinery/computer/ai_core.dm @@ -11,7 +11,7 @@ var/global/list/empty_playable_ai_cores = list() var/datum/ai_laws/laws var/obj/item/stock_parts/circuitboard/circuit - var/obj/item/mmi/brain + var/obj/item/organ/internal/brain var/authorized var/circuit_secured = FALSE @@ -139,30 +139,24 @@ var/global/list/empty_playable_ai_cores = list() if(circuit && circuit_secured) - if((istype(P, /obj/item/mmi) || istype(P, /obj/item/organ/internal/posibrain)) && wired && circuit && circuit_secured) - var/mob/living/carbon/brain/B - if(istype(P, /obj/item/mmi)) - var/obj/item/mmi/M = P - B = M.brainmob - else - var/obj/item/organ/internal/posibrain/PB = P - B = PB.brainmob - if(!B) - to_chat(user, SPAN_WARNING("Sticking an empty [P] into the frame would sort of defeat the purpose.")) + if(istype(P, /obj/item/organ/internal) && wired && circuit && circuit_secured) + var/obj/item/organ/internal/M = P + var/mob/living/brainmob = M.get_brainmob() + if(!brainmob) + to_chat(user, SPAN_WARNING("Sticking a mindless [P] into the frame would be pointless.")) return - if(B.stat == DEAD) + if(brainmob.stat == DEAD) to_chat(user, SPAN_WARNING("Sticking a dead [P] into the frame would sort of defeat the purpose.")) return - if(jobban_isbanned(B, "AI")) + if(jobban_isbanned(brainmob, "AI")) to_chat(user, SPAN_WARNING("This [P] does not seem to fit.")) return if(!user.try_unequip(P, src)) - return - if(B.mind) - clear_antag_roles(B.mind, 1) - brain = P - to_chat(usr, "Added [P].") - update_icon() + if(brainmob.mind) + clear_antag_roles(brainmob.mind, 1) + brain = P + to_chat(usr, "You connect \the [P] to the frame and slide it into the casing.") + update_icon() return TRUE if(istype(P, /obj/item/stack/material)) diff --git a/code/game/machinery/cryopod.dm b/code/game/machinery/cryopod.dm index b23a5df9c9f..858a29659b9 100644 --- a/code/game/machinery/cryopod.dm +++ b/code/game/machinery/cryopod.dm @@ -314,14 +314,14 @@ // Also make sure there is a valid control computer /obj/machinery/cryopod/robot/despawn_occupant() var/mob/living/silicon/robot/R = occupant - if(!istype(R)) return ..() - - qdel(R.mmi) - for(var/obj/item/I in R.module) // the tools the borg has; metal, glass, guns etc - for(var/obj/item/O in I.get_contained_external_atoms()) // the things inside the tools, if anything; mainly for janiborg trash bags - O.forceMove(R) - qdel(I) - qdel(R.module) + if(istype(R)) + R.clear_brain() + if(R.module) + for(var/obj/item/I in R.module) // the tools the borg has; metal, glass, guns etc + for(var/obj/item/O in I.get_contained_external_atoms()) // the things inside the tools, if anything; mainly for janiborg trash bags + O.forceMove(R) + qdel(I) + qdel(R.module) . = ..() diff --git a/code/game/objects/items/robot/robot_frame.dm b/code/game/objects/items/robot/robot_frame.dm index a13106e420c..b47813774c0 100644 --- a/code/game/objects/items/robot/robot_frame.dm +++ b/code/game/objects/items/robot/robot_frame.dm @@ -60,8 +60,8 @@ parts[part.bp_tag] = part update_icon() - // Install an MMI/brain. - else if(istype(W, /obj/item/mmi) || istype(W, /obj/item/organ/internal/posibrain)) + // Install a brain. + else if(istype(W, /obj/item/organ/internal/brain_interface)) if(!isturf(loc)) to_chat(user, SPAN_WARNING("You can't put \the [W] in without the frame being on the ground.")) @@ -71,31 +71,25 @@ to_chat(user, SPAN_WARNING("The frame is not ready for the central processor to be installed.")) return - var/mob/living/carbon/brain/B - if(istype(W, /obj/item/mmi)) - var/obj/item/mmi/M = W - B = M.brainmob - else - var/obj/item/organ/internal/posibrain/P = W - B = P.brainmob - - if(!B) + var/obj/item/organ/internal/brain_interface/M = W + var/mob/living/brainmob = M?.get_brainmob() + if(!brainmob) to_chat(user, SPAN_WARNING("Sticking an empty [W.name] into the frame would sort of defeat the purpose.")) return - if(jobban_isbanned(B, ASSIGNMENT_ROBOT)) + if(jobban_isbanned(brainmob, ASSIGNMENT_ROBOT)) to_chat(user, SPAN_WARNING("\The [W] does not seem to fit.")) return - if(B.stat == DEAD) + if(brainmob.stat == DEAD) to_chat(user, SPAN_WARNING("Sticking a dead [W.name] into the frame would sort of defeat the purpose.")) return var/ghost_can_reenter = 0 - if(B.mind) - if(!B.key) + if(brainmob.mind) + if(!brainmob.key) for(var/mob/observer/ghost/G in global.player_list) - if(G.can_reenter_corpse && G.mind == B.mind) + if(G.can_reenter_corpse && G.mind == brainmob.mind) ghost_can_reenter = 1 break else @@ -112,11 +106,12 @@ if(!O) return - O.mmi = W + O.central_processor = W O.set_invisibility(INVISIBILITY_NONE) O.custom_name = created_name O.updatename("Default") - B.mind.transfer_to(O) + + brainmob.mind.transfer_to(O) if(O.mind && O.mind.assigned_role) O.job = O.mind.assigned_role else diff --git a/code/modules/brain_interface/_brain_interface.dm b/code/modules/brain_interface/_brain_interface.dm new file mode 100644 index 00000000000..406ea961d40 --- /dev/null +++ b/code/modules/brain_interface/_brain_interface.dm @@ -0,0 +1,150 @@ +// Many values copied from brains. Not inheriting to avoid redundant brainmob creation. +/obj/item/organ/internal/brain_interface + name = "neural interface" + desc = "A complex life support shell that interfaces between a brain and an electronic device." + organ_tag = BP_BRAIN + parent_organ = BP_HEAD + origin_tech = "{'biotech':3}" + icon = 'icons/obj/items/brain_interface_organic.dmi' + icon_state = ICON_STATE_WORLD + req_access = list(access_robotics) + material = /decl/material/solid/metal/steel + matter = list(/decl/material/solid/glass = MATTER_AMOUNT_REINFORCEMENT) + w_class = ITEM_SIZE_SMALL + throwforce = 1 + throw_speed = 3 + throw_range = 5 + attack_verb = list("attacked", "slapped", "whacked") + relative_size = 85 + damage_reduction = 0 + scale_max_damage_to_species_health = FALSE + transfer_brainmob_with_organ = TRUE + var/locked = FALSE + var/obj/item/organ/internal/brain/holding_brain = /obj/item/organ/internal/brain + +/obj/item/organ/internal/brain_interface/empty + holding_brain = null + +/obj/item/organ/internal/brain_interface/Initialize() + set_bodytype(/decl/bodytype/prosthetic/basic_human) + if(ispath(holding_brain)) + holding_brain = new holding_brain(src) + if(get_radio()) + verbs |= /obj/item/organ/internal/brain_interface/proc/toggle_radio_listening + verbs |= /obj/item/organ/internal/brain_interface/proc/toggle_radio_broadcasting + . = ..() + update_icon() + +/obj/item/organ/internal/brain_interface/get_brainmob(var/create_if_missing = FALSE) + return holding_brain?.get_brainmob(create_if_missing) + +/obj/item/organ/internal/brain_interface/on_update_icon() + icon_state = get_world_inventory_state() + if(holding_brain) + var/mob/living/brainmob = get_brainmob() + if(!brainmob || brainmob.stat == DEAD) + icon_state = "[icon_state]-dead" + else + icon_state = "[icon_state]-full" + +/obj/item/organ/internal/brain_interface/examine(mob/user, distance) + . = ..() + if(distance <= 1) + var/mob/living/brain/brainmob = get_brainmob() + if(istype(brainmob)) + if(brainmob.emp_damage) + to_chat(user, SPAN_WARNING("The neural interface socket is damaged.")) + else + to_chat(user, SPAN_NOTICE("It is undamaged.")) + +/obj/item/organ/internal/brain_interface/attackby(var/obj/item/O, var/mob/user) + + if(istype(O, /obj/item/stack/nanopaste)) + var/mob/living/brain/brainmob = get_brainmob() + if(!istype(brainmob) || !brainmob.emp_damage) + to_chat(user, SPAN_WARNING("\The [src] has no damage to repair.")) + return TRUE + var/obj/item/stack/nanopaste/pasta = O + pasta.use(1) + to_chat(user, SPAN_NOTICE("You repair some of the damage to \the [src]'s electronics with the nanopaste.")) + brainmob.emp_damage = max(brainmob.emp_damage - rand(5,10), 0) + return TRUE + + if(istype(O, /obj/item/organ/internal/brain)) + + if(holding_brain) + to_chat(user, SPAN_WARNING("\The [src] already has a brain in it.")) + return TRUE + + var/obj/item/organ/internal/brain/inserting_brain = O + if(BP_IS_PROSTHETIC(inserting_brain)) + to_chat(user, SPAN_WARNING("You don't need to put a robotic brain into an interface.")) + return TRUE + + if(inserting_brain.damage >= inserting_brain.max_damage) + to_chat(user, SPAN_WARNING("That brain is well and truly dead.")) + return TRUE + + if(!inserting_brain.get_brainmob() || !inserting_brain.can_use_brain_interface) + to_chat(user, SPAN_WARNING("\The [inserting_brain] is completely useless.")) + return TRUE + + if(user.try_unequip(O, src)) + user.visible_message(SPAN_NOTICE("\The [user] sticks \the [inserting_brain] into \the [src].")) + SetName("[initial(name)] (\the [inserting_brain])") + holding_brain = inserting_brain + update_icon() + locked = TRUE + SSstatistics.add_field("cyborg_mmis_filled",1) + return TRUE + + if(istype(O,/obj/item/card/id) || istype(O,/obj/item/modular_computer)) + if(allowed(user)) + locked = !locked + to_chat(user, SPAN_NOTICE("You [locked ? "lock" : "unlock"] \the [src].")) + else + to_chat(user, SPAN_WARNING("Access denied.")) + return TRUE + + if(holding_brain) + return holding_brain.attackby(O, user) + + . = ..() + +/obj/item/organ/internal/brain_interface/relaymove(var/mob/user, var/direction) + if(user.incapacitated()) + return + var/obj/item/rig/rig = src.get_rig() + if(rig) + rig.forced_move(direction, user) + +/obj/item/organ/internal/brain_interface/Destroy() + STOP_PROCESSING(SSprocessing, src) + if(isrobot(loc)) + var/mob/living/silicon/robot/borg = loc + if(borg.central_processor == src) + borg.central_processor = null + if(holding_brain) + if(!QDELETED(holding_brain)) + qdel(holding_brain) + holding_brain = null + for(var/obj/item/thing in contents) + qdel(thing) + . = ..() + +/obj/item/organ/internal/brain_interface/attack_self(mob/user) + + if(locked) + to_chat(user, SPAN_WARNING("You upend \the [src], but the case is locked shut.")) + return TRUE + + if(!holding_brain) + to_chat(user, SPAN_WARNING("You upend \the [src], but there's nothing in it.")) + return TRUE + + to_chat(user, SPAN_NOTICE("You upend \the [src], spilling \the [holding_brain] onto \the [get_turf(src)].")) + + holding_brain.dropInto(user.loc) + holding_brain = null + update_icon() + SetName(initial(name)) diff --git a/code/modules/brain_interface/interface_radio.dm b/code/modules/brain_interface/interface_radio.dm new file mode 100644 index 00000000000..3ba4ffae135 --- /dev/null +++ b/code/modules/brain_interface/interface_radio.dm @@ -0,0 +1,61 @@ +/obj/item/organ/internal/brain_interface/radio_enabled + name = "radio-enabled neural interface" + desc = "A complex life support shell that interfaces between a brain and an electronic device. This one comes with a built-in radio." + origin_tech = "{'biotech':4}" + var/VAR_PRIVATE/weakref/_radio + +/obj/item/organ/internal/brain_interface/radio_enabled/empty + holding_brain = null + +/obj/item/organ/internal/brain_interface/radio_enabled/get_radio() + var/obj/item/radio/radio_instance = _radio?.resolve() + if(radio_instance && (!istype(radio_instance) || QDELETED(radio_instance) || radio_instance.loc != src)) + radio_instance = null + _radio = null + return radio_instance?.get_radio() + +/obj/item/organ/internal/brain_interface/radio_enabled/Initialize() + _radio = weakref(new /obj/item/radio(src)) + . = ..() + +/obj/item/organ/internal/brain_interface/radio_enabled/Destroy() + var/obj/item/radio/radio_instance = get_radio() + if(radio_instance) + qdel(radio_instance) + _radio = null + return ..() + +/obj/item/organ/internal/brain_interface/proc/toggle_radio_broadcasting() + set name = "Toggle Broadcasting" + set desc = "Toggle broadcasting channel on or off." + set category = "Brain Interface" + set src in view(1) + set popup_menu = 0 + + if(usr.incapacitated()) + to_chat(usr, SPAN_WARNING("You must be alive and conscious to interact with \the [src].")) + return + + var/obj/item/radio/radio_instance = get_radio() + if(istype(radio_instance)) + radio_instance.broadcasting = !radio_instance.broadcasting + to_chat(usr, SPAN_NOTICE("You adjust the radio on \the [src]. It is [radio_instance.broadcasting ? "now broadcasting" : "no longer broadcasting"].")) + else + verbs -= /obj/item/organ/internal/brain_interface/proc/toggle_radio_broadcasting + +/obj/item/organ/internal/brain_interface/proc/toggle_radio_listening() + set name = "Toggle Listening" + set desc = "Toggle listening channel on or off." + set category = "Brain Interface" + set src in view(1) + + set popup_menu = 0 + if(usr.incapacitated()) + to_chat(usr, SPAN_WARNING("You must be alive and conscious to interact with \the [src].")) + return + var/obj/item/radio/radio_instance = get_radio() + if(radio_instance) + radio_instance.listening = !radio_instance.listening + to_chat(usr, SPAN_NOTICE("You adjust the radio on \the [src]. It is [radio_instance.listening ? "now receiving broadcasts" : "no longer receiving broadcasts"].")) + else + verbs -= /obj/item/organ/internal/brain_interface/proc/toggle_radio_listening diff --git a/code/modules/clothing/spacesuits/rig/modules/computer.dm b/code/modules/clothing/spacesuits/rig/modules/computer.dm index edf3646c03a..d92a4c0e239 100644 --- a/code/modules/clothing/spacesuits/rig/modules/computer.dm +++ b/code/modules/clothing/spacesuits/rig/modules/computer.dm @@ -57,7 +57,7 @@ origin_tech = @'{"programming":6,"materials":5,"engineering":6}' var/mob/integrated_ai // Direct reference to the actual mob held in the suit. - var/obj/item/ai_card // Reference to the MMI, posibrain, inteliCard or pAI card previously holding the AI. + var/obj/item/ai_card // Reference to the object previously holding the AI. var/obj/item/ai_verbs/verb_holder /mob @@ -136,7 +136,7 @@ return 1 // Okay, it wasn't a terminal being touched, check for all the simple insertions. - if(input_device.type in list(/obj/item/paicard, /obj/item/mmi, /obj/item/organ/internal/posibrain)) + if(input_device.type in list(/obj/item/paicard, /obj/item/organ/internal/brain_interface)) if(integrated_ai) integrated_ai.attackby(input_device,user) // If the transfer was successful, we can clear out our vars. diff --git a/code/modules/emotes/definitions/_mob.dm b/code/modules/emotes/definitions/_mob.dm index 90598baaa78..c6fd0855eeb 100644 --- a/code/modules/emotes/definitions/_mob.dm +++ b/code/modules/emotes/definitions/_mob.dm @@ -42,19 +42,7 @@ /decl/emote/audible/choke, /decl/emote/audible/moan, /decl/emote/audible/gnarl - ) - -/mob/living/carbon/brain - default_emotes = list( - /decl/emote/audible/alarm, - /decl/emote/audible/alert, - /decl/emote/audible/notice, - /decl/emote/audible/whistle, - /decl/emote/audible/synth, - /decl/emote/audible/boop, - /decl/emote/visible/blink, - /decl/emote/visible/flash - ) + ) /mob/living/carbon/human default_emotes = list( @@ -168,4 +156,4 @@ /decl/emote/audible/synth/deny, /decl/emote/audible/synth/security, /decl/emote/audible/synth/security/halt - ) + ) diff --git a/code/modules/emotes/emote_mob.dm b/code/modules/emotes/emote_mob.dm index 24f6c9dfdb9..7713886e073 100644 --- a/code/modules/emotes/emote_mob.dm +++ b/code/modules/emotes/emote_mob.dm @@ -10,8 +10,8 @@ /mob/living/check_mob_can_emote(var/emote_type) return ..() && !(HAS_STATUS(src, STAT_SILENCE) && emote_type == AUDIBLE_MESSAGE) -/mob/living/carbon/brain/check_mob_can_emote(var/emote_type) - return ..() && (istype(container, /obj/item/mmi) || istype(loc, /obj/item/organ/internal/posibrain)) +/mob/living/brain/check_mob_can_emote(var/emote_type) + return ..() && istype(get_container(), /obj/item/organ/internal/brain_interface) /mob/proc/emote(var/act, var/m_type, var/message) set waitfor = FALSE diff --git a/code/modules/fabrication/designs/protolathe/designs_machine_intelligence.dm b/code/modules/fabrication/designs/protolathe/designs_machine_intelligence.dm index e2a471367ec..3b52762f8f9 100644 --- a/code/modules/fabrication/designs/protolathe/designs_machine_intelligence.dm +++ b/code/modules/fabrication/designs/protolathe/designs_machine_intelligence.dm @@ -1,15 +1,15 @@ /datum/fabricator_recipe/protolathe/brains category = "Machine Intelligence" - path = /obj/item/mmi + path = /obj/item/organ/internal/brain_interface/empty /datum/fabricator_recipe/protolathe/brains/get_product_name() . = "intelligence storage ([..()])" +/datum/fabricator_recipe/protolathe/brains/robotic + path = /obj/item/organ/internal/brain/robotic + /datum/fabricator_recipe/protolathe/brains/mmi_radio - path = /obj/item/mmi/radio_enabled - -/datum/fabricator_recipe/protolathe/brains/posibrain - path = /obj/item/organ/internal/posibrain + path = /obj/item/organ/internal/brain_interface/radio_enabled/empty /datum/fabricator_recipe/protolathe/brains/paicard path = /obj/item/paicard diff --git a/code/modules/ghosttrap/trap.dm b/code/modules/ghosttrap/trap.dm index 4d361a43886..73f2aade08a 100644 --- a/code/modules/ghosttrap/trap.dm +++ b/code/modules/ghosttrap/trap.dm @@ -1,5 +1,4 @@ -// This system is used to grab a ghost from observers with the required preferences -// and lack of bans set. See posibrain.dm for an example of how they are called/used. +// This system is used to grab a ghost from observers with the required preferences and lack of bans set. /decl/ghosttrap var/name var/minutes_since_death = 0 // If non-zero the ghost must have been dead for this many minutes to be allowed to spawn @@ -95,33 +94,42 @@ target.SetName(target.real_name) /*********************************** -* Positronic brains. * +* Computer intelligence cores. * ***********************************/ -/decl/ghosttrap/positronic_brain - name = "positronic brain" +/decl/ghosttrap/machine_intelligence + name = "machine intelligence" ban_checks = list("AI",ASSIGNMENT_ROBOT) - pref_check = "ghost_posibrain" - ghost_trap_message = "They are occupying a positronic brain now." + pref_check = "ghost_machine_intelligence" + ghost_trap_message = "They are occupying a computer intelligence core now." -/decl/ghosttrap/positronic_brain/forced(var/mob/user) - var/obj/item/organ/internal/posibrain/brain = new(get_turf(user)) - if(!brain.brainmob) - brain.init() - request_player(brain.brainmob, "Someone is requesting a personality for a positronic brain.", 60 SECONDS) +/decl/ghosttrap/machine_intelligence/transfer_personality(mob/candidate, mob/target) + if(assess_candidate(candidate)) -/decl/ghosttrap/positronic_brain/welcome_candidate(var/mob/target) - to_chat(target, "You are a positronic brain, brought into existence on [station_name()].") + var/obj/item/organ/internal/brain/robotic/brain = target.loc?.loc + if(!istype(brain)) + return FALSE + + brain.transfer_key_to_brainmob(candidate, update_brainmob = FALSE) + brain.searching = FALSE + brain.update_icon() + announce_ghost_joinleave(candidate, 0, "[ghost_trap_message]") + + var/mob/living/brainmob = brain.get_brainmob(create_if_missing = TRUE) + if(brainmob) + welcome_candidate(brainmob) + return TRUE + +/decl/ghosttrap/machine_intelligence/forced(var/mob/user) + var/obj/item/organ/internal/brain/robotic/brain = new(get_turf(user)) + request_player(brain.get_brainmob(create_if_missing = TRUE), "Someone is requesting a player for a machine intelligence.", 60 SECONDS) + +/decl/ghosttrap/machine_intelligence/welcome_candidate(var/mob/target) + to_chat(target, "You are a machine intelligence, brought into existence on [station_name()].") to_chat(target, "As a synthetic intelligence, you answer to all crewmembers, as well as the AI.") to_chat(target, "Remember, the purpose of your existence is to serve the crew and the [station_name()]. Above all else, do no harm.") to_chat(target, "Use say [target.get_language_prefix()]b to speak to other artificial intelligences.") var/turf/T = get_turf(target) - var/obj/item/organ/internal/posibrain/P = target.loc - T.visible_message("\The [P] chimes quietly.") - if(!istype(P)) //wat - return - P.searching = 0 - P.SetName("positronic brain ([P.brainmob.name])") - P.update_icon() + T.visible_message(SPAN_NOTICE("\The [target] beeps loudly.")) /*********************************** * Walking mushrooms and such. * diff --git a/code/modules/integrated_electronics/subtypes/manipulation.dm b/code/modules/integrated_electronics/subtypes/manipulation.dm index d3e1fff3922..869bb426379 100644 --- a/code/modules/integrated_electronics/subtypes/manipulation.dm +++ b/code/modules/integrated_electronics/subtypes/manipulation.dm @@ -654,7 +654,7 @@ /obj/item/integrated_circuit/manipulation/ai/attackby(var/obj/item/I, var/mob/user) - if(is_type_in_list(I, list(/obj/item/aicard, /obj/item/paicard, /obj/item/mmi))) + if(is_type_in_list(I, list(/obj/item/aicard, /obj/item/paicard, /obj/item/organ/internal/brain_interface))) load_ai(user, I) else return ..() diff --git a/code/modules/mob/death.dm b/code/modules/mob/death.dm index e3d6ca61dcb..a8945326700 100644 --- a/code/modules/mob/death.dm +++ b/code/modules/mob/death.dm @@ -26,7 +26,7 @@ //This is the proc for turning a mob into ash. Mostly a copy of gib code (above). //Originally created for wizard disintegrate. I've removed the virus code since it's irrelevant here. -//Dusting robots does not eject the MMI, so it's a bit more powerful than gib() /N +//Dusting robots does not eject the brain, so it's a bit more powerful than gib() /N /mob/proc/dust(anim="dust-m",remains=/obj/effect/decal/cleanable/ash) death(1) var/atom/movable/overlay/animation = null diff --git a/code/modules/mob/living/brain/brain.dm b/code/modules/mob/living/brain/brain.dm new file mode 100644 index 00000000000..1debc374281 --- /dev/null +++ b/code/modules/mob/living/brain/brain.dm @@ -0,0 +1,119 @@ +/mob/living/brain + name = "brain" + icon = 'icons/obj/surgery.dmi' + icon_state = "brain1" + default_emotes = list( + /decl/emote/audible/alarm, + /decl/emote/audible/alert, + /decl/emote/audible/notice, + /decl/emote/audible/whistle, + /decl/emote/audible/synth, + /decl/emote/audible/boop, + /decl/emote/visible/blink, + /decl/emote/visible/flash + ) + + // Used for EMP damage when inside an interface or robobrain. + var/emp_damage = 0 + var/last_emp_message = 0 + var/static/max_emp_damage = 30 + var/static/list/emp_reboot_strings = list( + SPAN_NOTICE("System reboot nearly complete."), + SPAN_NOTICE("Primary systems are now online."), + SPAN_DANGER("Major electrical distruption detected: System rebooting.") + ) + +/mob/living/brain/handle_regular_status_updates() + . = ..() + if(health <= 0 && stat != DEAD) + death() + if(emp_damage || stat == DEAD || !is_in_interface()) + SET_STATUS_MAX(src, STAT_SILENCE, 2) + +/mob/living/brain/death() + var/obj/item/organ/holder = loc + . = ..() + if(stat == DEAD && istype(holder)) + holder.die() + +/mob/living/brain/is_deaf() + return emp_damage || stat == DEAD || !is_in_interface() + +/mob/living/brain/is_blind() + return emp_damage || stat == DEAD || !is_in_interface() + +/mob/living/brain/Logout() + . = ..() + var/obj/item/organ/internal/container = get_container() + if(istype(container)) + container.queue_icon_update() + +/mob/living/brain/proc/get_container() + . = loc?.loc + +/mob/living/brain/Login() + . = ..() + var/obj/item/organ/internal/container = get_container() + if(istype(container)) + var/obj/item/organ/internal/brain_interface/interface = container + if(istype(interface)) + interface.locked = TRUE + container.update_icon() + +/mob/living/brain/proc/is_in_interface() + var/container = get_container() + return istype(container, /obj/item/organ/internal/brain_interface) || istype(container, /obj/item/organ/internal/brain/robotic) + +/mob/living/brain/can_emote() + return is_in_interface() && ..() + +/mob/living/brain/can_use_rig() + return is_in_interface() + +/mob/living/brain/Destroy() + ghostize() + . = ..() + +/mob/living/brain/say_understands(var/other) + . = ishuman(other) || (is_in_interface() && issilicon(other)) || ..() + +/mob/living/brain/UpdateLyingBuckledAndVerbStatus() + return + +/mob/living/brain/isSynthetic() + return istype(get_container(), /obj/item/organ/internal/brain/robotic) + +/mob/living/brain/binarycheck() + return isSynthetic() + +/mob/living/brain/check_has_mouth() + return FALSE + +/mob/living/brain/emp_act(severity) + if(!isSynthetic()) + return + switch(severity) + if(1) + emp_damage += rand(20,30) + if(2) + emp_damage += rand(10,20) + if(3) + emp_damage += rand(0,10) + emp_damage = clamp(emp_damage, 0, max_emp_damage) + +/mob/living/brain/Life() + . = ..() + if(stat == DEAD || !isSynthetic()) + emp_damage = 0 + return + if(!emp_damage) + return + emp_damage -= 1 + var/msg_threshold = clamp(CEILING(emp_damage / (max_emp_damage / length(emp_reboot_strings))), 1, length(emp_reboot_strings)) + if(last_emp_message != msg_threshold) + last_emp_message = msg_threshold + to_chat(src, emp_reboot_strings[msg_threshold]) + if(emp_damage <= 0) + last_emp_message = 0 + emp_damage = 0 + to_chat(src, SPAN_NOTICE("All systems restored.")) diff --git a/code/modules/mob/living/brain/death.dm b/code/modules/mob/living/brain/death.dm new file mode 100644 index 00000000000..0a70b0d7683 --- /dev/null +++ b/code/modules/mob/living/brain/death.dm @@ -0,0 +1,17 @@ +/mob/living/brain/death(gibbed) + var/death_message = "no message" + var/obj/item/organ/internal/brain_interface/container = get_container() + if(!gibbed && istype(container)) + death_message = "beeps shrilly as \the [container] flatlines!" + . = ..(gibbed, death_message) + if(istype(container)) + container.update_icon() + +/mob/living/brain/gib() + var/obj/item/organ/internal/brain_interface/container = get_container() + var/obj/item/organ/internal/brain/sponge = loc + . = ..(null, 1) + if(container && !QDELETED(container)) + qdel(container) + if(istype(sponge) && !QDELETED(sponge)) + qdel(sponge) diff --git a/code/modules/mob/living/brain/say.dm b/code/modules/mob/living/brain/say.dm new file mode 100644 index 00000000000..4a59e86f064 --- /dev/null +++ b/code/modules/mob/living/brain/say.dm @@ -0,0 +1,16 @@ +/mob/living/brain/say(var/message, var/decl/language/speaking, var/verb = "says", var/alt_name = "", whispering) + if(GET_STATUS(src, STAT_SILENCE) || !is_in_interface()) + return + if(prob(emp_damage*4)) + if(prob(10)) + return + message = Gibberish(message, (emp_damage*6)) + . = ..(message, speaking, verb, alt_name, whispering) + var/obj/item/radio/radio = get_radio() + if(radio) + radio.hear_talk(src, sanitize(message), verb, speaking) + +/mob/living/brain/get_radio() + var/obj/item/organ/internal/brain_interface/container = get_container() + if(istype(container)) + return container.get_radio() diff --git a/code/modules/mob/living/carbon/brain/MMI.dm b/code/modules/mob/living/carbon/brain/MMI.dm deleted file mode 100644 index a8eca098a30..00000000000 --- a/code/modules/mob/living/carbon/brain/MMI.dm +++ /dev/null @@ -1,189 +0,0 @@ -/obj/item/mmi/digital/Initialize() - brainmob = new(src) - brainmob.set_stat(CONSCIOUS) - brainmob.add_language(/decl/language/binary) - brainmob.add_language(/decl/language/machine) - brainmob.container = src - brainmob.set_status(STAT_SILENCE, 0) - PickName() - . = ..() - -/obj/item/mmi/digital/proc/PickName() - return - -/obj/item/mmi/digital/attackby() - return - -/obj/item/mmi/digital/attack_self() - return - -/obj/item/mmi - name = "\improper Man-Machine Interface" - desc = "A complex life support shell that interfaces between a brain and electronic devices." - icon = 'icons/obj/assemblies.dmi' - icon_state = "mmi_empty" - w_class = ITEM_SIZE_NORMAL - origin_tech = @'{"biotech":3}' - material = /decl/material/solid/metal/steel - matter = list(/decl/material/solid/glass = MATTER_AMOUNT_REINFORCEMENT) - req_access = list(access_robotics) - - //Revised. Brainmob is now contained directly within object of transfer. MMI in this case. - - var/locked = 0 - var/mob/living/carbon/brain/brainmob = null//The current occupant. - var/obj/item/organ/internal/brain/brainobj = null //The current brain organ. - -/obj/item/mmi/attackby(var/obj/item/O, var/mob/user) - if(istype(O,/obj/item/organ/internal/brain) && !brainmob) //Time to stick a brain in it --NEO - - var/obj/item/organ/internal/brain/B = O - if(B.damage >= B.max_damage) - to_chat(user, "That brain is well and truly dead.") - return - else if(!B.brainmob || !B.can_use_mmi) - to_chat(user, "This brain is completely useless to you.") - return - if(!user.try_unequip(O, src)) - return - user.visible_message("\The [user] sticks \a [O] into \the [src].") - - brainmob = B.brainmob - B.brainmob = null - brainmob.forceMove(src) - brainmob.container = src - brainmob.set_stat(CONSCIOUS) - brainmob.switch_from_dead_to_living_mob_list() //Update dem lists - - brainobj = O - - SetName("[initial(name)]: ([brainmob.real_name])") - update_icon() - - locked = 1 - - SSstatistics.add_field("cyborg_mmis_filled",1) - - return - - if((istype(O,/obj/item/card/id)||istype(O,/obj/item/modular_computer)) && brainmob) - if(allowed(user)) - locked = !locked - to_chat(user, "You [locked ? "lock" : "unlock"] the brain holder.") - else - to_chat(user, "Access denied.") - return - if(brainmob) - O.attack(brainmob, user)//Oh noooeeeee - return - ..() - - //TODO: ORGAN REMOVAL UPDATE. Make the brain remain in the MMI so it doesn't lose organ data. -/obj/item/mmi/attack_self(mob/user) - if(!brainmob) - to_chat(user, "You upend the MMI, but there's nothing in it.") - else if(locked) - to_chat(user, "You upend the MMI, but the brain is clamped into place.") - else - to_chat(user, "You upend the MMI, spilling the brain onto the floor.") - var/obj/item/organ/internal/brain/brain - if (brainobj) //Pull brain organ out of MMI. - brainobj.forceMove(user.loc) - brain = brainobj - brainobj = null - else //Or make a new one if empty. - brain = new(user.loc) - brainmob.container = null//Reset brainmob mmi var. - brainmob.forceMove(brain)//Throw mob into brain. - brainmob.remove_from_living_mob_list() //Get outta here - brain.brainmob = brainmob//Set the brain to use the brainmob - brainmob = null//Set mmi brainmob var to null - - update_icon() - SetName(initial(name)) - -/obj/item/mmi/proc/transfer_identity(var/mob/living/carbon/human/H)//Same deal as the regular brain proc. Used for human-->robot people. - brainmob = new(src) - brainmob.SetName(H.real_name) - brainmob.real_name = H.real_name - brainmob.dna = H.dna - brainmob.container = src - brainmob.timeofhostdeath = H.timeofdeath - brainmob.set_stat(CONSCIOUS) - - SetName("[initial(name)]: [brainmob.real_name]") - update_icon() - locked = 1 - -/obj/item/mmi/preserve_in_cryopod(obj/machinery/cryopod/pod) - return brainmob && brainmob.client && brainmob.key - -/obj/item/mmi/relaymove(var/mob/user, var/direction) - if(user.incapacitated(INCAPACITATION_KNOCKOUT)) - return - var/obj/item/rig/rig = get_rig() - if(rig) - rig.forced_move(direction, user) - -/obj/item/mmi/Destroy() - if(isrobot(loc)) - var/mob/living/silicon/robot/borg = loc - borg.mmi = null - QDEL_NULL(brainmob) - return ..() - -/obj/item/mmi/radio_enabled - name = "radio-enabled man-machine interface" - desc = "The Warrior's bland acronym, MMI, obscures the true horror of this monstrosity. This one comes with a built-in radio." - origin_tech = @'{"biotech":4}' - material = /decl/material/solid/metal/steel - matter = list(/decl/material/solid/glass = MATTER_AMOUNT_REINFORCEMENT) - var/obj/item/radio/radio = null//Let's give it a radio. - -/obj/item/mmi/radio_enabled/Initialize() - . = ..() - radio = new(src)//Spawns a radio inside the MMI. - radio.broadcasting = 1//So it's broadcasting from the start. - -/obj/item/mmi/radio_enabled/verb/Toggle_Broadcasting() //Allows the brain to toggle the radio functions. - set name = "Toggle Broadcasting" - set desc = "Toggle broadcasting channel on or off." - set category = "MMI" - set src = usr.loc//In user location, or in MMI in this case. - set popup_menu = 0//Will not appear when right clicking. - - if(brainmob.stat)//Only the brainmob will trigger these so no further check is necessary. - to_chat(brainmob, "Can't do that while incapacitated or dead.") - - radio.broadcasting = radio.broadcasting==1 ? 0 : 1 - to_chat(brainmob, "Radio is [radio.broadcasting==1 ? "now" : "no longer"] broadcasting.") - -/obj/item/mmi/radio_enabled/verb/Toggle_Listening() - set name = "Toggle Listening" - set desc = "Toggle listening channel on or off." - set category = "MMI" - set src = usr.loc - set popup_menu = 0 - - if(brainmob.stat) - to_chat(brainmob, "Can't do that while incapacitated or dead.") - - radio.listening = radio.listening==1 ? 0 : 1 - to_chat(brainmob, "Radio is [radio.listening==1 ? "now" : "no longer"] receiving broadcast.") - -/obj/item/mmi/emp_act(severity) - if(!brainmob) - return - else - switch(severity) - if(1) - brainmob.emp_damage += rand(20,30) - if(2) - brainmob.emp_damage += rand(10,20) - if(3) - brainmob.emp_damage += rand(0,10) - ..() - -/obj/item/mmi/on_update_icon() - . = ..() - icon_state = brainmob ? "mmi_full" : "mmi_empty" diff --git a/code/modules/mob/living/carbon/brain/brain.dm b/code/modules/mob/living/carbon/brain/brain.dm deleted file mode 100644 index 3eac062963b..00000000000 --- a/code/modules/mob/living/carbon/brain/brain.dm +++ /dev/null @@ -1,38 +0,0 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32 - -/mob/living/carbon/brain - var/obj/item/container = null - var/timeofhostdeath = 0 - var/emp_damage = 0//Handles a type of MMI damage - var/alert = null - icon = 'icons/obj/surgery.dmi' - icon_state = "brain1" - mob_sort_value = 7 - -/mob/living/carbon/brain/can_emote() - return stat == CONSCIOUS && (istype(container, /obj/item/mmi) || istype(loc, /obj/item/organ/internal/posibrain)) - -/mob/living/carbon/brain/Initialize() - create_reagents(1000) - . = ..() - -/mob/living/carbon/brain/Destroy() - if(key) //If there is a mob connected to this thing. Have to check key twice to avoid false death reporting. - ghostize() //Ghostize checks for key so nothing else is necessary. - . = ..() - -/mob/living/carbon/brain/say_understands(mob/speaker, decl/language/speaking) - return (issilicon(speaker) && (istype(container, /obj/item/mmi) || istype(loc, /obj/item/organ/internal/posibrain))) || ishuman(speaker) || ..() - -/mob/living/carbon/brain/UpdateLyingBuckledAndVerbStatus() - return - -/mob/living/carbon/brain/isSynthetic() - return istype(container, /obj/item/mmi/digital) || istype(loc, /obj/item/organ/internal/posibrain) - -/mob/living/carbon/brain/binarycheck() - return isSynthetic() - -/mob/living/carbon/brain/check_has_mouth() - return FALSE - diff --git a/code/modules/mob/living/carbon/brain/death.dm b/code/modules/mob/living/carbon/brain/death.dm deleted file mode 100644 index 5f728287cec..00000000000 --- a/code/modules/mob/living/carbon/brain/death.dm +++ /dev/null @@ -1,14 +0,0 @@ -/mob/living/carbon/brain/death(gibbed) - if(!gibbed && istype(container, /obj/item/mmi)) //If not gibbed but in a container. - container.icon_state = "mmi_dead" - return ..(gibbed,"beeps shrilly as the MMI flatlines!") - else - return ..(gibbed,"no message") - -/mob/living/carbon/brain/gib(anim="gibbed-m",do_gibs) - if(istype(container, /obj/item/mmi)) - qdel(container)//Gets rid of the MMI if there is one - if(loc) - if(istype(loc,/obj/item/organ/internal/brain)) - qdel(loc)//Gets rid of the brain item - ..(null,1) \ No newline at end of file diff --git a/code/modules/mob/living/carbon/brain/life.dm b/code/modules/mob/living/carbon/brain/life.dm deleted file mode 100644 index 692e2ce7fcf..00000000000 --- a/code/modules/mob/living/carbon/brain/life.dm +++ /dev/null @@ -1,170 +0,0 @@ -/mob/living/carbon/brain/need_breathe() - return FALSE - -/mob/living/carbon/brain/should_breathe() - return FALSE - -/mob/living/carbon/brain/handle_mutations_and_radiation() - ..() - if (radiation) - if (radiation > 100) - radiation = 100 - if(!container)//If it's not in an MMI - to_chat(src, "You feel weak.") - else//Fluff-wise, since the brain can't detect anything itself, the MMI handles thing like that - to_chat(src, "STATUS: CRITICAL AMOUNTS OF RADIATION DETECTED.") - switch(radiation) - if(1 to 49) - radiation-- - if(prob(25)) - adjustToxLoss(1) - - if(50 to 74) - radiation -= 2 - adjustToxLoss(1) - if(prob(5)) - radiation -= 5 - if(!container) - to_chat(src, "You feel weak.") - else - to_chat(src, "STATUS: DANGEROUS LEVELS OF RADIATION DETECTED.") - - if(75 to 100) - radiation -= 3 - adjustToxLoss(3) - -/mob/living/carbon/brain/handle_environment(datum/gas_mixture/environment) - ..() - if(!environment) - return - var/environment_heat_capacity = environment.heat_capacity() - if(isspaceturf(get_turf(src))) - var/turf/heat_turf = get_turf(src) - environment_heat_capacity = heat_turf.heat_capacity - if((environment.temperature > (T0C + 50)) || (environment.temperature < (T0C + 10))) - var/transfer_coefficient = 1 - handle_temperature_damage(SLOT_HEAD, environment.temperature, environment_heat_capacity*transfer_coefficient) - if(stat == DEAD) - bodytemperature += 0.1*(environment.temperature - bodytemperature)*environment_heat_capacity/(environment_heat_capacity + 270000) - - -/mob/living/carbon/brain/proc/handle_temperature_damage(body_part, exposed_temperature, exposed_intensity) - if(status_flags & GODMODE) return - if(exposed_temperature > bodytemperature) - var/discomfort = min( abs(exposed_temperature - bodytemperature)*(exposed_intensity)/2000000, 1.0) - adjustFireLoss(20.0*discomfort) - else - var/discomfort = min( abs(exposed_temperature - bodytemperature)*(exposed_intensity)/2000000, 1.0) - adjustFireLoss(5.0*discomfort) - -/mob/living/carbon/brain/apply_chemical_effects() - . = ..() - if(resting) - ADJ_STATUS(src, STAT_DIZZY, -4) - return TRUE - -/mob/living/carbon/brain/is_blind() - return !container || ..() - -/mob/living/carbon/brain/should_be_dead() - if(container) - return FALSE - if(current_health >= get_config_value(/decl/config/num/health_health_threshold_dead)) - return FALSE - var/revival_brain_life = get_config_value(/decl/config/num/health_revival_brain_life) - return revival_brain_life >= 0 && (world.time - timeofhostdeath) > revival_brain_life - -/mob/living/carbon/brain/handle_regular_status_updates() - - . = ..() - if(!. || stat == DEAD || !emp_damage || !container) - return - - //Handling EMP effect in the Life(), it's made VERY simply, and has some additional effects handled elsewhere - //This is pretty much a damage type only used by MMIs, dished out by the emp_act - emp_damage = round(emp_damage,1)//Let's have some nice numbers to work with - switch(emp_damage) - if(31 to INFINITY) - emp_damage = 30//Let's not overdo it - if(21 to 30)//High level of EMP damage, unable to see, hear, or speak - SET_STATUS_MAX(src, STAT_BLIND, 2) - SET_STATUS_MAX(src, STAT_DEAF, 1) - set_status(STAT_SILENCE, 1) - if(!alert)//Sounds an alarm, but only once per 'level' - emote("alarm") - to_chat(src, SPAN_WARNING("Major electrical distruption detected: System rebooting.")) - alert = 1 - if(prob(75)) - emp_damage -= 1 - if(20) - alert = 0 - set_status(STAT_BLIND, 0) - set_status(STAT_DEAF, 0) - set_status(STAT_SILENCE, 0) - emp_damage -= 1 - if(11 to 19)//Moderate level of EMP damage, resulting in nearsightedness and ear damage - set_status(STAT_BLURRY, 1) - set_status(STAT_TINNITUS, 1) - if(!alert) - emote("alert") - to_chat(src, SPAN_WARNING("Primary systems are now online.")) - alert = 1 - if(prob(50)) - emp_damage -= 1 - if(10) - alert = 0 - set_status(STAT_BLURRY, 0) - set_status(STAT_TINNITUS, 0) - emp_damage -= 1 - if(2 to 9)//Low level of EMP damage, has few effects(handled elsewhere) - if(!alert) - emote("notice") - to_chat(src, SPAN_WARNING("System reboot nearly complete.")) - alert = 1 - if(prob(25)) - emp_damage -= 1 - if(1) - alert = 0 - to_chat(src, SPAN_WARNING("All systems restored.")) - emp_damage -= 1 - -/mob/living/carbon/brain/handle_regular_hud_updates() - . = ..() - if(!.) - return - update_sight() - if (healths) - if (stat != DEAD) - switch(current_health) - if(100 to INFINITY) - healths.icon_state = "health0" - if(80 to 100) - healths.icon_state = "health1" - if(60 to 80) - healths.icon_state = "health2" - if(40 to 60) - healths.icon_state = "health3" - if(20 to 40) - healths.icon_state = "health4" - if(0 to 20) - healths.icon_state = "health5" - else - healths.icon_state = "health6" - else - healths.icon_state = "health7" - - if(stat != DEAD) - if(is_blind()) - overlay_fullscreen("blind", /obj/screen/fullscreen/blind) - else - clear_fullscreen("blind") - set_fullscreen(disabilities & NEARSIGHTED, "impaired", /obj/screen/fullscreen/impaired, 1) - set_fullscreen(GET_STATUS(src, STAT_BLURRY), "blurry", /obj/screen/fullscreen/blurry) - set_fullscreen(GET_STATUS(src, STAT_DRUGGY), "high", /obj/screen/fullscreen/high) - if (machine) - if (!( machine.check_eye(src) )) - reset_view(null) - return 1 - -/mob/living/carbon/brain/can_change_intent() - return TRUE diff --git a/code/modules/mob/living/carbon/brain/login.dm b/code/modules/mob/living/carbon/brain/login.dm deleted file mode 100644 index 4f8a38ca269..00000000000 --- a/code/modules/mob/living/carbon/brain/login.dm +++ /dev/null @@ -1,3 +0,0 @@ -/mob/living/carbon/brain/Login() - ..() - set_status(STAT_ASLEEP, 0) diff --git a/code/modules/mob/living/carbon/brain/robot.dm b/code/modules/mob/living/carbon/brain/robot.dm deleted file mode 100644 index 4c689adfe65..00000000000 --- a/code/modules/mob/living/carbon/brain/robot.dm +++ /dev/null @@ -1,15 +0,0 @@ -/obj/item/mmi/digital/robot - name = "robotic intelligence circuit" - desc = "The pinnacle of artifical intelligence which can be achieved using classical computer science." - icon = 'icons/obj/modules/module_mainboard.dmi' - icon_state = ICON_STATE_WORLD - w_class = ITEM_SIZE_NORMAL - origin_tech = @'{"engineering":4,"materials":3,"programming":4}' - -/obj/item/mmi/digital/robot/PickName() - src.brainmob.SetName("[pick(list("ADA","DOS","GNU","MAC","WIN"))]-[random_id(type,1000,9999)]") - src.brainmob.real_name = src.brainmob.name - -/obj/item/mmi/digital/robot/on_update_icon() - . = ..() - icon_state = initial(icon_state) diff --git a/code/modules/mob/living/carbon/brain/say.dm b/code/modules/mob/living/carbon/brain/say.dm deleted file mode 100644 index 793e0ea9033..00000000000 --- a/code/modules/mob/living/carbon/brain/say.dm +++ /dev/null @@ -1,38 +0,0 @@ -//TODO: Convert this over for languages. -/mob/living/carbon/brain/say(var/message) - if(HAS_STATUS(src, STAT_SILENCE)) - return - - message = sanitize(message) - - if(!(container && istype(container, /obj/item/mmi))) - return //No MMI, can't speak, bucko./N - else - var/decl/language/speaking = parse_language(message) - if(speaking) - message = copytext(message, 2+length(speaking.key)) - var/verb = "says" - var/ending = copytext(message, length(message)) - if (speaking) - verb = speaking.get_spoken_verb(src, ending) - else - if(ending=="!") - verb=pick("exclaims","shouts","yells") - if(ending=="?") - verb="asks" - - if(prob(emp_damage*4)) - if(prob(10))//10% chane to drop the message entirely - return - else - message = Gibberish(message, (emp_damage*6))//scrambles the message, gets worse when emp_damage is higher - - if(speaking && speaking.flags & LANG_FLAG_HIVEMIND) - speaking.broadcast(src,trim(message)) - return - - if(istype(container, /obj/item/mmi/radio_enabled)) - var/obj/item/mmi/radio_enabled/R = container - if(R.radio) - spawn(0) R.radio.hear_talk(src, sanitize(message), verb, speaking) - ..(trim(message), speaking, verb) diff --git a/code/modules/mob/living/carbon/human/death.dm b/code/modules/mob/living/carbon/human/death.dm index 2b2ec274b61..0bd6e1fb1e5 100644 --- a/code/modules/mob/living/carbon/human/death.dm +++ b/code/modules/mob/living/carbon/human/death.dm @@ -47,11 +47,14 @@ deathmessage = species.get_death_message(src) || "seizes up and falls limp..." else deathmessage = "no message" + . = ..(gibbed, deathmessage, show_dead_message) + if(!gibbed) handle_organs() if(species.death_sound) playsound(loc, species.death_sound, 80, 1, 1) + handle_hud_list() /mob/living/carbon/human/proc/is_husked() diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index e6dbd75befe..b49c1dd1ad2 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -400,7 +400,7 @@ reset_blood() if(!client || !key) //Don't boot out anyone already in the mob. - for(var/mob/living/carbon/brain/brain in global.player_list) // This is really nasty, does it even work anymore? + for(var/mob/living/brain/brain in global.player_list) // This is really nasty, does it even work anymore? if(brain.real_name == src.real_name && brain.mind) brain.mind.transfer_to(src) qdel(brain.loc) diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm index dabfafbc503..8f2b8462ee2 100644 --- a/code/modules/mob/living/silicon/ai/ai.dm +++ b/code/modules/mob/living/silicon/ai/ai.dm @@ -104,7 +104,7 @@ var/global/list/ai_verbs_default = list( src.verbs -= ai_verbs_default src.verbs += /mob/living/verb/ghost -/mob/living/silicon/ai/Initialize(mapload, var/datum/ai_laws/L, var/obj/item/mmi/B, var/safety = 0) +/mob/living/silicon/ai/Initialize(mapload, var/datum/ai_laws/L, var/obj/item/organ/internal/brain_interface/B, var/safety = 0) announcement = new() announcement.title = "A.I. Announcement" announcement.announcement_type = "A.I. Announcement" @@ -144,11 +144,12 @@ var/global/list/ai_verbs_default = list( add_language(/decl/language/sign, 0) if(!safety)//Only used by AIize() to successfully spawn an AI. - if (!B)//If there is no player/brain inside. + var/mob/living/brainmob = B?.get_brainmob() + if(!brainmob) // If there is no player/brain inside. empty_playable_ai_cores += new/obj/structure/aicore/deactivated(loc)//New empty terminal. . = INITIALIZE_HINT_QDEL - else if(B.brainmob.mind) - B.brainmob.mind.transfer_to(src) + else if(brainmob.mind) + brainmob.mind.transfer_to(src) hud_list[HEALTH_HUD] = new /image/hud_overlay('icons/mob/hud.dmi', src, "hudblank") hud_list[STATUS_HUD] = new /image/hud_overlay('icons/mob/hud.dmi', src, "hudblank") hud_list[LIFE_HUD] = new /image/hud_overlay('icons/mob/hud.dmi', src, "hudblank") diff --git a/code/modules/mob/living/silicon/robot/component.dm b/code/modules/mob/living/silicon/robot/component.dm index 245fce44990..3fd56f6b134 100644 --- a/code/modules/mob/living/silicon/robot/component.dm +++ b/code/modules/mob/living/silicon/robot/component.dm @@ -1,4 +1,4 @@ -// TODO: remove the robot.mmi and robot.cell variables and completely rely on the robot component system +// TODO: remove the robot.central_processor and robot.cell variables and completely rely on the robot component system /datum/robot_component/var/name /datum/robot_component/var/installed = 0 diff --git a/code/modules/mob/living/silicon/robot/death.dm b/code/modules/mob/living/silicon/robot/death.dm index c275e273f0f..655e3a9c54f 100644 --- a/code/modules/mob/living/silicon/robot/death.dm +++ b/code/modules/mob/living/silicon/robot/death.dm @@ -1,7 +1,5 @@ /mob/living/silicon/robot/dust() - //Delete the MMI first so that it won't go popping out. - if(mmi) - qdel(mmi) + clear_brain() ..() /mob/living/silicon/robot/death(gibbed,deathmessage, show_dead_message) diff --git a/code/modules/mob/living/silicon/robot/drone/drone.dm b/code/modules/mob/living/silicon/robot/drone/drone.dm index 31414a6c4f4..57ed84e6ed3 100644 --- a/code/modules/mob/living/silicon/robot/drone/drone.dm +++ b/code/modules/mob/living/silicon/robot/drone/drone.dm @@ -46,7 +46,7 @@ default_language = /decl/language/binary/drone // NO BRAIN. - mmi = null + central_processor = null //We need to screw with their HP a bit. They have around one fifth as much HP as a full borg. for(var/V in components) if(V != "power cell") diff --git a/code/modules/mob/living/silicon/robot/drone/drone_items.dm b/code/modules/mob/living/silicon/robot/drone/drone_items.dm index 20b5a0bf3d8..cef20e56fe9 100644 --- a/code/modules/mob/living/silicon/robot/drone/drone_items.dm +++ b/code/modules/mob/living/silicon/robot/drone/drone_items.dm @@ -76,12 +76,11 @@ can_hold = list( /obj/item/cell, /obj/item/stock_parts, - /obj/item/mmi, + /obj/item/organ/internal/brain_interface, /obj/item/robot_parts, /obj/item/borg/upgrade, /obj/item/flash, /obj/item/organ/internal/brain, - /obj/item/organ/internal/posibrain, /obj/item/stack/cable_coil, /obj/item/stock_parts/circuitboard, /obj/item/chems/glass, diff --git a/code/modules/mob/living/silicon/robot/life.dm b/code/modules/mob/living/silicon/robot/life.dm index 46eead28499..bf9ed519c5d 100644 --- a/code/modules/mob/living/silicon/robot/life.dm +++ b/code/modules/mob/living/silicon/robot/life.dm @@ -242,7 +242,7 @@ if (src.client) src.client.screen -= src.contents for(var/obj/I in src.contents) - if(I && !(istype(I,/obj/item/cell) || istype(I,/obj/item/radio) || istype(I,/obj/machinery/camera) || istype(I,/obj/item/mmi))) + if(I && !(istype(I,/obj/item/cell) || istype(I,/obj/item/radio) || istype(I,/obj/machinery/camera) || istype(I,/obj/item/organ/internal/brain_interface))) src.client.screen += I if(src.module_state_1) src.module_state_1:screen_loc = ui_inv1 diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index e7c0646d9f3..6bfee52611b 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -60,7 +60,7 @@ // Components are basically robot organs. var/list/components = list() - var/obj/item/mmi/mmi = null + var/obj/item/organ/internal/central_processor var/opened = 0 var/emagged = 0 @@ -195,21 +195,15 @@ return amount return 0 -//If there's an MMI in the robot, have it ejected when the mob goes away. --NEO -//Improved /N /mob/living/silicon/robot/Destroy() - if(mmi)//Safety for when a cyborg gets dust()ed. Or there is no MMI inside. - if(mind) - mmi.dropInto(loc) - if(mmi.brainmob) - mind.transfer_to(mmi.brainmob) - else - to_chat(src, "Oops! Something went very wrong, your MMI was unable to receive your mind. You have been ghosted. Please make a bug report so we can fix this bug.") - ghostize() - //ERROR("A borg has been destroyed, but its MMI lacked a brainmob, so the mind could not be transferred. Player: [ckey].") - mmi = null + if(central_processor) + central_processor.dropInto(loc) + var/mob/living/brainmob = central_processor.get_brainmob() + if(mind && brainmob) + mind.transfer_to(brainmob) else - QDEL_NULL(mmi) + ghostize() + central_processor = null if(connected_ai) connected_ai.connected_robots -= src connected_ai = null @@ -280,12 +274,10 @@ if(prefix) modtype = prefix - if(istype(mmi, /obj/item/organ/internal/posibrain)) - braintype = "Robot" - else if(istype(mmi, /obj/item/mmi/digital/robot)) - braintype = "Drone" + if(istype(central_processor)) + braintype = central_processor.get_synthetic_owner_name() else - braintype = "Cyborg" + braintype = "Robot" var/changed_name = "" if(custom_name) @@ -514,24 +506,31 @@ else if(IS_CROWBAR(W) && user.a_intent != I_HURT) // crowbar means open or close the cover - we all know what a crowbar is by now if(opened) if(cell) - user.visible_message("\The [user] begins clasping shut \the [src]'s maintenance hatch.", "You begin closing up \the [src].") + + user.visible_message( + SPAN_NOTICE("\The [user] begins clasping shut \the [src]'s maintenance hatch."), + SPAN_NOTICE("You begin closing up \the [src].")) + if(do_after(user, 50, src)) - to_chat(user, "You close \the [src]'s maintenance hatch.") + to_chat(user, SPAN_NOTICE("You close \the [src]'s maintenance hatch.")) opened = 0 update_icon() else if(wiresexposed && wires.IsAllCut()) - //Cell is out, wires are exposed, remove MMI, produce damaged chassis, baleet original mob. - if(!mmi) - to_chat(user, "\The [src] has no brain to remove.") + //Cell is out, wires are exposed, remove CPU, produce damaged chassis, baleet original mob. + if(!central_processor) + to_chat(user, "\The [src] has no central processor to remove.") return - user.visible_message("\The [user] begins ripping [mmi] from [src].", "You jam the crowbar into the robot and begin levering [mmi].") + user.visible_message( + SPAN_NOTICE("\The [user] begins ripping \the [central_processor] out of \the [src]."), + SPAN_NOTICE("You jam the crowbar into the robot and begin levering out \the [central_processor].")) + if(do_after(user, 50, src)) dismantle(user) else - // Okay we're not removing the cell or an MMI, but maybe something else? + // Okay we're not removing the cell or a CPU, but maybe something else? var/list/removable_components = list() for(var/V in components) if(V == "power cell") continue @@ -1099,7 +1098,10 @@ return ASSIGNMENT_ROBOT /mob/living/silicon/robot/handle_pre_transformation() - QDEL_NULL(mmi) + clear_brain() + +/mob/living/silicon/robot/proc/clear_brain() + QDEL_NULL(central_processor) /mob/living/silicon/robot/do_flash_animation() set waitfor = FALSE diff --git a/code/modules/mob/mob_transformation_simple.dm b/code/modules/mob/mob_transformation_simple.dm index f9f04a6e329..e4f273ad596 100644 --- a/code/modules/mob/mob_transformation_simple.dm +++ b/code/modules/mob/mob_transformation_simple.dm @@ -50,7 +50,7 @@ var/global/list/href_to_mob_type = list( //This proc is the most basic of the procs. All it does is make a new mob on the same tile and transfer over a few variables. //Returns the new mob -//Note that this proc does NOT do MMI related stuff! +//Note that this proc does NOT do brain related stuff! /mob/proc/change_mob_type(var/new_type, var/turf/location, var/new_name, var/subspecies) if(!new_type) diff --git a/code/modules/mob/skills/skill.dm b/code/modules/mob/skills/skill.dm index 7f7dcbed4e1..6525d69a964 100644 --- a/code/modules/mob/skills/skill.dm +++ b/code/modules/mob/skills/skill.dm @@ -334,7 +334,7 @@ var/global/list/skills = list() levels = list( "Unskilled" = "You know how to use the technology that was present in whatever society you grew up in. You know how to tell when something is malfunctioning, but you have to call tech support to get it fixed.", "Basic" = "You use and repair high-tech equipment in the course of your daily work. You can fix simple problems, and you know how to use a circuit printer or autolathe. You can build simple robots such as cleanbots and medibots.", - "Trained" = "You can build or repair an exosuit or cyborg chassis, use advanced fabricators and analyzers, and build prosthetic limbs. You can safely transfer an MMI or posibrain into a cyborg chassis.
- You can attach robotic limbs. Its speed increases with level.", + "Trained" = "You can build or repair an exosuit or cyborg chassis, use advanced fabricators and analyzers, and build prosthetic limbs. You can safely transfer a neural interface into a cyborg chassis.
- You can attach robotic limbs. Its speed increases with level.", "Experienced" = "You have years of experience building or reverse-engineering complex devices. Your use of fabricators and destructive analyzers is efficient and methodical. You can design contraptions to order, and likely sell those designs at a profit.", "Master" = "You are an inventor or researcher. You can design, build, and modify equipment that most people don't even know exists. You are at home in the lab and the workshop and you've never met a gadget you couldn't take apart, put back together, and replicate." ) diff --git a/code/modules/mob/transform_procs.dm b/code/modules/mob/transform_procs.dm index 38b7caac0fb..ae06234265a 100644 --- a/code/modules/mob/transform_procs.dm +++ b/code/modules/mob/transform_procs.dm @@ -64,7 +64,7 @@ sound_to(src, sound(null, repeat = 0, wait = 0, volume = 85, channel = sound_channels.lobby_channel))// stop the jams for AIs - var/mob/living/silicon/ai/O = new (loc, global.using_map.default_law_type,,1)//No MMI but safety is in effect. + var/mob/living/silicon/ai/O = new (loc, global.using_map.default_law_type,,1)//No brain but safety is in effect. O.set_invisibility(INVISIBILITY_NONE) O.aiRestorePowerRoutine = 0 if(mind) @@ -128,10 +128,9 @@ mind.transfer_to(O) if(O.mind && O.mind.assigned_role == ASSIGNMENT_ROBOT) O.mind.original = O - var/mmi_type = SSrobots.get_mmi_type_by_title(O.mind.role_alt_title ? O.mind.role_alt_title : O.mind.assigned_role) + var/mmi_type = SSrobots.get_brain_type_by_title(O.mind.role_alt_title ? O.mind.role_alt_title : O.mind.assigned_role) if(mmi_type) - O.mmi = new mmi_type(O) - O.mmi.transfer_identity(src) + O.central_processor = new mmi_type(O) O.dropInto(loc) O.job = ASSIGNMENT_ROBOT diff --git a/code/modules/organs/external/_external.dm b/code/modules/organs/external/_external.dm index b84012f2d46..cdeac308694 100644 --- a/code/modules/organs/external/_external.dm +++ b/code/modules/organs/external/_external.dm @@ -456,7 +456,7 @@ // //If we contain any child organs add them to the owner // - for(var/obj/item/organ/organ in internal_organs) + for(var/obj/item/organ/organ in implants) owner.add_organ(organ, src, in_place, update_icon, detached) for(var/obj/item/organ/external/organ in children) @@ -521,6 +521,10 @@ if(!in_place) parent.update_wounds() + // Notify our children. + for(var/obj/item/organ/internal/I in internal_organs) + I.on_holding_organ_installed(src) + /obj/item/organ/external/proc/drop_equipped_clothing() if(!owner) return @@ -1352,6 +1356,7 @@ Note that amputating the affected organ does in fact remove the infection from t W.forceMove(owner) /obj/item/organ/external/do_uninstall(in_place, detach, ignore_children, update_icon) + var/mob/living/carbon/human/victim = owner //parent proc clears owner if(!(. = ..())) return @@ -1412,6 +1417,10 @@ Note that amputating the affected organ does in fact remove the infection from t LAZYREMOVE(parent.children, src) parent = null + // Notify our children. + for(var/obj/item/organ/internal/I in internal_organs) + I.on_holding_organ_uninstalled(src) + /obj/item/organ/external/on_remove_effects(mob/living/last_owner) . = ..() drop_equipped_clothing() @@ -1431,7 +1440,7 @@ Note that amputating the affected organ does in fact remove the infection from t /obj/item/organ/external/set_detached(is_detached) if(BP_IS_PROSTHETIC(src)) is_detached = FALSE //External prosthetics are never detached - return ..(is_detached) + . = ..(is_detached) /obj/item/organ/external/proc/disfigure(var/type = BRUTE) if(status & ORGAN_DISFIGURED) diff --git a/code/modules/organs/internal/_internal.dm b/code/modules/organs/internal/_internal.dm index 009577fb2d4..0c8e936d4a5 100644 --- a/code/modules/organs/internal/_internal.dm +++ b/code/modules/organs/internal/_internal.dm @@ -24,6 +24,9 @@ var/min_bruised_damage = 10 // Damage before considered bruised var/damage_reduction = 0.5 //modifier for internal organ injury + /// Whether or not we should try to transfer a brainmob when removed or replaced in a mob. + var/transfer_brainmob_with_organ = FALSE + /obj/item/organ/internal/Initialize(mapload, material_key, datum/dna/given_dna, decl/bodytype/new_bodytype) if(!alive_icon) alive_icon = initial(icon_state) @@ -253,3 +256,69 @@ var/obj/item/organ/O = last_owner.get_organ(parent_organ) if(O) O.vital_to_owner = null + +// Stub to allow brain interfaces to return their wrapped brainmob. +/obj/item/organ/internal/proc/get_brainmob(var/create_if_missing = FALSE) + return + +/obj/item/organ/internal/proc/transfer_key_to_brainmob(var/mob/living/M, var/update_brainmob = TRUE) + var/mob/living/brainmob = get_brainmob(create_if_missing = TRUE) + if(brainmob) + transfer_key_from_mob_to_mob(M, brainmob) + if(update_brainmob) + brainmob.SetName(M.real_name) + brainmob.real_name = M.real_name + brainmob.dna = M.dna?.Clone() + brainmob.languages = M.languages?.Copy() + brainmob.default_language = M.default_language + to_chat(brainmob, SPAN_NOTICE("You feel slightly disoriented. That's normal when you're just \a [initial(src.name)].")) + callHook("debrain", list(brainmob)) + return TRUE + return FALSE + +/obj/item/organ/internal/proc/get_synthetic_owner_name() + return "Cyborg" + +/obj/item/organ/internal/preserve_in_cryopod(var/obj/machinery/cryopod/pod) + var/mob/living/brainmob = get_brainmob() + return brainmob?.key + +// This might need revisiting to stop people successfully implanting brains in groins and transferring minds. +/obj/item/organ/internal/do_install(mob/living/carbon/human/target, obj/item/organ/external/affected, in_place, update_icon, detached) + . = ..() + if(transfer_brainmob_with_organ && istype(owner)) + var/mob/living/brainmob = get_brainmob(create_if_missing = TRUE) + if(brainmob) + if(status & ORGAN_CUT_AWAY) + transfer_key_to_brainmob(owner, update_brainmob = TRUE) + else + transfer_key_from_mob_to_mob(brainmob, owner) + +/obj/item/organ/internal/do_uninstall(in_place, detach, ignore_children, update_icon) + var/mob/living/victim = owner // cleared in parent proc + . = ..() + if(transfer_brainmob_with_organ && istype(victim)) + var/mob/living/brainmob = get_brainmob(create_if_missing = TRUE) + if(brainmob) + if(status & ORGAN_CUT_AWAY) + transfer_key_to_brainmob(victim, update_brainmob = TRUE) + else + transfer_key_from_mob_to_mob(brainmob, victim) + +/obj/item/organ/internal/proc/on_holding_organ_installed(var/obj/item/organ/external/holding) + if(istype(holding) && transfer_brainmob_with_organ && istype(owner)) + var/mob/living/brainmob = get_brainmob(create_if_missing = TRUE) + if(brainmob) + if(holding.status & ORGAN_CUT_AWAY) + transfer_key_to_brainmob(owner, update_brainmob = TRUE) + else + transfer_key_from_mob_to_mob(brainmob, owner) + +/obj/item/organ/internal/proc/on_holding_organ_uninstalled(var/obj/item/organ/external/holding) + if(istype(holding) && transfer_brainmob_with_organ && istype(owner)) + var/mob/living/brainmob = get_brainmob(create_if_missing = TRUE) + if(brainmob) + if(holding.status & ORGAN_CUT_AWAY) + transfer_key_to_brainmob(owner, update_brainmob = TRUE) + else + transfer_key_from_mob_to_mob(brainmob, owner) diff --git a/code/modules/organs/internal/brain.dm b/code/modules/organs/internal/brain.dm index 903247ae18e..d84032440d0 100644 --- a/code/modules/organs/internal/brain.dm +++ b/code/modules/organs/internal/brain.dm @@ -14,43 +14,58 @@ relative_size = 85 damage_reduction = 0 scale_max_damage_to_species_health = FALSE - var/can_use_mmi = TRUE - var/mob/living/carbon/brain/brainmob = null + transfer_brainmob_with_organ = TRUE + var/can_use_brain_interface = TRUE var/should_announce_brain_damage = TRUE var/oxygen_reserve = 6 + VAR_PRIVATE/mob/living/_brainmob = /mob/living/brain + +/obj/item/organ/internal/brain/get_brainmob(var/create_if_missing = FALSE) + if(!istype(_brainmob) && create_if_missing) + initialize_brainmob() + if(istype(_brainmob)) + return _brainmob + +/obj/item/organ/internal/brain/Initialize() + . = ..() + if(species) + set_max_damage(species.total_health) + else + set_max_damage(200) + +/obj/item/organ/internal/brain/proc/initialize_brainmob() + if(istype(_brainmob)) + return + if(!ispath(_brainmob)) + _brainmob = initial(_brainmob) + if(ispath(_brainmob)) + _brainmob = new _brainmob(src) + else + _brainmob = null /obj/item/organ/internal/brain/getToxLoss() return 0 /obj/item/organ/internal/brain/set_species(species_name) . = ..() + icon_state = "brain-prosthetic" if(species) set_max_damage(species.total_health) else set_max_damage(200) /obj/item/organ/internal/brain/Destroy() - QDEL_NULL(brainmob) + if(istype(_brainmob)) + QDEL_NULL(_brainmob) . = ..() -/obj/item/organ/internal/brain/proc/transfer_identity(var/mob/living/carbon/H) - - if(!brainmob) - brainmob = new(src) - brainmob.SetName(H.real_name) - brainmob.real_name = H.real_name - brainmob.dna = H.dna.Clone() - brainmob.timeofhostdeath = H.timeofdeath - - if(H.mind) - H.mind.transfer_to(brainmob) - - to_chat(brainmob, "You feel slightly disoriented. That's normal when you're just \a [initial(src.name)].") - callHook("debrain", list(brainmob)) - -/obj/item/organ/internal/brain/examine(mob/user) +/obj/item/organ/internal/brain/examine(mob/user, var/distance) . = ..() - if(brainmob && brainmob.client)//if thar be a brain inside... the brain. + if(distance <= 1) + show_brain_status(user) + +/obj/item/organ/internal/brain/proc/show_brain_status(mob/user) + if(istype(_brainmob) && _brainmob?.client) //if thar be a brain inside... the brain. to_chat(user, "You can feel the small spark of life still left in this one.") else to_chat(user, "This one seems particularly lifeless. Perhaps it will regain some of its luster later..") @@ -67,21 +82,6 @@ if(!(. = ..())) return -/obj/item/organ/internal/brain/on_remove_effects() - if(istype(owner)) - transfer_identity(owner) - return ..() - -/obj/item/organ/internal/brain/on_add_effects() - if(brainmob) - if(brainmob.mind) - if(owner.key) - owner.ghostize() - brainmob.mind.transfer_to(owner) - else - owner.key = brainmob.key - return ..() - /obj/item/organ/internal/brain/can_recover() return !(status & ORGAN_DEAD) @@ -221,7 +221,7 @@ /obj/item/organ/internal/brain/surgical_fix(mob/user) var/blood_volume = owner.get_blood_oxygenation() if(blood_volume < BLOOD_VOLUME_SURVIVE) - to_chat(user, "Parts of [src] didn't survive the procedure due to lack of air supply!") + to_chat(user, SPAN_DANGER("Parts of \the [src] didn't survive the procedure due to lack of air supply!")) set_max_damage(FLOOR(max_damage - 0.25*damage)) heal_damage(damage) @@ -229,4 +229,9 @@ . = (species.total_health - max_damage)/species.total_health /obj/item/organ/internal/brain/get_mechanical_assisted_descriptor() - return "machine-interface [name]" \ No newline at end of file + return "machine-interface [name]" + +/obj/item/organ/internal/brain/die() + if(istype(_brainmob) && _brainmob.stat != DEAD) + _brainmob.death() + ..() diff --git a/code/modules/organs/internal/brain_computer.dm b/code/modules/organs/internal/brain_computer.dm new file mode 100644 index 00000000000..46fa79eb974 --- /dev/null +++ b/code/modules/organs/internal/brain_computer.dm @@ -0,0 +1,92 @@ +// Robobrain. +/obj/item/organ/internal/brain/robotic + name = "computer intelligence core" + desc = "The pinnacle of artifical intelligence technology, conveniently stored in a fist-sized cube." + icon = 'icons/obj/items/brain_interface_robotic.dmi' + origin_tech = "{'engineering':4,'materials':4,'wormholes':2,'programming':4}" + material = /decl/material/solid/metal/steel + matter = list( + /decl/material/solid/glass = MATTER_AMOUNT_REINFORCEMENT, + /decl/material/solid/metal/silver = MATTER_AMOUNT_TRACE, + /decl/material/solid/metal/gold = MATTER_AMOUNT_TRACE, + /decl/material/solid/gemstone/diamond = MATTER_AMOUNT_TRACE + ) + can_use_brain_interface = FALSE + var/searching = FALSE + var/brain_name + +/obj/item/organ/internal/brain/robotic/handle_severe_damage() + return // TODO: computer maladies + +/obj/item/organ/internal/brain/robotic/handle_disabilities() + return // TODO: computer maladies + +/obj/item/organ/internal/brain/robotic/Initialize() + . = ..() + set_bodytype(/decl/bodytype/prosthetic/basic_human) + update_icon() + brain_name = "[pick(list("ADA","DOS","GNU","MAC","WIN"))]-[random_id(type,1000,9999)]" + SetName("[name] ([brain_name])") + +/obj/item/organ/internal/brain/robotic/initialize_brainmob() + ..() + if(istype(_brainmob)) + _brainmob.SetName(brain_name) + _brainmob.add_language(/decl/language/machine) + +/obj/item/organ/internal/brain/robotic/on_update_icon() + var/mob/living/brainmob = get_brainmob() + icon_state = get_world_inventory_state() + if(!searching) + if(!brainmob?.key || brainmob.stat == DEAD) + icon_state = "[icon_state]-dead" + else + icon_state = "[icon_state]-full" + +/obj/item/organ/internal/brain/robotic/attack_self(mob/user) + var/mob/living/brainmob = get_brainmob(create_if_missing = TRUE) + if(!brainmob?.key && !searching) + to_chat(user, SPAN_NOTICE("You press the power button and boot up \the [src].")) + searching = TRUE + update_icon() + var/decl/ghosttrap/G = GET_DECL(/decl/ghosttrap/machine_intelligence) + G.request_player(brainmob, "Someone is requesting a personality for a [name].", 1 MINUTE) + addtimer(CALLBACK(src, .proc/reset_search), 1 MINUTE) + return TRUE + . = ..() + +/obj/item/organ/internal/brain/robotic/proc/reset_search() + searching = FALSE + update_icon() + var/mob/living/brainmob = get_brainmob() + if(!brainmob?.key) + visible_message(SPAN_WARNING("\The [src] emits a series of loud beeps, indicating a failure to boot. Try again in a few minutes.")) + +/obj/item/organ/internal/brain/robotic/attack_ghost(var/mob/observer/ghost/user) + var/mob/living/brainmob = get_brainmob(create_if_missing = TRUE) + if(brainmob?.key) + to_chat(user, SPAN_WARNING("\The [src] is already inhabited; there's no room for you!")) + return TRUE + + var/decl/ghosttrap/G = GET_DECL(/decl/ghosttrap/machine_intelligence) + if(G.assess_candidate(user)) + var/response = alert(user, "Are you sure you wish to possess \the [src]?", "Possess [capitalize(name)]", "Yes", "No") + if(response == "Yes" && brainmob && !brainmob?.key && G.assess_candidate(user)) + G.transfer_personality(user, brainmob) + +/obj/item/organ/internal/brain/robotic/show_brain_status(mob/user) + var/mob/living/brainmob = get_brainmob() + if(brainmob?.key) + switch(brainmob.stat) + if(CONSCIOUS) + if(!brainmob.client) + to_chat(user, SPAN_WARNING("It appears to be in stand-by mode.")) + if(UNCONSCIOUS) + to_chat(user, SPAN_WARNING("It doesn't seem to be responsive.")) + if(DEAD) + to_chat(user, SPAN_WARNING("It appears to be completely inactive.")) + else + to_chat(user, SPAN_WARNING("It appears to be completely inactive.")) + +/obj/item/organ/internal/brain/robotic/get_synthetic_owner_name() + return "Robot" diff --git a/code/modules/organs/internal/cell.dm b/code/modules/organs/internal/cell.dm new file mode 100644 index 00000000000..b9d478150f1 --- /dev/null +++ b/code/modules/organs/internal/cell.dm @@ -0,0 +1,96 @@ +/obj/item/organ/internal/cell + name = "microbattery" + desc = "A small, powerful cell for use in fully prosthetic bodies." + icon_state = "cell" + dead_icon = "cell_bork" + organ_tag = BP_CELL + parent_organ = BP_CHEST + var/open + var/obj/item/cell/cell = /obj/item/cell/hyper + //at 0.8 completely depleted after 60ish minutes of constant walking or 130 minutes of standing still + var/servo_cost = 0.8 + +/obj/item/organ/internal/cell/Initialize() + if(ispath(cell)) + cell = new cell(src) + . = ..() + +/obj/item/organ/internal/cell/proc/percent() + if(!cell) + return 0 + return get_charge()/cell.maxcharge * 100 + +/obj/item/organ/internal/cell/proc/get_charge() + if(!cell) + return 0 + if(status & ORGAN_DEAD) + return 0 + return round(cell.charge*(1 - damage/max_damage)) + +/obj/item/organ/internal/cell/proc/checked_use(var/amount) + if(!is_usable()) + return FALSE + return cell && cell.checked_use(amount) + +/obj/item/organ/internal/cell/proc/use(var/amount) + if(!is_usable()) + return 0 + return cell && cell.use(amount) + +/obj/item/organ/internal/cell/proc/get_power_drain() + var/damage_factor = 1 + 10 * damage/max_damage + return servo_cost * damage_factor + +/obj/item/organ/internal/cell/Process() + ..() + if(!owner) + return + if(owner.stat == DEAD) //not a drain anymore + return + var/cost = get_power_drain() + if(world.time - owner.l_move_time < 15) + cost *= 2 + if(!checked_use(cost) && owner.isSynthetic()) + if(!owner.lying && !owner.buckled) + to_chat(owner, SPAN_WARNING("You don't have enough energy to function!")) + SET_STATUS_MAX(owner, STAT_PARA, 3) + +/obj/item/organ/internal/cell/emp_act(severity) + ..() + if(cell) + cell.emp_act(severity) + +/obj/item/organ/internal/cell/attackby(obj/item/W, mob/user) + if(IS_SCREWDRIVER(W)) + if(open) + open = 0 + to_chat(user, SPAN_NOTICE("You screw the battery panel in place.")) + else + open = 1 + to_chat(user, SPAN_NOTICE("You unscrew the battery panel.")) + + if(IS_CROWBAR(W)) + if(open) + if(cell) + user.put_in_hands(cell) + to_chat(user, SPAN_NOTICE("You remove \the [cell] from \the [src].")) + cell = null + + if (istype(W, /obj/item/cell)) + if(open) + if(cell) + to_chat(user, SPAN_WARNING("There is a power cell already installed.")) + else if(user.try_unequip(W, src)) + cell = W + to_chat(user, SPAN_NOTICE("You insert \the [cell].")) + +/obj/item/organ/internal/cell/on_add_effects() + . = ..() + // This is very ghetto way of rebooting an IPC. TODO better way. + if(owner && owner.stat == DEAD) + owner.set_stat(CONSCIOUS) + owner.visible_message(SPAN_NOTICE("\The [owner] twitches visibly!")) + +/obj/item/organ/internal/cell/listen() + if(get_charge()) + return "faint hum of the power bank" diff --git a/code/modules/organs/internal/heart.dm b/code/modules/organs/internal/heart.dm index e160e318278..be7f416b0d4 100644 --- a/code/modules/organs/internal/heart.dm +++ b/code/modules/organs/internal/heart.dm @@ -5,13 +5,13 @@ icon_state = "heart-on" dead_icon = "heart-off" prosthetic_icon = "heart-prosthetic" + damage_reduction = 0.7 + relative_size = 5 + max_damage = 45 var/pulse = PULSE_NORM var/heartbeat = 0 var/beat_sound = 'sound/effects/singlebeat.ogg' var/tmp/next_blood_squirt = 0 - damage_reduction = 0.7 - relative_size = 5 - max_damage = 45 var/open var/list/external_pump diff --git a/code/modules/organs/internal/posibrain.dm b/code/modules/organs/internal/posibrain.dm deleted file mode 100644 index ba69416cdd0..00000000000 --- a/code/modules/organs/internal/posibrain.dm +++ /dev/null @@ -1,333 +0,0 @@ -/obj/item/organ/internal/posibrain - name = "positronic brain" - desc = "A cube of shining metal, four inches to a side and covered in shallow grooves." - icon = 'icons/obj/assemblies.dmi' - icon_state = "posibrain" - organ_tag = BP_POSIBRAIN - parent_organ = BP_CHEST - force = 1.0 - w_class = ITEM_SIZE_NORMAL - throwforce = 1 - throw_speed = 3 - throw_range = 5 - origin_tech = @'{"engineering":4,"materials":4,"wormholes":2,"programming":4}' - attack_verb = list("attacked", "slapped", "whacked") - material = /decl/material/solid/metal/steel - matter = list( - /decl/material/solid/glass = MATTER_AMOUNT_REINFORCEMENT, - /decl/material/solid/metal/silver = MATTER_AMOUNT_TRACE, - /decl/material/solid/metal/gold = MATTER_AMOUNT_TRACE, - /decl/material/solid/gemstone/diamond = MATTER_AMOUNT_TRACE - ) - relative_size = 60 - req_access = list(access_robotics) - organ_properties = ORGAN_PROP_PROSTHETIC //triggers robotization on init - scale_max_damage_to_species_health = FALSE - - var/mob/living/carbon/brain/brainmob = null - var/searching = 0 - var/askDelay = 60 SECONDS - -/obj/item/organ/internal/posibrain/Initialize() - . = ..() - if(!brainmob && iscarbon(loc)) - init(loc) //Not sure why we're creating a braimob on load, and also why not installing it in the owner... - -/obj/item/organ/internal/posibrain/proc/init(var/mob/living/carbon/H) - if(brainmob) - return - brainmob = new(src) - if(istype(H)) - brainmob.SetName(H.real_name) - brainmob.real_name = H.real_name - brainmob.dna = H.dna.Clone() - brainmob.add_language(/decl/language/human/common) - brainmob.add_language(/decl/language/binary) - -/obj/item/organ/internal/posibrain/Destroy() - QDEL_NULL(brainmob) - return ..() - -/obj/item/organ/internal/posibrain/attack_self(mob/user) - if(brainmob && !brainmob.key && searching == 0) - //Start the process of searching for a new user. - to_chat(user, "You carefully locate the manual activation switch and start the positronic brain's boot process.") - icon_state = "posibrain-searching" - src.searching = 1 - var/decl/ghosttrap/G = GET_DECL(/decl/ghosttrap/positronic_brain) - G.request_player(brainmob, "Someone is requesting a personality for a positronic brain.", 60 SECONDS) - addtimer(CALLBACK(src, PROC_REF(reset_search)), askDelay) - -/obj/item/organ/internal/posibrain/proc/reset_search() //We give the players time to decide, then reset the timer. - if(!brainmob?.key) - searching = FALSE - icon_state = "posibrain" - visible_message(SPAN_WARNING("The positronic brain buzzes quietly, and the golden lights fade away. Perhaps you could try again?")) - -/obj/item/organ/internal/posibrain/attack_ghost(var/mob/observer/ghost/user) - if(!searching || (src.brainmob && src.brainmob.key)) - return - - var/decl/ghosttrap/G = GET_DECL(/decl/ghosttrap/positronic_brain) - if(!G.assess_candidate(user)) - return - var/response = alert(user, "Are you sure you wish to possess this [src]?", "Possess [src]", "Yes", "No") - if(response == "Yes") - G.transfer_personality(user, brainmob) - -/obj/item/organ/internal/posibrain/examine(mob/user) - . = ..() - - var/msg = "*---------*\nThis is [html_icon(src)] \a [src]!\n[desc]\n" - - msg += "" - - if(src.brainmob && src.brainmob.key) - switch(src.brainmob.stat) - if(CONSCIOUS) - if(!src.brainmob.client) msg += "It appears to be in stand-by mode.\n" //afk - if(UNCONSCIOUS) msg += "It doesn't seem to be responsive.\n" - if(DEAD) msg += "It appears to be completely inactive.\n" - else - msg += "It appears to be completely inactive.\n" - - msg += "*---------*" - to_chat(user, msg) - return - -/obj/item/organ/internal/posibrain/emp_act(severity) - if(!src.brainmob) - return - else - switch(severity) - if(1) - src.brainmob.emp_damage += rand(20,30) - if(2) - src.brainmob.emp_damage += rand(10,20) - if(3) - src.brainmob.emp_damage += rand(0,10) - ..() - -/obj/item/organ/internal/posibrain/proc/PickName() - src.brainmob.SetName("[pick(list("PBU","HIU","SINA","ARMA","OSI"))]-[random_id(type,100,999)]") - src.brainmob.real_name = src.brainmob.name - -/obj/item/organ/internal/posibrain/on_update_icon() - . = ..() - if(src.brainmob && src.brainmob.key) - icon_state = "posibrain-occupied" - else - icon_state = "posibrain" - -/obj/item/organ/internal/posibrain/proc/transfer_identity(var/mob/living/carbon/H) - if(H && H.mind) - brainmob.set_stat(CONSCIOUS) - H.mind.transfer_to(brainmob) - brainmob.SetName(H.real_name) - brainmob.real_name = H.real_name - brainmob.dna = H.dna.Clone() - - update_icon() - - to_chat(brainmob, "You feel slightly disoriented. That's normal when you're just \a [initial(src.name)].") - callHook("debrain", list(brainmob)) - -/obj/item/organ/internal/posibrain/on_add_effects() - if(brainmob) - if(brainmob.mind) - if(owner.key) - owner.ghostize() - brainmob.mind.transfer_to(owner) - else if(brainmob.key) //posibrain init with a dummy brainmob for some reasons, so gotta do this or its gonna disconnect the client on mob transformation - owner.key = brainmob.key - return ..() - -/obj/item/organ/internal/posibrain/on_remove_effects() - if(istype(owner)) - transfer_identity(owner) - return ..() - -/obj/item/organ/internal/posibrain/do_install(mob/living/carbon/human/target, obj/item/organ/external/affected, in_place, update_icon, detached) - if(!(. = ..())) - return - if(istype(owner)) - SetName(initial(name)) //Reset the organ's name to stay coherent if we're put back into someone's skull - -/obj/item/organ/internal/posibrain/do_uninstall(in_place, detach, ignore_children) - if(!in_place && istype(owner) && name == initial(name)) - SetName("\the [owner.real_name]'s [initial(name)]") - return ..() - -/obj/item/organ/internal/cell - name = "microbattery" - desc = "A small, powerful cell for use in fully prosthetic bodies." - icon_state = "cell" - dead_icon = "cell_bork" - organ_tag = BP_CELL - parent_organ = BP_CHEST - organ_properties = ORGAN_PROP_PROSTHETIC //triggers robotization on init - var/open - var/obj/item/cell/cell = /obj/item/cell/hyper - //at 0.8 completely depleted after 60ish minutes of constant walking or 130 minutes of standing still - var/servo_cost = 0.8 - -/obj/item/organ/internal/cell/Initialize() - if(ispath(cell)) - cell = new cell(src) - . = ..() - -/obj/item/organ/internal/cell/proc/percent() - if(!cell) - return 0 - return get_charge()/cell.maxcharge * 100 - -/obj/item/organ/internal/cell/proc/get_charge() - if(!cell) - return 0 - if(status & ORGAN_DEAD) - return 0 - return round(cell.charge*(1 - damage/max_damage)) - -/obj/item/organ/internal/cell/proc/checked_use(var/amount) - if(!is_usable()) - return FALSE - return cell && cell.checked_use(amount) - -/obj/item/organ/internal/cell/proc/use(var/amount) - if(!is_usable()) - return 0 - return cell && cell.use(amount) - -/obj/item/organ/internal/cell/proc/get_power_drain() - var/damage_factor = 1 + 10 * damage/max_damage - return servo_cost * damage_factor - -/obj/item/organ/internal/cell/Process() - ..() - if(!owner) - return - if(owner.stat == DEAD) //not a drain anymore - return - var/cost = get_power_drain() - if(world.time - owner.l_move_time < 15) - cost *= 2 - if(!checked_use(cost) && owner.isSynthetic()) - if(!owner.lying && !owner.buckled) - to_chat(owner, "You don't have enough energy to function!") - SET_STATUS_MAX(owner, STAT_PARA, 3) - -/obj/item/organ/internal/cell/emp_act(severity) - ..() - if(cell) - cell.emp_act(severity) - -/obj/item/organ/internal/cell/attackby(obj/item/W, mob/user) - if(IS_SCREWDRIVER(W)) - if(open) - open = 0 - to_chat(user, "You screw the battery panel in place.") - else - open = 1 - to_chat(user, "You unscrew the battery panel.") - - if(IS_CROWBAR(W)) - if(open) - if(cell) - user.put_in_hands(cell) - to_chat(user, "You remove \the [cell] from \the [src].") - cell = null - - if (istype(W, /obj/item/cell)) - if(open) - if(cell) - to_chat(user, "There is a power cell already installed.") - else if(user.try_unequip(W, src)) - cell = W - to_chat(user, "You insert \the [cell].") - -/obj/item/organ/internal/cell/on_add_effects() - . = ..() - // This is very ghetto way of rebooting an IPC. TODO better way. - if(owner && owner.stat == DEAD) - owner.set_stat(CONSCIOUS) - owner.visible_message("\The [owner] twitches visibly!") - -/obj/item/organ/internal/cell/listen() - if(get_charge()) - return "faint hum of the power bank" - -// Used for an MMI or posibrain being installed into a human. -/obj/item/organ/internal/mmi_holder - name = "brain interface" - icon_state = "mmi-empty" - organ_tag = BP_BRAIN - parent_organ = BP_HEAD - organ_properties = ORGAN_PROP_PROSTHETIC //triggers robotization on init - scale_max_damage_to_species_health = FALSE - var/obj/item/mmi/stored_mmi - var/datum/mind/persistantMind //Mind that the organ will hold on to after being removed, used for transfer_and_delete - var/ownerckey // used in the event the owner is out of body - -/obj/item/organ/internal/mmi_holder/Destroy() - stored_mmi = null - persistantMind = null - return ..() - -/obj/item/organ/internal/mmi_holder/do_install(mob/living/carbon/human/target, obj/item/organ/external/affected, in_place) - if(status & ORGAN_CUT_AWAY || !(. = ..())) - return - - if(!stored_mmi) - stored_mmi = new(src) - update_from_mmi() - persistantMind = owner.mind - ownerckey = owner.ckey - -/obj/item/organ/internal/mmi_holder/proc/update_from_mmi() - - if(!stored_mmi.brainmob) - stored_mmi.brainmob = new(stored_mmi) - stored_mmi.brainobj = new(stored_mmi) - stored_mmi.brainmob.container = stored_mmi - stored_mmi.brainmob.real_name = owner.real_name - stored_mmi.brainmob.SetName(stored_mmi.brainmob.real_name) - stored_mmi.SetName("[initial(stored_mmi.name)] ([owner.real_name])") - - if(!owner) return - - name = stored_mmi.name - desc = stored_mmi.desc - icon = stored_mmi.icon - - stored_mmi.icon_state = "mmi-full" - icon_state = stored_mmi.icon_state - - if(owner && owner.stat == DEAD) - owner.set_stat(CONSCIOUS) - owner.switch_from_dead_to_living_mob_list() - owner.visible_message("\The [owner] twitches visibly!") - -/obj/item/organ/internal/mmi_holder/on_remove_effects(mob/living/last_owner) - if(last_owner && last_owner.mind) - persistantMind = last_owner.mind - if(last_owner.ckey) - ownerckey = last_owner.ckey - . = ..() - -/obj/item/organ/internal/mmi_holder/proc/transfer_and_delete() - if(stored_mmi) - . = stored_mmi - stored_mmi.forceMove(src.loc) - if(persistantMind) - persistantMind.transfer_to(stored_mmi.brainmob) - else - var/response = input(find_dead_player(ownerckey, 1), "Your [initial(stored_mmi.name)] has been removed from your body. Do you wish to return to life?", "Robotic Rebirth") as anything in list("Yes", "No") - if(response == "Yes") - persistantMind.transfer_to(stored_mmi.brainmob) - qdel(src) - -//Since the mmi_holder is an horrible hacky pos we turn it into a mmi on drop, since it shouldn't exist outside a mob -/obj/item/organ/internal/mmi_holder/dropInto(atom/destination) - . = ..() - if (!QDELETED(src)) - transfer_and_delete() diff --git a/code/modules/organs/organ.dm b/code/modules/organs/organ.dm index 98cc4129f70..f2968c38634 100644 --- a/code/modules/organs/organ.dm +++ b/code/modules/organs/organ.dm @@ -202,7 +202,7 @@ //dead already, no need for more processing if(status & ORGAN_DEAD) return - // Don't process if we're in a freezer, an MMI or a stasis bag.or a freezer or something I dunno + // Don't process if we're in a freezer, an interface or a stasis bag. if(is_preserved()) return //Process infections @@ -251,8 +251,7 @@ if(istype(loc,/obj/item/organ)) var/obj/item/organ/O = loc return O.is_preserved() - else - return (istype(loc,/obj/item/mmi) || istype(loc,/obj/structure/closet/body_bag/cryobag) || istype(loc,/obj/structure/closet/crate/freezer) || istype(loc,/obj/item/storage/box/freezer)) + return (istype(loc,/obj/item/organ/internal/brain_interface) || istype(loc,/obj/structure/closet/body_bag/cryobag) || istype(loc,/obj/structure/closet/crate/freezer) || istype(loc,/obj/item/storage/box/freezer)) /obj/item/organ/examine(mob/user) . = ..(user) @@ -544,6 +543,8 @@ var/global/list/ailment_reference_cache = list() /obj/item/organ/proc/do_install(var/mob/living/carbon/human/target, var/obj/item/organ/external/affected, var/in_place = FALSE, var/update_icon = TRUE, var/detached = FALSE) //Make sure to force the flag accordingly set_detached(detached) + if(QDELETED(src)) + return owner = target vital_to_owner = null @@ -579,7 +580,8 @@ var/global/list/ailment_reference_cache = list() else owner = null vital_to_owner = null - return src + if(!QDELETED(src)) + return src //Events handling for checks and effects that should happen when removing the organ through interactions. Called by the owner mob. /obj/item/organ/proc/on_remove_effects(var/mob/living/last_owner) diff --git a/code/modules/projectiles/projectile/change.dm b/code/modules/projectiles/projectile/change.dm index 30f3b48d403..a88e4070f8e 100644 --- a/code/modules/projectiles/projectile/change.dm +++ b/code/modules/projectiles/projectile/change.dm @@ -25,8 +25,8 @@ var/mob/living/silicon/robot/R = new(get_turf(M)) R.set_gender(M.get_gender()) R.job = ASSIGNMENT_ROBOT - R.mmi = new /obj/item/mmi(R) - R.mmi.transfer_identity(M) + R.central_processor = new /obj/item/organ/internal/brain_interface(R) + transfer_key_from_mob_to_mob(M, R) return R if(get_species_by_key(choice)) @@ -53,12 +53,8 @@ for (var/spell/S in M.mind.learned_spells) new_mob.add_spell(new S.type) new_mob.a_intent = "hurt" - if(M.mind) - M.mind.transfer_to(new_mob) - else - new_mob.key = M.key + transfer_key_from_mob_to_mob(M, new_mob) to_chat(new_mob, "Your form morphs into that of \a [choice].") - qdel(M) else to_chat(M, "Your form morphs into that of \a [choice].") diff --git a/code/modules/surgery/limb_reattach.dm b/code/modules/surgery/limb_reattach.dm index 71a67326c50..d51f538ac2a 100644 --- a/code/modules/surgery/limb_reattach.dm +++ b/code/modules/surgery/limb_reattach.dm @@ -128,7 +128,7 @@ name = "Connect limb" description = "This procedure is used to reconnect a replaced severed limb." allowed_tools = list( - TOOL_HEMOSTAT = 100, + TOOL_SUTURES = 100, TOOL_CABLECOIL = 75 ) can_infect = 1 @@ -145,15 +145,15 @@ /decl/surgery_step/limb/connect/begin_step(mob/user, mob/living/target, target_zone, obj/item/tool) var/obj/item/organ/external/E = GET_EXTERNAL_ORGAN(target, target_zone) - user.visible_message("[user] starts connecting tendons and muscles in [target]'s [E.amputation_point] with [tool].", \ - "You start connecting tendons and muscle in [target]'s [E.amputation_point].") + user.visible_message("[user] starts reattaching tendons and muscles in [target]'s [E.amputation_point] with [tool].", \ + "You start reattaching tendons and muscle in [target]'s [E.amputation_point].") ..() /decl/surgery_step/limb/connect/end_step(mob/living/user, mob/living/target, target_zone, obj/item/tool) var/obj/item/organ/external/E = GET_EXTERNAL_ORGAN(target, target_zone) var/obj/item/organ/external/P = GET_EXTERNAL_ORGAN(target, E.parent_organ) - user.visible_message("[user] has connected tendons and muscles in [target]'s [E.amputation_point] with [tool].", \ - "You have connected tendons and muscles in [target]'s [E.amputation_point] with [tool].") + user.visible_message("[user] has reattached tendons and muscles in [target]'s [E.amputation_point] with [tool].", \ + "You have reattached tendons and muscles in [target]'s [E.amputation_point] with [tool].") //This time we call add_organ but we want it to install in a non detached state target.add_organ(E, P, FALSE, TRUE, FALSE) diff --git a/code/modules/surgery/organs_internal.dm b/code/modules/surgery/organs_internal.dm index 5ee4834aa70..886f09cbe8a 100644 --- a/code/modules/surgery/organs_internal.dm +++ b/code/modules/surgery/organs_internal.dm @@ -198,13 +198,6 @@ //Now call remove again with detach = FALSE so we fully remove it target.remove_organ(O, TRUE, FALSE) - // Just in case somehow the organ we're extracting from an organic is an MMI - if(istype(O, /obj/item/organ/internal/mmi_holder)) - var/obj/item/organ/internal/mmi_holder/brain = O - brain.transfer_and_delete() - log_warning("Organ removal surgery on '[target]' returned a mmi_holder '[O]' instead of a mmi!!") - ..() - /decl/surgery_step/internal/remove_organ/fail_step(mob/living/user, mob/living/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = GET_EXTERNAL_ORGAN(target, target_zone) user.visible_message("[user]'s hand slips, damaging [target]'s [affected.name] with \the [tool]!", \ @@ -251,9 +244,7 @@ CRASH("Target ([target]) of surgery [type] has no bodytype!") else var/decl/pronouns/G = O.get_pronouns() - if(O.organ_tag == BP_POSIBRAIN && !target.should_have_organ(BP_POSIBRAIN)) - to_chat(user, SPAN_WARNING("There's no place in [target] to fit \the [O.organ_tag].")) - else if(O.damage > (O.max_damage * 0.75)) + if(O.damage > (O.max_damage * 0.75)) to_chat(user, SPAN_WARNING("\The [O.name] [G.is] in no state to be transplanted.")) else if(O.w_class > affected.cavity_max_w_class) to_chat(user, SPAN_WARNING("\The [O.name] [G.is] too big for [affected.cavity_name] cavity!")) diff --git a/code/modules/surgery/robotics.dm b/code/modules/surgery/robotics.dm index e972107b4b2..e4c55f59a1d 100644 --- a/code/modules/surgery/robotics.dm +++ b/code/modules/surgery/robotics.dm @@ -1,4 +1,4 @@ -//Procedures in this file: Robotic surgery steps, organ removal, replacement. MMI insertion, synthetic organ repair. +//Procedures in this file: Robotic surgery steps, organ removal, replacement, synthetic organ repair. ////////////////////////////////////////////////////////////////// // ROBOTIC SURGERY // ////////////////////////////////////////////////////////////////// @@ -494,73 +494,6 @@ "Your hand slips, unseating \the [tool].") ..() -////////////////////////////////////////////////////////////////// -// mmi installation surgery step -////////////////////////////////////////////////////////////////// -/decl/surgery_step/robotics/install_mmi - name = "Install MMI" - description = "This procedure installs an MMI within a prosthetic organ." - allowed_tools = list( - /obj/item/mmi = 100 - ) - min_duration = 60 - max_duration = 80 - surgery_candidate_flags = SURGERY_NO_CRYSTAL | SURGERY_NO_FLESH | SURGERY_NEEDS_ENCASEMENT - -/decl/surgery_step/robotics/install_mmi/pre_surgery_step(mob/living/user, mob/living/target, target_zone, obj/item/tool) - var/obj/item/mmi/M = tool - var/obj/item/organ/external/affected = GET_EXTERNAL_ORGAN(target, target_zone) - if(affected && istype(M)) - if(!M.brainmob || !M.brainmob.client || !M.brainmob.ckey || M.brainmob.stat >= DEAD) - to_chat(user, SPAN_WARNING("That brain is not usable.")) - else if(BP_IS_CRYSTAL(affected)) - to_chat(user, SPAN_WARNING("The crystalline interior of \the [affected] is incompatible with \the [M].")) - else if(!target.isSynthetic()) - to_chat(user, SPAN_WARNING("You cannot install a computer brain into a meat body.")) - else if(!target.should_have_organ(BP_BRAIN)) - var/decl/species/species = target.get_species() - to_chat(user, SPAN_WARNING("You're pretty sure [species ? "[species.name_plural] don't" : "\the [target] doesn't"] normally have a brain.")) - else if(target.has_brain()) - to_chat(user, SPAN_WARNING("Your subject already has a brain.")) - else - return TRUE - return FALSE - -/decl/surgery_step/robotics/install_mmi/assess_bodypart(mob/living/user, mob/living/target, target_zone, obj/item/tool) - var/obj/item/organ/external/affected = ..() - if(affected && target_zone == BP_HEAD) - return affected - -/decl/surgery_step/robotics/install_mmi/begin_step(mob/user, mob/living/target, target_zone, obj/item/tool) - var/obj/item/organ/external/affected = GET_EXTERNAL_ORGAN(target, target_zone) - user.visible_message("[user] starts installing \the [tool] into [target]'s [affected.name].", \ - "You start installing \the [tool] into [target]'s [affected.name].") - ..() - -/decl/surgery_step/robotics/install_mmi/end_step(mob/living/user, mob/living/target, target_zone, obj/item/tool) - if(!user.try_unequip(tool) || !ishuman(target)) - return - var/obj/item/organ/external/affected = GET_EXTERNAL_ORGAN(target, target_zone) - user.visible_message("[user] has installed \the [tool] into [target]'s [affected.name].", \ - "You have installed \the [tool] into [target]'s [affected.name].") - - var/obj/item/mmi/M = tool - var/obj/item/organ/internal/mmi_holder/holder = new(target, 1) - var/mob/living/carbon/human/H = target - H.add_organ(holder, affected, TRUE) - tool.forceMove(holder) - holder.stored_mmi = tool - holder.update_from_mmi() - - if(M.brainmob && M.brainmob.mind) - M.brainmob.mind.transfer_to(target) - ..() - -/decl/surgery_step/robotics/install_mmi/fail_step(mob/living/user, mob/living/target, target_zone, obj/item/tool) - user.visible_message("[user]'s hand slips.", \ - "Your hand slips.") - ..() - /decl/surgery_step/internal/remove_organ/robotic name = "Remove robotic component" description = "This procedure removes a robotic component." @@ -573,54 +506,3 @@ can_infect = 0 robotic_surgery = TRUE surgery_candidate_flags = SURGERY_NO_CRYSTAL | SURGERY_NO_FLESH | SURGERY_NEEDS_ENCASEMENT - -/decl/surgery_step/remove_mmi - name = "Remove MMI" - description = "This procedure removes an MMI from a prosthetic organ." - min_duration = 60 - max_duration = 80 - allowed_tools = list( - TOOL_HEMOSTAT = 100, - TOOL_WIRECUTTERS = 75, - ) - can_infect = 0 - surgery_candidate_flags = SURGERY_NO_CRYSTAL | SURGERY_NO_FLESH | SURGERY_NEEDS_ENCASEMENT - -/decl/surgery_step/remove_mmi/get_skill_reqs(mob/living/user, mob/living/target, obj/item/tool) - return SURGERY_SKILLS_ROBOTIC - -/decl/surgery_step/remove_mmi/assess_bodypart(mob/living/user, mob/living/target, target_zone, obj/item/tool) - var/obj/item/organ/external/affected = ..() - if(affected && (locate(/obj/item/mmi) in affected.implants)) - return affected - -/decl/surgery_step/remove_mmi/begin_step(mob/user, mob/living/target, target_zone, obj/item/tool) - var/obj/item/organ/external/affected = GET_EXTERNAL_ORGAN(target, target_zone) - user.visible_message( \ - "\The [user] starts poking around inside [target]'s [affected.name] with \the [tool].", \ - "You start poking around inside [target]'s [affected.name] with \the [tool]." ) - target.custom_pain("The pain in your [affected.name] is living hell!",1,affecting = affected) - ..() - -/decl/surgery_step/remove_mmi/end_step(mob/living/user, mob/living/target, target_zone, obj/item/tool) - var/obj/item/organ/external/affected = GET_EXTERNAL_ORGAN(target, target_zone) - if(affected) - var/obj/item/mmi/mmi = locate() in affected.implants - if(affected && mmi) - user.visible_message( \ - SPAN_NOTICE("\The [user] removes \the [mmi] from \the [target]'s [affected.name] with \the [tool]."), \ - SPAN_NOTICE("You remove \the [mmi] from \the [target]'s [affected.name] with \the [tool].")) - target.remove_implant(mmi, TRUE, affected) - else - user.visible_message( \ - SPAN_NOTICE("\The [user] could not find anything inside [target]'s [affected.name]."), \ - SPAN_NOTICE("You could not find anything inside [target]'s [affected.name].")) - ..() - -/decl/surgery_step/remove_mmi/fail_step(mob/living/user, mob/living/target, target_zone, obj/item/tool) - var/obj/item/organ/external/affected = GET_EXTERNAL_ORGAN(target, target_zone) - user.visible_message( \ - SPAN_WARNING("\The [user]'s hand slips, damaging \the [target]'s [affected.name] with \the [tool]!"), \ - SPAN_WARNING("Your hand slips, damaging \the [target]'s [affected.name] with \the [tool]!")) - affected.take_external_damage(3, 0, used_weapon = tool) - ..() diff --git a/icons/obj/assemblies.dmi b/icons/obj/assemblies.dmi index 93f5bb50f6fd4b12251af6bd381d67b32f4402c9..26ee3a4c8d6676c2ca73833e446187e65f5a2583 100644 GIT binary patch literal 16447 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 G2K`^)reOR4 literal 24039 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 diff --git a/icons/obj/items/brain_interface_organic.dmi b/icons/obj/items/brain_interface_organic.dmi new file mode 100644 index 0000000000000000000000000000000000000000..940f246aecd37b690e4bd309b7de6cd28b32fa62 GIT binary patch literal 3568 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 oq#>o~+ICq1T1hf+ Date: Wed, 18 Oct 2023 15:31:57 +1100 Subject: [PATCH 2/3] Getting brains rewrite working. --- .../brain_interface/_brain_interface.dm | 3 ++ code/modules/organs/external/_external.dm | 15 ++------ code/modules/organs/internal/_internal.dm | 34 +++---------------- code/modules/organs/organ.dm | 12 +++++-- code/modules/surgery/organs_internal.dm | 6 ++-- 5 files changed, 22 insertions(+), 48 deletions(-) diff --git a/code/modules/brain_interface/_brain_interface.dm b/code/modules/brain_interface/_brain_interface.dm index 406ea961d40..c5c6accb9ba 100644 --- a/code/modules/brain_interface/_brain_interface.dm +++ b/code/modules/brain_interface/_brain_interface.dm @@ -22,6 +22,9 @@ var/locked = FALSE var/obj/item/organ/internal/brain/holding_brain = /obj/item/organ/internal/brain +/obj/item/organ/internal/brain_interface/is_preserved() + return TRUE + /obj/item/organ/internal/brain_interface/empty holding_brain = null diff --git a/code/modules/organs/external/_external.dm b/code/modules/organs/external/_external.dm index cdeac308694..9f6723cb5c0 100644 --- a/code/modules/organs/external/_external.dm +++ b/code/modules/organs/external/_external.dm @@ -456,11 +456,8 @@ // //If we contain any child organs add them to the owner // - for(var/obj/item/organ/organ in implants) - owner.add_organ(organ, src, in_place, update_icon, detached) - - for(var/obj/item/organ/external/organ in children) - owner.add_organ(organ, src, in_place, update_icon, detached) + for(var/obj/item/organ/organ in (implants|children|internal_organs)) + owner.add_organ(organ, src, in_place, update_icon, FALSE) // //Add any existing organs in the owner that have us as parent @@ -521,10 +518,6 @@ if(!in_place) parent.update_wounds() - // Notify our children. - for(var/obj/item/organ/internal/I in internal_organs) - I.on_holding_organ_installed(src) - /obj/item/organ/external/proc/drop_equipped_clothing() if(!owner) return @@ -1417,10 +1410,6 @@ Note that amputating the affected organ does in fact remove the infection from t LAZYREMOVE(parent.children, src) parent = null - // Notify our children. - for(var/obj/item/organ/internal/I in internal_organs) - I.on_holding_organ_uninstalled(src) - /obj/item/organ/external/on_remove_effects(mob/living/last_owner) . = ..() drop_equipped_clothing() diff --git a/code/modules/organs/internal/_internal.dm b/code/modules/organs/internal/_internal.dm index 0c8e936d4a5..28b6da02c03 100644 --- a/code/modules/organs/internal/_internal.dm +++ b/code/modules/organs/internal/_internal.dm @@ -287,38 +287,12 @@ /obj/item/organ/internal/do_install(mob/living/carbon/human/target, obj/item/organ/external/affected, in_place, update_icon, detached) . = ..() if(transfer_brainmob_with_organ && istype(owner)) - var/mob/living/brainmob = get_brainmob(create_if_missing = TRUE) - if(brainmob) - if(status & ORGAN_CUT_AWAY) - transfer_key_to_brainmob(owner, update_brainmob = TRUE) - else - transfer_key_from_mob_to_mob(brainmob, owner) + var/mob/living/brainmob = get_brainmob(create_if_missing = FALSE) + if(brainmob?.key) + transfer_key_from_mob_to_mob(brainmob, owner) /obj/item/organ/internal/do_uninstall(in_place, detach, ignore_children, update_icon) var/mob/living/victim = owner // cleared in parent proc . = ..() if(transfer_brainmob_with_organ && istype(victim)) - var/mob/living/brainmob = get_brainmob(create_if_missing = TRUE) - if(brainmob) - if(status & ORGAN_CUT_AWAY) - transfer_key_to_brainmob(victim, update_brainmob = TRUE) - else - transfer_key_from_mob_to_mob(brainmob, victim) - -/obj/item/organ/internal/proc/on_holding_organ_installed(var/obj/item/organ/external/holding) - if(istype(holding) && transfer_brainmob_with_organ && istype(owner)) - var/mob/living/brainmob = get_brainmob(create_if_missing = TRUE) - if(brainmob) - if(holding.status & ORGAN_CUT_AWAY) - transfer_key_to_brainmob(owner, update_brainmob = TRUE) - else - transfer_key_from_mob_to_mob(brainmob, owner) - -/obj/item/organ/internal/proc/on_holding_organ_uninstalled(var/obj/item/organ/external/holding) - if(istype(holding) && transfer_brainmob_with_organ && istype(owner)) - var/mob/living/brainmob = get_brainmob(create_if_missing = TRUE) - if(brainmob) - if(holding.status & ORGAN_CUT_AWAY) - transfer_key_to_brainmob(owner, update_brainmob = TRUE) - else - transfer_key_from_mob_to_mob(brainmob, owner) + transfer_key_to_brainmob(victim, update_brainmob = TRUE) diff --git a/code/modules/organs/organ.dm b/code/modules/organs/organ.dm index f2968c38634..5a685b7c59d 100644 --- a/code/modules/organs/organ.dm +++ b/code/modules/organs/organ.dm @@ -248,10 +248,18 @@ ailment.was_treated_by_chem_effect() /obj/item/organ/proc/is_preserved() - if(istype(loc,/obj/item/organ)) + if(istype(loc, /obj/item/organ)) var/obj/item/organ/O = loc return O.is_preserved() - return (istype(loc,/obj/item/organ/internal/brain_interface) || istype(loc,/obj/structure/closet/body_bag/cryobag) || istype(loc,/obj/structure/closet/crate/freezer) || istype(loc,/obj/item/storage/box/freezer)) + var/static/list/preserved_types = list( + /obj/item/storage/box/freezer, + /obj/structure/closet/crate/freezer, + /obj/structure/closet/body_bag/cryobag + ) + for(var/preserved_type in preserved_types) + if(istype(loc, preserved_type)) + return TRUE + return FALSE /obj/item/organ/examine(mob/user) . = ..(user) diff --git a/code/modules/surgery/organs_internal.dm b/code/modules/surgery/organs_internal.dm index 886f09cbe8a..d873e0406eb 100644 --- a/code/modules/surgery/organs_internal.dm +++ b/code/modules/surgery/organs_internal.dm @@ -190,13 +190,13 @@ /decl/surgery_step/internal/remove_organ/end_step(mob/living/user, mob/living/target, target_zone, obj/item/tool) user.visible_message("\The [user] has removed \the [target]'s [LAZYACCESS(global.surgeries_in_progress["\ref[target]"], target_zone)] with \the [tool].", \ "You have removed \the [target]'s [LAZYACCESS(global.surgeries_in_progress["\ref[target]"], target_zone)] with \the [tool].") - // Extract the organ! var/obj/item/organ/O = LAZYACCESS(global.surgeries_in_progress["\ref[target]"], target_zone) var/obj/item/organ/external/affected = GET_EXTERNAL_ORGAN(target, target_zone) if(istype(O) && istype(affected)) //Now call remove again with detach = FALSE so we fully remove it target.remove_organ(O, TRUE, FALSE) + ..() /decl/surgery_step/internal/remove_organ/fail_step(mob/living/user, mob/living/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = GET_EXTERNAL_ORGAN(target, target_zone) @@ -316,8 +316,8 @@ var/list/attachable_organs var/obj/item/organ/external/affected = GET_EXTERNAL_ORGAN(target, target_zone) - for(var/obj/item/organ/I in affected.implants) - if(I && (I.status & ORGAN_CUT_AWAY)) + for(var/obj/item/organ/I in (affected.implants|affected.internal_organs)) + if(I.status & ORGAN_CUT_AWAY) var/image/radial_button = image(icon = I.icon, icon_state = I.icon_state) radial_button.name = "Attach \the [I.name]" LAZYSET(attachable_organs, I, radial_button) From 4f96b4cc0bfe0d40577ba10fe0fc2c7bfdfe20a6 Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Wed, 18 Oct 2023 15:54:20 +1100 Subject: [PATCH 3/3] Making utility frames depend on shackles. --- code/modules/brain_interface/_brain_interface.dm | 2 +- code/modules/brain_interface/interface_radio.dm | 2 +- code/modules/mob/living/brain/brain.dm | 6 ++---- code/modules/organs/internal/brain_computer.dm | 4 ++-- code/modules/organs/prosthetics/prosthetics_manufacturer.dm | 2 +- mods/content/shackles/laws_pref.dm | 5 ----- mods/species/utility_frames/_utility_frames.dme | 1 + mods/species/utility_frames/species.dm | 5 ----- 8 files changed, 8 insertions(+), 19 deletions(-) diff --git a/code/modules/brain_interface/_brain_interface.dm b/code/modules/brain_interface/_brain_interface.dm index c5c6accb9ba..4b96d8f9f24 100644 --- a/code/modules/brain_interface/_brain_interface.dm +++ b/code/modules/brain_interface/_brain_interface.dm @@ -4,7 +4,7 @@ desc = "A complex life support shell that interfaces between a brain and an electronic device." organ_tag = BP_BRAIN parent_organ = BP_HEAD - origin_tech = "{'biotech':3}" + origin_tech = @'{"biotech":3}' icon = 'icons/obj/items/brain_interface_organic.dmi' icon_state = ICON_STATE_WORLD req_access = list(access_robotics) diff --git a/code/modules/brain_interface/interface_radio.dm b/code/modules/brain_interface/interface_radio.dm index 3ba4ffae135..a793f944ce7 100644 --- a/code/modules/brain_interface/interface_radio.dm +++ b/code/modules/brain_interface/interface_radio.dm @@ -1,7 +1,7 @@ /obj/item/organ/internal/brain_interface/radio_enabled name = "radio-enabled neural interface" desc = "A complex life support shell that interfaces between a brain and an electronic device. This one comes with a built-in radio." - origin_tech = "{'biotech':4}" + origin_tech = @'{"biotech":4}' var/VAR_PRIVATE/weakref/_radio /obj/item/organ/internal/brain_interface/radio_enabled/empty diff --git a/code/modules/mob/living/brain/brain.dm b/code/modules/mob/living/brain/brain.dm index 1debc374281..faa6edb9b7f 100644 --- a/code/modules/mob/living/brain/brain.dm +++ b/code/modules/mob/living/brain/brain.dm @@ -25,8 +25,6 @@ /mob/living/brain/handle_regular_status_updates() . = ..() - if(health <= 0 && stat != DEAD) - death() if(emp_damage || stat == DEAD || !is_in_interface()) SET_STATUS_MAX(src, STAT_SILENCE, 2) @@ -101,12 +99,12 @@ emp_damage += rand(0,10) emp_damage = clamp(emp_damage, 0, max_emp_damage) -/mob/living/brain/Life() +/mob/living/brain/handle_regular_status_updates() // Status & health update, are we dead or alive etc. . = ..() if(stat == DEAD || !isSynthetic()) emp_damage = 0 return - if(!emp_damage) + if(emp_damage <= 0) return emp_damage -= 1 var/msg_threshold = clamp(CEILING(emp_damage / (max_emp_damage / length(emp_reboot_strings))), 1, length(emp_reboot_strings)) diff --git a/code/modules/organs/internal/brain_computer.dm b/code/modules/organs/internal/brain_computer.dm index 46fa79eb974..3d298b04b30 100644 --- a/code/modules/organs/internal/brain_computer.dm +++ b/code/modules/organs/internal/brain_computer.dm @@ -3,7 +3,7 @@ name = "computer intelligence core" desc = "The pinnacle of artifical intelligence technology, conveniently stored in a fist-sized cube." icon = 'icons/obj/items/brain_interface_robotic.dmi' - origin_tech = "{'engineering':4,'materials':4,'wormholes':2,'programming':4}" + origin_tech = @'{"engineering":4,"materials":4,"wormholes":2,"programming":4}' material = /decl/material/solid/metal/steel matter = list( /decl/material/solid/glass = MATTER_AMOUNT_REINFORCEMENT, @@ -51,7 +51,7 @@ update_icon() var/decl/ghosttrap/G = GET_DECL(/decl/ghosttrap/machine_intelligence) G.request_player(brainmob, "Someone is requesting a personality for a [name].", 1 MINUTE) - addtimer(CALLBACK(src, .proc/reset_search), 1 MINUTE) + addtimer(CALLBACK(src, PROC_REF(reset_search)), 1 MINUTE) return TRUE . = ..() diff --git a/code/modules/organs/prosthetics/prosthetics_manufacturer.dm b/code/modules/organs/prosthetics/prosthetics_manufacturer.dm index 941a87db6ff..588098e60f6 100644 --- a/code/modules/organs/prosthetics/prosthetics_manufacturer.dm +++ b/code/modules/organs/prosthetics/prosthetics_manufacturer.dm @@ -14,7 +14,7 @@ 'sound/foley/metal1.ogg' ) has_organ = list( - BP_BRAIN = /obj/item/organ/internal/mmi_holder, + BP_BRAIN = /obj/item/organ/internal/brain_interface, BP_EYES = /obj/item/organ/internal/eyes, BP_CELL = /obj/item/organ/internal/cell ) diff --git a/mods/content/shackles/laws_pref.dm b/mods/content/shackles/laws_pref.dm index ad940910434..77c3b8baffa 100644 --- a/mods/content/shackles/laws_pref.dm +++ b/mods/content/shackles/laws_pref.dm @@ -108,8 +108,3 @@ /decl/bodytype var/can_be_shackled - -#ifdef MODPACK_UTILITY_FRAMES -/decl/species/utility_frame - can_be_shackled = TRUE -#endif diff --git a/mods/species/utility_frames/_utility_frames.dme b/mods/species/utility_frames/_utility_frames.dme index 776d098bb5d..728a6e05d86 100644 --- a/mods/species/utility_frames/_utility_frames.dme +++ b/mods/species/utility_frames/_utility_frames.dme @@ -1,5 +1,6 @@ #ifndef MODPACK_UTILITY_FRAMES #define MODPACK_UTILITY_FRAMES +#include "../../content/shackles/_shackles.dme" #include "_utility_frames.dm" #include "species.dm" #include "species_bodytypes.dm" diff --git a/mods/species/utility_frames/species.dm b/mods/species/utility_frames/species.dm index 10b6e78011b..5e3f44eacf4 100644 --- a/mods/species/utility_frames/species.dm +++ b/mods/species/utility_frames/species.dm @@ -57,8 +57,3 @@ /decl/species/utility_frame/disfigure_msg(var/mob/living/carbon/human/H) . = SPAN_DANGER("The faceplate is dented and cracked!\n") - -#ifdef MODPACK_SHACKLES -/decl/species/utility_frame - can_be_shackled = TRUE -#endif