From 32bc9a96b60eb51c84e89f972635bdf11f315f3d Mon Sep 17 00:00:00 2001 From: SkyratBot <59378654+SkyratBot@users.noreply.github.com> Date: Sun, 16 Jan 2022 18:40:00 +0100 Subject: [PATCH] [MIRROR] General pAI code improvements [MDB IGNORE] (#10683) * General pAI code improvements * Feex Co-authored-by: John Willard <53777086+JohnFulpWillard@users.noreply.github.com> Co-authored-by: GoldenAlpharex <58045821+GoldenAlpharex@users.noreply.github.com> --- code/__DEFINES/jobs.dm | 1 + code/__DEFINES/pai.dm | 2 - code/_globalvars/lists/poll_ignore.dm | 2 +- code/_onclick/item_attack.dm | 4 +- code/game/objects/items/devices/paicard.dm | 33 +- .../game/objects/items/devices/radio/radio.dm | 51 ++- code/game/objects/items/paiwire.dm | 2 +- code/game/say.dm | 2 +- code/modules/mob/living/silicon/pai/login.dm | 10 + code/modules/mob/living/silicon/pai/pai.dm | 290 +++++++----------- .../mob/living/silicon/pai/pai_actions.dm | 58 ++++ .../mob/living/silicon/pai/pai_defense.dm | 37 +-- .../modules/mob/living/silicon/pai/pai_say.dm | 6 - .../mob/living/silicon/pai/personality.dm | 28 +- .../mob/living/silicon/pai/software.dm | 111 +++---- .../mob/living/silicon/robot/robot_defense.dm | 4 +- tgstation.dme | 3 +- 17 files changed, 303 insertions(+), 341 deletions(-) delete mode 100644 code/__DEFINES/pai.dm create mode 100644 code/modules/mob/living/silicon/pai/login.dm create mode 100644 code/modules/mob/living/silicon/pai/pai_actions.dm diff --git a/code/__DEFINES/jobs.dm b/code/__DEFINES/jobs.dm index 959d7d30b1a..2b169b004ec 100644 --- a/code/__DEFINES/jobs.dm +++ b/code/__DEFINES/jobs.dm @@ -39,6 +39,7 @@ //Silicon #define JOB_AI "AI" #define JOB_CYBORG "Cyborg" +#define JOB_PERSONAL_AI "Personal AI" //Security #define JOB_WARDEN "Warden" #define JOB_DETECTIVE "Detective" diff --git a/code/__DEFINES/pai.dm b/code/__DEFINES/pai.dm deleted file mode 100644 index 4359cf9d8ea..00000000000 --- a/code/__DEFINES/pai.dm +++ /dev/null @@ -1,2 +0,0 @@ -/// Total percent required to hack a door. -#define HACK_COMPLETE 100 diff --git a/code/_globalvars/lists/poll_ignore.dm b/code/_globalvars/lists/poll_ignore.dm index 1d464c16456..7fb95512adf 100644 --- a/code/_globalvars/lists/poll_ignore.dm +++ b/code/_globalvars/lists/poll_ignore.dm @@ -44,7 +44,7 @@ GLOBAL_LIST_INIT(poll_ignore_desc, list( POLL_IGNORE_CONTRACTOR_SUPPORT = "Contractor Support Unit", POLL_IGNORE_ACADEMY_WIZARD = "Academy Wizard Defender", POLL_IGNORE_CORTICAL_BORER = "Cortical Borer", - POLL_IGNORE_PAI = "Personal AI" + POLL_IGNORE_PAI = JOB_PERSONAL_AI, )) //Skyrat Edit Stop: Cortical Borer GLOBAL_LIST_INIT(poll_ignore, init_poll_ignore()) diff --git a/code/_onclick/item_attack.dm b/code/_onclick/item_attack.dm index f1ce7ede67f..86febd87c0d 100644 --- a/code/_onclick/item_attack.dm +++ b/code/_onclick/item_attack.dm @@ -144,8 +144,8 @@ return SECONDARY_ATTACK_CALL_NORMAL -/obj/attackby(obj/item/I, mob/living/user, params) - return ..() || ((obj_flags & CAN_BE_HIT) && I.attack_atom(src, user, params)) +/obj/attackby(obj/item/attacking_item, mob/user, params) + return ..() || ((obj_flags & CAN_BE_HIT) && attacking_item.attack_atom(src, user, params)) /mob/living/attackby(obj/item/attacking_item, mob/living/user, params) if(..()) diff --git a/code/game/objects/items/devices/paicard.dm b/code/game/objects/items/devices/paicard.dm index beee288737e..b465dec9060 100644 --- a/code/game/objects/items/devices/paicard.dm +++ b/code/game/objects/items/devices/paicard.dm @@ -9,7 +9,9 @@ w_class = WEIGHT_CLASS_SMALL slot_flags = ITEM_SLOT_BELT custom_premium_price = PAYCHECK_HARD * 1.25 - ///don't spam alert messages. + resistance_flags = FIRE_PROOF | ACID_PROOF | INDESTRUCTIBLE + + /// Spam alert prevention var/alert_cooldown /// If the pAIcard is slotted in a PDA var/slotted = FALSE @@ -17,7 +19,6 @@ var/mob/living/silicon/pai/pai ///what emotion icon we have. handled in /mob/living/silicon/pai/Topic() var/emotion_icon = "off" - resistance_flags = FIRE_PROOF | ACID_PROOF | INDESTRUCTIBLE /obj/item/paicard/suicide_act(mob/living/user) user.visible_message(span_suicide("[user] is staring sadly at [src]! [user.p_they()] can't keep living without real human intimacy!")) @@ -102,16 +103,16 @@ break if(isnull(candidate)) return FALSE - if(src.pai) + if(pai) return FALSE if(SSpai.check_ready(candidate) != candidate) return FALSE /// The newly downloaded pAI personality - var/mob/living/silicon/pai/pai = new(src) - pai.name = candidate.name || pick(GLOB.ninja_names) - pai.real_name = pai.name - pai.key = candidate.key - src.setPersonality(pai) + var/mob/living/silicon/pai/new_pai = new(src) + new_pai.name = candidate.name || pick(GLOB.ninja_names) + new_pai.real_name = new_pai.name + new_pai.key = candidate.key + setPersonality(new_pai) SSpai.candidates -= candidate if("fix_speech") to_chat(pai, span_notice("Your owner has corrected your speech modulation!")) @@ -159,13 +160,15 @@ to_chat(pai, span_notice("Your owner has [transmit_holder ? "enabled" : "disabled"] your [transmitting ? "outgoing" : "incoming"] radio transmissions!")) if("wipe_pai") var/confirm = tgui_alert(usr, "Are you certain you wish to delete the current personality? This action cannot be undone.", "Personality Wipe", list("Yes", "No")) - if(confirm == "Yes") - if(pai) - to_chat(pai, span_warning("You feel yourself slipping away from reality.")) - to_chat(pai, span_danger("Byte by byte you lose your sense of self.")) - to_chat(pai, span_userdanger("Your mental faculties leave you.")) - to_chat(pai, span_rose("oblivion... ")) - qdel(pai) + if(confirm != "Yes") + return + if(!pai) + return + to_chat(pai, span_warning("You feel yourself slipping away from reality.")) + to_chat(pai, span_danger("Byte by byte you lose your sense of self.")) + to_chat(pai, span_userdanger("Your mental faculties leave you.")) + to_chat(pai, span_rose("oblivion... ")) + qdel(pai) return // WIRE_SIGNAL = 1 diff --git a/code/game/objects/items/devices/radio/radio.dm b/code/game/objects/items/devices/radio/radio.dm index 05c9ad4e8c5..b07cdf80e12 100644 --- a/code/game/objects/items/devices/radio/radio.dm +++ b/code/game/objects/items/devices/radio/radio.dm @@ -439,16 +439,13 @@ else . += span_notice("It cannot be modified or attached.") -/obj/item/radio/attackby(obj/item/W, mob/user, params) +/obj/item/radio/screwdriver_act(mob/living/user, obj/item/tool) add_fingerprint(user) - if(W.tool_behaviour == TOOL_SCREWDRIVER) - unscrewed = !unscrewed - if(unscrewed) - to_chat(user, span_notice("The radio can now be attached and modified!")) - else - to_chat(user, span_notice("The radio can no longer be modified or attached!")) + unscrewed = !unscrewed + if(unscrewed) + to_chat(user, span_notice("The radio can now be attached and modified!")) else - return ..() + to_chat(user, span_notice("The radio can no longer be modified or attached!")) /obj/item/radio/emp_act(severity) . = ..() @@ -508,36 +505,36 @@ . = ..() set_frequency(FREQ_SYNDICATE) -/obj/item/radio/borg/attackby(obj/item/W, mob/user, params) +/obj/item/radio/borg/screwdriver_act(mob/living/user, obj/item/tool) + if(!keyslot) + to_chat(user, span_warning("This radio doesn't have any encryption keys!")) + return - if(W.tool_behaviour == TOOL_SCREWDRIVER) - if(keyslot) - for(var/ch_name in channels) - SSradio.remove_object(src, GLOB.radiochannels[ch_name]) - secure_radio_connections[ch_name] = null + for(var/ch_name in channels) + SSradio.remove_object(src, GLOB.radiochannels[ch_name]) + secure_radio_connections[ch_name] = null + if(keyslot) + var/turf/user_turf = get_turf(user) + if(user_turf) + keyslot.forceMove(user_turf) + keyslot = null - if(keyslot) - var/turf/T = get_turf(user) - if(T) - keyslot.forceMove(T) - keyslot = null + recalculateChannels() + to_chat(user, span_notice("You pop out the encryption key in the radio.")) + return ..() - recalculateChannels() - to_chat(user, span_notice("You pop out the encryption key in the radio.")) +/obj/item/radio/borg/attackby(obj/item/attacking_item, mob/user, params) - else - to_chat(user, span_warning("This radio doesn't have any encryption keys!")) - - else if(istype(W, /obj/item/encryptionkey/)) + if(istype(attacking_item, /obj/item/encryptionkey)) if(keyslot) to_chat(user, span_warning("The radio can't hold another key!")) return if(!keyslot) - if(!user.transferItemToLoc(W, src)) + if(!user.transferItemToLoc(attacking_item, src)) return - keyslot = W + keyslot = attacking_item recalculateChannels() diff --git a/code/game/objects/items/paiwire.dm b/code/game/objects/items/paiwire.dm index f9ba79253ef..118da08cf44 100644 --- a/code/game/objects/items/paiwire.dm +++ b/code/game/objects/items/paiwire.dm @@ -6,7 +6,7 @@ item_flags = NOBLUDGEON var/obj/machinery/machine //what machine we're currently hacking. -/obj/item/pai_cable/machine/Destroy() +/obj/item/pai_cable/Destroy() machine = null return ..() diff --git a/code/game/say.dm b/code/game/say.dm index b1a8bda6bae..f01573a16d5 100644 --- a/code/game/say.dm +++ b/code/game/say.dm @@ -225,7 +225,7 @@ INITIALIZE_IMMEDIATE(/atom/movable/virtualspeaker) var/mob/living/silicon/robot/B = M job = "[B.designation] Cyborg" else if(istype(M, /mob/living/silicon/pai)) // Personal AI (pAI) - job = "Personal AI" + job = JOB_PERSONAL_AI else if(isobj(M)) // Cold, emotionless machines job = "Machine" else // Unidentifiable mob diff --git a/code/modules/mob/living/silicon/pai/login.dm b/code/modules/mob/living/silicon/pai/login.dm new file mode 100644 index 00000000000..0f418c4bf32 --- /dev/null +++ b/code/modules/mob/living/silicon/pai/login.dm @@ -0,0 +1,10 @@ +/mob/living/silicon/pai/Login() + . = ..() + if(!. || !client) + return FALSE + + client.perspective = EYE_PERSPECTIVE + if(holoform) + client.eye = src + else + client.eye = card diff --git a/code/modules/mob/living/silicon/pai/pai.dm b/code/modules/mob/living/silicon/pai/pai.dm index ddd88e42018..69e02e80d21 100644 --- a/code/modules/mob/living/silicon/pai/pai.dm +++ b/code/modules/mob/living/silicon/pai/pai.dm @@ -1,6 +1,9 @@ /mob/living/silicon/pai name = "pAI" icon = 'icons/mob/pai.dmi' + held_lh = 'icons/mob/pai_item_lh.dmi' + held_rh = 'icons/mob/pai_item_rh.dmi' + head_icon = 'icons/mob/pai_item_head.dmi' icon_state = "repairbot" mouse_opacity = MOUSE_OPACITY_ICON density = FALSE @@ -16,74 +19,78 @@ 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' - head_icon = 'icons/mob/pai_item_head.dmi' radio = /obj/item/radio/headset/silicon/pai can_buckle_to = FALSE mobility_flags = MOBILITY_FLAGS_REST_CAPABLE_DEFAULT - var/network = "ss13" - var/obj/machinery/camera/current = null + + /// The card we inhabit + var/obj/item/paicard/card + /// Used as currency to purchase different abilities var/ram = 100 /// Installed software on the pAI var/list/software = list() - /// current user's DNA - var/userDNA - /// The card we inhabit - var/obj/item/paicard/card - /// Are we hacking a door? - var/hacking = FALSE - /// The progress for hacking - var/datum/progressbar/hackbar + /// The strength of the internal flashlight + var/brightness_power = 5 /// Changes the display to syndi if true var/emagged = FALSE - var/speakStatement = "states" - var/speakExclamation = "declares" - var/speakDoubleExclamation = "alarms" - var/speakQuery = "queries" - /// The cable we produce when hacking a door - var/obj/item/pai_cable/hacking_cable /// Name of the one who commands us var/master /// DNA string for owner verification var/master_dna + /// Whether we are currently holoformed + var/holoform = FALSE + /// Whether the pAI can enter holoform or not + var/canholo = TRUE + /// Whether this pAI can transmit radio messages + var/can_transmit = TRUE + /// Whether this pAI can recieve radio messages + var/can_receive = TRUE -// Various software-specific vars + + // Various software-specific vars /// Toggles whether the Security HUD is active or not var/secHUD = FALSE /// Toggles whether the Medical HUD is active or not var/medHUD = FALSE + /// Toggles whether the pAI can hold encryption keys or not + var/encryptmod = FALSE /// Toggles whether universal translator has been activated. Cannot be reversed var/languages_granted = FALSE - /// The airlock being hacked - var/obj/machinery/door/hackdoor - /// Possible values: 0 - 100, >= 100 means the hack is complete and will be reset upon next check - var/hackprogress = 0 - - // Software /// Atmospheric analyzer var/obj/item/analyzer/atmos_analyzer /// AI's signaler var/obj/item/assembly/signaler/internal/signaler - /// Synthesizer + /// pAI Synthesizer var/obj/item/instrument/piano_synth/internal_instrument /// pAI Newscaster var/obj/machinery/newscaster /// pAI healthanalyzer var/obj/item/healthanalyzer/hostscan /// Internal pAI GPS, enabled if pAI downloads GPS software, and then uses it. - var/obj/item/gps/pai/internal_gps = null + var/obj/item/gps/pai/internal_gps + /// The cable we produce when hacking a door + var/obj/item/pai_cable/hacking_cable - var/encryptmod = FALSE - var/holoform = FALSE - var/canholo = TRUE - var/can_transmit = TRUE - var/can_receive = TRUE + /// The current chasis that will appear when in holoform 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. + /// List of all possible chasises. TRUE means the pAI can be picked up in this chasis. + 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, + ) var/emitterhealth = 20 var/emittermaxhealth = 20 @@ -92,62 +99,68 @@ var/emitteroverloadcd = 100 var/emittersemicd = FALSE - var/overload_ventcrawl = 0 - var/overload_bulletblock = 0 //Why is this a good idea? - var/overload_maxhealth = 0 - var/silent = FALSE - var/brightness_power = 5 + /// Bool that determines if the pAI can refresh medical/security records. + var/refresh_spam = FALSE + /// Cached list for medical records to send as static data + var/list/medical_records = list() + /// Cached list for security records to send as static data + var/list/security_records = list() + + var/list/available_software = list( + "crew manifest" = 5, + "digital messenger" = 5, + "atmosphere sensor" = 5, + "photography module" = 5, + "camera zoom" = 10, + "printer module" = 10, + "remote signaler" = 10, + "medical records" = 10, + "security records" = 10, + "host scan" = 10, + "medical HUD" = 20, + "security HUD" = 20, + "loudness booster" = 20, + "newscaster" = 20, + "door jack" = 25, + "encryption keys" = 25, + "internal gps" = 35, + "universal translator" = 35, + ) /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) +/mob/living/silicon/pai/handle_atom_del(atom/deleting_atom) + if(deleting_atom == hacking_cable) hacking_cable = null if(!QDELETED(card)) card.update_appearance() - if(A == atmos_analyzer) + if(deleting_atom == atmos_analyzer) atmos_analyzer = null - if(A == internal_instrument) + if(deleting_atom == internal_instrument) internal_instrument = null - if(A == newscaster) + if(deleting_atom == newscaster) newscaster = null - if(A == signaler) + if(deleting_atom == signaler) signaler = null - if(A == hostscan) + if(deleting_atom == hostscan) hostscan = null - if(A == internal_gps) + if(deleting_atom == internal_gps) internal_gps = null return ..() -/mob/living/silicon/pai/Destroy() - QDEL_NULL(atmos_analyzer) - QDEL_NULL(internal_instrument) - QDEL_NULL(hacking_cable) - QDEL_NULL(newscaster) - QDEL_NULL(signaler) - QDEL_NULL(hostscan) - QDEL_NULL(internal_gps) - if(!QDELETED(card) && loc != card) - card.forceMove(drop_location()) - card.pai = null //these are otherwise handled by paicard/handle_atom_del() - card.emotion_icon = initial(card.emotion_icon) - card.update_appearance() - GLOB.pai_list -= src - return ..() - /mob/living/silicon/pai/Initialize(mapload) - var/obj/item/paicard/P = loc + var/obj/item/paicard/pai_card = loc START_PROCESSING(SSfastprocess, src) GLOB.pai_list += src make_laws() - if(!istype(P)) //when manually spawning a pai, we create a card to put it into. - var/newcardloc = P - P = new /obj/item/paicard(newcardloc) - P.setPersonality(src) - forceMove(P) - card = P - job = "Personal AI" + if(!istype(pai_card)) //when manually spawning a pai, we create a card to put it into. + var/newcardloc = pai_card + pai_card = new /obj/item/paicard(newcardloc) + pai_card.setPersonality(src) + forceMove(pai_card) + card = pai_card + job = JOB_PERSONAL_AI atmos_analyzer = new /obj/item/analyzer(src) signaler = new /obj/item/assembly/signaler/internal(src) hostscan = new /obj/item/healthanalyzer(src) @@ -167,57 +180,37 @@ ADD_TRAIT(src, TRAIT_IMMOBILIZED, PAI_FOLDED) ADD_TRAIT(src, TRAIT_HANDS_BLOCKED, PAI_FOLDED) +/mob/living/silicon/pai/Destroy() + QDEL_NULL(atmos_analyzer) + QDEL_NULL(internal_instrument) + QDEL_NULL(hacking_cable) + QDEL_NULL(newscaster) + QDEL_NULL(signaler) + QDEL_NULL(hostscan) + QDEL_NULL(internal_gps) + if(!QDELETED(card) && loc != card) + card.forceMove(drop_location()) + card.pai = null //these are otherwise handled by paicard/handle_atom_del() + card.emotion_icon = initial(card.emotion_icon) + card.update_appearance() + GLOB.pai_list -= src + return ..() /mob/living/silicon/pai/proc/pdaconfig() //PDA - aiPDA = new/obj/item/pda/ai(src) + aiPDA = new /obj/item/pda/ai(src) aiPDA.owner = real_name aiPDA.ownjob = "pAI Messenger" - aiPDA.name = real_name + " (" + aiPDA.ownjob + ")" - -/mob/living/silicon/pai/proc/process_hack(delta_time, times_fired) - if(hacking_cable?.machine && istype(hacking_cable.machine, /obj/machinery/door) && hacking_cable.machine == hackdoor && get_dist(src, hackdoor) <= 1) - hackprogress = clamp(hackprogress + (2 * delta_time), 0, HACK_COMPLETE) - hackbar.update(hackprogress) - else - to_chat(src, span_notice("Door Jack: Connection to airlock has been lost. Hack aborted.")) - hackprogress = 0 - hacking = FALSE - hackdoor = null - hackbar.end_progress() - QDEL_NULL(hackbar) - QDEL_NULL(hacking_cable) - if(!QDELETED(card)) - card.update_appearance() - return - if(hackprogress >= HACK_COMPLETE) - hackprogress = 0 - hacking = FALSE - hackbar.end_progress() - var/obj/machinery/door/door = hacking_cable.machine - door.open() - QDEL_NULL(hackbar) - QDEL_NULL(hacking_cable) + aiPDA.name = "[real_name] ([aiPDA.ownjob])" /mob/living/silicon/pai/make_laws() laws = new /datum/ai_laws/pai() return TRUE -/mob/living/silicon/pai/Login() - . = ..() - if(!. || !client) - return FALSE - - client.perspective = EYE_PERSPECTIVE - if(holoform) - client.eye = src - else - client.eye = card - /mob/living/silicon/pai/get_status_tab_items() . += ..() if(!stat) - . += text("Emitter Integrity: [emitterhealth * (100/emittermaxhealth)]") + . += text("Emitter Integrity: [emitterhealth * (100 / emittermaxhealth)]") else . += text("Systems nonfunctional") @@ -236,65 +229,6 @@ if(delold) qdel(src) -/datum/action/innate/pai - name = "PAI Action" - icon_icon = 'icons/mob/actions/actions_silicon.dmi' - var/mob/living/silicon/pai/P - -/datum/action/innate/pai/Trigger() - if(!ispAI(owner)) - return 0 - P = owner - -/datum/action/innate/pai/software - name = "Software Interface" - button_icon_state = "pai" - background_icon_state = "bg_tech" - -/datum/action/innate/pai/software/Trigger() - ..() - P.ui_act() - -/datum/action/innate/pai/shell - name = "Toggle Holoform" - button_icon_state = "pai_holoform" - background_icon_state = "bg_tech" - -/datum/action/innate/pai/shell/Trigger() - ..() - if(P.holoform) - P.fold_in(0) - else - P.fold_out() - -/datum/action/innate/pai/chassis - name = "Holochassis Appearance Composite" - button_icon_state = "pai_chassis" - background_icon_state = "bg_tech" - -/datum/action/innate/pai/chassis/Trigger() - ..() - P.choose_chassis() - -/datum/action/innate/pai/rest - name = "Rest" - button_icon_state = "pai_rest" - background_icon_state = "bg_tech" - -/datum/action/innate/pai/rest/Trigger() - ..() - P.toggle_resting() - -/datum/action/innate/pai/light - name = "Toggle Integrated Lights" - icon_icon = 'icons/mob/actions/actions_spells.dmi' - button_icon_state = "emp" - background_icon_state = "bg_tech" - -/datum/action/innate/pai/light/Trigger() - ..() - P.toggle_integrated_light() - /mob/living/silicon/pai/Process_Spacemove(movement_dir = 0) . = ..() if(!.) @@ -307,21 +241,6 @@ . = ..() . += "A personal AI in holochassis mode. Its master ID string seems to be [master]." -/mob/living/silicon/pai/Life(delta_time = SSMOBS_DT, times_fired) - . = ..() - if(QDELETED(src) || stat == DEAD) - return - if(hacking_cable) - if(get_dist(src, hacking_cable) > 1) - var/turf/T = get_turf(src) - T.visible_message(span_warning("[hacking_cable] rapidly retracts back into its spool."), span_hear("You hear a click and the sound of wire spooling rapidly.")) - QDEL_NULL(hacking_cable) - if(!QDELETED(card)) - card.update_appearance() - else if(hacking) - process_hack(delta_time, times_fired) - silent = max(silent - (0.5 * delta_time), 0) - /mob/living/silicon/pai/updatehealth() if(status_flags & GODMODE) return @@ -337,8 +256,11 @@ return ..() +/obj/item/paicard/screwdriver_act(mob/living/user, obj/item/tool) + return pai.radio.screwdriver_act(user, tool) + /obj/item/paicard/attackby(obj/item/used, mob/user, params) - if(pai && (istype(used, /obj/item/encryptionkey) || used.tool_behaviour == TOOL_SCREWDRIVER)) + if(pai && istype(used, /obj/item/encryptionkey)) if(!pai.encryptmod) to_chat(user, span_alert("Encryption Key ports not configured.")) return diff --git a/code/modules/mob/living/silicon/pai/pai_actions.dm b/code/modules/mob/living/silicon/pai/pai_actions.dm new file mode 100644 index 00000000000..c6e92cea578 --- /dev/null +++ b/code/modules/mob/living/silicon/pai/pai_actions.dm @@ -0,0 +1,58 @@ +/datum/action/innate/pai + name = "PAI Action" + icon_icon = 'icons/mob/actions/actions_silicon.dmi' + var/mob/living/silicon/pai/pai_owner + +/datum/action/innate/pai/Trigger() + if(!ispAI(owner)) + return FALSE + pai_owner = owner + +/datum/action/innate/pai/software + name = "Software Interface" + button_icon_state = "pai" + background_icon_state = "bg_tech" + +/datum/action/innate/pai/software/Trigger() + ..() + pai_owner.ui_act() + +/datum/action/innate/pai/shell + name = "Toggle Holoform" + button_icon_state = "pai_holoform" + background_icon_state = "bg_tech" + +/datum/action/innate/pai/shell/Trigger() + ..() + if(pai_owner.holoform) + pai_owner.fold_in(0) + else + pai_owner.fold_out() + +/datum/action/innate/pai/chassis + name = "Holochassis Appearance Composite" + button_icon_state = "pai_chassis" + background_icon_state = "bg_tech" + +/datum/action/innate/pai/chassis/Trigger() + ..() + pai_owner.choose_chassis() + +/datum/action/innate/pai/rest + name = "Rest" + button_icon_state = "pai_rest" + background_icon_state = "bg_tech" + +/datum/action/innate/pai/rest/Trigger() + ..() + pai_owner.toggle_resting() + +/datum/action/innate/pai/light + name = "Toggle Integrated Lights" + icon_icon = 'icons/mob/actions/actions_spells.dmi' + button_icon_state = "emp" + background_icon_state = "bg_tech" + +/datum/action/innate/pai/light/Trigger() + ..() + pai_owner.toggle_integrated_light() diff --git a/code/modules/mob/living/silicon/pai/pai_defense.dm b/code/modules/mob/living/silicon/pai/pai_defense.dm index 6a4263fd56c..517f2608b5f 100644 --- a/code/modules/mob/living/silicon/pai/pai_defense.dm +++ b/code/modules/mob/living/silicon/pai/pai_defense.dm @@ -6,9 +6,8 @@ . = ..() if(. & EMP_PROTECT_SELF) return - take_holo_damage(50/severity) - Paralyze(400/severity) - silent = max(20/severity, silent) + take_holo_damage(50 / severity) + Stun(400 / severity) if(holoform) fold_in(force = TRUE) //Need more effects that aren't instadeath or permanent law corruption. @@ -41,19 +40,20 @@ Paralyze(200) /mob/living/silicon/pai/attack_hand(mob/living/carbon/human/user, list/modifiers) - if(user.combat_mode) - user.do_attack_animation(src) - if (user.name == master) - visible_message(span_notice("Responding to its master's touch, [src] disengages its holochassis emitter, rapidly losing coherence.")) - if(do_after(user, 1 SECONDS, TRUE, src)) - fold_in() - if(user.put_in_hands(card)) - user.visible_message(span_notice("[user] promptly scoops up [user.p_their()] pAI's card.")) - else - visible_message(span_danger("[user] stomps on [src]!.")) - take_holo_damage(2) - else + if(!user.combat_mode) visible_message(span_notice("[user] gently pats [src] on the head, eliciting an off-putting buzzing from its holographic field.")) + return + user.do_attack_animation(src) + if(user.name != master) + visible_message(span_danger("[user] stomps on [src]!.")) + take_holo_damage(2) + return + visible_message(span_notice("Responding to its master's touch, [src] disengages its holochassis emitter, rapidly losing coherence.")) + if(!do_after(user, 1 SECONDS, TRUE, src)) + return + fold_in() + if(user.put_in_hands(card)) + user.visible_message(span_notice("[user] promptly scoops up [user.p_their()] pAI's card.")) /mob/living/silicon/pai/bullet_act(obj/projectile/Proj) if(Proj.stun) @@ -61,14 +61,15 @@ src.visible_message(span_warning("The electrically-charged projectile disrupts [src]'s holomatrix, forcing [src] to fold in!")) . = ..(Proj) -/mob/living/silicon/pai/IgniteMob(mob/living/silicon/pai/P) - return FALSE //No we're not flammable +/mob/living/silicon/pai/IgniteMob() + fire_stacks = 0 + . = ..() /mob/living/silicon/pai/proc/take_holo_damage(amount) emitterhealth = clamp((emitterhealth - amount), -50, emittermaxhealth) if(emitterhealth < 0) fold_in(force = TRUE) - if (amount > 0) + if(amount > 0) to_chat(src, span_userdanger("The impact degrades your holochassis!")) return amount diff --git a/code/modules/mob/living/silicon/pai/pai_say.dm b/code/modules/mob/living/silicon/pai/pai_say.dm index d98f615a879..b35abfe7f9d 100644 --- a/code/modules/mob/living/silicon/pai/pai_say.dm +++ b/code/modules/mob/living/silicon/pai/pai_say.dm @@ -1,8 +1,2 @@ -/mob/living/silicon/pai/say(message, bubble_type, list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null, filterproof = null) - if(silent) - to_chat(src, span_warning("Communication circuits remain uninitialized.")) - else - ..() - /mob/living/silicon/pai/binarycheck() return radio?.translate_binary diff --git a/code/modules/mob/living/silicon/pai/personality.dm b/code/modules/mob/living/silicon/pai/personality.dm index b00fe994c10..92cf5747801 100644 --- a/code/modules/mob/living/silicon/pai/personality.dm +++ b/code/modules/mob/living/silicon/pai/personality.dm @@ -1,11 +1,11 @@ -/* - name - key - description - role - comments - ready = 0 -*/ +/** + * name + * key + * description + * role + * comments + * ready = TRUE + */ /datum/pai_candidate/proc/savefile_path(mob/user) return "data/player_saves/[user.ckey[1]]/[user.ckey]/pai.sav" @@ -24,17 +24,17 @@ // loads the savefile corresponding to the mob's ckey // if silent=true, report incompatible savefiles -// returns 1 if loaded (or file was incompatible) -// returns 0 if savefile did not exist +// returns TRUE if loaded (or file was incompatible) +// returns FALSE if savefile did not exist /datum/pai_candidate/proc/savefile_load(mob/user, silent = TRUE) if (is_guest_key(user.key)) - return 0 + return FALSE var/path = savefile_path(user) if (!fexists(path)) - return 0 + return FALSE var/savefile/F = new /savefile(path) @@ -48,9 +48,9 @@ fdel(path) if (!silent) tgui_alert(user, "Your savefile was incompatible with this version and was deleted.") - return 0 + return FALSE F["name"] >> src.name F["description"] >> src.description F["comments"] >> src.comments - return 1 + return TRUE diff --git a/code/modules/mob/living/silicon/pai/software.dm b/code/modules/mob/living/silicon/pai/software.dm index 6f541bdbf99..0c4a0fcdc08 100644 --- a/code/modules/mob/living/silicon/pai/software.dm +++ b/code/modules/mob/living/silicon/pai/software.dm @@ -1,35 +1,3 @@ -/mob/living/silicon/pai/var/list/available_software = list( - //Nightvision - //T-Ray - //radiation eyes - //chem goggs - //mesons - "crew manifest" = 5, - "digital messenger" = 5, - "atmosphere sensor" = 5, - "photography module" = 5, - "camera zoom" = 10, - "printer module" = 10, - "remote signaler" = 10, - "medical records" = 10, - "security records" = 10, - "host scan" = 10, - "medical HUD" = 20, - "security HUD" = 20, - "loudness booster" = 20, - "newscaster" = 20, - "door jack" = 25, - "encryption keys" = 25, - "internal gps" = 35, - "universal translator" = 35 - ) -/// Bool that determines if the pAI can refresh medical/security records. -/mob/living/silicon/pai/var/refresh_spam = FALSE -/// Cached list for medical records to send as static data -/mob/living/silicon/pai/var/list/medical_records = list() -/// Cached list for security records to send as static data -/mob/living/silicon/pai/var/list/security_records = list() - // Opens TGUI interface /mob/living/silicon/pai/ui_interact(mob/user, datum/tgui/ui) ui = SStgui.try_update_ui(user, src, ui) @@ -124,10 +92,8 @@ if("door_jack") if(params["jack"] == "jack") if(hacking_cable?.machine) - hackdoor = hacking_cable.machine - hackloop() + hack_door() if(params["jack"] == "cancel") - hackdoor = null QDEL_NULL(hacking_cable) if(params["jack"] == "cable") extendcable() @@ -213,20 +179,20 @@ return to_chat(pai, span_notice("Requesting a DNA sample.")) var/confirm = tgui_alert(master, "[pai] is requesting a DNA sample from you. Will you allow it to confirm your identity?", "Checking DNA", list("Yes", "No")) - if(confirm == "Yes") - master.visible_message(span_notice("[master] presses [master.p_their()] thumb against [pai]."),\ - span_notice("You press your thumb against [pai]."),\ - span_notice("[pai] makes a sharp clicking sound as it extracts DNA material from [master].")) - if(!master.has_dna()) - to_chat(pai, "No DNA detected.") - return - to_chat(pai, "

[master]'s UE string : [master.dna.unique_enzymes]

") - if(master.dna.unique_enzymes == pai.master_dna) - to_chat(pai, "DNA is a match to stored Master DNA.") - else - to_chat(pai, "DNA does not match stored Master DNA.") - else + if(confirm != "Yes") to_chat(pai, span_warning("[master] does not seem like [master.p_theyre()] going to provide a DNA sample willingly.")) + return + master.visible_message(span_notice("[master] presses [master.p_their()] thumb against [pai]."),\ + span_notice("You press your thumb against [pai]."),\ + span_notice("[pai] makes a sharp clicking sound as it extracts DNA material from [master].")) + if(!master.has_dna()) + to_chat(pai, "No DNA detected.") + return + to_chat(pai, "

[master]'s UE string : [master.dna.unique_enzymes]

") + if(master.dna.unique_enzymes == pai.master_dna) + to_chat(pai, span_bold("DNA is a match to stored Master DNA.")) + else + to_chat(pai, span_bold("DNA does not match stored Master DNA.")) /** * Host scan supporting proc @@ -253,35 +219,46 @@ /mob/living/silicon/pai/proc/extendcable() QDEL_NULL(hacking_cable) //clear any old cables hacking_cable = new - var/transfered_to_mob - if(isliving(card.loc)) - var/mob/living/hacker = card.loc - if(hacker.put_in_hands(hacking_cable)) - transfered_to_mob = TRUE - hacker.visible_message(span_warning("A port on [src] opens to reveal \a [hacking_cable], which you quickly grab hold of."), span_hear("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(span_warning("A port on [src] opens to reveal \a [hacking_cable], which promptly falls to the floor."), span_hear("You hear the soft click of something light and hard falling to the ground.")) + if(!isliving(card.loc)) + return + var/mob/living/hacker = card.loc + if(hacker.put_in_hands(hacking_cable)) + hacker.visible_message(span_warning("A port on [src] opens to reveal \a [hacking_cable], which you quickly grab hold of."), span_hear("You hear the soft click of something light and manage to catch hold of [hacking_cable].")) + return + hacking_cable.forceMove(drop_location()) + hacking_cable.visible_message(span_warning("A port on [src] opens to reveal \a [hacking_cable], which promptly falls to the floor."), span_hear("You hear the soft click of something light and hard falling to the ground.")) /** * Door jacking supporting proc * - * This begins the hacking process on a door. - * Mostly, this gives UI feedback, while the "hack" - * is handled inside pai.dm itself. + * This will, after alerting any AIs on station, begin to hack open a door. + * After a 10 second timer, the door will crack open, provided they don't move out of the way. */ -/mob/living/silicon/pai/proc/hackloop() + +/mob/living/silicon/pai/proc/hack_door() var/turf/turf = get_turf(src) playsound(src, 'sound/machines/airlock_alien_prying.ogg', 50, TRUE) to_chat(usr, span_boldnotice("You begin overriding the airlock security protocols.")) - for(var/mob/living/silicon/ai/AI in GLOB.player_list) + for(var/mob/living/silicon/ai/all_ais in GLOB.player_list) + if(!all_ais.stat) + continue if(turf.loc) - to_chat(AI, "Network Alert: Brute-force security override in progress in [turf.loc].") + to_chat(all_ais, span_boldannounce("Network Alert: Brute-force security override in progress in [turf.loc].")) else - to_chat(AI, "Network Alert: Brute-force security override in progress. Unable to pinpoint location.") - if(!hackbar) - hackbar = new(src, HACK_COMPLETE, hacking_cable.machine) - hacking = TRUE + to_chat(all_ais, span_boldannounce("Network Alert: Brute-force security override in progress. Unable to pinpoint location.")) + //Now begin hacking + if(!do_after(src, 10 SECONDS, hacking_cable.machine, timed_action_flags = NONE, progress = TRUE)) + to_chat(src, span_notice("Door Jack: Connection to airlock has been lost. Hack aborted.")) + hacking_cable.visible_message( + span_warning("[hacking_cable] rapidly retracts back into its spool."),\ + span_hear("You hear a click and the sound of wire spooling rapidly.")) + QDEL_NULL(hacking_cable) + if(!QDELETED(card)) + card.update_appearance() + return + var/obj/machinery/door/door = hacking_cable.machine + door.open() + QDEL_NULL(hacking_cable) /** * Proc that switches whether a pAI can refresh diff --git a/code/modules/mob/living/silicon/robot/robot_defense.dm b/code/modules/mob/living/silicon/robot/robot_defense.dm index 11f94a5361e..5035055472e 100644 --- a/code/modules/mob/living/silicon/robot/robot_defense.dm +++ b/code/modules/mob/living/silicon/robot/robot_defense.dm @@ -82,7 +82,7 @@ GLOBAL_LIST_INIT(blacklisted_borg_hats, typecacheof(list( //Hats that don't real if(shell) to_chat(user, span_warning("You cannot seem to open the radio compartment!")) //Prevent AI radio key theft else if(radio) - radio.attackby(W,user)//Push it to the radio to let it handle everything + radio.screwdriver_act(user, W)//Push it to the radio to let it handle everything else to_chat(user, span_warning("Unable to locate a radio!")) update_icons() @@ -159,7 +159,7 @@ GLOBAL_LIST_INIT(blacklisted_borg_hats, typecacheof(list( //Hats that don't real MOD.install(laws, user) //Proc includes a success mesage so we don't need another one return - if(istype(W, /obj/item/encryptionkey/) && opened) + if(istype(W, /obj/item/encryptionkey) && opened) if(radio)//sanityyyyyy radio.attackby(W,user)//GTFO, you have your own procs else diff --git a/tgstation.dme b/tgstation.dme index d099575acb1..901908aa0b2 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -111,7 +111,6 @@ #include "code\__DEFINES\networks.dm" #include "code\__DEFINES\nitrile.dm" #include "code\__DEFINES\obj_flags.dm" -#include "code\__DEFINES\pai.dm" #include "code\__DEFINES\pinpointers.dm" #include "code\__DEFINES\pipe_construction.dm" #include "code\__DEFINES\plumbing.dm" @@ -3149,7 +3148,9 @@ #include "code\modules\mob\living\silicon\ai\freelook\chunk.dm" #include "code\modules\mob\living\silicon\ai\freelook\eye.dm" #include "code\modules\mob\living\silicon\pai\death.dm" +#include "code\modules\mob\living\silicon\pai\login.dm" #include "code\modules\mob\living\silicon\pai\pai.dm" +#include "code\modules\mob\living\silicon\pai\pai_actions.dm" #include "code\modules\mob\living\silicon\pai\pai_defense.dm" #include "code\modules\mob\living\silicon\pai\pai_say.dm" #include "code\modules\mob\living\silicon\pai\pai_shell.dm"