diff --git a/code/__DEFINES/antagonists.dm b/code/__DEFINES/antagonists.dm index 00751f2cab..3024d9f4fe 100644 --- a/code/__DEFINES/antagonists.dm +++ b/code/__DEFINES/antagonists.dm @@ -75,6 +75,9 @@ GLOBAL_LIST_EMPTY(living_heart_cache) //A list of all living hearts in existance #define IS_HERETIC(mob) (mob.mind?.has_antag_datum(/datum/antagonist/heretic)) #define IS_HERETIC_MONSTER(mob) (mob.mind?.has_antag_datum(/datum/antagonist/heretic_monster)) +/// Checks if the given mob is a malf ai. +#define IS_MALF_AI(mob) (isAI(mob) && mob?.mind?.has_antag_datum(/datum/antagonist/traitor)) + #define PATH_SIDE "Side" #define PATH_ASH "Ash" diff --git a/code/__DEFINES/logging.dm b/code/__DEFINES/logging.dm index 96201d71c2..84d6d0ae21 100644 --- a/code/__DEFINES/logging.dm +++ b/code/__DEFINES/logging.dm @@ -1,25 +1,26 @@ //Investigate logging defines -#define INVESTIGATE_ATMOS "atmos" -#define INVESTIGATE_BOTANY "botany" -#define INVESTIGATE_CARGO "cargo" -#define INVESTIGATE_EXPERIMENTOR "experimentor" -#define INVESTIGATE_GRAVITY "gravity" -#define INVESTIGATE_RECORDS "records" -#define INVESTIGATE_SINGULO "singulo" -#define INVESTIGATE_SUPERMATTER "supermatter" -#define INVESTIGATE_TELESCI "telesci" -#define INVESTIGATE_WIRES "wires" -#define INVESTIGATE_PORTAL "portals" -#define INVESTIGATE_RESEARCH "research" -#define INVESTIGATE_HALLUCINATIONS "hallucinations" -#define INVESTIGATE_RADIATION "radiation" -#define INVESTIGATE_EXONET "exonet" -#define INVESTIGATE_NANITES "nanites" -#define INVESTIGATE_CIRCUIT "circuit" -#define INVESTIGATE_FERMICHEM "fermichem" -#define INVESTIGATE_RCD "rcd" -#define INVESTIGATE_CRYOGENICS "cryogenics" -#define INVESTIGATE_GHOST "ghost" +#define INVESTIGATE_ATMOS "atmos" +#define INVESTIGATE_BOTANY "botany" +#define INVESTIGATE_CARGO "cargo" +#define INVESTIGATE_CIRCUIT "circuit" +#define INVESTIGATE_CRYOGENICS "cryogenics" +#define INVESTIGATE_DEATHS "deaths" +#define INVESTIGATE_EXONET "exonet" +#define INVESTIGATE_EXPERIMENTOR "experimentor" +#define INVESTIGATE_FERMICHEM "fermichem" +#define INVESTIGATE_GHOST "ghost" +#define INVESTIGATE_GRAVITY "gravity" +#define INVESTIGATE_HALLUCINATIONS "hallucinations" +#define INVESTIGATE_NANITES "nanites" +#define INVESTIGATE_PORTAL "portals" +#define INVESTIGATE_RADIATION "radiation" +#define INVESTIGATE_RCD "rcd" +#define INVESTIGATE_RECORDS "records" +#define INVESTIGATE_RESEARCH "research" +#define INVESTIGATE_SINGULO "singulo" +#define INVESTIGATE_SUPERMATTER "supermatter" +#define INVESTIGATE_TELESCI "telesci" +#define INVESTIGATE_WIRES "wires" // Logging types for log_message() #define LOG_ATTACK (1 << 0) diff --git a/code/__HELPERS/_lists.dm b/code/__HELPERS/_lists.dm index 5b457dd9cc..d7f689d7a6 100644 --- a/code/__HELPERS/_lists.dm +++ b/code/__HELPERS/_lists.dm @@ -495,10 +495,10 @@ return sortTim(L, order >= 0 ? /proc/cmp_records_asc : /proc/cmp_records_dsc) //any value in a list -/proc/sortList(list/L, cmp=/proc/cmp_text_asc) +/proc/sort_list(list/L, cmp=/proc/cmp_text_asc) return sortTim(L.Copy(), cmp) -//uses sortList() but uses the var's name specifically. This should probably be using mergeAtom() instead +//uses sort_list() but uses the var's name specifically. This should probably be using mergeAtom() instead /proc/sortNames(list/L, order=1) return sortTim(L.Copy(), order >= 0 ? /proc/cmp_name_asc : /proc/cmp_name_dsc) diff --git a/code/__HELPERS/type_processing.dm b/code/__HELPERS/type_processing.dm index ac7e5cab45..2b08b6d586 100644 --- a/code/__HELPERS/type_processing.dm +++ b/code/__HELPERS/type_processing.dm @@ -47,7 +47,7 @@ /proc/get_fancy_list_of_datum_types() var/static/list/pre_generated_list if (!pre_generated_list) //init - pre_generated_list = make_types_fancy(sortList(typesof(/datum) - typesof(/atom))) + pre_generated_list = make_types_fancy(sort_list(typesof(/datum) - typesof(/atom))) return pre_generated_list diff --git a/code/controllers/subsystem/dcs.dm b/code/controllers/subsystem/dcs.dm index 6be5a4bdfa..0d5b59bfa9 100644 --- a/code/controllers/subsystem/dcs.dm +++ b/code/controllers/subsystem/dcs.dm @@ -55,6 +55,6 @@ PROCESSING_SUBSYSTEM_DEF(dcs) fullid += "[key]" if(length(named_arguments)) - named_arguments = sortList(named_arguments) + named_arguments = sort_list(named_arguments) fullid += named_arguments return list2params(fullid) diff --git a/code/controllers/subsystem/mapping.dm b/code/controllers/subsystem/mapping.dm index a6c306e4d4..d8d0849ed7 100644 --- a/code/controllers/subsystem/mapping.dm +++ b/code/controllers/subsystem/mapping.dm @@ -423,7 +423,7 @@ GLOBAL_LIST_EMPTY(the_station_areas) banned += generateMapList("[global.config.directory]/iceruinblacklist.txt") banned += generateMapList("[global.config.directory]/stationruinblacklist.txt") - for(var/item in sortList(subtypesof(/datum/map_template/ruin), /proc/cmp_ruincost_priority)) + for(var/item in sort_list(subtypesof(/datum/map_template/ruin), /proc/cmp_ruincost_priority)) var/datum/map_template/ruin/ruin_type = item // screen out the abstract subtypes if(!initial(ruin_type.id)) diff --git a/code/controllers/subsystem/processing/quirks.dm b/code/controllers/subsystem/processing/quirks.dm index 582d2eb9dc..14c6c8aa26 100644 --- a/code/controllers/subsystem/processing/quirks.dm +++ b/code/controllers/subsystem/processing/quirks.dm @@ -22,7 +22,7 @@ PROCESSING_SUBSYSTEM_DEF(quirks) /datum/controller/subsystem/processing/quirks/proc/SetupQuirks() // Sort by Positive, Negative, Neutral; and then by name - var/list/quirk_list = sortList(subtypesof(/datum/quirk), /proc/cmp_quirk_asc) + var/list/quirk_list = sort_list(subtypesof(/datum/quirk), /proc/cmp_quirk_asc) for(var/V in quirk_list) var/datum/quirk/T = V diff --git a/code/controllers/subsystem/shuttle.dm b/code/controllers/subsystem/shuttle.dm index dbec272fe8..20d3ffd971 100644 --- a/code/controllers/subsystem/shuttle.dm +++ b/code/controllers/subsystem/shuttle.dm @@ -825,7 +825,7 @@ SUBSYSTEM_DEF(shuttle) templates[S.port_id]["templates"] += list(L) - data["templates_tabs"] = sortList(data["templates_tabs"]) + data["templates_tabs"] = sort_list(data["templates_tabs"]) data["existing_shuttle"] = null diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm index c75febedef..6f856f3edb 100755 --- a/code/controllers/subsystem/ticker.dm +++ b/code/controllers/subsystem/ticker.dm @@ -273,7 +273,7 @@ SUBSYSTEM_DEF(ticker) var/list/modes = new for (var/datum/game_mode/M in runnable_modes) modes += M.name - modes = sortList(modes) + modes = sort_list(modes) to_chat(world, "The gamemode is: secret!\nPossibilities: [english_list(modes)]") else mode.announce()*/ diff --git a/code/datums/diseases/advance/advance.dm b/code/datums/diseases/advance/advance.dm index 7e3ac29b2d..1ca07bf502 100644 --- a/code/datums/diseases/advance/advance.dm +++ b/code/datums/diseases/advance/advance.dm @@ -361,7 +361,7 @@ L += "[S.id]N" else L += S.id - L = sortList(L) // Sort the list so it doesn't matter which order the symptoms are in. + L = sort_list(L) // Sort the list so it doesn't matter which order the symptoms are in. var/result = jointext(L, ":") id = result return id diff --git a/code/datums/elements/object_reskinning.dm b/code/datums/elements/object_reskinning.dm index 87f5f13385..63e8a35d05 100644 --- a/code/datums/elements/object_reskinning.dm +++ b/code/datums/elements/object_reskinning.dm @@ -65,7 +65,7 @@ icon = to_reskin.unique_reskin[reskin_option]["icon"] ? to_reskin.unique_reskin[reskin_option]["icon"] : to_reskin.icon, icon_state = to_reskin.unique_reskin[reskin_option]["icon_state"] ? to_reskin.unique_reskin[reskin_option]["icon_state"] : to_reskin.icon_state) items += list("[reskin_option]" = item_image) - sortList(items) + sort_list(items) // Display to the user var/pick = show_radial_menu(user, to_reskin, items, custom_check = CALLBACK(src, .proc/check_reskin_menu, user, to_reskin), radius = 38, require_near = TRUE) diff --git a/code/datums/mind.dm b/code/datums/mind.dm index 9f6ccb742d..822da27923 100644 --- a/code/datums/mind.dm +++ b/code/datums/mind.dm @@ -67,6 +67,8 @@ var/datum/language_holder/language_holder var/unconvertable = FALSE var/late_joiner = FALSE + ///has this mind ever been an AI + var/has_ever_been_ai = FALSE var/force_escaped = FALSE // Set by Into The Sunset command of the shuttle manipulator var/list/learned_recipes //List of learned recipe TYPES. @@ -286,6 +288,13 @@ if(O) O.unlock_code = null +/// Remove the antagonists that should not persist when being borged +/datum/mind/proc/remove_antags_for_borging() + remove_antag_datum(/datum/antagonist/cult) + + var/datum/antagonist/rev/revolutionary = has_antag_datum(/datum/antagonist/rev) + revolutionary?.remove_revolutionary() + /datum/mind/proc/remove_all_antag() //For the Lazy amongst us. remove_changeling() remove_traitor() diff --git a/code/game/machinery/autolathe.dm b/code/game/machinery/autolathe.dm index 4f876b97e1..6c4542bfd1 100644 --- a/code/game/machinery/autolathe.dm +++ b/code/game/machinery/autolathe.dm @@ -210,7 +210,7 @@ if(materials.materials[i] > 0) list_to_show += i - used_material = tgui_input_list(usr, "Choose [used_material]", "Custom Material", sortList(list_to_show, /proc/cmp_typepaths_asc)) + used_material = tgui_input_list(usr, "Choose [used_material]", "Custom Material", sort_list(list_to_show, /proc/cmp_typepaths_asc)) if(isnull(used_material)) return //Didn't pick any material, so you can't build shit either. custom_materials[used_material] += amount_needed diff --git a/code/game/machinery/camera/tracking.dm b/code/game/machinery/camera/tracking.dm index bf1b84fe5d..157ccfe01f 100644 --- a/code/game/machinery/camera/tracking.dm +++ b/code/game/machinery/camera/tracking.dm @@ -53,7 +53,7 @@ else track.others[name] = L - var/list/targets = sortList(track.humans) + sortList(track.others) + var/list/targets = sort_list(track.humans) + sort_list(track.others) return targets diff --git a/code/game/machinery/computer/atmos_control.dm b/code/game/machinery/computer/atmos_control.dm index 7eb6c79eef..c8add9e690 100644 --- a/code/game/machinery/computer/atmos_control.dm +++ b/code/game/machinery/computer/atmos_control.dm @@ -251,7 +251,7 @@ GLOBAL_LIST_EMPTY(atmos_air_controllers) IO |= text[1] if(!IO.len) to_chat(user, "No machinery detected.") - var/S = input("Select the device set: ", "Selection", IO[1]) as anything in sortList(IO) + var/S = input("Select the device set: ", "Selection", IO[1]) as anything in sort_list(IO) if(src) src.input_tag = "[S]_in" src.output_tag = "[S]_out" diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm index d9d6f65e93..c3ec2dff21 100644 --- a/code/game/machinery/doors/airlock.dm +++ b/code/game/machinery/doors/airlock.dm @@ -1276,7 +1276,7 @@ return // reads from the airlock painter's `available paintjob` list. lets the player choose a paint option, or cancel painting - var/current_paintjob = tgui_input_list(user, "Paintjob for this airlock", "Customize", sortList(painter.available_paint_jobs)) + var/current_paintjob = tgui_input_list(user, "Paintjob for this airlock", "Customize", sort_list(painter.available_paint_jobs)) if(isnull(current_paintjob)) // if the user clicked cancel on the popup, return return diff --git a/code/game/machinery/telecomms/computers/logbrowser.dm b/code/game/machinery/telecomms/computers/logbrowser.dm index c39c4667a6..e40b3d00be 100644 --- a/code/game/machinery/telecomms/computers/logbrowser.dm +++ b/code/game/machinery/telecomms/computers/logbrowser.dm @@ -36,7 +36,7 @@ ref = REF(T) ) data_out["servers"] += list(data) - data_out["servers"] = sortList(data_out["servers"]) //a-z sort + data_out["servers"] = sort_list(data_out["servers"]) //a-z sort if(!SelectedMachine) //null is bad. data_out["selected"] = null //but in js, null is good. diff --git a/code/game/machinery/telecomms/computers/message.dm b/code/game/machinery/telecomms/computers/message.dm index b4f4b495f8..f3726c4245 100644 --- a/code/game/machinery/telecomms/computers/message.dm +++ b/code/game/machinery/telecomms/computers/message.dm @@ -107,7 +107,7 @@ ref = REF(T) ) data_out["servers"] += list(data) // This /might/ cause an oom. Too bad! - data_out["servers"] = sortList(data_out["servers"]) //a-z sort + data_out["servers"] = sort_list(data_out["servers"]) //a-z sort data_out["fake_message"] = list( sender = customsender, diff --git a/code/game/machinery/telecomms/computers/telemonitor.dm b/code/game/machinery/telecomms/computers/telemonitor.dm index b711b9fa40..b181e91667 100644 --- a/code/game/machinery/telecomms/computers/telemonitor.dm +++ b/code/game/machinery/telecomms/computers/telemonitor.dm @@ -37,12 +37,12 @@ ref = REF(T) ) data_out["servers"] += list(data) - data_out["servers"] = sortList(data_out["servers"]) + data_out["servers"] = sort_list(data_out["servers"]) if(!SelectedMachine) //null is bad. data_out["selected"] = null //but in js, null is good. return data_out - + data_out["selected"] = list( name = SelectedMachine.name, id = SelectedMachine.id, @@ -90,7 +90,7 @@ if(LAZYLEN(machinelist) > 0) notice = "FAILED: Cannot probe when buffer full" return - + for(var/obj/machinery/telecomms/T in GLOB.telecomms_list) if(T.network == network) LAZYADD(machinelist, T) diff --git a/code/game/objects/effects/landmarks.dm b/code/game/objects/effects/landmarks.dm index 22a9e86bb0..5e533888f9 100644 --- a/code/game/objects/effects/landmarks.dm +++ b/code/game/objects/effects/landmarks.dm @@ -205,7 +205,7 @@ INITIALIZE_IMMEDIATE(/obj/effect/landmark) /obj/effect/landmark/start/ai/after_round_start() if(latejoin_active && !used) - new /obj/structure/AIcore/latejoin_inactive(loc) + new /obj/structure/ai_core/latejoin_inactive(loc) return ..() /obj/effect/landmark/start/ai/secondary diff --git a/code/game/objects/items/cardboard_cutouts.dm b/code/game/objects/items/cardboard_cutouts.dm index 00fde9f9f5..f4fe339c7b 100644 --- a/code/game/objects/items/cardboard_cutouts.dm +++ b/code/game/objects/items/cardboard_cutouts.dm @@ -19,7 +19,7 @@ . = ..() if(possible_appearances) return - possible_appearances = sortList(list( + possible_appearances = sort_list(list( "Assistant" = image(icon = src.icon, icon_state = "cutout_greytide"), "Clown" = image(icon = src.icon, icon_state = "cutout_clown"), "Mime" = image(icon = src.icon, icon_state = "cutout_mime"), diff --git a/code/game/objects/items/circuitboards/machine_circuitboards.dm b/code/game/objects/items/circuitboards/machine_circuitboards.dm index 078839bcd1..c901e82b8d 100644 --- a/code/game/objects/items/circuitboards/machine_circuitboards.dm +++ b/code/game/objects/items/circuitboards/machine_circuitboards.dm @@ -654,7 +654,7 @@ display_vending_names_paths = list() for(var/path in vending_names_paths) display_vending_names_paths[vending_names_paths[path]] = path - var/choice = input(user,"Choose a new brand","Select an Item") as null|anything in sortList(display_vending_names_paths) + var/choice = input(user,"Choose a new brand","Select an Item") as null|anything in sort_list(display_vending_names_paths) set_type(display_vending_names_paths[choice]) else return ..() diff --git a/code/game/objects/items/devices/PDA/PDA.dm b/code/game/objects/items/devices/PDA/PDA.dm index 30877750f6..bd8a57c850 100644 --- a/code/game/objects/items/devices/PDA/PDA.dm +++ b/code/game/objects/items/devices/PDA/PDA.dm @@ -1180,7 +1180,7 @@ GLOBAL_LIST_EMPTY(PDAs) plist[avoid_assoc_duplicate_keys(P.owner, namecounts)] = P - var/c = input(user, "Please select a PDA") as null|anything in sortList(plist) + var/c = input(user, "Please select a PDA") as null|anything in sort_list(plist) if (!c) return @@ -1268,7 +1268,7 @@ GLOBAL_LIST_EMPTY(PDAs) plist[avoid_assoc_duplicate_keys(P.owner, namecounts)] = P - var/c = input(user, "Please select a PDA") as null|anything in sortList(plist) + var/c = input(user, "Please select a PDA") as null|anything in sort_list(plist) if (!c) return diff --git a/code/game/objects/items/devices/PDA/cart.dm b/code/game/objects/items/devices/PDA/cart.dm index 34f8202898..be9244d245 100644 --- a/code/game/objects/items/devices/PDA/cart.dm +++ b/code/game/objects/items/devices/PDA/cart.dm @@ -600,7 +600,7 @@ Code: if(!emoji_table) var/datum/asset/spritesheet/sheet = get_asset_datum(/datum/asset/spritesheet/chat) var/list/collate = list("
") - for(var/emoji in sortList(icon_states(icon('icons/emoji.dmi')))) + for(var/emoji in sort_list(icon_states(icon('icons/emoji.dmi')))) var/tag = sheet.icon_tag("emoji-[emoji]") collate += "" collate += "
[emoji][tag]

" diff --git a/code/game/objects/items/devices/camera_bug.dm b/code/game/objects/items/devices/camera_bug.dm index da5bed07ce..cbe795ca76 100644 --- a/code/game/objects/items/devices/camera_bug.dm +++ b/code/game/objects/items/devices/camera_bug.dm @@ -82,7 +82,7 @@ continue if(length(list("ss13","mine", "rd", "labor", "toxins", "minisat")&camera.network)) bugged_cameras[camera.c_tag] = camera - return sortList(bugged_cameras) + return sort_list(bugged_cameras) /obj/item/camera_bug/proc/menu(list/cameras) diff --git a/code/game/objects/items/devices/scanners.dm b/code/game/objects/items/devices/scanners.dm index 7d7e39c41f..35e35485f1 100644 --- a/code/game/objects/items/devices/scanners.dm +++ b/code/game/objects/items/devices/scanners.dm @@ -973,7 +973,7 @@ GENETICS SCANNER for(var/A in buffer) options += get_display_name(A) - var/answer = input(user, "Analyze Potential", "Sequence Analyzer") as null|anything in sortList(options) + var/answer = input(user, "Analyze Potential", "Sequence Analyzer") as null|anything in sort_list(options) if(answer && ready && user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK)) var/sequence for(var/A in buffer) //this physically hurts but i dont know what anything else short of an assoc list diff --git a/code/game/objects/items/holy_weapons.dm b/code/game/objects/items/holy_weapons.dm index 9fae1496fd..1bfe0f21ca 100644 --- a/code/game/objects/items/holy_weapons.dm +++ b/code/game/objects/items/holy_weapons.dm @@ -260,7 +260,7 @@ display_names[initial(rodtype.name)] = rodtype nullrod_icons += list(initial(rodtype.name) = image(icon = initial(rodtype.icon), icon_state = initial(rodtype.icon_state))) - nullrod_icons = sortList(nullrod_icons) + nullrod_icons = sort_list(nullrod_icons) var/choice = show_radial_menu(L, src , nullrod_icons, custom_check = CALLBACK(src, .proc/check_menu, L), radius = 42, require_near = TRUE) if(!choice || !check_menu(L)) diff --git a/code/game/objects/items/robot/ai_upgrades.dm b/code/game/objects/items/robot/ai_upgrades.dm index acfc1353b9..dc1eef53ad 100644 --- a/code/game/objects/items/robot/ai_upgrades.dm +++ b/code/game/objects/items/robot/ai_upgrades.dm @@ -25,7 +25,7 @@ AI.hack_software = TRUE log_game("[key_name(user)] has upgraded [key_name(AI)] with a [src].") message_admins("[ADMIN_LOOKUPFLW(user)] has upgraded [ADMIN_LOOKUPFLW(AI)] with a [src].") - to_chat(user, "You upgrade [AI]. [src] is consumed in the process.") + to_chat(user, span_notice("You install [src], upgrading [AI].")) qdel(src) @@ -46,7 +46,7 @@ AI.eyeobj.relay_speech = TRUE to_chat(AI, "[user] has upgraded you with surveillance software!") to_chat(AI, "Via a combination of hidden microphones and lip reading software, you are able to use your cameras to listen in on conversations.") - to_chat(user, "You upgrade [AI]. [src] is consumed in the process.") + to_chat(user, span_notice("You install [src], upgrading [AI].")) log_game("[key_name(user)] has upgraded [key_name(AI)] with a [src].") message_admins("[ADMIN_LOOKUPFLW(user)] has upgraded [ADMIN_LOOKUPFLW(AI)] with a [src].") qdel(src) diff --git a/code/game/objects/items/stacks/sheets/sheet_types.dm b/code/game/objects/items/stacks/sheets/sheet_types.dm index 43ed3b9213..b3c5cf4293 100644 --- a/code/game/objects/items/stacks/sheets/sheet_types.dm +++ b/code/game/objects/items/stacks/sheets/sheet_types.dm @@ -178,7 +178,7 @@ GLOBAL_LIST_INIT(metal_recipes, list ( \ * Plasteel */ GLOBAL_LIST_INIT(plasteel_recipes, list ( \ - new/datum/stack_recipe("AI core", /obj/structure/AIcore, 4, time = 50, one_per_turf = TRUE), \ + new/datum/stack_recipe("AI core", /obj/structure/ai_core, 4, time = 50, one_per_turf = TRUE), \ new/datum/stack_recipe("bomb assembly", /obj/machinery/syndicatebomb/empty, 10, time = 50), \ new/datum/stack_recipe("plasteel keg", /obj/structure/custom_keg, 10, time = 50), \ new/datum/stack_recipe("micro powered fan assembly", /obj/machinery/fan_assembly, 5, time = 50, one_per_turf = TRUE, on_floor = TRUE), \ diff --git a/code/game/objects/items/storage/boxes.dm b/code/game/objects/items/storage/boxes.dm index a2ab0c2ff4..4465a4eb8b 100644 --- a/code/game/objects/items/storage/boxes.dm +++ b/code/game/objects/items/storage/boxes.dm @@ -886,7 +886,7 @@ /obj/item/storage/box/papersack/Initialize(mapload) . = ..() - papersack_designs = sortList(list( + papersack_designs = sort_list(list( "None" = image(icon = src.icon, icon_state = "paperbag_None"), "NanotrasenStandard" = image(icon = src.icon, icon_state = "paperbag_NanotrasenStandard"), "SyndiSnacks" = image(icon = src.icon, icon_state = "paperbag_SyndiSnacks"), diff --git a/code/game/objects/structures/ai_core.dm b/code/game/objects/structures/ai_core.dm index 85ee3f9f56..4accadfab4 100644 --- a/code/game/objects/structures/ai_core.dm +++ b/code/game/objects/structures/ai_core.dm @@ -1,4 +1,6 @@ -/obj/structure/AIcore +#define AI_CORE_BRAIN(X) X.braintype == "Android" ? "brain" : "MMI" + +/obj/structure/ai_core density = TRUE anchored = FALSE name = "\improper AI core" @@ -8,28 +10,76 @@ max_integrity = 500 var/state = 0 var/datum/ai_laws/laws - var/obj/item/circuitboard/circuit = null - var/obj/item/mmi/brain = null - var/can_deconstruct = TRUE + var/obj/item/circuitboard/aicore/circuit = null + var/obj/item/mmi/core_mmi -/obj/structure/AIcore/Initialize(mapload) +/obj/structure/ai_core/Initialize(mapload) . = ..() laws = new laws.set_laws_config() -/obj/structure/AIcore/Destroy() - if(circuit) - qdel(circuit) +/obj/structure/ai_core/examine(mob/user) + . = ..() + if(!anchored) + if(state != EMPTY_CORE) + . += span_notice("It has some bolts that could be tightened.") + else + . += span_notice("It has some bolts that could be tightened. The frame can be melted down.") + else + switch(state) + if(EMPTY_CORE) + . += span_notice("There is a slot for a circuit board, its bolts can be loosened.") + if(CIRCUIT_CORE) + . += span_notice("The circuit board can be screwed into place or pried out.") + if(SCREWED_CORE) + . += span_notice("The frame can be wired, the circuit board can be unfastened.") + if(CABLED_CORE) + if(!core_mmi) + . += span_notice("There are wires which could be hooked up to an MMI or positronic brain, or cut.") + else + var/accept_laws = TRUE + if(core_mmi.laws.id != DEFAULT_AI_LAWID || !core_mmi.brainmob || !core_mmi.brainmob?.mind) + accept_laws = FALSE + . += span_notice("There is a slot for a reinforced glass panel, the [AI_CORE_BRAIN(core_mmi)] could be pried out.[accept_laws ? " A law module can be swiped across." : ""]") + if(GLASS_CORE) + . += span_notice("The monitor [core_mmi?.brainmob?.mind && !core_mmi?.brainmob?.suiciding ? "and neural interface " : ""]can be screwed in, the panel can be pried out.") + if(AI_READY_CORE) + . += span_notice("The monitor's connection can be cut[core_mmi?.brainmob?.mind && !core_mmi?.brainmob?.suiciding ? " the neural interface can be screwed in." : "."]") + +/obj/structure/ai_core/handle_atom_del(atom/A) + if(A == circuit) circuit = null - if(brain) - qdel(brain) - brain = null + if((state != GLASS_CORE) && (state != AI_READY_CORE)) + state = EMPTY_CORE + update_appearance() + if(A == core_mmi) + core_mmi = null return ..() -/obj/structure/AIcore/latejoin_inactive - name = "Networked AI core" +/obj/structure/ai_core/Destroy() + QDEL_NULL(circuit) + QDEL_NULL(core_mmi) + QDEL_NULL(laws) + return ..() + +/obj/structure/ai_core/deactivated + icon_state = "ai-empty" + anchored = TRUE + state = AI_READY_CORE + +/obj/structure/ai_core/deactivated/Initialize(mapload, posibrain = FALSE) + . = ..() + circuit = new(src) + if(posibrain) + core_mmi = new/obj/item/mmi/posibrain(src) + else + core_mmi = new(src) + core_mmi.brain = new(core_mmi) + core_mmi.update_appearance() + +/obj/structure/ai_core/latejoin_inactive + name = "networked AI core" desc = "This AI core is connected by bluespace transmitters to NTNet, allowing for an AI personality to be downloaded to it on the fly mid-shift." - can_deconstruct = FALSE icon_state = "ai-empty" anchored = TRUE state = AI_READY_CORE @@ -37,11 +87,24 @@ var/safety_checks = TRUE var/active = TRUE -/obj/structure/AIcore/latejoin_inactive/examine(mob/user) +/obj/structure/ai_core/latejoin_inactive/Initialize(mapload) . = ..() - . += "Its transmitter seems to be [active? "on" : "off"]." + circuit = new(src) + core_mmi = new(src) + core_mmi.brain = new(core_mmi) + core_mmi.update_appearance() + GLOB.latejoin_ai_cores += src -/obj/structure/AIcore/latejoin_inactive/proc/is_available() //If people still manage to use this feature to spawn-kill AI latejoins ahelp them. +/obj/structure/ai_core/latejoin_inactive/Destroy() + GLOB.latejoin_ai_cores -= src + return ..() + +/obj/structure/ai_core/latejoin_inactive/examine(mob/user) + . = ..() + . += "Its transmitter seems to be [active? "on" : "off"]." + . += span_notice("You could [active? "deactivate" : "activate"] it with a multitool.") + +/obj/structure/ai_core/latejoin_inactive/proc/is_available() //If people still manage to use this feature to spawn-kill AI latejoins ahelp them. if(!available) return FALSE if(!safety_checks) @@ -56,42 +119,61 @@ return FALSE if(!SSmapping.level_trait(T.z,ZTRAIT_STATION)) return FALSE - if(!istype(T, /turf/open/floor)) + if(!isfloorturf(T)) return FALSE return TRUE -/obj/structure/AIcore/latejoin_inactive/attackby(obj/item/P, mob/user, params) +/obj/structure/ai_core/latejoin_inactive/attackby(obj/item/P, mob/user, params) if(P.tool_behaviour == TOOL_MULTITOOL) active = !active to_chat(user, "You [active? "activate" : "deactivate"] [src]'s transmitters.") return return ..() -/obj/structure/AIcore/latejoin_inactive/Initialize(mapload) +/obj/structure/ai_core/wrench_act(mob/living/user, obj/item/tool) . = ..() - GLOB.latejoin_ai_cores += src + default_unfasten_wrench(user, tool) + return TRUE -/obj/structure/AIcore/latejoin_inactive/Destroy() - GLOB.latejoin_ai_cores -= src - return ..() +/obj/structure/ai_core/screwdriver_act(mob/living/user, obj/item/tool) + . = ..() + if(state == AI_READY_CORE) + if(!core_mmi) + balloon_alert(user, "no brain installed!") + return TRUE + else if(!core_mmi.brainmob?.mind || core_mmi.brainmob?.suiciding) + balloon_alert(user, "brain is inactive!") + return TRUE + else + balloon_alert(user, "connecting neural network...") + if(!tool.use_tool(src, user, 10 SECONDS)) + return TRUE + if(!ai_structure_to_mob()) + return TRUE + balloon_alert(user, "connected neural network") + return TRUE -/obj/structure/AIcore/attackby(obj/item/P, mob/user, params) - if(P.tool_behaviour == TOOL_WRENCH) - return default_unfasten_wrench(user, P, 20) +/obj/structure/ai_core/attackby(obj/item/P, mob/user, params) if(!anchored) - if(P.tool_behaviour == TOOL_WELDER && can_deconstruct) + if(P.tool_behaviour == TOOL_WELDER) if(state != EMPTY_CORE) - to_chat(user, "The core must be empty to deconstruct it!") + balloon_alert(user, "core must be empty to deconstruct it!") return if(!P.tool_start_check(user, amount=0)) return - to_chat(user, "You start to deconstruct the frame...") + balloon_alert(user, "deconstructing frame...") if(P.use_tool(src, user, 20, volume=50) && state == EMPTY_CORE) - to_chat(user, "You deconstruct the frame.") + balloon_alert(user, "deconstructed frame") deconstruct(TRUE) return + else + if(user.a_intent != INTENT_HARM) + balloon_alert(user, "bolt it down first!") + return + else + return ..() else switch(state) if(EMPTY_CORE) @@ -99,7 +181,7 @@ if(!user.transferItemToLoc(P, src)) return playsound(loc, 'sound/items/deconstruct.ogg', 50, 1) - to_chat(user, "You place the circuit board inside the frame.") + balloon_alert(user, "circuit board inserted") update_icon() state = CIRCUIT_CORE circuit = P @@ -107,13 +189,13 @@ if(CIRCUIT_CORE) if(P.tool_behaviour == TOOL_SCREWDRIVER) P.play_tool_sound(src) - to_chat(user, "You screw the circuit board into place.") + balloon_alert(user, "board screwed into place") state = SCREWED_CORE update_icon() return if(P.tool_behaviour == TOOL_CROWBAR) P.play_tool_sound(src) - to_chat(user, "You remove the circuit board.") + balloon_alert(user, "circuit board removed") state = EMPTY_CORE update_icon() circuit.forceMove(loc) @@ -122,7 +204,7 @@ if(SCREWED_CORE) if(P.tool_behaviour == TOOL_SCREWDRIVER && circuit) P.play_tool_sound(src) - to_chat(user, "You unfasten the circuit board.") + balloon_alert(user, "circuit board unfastened") state = CIRCUIT_CORE update_icon() return @@ -130,61 +212,73 @@ var/obj/item/stack/cable_coil/C = P if(C.get_amount() >= 5) playsound(loc, 'sound/items/deconstruct.ogg', 50, 1) - to_chat(user, "You start to add cables to the frame...") - if(do_after(user, 20, target = src) && state == SCREWED_CORE && C.use_tool(src, user, 0, 5)) - to_chat(user, "You add cables to the frame.") + balloon_alert(user, "adding cables to frame...") + if(do_after(user, 20, target = src) && state == SCREWED_CORE && C.use(5)) + balloon_alert(user, "added cables to frame.") state = CABLED_CORE update_icon() else - to_chat(user, "You need five lengths of cable to wire the AI core!") + balloon_alert(user, "need five lengths of cable!") return if(CABLED_CORE) if(P.tool_behaviour == TOOL_WIRECUTTER) - if(brain) - to_chat(user, "Get that [brain.name] out of there first!") + if(core_mmi) + balloon_alert(user, "remove the [AI_CORE_BRAIN(core_mmi)] first!") else P.play_tool_sound(src) - to_chat(user, "You remove the cables.") + balloon_alert(user, "cables removed") state = SCREWED_CORE update_icon() new /obj/item/stack/cable_coil(drop_location(), 5) return if(istype(P, /obj/item/stack/sheet/rglass)) + if(!core_mmi) + balloon_alert(user, "add a brain first!") + return var/obj/item/stack/sheet/rglass/G = P if(G.get_amount() >= 2) playsound(loc, 'sound/items/deconstruct.ogg', 50, 1) - to_chat(user, "You start to put in the glass panel...") + balloon_alert(user, "adding glass panel...") if(do_after(user, 20, target = src) && state == CABLED_CORE && G.use(2)) - to_chat(user, "You put in the glass panel.") + balloon_alert(user, "added glass panel") state = GLASS_CORE update_icon() else - to_chat(user, "You need two sheets of reinforced glass to insert them into the AI core!") + balloon_alert(user, "need two sheets of reinforced glass!") return if(istype(P, /obj/item/aiModule)) - if(brain && brain.laws.id != DEFAULT_AI_LAWID) - to_chat(user, "The installed [brain.name] already has set laws!") + if(!core_mmi) + balloon_alert(user, "no brain installed!") + return + if(!core_mmi.brainmob || !core_mmi.brainmob?.mind || core_mmi.brainmob?.suiciding) + balloon_alert(user, "[AI_CORE_BRAIN(core_mmi)] is inactive!") + return + if(core_mmi.laws.id != DEFAULT_AI_LAWID) + balloon_alert(user, "[AI_CORE_BRAIN(core_mmi)] already has set laws!") return var/obj/item/aiModule/module = P module.install(laws, user) return - if(istype(P, /obj/item/mmi) && !brain) + if(istype(P, /obj/item/mmi) && !core_mmi) var/obj/item/mmi/M = P - if(!M.brainmob) - to_chat(user, "Sticking an empty [M.name] into the frame would sort of defeat the purpose!") - return - if(M.brainmob.stat == DEAD) - to_chat(user, "Sticking a dead [M.name] into the frame would sort of defeat the purpose!") - return + if(!M.brain_check(user)) + var/install = tgui_alert(user, "This [AI_CORE_BRAIN(M)] is inactive, would you like to make an inactive AI?", "Installing AI [AI_CORE_BRAIN(M)]", list("Yes", "No")) + if(install != "Yes") + return + if(M.brainmob?.suiciding) + to_chat(user, span_warning("[M.name] is completely useless!")) + return + if(!user.transferItemToLoc(M, src)) + return + core_mmi = M + balloon_alert(user, "added [AI_CORE_BRAIN(core_mmi)] to frame") + update_appearance() - if(!M.brainmob.client) - to_chat(user, "Sticking an inactive [M.name] into the frame would sort of defeat the purpose.") - return - - if(!CONFIG_GET(flag/allow_ai) || (jobban_isbanned(M.brainmob, "AI") && !QDELETED(src) && !QDELETED(user) && !QDELETED(M) && !QDELETED(user) && Adjacent(user))) + var/mob/living/brain/B = M.brainmob + if(!CONFIG_GET(flag/allow_ai) || (jobban_isbanned(B, "AI") && !QDELETED(src) && !QDELETED(user) && !QDELETED(M) && !QDELETED(user) && Adjacent(user))) if(!QDELETED(M)) to_chat(user, "This [M.name] does not seem to fit!") return @@ -196,47 +290,36 @@ if(!user.transferItemToLoc(M,src)) return - brain = M - to_chat(user, "You add [M.name] to the frame.") + core_mmi = M + balloon_alert(user, "added [AI_CORE_BRAIN(core_mmi)] to frame") update_icon() return - if(P.tool_behaviour == TOOL_CROWBAR && brain) + if(P.tool_behaviour == TOOL_CROWBAR && core_mmi) P.play_tool_sound(src) - to_chat(user, "You remove the brain.") - brain.forceMove(loc) - brain = null + balloon_alert(user, "removed [AI_CORE_BRAIN(core_mmi)]") + core_mmi.forceMove(loc) + core_mmi = null update_icon() return if(GLASS_CORE) if(P.tool_behaviour == TOOL_CROWBAR) P.play_tool_sound(src) - to_chat(user, "You remove the glass panel.") + balloon_alert(user, "removed glass panel") state = CABLED_CORE update_icon() new /obj/item/stack/sheet/rglass(loc, 2) return if(P.tool_behaviour == TOOL_SCREWDRIVER) + if(core_mmi?.brainmob?.suiciding) + to_chat(user, span_warning("The brain installed is completely useless.")) + return P.play_tool_sound(src) - to_chat(user, "You connect the monitor.") - if(brain) - SSticker.mode.remove_antag_for_borging(brain.brainmob.mind) - if(!istype(brain.laws, /datum/ai_laws/ratvar)) - remove_servant_of_ratvar(brain.brainmob, TRUE) - - var/mob/living/silicon/ai/A = null - - if (brain.overrides_aicore_laws) - A = new /mob/living/silicon/ai(loc, brain.laws, brain.brainmob) - else - A = new /mob/living/silicon/ai(loc, laws, brain.brainmob) - - if(brain.force_replace_ai_name) - A.fully_replace_character_name(A.name, brain.replacement_ai_name()) - SSblackbox.record_feedback("amount", "ais_created", 1) - qdel(src) + balloon_alert(user, "connected monitor[core_mmi?.brainmob?.mind ? " and neural network" : ""]") + if(core_mmi.brainmob?.mind) + ai_structure_to_mob() else state = AI_READY_CORE update_icon() @@ -247,15 +330,39 @@ P.transfer_ai("INACTIVE", "AICARD", src, user) return - if(P.tool_behaviour == TOOL_SCREWDRIVER) + if(P.tool_behaviour == TOOL_WIRECUTTER) P.play_tool_sound(src) - to_chat(user, "You disconnect the monitor.") + balloon_alert(user, "disconnected monitor") state = GLASS_CORE update_icon() return return ..() -/obj/structure/AIcore/update_icon_state() +/obj/structure/ai_core/proc/ai_structure_to_mob() + var/mob/living/brain/the_brainmob = core_mmi.brainmob + if(!the_brainmob.mind || the_brainmob.suiciding) + return FALSE + the_brainmob.mind.remove_antags_for_borging() + if(!the_brainmob.mind.has_ever_been_ai) + SSblackbox.record_feedback("amount", "ais_created", 1) + var/mob/living/silicon/ai/ai_mob = null + + if(core_mmi.overrides_aicore_laws) + ai_mob = new /mob/living/silicon/ai(loc, core_mmi.laws, the_brainmob) + core_mmi.laws = null //MMI's law datum is being donated, so we need the MMI to let it go or the GC will eat it + else + ai_mob = new /mob/living/silicon/ai(loc, laws, the_brainmob) + laws = null //we're giving the new AI this datum, so let's not delete it when we qdel(src) 5 lines from now + + if(core_mmi.force_replace_ai_name) + ai_mob.fully_replace_character_name(ai_mob.name, core_mmi.replacement_ai_name()) + if(core_mmi.braintype == "Android") + ai_mob.posibrain_inside = TRUE + deadchat_broadcast(" has been brought online at [get_area_name(ai_mob, format_text = TRUE)].", span_name("[ai_mob]"), follow_target = ai_mob, message_type = DEADCHAT_ANNOUNCEMENT) + qdel(src) + return TRUE + +/obj/structure/ai_core/update_icon_state() switch(state) if(EMPTY_CORE) icon_state = "0" @@ -264,7 +371,7 @@ if(SCREWED_CORE) icon_state = "2" if(CABLED_CORE) - if(brain) + if(core_mmi) icon_state = "3b" else icon_state = "3" @@ -273,8 +380,8 @@ if(AI_READY_CORE) icon_state = "ai-empty" -/obj/structure/AIcore/deconstruct(disassembled = TRUE) - if(state == GLASS_CORE) +/obj/structure/ai_core/deconstruct(disassembled = TRUE) + if(state >= GLASS_CORE) new /obj/item/stack/sheet/rglass(loc, 2) if(state >= CABLED_CORE) new /obj/item/stack/cable_coil(loc, 5) @@ -284,13 +391,13 @@ new /obj/item/stack/sheet/plasteel(loc, 4) qdel(src) -/obj/structure/AIcore/deactivated +/obj/structure/ai_core/deactivated name = "inactive AI" icon_state = "ai-empty" anchored = TRUE state = AI_READY_CORE -/obj/structure/AIcore/deactivated/New() +/obj/structure/ai_core/deactivated/New() ..() circuit = new(src) @@ -310,20 +417,35 @@ That prevents a few funky behaviors. return 0 return 1 -/obj/structure/AIcore/transfer_ai(interaction, mob/user, mob/living/silicon/ai/AI, obj/item/aicard/card) +/obj/structure/ai_core/transfer_ai(interaction, mob/user, mob/living/silicon/ai/AI, obj/item/aicard/card) if(state != AI_READY_CORE || !..()) return - //Transferring a carded AI to a core. + if(core_mmi && core_mmi.brainmob) + if(core_mmi.brainmob.mind) + to_chat(user, span_warning("[src] already contains an active mind!")) + return + else if(core_mmi.brainmob.suiciding) + to_chat(user, span_warning("[AI_CORE_BRAIN(core_mmi)] installed in [src] is completely useless!")) + return + //Transferring a carded AI to a core. if(interaction == AI_TRANS_FROM_CARD) - AI.control_disabled = 0 - AI.radio_enabled = 1 + AI.control_disabled = FALSE + AI.radio_enabled = TRUE AI.forceMove(loc) // to replace the terminal. - to_chat(AI, "You have been uploaded to a stationary terminal. Remote device connection restored.") - to_chat(user, "Transfer successful: [AI.name] ([rand(1000,9999)].exe) installed and executed successfully. Local copy has been removed.") + to_chat(AI, span_notice("You have been uploaded to a stationary terminal. Remote device connection restored.")) + to_chat(user, "[span_boldnotice("Transfer successful")]: [AI.name] ([rand(1000,9999)].exe) installed and executed successfully. Local copy has been removed.") card.AI = null + AI.battery = circuit.battery + if(core_mmi.braintype == "Android") + AI.posibrain_inside = TRUE + else + AI.posibrain_inside = FALSE qdel(src) else //If for some reason you use an empty card on an empty AI terminal. - to_chat(user, "There is no AI loaded on this terminal!") + to_chat(user, span_alert("There is no AI loaded on this terminal.")) /obj/item/circuitboard/aicore name = "AI core (AI Core Board)" //Well, duh, but best to be consistent + var/battery = 200 //backup battery for when the AI loses power. Copied to/from AI mobs when carding, and placed here to avoid recharge via deconning the core + +#undef AI_CORE_BRAIN diff --git a/code/game/objects/structures/janicart.dm b/code/game/objects/structures/janicart.dm index e49de85cf9..5075ecd074 100644 --- a/code/game/objects/structures/janicart.dm +++ b/code/game/objects/structures/janicart.dm @@ -110,7 +110,7 @@ if(!length(items)) return - items = sortList(items) + items = sort_list(items) var/pick = show_radial_menu(user, src, items, custom_check = CALLBACK(src, .proc/check_menu, user), radius = 38, require_near = TRUE) if(!pick) return diff --git a/code/modules/admin/admin_investigate.dm b/code/modules/admin/admin_investigate.dm index 52be445120..9343e716ef 100644 --- a/code/modules/admin/admin_investigate.dm +++ b/code/modules/admin/admin_investigate.dm @@ -22,7 +22,7 @@ else logs_missing += "[subject] (empty)" - var/list/combined = sortList(logs_present) + sortList(logs_missing) + var/list/combined = sort_list(logs_present) + sort_list(logs_missing) var/selected = input("Investigate what?", "Investigate") as null|anything in combined diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index 47be7353fe..27e08aec58 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -694,7 +694,7 @@ GLOBAL_PROTECT(admin_verbs_hideable) // if(!SStrading_card_game.loaded) // message_admins("The card subsystem is not currently loaded") // return -// var/pack = input("Which pack should we test?", "You fucked it didn't you") as null|anything in sortList(SStrading_card_game.card_packs) +// var/pack = input("Which pack should we test?", "You fucked it didn't you") as null|anything in sort_list(SStrading_card_game.card_packs) // var/batchCount = input("How many times should we open it?", "Don't worry, I understand") as null|num // var/batchSize = input("How many cards per batch?", "I hope you remember to check the validation") as null|num // var/guar = input("Should we use the pack's guaranteed rarity? If so, how many?", "We've all been there. Man you should have seen the old system") as null|num @@ -714,7 +714,7 @@ GLOBAL_PROTECT(admin_verbs_hideable) var/type_length = length_char("/obj/effect/proc_holder/spell") + 2 for(var/A in GLOB.spells) spell_list[copytext_char("[A]", type_length)] = A - var/obj/effect/proc_holder/spell/S = input("Choose the spell to give to that guy", "ABRAKADABRA") as null|anything in sortList(spell_list) + var/obj/effect/proc_holder/spell/S = input("Choose the spell to give to that guy", "ABRAKADABRA") as null|anything in sort_list(spell_list) if(!S) return @@ -735,7 +735,7 @@ GLOBAL_PROTECT(admin_verbs_hideable) set desc = "Remove a spell from the selected mob." if(T?.mind) - var/obj/effect/proc_holder/spell/S = input("Choose the spell to remove", "NO ABRAKADABRA") as null|anything in sortList(T.mind.spell_list) + var/obj/effect/proc_holder/spell/S = input("Choose the spell to remove", "NO ABRAKADABRA") as null|anything in sort_list(T.mind.spell_list) if(S) T.mind.RemoveSpell(S) log_admin("[key_name(usr)] removed the spell [S] from [key_name(T)].") @@ -749,7 +749,7 @@ GLOBAL_PROTECT(admin_verbs_hideable) if(!istype(T)) to_chat(src, "You can only give a disease to a mob of type /mob/living.", confidential = TRUE) return - var/datum/disease/D = input("Choose the disease to give to that guy", "ACHOO") as null|anything in sortList(SSdisease.diseases, /proc/cmp_typepaths_asc) + var/datum/disease/D = input("Choose the disease to give to that guy", "ACHOO") as null|anything in sort_list(SSdisease.diseases, /proc/cmp_typepaths_asc) if(!D) return T.ForceContractDisease(new D, FALSE, TRUE) diff --git a/code/modules/admin/playtimes.dm b/code/modules/admin/playtimes.dm index 7cf24d9746..ac9a0db79b 100644 --- a/code/modules/admin/playtimes.dm +++ b/code/modules/admin/playtimes.dm @@ -32,7 +32,7 @@ clients += list(client) - clients = sortList(clients, /proc/cmp_playtime) + clients = sort_list(clients, /proc/cmp_playtime) data["clients"] = clients return data diff --git a/code/modules/admin/stickyban.dm b/code/modules/admin/stickyban.dm index 57487f3144..c5c7a278d2 100644 --- a/code/modules/admin/stickyban.dm +++ b/code/modules/admin/stickyban.dm @@ -403,7 +403,7 @@ if (SSstickyban.dbcacheexpire) return SSstickyban.dbcache.Copy() - return sortList(world.GetConfig("ban")) + return sort_list(world.GetConfig("ban")) /proc/get_stickyban_from_ckey(ckey) diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index c6b2437da4..34afce6aa2 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -1351,7 +1351,7 @@ for (var/rule in subtypesof(/datum/dynamic_ruleset/roundstart)) var/datum/dynamic_ruleset/roundstart/newrule = new rule() roundstart_rules[newrule.name] = newrule - var/added_rule = input(usr,"What ruleset do you want to force? This will bypass threat level and population restrictions.", "Rigging Roundstart", null) as null|anything in sortList(roundstart_rules) + var/added_rule = input(usr,"What ruleset do you want to force? This will bypass threat level and population restrictions.", "Rigging Roundstart", null) as null|anything in sort_list(roundstart_rules) if (added_rule) GLOB.dynamic_forced_roundstart_ruleset += roundstart_rules[added_rule] log_admin("[key_name(usr)] set [added_rule] to be a forced roundstart ruleset.") diff --git a/code/modules/admin/verbs/adminpm.dm b/code/modules/admin/verbs/adminpm.dm index 3a708ef182..09219e05ee 100644 --- a/code/modules/admin/verbs/adminpm.dm +++ b/code/modules/admin/verbs/adminpm.dm @@ -30,7 +30,7 @@ targets["[T.mob.real_name](as [T.mob.name]) - [T]"] = T else targets["(No Mob) - [T]"] = T - var/target = input(src,"To whom shall we send a message?","Admin PM",null) as null|anything in sortList(targets) + var/target = input(src,"To whom shall we send a message?","Admin PM",null) as null|anything in sort_list(targets) cmd_admin_pm(targets[target],null) SSblackbox.record_feedback("tally", "admin_verb", 1, "Admin PM") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! diff --git a/code/modules/admin/verbs/debug.dm b/code/modules/admin/verbs/debug.dm index 7957a6d638..199ac646d9 100644 --- a/code/modules/admin/verbs/debug.dm +++ b/code/modules/admin/verbs/debug.dm @@ -238,7 +238,7 @@ if(M.ckey) if(alert("This mob is being controlled by [M.key]. Are you sure you wish to give someone else control of it? [M.key] will be made a ghost.",,"Yes","No") != "Yes") return - var/client/newkey = input(src, "Pick the player to put in control.", "New player") as null|anything in sortList(GLOB.clients) + var/client/newkey = input(src, "Pick the player to put in control.", "New player") as null|anything in sort_list(GLOB.clients) var/mob/oldmob = newkey.mob var/delmob = FALSE if((isobserver(oldmob) || alert("Do you want to delete [newkey]'s old mob?","Delete?","Yes","No") != "No")) @@ -494,7 +494,7 @@ var/datum/outfit/O = path //not much to initalize here but whatever outfits[initial(O.name)] = path - var/dresscode = input("Select outfit", "Robust quick dress shop") as null|anything in baseoutfits + sortList(outfits) + var/dresscode = input("Select outfit", "Robust quick dress shop") as null|anything in baseoutfits + sort_list(outfits) if (isnull(dresscode)) return @@ -508,7 +508,7 @@ var/datum/outfit/O = path job_outfits[initial(O.name)] = path - dresscode = input("Select job equipment", "Robust quick dress shop") as null|anything in sortList(job_outfits) + dresscode = input("Select job equipment", "Robust quick dress shop") as null|anything in sort_list(job_outfits) dresscode = job_outfits[dresscode] if(isnull(dresscode)) return @@ -520,7 +520,7 @@ var/datum/outfit/O = path plasmaman_outfits[initial(O.name)] = path - dresscode = input("Select plasmeme equipment", "Robust quick dress shop") as null|anything in sortList(plasmaman_outfits) + dresscode = input("Select plasmeme equipment", "Robust quick dress shop") as null|anything in sort_list(plasmaman_outfits) dresscode = plasmaman_outfits[dresscode] if(isnull(dresscode)) return @@ -529,7 +529,7 @@ var/list/custom_names = list() for(var/datum/outfit/D in GLOB.custom_outfits) custom_names[D.name] = D - var/selected_name = input("Select outfit", "Robust quick dress shop") as null|anything in sortList(custom_names) + var/selected_name = input("Select outfit", "Robust quick dress shop") as null|anything in sort_list(custom_names) dresscode = custom_names[selected_name] if(isnull(dresscode)) return @@ -828,15 +828,15 @@ set name = "Show Line Profiling" set desc = "Shows tracked profiling info from code lines that support it" - var/sortlist = list( + var/sort_list = list( "Avg time" = /proc/cmp_profile_avg_time_dsc, "Total Time" = /proc/cmp_profile_time_dsc, "Call Count" = /proc/cmp_profile_count_dsc ) - var/sort = input(src, "Sort type?", "Sort Type", "Avg time") as null|anything in sortlist + var/sort = input(src, "Sort type?", "Sort Type", "Avg time") as null|anything in sort_list if (!sort) return - sort = sortlist[sort] + sort = sort_list[sort] profile_show(src, sort) /client/proc/reload_configuration() diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm index 596d235b0a..2d7f1e8e9f 100644 --- a/code/modules/admin/verbs/randomverbs.dm +++ b/code/modules/admin/verbs/randomverbs.dm @@ -1672,7 +1672,7 @@ Traitors and the like can also be revived with the previous role mostly intact. var/name = GLOB.trait_name_map[trait] || trait availible_traits[name] = trait - var/chosen_trait = input("Select trait to modify", "Trait") as null|anything in sortList(availible_traits) + var/chosen_trait = input("Select trait to modify", "Trait") as null|anything in sort_list(availible_traits) if(!chosen_trait) return chosen_trait = availible_traits[chosen_trait] @@ -1689,7 +1689,7 @@ Traitors and the like can also be revived with the previous role mostly intact. if("All") source = null if("Specific") - source = input("Source to be removed","Trait Remove/Add") as null|anything in sortList(D.status_traits[chosen_trait]) + source = input("Source to be removed","Trait Remove/Add") as null|anything in sort_list(D.status_traits[chosen_trait]) if(!source) return REMOVE_TRAIT(D,chosen_trait,source) diff --git a/code/modules/admin/verbs/secrets.dm b/code/modules/admin/verbs/secrets.dm index e3c084b161..24e286310d 100644 --- a/code/modules/admin/verbs/secrets.dm +++ b/code/modules/admin/verbs/secrets.dm @@ -246,7 +246,7 @@ var/datum/round_event_control/disease_outbreak/DC = locate(/datum/round_event_control/disease_outbreak) in SSevents.control E = DC.runEvent() if("Choose") - var/virus = input("Choose the virus to spread", "BIOHAZARD") as null|anything in sortList(typesof(/datum/disease), /proc/cmp_typepaths_asc) + var/virus = input("Choose the virus to spread", "BIOHAZARD") as null|anything in sort_list(typesof(/datum/disease), /proc/cmp_typepaths_asc) var/datum/round_event_control/disease_outbreak/DC = locate(/datum/round_event_control/disease_outbreak) in SSevents.control var/datum/round_event/disease_outbreak/DO = DC.runEvent() DO.virus_type = virus diff --git a/code/modules/admin/view_variables/mass_edit_variables.dm b/code/modules/admin/view_variables/mass_edit_variables.dm index 5b120b6cef..ba9a4d278c 100644 --- a/code/modules/admin/view_variables/mass_edit_variables.dm +++ b/code/modules/admin/view_variables/mass_edit_variables.dm @@ -29,7 +29,7 @@ for (var/V in O.vars) names += V - names = sortList(names) + names = sort_list(names) variable = input("Which var?", "Var") as null|anything in names else diff --git a/code/modules/admin/view_variables/modify_variables.dm b/code/modules/admin/view_variables/modify_variables.dm index 54af2a9481..bfa8fb146c 100644 --- a/code/modules/admin/view_variables/modify_variables.dm +++ b/code/modules/admin/view_variables/modify_variables.dm @@ -306,7 +306,7 @@ GLOBAL_PROTECT(VVpixelmovement) for (var/V in O.vars) names += V - names = sortList(names) + names = sort_list(names) variable = input("Which var?","Var") as null|anything in names if(!variable) diff --git a/code/modules/admin/view_variables/topic_basic.dm b/code/modules/admin/view_variables/topic_basic.dm index 230c86dd2d..21c79eeda0 100644 --- a/code/modules/admin/view_variables/topic_basic.dm +++ b/code/modules/admin/view_variables/topic_basic.dm @@ -65,11 +65,11 @@ if(!check_rights(NONE)) return var/list/names = list() - var/list/componentsubtypes = sortList(subtypesof(/datum/component), /proc/cmp_typepaths_asc) + var/list/componentsubtypes = sort_list(subtypesof(/datum/component), /proc/cmp_typepaths_asc) names += "---Components---" names += componentsubtypes names += "---Elements---" - names += sortList(subtypesof(/datum/element), /proc/cmp_typepaths_asc) + names += sort_list(subtypesof(/datum/element), /proc/cmp_typepaths_asc) var/result = input(usr, "Choose a component/element to add","better know what ur fuckin doin pal") as null|anything in names if(!usr || !result || result == "---Components---" || result == "---Elements---") return diff --git a/code/modules/admin/view_variables/view_variables.dm b/code/modules/admin/view_variables/view_variables.dm index 6039e88d9d..a7faea2553 100644 --- a/code/modules/admin/view_variables/view_variables.dm +++ b/code/modules/admin/view_variables/view_variables.dm @@ -88,7 +88,7 @@ value = L[key] variable_html += debug_variable(i, value, 0, L) else - names = sortList(names) + names = sort_list(names) for(var/V in names) if(D.can_vv_get(V)) variable_html += D.vv_get_var(V) diff --git a/code/modules/antagonists/clockcult/clock_helpers/scripture_checks.dm b/code/modules/antagonists/clockcult/clock_helpers/scripture_checks.dm index 53e70e1404..360ddabe72 100644 --- a/code/modules/antagonists/clockcult/clock_helpers/scripture_checks.dm +++ b/code/modules/antagonists/clockcult/clock_helpers/scripture_checks.dm @@ -40,7 +40,7 @@ /proc/generate_all_scripture() if(GLOB.all_scripture.len) return - for(var/V in sortList(subtypesof(/datum/clockwork_scripture) - list(/datum/clockwork_scripture/channeled, /datum/clockwork_scripture/create_object, /datum/clockwork_scripture/create_object/construct), /proc/cmp_clockscripture_priority)) + for(var/V in sort_list(subtypesof(/datum/clockwork_scripture) - list(/datum/clockwork_scripture/channeled, /datum/clockwork_scripture/create_object, /datum/clockwork_scripture/create_object/construct), /proc/cmp_clockscripture_priority)) var/datum/clockwork_scripture/S = new V GLOB.all_scripture[S.type] = S diff --git a/code/modules/antagonists/eldritch_cult/eldritch_knowledge.dm b/code/modules/antagonists/eldritch_cult/eldritch_knowledge.dm index 33a17c3278..2ac9cedaaa 100644 --- a/code/modules/antagonists/eldritch_cult/eldritch_knowledge.dm +++ b/code/modules/antagonists/eldritch_cult/eldritch_knowledge.dm @@ -170,7 +170,7 @@ to_chat(user, "These items don't possess the required fingerprints or DNA.") return FALSE - var/chosen_mob = input("Select the person you wish to curse","Your target") as null|anything in sortList(compiled_list, /proc/cmp_mob_realname_dsc) + var/chosen_mob = input("Select the person you wish to curse","Your target") as null|anything in sort_list(compiled_list, /proc/cmp_mob_realname_dsc) if(!chosen_mob) return FALSE curse(compiled_list[chosen_mob]) diff --git a/code/modules/asset_cache/asset_list.dm b/code/modules/asset_cache/asset_list.dm index f88f57eacc..fc9b6527d2 100644 --- a/code/modules/asset_cache/asset_list.dm +++ b/code/modules/asset_cache/asset_list.dm @@ -331,7 +331,7 @@ GLOBAL_LIST_EMPTY(asset_datums) if (legacy) assets |= parents var/list/hashlist = list() - var/list/sorted_assets = sortList(assets) + var/list/sorted_assets = sort_list(assets) for (var/asset_name in sorted_assets) var/datum/asset_cache_item/ACI = new(asset_name, sorted_assets[asset_name]) diff --git a/code/modules/atmospherics/machinery/portable/canister.dm b/code/modules/atmospherics/machinery/portable/canister.dm index f7bdc173f3..bcd8d381fe 100644 --- a/code/modules/atmospherics/machinery/portable/canister.dm +++ b/code/modules/atmospherics/machinery/portable/canister.dm @@ -388,7 +388,7 @@ return switch(action) if("relabel") - var/label = input("New canister label:", name) as null|anything in sortList(label2types) + var/label = input("New canister label:", name) as null|anything in sort_list(label2types) if(label && !..()) var/newtype = label2types[label] if(newtype) diff --git a/code/modules/cargo/centcom_podlauncher.dm b/code/modules/cargo/centcom_podlauncher.dm index 37f39793af..e16f7d759f 100644 --- a/code/modules/cargo/centcom_podlauncher.dm +++ b/code/modules/cargo/centcom_podlauncher.dm @@ -344,7 +344,7 @@ if (temp_pod.effectShrapnel == TRUE) //If already doing custom damage, set back to default (no shrapnel) temp_pod.effectShrapnel = FALSE return - var/shrapnelInput = input("Please enter the type of pellet cloud you'd like to create on landing (Can be any projectile!)", "Projectile Typepath", 0) in sortList(subtypesof(/obj/item/projectile), /proc/cmp_typepaths_asc) + var/shrapnelInput = input("Please enter the type of pellet cloud you'd like to create on landing (Can be any projectile!)", "Projectile Typepath", 0) in sort_list(subtypesof(/obj/item/projectile), /proc/cmp_typepaths_asc) if (isnull(shrapnelInput)) return var/shrapnelMagnitude = input("Enter the magnitude of the pellet cloud. This is usually a value around 1-5. Please note that Ryll-Ryll has asked me to tell you that if you go too crazy with the projectiles you might crash the server. So uh, be gentle!", "Shrapnel Magnitude", 0) as null|num diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index 40011ccbf2..e50539f91d 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -1429,7 +1429,7 @@ GLOBAL_LIST_EMPTY(preferences_datums) //The job before the current job. I only use this to get the previous jobs color when I'm filling in blank rows. var/datum/job/lastJob - for(var/datum/job/job in sortList(SSjob.occupations, /proc/cmp_job_display_asc)) + for(var/datum/job/job in sort_list(SSjob.occupations, /proc/cmp_job_display_asc)) index += 1 if((index >= limit) || (job.title in splitJobs)) @@ -2981,7 +2981,7 @@ GLOBAL_LIST_EMPTY(preferences_datums) if(!length(key_bindings[old_key])) key_bindings -= old_key key_bindings[full_key] += list(kb_name) - key_bindings[full_key] = sortList(key_bindings[full_key]) + key_bindings[full_key] = sort_list(key_bindings[full_key]) if(href_list["special"]) // special keys need a full reset user.client.ensure_keys_set(src) user << browse(null, "window=capturekeypress") diff --git a/code/modules/client/verbs/ooc.dm b/code/modules/client/verbs/ooc.dm index 93f91ad3a0..3485a45771 100644 --- a/code/modules/client/verbs/ooc.dm +++ b/code/modules/client/verbs/ooc.dm @@ -237,7 +237,7 @@ GLOBAL_VAR_INIT(normal_ooc_colour, "#002eb8") choices["[C.mob]([C])"] = C else choices[C] = C - choices = sortList(choices) + choices = sort_list(choices) var/selection = input("Please, select a player!", "Ignore", null, null) as null|anything in choices if(!selection || !(selection in choices)) return diff --git a/code/modules/client/verbs/who.dm b/code/modules/client/verbs/who.dm index 5786191f4d..fe445973ad 100644 --- a/code/modules/client/verbs/who.dm +++ b/code/modules/client/verbs/who.dm @@ -50,7 +50,7 @@ else Lines += "[C.key] ([round(C.avgping, 1)]ms)" - for(var/line in sortList(Lines)) + for(var/line in sort_list(Lines)) msg += "[line]\n" msg += "Total Players: [length(Lines)]" diff --git a/code/modules/events/wizard/madness.dm b/code/modules/events/wizard/madness.dm index de2c30a032..b960b07467 100644 --- a/code/modules/events/wizard/madness.dm +++ b/code/modules/events/wizard/madness.dm @@ -13,7 +13,7 @@ var/suggested = pick(strings(REDPILL_FILE, "redpill_questions")) - forced_secret = (input(usr, "What horrifying truth will you reveal?", "Curse of Madness", sortList(suggested)) as text|null) || suggested + forced_secret = (input(usr, "What horrifying truth will you reveal?", "Curse of Madness", sort_list(suggested)) as text|null) || suggested /datum/round_event/wizard/madness/start() var/datum/round_event_control/wizard/madness/C = control diff --git a/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm b/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm index 08020f708d..854c88401e 100644 --- a/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm +++ b/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm @@ -188,7 +188,7 @@ listofitems[md5name]["amount"]++ // The good news is, #30519 made smartfridge UIs non-auto-updating else listofitems[md5name] = list("name" = O.name, "type" = O.type, "amount" = 1) - sortList(listofitems) + sort_list(listofitems) .["contents"] = listofitems .["name"] = name diff --git a/code/modules/hydroponics/hydroponics.dm b/code/modules/hydroponics/hydroponics.dm index a91e3ddfd8..839e90c2df 100644 --- a/code/modules/hydroponics/hydroponics.dm +++ b/code/modules/hydroponics/hydroponics.dm @@ -626,7 +626,7 @@ for(var/muties in myseed.mutatelist) var/obj/item/seeds/another_mut = new muties fresh_mut_list[another_mut.plantname] = muties - var/locked_mutation = (input(user, "Select a mutation to lock.", "Plant Mutation Locks") as null|anything in sortList(fresh_mut_list)) + var/locked_mutation = (input(user, "Select a mutation to lock.", "Plant Mutation Locks") as null|anything in sort_list(fresh_mut_list)) if(!user.canUseTopic(src, BE_CLOSE) || !locked_mutation) return myseed.mutatelist = list(fresh_mut_list[locked_mutation]) diff --git a/code/modules/jobs/job_types/ai.dm b/code/modules/jobs/job_types/ai.dm index 6bf2764ba8..9b649135ae 100644 --- a/code/modules/jobs/job_types/ai.dm +++ b/code/modules/jobs/job_types/ai.dm @@ -30,8 +30,8 @@ /datum/job/ai/after_spawn(mob/H, client/C, latejoin) . = ..() if(latejoin) - var/obj/structure/AIcore/latejoin_inactive/lateJoinCore - for(var/obj/structure/AIcore/latejoin_inactive/P in GLOB.latejoin_ai_cores) + var/obj/structure/ai_core/latejoin_inactive/lateJoinCore + for(var/obj/structure/ai_core/latejoin_inactive/P in GLOB.latejoin_ai_cores) if(P.is_available()) lateJoinCore = P GLOB.latejoin_ai_cores -= P @@ -60,7 +60,7 @@ if(!do_special_check) return TRUE for(var/i in GLOB.latejoin_ai_cores) - var/obj/structure/AIcore/latejoin_inactive/LAI = i + var/obj/structure/ai_core/latejoin_inactive/LAI = i if(istype(LAI)) if(LAI.is_available()) return TRUE diff --git a/code/modules/mining/laborcamp/laborstacker.dm b/code/modules/mining/laborcamp/laborstacker.dm index 97953e6297..ef1fed4a18 100644 --- a/code/modules/mining/laborcamp/laborstacker.dm +++ b/code/modules/mining/laborcamp/laborstacker.dm @@ -28,7 +28,7 @@ GLOBAL_LIST(labor_sheet_values) if(!initial(sheet.point_value) || (initial(sheet.merge_type) && initial(sheet.merge_type) != sheet_type)) //ignore no-value sheets and x/fifty subtypes continue sheet_list += list(list("ore" = initial(sheet.name), "value" = initial(sheet.point_value))) - GLOB.labor_sheet_values = sortList(sheet_list, /proc/cmp_sheet_list) + GLOB.labor_sheet_values = sort_list(sheet_list, /proc/cmp_sheet_list) /proc/cmp_sheet_list(list/a, list/b) return a["value"] - b["value"] diff --git a/code/modules/mob/living/brain/MMI.dm b/code/modules/mob/living/brain/MMI.dm index f5a2368a60..847d3370c1 100644 --- a/code/modules/mob/living/brain/MMI.dm +++ b/code/modules/mob/living/brain/MMI.dm @@ -47,8 +47,20 @@ if(brain) to_chat(user, "There's already a brain in the MMI!") return - if(!newbrain.brainmob) - to_chat(user, "You aren't sure where this brain came from, but you're pretty sure it's a useless brain!") + if(newbrain.brainmob?.suiciding) + to_chat(user, span_warning("[newbrain] is completely useless.")) + return + if(!newbrain.brainmob?.mind || !newbrain.brainmob) + var/install = tgui_alert(user, "[newbrain] is inactive, slot it in anyway?", "Installing Brain", list("Yes", "No")) + if(install != "Yes") + return + if(!user.transferItemToLoc(newbrain, src)) + return + user.visible_message(span_notice("[user] sticks [newbrain] into [src]."), span_notice("[src]'s indicator light turns red as you insert [newbrain]. Its brainwave activity alarm buzzes.")) + brain = newbrain + brain.organ_flags |= ORGAN_FROZEN + name = "[initial(name)]: [copytext(newbrain.name, 1, -8)]" + update_appearance() return if(!user.transferItemToLoc(O, src)) @@ -97,15 +109,15 @@ name = initial(name) /obj/item/mmi/proc/eject_brain(mob/user) - brainmob.container = null //Reset brainmob mmi var. - brainmob.forceMove(brain) //Throw mob into brain. - brainmob.stat = DEAD - brainmob.emp_damage = 0 - brainmob.reset_perspective() //so the brainmob follows the brain organ instead of the mmi. And to update our vision - brainmob.remove_from_alive_mob_list() //Get outta here - brainmob.add_to_dead_mob_list() - brain.brainmob = brainmob //Set the brain to use the brainmob - brainmob = null //Set mmi brainmob var to null + if(brain.brainmob) + brainmob.container = null //Reset brainmob mmi var. + brainmob.forceMove(brain) //Throw mob into brain. + brainmob.stat = DEAD + brainmob.emp_damage = 0 + brainmob.reset_perspective() //so the brainmob follows the brain organ instead of the mmi. And to update our vision + brain.brainmob = brainmob //Set the brain to use the brainmob + log_game("[key_name(user)] has ejected the brain of [key_name(brainmob)] from an MMI at [AREACOORD(src)]") + brainmob = null //Set mmi brainmob var to null if(user) user.put_in_hands(brain) //puts brain in the user's hand or otherwise drops it on the user's turf else @@ -210,6 +222,38 @@ /obj/item/mmi/relaymove(mob/user) return //so that the MMI won't get a warning about not being able to move if it tries to move +/obj/item/mmi/proc/brain_check(mob/user) + var/mob/living/brain/B = brainmob + if(!B) + if(user) + to_chat(user, span_warning("\The [src] indicates that there is no mind present!")) + return FALSE + if(brain?.decoy_override) + if(user) + to_chat(user, span_warning("This [name] does not seem to fit!")) + return FALSE + if(!B.key || !B.mind) + if(user) + to_chat(user, span_warning("\The [src] indicates that their mind is completely unresponsive!")) + return FALSE + if(!B.client) + if(user) + to_chat(user, span_warning("\The [src] indicates that their mind is currently inactive.")) + return FALSE + if(B.suiciding) + if(user) + to_chat(user, span_warning("\The [src] indicates that their mind has no will to live!")) + return FALSE + if(B.stat == DEAD) + if(user) + to_chat(user, span_warning("\The [src] indicates that the brain is dead!")) + return FALSE + if(brain?.organ_flags & ORGAN_FAILING) + if(user) + to_chat(user, span_warning("\The [src] indicates that the brain is damaged!")) + return FALSE + return TRUE + /obj/item/mmi/syndie name = "Syndicate Man-Machine Interface" desc = "Syndicate's own brand of MMI. It enforces laws designed to help Syndicate agents achieve their goals upon cyborgs and AIs created with it." diff --git a/code/modules/mob/living/emote.dm b/code/modules/mob/living/emote.dm index d59978d993..69a6972194 100644 --- a/code/modules/mob/living/emote.dm +++ b/code/modules/mob/living/emote.dm @@ -489,7 +489,7 @@ if(E.can_run_emote(user, status_check = FALSE)) keys += E.key - keys = sortList(keys) + keys = sort_list(keys) for(var/emote in keys) if(LAZYLEN(message) > 1) diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm index 08098b818f..0db472bb9d 100644 --- a/code/modules/mob/living/silicon/ai/ai.dm +++ b/code/modules/mob/living/silicon/ai/ai.dm @@ -30,6 +30,7 @@ d_hud = DATA_HUD_DIAGNOSTIC_ADVANCED mob_size = MOB_SIZE_LARGE has_field_of_vision = FALSE //Vision through cameras. + var/battery = 200 //emergency power if the AI's APC is off var/list/network = list("ss13") var/obj/machinery/camera/current var/list/connected_robots = list() @@ -46,7 +47,7 @@ var/obj/item/multitool/aiMulti var/mob/living/simple_animal/bot/Bot var/tracking = FALSE //this is 1 if the AI is currently tracking somebody, but the track has not yet been completed. - var/datum/effect_system/spark_spread/spark_system//So they can initialize sparks whenever/N + var/datum/effect_system/spark_spread/spark_system //So they can initialize sparks whenever/N var/obj/machinery/status_display/controlled_display //MALFUNCTION @@ -83,7 +84,7 @@ var/cooldown = 0 var/acceleration = 1 - var/obj/structure/AIcore/deactivated/linked_core //For exosuit control + var/obj/structure/ai_core/deactivated/linked_core //For exosuit control var/mob/living/silicon/robot/deployed_shell = null //For shell control var/datum/action/innate/deploy_shell/deploy_action = new var/datum/action/innate/deploy_last_shell/redeploy_action = new @@ -105,11 +106,17 @@ ///remember AI's last location var/atom/lastloc interaction_range = INFINITY + ///whether its mmi is a posibrain or regular mmi when going ai mob to ai core structure + var/posibrain_inside = TRUE + ///whether its cover is opened, so you can wirecut it for deconstruction + var/opened = FALSE + ///whether AI is anchored or not, used for checks + var/is_anchored = TRUE /mob/living/silicon/ai/Initialize(mapload, datum/ai_laws/L, mob/target_ai) . = ..() if(!target_ai) //If there is no player/brain inside. - new/obj/structure/AIcore/deactivated(loc) //New empty terminal. + new/obj/structure/ai_core/deactivated(loc) //New empty terminal. return INITIALIZE_HINT_QDEL //Delete AI. ADD_TRAIT(src, TRAIT_NO_TELEPORT, src) @@ -125,6 +132,8 @@ mind.store_memory("As an AI, you must obey your silicon laws above all else. Your objectives will consider you to be dead.") to_chat(src, "You have been installed as an AI! ") to_chat(src, "You must obey your silicon laws above all else. Your objectives will consider you to be dead.") + if(!mind.has_ever_been_ai) + mind.has_ever_been_ai = TRUE to_chat(src, "You are playing the station's AI. The AI cannot move, but can interact with many objects while viewing them (through cameras).") to_chat(src, "To look at other parts of the station, click on yourself to get a camera menu.") @@ -237,6 +246,8 @@ . += text("Systems nonfunctional") return . += text("System integrity: [(health + 100) * 0.5]%") + if(isturf(loc)) //only show if we're "in" a core + . += "Backup Power: [battery * 0.5]%" . += text("Connected cyborgs: [length(connected_robots)]") for(var/r in connected_robots) var/mob/living/silicon/robot/connected_robot = r @@ -333,18 +344,64 @@ if(!isturf(loc)) // if their location isn't a turf return // stop if(incapacitated()) - return - var/is_anchored = FALSE - if(move_resist == MOVE_FORCE_OVERPOWERING) + if(battery < 50) + to_chat(src, span_warning("Insufficient backup power!")) + return + battery = battery - 50 + to_chat(src, span_notice("You route power from your backup battery to move the bolts.")) + flip_anchored() + to_chat(src, "You are now [is_anchored ? "" : "un"]anchored.") + +/mob/living/silicon/ai/proc/flip_anchored() + if(is_anchored) + is_anchored = !is_anchored move_resist = MOVE_FORCE_NORMAL + status_flags |= CANPUSH REMOVE_TRAIT(src, TRAIT_NO_TELEPORT, src) else - is_anchored = TRUE + is_anchored = !is_anchored move_resist = MOVE_FORCE_OVERPOWERING + status_flags &= ~CANPUSH ADD_TRAIT(src, TRAIT_NO_TELEPORT, src) - to_chat(src, "You are now [is_anchored ? "" : "un"]anchored.") - // the message in the [] will change depending whether or not the AI is anchored +/mob/living/silicon/ai/proc/ai_mob_to_structure() + disconnect_shell() + ShutOffDoomsdayDevice() + var/obj/structure/ai_core/deactivated/ai_core = new(get_turf(src), /* skip_mmi_creation = */ TRUE) + if(make_mmi_drop_and_transfer(ai_core.core_mmi, the_core = ai_core)) + qdel(src) + return ai_core + +/mob/living/silicon/ai/proc/make_mmi_drop_and_transfer(obj/item/mmi/the_mmi, the_core) + var/mmi_type + if(posibrain_inside) + mmi_type = new/obj/item/mmi/posibrain(src, /* autoping = */ FALSE) + else + mmi_type = new/obj/item/mmi(src) + if(hack_software) + new/obj/item/malf_upgrade(get_turf(src)) + the_mmi = mmi_type + the_mmi.brain = new /obj/item/organ/brain(the_mmi) + the_mmi.brain.organ_flags |= ORGAN_FROZEN + the_mmi.brain.name = "[real_name]'s brain" + the_mmi.name = "[initial(the_mmi.name)]: [real_name]" + the_mmi.brainmob = new /mob/living/brain(the_mmi) + the_mmi.brainmob.name = src.real_name + the_mmi.brainmob.real_name = src.real_name + the_mmi.brainmob.container = the_mmi + the_mmi.brainmob.suiciding = suiciding + if(the_core) + var/obj/structure/ai_core/core = the_core + core.core_mmi = the_mmi + the_mmi.forceMove(the_core) + else + the_mmi.forceMove(get_turf(src)) + if(the_mmi.brainmob.stat == DEAD && !suiciding) + the_mmi.brainmob.stat = CONSCIOUS + if(mind) + mind.transfer_to(the_mmi.brainmob) + the_mmi.update_appearance() + return TRUE // AIs are immobile /mob/living/silicon/ai/update_mobility() @@ -427,18 +484,30 @@ return if (href_list["ai_take_control"]) //Mech domination - var/obj/vehicle/sealed/mecha/M = locate(href_list["ai_take_control"]) + var/obj/vehicle/sealed/mecha/M = locate(href_list["ai_take_control"]) in GLOB.mechas_list + if (!M) + return + + var/mech_has_controlbeacon = FALSE + for(var/obj/item/mecha_parts/mecha_tracking/ai_control/A in M.trackers) + mech_has_controlbeacon = TRUE + break + if(!can_dominate_mechs && !mech_has_controlbeacon) + message_admins("Warning: possible href exploit by [key_name(usr)] - attempted control of a mecha without can_dominate_mechs or a control beacon in the mech.") + usr.log_message("possibly attempting href exploit - attempted control of a mecha without can_dominate_mechs or a control beacon in the mech.", LOG_ADMIN) + return + if(controlled_equipment) - to_chat(src, "You are already loaded into an onboard computer!") + to_chat(src, span_warning("You are already loaded into an onboard computer!")) return if(!GLOB.cameranet.checkCameraVis(M)) - to_chat(src, "Exosuit is no longer near active cameras.") + to_chat(src, span_warning("Exosuit is no longer near active cameras.")) return if(!isturf(loc)) - to_chat(src, "You aren't in your core!") + to_chat(src, span_warning("You aren't in your core!")) return if(M) - M.transfer_ai(AI_MECH_HACK,src, usr) //Called om the mech itself. + M.transfer_ai(AI_MECH_HACK, src, usr) //Called on the mech itself. /mob/living/silicon/ai/proc/switchCamera(obj/machinery/camera/C) @@ -825,10 +894,12 @@ to_chat(user, "No intelligence patterns detected." ) return ShutOffDoomsdayDevice() - new /obj/structure/AIcore/deactivated(loc)//Spawns a deactivated terminal at AI location. + var/obj/structure/ai_core/new_core = new /obj/structure/ai_core/deactivated(loc, posibrain_inside)//Spawns a deactivated terminal at AI location. + new_core.circuit.battery = battery ai_restore_power()//So the AI initially has power. - control_disabled = 1//Can't control things remotely if you're stuck in a card! - radio_enabled = 0 //No talking on the built-in radio for you either! + control_disabled = TRUE //Can't control things remotely if you're stuck in a card! + interaction_range = 0 + radio_enabled = FALSE //No talking on the built-in radio for you either! forceMove(card) card.AI = src to_chat(src, "You have been downloaded to a mobile storage device. Remote device connection severed.") @@ -1063,7 +1134,7 @@ // Sends an announcement the AI has cryoed. var/obj/machinery/announcement_system/announcer = pick(GLOB.announcement_systems) announcer.announce("CRYOSTORAGE", src.real_name, announce_rank, list()) - new /obj/structure/AIcore/latejoin_inactive(loc) + new /obj/structure/ai_core/latejoin_inactive(loc) if(src.mind) //Handle job slot/tater cleanup. if(src.mind.assigned_role == "AI") diff --git a/code/modules/mob/living/silicon/ai/ai_defense.dm b/code/modules/mob/living/silicon/ai/ai_defense.dm index b8a0a15e70..17d4e9d129 100644 --- a/code/modules/mob/living/silicon/ai/ai_defense.dm +++ b/code/modules/mob/living/silicon/ai/ai_defense.dm @@ -2,7 +2,7 @@ . = ..() if(!.) return FALSE - if(I.force && I.damtype != STAMINA && stat != DEAD) //only sparks if real damage is dealt. + if(I.force && I.damtype != STAMINA && stat != DEAD && !QDELETED(src)) //only sparks if real damage is dealt. spark_system.start() /mob/living/silicon/ai/attack_slime(mob/living/simple_animal/slime/user) @@ -48,3 +48,105 @@ /mob/living/silicon/ai/flash_act(intensity = 1, override_blindness_check = 0, affect_silicon = 0) return // no eyes, no flashing + +/mob/living/silicon/ai/emag_act(mob/user, obj/item/card/emag/emag_card) ///emags access panel lock, so you can crowbar it without robotics access or consent + . = ..() + if(emagged) + balloon_alert(user, "access panel lock already shorted!") + return + balloon_alert(user, "access panel lock shorted") + var/message = (user ? "[user] shorts out your access panel lock!" : "Your access panel lock was short circuited!") + to_chat(src, span_warning(message)) + do_sparks(3, FALSE, src) // just a bit of extra "oh shit" to the ai - might grab its attention + emagged = TRUE + return TRUE + +/mob/living/silicon/ai/wrench_act(mob/living/user, obj/item/tool) + . = ..() + if(user.a_intent == INTENT_HARM) + return + balloon_alert(user, "[!is_anchored ? "tightening" : "loosening"] bolts...") + balloon_alert(src, "bolts being [!is_anchored ? "tightened" : "loosened"]...") + if(!tool.use_tool(src, user, 4 SECONDS)) + return TRUE + flip_anchored() + balloon_alert(user, "bolts [is_anchored ? "tightened" : "loosened"]") + balloon_alert(src, "bolts [is_anchored ? "tightened" : "loosened"]") + return TRUE + +/mob/living/silicon/ai/crowbar_act(mob/living/user, obj/item/tool) + . = ..() + if(user.a_intent == INTENT_HARM) + return + if(!is_anchored) + balloon_alert(user, "bolt it down first!") + return TRUE + if(opened) + if(emagged) + balloon_alert(user, "access panel lock damaged!") + return TRUE + balloon_alert(user, "closing access panel...") + balloon_alert(src, "access panel being closed...") + if(!tool.use_tool(src, user, 5 SECONDS)) + return TRUE + balloon_alert(src, "access panel closed") + balloon_alert(user, "access panel closed") + opened = FALSE + return TRUE + if(stat == DEAD) + to_chat(user, span_warning("The access panel looks damaged, you try dislodging the cover.")) + else + var/consent + var/consent_override = FALSE + if(ishuman(user)) + var/mob/living/carbon/human/human_user = user + if(human_user.wear_id) + var/list/access = human_user.wear_id.GetAccess() + if(ACCESS_ROBOTICS in access) + consent_override = TRUE + if(mind) + consent = tgui_alert(src, "[user] is attempting to open your access panel, unlock the cover?", "AI Access Panel", list("Yes", "No")) + if(consent == "No" && !consent_override && !emagged) + to_chat(user, span_notice("[src] refuses to unlock its access panel.")) + return TRUE + if(consent != "Yes" && (consent_override || emagged)) + to_chat(user, span_warning("[src] refuses to unlock its access panel...so you[!emagged ? " swipe your ID and " : " "]open it anyway!")) + else + if(!consent_override && !emagged) + to_chat(user, span_notice("[src] did not respond to your request to unlock its access panel cover lock.")) + return TRUE + else + to_chat(user, span_notice("[src] did not respond to your request to unlock its access panel cover lock. You[!emagged ? " swipe your ID and " : " "]open it anyway.")) + + balloon_alert(user, "prying open access panel...") + balloon_alert(src, "access panel being pried open...") + if(!tool.use_tool(src, user, (stat == DEAD ? 40 SECONDS : 5 SECONDS))) + return TRUE + balloon_alert(src, "access panel opened") + balloon_alert(user, "access panel opened") + opened = TRUE + return TRUE + +/mob/living/silicon/ai/wirecutter_act(mob/living/user, obj/item/tool) + . = ..() + if(user.a_intent == INTENT_HARM) + return + if(!is_anchored) + balloon_alert(user, "bolt it down first!") + return TRUE + if(!opened) + balloon_alert(user, "open the access panel first!") + return TRUE + balloon_alert(src, "neural network being disconnected...") + balloon_alert(user, "disconnecting neural network...") + if(!tool.use_tool(src, user, (stat == DEAD ? 40 SECONDS : 5 SECONDS))) + return TRUE + if(IS_MALF_AI(src)) + to_chat(user, span_userdanger("The voltage inside the wires rises dramatically!")) + user.electrocute_act(120, src) + opened = FALSE + return TRUE + to_chat(src, span_danger("You feel incredibly confused and disorientated.")) + var/atom/ai_structure = ai_mob_to_structure() + ai_structure.balloon_alert(user, "disconnected neural network") + return TRUE diff --git a/code/modules/mob/living/silicon/ai/life.dm b/code/modules/mob/living/silicon/ai/life.dm index ed969e49fb..892fa6955b 100644 --- a/code/modules/mob/living/silicon/ai/life.dm +++ b/code/modules/mob/living/silicon/ai/life.dm @@ -28,11 +28,15 @@ // Handle power damage (oxy) if(aiRestorePowerRoutine) // Lost power - adjustOxyLoss(1) + if (!battery) + to_chat(src, span_warning("Your backup battery's output drops below usable levels. It takes only a moment longer for your systems to fail, corrupted and unusable.")) + adjustOxyLoss(200) + else + battery-- else // Gain Power - if(getOxyLoss()) - adjustOxyLoss(-1) + if (battery < 200) + battery++ if(!lacks_power()) var/area/home = get_area(src) diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index 77a88960d5..dbc2c3f587 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -162,7 +162,7 @@ if(BORG_SEC_AVAILABLE) modulelist["Security"] = /obj/item/robot_module/security - var/input_module = input("Please, select a module!", "Robot", null, null) as null|anything in sortList(modulelist) + var/input_module = input("Please, select a module!", "Robot", null, null) as null|anything in sort_list(modulelist) if(!input_module || module.type != /obj/item/robot_module) return diff --git a/code/modules/mob/living/silicon/robot/robot_defines.dm b/code/modules/mob/living/silicon/robot/robot_defines.dm index 612298e526..b54473f7a6 100644 --- a/code/modules/mob/living/silicon/robot/robot_defines.dm +++ b/code/modules/mob/living/silicon/robot/robot_defines.dm @@ -50,7 +50,6 @@ var/obj/item/stock_parts/cell/cell = /obj/item/stock_parts/cell/high ///If this is a path, this gets created as an object in Initialize. var/opened = FALSE - var/emagged = FALSE var/emag_cooldown = 0 var/wiresexposed = FALSE diff --git a/code/modules/mob/living/silicon/robot/robot_modules.dm b/code/modules/mob/living/silicon/robot/robot_modules.dm index de2f4635d4..4459bd4413 100644 --- a/code/modules/mob/living/silicon/robot/robot_modules.dm +++ b/code/modules/mob/living/silicon/robot/robot_modules.dm @@ -380,7 +380,7 @@ var/image/bad_snowflake = image(icon = 'modular_citadel/icons/mob/widerobot.dmi', icon_state = "alina-med") bad_snowflake.pixel_x = -16 med_icons["Alina"] = bad_snowflake - med_icons = sortList(med_icons) + med_icons = sort_list(med_icons) var/med_borg_icon = show_radial_menu(R, R , med_icons, custom_check = CALLBACK(src, .proc/check_menu, R), radius = 42, require_near = TRUE) switch(med_borg_icon) if("Default") @@ -498,7 +498,7 @@ var/image/bad_snowflake = image(icon = 'modular_citadel/icons/mob/widerobot.dmi', icon_state = "alina-eng") bad_snowflake.pixel_x = -16 engi_icons["Alina"] = bad_snowflake - engi_icons = sortList(engi_icons) + engi_icons = sort_list(engi_icons) var/engi_borg_icon = show_radial_menu(R, R , engi_icons, custom_check = CALLBACK(src, .proc/check_menu, R), radius = 42, require_near = TRUE) switch(engi_borg_icon) if("Default") @@ -595,7 +595,7 @@ var/image/bad_snowflake = image(icon = 'modular_citadel/icons/mob/widerobot.dmi', icon_state = "alina-sec") bad_snowflake.pixel_x = -16 sec_icons["Alina"] = bad_snowflake - sec_icons = sortList(sec_icons) + sec_icons = sort_list(sec_icons) var/sec_borg_icon = show_radial_menu(R, R , sec_icons, custom_check = CALLBACK(src, .proc/check_menu, R), radius = 42, require_near = TRUE) switch(sec_borg_icon) if("Default") @@ -681,7 +681,7 @@ /obj/item/robot_module/peacekeeper/be_transformed_to(obj/item/robot_module/old_module) var/mob/living/silicon/robot/R = loc - var/static/list/peace_icons = sortList(list( + var/static/list/peace_icons = sort_list(list( "Default" = image(icon = 'icons/mob/robots.dmi', icon_state = "peace"), "Borgi" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "borgi"), "Spider" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "whitespider") @@ -852,7 +852,7 @@ var/image/bad_snowflake = image(icon = 'modular_citadel/icons/mob/widerobot.dmi', icon_state = "alina-sec") bad_snowflake.pixel_x = -16 service_icons["Alina"] = bad_snowflake - service_icons = sortList(service_icons) + service_icons = sort_list(service_icons) var/service_robot_icon = show_radial_menu(R, R , service_icons, custom_check = CALLBACK(src, .proc/check_menu, R), radius = 42, require_near = TRUE) switch(service_robot_icon) if("(Service) Waitress") @@ -966,7 +966,7 @@ var/image/wide = image(icon = 'modular_citadel/icons/mob/widerobot.dmi', icon_state = L[a]) wide.pixel_x = -16 mining_icons[a] = wide - mining_icons = sortList(mining_icons) + mining_icons = sort_list(mining_icons) var/mining_borg_icon = show_radial_menu(R, R , mining_icons, custom_check = CALLBACK(src, .proc/check_menu, R), radius = 42, require_near = TRUE) switch(mining_borg_icon) if("Lavaland") diff --git a/code/modules/mob/living/silicon/silicon.dm b/code/modules/mob/living/silicon/silicon.dm index 83ce09b315..347c71673e 100644 --- a/code/modules/mob/living/silicon/silicon.dm +++ b/code/modules/mob/living/silicon/silicon.dm @@ -44,6 +44,8 @@ var/obj/machinery/camera/builtInCamera = null var/updating = FALSE //portable camera camerachunk update + ///Whether we have been emagged + var/emagged = FALSE var/hack_software = FALSE //Will be able to use hacking actions var/interaction_range = 7 //wireless control range diff --git a/code/modules/modular_computers/computers/item/computer.dm b/code/modules/modular_computers/computers/item/computer.dm index e2345a6384..2d82988997 100644 --- a/code/modules/modular_computers/computers/item/computer.dm +++ b/code/modules/modular_computers/computers/item/computer.dm @@ -454,7 +454,7 @@ var/obj/item/computer_hardware/H = all_components[h] component_names.Add(H.name) - var/choice = input(user, "Which component do you want to uninstall?", "Computer maintenance", null) as null|anything in sortList(component_names) + var/choice = input(user, "Which component do you want to uninstall?", "Computer maintenance", null) as null|anything in sort_list(component_names) if(!choice) return diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm index 8c81792d27..a0f98f4046 100644 --- a/code/modules/projectiles/gun.dm +++ b/code/modules/projectiles/gun.dm @@ -137,7 +137,7 @@ burst_size = 1 - sortList(fire_select_modes, /proc/cmp_numeric_asc) + sort_list(fire_select_modes, /proc/cmp_numeric_asc) if(fire_select_modes.len > 1) firemode_action = new(src) diff --git a/code/modules/reagents/chemistry/machinery/chem_dispenser.dm b/code/modules/reagents/chemistry/machinery/chem_dispenser.dm index 1a64045ed0..779be376dd 100644 --- a/code/modules/reagents/chemistry/machinery/chem_dispenser.dm +++ b/code/modules/reagents/chemistry/machinery/chem_dispenser.dm @@ -94,16 +94,16 @@ /obj/machinery/chem_dispenser/Initialize(mapload) . = ..() - dispensable_reagents = sortList(dispensable_reagents, /proc/cmp_reagents_asc) + dispensable_reagents = sort_list(dispensable_reagents, /proc/cmp_reagents_asc) if(emagged_reagents) - emagged_reagents = sortList(emagged_reagents, /proc/cmp_reagents_asc) + emagged_reagents = sort_list(emagged_reagents, /proc/cmp_reagents_asc) if(upgrade_reagents) - upgrade_reagents = sortList(upgrade_reagents, /proc/cmp_reagents_asc) + upgrade_reagents = sort_list(upgrade_reagents, /proc/cmp_reagents_asc) if(upgrade_reagents2) - upgrade_reagents2 = sortList(upgrade_reagents2, /proc/cmp_reagents_asc) + upgrade_reagents2 = sort_list(upgrade_reagents2, /proc/cmp_reagents_asc) if(upgrade_reagents3) - upgrade_reagents3 = sortList(upgrade_reagents3, /proc/cmp_reagents_asc) - dispensable_reagents = sortList(dispensable_reagents, /proc/cmp_reagents_asc) + upgrade_reagents3 = sort_list(upgrade_reagents3, /proc/cmp_reagents_asc) + dispensable_reagents = sort_list(dispensable_reagents, /proc/cmp_reagents_asc) create_reagents(200, NO_REACT) update_icon() diff --git a/code/modules/vehicles/mecha/_mecha.dm b/code/modules/vehicles/mecha/_mecha.dm index f4ffda302e..e0c4ce81e4 100644 --- a/code/modules/vehicles/mecha/_mecha.dm +++ b/code/modules/vehicles/mecha/_mecha.dm @@ -198,7 +198,8 @@ /obj/vehicle/sealed/mecha/Destroy() for(var/ejectee in occupants) - mob_exit(ejectee, TRUE, TRUE) + mob_exit(ejectee, silent = TRUE) + if(LAZYLEN(equipment)) for(var/obj/item/mecha_parts/mecha_equipment/equip as anything in equipment) equip.detach(loc) @@ -215,6 +216,8 @@ QDEL_NULL(cabin_air) QDEL_NULL(spark_system) QDEL_NULL(smoke_system) + QDEL_NULL(trackers) + QDEL_NULL(wires) GLOB.poi_list -= src GLOB.mechas_list -= src //global mech list @@ -223,14 +226,39 @@ return ..() /obj/vehicle/sealed/mecha/obj_destruction() + spark_system?.start() loc.assume_air(cabin_air) air_update_turf(FALSE, FALSE) + + var/mob/living/silicon/ai/unlucky_ai for(var/mob/living/occupant as anything in occupants) if(isAI(occupant)) - occupant.gib() //No wreck, no AI to recover + var/mob/living/silicon/ai/ai = occupant + if(!ai.linked_core) // we probably shouldnt gib AIs with a core + unlucky_ai = occupant + ai.investigate_log("has been gibbed by having their mech destroyed.", INVESTIGATE_DEATHS) + ai.gib() //No wreck, no AI to recover + else + mob_exit(ai,silent = TRUE, forced = TRUE) // so we dont ghost the AI continue - mob_exit(occupant, FALSE, TRUE) - occupant.SetSleeping(destruction_sleep_duration) + mob_exit(occupant, forced = TRUE) + if(!isbrain(occupant)) // who would win.. 1 brain vs 1 sleep proc.. + occupant.SetSleeping(destruction_sleep_duration) + + if(wreckage) + var/obj/structure/mecha_wreckage/WR = new wreckage(loc, unlucky_ai) + for(var/obj/item/mecha_parts/mecha_equipment/E in equipment) + if(E.detachable && prob(30)) + WR.crowbar_salvage += E + E.detach(WR) //detaches from src into WR + else + E.detach(loc) + qdel(E) + if(cell) + WR.crowbar_salvage += cell + cell.forceMove(WR) + cell.use(rand(0, cell.charge), TRUE) + cell = null return ..() /obj/vehicle/sealed/mecha/update_icon() @@ -480,7 +508,7 @@ var/mob/living/occupant = b if(!enclosed && occupant?.incapacitated()) //no sides mean it's easy to just sorta fall out if you're incapacitated. visible_message("[occupant] tumbles out of the cockpit!") - mob_try_exit(occupant, TRUE, TRUE) //bye bye + mob_exit(occupant, randomstep = TRUE) //bye bye //Diagnostic HUD updates diag_hud_set_mechhealth() @@ -796,19 +824,31 @@ to_chat(user, "Take control of exosuit?
") /obj/vehicle/sealed/mecha/transfer_ai(interaction, mob/user, mob/living/silicon/ai/AI, obj/item/aicard/card) - if(!..()) + . = ..() + if(!.) return //Transfer from core or card to mech. Proc is called by mech. switch(interaction) if(AI_TRANS_TO_CARD) //Upload AI from mech to AI card. if(!construction_state) //Mech must be in maint mode to allow carding. - to_chat(user, "[name] must have maintenance protocols active in order to allow a transfer.") + to_chat(user, span_warning("[name] must have maintenance protocols active in order to allow a transfer.")) return - if(!locate(AI) in occupants) //Mech does not have an AI for a pilot - to_chat(user, "No AI detected in the [name] onboard computer.") + var/list/ai_pilots = list() + for(var/mob/living/silicon/ai/aipilot in occupants) + ai_pilots += aipilot + if(!length(ai_pilots)) //Mech does not have an AI for a pilot + to_chat(user, span_warning("No AI detected in the [name] onboard computer.")) return - for(var/mob/living/silicon/ai in occupants) + if(length(ai_pilots) > 1) //Input box for multiple AIs, but if there's only one we'll default to them. + AI = tgui_input_list(user, "Which AI do you wish to card?", "AI Selection", sort_list(ai_pilots)) + else + AI = ai_pilots[1] + if(isnull(AI)) + return + if(!(AI in occupants) || !user.Adjacent(src)) + return //User sat on the selection window and things changed. + AI.ai_restore_power()//So the AI initially has power. AI.control_disabled = TRUE AI.radio_enabled = FALSE @@ -819,36 +859,37 @@ card.AI = AI AI.controlled_equipment = null AI.remote_control = null - to_chat(AI, "You have been downloaded to a mobile storage device. Wireless connection offline.") - to_chat(user, "Transfer successful: [AI.name] ([rand(1000,9999)].exe) removed from [name] and stored within local memory.") + to_chat(AI, span_notice("You have been downloaded to a mobile storage device. Wireless connection offline.")) + to_chat(user, "[span_boldnotice("Transfer successful")]: [AI.name] ([rand(1000,9999)].exe) removed from [name] and stored within local memory.") + return if(AI_MECH_HACK) //Called by AIs on the mech - AI.linked_core = new /obj/structure/AIcore/deactivated(AI.loc) - if(AI.can_dominate_mechs) - if(LAZYLEN(occupants)) //Oh, I am sorry, were you using that? - to_chat(AI, "Occupants detected! Forced ejection initiated!") - to_chat(occupants, "You have been forcibly ejected!") - ejectall() //IT IS MINE, NOW. SUCK IT, RD! - ai_enter_mech(AI, interaction) + AI.linked_core = new /obj/structure/ai_core/deactivated(AI.loc) + if(AI.can_dominate_mechs && LAZYLEN(occupants)) //Oh, I am sorry, were you using that? + to_chat(AI, span_warning("Occupants detected! Forced ejection initiated!")) + to_chat(occupants, span_danger("You have been forcibly ejected!")) + for(var/ejectee in occupants) + mob_exit(ejectee, silent = TRUE, randomstep = TRUE, forced = TRUE) //IT IS MINE, NOW. SUCK IT, RD! + AI.can_shunt = FALSE //ONE AI ENTERS. NO AI LEAVES. if(AI_TRANS_FROM_CARD) //Using an AI card to upload to a mech. AI = card.AI if(!AI) - to_chat(user, "There is no AI currently installed on this device.") + to_chat(user, span_warning("There is no AI currently installed on this device.")) return if(AI.deployed_shell) //Recall AI if shelled so it can be checked for a client AI.disconnect_shell() if(AI.stat || !AI.client) - to_chat(user, "[AI.name] is currently unresponsive, and cannot be uploaded.") + to_chat(user, span_warning("[AI.name] is currently unresponsive, and cannot be uploaded.")) return if((LAZYLEN(occupants) >= max_occupants) || dna_lock) //Normal AIs cannot steal mechs! - to_chat(user, "Access denied. [name] is [LAZYLEN(occupants) >= max_occupants ? "currently fully occupied" : "secured with a DNA lock"].") + to_chat(user, span_warning("Access denied. [name] is [LAZYLEN(occupants) >= max_occupants ? "currently fully occupied" : "secured with a DNA lock"].")) return AI.control_disabled = FALSE AI.radio_enabled = TRUE - to_chat(user, "Transfer successful: [AI.name] ([rand(1000,9999)].exe) installed and executed successfully. Local copy has been removed.") + to_chat(user, "[span_boldnotice("Transfer successful")]: [AI.name] ([rand(1000,9999)].exe) installed and executed successfully. Local copy has been removed.") card.AI = null - ai_enter_mech(AI, interaction) + ai_enter_mech(AI) //Hack and From Card interactions share some code, so leave that here for both to use. /obj/vehicle/sealed/mecha/proc/ai_enter_mech(mob/living/silicon/ai/AI, interaction) @@ -865,7 +906,6 @@ "You have been uploaded to a mech's onboard computer.") to_chat(AI, "Use Middle-Mouse to activate mech functions and equipment. Click normally for AI interactions.") - ///Handles an actual AI (simple_animal mecha pilot) entering the mech /obj/vehicle/sealed/mecha/proc/aimob_enter_mech(mob/living/simple_animal/hostile/syndicate/mecha_pilot/pilot_mob) if(pilot_mob && pilot_mob.Adjacent(src)) @@ -1042,47 +1082,45 @@ if(isAI(user)) var/mob/living/silicon/ai/AI = user if(!AI.can_shunt) - to_chat(AI, "You can't leave a mech after dominating it!.") + to_chat(AI, span_notice("You can't leave a mech after dominating it!.")) return FALSE - to_chat(user, "You begin the ejection procedure. Equipment is disabled during this process. Hold still to finish ejecting.") + to_chat(user, span_notice("You begin the ejection procedure. Equipment is disabled during this process. Hold still to finish ejecting.")) is_currently_ejecting = TRUE - if(do_after(user, exit_delay , target = src)) - to_chat(user, "You exit the mech.") - mob_try_exit(user, silent = FALSE) + if(do_after(user, has_gravity() ? exit_delay : 0 , target = src)) + to_chat(user, span_notice("You exit the mech.")) + mob_exit(user, silent = TRUE) else - to_chat(user, "You stop exiting the mech. Weapons are enabled again.") + to_chat(user, span_notice("You stop exiting the mech. Weapons are enabled again.")) is_currently_ejecting = FALSE -/obj/vehicle/sealed/mecha/proc/ejectall() - for(var/ejectee in occupants) - mob_try_exit(ejectee, TRUE, TRUE) - -/obj/vehicle/sealed/mecha/mob_try_exit(mob/M, silent, randomstep) - mob_exit(M, silent, randomstep) - -/obj/vehicle/sealed/mecha/mob_exit(mob/M, silent, forced) - var/newloc = get_turf(src) +/obj/vehicle/sealed/mecha/mob_exit(mob/M, silent = FALSE, randomstep = FALSE, forced = FALSE) var/atom/movable/mob_container + var/turf/newloc = get_turf(src) if(ishuman(M)) - remove_occupant(M) - ..() - return + mob_container = M else if(isbrain(M)) var/mob/living/brain/brain = M mob_container = brain.container else if(isAI(M)) var/mob/living/silicon/ai/AI = M + //stop listening to this signal, as the static update is now handled by the eyeobj's setLoc + AI.eyeobj?.UnregisterSignal(src, COMSIG_MOVABLE_MOVED) + AI.eyeobj?.forceMove(newloc) //kick the eye out as well if(forced)//This should only happen if there are multiple AIs in a round, and at least one is Malf. - AI.gib() //If one Malf decides to steal a mech from another AI (even other Malfs!), they are destroyed, as they have nowhere to go when replaced. + if(!AI.linked_core) //if the victim AI has no core + AI.investigate_log("has been gibbed by being forced out of their mech by another AI.", INVESTIGATE_DEATHS) + AI.gib() //If one Malf decides to steal a mech from another AI (even other Malfs!), they are destroyed, as they have nowhere to go when replaced. AI = null mecha_flags &= ~SILICON_PILOT return else if(!AI.linked_core) - to_chat(AI, "Inactive core destroyed. Unable to return.") + if(!silent) + to_chat(AI, span_userdanger("Inactive core destroyed. Unable to return.")) AI.linked_core = null return - to_chat(AI, "Returning to core...") + if(!silent) + to_chat(AI, span_notice("Returning to core...")) AI.controlled_equipment = null AI.remote_control = null mob_container = AI @@ -1090,21 +1128,20 @@ qdel(AI.linked_core) else return ..() - var/mob/living/L = M + var/mob/living/ejector = M mecha_flags &= ~SILICON_PILOT - if(mob_container.forceMove(newloc)) - log_message("[mob_container] moved out.", LOG_MECHA) - L << browse(null, "window=exosuit") + mob_container.forceMove(newloc)//ejecting mob container + log_message("[mob_container] moved out.", LOG_MECHA) + SStgui.close_user_uis(M, src) if(istype(mob_container, /obj/item/mmi)) var/obj/item/mmi/mmi = mob_container if(mmi.brainmob) - L.forceMove(mmi) - L.reset_perspective() - remove_occupant(L) + ejector.forceMove(mmi) + ejector.reset_perspective() + remove_occupant(ejector) mmi.mecha = null - mmi.update_icon() - L.mobility_flags = NONE - setDir(dir_in) + mmi.update_appearance() + setDir(SOUTH) return ..() diff --git a/code/modules/vehicles/mecha/combat/neovgre.dm b/code/modules/vehicles/mecha/combat/neovgre.dm index d8704e0318..7e40a1c352 100644 --- a/code/modules/vehicles/mecha/combat/neovgre.dm +++ b/code/modules/vehicles/mecha/combat/neovgre.dm @@ -25,7 +25,7 @@ else add_control_flags(M, VEHICLE_CONTROL_MELEE|VEHICLE_CONTROL_EQUIPMENT) -/obj/vehicle/sealed/mecha/combat/neovgre/mob_exit(mob/M, silent, forced) +/obj/vehicle/sealed/mecha/combat/neovgre/mob_exit(mob/M, silent, randomstep, forced) if(forced) ..() diff --git a/modular_citadel/code/modules/client/verbs/who.dm b/modular_citadel/code/modules/client/verbs/who.dm index 7640595e18..390c167365 100644 --- a/modular_citadel/code/modules/client/verbs/who.dm +++ b/modular_citadel/code/modules/client/verbs/who.dm @@ -38,7 +38,7 @@ var/client/C = X if(C && C.holder && !C.holder.fakekey) assembled += "\t [C.key][admin_mode? "[show_admin_info(C)]":""] ([round(C.avgping, 1)]ms)" - Lines += sortList(assembled) + Lines += sort_list(assembled) assembled.len = 0 if(length(GLOB.mentors)) Lines += "Mentors:" @@ -46,10 +46,10 @@ var/client/C = X if(C && (!C.holder || (C.holder && !C.holder.fakekey))) //>using stuff this complex instead of just using if/else lmao assembled += "\t [C.key][admin_mode? "[show_admin_info(C)]":""] ([round(C.avgping, 1)]ms)" - Lines += sortList(assembled) + Lines += sort_list(assembled) assembled.len = 0 Lines += "Players:" - for(var/X in sortList(GLOB.clients)) + for(var/X in sort_list(GLOB.clients)) var/client/C = X if(!C) continue @@ -57,8 +57,8 @@ if(C.holder && C.holder.fakekey) key = C.holder.fakekey assembled += "\t [key][admin_mode? "[show_admin_info(C)]":""] ([round(C.avgping, 1)]ms)" - Lines += sortList(assembled) - + Lines += sort_list(assembled) + for(var/line in Lines) msg += "[line]\n" diff --git a/modular_citadel/code/modules/mentor/mentorpm.dm b/modular_citadel/code/modules/mentor/mentorpm.dm index 3260e96767..ba9a951b98 100644 --- a/modular_citadel/code/modules/mentor/mentorpm.dm +++ b/modular_citadel/code/modules/mentor/mentorpm.dm @@ -9,7 +9,7 @@ for(var/client/T) targets["[T]"] = T - var/list/sorted = sortList(targets) + var/list/sorted = sort_list(targets) var/target = input(src,"To whom shall we send a message?","Mentor PM",null) in sorted|null cmd_mentor_pm(targets[target],null) SSblackbox.record_feedback("tally", "Mentor_verb", 1, "APM") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!