From 52afb7d200898b0b612ec393353dde1e710a4adc Mon Sep 17 00:00:00 2001 From: ShizCalev Date: Sat, 1 Aug 2020 05:44:21 -0400 Subject: [PATCH] Cleans up paicard and aicard code (#52575) * Cleans up paicard and aicard code * fix * more fixes --- code/game/machinery/camera/camera.dm | 5 - code/game/objects/items/devices/aicard.dm | 24 ++--- code/game/objects/items/devices/paicard.dm | 55 +++++----- code/game/objects/items/paiwire.dm | 7 +- code/game/objects/structures/ai_core.dm | 3 +- code/modules/mob/living/silicon/pai/pai.dm | 76 +++++++++----- .../mob/living/silicon/pai/software.dm | 94 +++++------------- icons/obj/aicards.dmi | Bin 3816 -> 3774 bytes 8 files changed, 119 insertions(+), 145 deletions(-) diff --git a/code/game/machinery/camera/camera.dm b/code/game/machinery/camera/camera.dm index 2ae4de8d754..8f590d77a4e 100644 --- a/code/game/machinery/camera/camera.dm +++ b/code/game/machinery/camera/camera.dm @@ -347,11 +347,6 @@ bug.bugged_cameras[src.c_tag] = src return - else if(istype(I, /obj/item/pai_cable)) - var/obj/item/pai_cable/cable = I - cable.plugin(src, user) - return - return ..() diff --git a/code/game/objects/items/devices/aicard.dm b/code/game/objects/items/devices/aicard.dm index a938d618b1a..68cf8d961f3 100644 --- a/code/game/objects/items/devices/aicard.dm +++ b/code/game/objects/items/devices/aicard.dm @@ -40,20 +40,20 @@ log_combat(user, AI, "carded", src) update_icon() //Whatever happened, update the card's state (icon, name) to match. -/obj/item/aicard/update_icon() - cut_overlays() - if(AI) - name = "[initial(name)] - [AI.name]" - if(AI.stat == DEAD) - icon_state = "[initial(icon_state)]-404" - else - icon_state = "[initial(icon_state)]-full" - if(!AI.control_disabled) - add_overlay("[initial(icon_state)]-on") - AI.cancel_camera() - else +/obj/item/aicard/update_icon_state() + if(!AI) name = initial(name) icon_state = initial(icon_state) + return + name = "[initial(name)] - [AI.name]" + icon_state = "[initial(icon_state)][AI.stat == DEAD ? "-404" : "-full"]" + AI.cancel_camera() + +/obj/item/aicard/update_overlays() + . = ..() + if(!AI || AI.control_disabled) + return + . += "[initial(icon_state)]-on" /obj/item/aicard/ui_state(mob/user) return GLOB.hands_state diff --git a/code/game/objects/items/devices/paicard.dm b/code/game/objects/items/devices/paicard.dm index cde3702087e..283f3637758 100644 --- a/code/game/objects/items/devices/paicard.dm +++ b/code/game/objects/items/devices/paicard.dm @@ -9,6 +9,7 @@ w_class = WEIGHT_CLASS_SMALL slot_flags = ITEM_SLOT_BELT var/mob/living/silicon/pai/pai + var/emotion_icon = "off" ///what emotion icon we have. handled in /mob/living/silicon/pai/Topic() resistance_flags = FIRE_PROOF | ACID_PROOF | INDESTRUCTIBLE /obj/item/paicard/suicide_act(mob/living/user) @@ -17,13 +18,31 @@ /obj/item/paicard/Initialize() SSpai.pai_card_list += src - add_overlay("pai-off") + . = ..() + update_icon() + +/obj/item/paicard/vv_edit_var(vname, vval) + . = ..() + if(vname == NAMEOF(src, emotion_icon)) + update_icon() + +/obj/item/paicard/handle_atom_del(atom/A) + if(A == pai) //double check /mob/living/silicon/pai/Destroy() if you change these. + pai = null + emotion_icon = initial(emotion_icon) + update_icon() return ..() +/obj/item/paicard/update_overlays() + . = ..() + . += "pai-[emotion_icon]" + if(pai?.hacking_cable) + . += "[initial(icon_state)]-connector" + /obj/item/paicard/Destroy() //Will stop people throwing friend pAIs into the singularity so they can respawn SSpai.pai_card_list -= src - if (!QDELETED(pai)) + if(!QDELETED(pai)) QDEL_NULL(pai) return ..() @@ -130,39 +149,13 @@ // WIRE_TRANSMIT = 4 /obj/item/paicard/proc/setPersonality(mob/living/silicon/pai/personality) - src.pai = personality - src.add_overlay("pai-null") + pai = personality + emotion_icon = "null" + update_icon() playsound(loc, 'sound/effects/pai_boot.ogg', 50, TRUE, -1) audible_message("\The [src] plays a cheerful startup noise!") -/obj/item/paicard/proc/setEmotion(emotion) - if(pai) - src.cut_overlays() - switch(emotion) - if(1) - src.add_overlay("pai-happy") - if(2) - src.add_overlay("pai-cat") - if(3) - src.add_overlay("pai-extremely-happy") - if(4) - src.add_overlay("pai-face") - if(5) - src.add_overlay("pai-laugh") - if(6) - src.add_overlay("pai-off") - if(7) - src.add_overlay("pai-sad") - if(8) - src.add_overlay("pai-angry") - if(9) - src.add_overlay("pai-what") - if(10) - src.add_overlay("pai-null") - if(11) - src.add_overlay("pai-sunglasses") - /obj/item/paicard/proc/alertUpdate() audible_message("[src] flashes a message across its screen, \"Additional personalities available for download.\"", "[src] vibrates with an alert.") diff --git a/code/game/objects/items/paiwire.dm b/code/game/objects/items/paiwire.dm index 0dbf23e937f..1e14be9a385 100644 --- a/code/game/objects/items/paiwire.dm +++ b/code/game/objects/items/paiwire.dm @@ -4,7 +4,12 @@ icon = 'icons/obj/power.dmi' icon_state = "wire1" item_flags = NOBLUDGEON - var/obj/machinery/machine + var/obj/machinery/machine //what machine we're currently hacking. + +/obj/item/pai_cable/machine/Destroy() + machine = null + return ..() + /obj/item/pai_cable/proc/plugin(obj/machinery/M, mob/living/user) if(!user.transferItemToLoc(src, M)) diff --git a/code/game/objects/structures/ai_core.dm b/code/game/objects/structures/ai_core.dm index 3dc69b1a592..cd6c285cebf 100644 --- a/code/game/objects/structures/ai_core.dm +++ b/code/game/objects/structures/ai_core.dm @@ -244,8 +244,7 @@ if(AI_READY_CORE) if(istype(P, /obj/item/aicard)) - P.transfer_ai("INACTIVE", "AICARD", src, user) - return + return //handled by /obj/structure/ai_core/transfer_ai() if(P.tool_behaviour == TOOL_SCREWDRIVER) P.play_tool_sound(src) diff --git a/code/modules/mob/living/silicon/pai/pai.dm b/code/modules/mob/living/silicon/pai/pai.dm index 4baf74fa5e1..4485e86e6a2 100644 --- a/code/modules/mob/living/silicon/pai/pai.dm +++ b/code/modules/mob/living/silicon/pai/pai.dm @@ -13,6 +13,9 @@ maxHealth = 500 layer = BELOW_MOB_LAYER can_be_held = TRUE + move_force = 0 + pull_force = 0 + move_resist = 0 worn_slot_flags = ITEM_SLOT_HEAD held_lh = 'icons/mob/pai_item_lh.dmi' held_rh = 'icons/mob/pai_item_rh.dmi' @@ -32,7 +35,7 @@ var/speakDoubleExclamation = "alarms" var/speakQuery = "queries" - var/obj/item/pai_cable/cable // The cable we produce and use when door or camera jacking + var/obj/item/pai_cable/hacking_cable // The cable we produce when hacking a door var/master // Name of the one who commands us var/master_dna // DNA string for owner verification @@ -66,7 +69,6 @@ var/canholo = TRUE var/can_transmit = TRUE var/can_receive = TRUE - var/obj/item/card/id/access_card = null var/chassis = "repairbot" var/list/possible_chassis = list("cat" = TRUE, "mouse" = TRUE, "monkey" = TRUE, "corgi" = FALSE, "fox" = FALSE, "repairbot" = TRUE, "rabbit" = TRUE, "bat" = FALSE, "butterfly" = FALSE, "hawk" = FALSE, "lizard" = FALSE, "duffel" = TRUE) //assoc value is whether it can be picked up. @@ -92,15 +94,32 @@ /mob/living/silicon/pai/add_sensors() //pAIs have to buy their HUDs return +/mob/living/silicon/pai/handle_atom_del(atom/A) + if(A == hacking_cable) + hacking_cable = null + if(!QDELETED(card)) + card.update_icon() + if(A == internal_instrument) + internal_instrument = null + if(A == newscaster) + newscaster = null + if(A == signaler) + signaler = null + if(A == hostscan) + hostscan = null + return ..() + /mob/living/silicon/pai/Destroy() QDEL_NULL(internal_instrument) - if(cable) - QDEL_NULL(cable) - if (loc != card) + QDEL_NULL(hacking_cable) + QDEL_NULL(newscaster) + QDEL_NULL(signaler) + QDEL_NULL(hostscan) + if(!QDELETED(card) && loc != card) card.forceMove(drop_location()) - card.pai = null - card.cut_overlays() - card.add_overlay("pai-off") + card.pai = null //these are otherwise handled by paicard/handle_atom_del() + card.emotion_icon = initial(card.emotion_icon) + card.update_icon() GLOB.pai_list -= src return ..() @@ -138,19 +157,24 @@ aiPDA.name = real_name + " (" + aiPDA.ownjob + ")" /mob/living/silicon/pai/proc/process_hack() - if(cable && cable.machine && istype(cable.machine, /obj/machinery/door) && cable.machine == hackdoor && get_dist(src, hackdoor) <= 1) + + + if(hacking_cable && hacking_cable.machine && istype(hacking_cable.machine, /obj/machinery/door) && hacking_cable.machine == hackdoor && get_dist(src, hackdoor) <= 1) hackprogress = clamp(hackprogress + 4, 0, 100) else temp = "Door Jack: Connection to airlock has been lost. Hack aborted." hackprogress = 0 hacking = FALSE hackdoor = null + QDEL_NULL(hacking_cable) + if(!QDELETED(card)) + card.update_icon() return if(screen == "doorjack" && subscreen == 0) // Update our view, if appropriate paiInterface() if(hackprogress >= 100) hackprogress = 0 - var/obj/machinery/door/D = cable.machine + var/obj/machinery/door/D = hacking_cable.machine D.open() hacking = FALSE @@ -272,13 +296,15 @@ . = ..() if(QDELETED(src) || stat == DEAD) return - if(cable) - if(get_dist(src, cable) > 1) + if(hacking_cable) + if(get_dist(src, hacking_cable) > 1) var/turf/T = get_turf(src) - T.visible_message("[cable] rapidly retracts back into its spool.", "You hear a click and the sound of wire spooling rapidly.") - QDEL_NULL(cable) - if(hacking) - process_hack() + T.visible_message("[hacking_cable] rapidly retracts back into its spool.", "You hear a click and the sound of wire spooling rapidly.") + QDEL_NULL(hacking_cable) + if(!QDELETED(card)) + card.update_icon() + else if(hacking) + process_hack() silent = max(silent - 1, 0) /mob/living/silicon/pai/updatehealth() @@ -291,12 +317,12 @@ emitterhealth = clamp((emitterhealth + emitterregen), -50, emittermaxhealth) /obj/item/paicard/attackby(obj/item/W, mob/user, params) - ..() - user.set_machine(src) - if(pai && pai.encryptmod == TRUE) - if(W.tool_behaviour == TOOL_SCREWDRIVER) - pai.radio.attackby(W, user, params) - else if(istype(W, /obj/item/encryptionkey)) - pai.radio.attackby(W, user, params) - else - to_chat(user, "Encryption Key ports not configured.") + if(pai && (istype(W, /obj/item/encryptionkey) || W.tool_behaviour == TOOL_SCREWDRIVER)) + if(!pai.encryptmod) + to_chat(user, "Encryption Key ports not configured.") + return + user.set_machine(src) + pai.radio.attackby(W, user, params) + return + + return ..() diff --git a/code/modules/mob/living/silicon/pai/software.dm b/code/modules/mob/living/silicon/pai/software.dm index a90b330c590..a2c4d999039 100644 --- a/code/modules/mob/living/silicon/pai/software.dm +++ b/code/modules/mob/living/silicon/pai/software.dm @@ -1,8 +1,6 @@ // TODO: // - Potentially roll HUDs and Records into one // - Shock collar/lock system for prisoner pAIs? -// - Put cable in user's hand instead of on the ground -// - Camera jack /mob/living/silicon/pai/var/list/available_software = list( @@ -20,9 +18,6 @@ "security records" = 10, "camera zoom" = 10, "host scan" = 10, - //"camera jack" = 10, - //"heartbeat sensor" = 10, - //"projection array" = 15, "medical HUD" = 20, "security HUD" = 20, "loudness booster" = 20, @@ -72,8 +67,6 @@ left_part = medicalAnalysis() if("doorjack") left_part = softwareDoor() - if("camerajack") - left_part = softwareCamera() if("signaller") left_part = softwareSignal() if("loudness") @@ -158,32 +151,14 @@ if("image") // Set pAI card display face var/newImage = input("Select your new display image.", "Display Image", "Happy") in sortList(list("Happy", "Cat", "Extremely Happy", "Face", "Laugh", "Off", "Sad", "Angry", "What", "Sunglasses")) - var/pID = 1 - switch(newImage) - if("Happy") - pID = 1 - if("Cat") - pID = 2 + if(null) + card.emotion_icon = "null" if("Extremely Happy") - pID = 3 - if("Face") - pID = 4 - if("Laugh") - pID = 5 - if("Off") - pID = 6 - if("Sad") - pID = 7 - if("Angry") - pID = 8 - if("What") - pID = 9 - if("Null") - pID = 10 - if("Sunglasses") - pID = 11 - card.setEmotion(pID) + card.emotion_icon = "extremely-happy" + else + card.emotion_icon = "[lowertext(newImage)]" + card.update_icon() if("news") newscaster.ui_interact(src) @@ -285,15 +260,25 @@ if("doorjack") if(href_list["jack"]) - if(cable && cable.machine) - hackdoor = cable.machine + if(hacking_cable?.machine) + hackdoor = hacking_cable.machine hackloop() if(href_list["cancel"]) hackdoor = null if(href_list["cable"]) - var/turf/T = get_turf(loc) - cable = new /obj/item/pai_cable(T) - T.visible_message("A port on [src] opens to reveal [cable], which promptly falls to the floor.", "You hear the soft click of something light and hard falling to the ground.") + qdel(hacking_cable) //clear any old cables + hacking_cable = new + var/transfered_to_mob + if(isliving(card.loc)) + var/mob/living/L = card.loc + if(L.put_in_hands(hacking_cable)) + transfered_to_mob = TRUE + L.visible_message("A port on [src] opens to reveal \a [hacking_cable], which you quickly grab hold of.", "You hear the soft click of something light and manage to catch hold of [hacking_cable].") + if(!transfered_to_mob) + hacking_cable.forceMove(drop_location()) + hacking_cable.visible_message("A port on [src] opens to reveal \a [hacking_cable], which promptly falls to the floor.", "You hear the soft click of something light and hard falling to the ground.") + + if("loudness") if(subscreen == 1) // Open Instrument @@ -329,8 +314,6 @@ dat += "Medical Records
" if(s == "security records") dat += "Security Records
" - if(s == "camera") - dat += "Camera Jack
" if(s == "remote signaller") dat += "Remote Signaller
" if(s == "loudness booster") @@ -344,8 +327,6 @@ dat += "Adjust Camera Zoom
" if(s == "atmosphere sensor") dat += "Atmospheric Sensor
" - if(s == "heartbeat sensor") - dat += "Heartbeat Sensor
" if(s == "security HUD") dat += "Facial Recognition Suite[(secHUD) ? " On" : " Off"]
" if(s == "medical HUD") @@ -355,10 +336,6 @@ if(s == "universal translator") var/datum/language_holder/H = get_language_holder() dat += "Universal Translator[H.omnitongue ? " On" : " Off"]
" - if(s == "projection array") - dat += "Projection Array
" - if(s == "camera jack") - dat += "Camera Jack
" if(s == "door jack") dat += "Door Jack
" dat += "
" @@ -576,41 +553,20 @@ dat += "Refresh Reading
" dat += "
" return dat - -// Camera Jack - Clearly not finished -/mob/living/silicon/pai/proc/softwareCamera() - var/dat = "

Camera Jack

" - dat += "Cable status : " - - if(!cable) - dat += "Retracted
" - return dat - if(!cable.machine) - dat += "Extended
" - return dat - - var/obj/machinery/machine = cable.machine - dat += "Connected
" - - if(!istype(machine, /obj/machinery/camera)) - to_chat(src, "DERP") - return dat - // Door Jack /mob/living/silicon/pai/proc/softwareDoor() var/dat = "

Airlock Jack

" dat += "Cable status : " - if(!cable) + if(!hacking_cable) dat += "Retracted
" - dat += "Extend Cable
" + dat += "Extend Cable
" return dat - if(!cable.machine) + if(!hacking_cable.machine) dat += "Extended
" return dat - var/obj/machinery/machine = cable.machine dat += "Connected
" - if(!istype(machine, /obj/machinery/door)) + if(!istype(hacking_cable.machine, /obj/machinery/door)) dat += "Connected device's firmware does not appear to be compatible with Airlock Jack protocols.
" return dat diff --git a/icons/obj/aicards.dmi b/icons/obj/aicards.dmi index 07bff4ad724f81733416ec389b360ea6830a1ec0..ccac1852a733f79c055aeb49257d2401546b8013 100644 GIT binary patch delta 3374 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 J`kGt(-vQPa2G{@q